6aa7564509040c66b5cd5e0f32036779128dce28
41
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b1bbe27c38 |
feat(#89): IMP-89 89-a u1~u5 Layer A render path activation (B4→mapper source-of-truth switch, default-OFF flag)
PHASE_Z_B4_MAPPER_SOURCE env flag (default OFF) switches slot_payload source-of-truth from legacy mapper-only / V4 rank-1 to B4 PlacementPlan .selected_template_id at the single switch site in the runtime loop. OFF preserves final.html SHA byte-equivalence (u4 parity guard, mdx 01-05). ON requires Layer A render-active path; BLOCKED exits on B4 no-cover and on B4-selected FitError (IMP-87 honesty gate pattern — NO silent fallback). Distinct from PHASE_Z_B4_GATEKEEPER (mismatch render-skip). Units (1 commit = 1 axis per Stage 1 scope_lock): u1 — _b4_mapper_source_enabled() flag reader (default OFF) u2 — _select_mapper_template_id() selector wired at the switch site u3 — _b4_mapper_source_blocked_exit() for b4_no_cover / b4_selected_fit_error u4 — render SHA parity regression (tests/regression/ baseline mdx 01-05) u5 — slot_payload byte-equivalence (matches_mapper=True axis, mdx 01-05) Targeted 89-a suite 63 PASS; Phase Z regression 323 PASS; IMP-87 mirror 20 PASS. Demo activation via .env only (no vite.config hardcoding). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
896f273ffa |
feat(#92): IMP-92 u1~u5 AI fallback config validation (model ping + operational error classification)
Replaces #84 UI-noise removal plan with positive operational-alert contract. Five-axis stack lands together: (1) default model literal moved to current Opus-family ID, (2) Anthropic SDK error classifier mapping exceptions to quota/billing/auth/other, (3) api_error_kind plumbed through ai_repair_status summary + per-record retention, (4) Step 0 preflight ping gated under ai_fallback_enabled (default OFF preserved) with fail-fast on invalid model/key, (5) frontend formatter rewritten to surface only operational quota/billing/auth toasts (non-operational paths return null per feedback_auto_pipeline_first silent-pipeline policy). u1 - default model literal claude-opus-4-6-20250415 -> claude-opus-4-7 (src/config.py + tests/test_phase_z2_ai_fallback_config.py lock mirror) u2 - classify_operational_error type+status_code dispatch + Step 12 api_error_kind stamp on except path (src/phase_z2_ai_fallback/client.py + src/phase_z2_ai_fallback/step12.py + tests/phase_z2_ai_fallback/test_step12.py) u3 - _summarize_ai_repair_status aggregates api_error_kinds {quota,billing, auth,other}; error_records[i].api_error_kind retained per-record (src/phase_z2_pipeline.py + tests/test_imp47b_failure_surface.py) u4 - _run_step0_ai_preflight + Step0PreflightError; preflight only fires when ai_fallback_enabled=true; one-token ping; invalid key/model => setup failure before Step 1 (src/phase_z2_pipeline.py + tests/phase_z2/test_pipeline_step0_preflight.py NEW) u5 - AiRepairStatus.api_error_kinds? interface + formatAiRepairHumanReview Message rewritten: operational quota/billing/auth -> Korean copy verbatim from issue body (tie-break quota -> billing -> auth); validation/coverage_violated/unsupported_kind/generic-other/legacy payload -> null (Front/client/src/services/designAgentApi.ts + Front/client/tests/imp47b_human_review_toast.test.tsx) Guardrails respected: - feedback_demo_env_toggle_policy: default OFF preserved; preflight skipped when ai_fallback_enabled=false (test_preflight_skipped_when_disabled asserts anthropic.Anthropic() not called). - feedback_auto_pipeline_first: non-operational AI failures stay silent; only quota/billing/auth reach user toast. - feedback_ai_isolation_contract: AI remains fallback-only; no normal-path migration; MDX preserved. - project_imp46_carveout_caveat: cache_key/fingerprints fields untouched on every record; no overlap with #62 cache region. - feedback_no_hardcoding: zero MDX-sample-specific literals; classifier dispatch by SDK type, not by string parsing. - feedback_artifact_status_naming: operational toast scoped to alert axis, not overall PASS signal. Tests: - Targeted u1+u2+u3+u4: 63 passed - u5 vitest (Front/): 10/10 passed - tests/phase_z2_ai_fallback dir regression: 240 passed - tests/phase_z2 dir regression: 323 passed - IMP-92-adjacent (-k "imp47b or ai_fallback or preflight or step12 or step0"): 299 passed (808 deselected) - u1 baseline lock (test_client_mock.py): 8 passed Zero failures, zero regressions outside scope. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
842a46144c |
feat(#87): IMP-87 u1~u5 empty_shell honesty gate + BLOCKED exit
EMPTY_SHELL_NO_CONTENT overall enum + 3-marker detection (frame_template_id="__empty__" OR label="empty_shell" OR merge_type="empty_shell") routes empty-placeholder-only slides to BLOCKED CLI exit 1 + red final_status.html, blocking fake PASS reports (feedback_artifact_status_naming). Coverage accounting split: legacy covered_section_ids preserved + new content_rendered_section_ids / empty_shell_section_ids. mdx05 Case B (zero V4 evidence) honestly classified instead of synthesizing fabricated rank-1 reject frames. IMP-30 u6/u7 stale empty-shell PASS assertions inverted (29 tests). IMP-85 smoke parametrize: mdx05 removed from exit-0 list + dedicated BLOCKED exit test added (4 tests). No production behavior change for chain_exhausted Case A; no AI route activation; no mdx-id hardcoding. 53 targeted + 76 adjacent Phase Z tests PASS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
c53722ad0b |
feat(#86): IMP-86 u1~u5 placeholder zones_data + invariant guard
Mapper FitError handler now appends a __empty__ placeholder to zones_data and a matching debug_zone so the surviving cardinality stays in sync with the active layout preset's grid rows. A pre-build_layout_css invariant guard fails fast with preset/positions/count diagnostics if drift recurs. Per-record telemetry (adapter_needed, mapper_fit_error, provisional) is exposed on both placeholder records; authoritative slide_status.adapter_ needed_units schema is unchanged. Closes mdx03 reject override regression: Step 12 AI router now reachable without heights_px ValueError; default-path behavior unaffected. u1 — FitError placeholder zones_data + debug_zone (src/phase_z2_pipeline.py) u2 — pre-build_layout_css invariant guard (src/phase_z2_pipeline.py) u3 — horizontal-2 normal+placeholder helper unit (test_compute_per_zone_geometry.py) u4 — mdx03 reject override → Step 12 integration + default regression u5 — placeholder telemetry surface (adapter_needed/mapper_fit_error/provisional) Tests: - u3 helper: 7 passed (0.06s) - u4+u5 integration: 2 passed (7.87s) - Phase Z2 + AI fallback regression: 544 passed (66.28s) - Broader sweep (excl. matching/pipeline heavy): 1066 passed (96.12s) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
cacc5b30db |
feat(#85): IMP catalog builder invariant + VP runtime gate (u1~u7)
- u1: BuilderMissingError(FitError) — narrow exception aligned with pipeline catch - u2: load_frame_contracts catalog invariant + VP skip + CatalogInvariantError - u3a: audit CLI I1~I3 (partial existence / declared builder / registry membership) - u3b: audit CLI I4 (slot_payload refs vs declared/generated payload keys) - u4: lookup_v4_candidates VP filter (lookup_v4_all_judgments raw telemetry untouched) - u5: catalog invariant regression coverage + temp non-VP failure fixtures - u6: mdx04 VP routing fixture tests (sw_dependency_four_problems excluded from live) - u7: tests/conftest.py env isolation + mdx03/mdx04/mdx05 subprocess smoke Targeted 74 PASS (12.31s). Full regression 1063 PASS (87.70s). Audit CLI clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f3ef4d917c |
feat(#64): IMP-35 details_popup_escalation u1~u10 + Stage 3 R7 anchor re-pin
Land the production + test surface for the Step 17 cascade POPUP terminal (DETERMINISTIC -> POPUP -> AI_REPAIR -> USER_OVERRIDE) per Stage 2 plan R2. u11 (baseline-red invariance gate) was already landed in |
||
|
|
6f1c7367e0 | feat(#79): IMP-51 image_overrides axis (u1~u11 backend stamp+CLI+CSS inject + frontend drag/resize+persistence + tests) | ||
|
|
9388e25e76 |
feat(#80): IMP-52 user_overrides.json persistence (u1~u10 backend + frontend + tests)
4-axis MDX-stem keyed persistence so layout / zone_geometries / zone_sections / frames
survive across `/api/run` sessions. Auto-restore on MDX reopen; CLI > file precedence
on backend pipeline entry; 300ms-debounced PUT flushed before Generate.
u1 src/user_overrides_io.py — load/save/validate_key (MDX-stem regex), 4-axis schema,
miss={}, corrupt warning+{}, atomic tmp+rename, foreign-key preserve.
u2 src/phase_z2_pipeline.py — post-argparse fallback fills only missing axes.
u3 Front/vite.config.ts — GET /api/user-overrides/:key (200 {} on miss, 400 traversal).
u4 Front/vite.config.ts — PUT /api/user-overrides/:key, 4-axis allowlist, partial merge.
u5 Front/client/src/services/userOverridesApi.ts — typed get/save + flushUserOverrides
with 300ms debounce and mutated-axis partial payloads.
u6 Front/client/src/pages/Home.tsx + slidePlanUtils.ts — restore on MDX upload (non-frame
axes immediately, frames remapped post-loadRun unit_id → region.id).
u7 Home.tsx — persist on 4 mutation handlers (section drop, layout select, zone resize,
frame select); zone_sizes and Generate excluded.
u8 tests/test_user_overrides_io.py — round-trip, unknown-key passthrough, missing/corrupt,
invalid keys (26 tests).
u9 tests/test_user_overrides_pipeline_fallback.py — per-axis fill, CLI-wins, no-file noop,
corrupt warning+skip (16 tests).
u10 Home.tsx + user_overrides_write.test.ts — await flushUserOverrides() before runPipeline
in handleGenerate try-block head; source-pattern regression assertions (20 → 22 tests).
Backend pytest 42/42 green. Frontend vitest 113/113 green (endpoint 42 / restore 21 /
service 28 / write 22). HEAD baseline ee97f4f; no spillover to phase_z2 templates /
families / frames / pipeline orchestration outside the IMP-52 surface.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
ee97f4fc78 |
feat(#77): IMP-48 composition planner re-split on all-reject (u1~u9)
Add resplit_all_reject_merges() helper in phase_z2_composition.py that
detects parent_merged / parent_merged_inferred units with label=reject
and rebuilds them as per-section single units using each section's own
rank-1 V4 evidence (no frame swap, MDX raw_content preserved).
Pipeline hook fires once after Step 6 settling chain (u12/u4/empty-shell)
and section_assignment_plan resolution, before Step 6 artifact write.
Guards: beneficial-split rule (>=1 non-reject), coverage equality, layout
cap (>4 abort), max_retry=1, section_assignment_override short-circuit.
Audit: comp_debug["imp48_resplit"] additive payload (applied, split_units,
skipped_units, post_split_unit_count, post_split_layout_preset);
selection_path="resplit_from_merge" telemetry on rebuilt singles;
layout_preset re-derived via select_layout_preset(new_units).
Tests: 39/39 PASS (composition u1~u6: 14 cases; pipeline u7~u9: 25 cases).
Scoped regression 720/6 with 6 failures isolated as pre-existing on
baseline
|
||
|
|
1186ad8ae2 |
feat(#76): IMP-47B reject-as-AI-adaptation activation (u1~u13 backend + tests)
- u1~u9: AI fallback infrastructure (router/prompts/schema/validator) + Step 12 hook - u10: e2e reject chain (writes final.html with AI-repaired slot, full coverage) - u11: frontend wiring deferred to follow-up commit (split from IMP-41 hunks) - u12: coverage_invariant guard - u13: cache save gate (visual_check PASS + user_approved/auto_cache) — Codex #22 verified Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
90503cadd6 |
feat(#67): IMP-38 V4 max_rank policy formalization (u1~u3, 4 round consensus)
- u1: separate templates/phase_z2/catalog/v4_fallback_policy.yaml + load_v4_fallback_policy() loader (catalog pollution prevention — Codex #1 correction) - u2: dynamic effective max_rank in lookup_v4_match_with_fallback (3-variable ceiling min, Codex #2 correction: min(configured, len(judgments_full32))) + 3-tier usable predicate (status + catalog + optional capacity) + trace 8 fields (requested/default/configured_extended/ judgments_count/effective_extended_ceiling/effective_max_rank/usable_count/policy_applied) - u3: 2 production call site cleanup (max_rank=3 removed, HEAD baseline) + tracked Front/vite.config.ts PHASE_Z_MAX_RANK env retired + 4 regression scenarios verified: 32 passed (IMP-38 focused scope) — IMP-05 L4 dedup / L2 schema preserved, IMP-30 allow_provisional byte-identical, caller_override backward compat (tests) Stage cycle (#67, 7 round Claude + 5 round Codex): - Stage 1: Claude #1 -> Codex #1 YES + 5 corrections - Stage 2 r1+r2: Claude #2-#4 -> Codex #2 Q2 -> Codex #3 YES (4 round consensus LOCK 23195) - Stage 3 U1+U2+U3: Claude #5-#9 -> Codex #6 NO 4to3 correction -> Codex #7 YES -> Codex #8 YES - Stage 4: Claude #11 -> Codex #9 (anchor attribution nuance) -> Codex #10 readiness -> Codex #11 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
c412f1ea75 |
refactor(#41): IMP-32 Step 9 application_plan helper extraction (u1~u5)
Pure refactor — extract inline Step 9 per-unit application_plan dict
assembly into module-level private helpers for testability. Replaces
IMP-05 Case 7 inspect.getsource() literal guard with direct helper-call
shape test. Behavior preserved: key set/order, candidate_evidence +
fallback_chain compat alias identity, IMP-06 additive plan fields,
IMP-11 D-2 markers (single _contract = get_contract(c.template_id)
bind + catalog_registered + min_height_px chain).
- u1 _application_candidates_for_unit(unit) at src/phase_z2_pipeline.py
:2829-2853 — APPLICATION_MODE_BY_V4_LABEL mapping (pure extraction)
- u2 _v4_all_judgments_for_unit(v4_all_for_unit) at :2855-2882 —
IMP-11 D-2 chain preserved literally
- u3 _build_application_plan_unit(unit, zone_plan, selection_trace,
plan_record, v4_all_for_unit, layout_preset, layout_candidates_list)
at :2885-2995 — byte-identical per-unit dict (key set + order +
value identity), candidate_evidence / fallback_chain compat alias,
v4_candidates list, v4_all_judgments, application_candidates, IMP-06
additive plan fields
- u4 Step 9 inline loop body at :4620-4658 replaced with helper call;
per-index/per-id lookups (zone_region_plans[i], v4_fallback_traces
.get(...), plan_record_by_unit_id.get(id(unit)), section_alias_by_id,
lookup_v4_all_judgments(...)) stay at call-site
- u5 tests/test_phase_z2_v4_fallback.py Case 7 rewritten to
test_build_application_plan_unit_emits_candidate_evidence_and_alias
— direct helper call with SimpleNamespace duck-typed input; asserts
candidate_evidence list identity (is), fallback_chain compat-alias
identity (is), key order (candidate_evidence before fallback_chain),
and compat-alias comment scoped to inspect.getsource(_build_
application_plan_unit)
Verification: targeted 22 passed, full pytest 408 passed (0 fail/skip),
smoke 11/11 PASS (2 pre-existing baseline SKIPs unchanged).
Cross-ref: IMP-05 (#5) commit
|
||
|
|
1efbf672bd |
feat(#39): IMP-30 first-render invariant + abort bypass (2 paths)
Restore first-render invariant: final.html + Step 20 slide_status MUST be written for every input where Step 0~5 succeed. Two abort paths replaced with provisional/empty-shell synthesis; MDX content preserved, AI-free. - u1 V4Match.provisional + lookup_v4_match_with_fallback(allow_provisional) chain_exhausted -> synthesize rank-1 provisional (opt-in, default-off) - u2 CompositionUnit.provisional propagation (single / parent_merged / parent_merged_inferred constructors) - u3 select_composition_units(allow_provisional_fill=True) last-resort fill + _candidate_state="selected_provisional" - u4 pipeline.py path-(a) abort guard replaced with provisional retry + terminal __empty__ shell (no sys.exit(1)) - u5 zones_data.provisional -> slide_base.html zone--provisional class + data-provisional + needs-adaptation badge (template-only) - u6 compute_slide_status additive provisional_first_render_count/_units (overall enum unchanged per IMP-05 Codex #10 D4) - u7 regression: tests/test_phase_z2_imp30_first_render.py (28 tests) + tests/test_phase_z2_v4_fallback.py (+5 cases) Guardrails verified: MVP1_ALLOWED_STATUSES unchanged, no calculate_fit, no LLM in fallback path, no MDX 03/04/05 hardcoding. Anchor sync (Rule 13): tests/orchestrator_unit/test_imp17_comment_anchor.py re-pinned 564/565 -> 570/571 to track V4Match.provisional shift at src/phase_z2_pipeline.py:179-184. Cross-ref: IMP-05 (#5) §5 defer + Codex #2 first-render invariant. |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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
|
||
|
|
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. |
||
|
|
1f15495117 |
feat(IMP-06): Stage 4 Part 2 — render-path integration (units rebuild + empty zone + Catch K fix)
Refs #6 After `position_assignment_plan` is built, rebuild the `units` list to be plan-aligned so downstream `zones_data` / `debug_zones` / mapper / render all see the post-override sequence. This resolves the long-standing trace-only gap and closes Codex Catch K naturally because the helper now actually drives downstream materialization. - run_phase_z2_mvp1: after `_build_position_assignment_plan`, rebuild `units` ordered by `position_assignment_plan`. `cli_override` entries synthesize a CompositionUnit (resolved template_id + concatenated section raw_content + contract frame_id + selection_path="cli_override" + override audit in rationale). `auto` entries reuse the original planner unit. empty/collision-skipped entries become None placeholders so the downstream zone loop can emit an explicit empty zone record without distorting layout allocation. - zones_data / debug_zones loop: handle `unit is None` by appending an explicit empty record with template_id="__empty__", content_weight=0, min_height_px=0, plus the plan's skipped_reason / replaced_auto_unit / skipped_collided_auto_units / uncovered_section_ids audit fields. - partial render loop: `template_id == "__empty__"` short-circuits to `partial_html = ""` so the slide_base zones loop preserves grid identity without raising TemplateNotFound. - Update the helper-invocation comment so it now describes the actual Part 2 behavior (units rebuild + empty zone handling). Catch K is no longer a future-tense placeholder. Stage 4 Part 3 (follow-up commit) will add: Step 9 application_plan plan-aware additive fields, Step 20 list-shaped filtered_section_reasons entries for override-uncovered sections (Codex #10 Catch O schema), and integration tests proving zones_data["top"] actually contains the overridden section ids when --override-section-assignment is supplied. Regression: 20/20 unit tests pass (9 IMP-06 helper + 8 IMP-05 fallback + 2 catalog invariant + 1 dedicated replaced_auto_unit test), smoke self-check 11/11 (IMP-04 F17 calibration intact). No AI, no calculate_fit, no full planner rerun, no frontend, no sample hardcoding. plan_composition() signature preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
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> |
||
|
|
fc3f7d8826 |
feat(step2+step3): slide-level rich ContentObject trace (IMP-03 #3)
- Add extract_rich_content_objects(normalized_assets, mdx_id) in
phase_z2_content_extractor.py emitting slide-level rich ContentObjects
for SPEC v1 §1.2 types: details (popups), image, table
- Extend ContentObject dataclass with optional scope/mdx_id/section_id
metadata fields (additive, default None — v0 unchanged)
- _stage0_chained_adapter() returns 5-tuple adding normalized_assets
({popups, images, tables}); empty on env=OFF / hard fallback
- Step 2 artifact gains additive stage0_normalized_assets nested field
(env=OFF / fallback → empty lists). Existing 7 fields preserved.
- Step 3 emits root-level rich_content_objects once at slide scope
with rich_content_objects_enabled / scope / source / disabled_reason /
skips / invariant_warnings. per_zone list still references v0 only.
- PHASE_Z_STEP3_RICH_OBJECTS_ENABLED env flag, default OFF (canary,
matches PHASE_Z_STAGE0_ADAPTER_ENABLED / PHASE_Z_B4_*). Enable
requires flag=1 AND non-empty normalized_assets; otherwise records
disabled_reason = FLAG_OFF or NO_NORMALIZED_ASSETS.
- transform_table dedup: arrow glyph detection in normalized table
rows/headers → skip with reason=skipped_transform_table_duplicate.
v0 _capture_3col_transform_table remains the sole transform_table
source; generic table only for non-transform tables.
- ID pattern {mdx_id}.{details,image,table}-N (slide-level namespace).
- plan_placement() input unchanged (v0 content_objects only) — rich
list never feeds placement/region planning in this issue.
- self-test extended with 5 rich extractor cases (popup/image/table
/arrow-skip/empty); v0 self-test unchanged and still PASS.
- mapper / V4 / composition / Step 6+ / AI/Kei / pipeline_path_connected
unchanged. trace fidelity only.
env OFF + rich OFF: legacy PASS, no regression
env OFF + rich=1 : disabled_reason=NO_NORMALIZED_ASSETS, rich list empty
env=1 + rich=1 : Step 2 stage0_normalized_assets populated (1 table on
MDX 03, invariant match adapter_counts). Step 3 write
blocked by inherited IMP-02 composition_planner abort
(downstream gap, not IMP-03 scope).
Refs Gitea #3 (IMP-03 A-1 popup/image/table trace)
|
||
|
|
bac13c09c4 |
feat(step2): chained adapter for Stage 0 normalize (IMP-02 #2)
- Add _stage0_chained_adapter() helper near parse_mdx() chaining
mdx_normalizer.normalize_mdx_content + section_parser.extract_major_sections
+ section_parser.extract_conclusion_text → reconstructed MdxSection list
- PHASE_Z_STAGE0_ADAPTER_ENABLED env flag, default OFF (canary, matches
PHASE_Z_B4_SOURCE_SHAPE_ENABLED / PHASE_Z_B4_GATEKEEPER pattern)
- Section ID reconstruction priority: raw_heading (pre-scan raw MDX
`## N. Title` heading → title→num map lookup) → raw_heading_inline
→ order_fallback. All paths logged in id_reconstruction_log
- 5 hard fallback enums: ADAPTER_EXCEPTION / NO_USABLE_SECTIONS /
MISSING_INVALID_IDS / DUPLICATE_IDS / NON_POSITIVE_SECTION_NUM
- Additive step02_normalized.json field stage0_adapter_diagnostics
(enabled / used / fallback_reason / id_reconstruction_log /
adapter_counts / diff_vs_legacy / legacy_counts)
- Preserve Step 2 existing 7 fields (slide_title / slide_footer /
sections_count / sections / orphans / details) — additive only
- Defer mdx_normalizer / section_parser imports so legacy default-OFF
path does not depend on those modules
- V4 / align_sections_to_v4_granularity / composition / AI/Kei /
frame selection / status semantics unchanged
env OFF: legacy path PASS on MDX 03, no regression
env=1 : adapter path activates, IDs 03-1/03-2, raw_heading reuse
triggered, downstream composition_planner abort surfaces
as canary finding (out of scope for IMP-02)
Refs Gitea #2 (IMP-02 A-1 Stage 0 normalize chained adapter)
|
||
|
|
1dc81e0692 |
feat(step14+step21): add zone_geometries_px artifact (IMP-01 #1)
- Add slide-relative bbox export of .zone elements via getBoundingClientRect
- Inline Selenium JS collects zone_geometries_px = [{position, template_id, x, y, w, h}]
- write_debug_json adds top-level additive zone_geometries_px field
- Existing visual_runtime_check / zones / frame_slot_metrics / PASS/FAIL logic unchanged
- AI/Kei/V4/frame selection paths not touched
Refs Gitea #1 (IMP-01 A-6 Zone DOM 좌표 export)
|
||
|
|
b56fd20ae5 |
feat: add Phase Z override CLI and trace support
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ec83405770 |
phase z pipeline: Step 5 / 6-A / 7-conn / 8-conn / Step 9 v0 axis 박힘
사용자 lock 2026-05-08 — Step 5/6/9 boundary reframe.
V4 가 frame 선택, Step 6 은 V4 rank-1 default 전사, Step 9 는 application_plan
번역. compat 매트릭스 안 폐기.
src/phase_z2_pipeline.py 변경 :
- lookup_v4_candidates(v4, section_id, max_n=6) 추가 — V4 non-reject max-6
후보 list. raw 32 entry 는 v4_full32_result.yaml 영속, step05 = 정제 list.
lookup_v4_match() (rank-1) 유지 — Step 6 backward compat. (Step 5 보완)
- step05_v4_evidence.json schema 확장 — evidence_per_section[i] =
{section_id, v4_candidates: [...], candidate_status: "ok" |
"no_non_reject_v4_candidate"}. (Step 5 보완)
- candidates_lookup_fn 정의 + plan_composition() 에 주입.
V4 raw dict 는 composition module 안 봄 — fn injection. (Step 6-A)
- Step 6 artifact 의 selected_units[i] 에 v4_candidates 필드 추가. (Step 6-A)
- step07_layout.json data 에 unit_count + layout_candidates 필드 추가
(Step 7-B 의 select_layout_candidates 결과). step07_selected_layout.html
에 Layout Candidates 섹션 추가 (default / alternative / selected badge).
(Step 7-conn)
- step08_zone_region_ratios.json 의 per_zone_plan[i] 에 region_layout_candidates
+ display_strategy_candidates 필드 추가 (Step 8-B-1/2 후보 함수 호출).
step8_conn_placeholder_signals 명시 — Step 3/4 부재 종속 placeholder
(region_count=1, content_type="text_block"). step08 HTML 에 candidates pill +
placeholder caveat. (Step 8-conn)
- APPLICATION_MODE_BY_V4_LABEL 상수 추가 — V4 label → application_mode 변환.
- Step 9 v0 artifact block 신설 (~180 줄) — step09_application_plan.json +
.html. unit 별 layout_candidates / region_layout_candidates /
display_strategy_candidates / v4_candidates / candidate_status /
application_status / current_default_candidate / application_candidates 박힘.
invariant 5 가지 자연 만족 (status.md §4). (Step 9 v0)
src/phase_z2_composition.py 변경 :
- CompositionUnit 에 v4_candidates: list field 추가 (additive, logic 무변).
duck typed (V4Match-shape) — circular import 회피. (Step 6-A)
- collect_candidates() 에 v4_candidates_lookup_fn 인자 추가. 3 분기
(single / parent_merged / parent_merged_inferred) 에서 v4_candidates 채움.
(Step 6-A)
- plan_composition() 도 v4_candidates_lookup_fn pass-through. (Step 6-A)
- stale "pipeline 호출처 X" 주석 정정 (3 곳: select_layout_candidates /
select_region_layout_candidates / select_display_strategy_candidates +
catalog header 2 곳). Step 7-conn / 8-conn 으로 호출처 박혀 *호출처 X* 사실
위배. (cleanup-1)
axis 닫힘 : Step 5 보완 + 6-A + 7-conn + 8-conn + Step 9 v0 + cleanup-1.
폐기 : 6-B (frame ownership transfer — misframed axis. PHASE-Z-CHANGE-LOG.md
2026-05-08 #2 entry 참조).
regression 0 : MDX03 fresh run 검증 — final.html / step10/12/13/20 byte-동일.
schema 확장만 (step05/06/07/08/09).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
761a43da5e |
Add Phase Z B4 source-shape-aware placement
- enable B1/B2/B4 source-shape-aware F13 placement behind env flag - align F13 placement_trace with mapper top_bullets cardinality - preserve canonical render output when flag is off |
||
|
|
8a201337f7 |
Add Phase Z B4 gatekeeper feature flag
- gate V4/B4 mismatch zones via PHASE_Z_B4_GATEKEEPER env (default OFF) - record mismatch as adapter_needed_units with reason and mismatch_detail - preserve render path byte-identical when flag unset |
||
|
|
425a3054c1 |
Add Phase Z placement trace telemetry
- run B1/B2/B4 placement planning in trace-only mode - record placement_trace per rendered zone in debug output - preserve existing render output and visual routing behavior |
||
|
|
02a6d44944 |
Add Phase Z frame slot telemetry markers
- add frame slot markers to F29 runtime partial - collect per-cell frame slot metrics in visual runtime check - preserve existing visual status and failure routing behavior |
||
|
|
e7848b602d |
Add Phase Z runtime foundation
- add visual fit classifier, router, retry, and failure routing modules - add composition planner and catalog-driven mapper - add Phase Z pipeline orchestration and architecture docs |