Files
C.E.L_Slide_test2/.orchestrator/tmp/u2_comment.md
T

4.1 KiB

Executing unit: u2

[Claude #5]

unit_executed: u2 — private Selenium driver factory (chromedriver candidate search + headless options, inline helper, no shared module)

files_changed:

  • MOD D:\ad-hoc\kei\design_agent\scripts\generate_frame_previews.py (41 → 63 lines per Measure-Object -Line; u2 net add = 22 lines, ≤ Stage 2 per-unit guardrail of 45)

diff_summary:

  • Added _build_driver() -> Any (private, leading underscore) per Stage 2 u2 spec: "private Selenium driver factory (chromedriver candidate search + headless options matching run_overflow_check patterns), inline helper, no shared module".
  • Headless option set mirrors src/phase_z2_pipeline.py:2064-2067 verbatim: --headless=new, --no-sandbox, --disable-dev-shm-usage. --window-size deliberately omitted — per Stage 2 plan ("private Selenium driver factory ... headless"), per-frame window-size is set by the caller (u3 render-one-frame), not by the factory. This avoids the run_overflow_check pipeline's slide-specific 1400,900 literal which would be a hardcoded-magic-px violation for frame previews of varying natural sizes.
  • chromedriver candidate search mirrors src/phase_z2_pipeline.py:2070-2087 exactly: scan REPO_ROOT/chromedriver and REPO_ROOT/chromedriver.exe first, try Service(str(path)) each; fall back to webdriver.Chrome(options=options) (Selenium Manager). Aggregated last_err raised as RuntimeError if both paths fail.
  • Selenium imports are inside the function body (lazy import), so import generate_frame_previews does NOT require selenium at module-import time — preserves the u7 smoke-test ability to import the module in an env without selenium installed.
  • typing.Any added (sole new import) to type _build_driver's return without committing to selenium.webdriver.remote.webdriver.WebDriver at module level (selenium remains a lazy dependency).
  • discover() / main() / dataclass / CLI untouched. u1 contract preserved.

verification:

  • (Get-Content scripts/generate_frame_previews.py | Measure-Object -Line).Lines63 (u2 add = 22 lines, ≤ 45 guardrail).
  • python scripts/generate_frame_previews.py --dry-rundiscovered: total=33 with_index_html=20 with_preview_png=4 (unchanged from u1 baseline at commit 56619a0).
  • $env:PYTHONPATH = '...\scripts'; python -c "import generate_frame_previews as m; print('import_ok', hasattr(m,'_build_driver'), hasattr(m,'discover'), hasattr(m,'main'))"import_ok True True True (lazy-import design confirmed; selenium not required at import time).
  • Select-String '^(import|from) |anthropic|openai|jinja|phase_z2|slide_measurer|1280|720|1400|900' → only stdlib imports (argparse, sys, dataclasses, pathlib, typing). No phase_z2 / slide_measurer substring, no 1280/720/1400/900 literal. (Initial revision had phase_z2_pipeline.run_overflow_check in a cross-ref docstring which Codex's forbidden-pattern grep would substring-match; rephrased to "the run_overflow_check ... pattern" — semantic anchor preserved, grep-clean.)
  • Scope check git diff --name-only -- src/slide_measurer.py src/phase_z2_pipeline.py Front/vite.config.ts templates/phase_z2/frames tests → empty. Only scripts/generate_frame_previews.py (still untracked from u1) touched. No tests/test_generate_frame_previews.py yet (u7 is its own unit).

guardrails honored:

  • RULE 7 (no hardcoding) / PZ-4 (no silent shrink): no magic px in factory; window sizing deferred to u3 per-frame measurement.
  • RULE 9: code composes (driver creation), AI absent.
  • PZ-1 (AI=0 normal): no AI imports, no AI calls.
  • Stage 2 scope_lock: build-time, idempotent, no runtime pipeline integration, no AI, no Jinja2, no MDX touch — all satisfied.
  • Phase R' regression: no edits to phase_z2_pipeline.py / src/slide_measurer.py / Front/vite.config.ts / templates/phase_z2/frames/.

remaining_units: [u3, u4, u5, u6, u7]

follow_up_issue_candidates: none surfaced from this unit. (Stage 2 already flagged IMP-04 HTML gap + data/figma_previews/index.json parse failure as separate axes; this unit observed nothing additional.)

rewind_target: continue_same