4 lines
5.7 KiB
JSON
4 lines
5.7 KiB
JSON
{
|
|
"body": "[Claude #4] Stage 3 code-edit Round #2 — unit u2\n\n### unit_executed\n- **id**: u2\n- **summary**: Extend `phase_z2_failure_router` classifier to surface 3 new salvage failure types (`cross_zone_redistribute_insufficient`, `glue_absorption_insufficient`, `font_step_insufficient`) from `retry_trace.salvage_steps[-1]`. **No `NEXT_ACTION_BY_FAILURE` row added — u3 scope.**\n\n### files_changed\n- `src/phase_z2_failure_router.py` (47 insertions, 0 deletions; ≤ 50-line u2 estimate, ≤ 1-file u2 cap)\n\n### diff_summary\n1. **3 entries added to `FAILURE_TYPE_DESCRIPTIONS`** — `cross_zone_redistribute_insufficient` / `glue_absorption_insufficient` / `font_step_insufficient`. Descriptions cite the underlying salvage source (`fit_verifier.redistribute` / `SPACING_GLUE` envelope / `FONT_SIZE_STEPS` floor) without prescribing routing.\n2. **`SALVAGE_FAILURE_TYPE_BY_ACTION` table** (3 rows) maps `salvage_steps[-1].action` lowercase string → failure_type label. Single mapping point so u3 / u8 do not duplicate the action vocabulary.\n3. **`classify_retry_failure` extended with 2 new branches** placed between the existing `retry_passed` short-circuit and the existing `retry_attempted` check:\n - **case 0.5** : `salvage_passed=True` → `None` (no failure; mirrors `retry_passed=True` semantics).\n - **case 0.7** : `salvage_steps` non-empty AND last record `passed=False` AND `action` is one of the 3 salvage labels → return new failure_type with `classification_rule` quoting raw `failure_reason`.\n4. **Defensive guards** — `salvage_steps` accessed via `or []`; record accessed via `or {}`; unknown actions fall through to existing classifier path (no false positive).\n5. **Routing surface NOT touched** — `NEXT_ACTION_BY_FAILURE` and `NEXT_ACTION_RATIONALE` unchanged; the 3 new failure_types currently return `next_proposed_action=None` via the existing graceful unknown-failure-type path. u3 will populate the cascade.\n\n### evidence\n- `python -m pytest -q tests/phase_z2` → **62 passed in 0.13s** (regression-free; u14 will add cascade-router tests).\n- `git diff --numstat src/phase_z2_failure_router.py` → `47\t0\tsrc/phase_z2_failure_router.py`.\n- Direct probes of `classify_retry_failure` (6 cases, all pass):\n - `retry_passed=True` → `None`\n - `salvage_passed=True` (even with failing salvage step present) → `None`\n - `salvage_steps[-1].action='cross_zone_redistribute', passed=False` → `cross_zone_redistribute_insufficient`\n - `salvage_steps[-1].action='glue_compression', passed=False` (after a passing earlier step) → `glue_absorption_insufficient`\n - `salvage_steps[-1].action='font_step_compression', passed=False` → `font_step_insufficient`\n - Existing plan-fail path with `'primary donor ... slack ... target_added_px'` reason → `donor_slack_insufficient` (regression preserved)\n- Direct probe of `route_retry_failure` on the 3 new failure_types → `next_proposed_action=None` (mapping intentionally empty — u3 will register). Existing 4 routes (`donor_slack_insufficient → layout_adjust`, `no_donor_candidates → layout_adjust`, `rerender_still_fails → frame_reselect`, `not_attempted → none`) unchanged.\n\n### scope_lock_compliance\n- ✅ deterministic — pure dict lookup + branch, no AI/Kei surface added (`feedback_ai_isolation_contract`)\n- ✅ no spacing shrink direction (`feedback_phase_z_spacing_direction`) — classifier-only change, no CSS / layout side-effects\n- ✅ `ACTION_BY_CATEGORY` unchanged (Stage 1 scope_lock — cascade-only actions stay off the 8-label surface)\n- ✅ files ≤ 3 (actual: 1) and `estimate_lines ≤ 50` (actual: +47 / -0)\n- ✅ status semantics held — `NEXT_ACTION_IMPLEMENTATION_STATUS` unchanged in this unit; u7 will register `IMPLEMENTED` for the 3 new actions once u4~u6 land\n- ✅ failure_type → next_action surface for the 3 new types deferred to u3 (no dual-edit footgun)\n\n### remaining_units\n- u3 — rewire `NEXT_ACTION_BY_FAILURE` (`donor_slack_insufficient` / `no_donor_candidates` → `cross_zone_redistribute`; cross_zone→glue→font_step→layout_adjust chain) + update `NEXT_ACTION_RATIONALE`\n- u4 — `plan_cross_zone_redistribute` (wraps `fit_verifier.redistribute`) + `apply_cross_zone_redistribute_css` scoped role-height overrides\n- u5 — `plan_glue_compression` (wraps `space_allocator.compute_glue_css_overrides`) + `apply_glue_compression_css` scoped to `[data-zone-position]`\n- u6 — `plan_font_step_compression` (wraps `find_fitting_font_size`) + `apply_font_step_compression_css` zone-scoped font-size override\n- u7 — register `IMPLEMENTED` status for 3 new salvage actions in `ACTION_IMPLEMENTATION_STATUS` + `NEXT_ACTION_IMPLEMENTATION_STATUS`; no `ACTION_BY_CATEGORY` change\n- u8 — `_attempt_salvage_chain` in `phase_z2_pipeline.py` driving failure_router cascade with per-step rerender + visual_check + (b)-revert\n- u9 — pipeline wiring after `_attempt_zone_ratio_retry`; Step 17 telemetry (`salvage_steps`, `salvage_passed`)\n- u10 — multi-donor `plan_zone_ratio_retry` tests (single-donor regression, multi-donor PASS, aggregate-insufficient FAIL)\n- u11 — `plan_cross_zone_redistribute` tests (multi-role feasible / single-role infeasible)\n- u12 — `plan_glue_compression` tests (scope-lock: `[data-zone-position]` only, no `:root`/`body`/`.slide`)\n- u13 — `plan_font_step_compression` tests (feasible / floor-reached infeasible / missing text_metrics defensive)\n- u14 — failure_router cascade tests (the unit that exercises u2 + u3 + u7 wiring end-to-end)\n- u15 — end-to-end `_attempt_salvage_chain` tests with (a) cross_zone pass / (b) glue pass / (c) all-fail (b)-revert\n\n### follow_up_issue_candidates\n(none — u2 stays inside the Stage 2 scope_lock. The deferred routing wiring is u3, not a follow-up issue.)\n\nrewind_target: continue_same"
|
|
}
|