2bb0acac1970e67146a9452b0bbb4867e27cf8bd
26
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e32f632464 |
fix(orchestrator): P4a baseline-diff guard + Stage 5 commit scope
P4 had two production issues blocking #50 integration audit deployment: 1. Stage 3 guard had no baseline awareness — flagged ALL forbidden-path changes including pre-existing dirty WIP. Empirical: 328 such files already in current working tree (tests/matching/ artifacts etc). #50 would have hit reject loops immediately without Claude doing anything wrong. 2. Stage 5 had no commit-scope guard — if Claude ran `git add -A` and committed user's existing WIP, audit commit would be polluted with unrelated production changes. P4a additions: - _audit_baseline_path / _ensure_audit_baseline / _load_audit_baseline: snapshot working-tree dirty paths at run_issue entry for audit issues. Resumed runs preserve existing baseline (no overwrite). - _check_audit_only_violations(baseline=None): accept baseline set, subtract from violations — only flags NEW forbidden changes introduced after audit start. - _check_audit_commit_scope: verify HEAD commit's file list matches AUDIT_ALLOWED_COMMIT_GLOBS (INTEGRATION-AUDIT-*.md, BACKLOG.md). - run_issue: save baseline on audit-mode entry only — no impact on normal issues. - Stage 5 (commit-push) YES gate: new guard rejects on out-of-scope files with remediation prompt (git reset --soft + force-with-lease). 19 new tests: - baseline subtraction (5): pre-existing removed, None=keep-all, empty-set=catch-all, full-coverage filter, Windows path normalize. - baseline persist (5): roundtrip, no-overwrite on resume, missing fallback, corrupt JSON fallback, non-list fallback. - commit scope detection (7): report-only allowed, backlog allowed, src/ rejected, unrelated docs rejected, git error fail-open, Windows backslash, empty commit pass. - allowed globs sanity (2): every glob has audit marker, all under docs/architecture/. Total: 94/94 pytest pass (75 prior + 19 new). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
4289a500b6 |
feat(orchestrator): P3 wrapper input/encoding fix + P4 audit-only mode
P3 hotfix (2026-05-18 — verified during #46 retry attempt): - _run_with_tree_kill: encode input only when Popen is in binary mode. Previously force-encoded str→bytes even with encoding= set, breaking text-mode stdin pipes with: write() argument must be str, not bytes. - run_claude path was the only affected call site. - 3 new C7 regression tests (input+encoding / bytes+binary / auto-encode). - C3/C6 test fixtures hardened with DEVNULL stdio isolation. P4 audit-only mode (2026-05-19, prep for #50 integration audit): - _is_audit_issue: title-based detection for [INTEGRATION-AUDIT*], [AUDIT-ONLY], or "integration audit" phrase. - _audit_mode + --audit-only CLI flag: manual override regardless of title. - AUDIT_ONLY_NOTE injected into context pack across all stages/rounds. - Stage 3 (code-edit) YES gate: deterministic git status check. Changes touching src/**, templates/**, tests/** auto-reject Stage 3 YES and post a supplement-request comment. LLM-independent enforcement. - 26 new audit-mode tests (title detection, CLI override, forbidden prefix detection, allowed paths pass, Windows backslash normalization, quoted paths with spaces, git error fail-open, constants sanity). Total: 75/75 pytest pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e10ec36617 |
feat(IMP-17): AI repair fallback infra carve-out — design-only boundary + 3-cond AND gate
u1 — src/phase_z2_pipeline.py:564 route hint comment corrected from non-existent IMP-31 to IMP-17 (carve-out, AI fallback only, normal path 밖). Line 565 IMP-29 frontend override reference untouched. u2 — docs/architecture/IMP-17-CARVE-OUT.md (new) defines: - allowed scope (Step 12 restructure proposal, Step 16/17 retry fallback) - forbidden scope (normal-path AI calls, MDX compression, HTML structure) - 3-condition AND activation gate (User GO ∧ B4 frame_selection evidence ∧ IMP-04 catalog + IMP-05 V4 fallback live) - pattern shape reference (link-only): content_editor.py:21,318 + sse_utils.py:16-50 (Phase Q Archive Candidate, no port) - AI 격리 contract + Kei persona 단절 (permanent) u3 — PHASE-Z-IMPLEMENTATION-ISSUE-BACKLOG.md:68 IMP-17 row gains carve-out doc link + 3-cond AND gate pointer. u4 — PHASE-Q-INSIGHT-TO-22STEP-MAP.md AI repair fallback infra registry row prefixed with IMP-17 + carve-out link; normal_path=no preserved. Anchor test: tests/orchestrator_unit/test_imp17_comment_anchor.py asserts line 564 IMP-17 wording AND line 565 IMP-29 preservation (2 tests pass). Runtime behavior change: 0. Only delta in executable file is one comment line. Normal-path AI invocation count remains 0. Refs: gitea #17 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
23ba8b68cd |
feat(IMP-16): U1 H3 verification utility port + U2 wiring design
U1 (runtime, u1-u10): new Phase Z-owned deterministic verification module src/phase_z2_verification_utils.py (335 LOC, stdlib only) porting H3 utility surface — VerificationResult, extract_text_from_html, normalize_for_comparison, extract_keywords, strip_meta_lines, split_into_sentences, verify_text_preservation, detect_invented_text. 10 unit tests under tests/phase_z2/test_pz2_vu_*.py (56 tests). u11 (design-only): docs/architecture/IMP-16-U2-WIRING-DESIGN.md fixes the Step 1/2/14/21/22 reverse-path contract, redesigned frame-contract pattern reservation (IMP-20), and IMP-07 hard-gate criteria. No runtime wiring lands in this commit — U2 stays blocked until IMP-07 reverse path is implemented + verified + runtime-hit. Guardrails: no src.content_verifier import; no FORBIDDEN_KEI_MEMOS / generate_with_retry / REQUIRED_PATTERNS / verify_structure / verify_area / verify_all_areas usage; no AI / Kei / httpx / SSE path; AI-isolation contract upheld (utility is deterministic). Tests: 56 targeted PASS (0.19s), 15 regression baseline PASS (7.59s). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
614c53358e |
feat(IMP-15): 실행-4 — debug.json event surfacing + spec taxonomy row
Issue: #48 (IMP-15 실행-4, axis 4: debug.json + spec doc trace). Parent: #15. Depends on 실행-1/2/3 (events + classifier outputs). Surfaces the image/table event streams that 실행-1/2/3 already produced and consumed, mirroring the existing `zone_geometries_px` top-level precedent (no new pattern introduced). Adds the matching taxonomy row to the Phase Z fit-classifier/router spec. src/phase_z2_pipeline.py (+3): - write_debug_json now lifts `image_events` and `table_events` to top-level of `debug.json` via `(visual_runtime_check or {}).get(<k>, [])`, exactly mirroring the immediately preceding `zone_geometries_px` surfacing line. Defaults to `[]` when `visual_runtime_check` is None — additive, no consumer-visible breakage. docs/architecture/PHASE-Z-FIT-CLASSIFIER-ROUTER-SPEC.md (+1): - §3.1 taxonomy adds `image_aspect_mismatch` row. Row text explicitly marks the signal as post-render `fail_reasons` from Step 14 visual_runtime_check (rendered vs declared aspect ratio mismatch), NOT a router-routed fit_classifier output, and notes the separate `image_events` stream surface. Prevents future readers from wiring this taxonomy into §3.2 priority list or §4 router action map. tests/phase_z2/test_debug_json_event_surfacing.py (new, 2 tests): - `test_write_debug_json_surfaces_image_and_table_events` invokes write_debug_json with synthetic visual_runtime_check containing both event lists; reads back the on-disk debug.json and asserts both keys are present at top level with the exact payloads. - `test_write_debug_json_defaults_when_visual_runtime_check_none` asserts both new keys default to `[]` when visual_runtime_check is None — guards the defensive `(… or {})` pattern. tests/phase_z2/test_spec_taxonomy_image_aspect_mismatch.py (new, 2 tests): - `test_spec_has_image_aspect_mismatch_row` opens the spec file and asserts exactly one `^\| image_aspect_mismatch \|` row exists inside the §3.1 table block (no markdown-parser dependency). - `test_spec_row_marks_post_render_fail_reasons_semantic` asserts the row text carries both "Post-render" and "fail_reasons" tokens — enforces the Stage 1 guardrail wording. Verification (Stage 4 PASS, Claude + Codex independent): - pytest -q tests/phase_z2/test_debug_json_event_surfacing.py \ tests/phase_z2/test_spec_taxonomy_image_aspect_mismatch.py → 4 passed in 0.07s. - git diff scope: 4 files, +148 insertions / 0 deletions. Scope-locked: no edits to classifier (실행-3), event generation (실행-1/2), Step 21 viewer, §3.2 priority list, §4 router action mapping, or `table_self_overflow` taxonomy row. Pre-existing dirty/untracked working-tree files left untouched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
535c4848fd |
feat(IMP-15): 실행-3 — classifier consumes image+table events
Issue #47 (IMP-15 실행-3 axis 3): extend `classify_visual_runtime_check` to consume the `image_events[]` and `table_events[]` arrays produced by `run_overflow_check` (실행-1/2) and widen `visual_check_passed`. Changes (src/phase_z2_classifier.py): - Remove `overflow.passed=True` early-return so image/table event scans always run, even when zone-level overflow was clean. - Deferred import of `IMAGE_ASPECT_DELTA_TOL` and `TABLE_SCROLL_TOL_PX` from `phase_z2_pipeline` (circular-safe SSoT; no duplicate literals). - New `image_events` scan emits `image_aspect_mismatch` when `delta is not None AND |delta| > IMAGE_ASPECT_DELTA_TOL` (delta=None ⇒ skip, image not loaded). - New `table_events` scan emits `tabular_overflow` when `wrapper_clipped_index is None AND (excess_x or excess_y > TABLE_SCROLL_TOL_PX)` (wrapper-clipped tables deduped against the existing zone cascade). - `visual_check_passed = overflow.passed AND not classifications` — any image/table classification now flips the gate. Guardrails preserved: - §3.2 8-rule zone cascade (clipped_inner / zone-self) untouched — the new emitters are ADDITIONAL. - `placement_diagnostics`, `categories_seen`, `unclassified_signals` return-shape preserved. - No `pipeline.py` production changes; no router action or `debug.json` passthrough changes. Tests (tests/phase_z2/test_phase_z2_visual_classifier.py — new): - `test_image_aspect_mismatch_emits_classification` (|delta|>TOL fires) - `test_image_aspect_delta_below_tol_no_classification` (≤TOL skipped) - `test_standalone_table_overflow_emits_classification` (wrapper_clipped_index=None, excess>TOL fires) - `test_table_dedup_when_wrapper_clipped` (wrapper_clipped_index set ⇒ no `tabular_overflow` emit) All 4 pure-dict (no Selenium / chromedriver / pipeline execution). Tolerances imported from `phase_z2_pipeline` (SSoT enforced via test import — no classifier-local literals). Verification (Stage 4): - New classifier tests: 4/4 PASS. - Regression `tests/phase_z2/` excluding new file: 93/93 PASS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
2827622858 |
feat(IMP-16): Step 14 table_self_overflow detection
Add table self-overflow detection with element-identity wrapper dedup, mirroring the image_aspect_mismatch axis pattern (#45). JS layer: TABLE_SCROLL_TOL_PX=5 module constant; clippedWrapperMap built as Map<Element,int> keyed by DOM node reference (NOT className) so two wrappers with identical class strings remain distinguishable; table_events collected via querySelectorAll('table').forEach with closest()-ancestor walk resolving wrapper_clipped_index = int|null. Py layer: aggregate result['table_events'] and append fail_reason 'table_self_overflow' only when (excess_x>TOL OR excess_y>TOL) AND wrapper_clipped_index is None; wrapper-clipped path continues to fail via existing clipped_inner reporting. Tests (Selenium, chromedriver guard mirrored from image_check): - Fixture D: standalone <table> overflow → table_self_overflow fail - Fixture E: <table> in clipped wrapper → dedup suppresses table fail - Fixture F (F1 acceptance): two wrappers with identical className f13b-cell, W1 clipped by non-table child, W2 hosts self-overflow <table> with W2 itself NOT clipped → element-identity ensures W2's table is not suppressed by W1's class; both fails emitted. Out of scope: image_events behavior (intact from #45), classifier pass/fail consumer (→실행-3), debug.json surfacing (→실행-4). Refs: #46 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f3bff898fb |
feat(orchestrator): initial orchestrator + subprocess cleanup hardening
Pre-existing P0+P1 fixes (verified via #45 pilot 2026-05-18): - P0-1: detect_agent first-line only (fixes #45 infinite loop) - P0-2: stage_start_count sanity reset on external comment delete - P0-3: 32 pytest cases for parse/detect regressions - P1-4: execution-issue mode prompt (compact scope-tight) - P1-5: Stage 2 COMPACT_PLAN_RULE (size budget, no code snippets) - P1-6: tests:[] orchestrator-level enforcement at Stage 2 YES guard - P1-7: dual-write CRLF/trailing-whitespace normalize P3 subprocess cleanup (PID 2780 orphan grandchild regression): - (pid, create_time) signature tracking — Windows PID reuse safe - _kill_process_tree: parent-alive traversal path - _kill_tracked: parent-dead orphan path - _run_with_tree_kill: 1s monitor thread captures descendants live - atexit + SIGINT safety net via _SPAWNED set - 4 subprocess.run sites switched to wrapper (compaction/exit_report/ run_claude/run_codex) - 12 cleanup pytest cases incl. C6 PID 2780 regression test Selenium boundary unchanged — driver.quit() in phase_z2_pipeline.py and slide_measurer.py already protected by try/finally. Total: 44/44 pytest pass (32 core + 12 cleanup). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e9b3d2e9c0 |
feat(IMP-15): 실행-1 — Step 14 image_aspect_mismatch detection
Issue: #45 (IMP-15 실행-1, image axis only). Adds Selenium-based <img> aspect ratio measurement to Step 14 run_overflow_check + numeric tolerance gate. Tolerance lives as module-scope constant so tests can import it. src/phase_z2_pipeline.py (+73/-2): - L131-L135 IMAGE_ASPECT_DELTA_TOL = 0.05 (module scope, importable) - L2216-L2261 JS payload extension: image_events[] per <img> (src, zone_position via closest('.zone') with 'unknown' fallback, zone_template_id, natural/rendered w+h+ratio, delta, slide-rel bbox) - L2262 run_overflow_check return extended with image_events - L2302-L2320 Python aggregation: abs(delta) > TOL ⇒ fail_reasons append 'image aspect mismatch in zone--<pos>: natural=<n> rendered=<r> delta=<+d> (template=<tid>, tol=0.05, src=<src>)'. Null-delta entries (image not loaded) are skipped — no false positive. Branch placed AFTER existing non-image branches; ordering & strings for slide/slide-body/zone/clipped_inner unchanged. - L4425-L4429 Step 14 note: image half closed, table half deferred to 실행-2. tests/phase_z2/test_phase_z2_step14_image_check.py (+196, new): - 3-tier chromedriver resolver mirroring pipeline (PROJECT_ROOT/ chromedriver{,.exe} → PATH → Selenium Manager probe). - pytestmark: skip when chromedriver unresolvable AND PHASE_Z_REQUIRE_SELENIUM != '1'; xfail(strict=True) opt-in when =='1'. - Fixture A: 200×100 img rendered 200×100 → aspect_delta < 0.05, passed. - Fixture B: 200×100 intrinsic forced to 200×200 → delta > 0.30, fail_reason present. - Fixture C: <img> with no .zone ancestor → zone_position == 'unknown'. Verification (Stage 4 PASS, Claude + Codex independent): - pytest -q tests/phase_z2/test_phase_z2_step14_image_check.py → 3 passed - PHASE_Z_REQUIRE_SELENIUM=1 same suite → 3 passed (strict opt-in) - pytest -q tests/phase_z2 → 90 passed (no regression) - pytest -q --ignore=tests/matching → 174 passed Scope-locked: no slide_base.html / catalog / classifier / debug.json / spec-doc changes. table_events (실행-2), visual_check_passed flip (실행-3), debug.json image_events surfacing + PHASE-Z spec doc row (실행-4) remain queued as separate IMP-15 child execution issues. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
7a52cebfaa |
feat(IMP-14): A-4 — slide_base embedded vs standalone mode contract
Step 13 owns iframe-vs-standalone CSS contract in slide_base.html via
3-valued embedded_mode enum (auto / embedded / standalone). Removes
SlideCanvas.tsx runtime CSS injection workaround; frontend now passes
?embedded=1 query so auto-mode script attaches html.embedded class and
scopes the standalone body centering/min-height/padding reset.
- templates/phase_z2/slide_base.html: conditional html.embedded class +
CSP-safe auto-mode <script> + additive html.embedded body/.slide rules
- src/phase_z2_pipeline.py: render_slide gains keyword-only embedded_mode
("auto" default) + ValueError guard; 3 existing call sites unchanged
- Front/client/src/components/SlideCanvas.tsx: derive embeddedSrc with
?embedded=1 (query-preserving), drop reset CSS injection block
- tests/phase_z2/test_slide_base_embedded_mode.py: 6 cases — auto script,
CSS rules, embedded/standalone explicit modes, byte-determinism,
invalid-mode guard
|
||
|
|
7d5639ad72 |
feat(IMP-13): A-3 — build-time frame preview generator (capture_slide_screenshot salvage)
scripts/generate_frame_previews.py iterates figma_to_html_agent/blocks/{frame_id}/index.html,
renders preview.png via Selenium headless (capture_slide_screenshot pattern reuse), and writes
_preview_manifest.json (schema v1) with idempotent stale-detect (mtime+sha256). Build-time only
— no runtime pipeline integration, no AI calls, no MDX/Jinja regen. Stage 2 baseline (commit
|
||
|
|
56619a0239 |
feat(IMP-12): Step 16/17 retry refinement — multi-donor + 3-stage salvage cascade
Extend Step 17 deterministic action surface so donor_slack_insufficient no longer abort-terminates at zone_ratio_retry. AI is NOT invoked on the normal salvage path. Source changes (4 files, scope-locked): - src/phase_z2_retry.py — plan_zone_ratio_retry: single-primary-donor → multi-donor greedy aggregation (donors_used / aggregate_slack_used / aggregate_slack_available); new plan/apply pairs: cross_zone_redistribute (wraps fit_verifier.redistribute, data-role scoped CSS), glue_compression (wraps space_allocator.compute_glue_css_overrides, data-zone-position scoped), font_step_compression (wraps find_fitting_font_size, zone-scoped, defensive feasible=False on missing text_metrics). - src/phase_z2_failure_router.py — classifier inspects salvage_steps[-1] via SALVAGE_FAILURE_TYPE_BY_ACTION; NEXT_ACTION_BY_FAILURE rewired into donor_slack_insufficient/no_donor_candidates → cross_zone_redistribute → glue → font_step → layout_adjust; 3 IMPLEMENTED salvage status rows added. - src/phase_z2_router.py — ACTION_IMPLEMENTATION_STATUS registers 3 new salvage actions as IMPLEMENTED; ACTION_BY_CATEGORY untouched (cascade-only labels). - src/phase_z2_pipeline.py — new _attempt_salvage_chain() iterates router next_proposed_action with retry_budget=1 per action; honors IMP-09 dynamic_cols / fr_default gate; preserves (b)-revert on all-fail; wires Step 17 telemetry (salvage_steps / salvage_passed). Tests (6 new pytest modules): - test_phase_z2_retry_multi_donor.py — single sufficient (regression), 1st insufficient + 2nd sufficient (multi-donor PASS), aggregate insufficient FAIL. - test_phase_z2_cross_zone_redistribute.py — multi-role zone feasible, single-role zone short-circuits infeasible. - test_phase_z2_glue_compression.py — feasible asserts emitted CSS contains [data-zone-position=...] selector and NO global :root/body/.slide rule. - test_phase_z2_font_step_compression.py — 15.2 → 13 closes excess; 8px floor; missing text_metrics → defensive infeasible reason. - test_phase_z2_failure_router_cascade.py — donor_slack_insufficient → cross_zone (impl=IMPLEMENTED); 3 new failure types → expected next actions; rerender_still_fails preserves frame_reselect terminus. - test_phase_z2_step17_salvage_chain.py — end-to-end (a) cross_zone PASS promotes final.html, (b) cross_zone FAIL + glue PASS promotes 2nd candidate, (c) all-3 FAIL preserves original final.html (revert). Guardrails preserved: - AI calls: 0 on normal path (feedback_ai_isolation_contract) - Spacing direction: no shrink-common-margin; resolve via donor/glue/font-step within frame envelope (feedback_phase_z_spacing_direction) - All CSS overrides scoped to [data-role=...] or [data-zone-position=...] - IMP-09 dynamic_cols / fr_default gate honored in cascade - (b)-revert preserved if all 3 salvage actions fail Refs: gitea#12 IMP-12 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
a79bd8bc43 |
feat(IMP-11): D-2 — frame min_height_px hint (backend → UI)
Step 9 v4_all_judgments[] now exposes per-candidate min_height_px from catalog frame_contracts.visual_hints.min_height_px (None when contract unregistered). SlideCanvas pendingLayout zones render a red ring + 'min H Npx' badge when zone height falls below the active frame's threshold. Visual hint only; resize clamp (minSize=0.05) unchanged. 5 axes (single commit per Stage 5 plan): - u1 backend: src/phase_z2_pipeline.py — Step 9 builder adds min_height_px via single get_contract(c.template_id) lookup; reuses _contract for catalog_registered (no double-lookup). - u2 type: Front/client/src/types/designAgent.ts — FrameCandidate gains optional minHeightPx?: number. - u3 mapper: Front/client/src/services/designAgentApi.ts — maps snake-case min_height_px → camelCase minHeightPx on v4_all_judgments path; v4_candidates fallback remains undefined (graceful). - u4 active-frame lookup: Front/client/src/components/SlideCanvas.tsx — activeFrameId = overrideFrameId ?? defaultFrameId; activeCandidate via region.frame_candidates.find. - u5 hint render: Front/client/src/components/SlideCanvas.tsx — zoneHeightPx = height * SLIDE_H (logical px, no double-apply); compare against activeCandidate.minHeightPx in pendingLayout mode only; red border + badge when below. Tests: 5/5 pass in tests/test_phase_z2_step9_v4_all_judgments_min_height.py (source-string + catalog-shape guards + None propagation, registered and unregistered template_ids). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
1fb973297f |
feat(IMP-09): PR 2 — 2-D dynamic dispatch for 5 preset families
Stage 3 lock implementation: extend build_layout_css dispatch beyond
the horizontal-2 / vertical-2 1-D dynamic paths. T / inverted-T /
side-T-left / side-T-right / 2x2 now flow through a 2-D track solver
instead of the fr_default sink, with length-locked heights_px (R) +
widths_px (C) on every return path (default and override).
PR 2 scope (u1~u5):
- u1: _aggregate_zone_signals_per_track — per-row + per-col virtual
zones via max(weight) + max(min_height_px) of single-span zones,
falling back to all-span when a track has none.
- u2: _build_grid_dynamic_2d default builder — feeds virtual zones
into compute_zone_layout + compute_zone_layout_cols; emits
computation="2d_dynamic_aggregated", dynamic_rows=True,
dynamic_cols=True.
- u3: _override_to_grid_tracks override builder — single-span
aggregation (max h per row, max w per col), normalize, multiply
by avail_h/avail_w, last-element diff absorb; emits
computation="user_override_geometry"; falls back to u2 when
total_h or total_w == 0.
- u4: build_layout_css dispatcher wiring — topology in
{T, inverted-T, side-T-left, side-T-right, 2x2} routes to
_build_grid_dynamic_2d (default) or _override_to_grid_tracks
(override); legacy [override-warning] stderr removed for the
5 presets; step08 trace gains a 2-D-aware print line that fires
before the dynamic_rows / dynamic_cols branches.
- u5: PR 1 lock test test_top_1_bottom_2_fr_default_populates_geometry
renamed to test_top_1_bottom_2_dynamic_2d_populates_geometry and
flipped to PR 2 reality (computation="2d_dynamic_aggregated",
dynamic_rows=True, dynamic_cols=True).
Fixtures: 10 build_layout_css (5 presets × {default, override}) +
5 retry_gate *_dynamic_2d.yaml locking the retry gate skip reason
"dynamic_cols (2-D topology) ... IMP-09 lock" for the 5 presets.
Tests: python -m pytest -q tests = 104 passed (Stage 2 baseline
10 RED → GREEN, 0 regressions). Kei archive
(build_containers_type_b / page_structure) untouched —
rg "build_containers_type_b|page_structure" src/phase_z2_pipeline.py
returns 0 hits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
201099e53b |
feat(IMP-09): PR 1 — col-axis solver + per-zone geometry mapper + retry gate
Stage 3 round 4 lock implementation: extend build_layout_css beyond
the horizontal-2-only dynamic path. Every layout_css return now
carries length-locked col-axis keys (widths_px, width_ratios,
dynamic_cols) matching the parsed css_areas grid (R rows, C cols),
so 2-D layouts (T / 2x2 in PR 2) and the unified
_compute_per_zone_geometry mapper can plug in without further
contract churn.
PR 1 scope:
- _parse_css_areas + _parse_fr_string + _compute_per_zone_geometry
(unified — 1-D and 2-D from the same code path)
- compute_zone_layout_cols (vertical-2 weight-only solver)
- _build_fr_default / _build_rows_dynamic / _build_cols_dynamic
(populate widths_px/heights_px on every return path)
- build_layout_css override branch keeps the warn-and-fallthrough
legacy for unsupported presets (PR 2 promotes to strict raise)
- retry gate in _attempt_zone_ratio_retry skips when dynamic_cols=True
or dynamic_rows=False, with explicit retry_skipped_reason
- Step 8 artifact gains zone_widths_px_planned /
zone_col_ratios_planned (top-level) + zone_width_px_planned /
zone_col_ratio_planned (per-zone)
- debug_zones width injection via _compute_per_zone_geometry
(replaces the legacy row-only zip)
Tests: tests/phase_z2/ — 47 new cases (parse / fr-string / cols solver /
per-zone geometry / build_layout_css contract / retry gate +
6 build_layout_css YAML fixtures + 3 retry_gate fixtures).
Verification: python -m pytest -q tests = 89 passed (was 42).
horizontal-2 grid CSS strings (areas/cols/rows) byte-identical to
legacy; only additive col-axis keys are introduced.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
8f6cffc2a7 |
fix(IMP-08): Stage 5 R2 — aligner force-drill on sub-id override targets
Codex #1 (Stage 5) reproduced a smoke regression on the actual checkout : when V4 carries the parent exact key (e.g., `04-2`) AND the drag/drop override targets a sub-id (`primary=04-2-sub-1`), the aligner kept the parent at parent granularity and emit `['04-1', '04-2']`, so the override flag failed with `unknown section_id(s) ['04-2-sub-1']`. Fix : `align_sections_to_v4_granularity` gains an optional `override_target_section_ids` keyword. From each canonical `${parent}-sub-N` target it derives the parent id and adds it to a `force_drill_parents` set. Sections in that set are drilled into sub-sections regardless of whether V4 carries the parent exact key. Top-level override targets (no derived parent) do not trigger force-drill, so backward-compat is preserved for parent-granularity overrides. The call site in `run_phase_z2_mvp1` collects sub-ids from `override_section_assignments` and forwards them to the aligner. Generalization (RULE 0) : - Trigger is the override schema (`X-sub-N`), not a specific MDX / section / frame id. Applies to all 32-frame MDX uniformly. - Decision is deterministic on the override target shape, independent of V4 yaml content. - Default (no override) path is unchanged byte-for-byte. Side fixes (forward-only RULE 1 cleanup, no history rewrite) : - `align_sections_to_v4_granularity` docstring rewritten in English (overwrites the Korean docstring committed in |
||
|
|
5191acad85 |
feat(IMP-08): U2 — aligner canonical sub-id + N-R5 decimal alias guard
align_sections_to_v4_granularity now emits canonical sub-section ids
of the form ${section_id}-sub-${ordinal} (e.g., "04-2-sub-1"), matching
the frontend drag/drop schema. Each drilled sub-section populates
heading_number (decimal "2.1" / integer "1" / None for undecorated)
and v4_alias_keys for legacy V4 keys.
N-R5 decimal-only alias guard : v4_alias_keys is populated only when
heading_number matches re.fullmatch(r"\d+\.\d+", ...). Integer-only
H3 headings (e.g., MDX 05's "### 1", "### 2") and bare H3 headings
produce no alias to avoid sibling-parent V4 collisions (RULE 0
generalization — applies to all 32-frame MDX, not MDX 05-specific).
The drill regex is broadened from r"^###\s+(\d+\.\d+)\s+..." to
r"^###\s+(?:(\d+(?:\.\d+)?)\s+)?(.+?)$" so integer-only and bare H3
headings are now recognised as sub-sections; they previously failed
the regex and were silently kept under the parent section.
Tests : 7 new cases (MdxSection default 4-positional callers, V4 exact
passthrough, decimal drill with alias, integer-only no-alias guard,
bare H3 no-alias, no-H3 passthrough, end-to-end aligner -> resolver
round-trip with legacy V4 alias). 15/15 in test_phase_z2_subsection_schema
+ 14 override + 8 fallback baseline = 37/37 PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
a422d72c0b |
feat(IMP-08): U1 — schema helper + V4 alias resolver (4 lookup sites)
Adds sub-section schema fields (heading_number / v4_alias_keys /
sub_sections) to MdxSection with defaults so existing 4-positional
constructions remain valid. Introduces _resolve_v4_section_key helper
that resolves a V4 mdx_sections key in exact > alias > None order with
no parent/sibling promotion (axis 7 hybrid lock).
Rewires four runtime V4 lookup sites (lookup_v4_match,
lookup_v4_match_with_fallback, lookup_v4_all_judgments,
lookup_v4_candidates) to accept an optional alias_keys kwarg and go
through the resolver. U1 callers pass empty alias lists so behaviour
is byte-identical to the previous exact-match path; U2 will populate
aliases from MDX heading_number metadata.
Closure callers in run_phase_z2 build section_alias_by_id from
MdxSection.v4_alias_keys and forward into lookup_fn /
candidates_lookup_fn / lookup_v4_all_judgments (Step 7-A trace) and
into _select_template_for_overrides single-section selector.
Step 9 candidate report (post-decision diagnostic) is marked with an
inline English exemption comment per N-R6 — runtime selection goes
through _resolve_v4_section_key, the report path stays a direct
dict-shape lookup to avoid debug_zones schema plumbing.
derive_parent_id now recognises canonical ordinal ids
("03-1-sub-2" -> "03-1") first and keeps the legacy decimal fallback
("04-2.1" -> "04-2") for V4 alias compatibility.
Tests : 8 synthetic cases in tests/test_phase_z2_subsection_schema.py
covering derive_parent_id ordinal/decimal/none and the resolver
exact/alias/no-promote/miss cases. 30/30 PASS combined with the 14
override + 8 fallback baseline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
52ccb7fc8b |
fix(IMP-06): Stage 4 blocker-fix — render_records + plan-aware traces
Three Codex #13 blockers in a single coherent commit. Blocker 1 (units None hazard) — drop None placeholders from `units` list. Replace with a separate `render_records` layer built AFTER frame_overrides apply. units = canonical renderable list (list[CompositionUnit] only); render_records = canonical per-position view including empty / collision- skipped / cli_override entries. Downstream loops (Step 6 print, frame_ overrides, zones_data/debug_zones, Step 9 application_plan, compute_slide_ status covered loop) no longer need None guards. Blocker 2 (no integration test) — add end-to-end pipeline integration test: `--override-section-assignment top=03-2` on sample 03 MDX produces zones_data[top].source_section_ids = ['03-2'], debug_zones[top].assignment _source = 'cli_override', debug_zones[bottom].v4_template_id = '__empty__' (override_collision whole-skip), step20 filtered_section_ids contains '03-1', and filtered_section_reasons carries a section_assignment_override _uncovered entry. Proves the render path — not only comp_debug — reflects the CLI override. Blocker 3 (Step 9/20 not plan-aware) — surface plan-aware additive fields in both render-path debug_zones/zones_data and Step 9 application_plan units: position, assignment_source, section_assignment_override, replaced_auto_unit, skipped_collided_auto_units, uncovered_section_ids, skipped_reason. compute_slide_status appends Codex #10 Catch O list-shaped filtered_section_reasons entries for override-uncovered sections and folds them into filtered_section_ids so full_coverage is re-evaluated post-override. Exact-id-only collision semantics enforced (Codex #14/#15/#16/#17): S3 and S3-1 are distinct ids; no prefix hierarchy, no parent cascade. Three new section-id invariant tests added (parent-like vs child-like, exact duplicate collision detected, distinct ids coexist). Test : 24 pytest pass (9 helper + 9 case + 3 invariant + 1 case 9b + 1 integration + 1 from v4_fallback baseline) ; smoke 11/11 PASS. Register `integration` pytest marker in pyproject.toml. |
||
|
|
b81e564f65 |
feat(IMP-06): Stage 4 part 1 — replaced_auto_unit field + comment fix
Refs #6 Stage 4 split per Codex #10 acceptance: this commit lands the schema + trace refinements required before the render-path rewiring. The actual units/zones_data/Step 9/Step 20 plan-driven materialization remains in Stage 4 part 2 (follow-up commit) so each commit is reviewable on its own and regression-safe. - _build_position_assignment_plan: add replaced_auto_unit field. Populated only when the explicitly overridden position already held an auto unit AND that auto unit had different source_section_ids than the override. Documents a same-position override replacement as a distinct audit fact, separate from skipped_collided_auto_units which captures cross-position whole-skips per the locked collision policy. - Backfill replaced_auto_unit = None on the empty/collision/auto branches for schema-stable consumers. - Update the override-application comment near the helper invocation so it no longer claims the helper "reorders units"; Stage 4 part 2 will be the commit that wires the plan into the actual render path. - Helper unit tests: assert replaced_auto_unit shape in the collision scenario and add a dedicated case that distinguishes same-sections (template swap via --override-frame -> None) from different-sections same-position replacement (populated, reason="same_position_override_replacement"). No AI, no calculate_fit, no full planner rerun, no frontend, no sample hardcoding. plan_composition() signature preserved. helper remains pure. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d596fabde0 |
feat(IMP-06): zone-section assignment override CLI + plan helper (trace-only)
Refs #6 Backend / CLI / composition path only — frontend bridge remains #38. - Add `--override-section-assignment ZONE_ID=section_id[,section_id]` to the Phase Z entry parser. Parse-time hard errors for malformed payloads, empty zone id, empty section list, duplicate zone id, and duplicate section across zones (a section may belong to at most one zone). - Add `_build_position_assignment_plan` helper (pure function, resolved `positions` injected). Builds a per-position assignment plan with the Codex-locked template_id ladder: (1) `--override-frame` exact unit_id wins, (2) exact existing auto unit reuse, (3) single-section direct-executable V4 selector via `lookup_v4_match_with_fallback(..., raw_content=section.raw_content)`, (4) ad-hoc multi-section override without exact auto + without explicit override-frame yields `skipped_reason='ad_hoc_merged_no_template'`. - Lock the collision policy: explicit override wins per position, sections appear in at most one position, overlapping auto units are skipped whole (no split, no cascade, no replan), uncovered sections from the previous same-position auto unit are recorded in `uncovered_section_ids`. - Additive trace fields on each plan entry: `previous_source_section_ids`, `skipped_collided_auto_units`, `uncovered_section_ids`, `v4_selector_trace`, `section_assignment_override`. Top-level `comp_debug["section_assignment_plan"]` + `comp_debug["section_assignment_summary"]` so Step 9 / debug artifacts can derive from a single source of truth. - Wire `run_phase_z2_mvp1(override_section_assignments=...)` after final layout preset resolution: validate ZONE_IDs against active layout positions and validate section_ids against aligned sections (fail-fast). The plan is attached to `comp_debug` for downstream artifacts. Actual `zones_data` / unit-list rewiring is deferred to a follow-up commit so this change stays regression-safe; trace artifacts already surface override intent and collision impact. - Add 9 helper unit tests with fully synthetic MOCK_ ids (no real catalog / no v4_full32_result.yaml): non-conflicting auto retention, collision whole-skip + uncovered tracing, template ladder steps 1/2/4, unit_id naming convention, previous_source_section_ids position history, empty-position case, summary aggregation invariants. No AI, no `calculate_fit`, no full planner rerun, no frontend, no sample hardcoding, no `restructure`/`reject` silent promotion. `plan_composition()` signature is preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
23d1b25144 |
test(IMP-05): tighten Step 9 candidate evidence guard
Refs #5 Replace the hand-built Case 7 payload assertion with a temporary production-source guard. The test now fails if Step 9 stops emitting candidate_evidence, breaks the fallback_chain compat alias, or removes the alias intent comment. This is intentionally temporary because Step 9 application-plan unit assembly is inline. Follow-up IMP-32 should extract a helper and replace this source-string guard with a direct helper test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
21476ae000 |
fix(IMP-05): complete V4 fallback evidence and dedup qualifiers
Refs #5 - Add runtime template_id dedup in lookup_v4_match_with_fallback with first-occurrence reservation; duplicate ranks become audit evidence, not new fallback candidates. - Add Step 9 candidate_evidence as the primary per-unit evidence field while keeping fallback_chain as a compat alias for legacy readers. - Add Step 20 fallback_selection_count and selection_paths derived from comp_debug.v4_fallback_summary with defensive defaults; top-level overall enum unchanged. - Tighten synthetic fallback tests for duplicate handling (rank-1 reject A + rank-2 use_as_is A + rank-3 distinct B → rank-3 wins) and add tests for candidate_evidence + alias equality and Step 20 qualifier presence with defensive defaults. - Verify with pytest (10 passed) and smoke_frame_render --self-check (11/11 partials, IMP-04 F17 calibration intact). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
15c5b9ae00 |
IMP-05 deterministic V4 candidate bridge — pre-render rank-2/3 fallback + trace schema + dedup invariant test
round 55~73 review-loop lock per Codex #11 final + Claude #13 6-axis L1~L9. Scope (deterministic only) : - pre-render rank-2/3 fallback via lookup_v4_match_with_fallback (selector only, no calculate_fit migration, no AI, no full planner rerun, no layout topology change, no abort behavior change) - Step 9 informative candidate_evidence schema (additive) — v4_label / phase_z_status / catalog_registered / filtered_for_direct_execution / route_hint / decision / reason - Step 20 qualifier fields (additive) — fallback_used / fallback_selection_count / selection_paths[] — top-level enum unchanged - restructure / reject candidates preserved as non-direct evidence with route hints (design_reference_only / ai_adaptation_required) — deferred actual handlers IMP-29/IMP-31 - catalog 1:1 invariant test (separate file tests/test_catalog_invariant.py) — fails fast if template_id/frame_id 1:1 mapping ever breaks - 6 behavior tests fully synthetic with MOCK_ prefix (no real catalog IDs, no v4_full32_result.yaml dependency) — monkeypatch get_contract + compute_capacity_fit (selector has no DI, function signature unchanged) Deferred to follow-up issues : - IMP-30 first-render invariant + abort bypass (zero-unit + section status filter) - IMP-29 frontend zone-level override (deterministic only) - IMP-31 AI-assisted frame-aware adaptation Guardrails locked : no calculate_fit / no AI / no frontend / no full rerun / no layout topology / no abort behavior change / no 1-2 sample hardcoding. Tests : 8/8 pass (6 selector behavior + 2 catalog invariant). Smoke regression : 11/11 partials pass (IMP-04 F17 calibration intact). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
85c680f02a |
docs + V4 catalog + samples + Phase Q legacy 보존
전체 26 files (20 추가 + 6 수정), 10507 insertions. Phase Z 문서 : - docs/architecture/PHASE-Z-CHANGE-LOG.md (신설) — axis-by-axis 의사결정 history (newest-on-top). Step 7-A 부터 6 entry 박힘 + 2026-05-08 / 2026-05-08 #2 (compat 매트릭스 폐기 / 6-B 폐기 / F14 표현 정정 / label gate policy 분리). - docs/architecture/PHASE-Z-PIPELINE-OVERVIEW.md (수정) — Step 5/6/9 Gap note append (구조 무변, append-only). 6-B 폐기 사실 + Refinement F. - docs/architecture/PHASE-Z-PIPELINE-STATUS-BOARD.md (수정) — snapshot date 2026-05-08 갱신. §3 핵심 missing item 5 (Step 5/6/9 boundary axis breakdown + 폐기 기록). §6 한 줄 갱신 — 다음 axis 후보 A~F. Project root docs : - PLAN.md / PROGRESS.md / README.md (수정) — 토큰 체계 / 폴더 구조 / 설계 문서 / 역할 분리 반영. - IMPROVEMENT-REDESIGN.md (신설) — Phase Z 설계 핵심 문서. - PROCESS_OVERVIEW.html (신설) — 파이프라인 개요 시각. - docs/tasks/* (신설) — Phase Z task 문서. V4 catalog (Phase Z runtime 필수 의존성) : - tests/matching/v4_full32_result.yaml (신설, 4888 줄) — V4 매칭 결과 32 frame × 10 MDX section. lookup_v4_match() / lookup_v4_candidates() 가 본 파일 read. Phase Z runtime 이 *없으면 즉시 abort* — clone 후 즉시 동작 가능 보장. Samples : - samples/mdx_batch/04.mdx (신설) — MDX04 기본 sample. - samples/mdx/04. DX 지연 요인.mdx (신설) — MDX04 원본. Phase Q legacy 보존 (별 axis "Phase Q audit & salvage" 영역) : - src/block_matcher_tfidf.py / catalog_blocks.py / frame_extractor.py / pipeline_v2.py — Phase Q (옛 파이프라인) src 신규 untracked 파일들. Phase Z runtime 와 의존성 0. Phase Q audit axis 에서 검토 예정. - scripts/eval_block_matcher.py / fetch_all_frame_screenshots.py / match_17_units_my_matcher.py / match_mdx_strict.py / match_mdx_to_frames_tfidf.py / ocr_augment_texts.py / run_pipeline_v2.py / previews/ — Phase Q 작업 시 사용한 옛 script. 같이 보존. - run_mdx03_pipeline.py (수정) — Phase Q 진입점 (no flag) + Phase Z 진입점 (--phase-z2 flag) 동시 wrapper. Phase Z 만 사용 시 `python -m src.phase_z2_pipeline samples/mdx_batch/03.mdx <run_id>` 직접 호출. 비-scope : - tests/matching/ (v4_full32_result.yaml 외 ~63MB) — V4 진화 history / reports / DECK / ATTACH. Phase Q audit axis 에서 검토. - tests/pipeline/ (~15MB) — pipeline data. Phase Q audit 영역. - templates/catalog/blocks.yaml — 옛 block catalog. Phase Q audit. - templates/phase_z2/frames/ — 옛 frame partial 위치. Phase Q audit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
c42e65fc7e |
Initial commit: Kei Design Agent
콘텐츠를 시각적으로 구조화된 슬라이드 HTML로 변환하는 독립 에이전트. 아키텍처 (4단계 파이프라인): 1. Kei 실장 (Opus) — 콘텐츠 유형 분류 + 블록 배치 2. 디자인 팀장 (Sonnet) — 레이아웃 컨셉 (블록 배치 + 페이지 수) 3. 텍스트 편집자 (Sonnet) — 슬롯 텍스트 정리 (핵심 유지) 4. CSS Grid 렌더러 — HTML 조립 블록 템플릿 7종: comparison, card-grid, relationship, process, quote-block, conclusion-bar, comparison-table 기술 스택: FastAPI + Anthropic API + Jinja2 + CSS Grid Pretendard Variable 한국어 폰트 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |