{ "body": "Executing unit: u7\n\n[Claude #9] Stage 3 Round #7 — IMP-09 PR 2 u7\n\n### unit_executed\n- **id**: `u7`\n- **summary**: Wire `build_layout_css` override branch — route 5 2-D topologies through `_override_to_grid_tracks`; downgrade warn-and-fallthrough to `single` only (text updated to name `single`)\n\n### files_changed\n- `src/phase_z2_pipeline.py` (+16 / -6 this unit; +244 / -12 cumulative across u1+u2+u3+u4+u5+u6+u7)\n\n### diff_summary\n- Two edits inside `build_layout_css` docstring + override branch (Step D-ext). PR 1 helpers (`_parse_css_areas`, `_parse_fr_string`, `compute_zone_layout`, `compute_zone_layout_cols`, `_compute_per_zone_geometry`, `_build_fr_default`, `_build_rows_dynamic`, `_build_cols_dynamic`) untouched. u1–u5 surfaces (`TOPOLOGY_AXIS_MAP`, `_TWO_D_TOPOLOGIES`, `_aggregate_axis_zone`, `_build_2d_dynamic`, `_derive_axis_tracks_from_override`, `_override_to_grid_tracks`) unchanged. u6 normal-path dispatch arms unchanged.\n\n- **Edit 1 — docstring `Step D-ext` block (−5 / +7 lines)**:\n - Replaced the PR 1 lock note (\"horizontal-2 / vertical-2 만 처리 … 다른 preset 은 warn-and-fallthrough\") with explicit per-branch enumeration: `horizontal-2 / vertical-2` = legacy inline byte-preserved; `topology in _TWO_D_TOPOLOGIES` (T / inverted-T / side-T-left / side-T-right / 2x2) = PR 2 (u7) routes through `_override_to_grid_tracks` (per-axis MAX with dynamic fallback); `single` = warn-and-fallthrough (PR 3 candidate).\n\n- **Edit 2 — Step D-ext override-branch dispatch (−7 / +14 lines of logic + warn text)**:\n - **New arm inserted between `elif layout_preset == \"vertical-2\"` and the warn-fallthrough `else`**:\n ```python\n elif topology in _TWO_D_TOPOLOGIES:\n # PR 2 (u7) — 2-D topologies route through unified\n # _override_to_grid_tracks (per-axis MAX with dynamic fallback).\n return _override_to_grid_tracks(\n preset, zones_data, override_zone_geometries, gap\n )\n ```\n Calls u5 helper with the legacy `(preset, zones_data, override_zone_geometries, gap)` signature. Returns the u5 dict shape (`computation=\"user_override_2d\"`, `dynamic_rows=True`, `dynamic_cols=True`, `raw_zone_layout={rows, cols, fallback}`).\n - **`else:` warn-and-fallthrough block downgraded to `single` only**:\n - Comment header changed: `# PR 1 lock — warn-and-fallthrough preserved.` + `# PR 2 promotes this to strict ValueError via _override_to_grid_tracks.` → `# \`single\` only — warn-and-fallthrough preserved.`\n - Warning text now names every supported preset and explicitly calls out `single`: `\"(현재 horizontal-2 / vertical-2 / T / inverted-T / side-T-left / side-T-right / 2x2 만; \`single\` 은 미지원). default layout_css 사용.\"` — matches Stage 2 spec \"text updated to name `single`\".\n - `print(..., file=sys.stderr)` call signature unchanged (still `sys.stderr`, still side-effect only; warn-and-then-fallthrough behavior preserved for the `single` case).\n - **Branch ordering**: the new `elif topology in _TWO_D_TOPOLOGIES` arm is placed AFTER both legacy `if`/`elif` arms but BEFORE the `else` — so `horizontal-2` and `vertical-2` continue to hit their legacy inline code (byte-preserve invariant) and only non-legacy 2-D presets reach the new arm. `single` (and any future non-2-D non-legacy preset) reaches the downgraded warn-fallthrough `else`.\n\n- **Length-locked R/C invariant**: 2-D override path returns the same `len(heights_px) == len(axis_map[\"rows\"]) == 2` and `len(widths_px) == len(axis_map[\"cols\"]) == 2` length-locked tracks that u5 produces. Smoke confirms `R=2 C=2` for all 5 in-scope presets.\n\n- **Retry gate preservation (lines 1621-1629)**: gate skips when `dynamic_cols=True` OR `dynamic_rows=False`. Verified for both PR 2 paths:\n - 2-D normal (u6): `dynamic_cols=True` → skips. ✓\n - 2-D override (u7): `dynamic_cols=True` → skips. ✓\n - legacy `horizontal-2` override: `dynamic_rows=True`, `dynamic_cols=False` → does NOT skip (existing semantic preserved).\n - legacy `vertical-2` override: `dynamic_rows=False`, `dynamic_cols=True` → skips (existing semantic preserved).\n\n### evidence\n- `git log -1 --format=%H` → `201099e53b0048f3040e17ee9b0394ba9ad6e940` (Stage 2 baseline preserved; no commit/push this turn per Stage 3 contract).\n- `git diff --stat src/phase_z2_pipeline.py` → `1 file changed, 244 insertions(+), 12 deletions(-)` (cumulative across u1+u2+u3+u4+u5+u6+u7; u7-only delta = 244−228=**+16** / 12−6=**−6** ⇒ **22 lines changed** ≤ 50-line gate ✓).\n- `git diff --numstat` → `244 12 src/phase_z2_pipeline.py` (single file ≤ 3-file gate ✓).\n- `git diff --check src/phase_z2_pipeline.py` → no whitespace errors.\n- `git diff --name-only HEAD -- src/space_allocator.py` → empty (Kei legacy `build_containers_type_b` invariant preserved ✓).\n- AST parse: `PARSE OK`.\n- **Behavior smoke** (import-load with module on sys.path):\n - `top-1-bottom-2` override → `comp=user_override_2d dyn_r=True dyn_c=True R=2 C=2` ✓\n - `top-2-bottom-1` override → `comp=user_override_2d dyn_r=True dyn_c=True R=2 C=2` ✓\n - `left-1-right-2` override → `comp=user_override_2d dyn_r=True dyn_c=True R=2 C=2` ✓\n - `left-2-right-1` override → `comp=user_override_2d dyn_r=True dyn_c=True R=2 C=2` ✓\n - `grid-2x2` override → `comp=user_override_2d dyn_r=True dyn_c=True R=2 C=2` ✓\n - `single` override → `[override-warning] … '\`single\` 은 미지원' …` printed to stderr, then falls through to `comp=fr_default_from_preset dyn_r=False dyn_c=False` ✓\n - `horizontal-2` override → `comp=user_override_geometry dyn_r=True dyn_c=False` ✓ (legacy byte-preserved)\n - `vertical-2` override → `comp=user_override_geometry dyn_r=False dyn_c=True` ✓ (legacy byte-preserved)\n\n### remaining_units\n`[u8, u9, u10, u11, u12, u13, u14, u15, u16, u17, u18, u19, u20, u21, u22, u23, u24, u25, u26]`\n(executed so far: u1, u2, u3, u4, u5, u6, u7 — 7 / 26)\n\n### follow_up_issue_candidates\nNone — u7 stays inside scope-lock (override-branch dispatch for the 5 declared 2-D topologies + downgrade of warn-fallthrough to `single`). Stage 2 already enumerated downstream consumers for follow-up:\n- `single` topology dynamic promotion (separate PR).\n- retry_gate extension to 2-D presets (currently only skip-validated by u12).\n- B4 frame_selection evidence integration.\n- Consumer audit for Step 8 `computation` label change (`\"2d_dynamic_aggregated\"` / `\"user_override_2d\"`) — u7 added the second label call site; u26 closing scope-lock audit can confirm no untracked consumer depends on the old fr_default label for these 5 presets." }