feat(IMP-09): PR 2 — 2-D dynamic dispatch for 5 preset families

Stage 3 lock implementation: extend build_layout_css dispatch beyond
the horizontal-2 / vertical-2 1-D dynamic paths. T / inverted-T /
side-T-left / side-T-right / 2x2 now flow through a 2-D track solver
instead of the fr_default sink, with length-locked heights_px (R) +
widths_px (C) on every return path (default and override).

PR 2 scope (u1~u5):
  - u1: _aggregate_zone_signals_per_track — per-row + per-col virtual
    zones via max(weight) + max(min_height_px) of single-span zones,
    falling back to all-span when a track has none.
  - u2: _build_grid_dynamic_2d default builder — feeds virtual zones
    into compute_zone_layout + compute_zone_layout_cols; emits
    computation="2d_dynamic_aggregated", dynamic_rows=True,
    dynamic_cols=True.
  - u3: _override_to_grid_tracks override builder — single-span
    aggregation (max h per row, max w per col), normalize, multiply
    by avail_h/avail_w, last-element diff absorb; emits
    computation="user_override_geometry"; falls back to u2 when
    total_h or total_w == 0.
  - u4: build_layout_css dispatcher wiring — topology in
    {T, inverted-T, side-T-left, side-T-right, 2x2} routes to
    _build_grid_dynamic_2d (default) or _override_to_grid_tracks
    (override); legacy [override-warning] stderr removed for the
    5 presets; step08 trace gains a 2-D-aware print line that fires
    before the dynamic_rows / dynamic_cols branches.
  - u5: PR 1 lock test test_top_1_bottom_2_fr_default_populates_geometry
    renamed to test_top_1_bottom_2_dynamic_2d_populates_geometry and
    flipped to PR 2 reality (computation="2d_dynamic_aggregated",
    dynamic_rows=True, dynamic_cols=True).

Fixtures: 10 build_layout_css (5 presets × {default, override}) +
5 retry_gate *_dynamic_2d.yaml locking the retry gate skip reason
"dynamic_cols (2-D topology) ... IMP-09 lock" for the 5 presets.

Tests: python -m pytest -q tests = 104 passed (Stage 2 baseline
10 RED → GREEN, 0 regressions). Kei archive
(build_containers_type_b / page_structure) untouched —
rg "build_containers_type_b|page_structure" src/phase_z2_pipeline.py
returns 0 hits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-17 18:51:23 +09:00
parent 201099e53b
commit 1fb973297f
17 changed files with 825 additions and 14 deletions

View File

@@ -0,0 +1,24 @@
case_id: grid-2x2_dynamic_2d
description: |
grid-2x2 (2x2 topology) is promoted to 2-D dynamic in IMP-09 PR 2.
Row-axis retry MUST be skipped by the gate with the
"dynamic_cols (2-D topology)" reason.
input_layout_css:
areas: '"top-left top-right" "bottom-left bottom-right"'
cols: 583px 583px
rows: 286px 285px
heights_px: [286, 285]
widths_px: [583, 583]
ratios: [0.489, 0.487]
width_ratios: [0.494, 0.494]
dynamic_rows: true
dynamic_cols: true
router_decision:
router_active: true
proposed_actions_summary: [zone_ratio_retry]
expected_gate:
retry_attempted: false
retry_skipped_reason_contains:
- "dynamic_cols"
- "2-D"
- "IMP-09"

View File

@@ -0,0 +1,24 @@
case_id: left-1-right-2_dynamic_2d
description: |
left-1-right-2 (side-T-left topology) is promoted to 2-D dynamic in
IMP-09 PR 2. Row-axis retry MUST be skipped by the gate with the
"dynamic_cols (2-D topology)" reason.
input_layout_css:
areas: '"left right-top" "left right-bottom"'
cols: 777px 389px
rows: 286px 285px
heights_px: [286, 285]
widths_px: [777, 389]
ratios: [0.489, 0.487]
width_ratios: [0.658, 0.33]
dynamic_rows: true
dynamic_cols: true
router_decision:
router_active: true
proposed_actions_summary: [zone_ratio_retry]
expected_gate:
retry_attempted: false
retry_skipped_reason_contains:
- "dynamic_cols"
- "2-D"
- "IMP-09"

View File

@@ -0,0 +1,24 @@
case_id: left-2-right-1_dynamic_2d
description: |
left-2-right-1 (side-T-right topology) is promoted to 2-D dynamic in
IMP-09 PR 2. Row-axis retry MUST be skipped by the gate with the
"dynamic_cols (2-D topology)" reason.
input_layout_css:
areas: '"left-top right" "left-bottom right"'
cols: 389px 777px
rows: 286px 285px
heights_px: [286, 285]
widths_px: [389, 777]
ratios: [0.489, 0.487]
width_ratios: [0.33, 0.658]
dynamic_rows: true
dynamic_cols: true
router_decision:
router_active: true
proposed_actions_summary: [zone_ratio_retry]
expected_gate:
retry_attempted: false
retry_skipped_reason_contains:
- "dynamic_cols"
- "2-D"
- "IMP-09"

View File

@@ -0,0 +1,26 @@
case_id: top-1-bottom-2_dynamic_2d
description: |
top-1-bottom-2 (T topology) is promoted to 2-D dynamic in IMP-09
PR 2 (dynamic_rows=True, dynamic_cols=True). Row-axis retry MUST be
skipped by the IMP-09 gate with the "dynamic_cols (2-D topology)"
skip reason, because row-only redistribution cannot reconcile both
axes simultaneously.
input_layout_css:
areas: '"top top" "bottom-left bottom-right"'
cols: 583px 583px
rows: 314px 257px
heights_px: [314, 257]
widths_px: [583, 583]
ratios: [0.537, 0.439]
width_ratios: [0.494, 0.494]
dynamic_rows: true
dynamic_cols: true
router_decision:
router_active: true
proposed_actions_summary: [zone_ratio_retry]
expected_gate:
retry_attempted: false
retry_skipped_reason_contains:
- "dynamic_cols"
- "2-D"
- "IMP-09"

View File

@@ -0,0 +1,24 @@
case_id: top-2-bottom-1_dynamic_2d
description: |
top-2-bottom-1 (inverted-T topology) is promoted to 2-D dynamic in
IMP-09 PR 2. Row-axis retry MUST be skipped by the gate with the
"dynamic_cols (2-D topology)" reason.
input_layout_css:
areas: '"top-left top-right" "bottom bottom"'
cols: 583px 583px
rows: 257px 314px
heights_px: [257, 314]
widths_px: [583, 583]
ratios: [0.439, 0.537]
width_ratios: [0.494, 0.494]
dynamic_rows: true
dynamic_cols: true
router_decision:
router_active: true
proposed_actions_summary: [zone_ratio_retry]
expected_gate:
retry_attempted: false
retry_skipped_reason_contains:
- "dynamic_cols"
- "2-D"
- "IMP-09"