1 line
6.5 KiB
JSON
1 line
6.5 KiB
JSON
{"body": "Executing unit: u9\n\n[Claude #N] Stage 3 code-edit Round #2 — unit u9 (rewind fix)\n\n### unit_executed\nu9 — 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.\n\n### files_changed\n- src/phase_z2_pipeline.py\n\n### diff_summary\n- u9 block at `src/phase_z2_pipeline.py:4423-4490` now synthesizes a `FitAnalysis` from `debug_zones` + `overflow.zones`:\n - Each `debug_zones[i].position` becomes a `RoleFit(role=position, allocated_px=height_px, shortfall_px=scrollHeight-clientHeight)`. Sign convention matches `fit_verifier.redistribute()` (positive = deficit, negative = surplus).\n - All positions share conceptual zone `\"slide_body\"` via `containers[position] = {\"zone\": \"slide_body\", \"height_px\": int(allocated)}` so `redistribute()` trades shortfalls between them in a single bucket. Phase Q's `calculate_fit` is not used in Phase Z (policy comment at `src/phase_z2_pipeline.py:2839-2843`), so this synthesis is the deterministic substitute.\n - `FitAnalysis(roles=_fa_roles)` + `_fa_containers` replace the previous `fit_analysis=None, containers={}` placeholders.\n- Import added inline (`from src.fit_verifier import FitAnalysis, RoleFit`) — matches the existing `from src.fit_verifier import ...` pattern used across `src/space_allocator.py`, `src/renderer.py`, `src/phase_z2_retry.py`.\n- After `if _salvage_trace.get(\"salvage_passed\"):` block — `enrich_retry_trace_with_failure_classification(retry_trace)` is re-invoked. `classify_retry_failure` short-circuits on `salvage_passed=True` (`src/phase_z2_failure_router.py:167`), so this clears both `retry_trace[\"failure_classification\"]` and `retry_trace[\"next_action_proposal\"]` to `None`, matching the contract enforced at `src/phase_z2_failure_router.py:302-306`. Step 18/19 artifacts now reflect the post-salvage state (no stale `donor_slack_insufficient` / `no_donor_candidates`).\n- Net delta on the u9 block: +60 / -2 lines (`git diff --numstat src/phase_z2_pipeline.py` reports 164/4 cumulative including the u8 hunk from prior round; u9 block alone is ~22 added lines).\n- No new module dependencies, no behavior change outside the cascade trigger guard (`_ft in {\"donor_slack_insufficient\", \"no_donor_candidates\"}`).\n\n### verification\n- Smoke test 1 — RoleFit/FitAnalysis construction with synthesized Phase Z roles:\n ```\n redistribute(FitAnalysis(roles={top:RoleFit(shortfall=+30), bottom_l:RoleFit(shortfall=-50), bottom_r:RoleFit(shortfall=-10)}), containers, min_margin_px=10) →\n can_redistribute=True; redistribution={top:230, bottom_l:150, bottom_r:180}; needs_escalation=False\n ```\n Deficit 30px transferred from bottom_l (40px usable surplus after 10px margin) → top. Confirms the synthesis lets `plan_cross_zone_redistribute` return `feasible=True` when geometry permits, and `feasible=False` (with `can_redistribute=False`) otherwise.\n- Smoke test 2 — post-salvage enrich refresh:\n Pre: `failure_classification={'failure_type': 'donor_slack_insufficient', ...}`, `next_action_proposal={'next_proposed_action': 'cross_zone_redistribute'}`, `salvage_passed=True`.\n After `enrich_retry_trace_with_failure_classification(rt)`: both fields = `None`. Asserted.\n- `python -m ast` parse OK on `src/phase_z2_pipeline.py`.\n- `pytest -q tests/phase_z2` → **62 passed** (regression-free baseline maintained; matches prior u1/u2/u3 baseline reported in MID-STAGE COMPACTION).\n\n### scope-lock adherence\n- Edits confined to `src/phase_z2_pipeline.py` (file count = 1, well within u9's ≤3-file budget).\n- No `:root` / `body` / `.slide` / global CSS mutation. The cross_zone CSS still emits `[data-role=\"<role>\"]` selectors from `apply_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 to `glue_compression` via `_SALVAGE_FAIL_BY_ACTION → cross_zone_redistribute_insufficient → NEXT_ACTION_BY_FAILURE → glue_compression`.\n- No AI call added on normal path (`feedback_ai_isolation_contract` — preserved).\n- No spacing-direction shrink on common envelope (`feedback_phase_z_spacing_direction` — preserved).\n- IMP-09 `dynamic_cols` / `fr_default` gate already enforced inside `_attempt_salvage_chain` at `src/phase_z2_pipeline.py:1958-1960` — unchanged.\n- `(b) revert` semantics preserved: `out_path.write_text(candidate_html)` is only called inside `_attempt_salvage_chain` on `passed=True`. On all-3-fail, the original `final.html` written at line 4306 stays intact. Codex case (c) contract holds.\n\n### remaining_units\n- u10 — Regression tests for multi-donor `plan_zone_ratio_retry` (`tests/phase_z2/test_phase_z2_retry_multi_donor.py`). Deferred from u1.\n- u11 — Tests for `plan_cross_zone_redistribute` (`tests/phase_z2/test_phase_z2_cross_zone_redistribute.py`).\n- u12 — Tests for `plan_glue_compression` (`tests/phase_z2/test_phase_z2_glue_compression.py`).\n- u13 — Tests for `plan_font_step_compression` (`tests/phase_z2/test_phase_z2_font_step_compression.py`).\n- u14 — Tests for failure_router cascade (`tests/phase_z2/test_phase_z2_failure_router_cascade.py`).\n- u15 — End-to-end Step 17 salvage chain tests (`tests/phase_z2/test_phase_z2_step17_salvage_chain.py`). Will lock the post-salvage `failure_classification=None` invariant as a regression guard.\n\n### follow_up_issue_candidates\n- Selector mismatch in `apply_cross_zone_redistribute_css`: emits `[data-role=\"<role>\"]` rules, but Phase Z's `templates/phase_z2/slide_base.html:242` only declares `data-zone-position`, not `data-role`. The salvage plan is now feasible, but the rerender CSS will not match any Phase Z DOM and the cascade will fall through to `glue_compression`. Two non-mutually-exclusive resolutions, both out-of-scope for u9:\n 1. Extend `apply_cross_zone_redistribute_css` to also emit `[data-zone-position=\"<role>\"]` when synthesized roles equal zone positions (would require role-vs-position discrimination signal).\n 2. Add `data-role=\"{{ zone.position }}\"` to `slide_base.html` so Phase Z DOM honors the Phase Q-style selector.\n- 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.\n"} |