Executing unit: u1 [Claude #3] Stage 3 Round #1 — IMP-09 PR 2 u1 ### unit_executed - **id**: `u1` - **summary**: Add `TOPOLOGY_AXIS_MAP` constant + `_TWO_D_TOPOLOGIES` frozenset (5 in-scope topology → row/col position groupings, no dispatch wiring) ### files_changed - `src/phase_z2_pipeline.py` (+36 / -0) ### diff_summary - Inserted after `_build_cols_dynamic` (HEAD line 1113) and before the existing comment at HEAD line 1116. No edits to any other surface; 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. - New constants: - `TOPOLOGY_AXIS_MAP: dict[str, dict[str, list[list[str]]]]` — keys = `{"T", "inverted-T", "side-T-left", "side-T-right", "2x2"}`; each value carries `"rows"` (row-track groupings for `compute_zone_layout` heights) and `"cols"` (col-track groupings for `compute_zone_layout_cols` widths). - `_TWO_D_TOPOLOGIES = frozenset(TOPOLOGY_AXIS_MAP)` — 5-element membership-test surface for u6/u7 dispatch. - Spanning-zone convention recorded in the docblock: - `T."top"` spans both cols → excluded from `cols`. - `inverted-T."bottom"` spans both cols → excluded from `cols`. - `side-T-left."left"` spans both rows → excluded from `rows`. - `side-T-right."right"` spans both rows → excluded from `rows`. - `2x2` — no spanning, every zone participates on both axes. - No dispatch wiring is added in this unit. `build_layout_css` normal path still routes only `topology in {"rows","cols"}` to dynamic sinks; everything else still falls through to `_build_fr_default`. u6/u7 will consume the new constants. ### evidence - `git log -1 --format=%H` → `201099e53b0048f3040e17ee9b0394ba9ad6e940` (Stage 2 baseline confirmed). - `git diff --stat src/phase_z2_pipeline.py` → `1 file changed, 36 insertions(+)` (≤50-line gate ✓; ≤3-file gate ✓ — single file). - `python -c "import ast; ast.parse(open(...).read())"` → `PARSE OK`. - Import-load smoke (without committing): `TOPOLOGY_AXIS_MAP` keys = `['2x2', 'T', 'inverted-T', 'side-T-left', 'side-T-right']`; `_TWO_D_TOPOLOGIES` = same 5 strings as a `frozenset`. - Per-topology position-union vs `templates/phase_z2/layouts/layouts.yaml`: - `T`: union `{top, bottom-left, bottom-right}` = `positions: [top, bottom-left, bottom-right]` ✓ - `inverted-T`: union `{top-left, top-right, bottom}` = `positions: [top-left, top-right, bottom]` ✓ - `side-T-left`: union `{left, right-top, right-bottom}` = `positions: [left, right-top, right-bottom]` ✓ - `side-T-right`: union `{left-top, right, left-bottom}` = `positions: [left-top, right, left-bottom]` ✓ - `2x2`: union `{top-left, top-right, bottom-left, bottom-right}` = `positions: [top-left, top-right, bottom-left, bottom-right]` ✓ - Working-tree sketch stashed pre-unit as `stash@{0}: IMP-09 PR2 sketch (Stage 2 design reference; re-derive per unit)` (preserved for u2–u7 consultation; Stage 3 re-derives from clean HEAD per Stage 1/2 contract). - `src/space_allocator.py` untouched (Kei legacy `build_containers_type_b` invariant preserved at u26). ### scope_lock compliance - ✓ AI=0 normal path (PZ-1) — pure data declaration, no LLM call surface. - ✓ 1 turn = 1 unit (PZ-2) — only u1 executed. - ✓ No hardcoding beyond the 5-preset whitelist that Stage 1 explicitly enumerated. - ✓ No edits to `src/space_allocator.py`. - ✓ No retry-gate behavioral change (this unit does not touch the gate path). - ✓ No new commits / pushes (deliverable = this comment). ### remaining_units `u2, u3, u4, u5, u6, u7, u8, u9, u10, u11, u12, u13, u14, u15, u16, u17, u18, u19, u20, u21, u22, u23, u24, u25, u26` (25 units, total plan = 26) ### follow_up_issue_candidates None — no out-of-scope axis discovered in u1. Stage 2 follow-ups remain as recorded: - `single` topology dynamic promotion (separate PR). - retry_gate extension to 2-D presets beyond skip semantics. - B4 frame_selection evidence integration. - Consumer audit for Step 8 `computation` label change (`"2d_dynamic_aggregated"` / `"user_override_2d"` — introduced later in u6/u7). ### next `continue_same` — proceed to u2 (`_aggregate_axis_zone` helper) in the next turn. Stage 3 not yet complete.