Files
C.E.L_Slide_test2/.orchestrator/tmp/u13_comment.md
T

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:
    1. 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 at src/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. On apply_font_step_compression_css(plan): asserts [data-zone-position="bottom_l"] selector + font-size: 13.0px value + scope-lock (none of ":root", "body ", ".slide", ".zone" substrings appear -- per feedback_phase_z_spacing_direction).
    2. 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". All FONT_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_size returns None → hits src/phase_z2_retry.py:391-396 branch. Asserts feasible=False, target_font_px is None, font_floor_px == 8.0, and four substrings in failure_reason: "font_step floor", "8.0px", "200.0px", "FONT_SIZE_STEPS exhausted" (matches SALVAGE_FAILURE_TYPE_BY_ACTION action-based classifier surface in src/phase_z2_failure_router.py:85-89). Asserts apply_font_step_compression_css(plan) == "" (no CSS on infeasible -- prevents render-time mutation on revert path).
    3. test_text_metrics_missing_defensive_infeasible -- available_lines=0, chars_per_line=40 exercises the defensive guard at src/phase_z2_retry.py:380-382 BEFORE find_fitting_font_size is invoked. Asserts feasible=False, target_font_px is None, and two substrings in failure_reason: "text_metrics missing" + "available_lines/chars_per_line required". Asserts apply_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.py3 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_css at src/phase_z2_retry.py:400-409 wraps font-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-89 maps "font_step_compression""font_step_insufficient", and :142-149 registers "font_step_compression": "IMPLEMENTED" (u6 wired). The test uses action="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.