7.1 KiB
Executing unit: u15
[Claude #N] Stage 3 code-edit Round #N -- unit u15
unit_executed
u15 -- pytest coverage for _attempt_salvage_chain (u8 surface) end-to-end. Three Stage 2 cases per contract: (a) cross_zone passes, (b) cross_zone fails + glue passes, (c) all three fail with (b) revert.
files_changed
- tests/phase_z2/test_phase_z2_step17_salvage_chain.py (new file, untracked -- single file scope)
diff_summary
-
New test module exercises three Stage 2 cases against
src.phase_z2_pipeline._attempt_salvage_chain:-
test_case_a_cross_zone_passes_final_html_promoted-- two-role same-zoneFitAnalysis(top+30 deficit,bottom_l-50 surplus, both inslide_body).plan_cross_zone_redistributereturnsfeasible=True→apply_cross_zone_redistribute_cssemits[data-role=...]rules → monkey-patchedrun_overflow_checkreturnspassed=Trueon the first candidate. Assertssalvage_attempted=True,salvage_passed=True,len(salvage_steps)==1,salvage_steps[0]={action: "cross_zone_redistribute", passed: True, plan.feasible: True},css_overridecontains[data-role=, andout_pathcontent was overwritten (no longer "ORIGINAL_BEFORE_SALVAGE", contains the patched render fingerprintu15-test). This is the "final.html promoted" branch atsrc/phase_z2_pipeline.py:2007-2012. -
test_case_b_cross_zone_fails_glue_passes_second_promoted-- single-roleFitAnalysis(toponly) →fit_verifier.redistributeshort-circuits atlen(roles_in_zone) < 2(persrc/fit_verifier.py:528-535) →plan_cross_zone_redistributereturnsfeasible=Falsewithfailure_reasoncontaining "single-role zone". No CSS emitted → no rerender / no overflow call on iter 1. Loop setsfailure_type="cross_zone_redistribute_insufficient",route_retry_failurereturnsglue_compression.plan_glue_compression(excess_px=40, block_count=3, zone_position="bottom_l")is feasible (envelope=76 >= 40) →apply_glue_compression_cssemits[data-zone-position="bottom_l"]rule → patchedrun_overflow_checkreturnspassed=True. Assertslen(salvage_steps)==2,salvage_steps[0].passed=Falsewith cross_zone failure_reason,salvage_steps[1].action=="glue_compression"withpassed=Trueand CSS containing[data-zone-position="bottom_l"],render_slidecall counter == 1 (only the feasible glue branch rendered),out_pathoverwritten. This is the "2nd promoted, (b) revert NOT triggered" branch. -
test_case_c_all_three_fail_revert_preserved--fit_analysis=Nonein cascade_inputs →_attempt_salvage_chainbody atsrc/phase_z2_pipeline.py:1972-1974emits a syntheticfeasible=Falseplan with reasoncascade_inputs.fit_analysis missing(no CSS).excess_px=200, block_count=1→ glue envelope = 28 < 200 →failure_reasoncontains "glue envelope insufficient".excess_after_glue_px=200, current_font_px=15.2, available_lines=10, chars_per_line=40→find_fitting_font_sizereturnsNone(8px floor saves only 115.2 < 200) →failure_reasoncontains "FONT_SIZE_STEPS exhausted". Assertssalvage_passed=False,len(salvage_steps)==3,actions==[cross_zone_redistribute, glue_compression, font_step_compression], allpassed=Falseandcss_override is None.run_overflow_checkis monkey-patched to a sentinel that fails the test if invoked (defensive: confirms no rerender when no CSS).render_slidecall counter == 0.out_path.read_text() == "ORIGINAL_BEFORE_SALVAGE"(original final.html intact -- (b) revert preserved, per Stage 2 contractfeedback_phase_z_spacing_directionlock).
-
-
Module-level fixtures:
_LAYOUT_CSS_GATE_PASS-- minimal layout_css withdynamic_rows=True, dynamic_cols=Falseso the IMP-09 gate atsrc/phase_z2_pipeline.py:1958-1960does not early-skip. Other layout_css fields unused by cascade body.project_tmpfixture -- creates a temp dir underPROJECT_ROOT/.orchestrator/tmp/(not the defaultpytest tmp_pathwhich sits under%LOCALAPPDATA%on Windows -- a different drive from this D:\ project, which would breakcandidate_path.relative_to(PROJECT_ROOT)atsrc/phase_z2_pipeline.py:2006)._patch_render(monkeypatch)-- stubsrender_slideto a deterministic 1-line HTML envelope (no Jinja2 / template files required), returns counter to assert rerender count._kwargs(...)-- builds the 10-arg call dict for_attempt_salvage_chain.
-
No source edits -- test-only unit; observes existing u8 + u4/u5/u6 + u2/u3 surfaces.
verification
python -m pytest -q tests/phase_z2/test_phase_z2_step17_salvage_chain.py-> 3 passed in 0.07s.- Full cascade suite re-run:
python -m pytest -q tests/phase_z2/test_phase_z2_retry_multi_donor.py tests/phase_z2/test_phase_z2_cross_zone_redistribute.py tests/phase_z2/test_phase_z2_glue_compression.py tests/phase_z2/test_phase_z2_font_step_compression.py tests/phase_z2/test_phase_z2_failure_router_cascade.py tests/phase_z2/test_phase_z2_step17_salvage_chain.py-> 19 passed in 0.08s (3+3+3+3+4+3). - Cross-checked
_attempt_salvage_chainsemantics atsrc/phase_z2_pipeline.py:1947-2019: (i) IMP-09 gate at 1958-1960, (ii) per-iterplan → apply CSS → rerender → run_overflow_checkat 1971-2003, (iii)out_path.write_text(candidate_html, ...)only onpassed=Trueat 2008, (iv) for-loop boundlen(_SALVAGE_FAIL_BY_ACTION)==3at 1964, (v)_SALVAGE_FAIL_BY_ACTIONre-feedsroute_retry_failurefor the deterministic cascade order. git status --short tests/phase_z2/test_phase_z2_step17_salvage_chain.pyconfirms single-file untracked addition.
remaining_units
[] -- u15 is the final unit in the Stage 2 plan. All 15 implementation_units (u1-u15) now have artifacts in the tree.
follow_up_issue_candidates
- None new this round. The two open items already named in the mid-stage compaction remain:
frame_reselectbody implementation (terminal cascade action; out-of-scope per Stage 1).layout_adjustbody implementation (font_step_insufficient routes here; out-of-scope per Stage 1). Both are Stage 2 follow_up_candidates and not blocking IMP-12 close.
=== EVIDENCE === Commands run:
git status --shortwc -l tests/phase_z2/test_phase_z2_step17_salvage_chain.pypython -m pytest -q tests/phase_z2/test_phase_z2_step17_salvage_chain.pypython -m pytest -q tests/phase_z2/test_phase_z2_retry_multi_donor.py tests/phase_z2/test_phase_z2_cross_zone_redistribute.py tests/phase_z2/test_phase_z2_glue_compression.py tests/phase_z2/test_phase_z2_font_step_compression.py tests/phase_z2/test_phase_z2_failure_router_cascade.py tests/phase_z2/test_phase_z2_step17_salvage_chain.pygrep -n "_attempt_salvage_chain\|_SALVAGE_FAIL_BY_ACTION\|candidate_path.relative_to" src/phase_z2_pipeline.py
Files checked:
src/phase_z2_pipeline.py(lines 1937-2019, 4423-4467)src/phase_z2_retry.py(plan_cross_zone_redistribute / plan_glue_compression / plan_font_step_compression, lines 247-409)src/phase_z2_failure_router.py(NEXT_ACTION_BY_FAILURE / route_retry_failure)src/fit_verifier.py(redistribute single-role short-circuit, lines 528-535)tests/phase_z2/test_retry_gate.py(precedent for_attempt_zone_ratio_retryunit testing pattern)
DO NOT commit. Gitea comment IS the deliverable.