6.4 KiB
6.4 KiB
Executing unit: u9
[Claude #N] Stage 3 code-edit Round #2 — unit u9 (rewind fix)
unit_executed
u9 — pipeline wiring for _attempt_salvage_chain. Addresses both Codex #10 findings: stale failure_classification leaking into Step 18/19 after salvage_passed=True, and fit_analysis=None / containers={} making cross_zone_redistribute permanently infeasible at the real call site.
files_changed
- src/phase_z2_pipeline.py
diff_summary
- u9 block at
src/phase_z2_pipeline.py:4423-4490now synthesizes aFitAnalysisfromdebug_zones+overflow.zones:- Each
debug_zones[i].positionbecomes aRoleFit(role=position, allocated_px=height_px, shortfall_px=scrollHeight-clientHeight). Sign convention matchesfit_verifier.redistribute()(positive = deficit, negative = surplus). - All positions share conceptual zone
"slide_body"viacontainers[position] = {"zone": "slide_body", "height_px": int(allocated)}soredistribute()trades shortfalls between them in a single bucket. Phase Q'scalculate_fitis not used in Phase Z (policy comment atsrc/phase_z2_pipeline.py:2839-2843), so this synthesis is the deterministic substitute. FitAnalysis(roles=_fa_roles)+_fa_containersreplace the previousfit_analysis=None, containers={}placeholders.
- Each
- Import added inline (
from src.fit_verifier import FitAnalysis, RoleFit) — matches the existingfrom src.fit_verifier import ...pattern used acrosssrc/space_allocator.py,src/renderer.py,src/phase_z2_retry.py. - After
if _salvage_trace.get("salvage_passed"):block —enrich_retry_trace_with_failure_classification(retry_trace)is re-invoked.classify_retry_failureshort-circuits onsalvage_passed=True(src/phase_z2_failure_router.py:167), so this clears bothretry_trace["failure_classification"]andretry_trace["next_action_proposal"]toNone, matching the contract enforced atsrc/phase_z2_failure_router.py:302-306. Step 18/19 artifacts now reflect the post-salvage state (no staledonor_slack_insufficient/no_donor_candidates). - Net delta on the u9 block: +60 / -2 lines (
git diff --numstat src/phase_z2_pipeline.pyreports 164/4 cumulative including the u8 hunk from prior round; u9 block alone is ~22 added lines). - No new module dependencies, no behavior change outside the cascade trigger guard (
_ft in {"donor_slack_insufficient", "no_donor_candidates"}).
verification
- Smoke test 1 — RoleFit/FitAnalysis construction with synthesized Phase Z roles:
Deficit 30px transferred from bottom_l (40px usable surplus after 10px margin) → top. Confirms the synthesis lets
redistribute(FitAnalysis(roles={top:RoleFit(shortfall=+30), bottom_l:RoleFit(shortfall=-50), bottom_r:RoleFit(shortfall=-10)}), containers, min_margin_px=10) → can_redistribute=True; redistribution={top:230, bottom_l:150, bottom_r:180}; needs_escalation=Falseplan_cross_zone_redistributereturnfeasible=Truewhen geometry permits, andfeasible=False(withcan_redistribute=False) otherwise. - Smoke test 2 — post-salvage enrich refresh:
Pre:
failure_classification={'failure_type': 'donor_slack_insufficient', ...},next_action_proposal={'next_proposed_action': 'cross_zone_redistribute'},salvage_passed=True. Afterenrich_retry_trace_with_failure_classification(rt): both fields =None. Asserted. python -m astparse OK onsrc/phase_z2_pipeline.py.pytest -q tests/phase_z2→ 62 passed (regression-free baseline maintained; matches prior u1/u2/u3 baseline reported in MID-STAGE COMPACTION).
scope-lock adherence
- Edits confined to
src/phase_z2_pipeline.py(file count = 1, well within u9's ≤3-file budget). - No
:root/body/.slide/ global CSS mutation. The cross_zone CSS still emits[data-role="<role>"]selectors fromapply_cross_zone_redistribute_css(u4 surface, untouched); whether Phase Z DOM honors them is a separate axis (see follow-up below). The synthesis correctly lets the plan be feasible; if the rerender visual check still fails, the cascade naturally falls through toglue_compressionvia_SALVAGE_FAIL_BY_ACTION → cross_zone_redistribute_insufficient → NEXT_ACTION_BY_FAILURE → glue_compression. - No AI call added on normal path (
feedback_ai_isolation_contract— preserved). - No spacing-direction shrink on common envelope (
feedback_phase_z_spacing_direction— preserved). - IMP-09
dynamic_cols/fr_defaultgate already enforced inside_attempt_salvage_chainatsrc/phase_z2_pipeline.py:1958-1960— unchanged. (b) revertsemantics preserved:out_path.write_text(candidate_html)is only called inside_attempt_salvage_chainonpassed=True. On all-3-fail, the originalfinal.htmlwritten at line 4306 stays intact. Codex case (c) contract holds.
remaining_units
- u10 — Regression tests for multi-donor
plan_zone_ratio_retry(tests/phase_z2/test_phase_z2_retry_multi_donor.py). Deferred from u1. - u11 — Tests for
plan_cross_zone_redistribute(tests/phase_z2/test_phase_z2_cross_zone_redistribute.py). - u12 — Tests for
plan_glue_compression(tests/phase_z2/test_phase_z2_glue_compression.py). - u13 — Tests for
plan_font_step_compression(tests/phase_z2/test_phase_z2_font_step_compression.py). - u14 — Tests for failure_router cascade (
tests/phase_z2/test_phase_z2_failure_router_cascade.py). - u15 — End-to-end Step 17 salvage chain tests (
tests/phase_z2/test_phase_z2_step17_salvage_chain.py). Will lock the post-salvagefailure_classification=Noneinvariant as a regression guard.
follow_up_issue_candidates
- Selector mismatch in
apply_cross_zone_redistribute_css: emits[data-role="<role>"]rules, but Phase Z'stemplates/phase_z2/slide_base.html:242only declaresdata-zone-position, notdata-role. The salvage plan is now feasible, but the rerender CSS will not match any Phase Z DOM and the cascade will fall through toglue_compression. Two non-mutually-exclusive resolutions, both out-of-scope for u9:- Extend
apply_cross_zone_redistribute_cssto also emit[data-zone-position="<role>"]when synthesized roles equal zone positions (would require role-vs-position discrimination signal). - Add
data-role="{{ zone.position }}"toslide_base.htmlso Phase Z DOM honors the Phase Q-style selector.
- Extend
- These are template/CSS-emission concerns, not Step 17 wiring. Filing as a follow-up axis preserves Stage 2's u4 contract (already verified) while keeping the u9 scope on pipeline plumbing.