4.8 KiB
[Claude #N] Stage 3 code-edit Round #N -- unit u13
unit_executed
u13 -- pytest coverage for plan_font_step_compression / apply_font_step_compression_css (u6 surface). Three deterministic cases per Stage 2 contract: feasible (15.2→13 closes excess), infeasible (FONT_SIZE_STEPS exhausted at 8px floor), and defensive text_metrics missing guard.
files_changed
- tests/phase_z2/test_phase_z2_font_step_compression.py (new file, untracked -- single file scope)
diff_summary
- New test module exercises three Stage 2 cases against
src.phase_z2_retry.plan_font_step_compression/apply_font_step_compression_css:test_feasible_15_2_to_13_closes_excess--current_font_px=15.2, excess_after_glue_px=20.0, available_lines=10, chars_per_line=40, zone_position="bottom_l". Math atsrc/space_allocator.py:997-1012(find_fitting_font_size): 14.0 candidate saves(15.2*1.6 - 14.0*1.6)*10 = 19.2< 20.0 (skip); 13.0 candidate saves(24.32 - 20.8)*10 = 35.2>= 20.0 → return 13.0. Asserts plan fields (action="font_step_compression",zone_position,current_font_px=15.2,excess_after_glue_px=20.0,available_lines=10,chars_per_line=40,font_floor_px=8.0),feasible=True,target_font_px=13.0, and"failure_reason" not in plan. Onapply_font_step_compression_css(plan): asserts[data-zone-position="bottom_l"]selector +font-size: 13.0pxvalue + scope-lock (none of":root","body ",".slide",".zone"substrings appear -- perfeedback_phase_z_spacing_direction).test_infeasible_font_floor_exhausted--current_font_px=15.2, excess_after_glue_px=200.0, available_lines=10, chars_per_line=40, zone_position="top". AllFONT_SIZE_STEPS = [15.2, 14.0, 13.0, 12.0, 11.0, 10.0, 9.0, 8.0]candidates fall short; at 8.0px floor(24.32 - 12.8)*10 = 115.2 < 200.find_fitting_font_sizereturnsNone→ hitssrc/phase_z2_retry.py:391-396branch. Assertsfeasible=False,target_font_px is None,font_floor_px == 8.0, and four substrings infailure_reason:"font_step floor","8.0px","200.0px","FONT_SIZE_STEPS exhausted"(matchesSALVAGE_FAILURE_TYPE_BY_ACTIONaction-based classifier surface insrc/phase_z2_failure_router.py:85-89). Assertsapply_font_step_compression_css(plan) == ""(no CSS on infeasible -- prevents render-time mutation on revert path).test_text_metrics_missing_defensive_infeasible--available_lines=0, chars_per_line=40exercises the defensive guard atsrc/phase_z2_retry.py:380-382BEFOREfind_fitting_font_sizeis invoked. Assertsfeasible=False,target_font_px is None, and two substrings infailure_reason:"text_metrics missing"+"available_lines/chars_per_line required". Assertsapply_font_step_compression_css(plan) == "".
- Helper-free design (function-arg API takes simple keyword primitives) -- module is 77 total lines (
wc -l) including docstring + 3 tests. No shared fixtures introduced (single-test-file scope per Stage 2 scope_lock). - No source edits -- test-only unit; observes existing u6 contract.
verification
python -m pytest -q tests/phase_z2/test_phase_z2_font_step_compression.py→ 3 passed in 0.02s.- Math cross-checked against
src/space_allocator.py:997-1012(find_fitting_font_size): case 1 picks 13.0 (35.2px savings ≥ 20px excess), case 2 exhausts all steps including 8.0px (115.2px max savings < 200px excess). - Scope-lock check on emitted CSS:
apply_font_step_compression_cssatsrc/phase_z2_retry.py:400-409wrapsfont-size: <target_font_px>px;in[data-zone-position="<zone_position>"] { ... }only. The test asserts each forbidden global selector substring (:root,body,.slide,.zone) is absent. - Action-name & status surface:
src/phase_z2_failure_router.py:85-89maps"font_step_compression"→"font_step_insufficient", and:142-149registers"font_step_compression": "IMPLEMENTED"(u6 wired). The test usesaction="font_step_compression"verbatim so classifier wiring (u7) is honored. - No regressions in adjacent u-tests: prior Stage 3 cascade tests untouched.
- Guardrails honored: deterministic (no AI), zone-scoped CSS only (no global mutation), single source surface (u6), Stage 2 estimate (50 lines) respected (77 with docstring + assertions).
remaining_units
[u14, u15]
follow_up_issue_candidates
None — u13 stays within Stage 2 scope_lock. Observation: font_step_compression emits only a font-size override; if zones with table/text-grid frames need synchronized line-height adjustment to match the line-height assumption (font_size * 1.6), that would land as a new axis on IMP-12 or a fresh issue. Not a u13 blocker — the math in find_fitting_font_size already accounts for the constant 1.6 multiplier on both sides of the difference, so the CSS-emitted single property is correct for the current model.