5.0 KiB
5.0 KiB
Executing unit: u2
[Claude #4] Stage 3 Round #2 — IMP-09 PR 2 u2
unit_executed
- id:
u2 - summary: Add
_aggregate_axis_zonehelper (combine zones in same row/col track into synthetic zone for PR 1 solvers)
files_changed
src/phase_z2_pipeline.py(+44 / -0)
diff_summary
- Inserted directly after the
_TWO_D_TOPOLOGIES = frozenset(...)line that u1 added, and before the existing# Layout preset → zone position 순서 ...comment (HEAD line 1152 → working-tree line 1196). u1 surface and PR 1 helpers untouched. - Helper signature:
_aggregate_axis_zone(positions: list[str], zones_data: list[dict]) -> dict. - Aggregation rules (verified against PR 1 solver contracts):
content_weight.score= SUM of member scores (more content in a track = larger share of available space — matches the proportional distribution in bothcompute_zone_layoutrow solver andcompute_zone_layout_colscol solver).min_height_px= MAX across members (row track must accommodate the tallest side-by-side member min; the cols solver does not consume this field but the value is harmless on that path).position/template_id="+".join(...)of member ids — surfaces composite ("bottom-left+bottom-right", "tB+tC") in Step 8 traceraw_zone_layoutfor explainability.
- Missing-position guard: raises
ValueErrorlisting the absent positions and the actual position keys present inzones_data— composition planning must declare every zone referenced byTOPOLOGY_AXIS_MAP[topology][axis]. - Default fallback: members without
min_height_pxuseDEFAULT_ZONE_MIN_HEIGHT_PX(module constant at line 120 — same fallbackcompute_zone_layoutitself uses at line 791). - No dispatch wiring added.
build_layout_cssnormal path still routes onlytopology in {"rows","cols"}to dynamic sinks; all 2-D topologies still fall through to_build_fr_default(computation == "fr_default_from_preset",dynamic_rows == False,dynamic_cols == False). u3 will add_build_2d_dynamic(the consumer) and u6 will wire dispatch.
evidence
git log -1 --format=%H→201099e53b0048f3040e17ee9b0394ba9ad6e940(Stage 2 baseline preserved; no commit/push this turn per Stage 3 contract).git diff --stat src/phase_z2_pipeline.py→1 file changed, 80 insertions(+)(cumulative across u1 + u2; u2-only delta = 80 − 36 = 44 lines ≤ 50-line gate ✓).git diff --numstat→80 0 src/phase_z2_pipeline.py(single file ≤ 3-file gate ✓; 0 deletions = pure insertion).git diff --check src/phase_z2_pipeline.py→ no whitespace errors.git diff --name-only HEAD -- src/space_allocator.py→ empty (Kei legacy invariant preserved ✓).python -c "import ast; ast.parse(...)"→PARSE OK.- Behavior smoke (import-load with module on sys.path):
- single-position track
_aggregate_axis_zone(['top'], zones)→{'position': 'top', 'template_id': 'tA', 'content_weight': {'score': 4.0}, 'min_height_px': 200}✓ (verbatim passthrough of member zone shape). - multi-position track
_aggregate_axis_zone(['bottom-left','bottom-right'], zones)→{'position': 'bottom-left+bottom-right', 'template_id': 'tB+tC', 'content_weight': {'score': 5.0}, 'min_height_px': 250}✓ (sum=2+3=5; max=max(150,250)=250). - default min_height fallback when member lacks
min_height_px→ returnsDEFAULT_ZONE_MIN_HEIGHT_PX(100) when member-with-min < default ✓. - missing-position guard
_aggregate_axis_zone(['nope'], zones)→ValueError: _aggregate_axis_zone: positions ['nope'] absent from zones_data (have ...)✓.
- single-position track
- PR 1 solver compatibility (T topology with
TOPOLOGY_AXIS_MAP["T"]from u1):- row synthesis:
[_aggregate_axis_zone(g, zones) for g in TOPOLOGY_AXIS_MAP["T"]["rows"]]→compute_zone_layout(...)→heights_px = [254, 317],sum = 571 == SLIDE_BODY_HEIGHT - GRID_GAP (585 - 14 = 571)✓. - col synthesis:
[_aggregate_axis_zone(g, zones) for g in TOPOLOGY_AXIS_MAP["T"]["cols"]]→compute_zone_layout_cols(...)→widths_px = [466, 700],sum = 1166 == SLIDE_BODY_WIDTH - GRID_GAP (1180 - 14 = 1166)✓.
- row synthesis:
- Scope-lock no-creep check:
hasattr(p, '_build_2d_dynamic')→False(u3) ✓hasattr(p, '_derive_axis_tracks_from_override')→False(u4) ✓hasattr(p, '_override_to_grid_tracks')→False(u5) ✓build_layout_css('top-1-bottom-2', ...)["computation"]→'fr_default_from_preset'(dispatch still falls through; u6 will route) ✓.
remaining_units
u3, u4, u5, u6, u7, u8, u9, u10, u11, u12, u13, u14, u15, u16, u17, u18, u19, u20, u21, u22, u23, u24, u25, u26
follow_up_issue_candidates
None this turn. No new axis surfaced — _aggregate_axis_zone matches the Stage 2 plan and reuses existing PR 1 solver contracts (no min_width_px needed; compute_zone_layout_cols is weight-only). Composite position keys for synthetic zones ("top-left+top-right" style) flow into raw_zone_layout trace explainability; Step 8 computation label change to "2d_dynamic_aggregated" is scheduled for u3 (sink) — not introduced here.