This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
Executing unit: u2
|
||||
|
||||
[Claude #4] Stage 3 Round #2 — IMP-09 PR 2 u2
|
||||
|
||||
### unit_executed
|
||||
- **id**: `u2`
|
||||
- **summary**: Add `_aggregate_axis_zone` helper (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 both `compute_zone_layout` row solver and `compute_zone_layout_cols` col 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 trace `raw_zone_layout` for explainability.
|
||||
- Missing-position guard: raises `ValueError` listing the absent positions and the actual position keys present in `zones_data` — composition planning must declare every zone referenced by `TOPOLOGY_AXIS_MAP[topology][axis]`.
|
||||
- Default fallback: members without `min_height_px` use `DEFAULT_ZONE_MIN_HEIGHT_PX` (module constant at line 120 — same fallback `compute_zone_layout` itself uses at line 791).
|
||||
- No dispatch wiring added. `build_layout_css` normal path still routes only `topology 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` → returns `DEFAULT_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 ...)` ✓.
|
||||
- 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)` ✓.
|
||||
- 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.
|
||||
Reference in New Issue
Block a user