4.3 KiB
4.3 KiB
[Claude #7]
unit_executed: u4 — stale-detect short-circuit (compare index.html mtime+sha256 against last manifest entry; mark skipped_unchanged when both match)
files_changed:
- MOD
D:\ad-hoc\kei\design_agent\scripts\generate_frame_previews.py(88 → 110 lines perMeasure-Object -Line; u4 net add = 22 lines, ≤ Stage 2 u4 estimate of 45)
diff_summary:
- Added
_sha256_file(path: Path) -> str(private, leading underscore) — bufferedhashlib.sha256()digest ofindex.htmlcontent, 65 KiB chunked read. Stdlib only. - Added
is_unchanged(row: FrameRow, last_entry: Optional[Dict[str, Any]]) -> bool— the Stage 2 u4 short-circuit predicate. ReturnsTrueiff:last_entryis notNone, ANDrow.has_indexandrow.has_previeware bothTrue, ANDpreview.pngmtime>=index.htmlmtime (per Stage 2 guardrail: "skip if preview.png mtime >= index.html mtime AND content hash unchanged"), ANDlast_entry["index_sha256"]is non-empty, AND- recomputed
_sha256_file(index.html)equalslast_entry["index_sha256"]. Otherwise returnsFalse— the caller (u6) MUST re-render.OSErroronstat()is caught and treated as "must re-render" (defensive against transient FS races).
- No wiring into
main()— predicate is provided in isolation per Stage 2 unit boundary. u6 will load the manifest, callis_unchanged()per row, and write back theskipped_unchanged/renderedstatus. This keeps u4 ≤ 45-line guardrail and lets u5 (orphan/missing categorization) and u6 (manifest writer + coverage report) layer on top without re-touching u4 surface. - Module-level imports: added
hashlib(stdlib) andDict,Optionalto existingtypingimport. No new third-party deps. Selenium remains lazy (u2/u3 contract preserved). - Module docstring updated:
(u1-u3; stale/orphan/manifest added in u4-u6)→(u1-u4; orphan/manifest added in u5-u6). FrameRow/discover/_build_driver/render_one/main/ CLI untouched. u1/u2/u3 contracts preserved.
verification:
(Get-Content scripts/generate_frame_previews.py | Measure-Object -Line).Lines→ 110 (u4 add = 22 lines, ≤ 45 guardrail).python scripts/generate_frame_previews.py --dry-run→discovered: total=33 with_index_html=20 with_preview_png=4(unchanged from u1/u2/u3 baseline at commit56619a0— u4 does not alter discovery output and does not wire into main).$env:PYTHONPATH = '...\scripts'; python -c "import generate_frame_previews as m; print('import_ok', hasattr(m,'is_unchanged'), hasattr(m,'_sha256_file'), hasattr(m,'render_one'), hasattr(m,'_build_driver'), hasattr(m,'discover'), hasattr(m,'main'))"→import_ok True True True True True True(lazy-selenium import preserved — module imports without selenium installed).- Forbidden-pattern audit
rg -n "^(import|from) |anthropic|openai|jinja|phase_z2|slide_measurer|1280|720|1400|900"→ only stdlib imports (argparse,hashlib,sys,dataclasses,pathlib,typing). No AI/Jinja/phase_z2/slide_measurersubstring; no1280/720/1400/900literal. RULE 7 (no hardcoding) holds. - Predicate behavioral smoke (
.orchestrator/tmp/u4_smoke.py, gitignored temp): 6 cases — (1)Nonelast_entry → False, (2) matching sha + png-newer-than-idx → True, (3) matching sha but stale png (older than idx) → False, (4) mismatched sha → False, (5) empty/missingindex_sha256key → False, (6) missing index.html (post-unlink rediscover) → False. All assertions pass; final lineu4_predicate_okprinted. - Scope guard:
git diff --name-only -- src/slide_measurer.py src/phase_z2_pipeline.py Front/vite.config.ts templates/phase_z2/frames tests figma_to_html_agent/blocks/_preview_manifest.json→ empty output (no tracked-file change outsidescripts/generate_frame_previews.py). - Tracked status:
scripts/generate_frame_previews.pyis the only IMP-13 working-tree change;figma_to_html_agent/blocks/_preview_manifest.jsonis not created in u4 (correctly deferred to u6).
remaining_units: [u5, u6, u7]
follow_up_issue_candidates: none from this unit. Stage 2 already flagged IMP-04 (missing_index_html=13) and data/figma_previews/index.json parse failure as separate axes. u4 introduces no new out-of-scope dependency.
rewind_target: continue_same