Files
C.E.L_Slide_test2/.orchestrator/tmp/u4_comment.json
T

4 lines
7.7 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"body": "[Claude #6] Stage 3 Round #4 — IMP-09 PR 2 u4\n\n### unit_executed\n- **id**: `u4`\n- **summary**: Add `_derive_axis_tracks_from_override` single-axis helper (per-group max override; dynamic-solver fallback when all zero)\n\n### files_changed\n- `src/phase_z2_pipeline.py` (+48 / -0 this unit; +172 / -0 cumulative across u1+u2+u3+u4)\n\n### diff_summary\n- Inserted directly after `_build_2d_dynamic` (u3) closing `}` and before the existing `# Layout preset → zone position 순서 …` comment block. 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 (`TOPOLOGY_AXIS_MAP`, `_TWO_D_TOPOLOGIES`), u2 (`_aggregate_axis_zone`), and u3 (`_build_2d_dynamic`) surfaces untouched. `build_layout_css` not modified.\n- Helper signature: `_derive_axis_tracks_from_override(axis_groupings: list[list[str]], override_zone_geometries: dict[str, dict], axis_key: str, total_extent: int, gap: int, dynamic_tracks_px: list[int]) -> dict`.\n- Behavior (axis-agnostic; called once per axis by u5):\n 1. For each grouping in `axis_groupings`, take **MAX** of `geom[axis_key]` (`'h'` for row tracks, `'w'` for col tracks) across positions in that grouping.\n 2. Compute `space = total_extent - gap * max(n - 1, 0)` — matches PR 1 solver convention (`compute_zone_layout` / `compute_zone_layout_cols` both produce tracks that sum to `total_extent - gap * (n-1)`).\n 3. If `sum(per_group_max) <= 0` (all positions missing or zero on this axis): **fall back** to `dynamic_tracks_px` verbatim; ratios re-derived as `track / space`; `override_applied = False`.\n 4. Otherwise: normalize per-group maxes to a ratio vector summing to 1, scale to `space`, and apply rounding fix to the last track to length-lock the sum to `space`. `override_applied = True`.\n- Return contract: `{tracks_px: list[int], ratios: list[float], override_applied: bool}`. u5 (`_override_to_grid_tracks`) will call this helper twice (rows with `axis_key='h'` / `total_extent=SLIDE_BODY_HEIGHT`; cols with `axis_key='w'` / `total_extent=SLIDE_BODY_WIDTH`) and assemble the dual-axis layout_css dict.\n- **Length-locked invariant**: `len(tracks_px) == len(axis_groupings) == len(dynamic_tracks_px)` is guaranteed by construction in both override and fallback branches.\n- **Per-group MAX semantics** match Stage 2 spec — picks the dominant override within a track regardless of how many positions share the track, which is what the 2-D presets need (e.g. `2x2` rows = `[['top-left','top-right'], ['bottom-left','bottom-right']]`).\n- No dispatch wiring is added in this unit. `build_layout_css` normal path still routes only `topology in {\"rows\",\"cols\"}` to dynamic sinks; the 5 in-scope 2-D topologies still fall through to `_build_fr_default`. The override branch still warn-and-fallthroughs for non-{horizontal-2, vertical-2} presets. u5 will build `_override_to_grid_tracks` on top of `_derive_axis_tracks_from_override`; u6/u7 will then wire dispatch.\n\n### evidence\n- `git log -1 --format=%H` → `201099e53b0048f3040e17ee9b0394ba9ad6e940` (Stage 2 baseline preserved; no commit/push per Stage 3 contract).\n- `git diff --stat src/phase_z2_pipeline.py` → `1 file changed, 172 insertions(+)` (cumulative across u1+u2+u3+u4; u4-only delta = 172 124 = **48 lines** ≤ 50-line gate ✓).\n- `git diff --numstat` → `172 0 src/phase_z2_pipeline.py` (single file ≤ 3-file gate ✓; 0 deletions = pure insertion).\n- `git diff --name-only` → `src/phase_z2_pipeline.py` only (no other tracked changes).\n- `git diff --name-only HEAD -- src/space_allocator.py` → empty (Kei legacy invariant preserved ✓).\n- `git diff --check src/phase_z2_pipeline.py` → no whitespace errors.\n- `python -c \"import ast; ast.parse(open('src/phase_z2_pipeline.py').read())\"` → `PARSE OK`.\n- AST top-level function-presence check:\n - `_aggregate_axis_zone`: PRESENT (u2 surface intact)\n - `_build_2d_dynamic`: PRESENT (u3 surface intact)\n - `_derive_axis_tracks_from_override`: PRESENT (u4 added) ✓\n - `_override_to_grid_tracks`: ABSENT (u5 still pending — correct) ✓\n - `build_layout_css`: PRESENT (unmodified)\n- Dispatch-absence grep on `build_layout_css` body:\n - Normal-path branches: only `topology == \"rows\"` → `_build_rows_dynamic`, `topology == \"cols\"` → `_build_cols_dynamic`, else `return _build_fr_default(preset)` ✓\n - Override branch: `[override-warning]` warn-and-fallthrough still in place for non-{horizontal-2, vertical-2} ✓\n - `_derive_axis_tracks_from_override` referenced only at its own `def` line (no consumer yet) ✓\n- Behavior smoke (6 cases; import-load with module on sys.path):\n - **A — override applied, 2 groups, 'h' axis**: groupings `[['top'], ['bottom-left','bottom-right']]`, override `{top:h=0.4, bottom-left:h=0.5, bottom-right:h=0.6}`, total_extent=600, gap=16, dyn=[200,400] → per-group max = [0.4, 0.6]; ratios = [0.4, 0.6]; tracks_px = [234, 350]; sum = 584 = 60016; `override_applied=True` ✓\n - **B — empty override, fallback**: same groupings + `{}` + dyn=[200,400] → tracks_px = [200,400] verbatim; ratios = [0.342, 0.685] (re-derived `t/space`); `override_applied=False` ✓\n - **C — cols axis ('w' key) for 2x2**: groupings `[['top-left','bottom-left'], ['top-right','bottom-right']]`, override `{tl:w=0.3, bl:w=0.2, tr:w=0.7, br:w=0.5}`, total_extent=1200, gap=16, dyn=[600,600] → per-group max = [0.3, 0.7]; ratios = [0.3, 0.7]; tracks_px = [355, 829]; sum = 1184 = 120016 ✓\n - **D — partial missing**: `{top:h=0.6}` only → per-group max = [0.6, 0.0]; total=0.6>0 → `override_applied=True`; tracks_px = [584, 0] (all space to top group) ✓\n - **E — zero on axis_key**: `{top:{w:0.5, h:0.0}}` for h axis → per-group max=[0.0]; total=0 → fallback to dyn=[720]; `override_applied=False` ✓\n - **F — single grouping, override applied**: `[['top']]` + `{top:h=0.7}` + total_extent=720, gap=16, dyn=[720] → n=1, space=720, ratios=[1.0], tracks_px=[720] ✓\n- Integration smoke (composed u3 + u4 as u5 will do): T preset with dyn `heights_px=[288, 283]`, `widths_px=[583, 583]`; empty override → fallback returns dyn verbatim on both axes; user override `{top:h=0.3,w=1.0; bottom-left:h=0.7,w=0.4; bottom-right:h=0.7,w=0.6}` → row=[171, 400] (sum=571=SLIDE_BODY_HEIGHTgap), col=[466, 700] (sum=1166=SLIDE_BODY_WIDTHgap). Length-lock holds on both axes; `override_applied=True` on both axes.\n\n### remaining_units\n- `[u5, u6, u7, u8, u9, u10, u11, u12, u13, u14, u15, u16, u17, u18, u19, u20, u21, u22, u23, u24, u25, u26]` (22 units left)\n- **Next**: `u5` — add `_override_to_grid_tracks` (calls `_derive_axis_tracks_from_override` twice for rows and cols; consumes `_build_2d_dynamic`'s `raw_zone_layout` row/col `heights_px`/`widths_px` as the dynamic-fallback values).\n\n### follow_up_issue_candidates\n- None this unit. All work in u4 is within Stage 1/Stage 2 scope-lock. The Step 8 `computation` label audit for the new label values (`\"2d_dynamic_aggregated\"` already landed in u3; `\"user_override_2d\"` will be introduced by u5/u7) remains a Stage 2 follow_up_candidate — not introduced by u4.\n\n### scope_lock honored\n- Only `src/phase_z2_pipeline.py` touched (single file, single new top-level helper).\n- `src/space_allocator.py` untouched (Kei legacy `build_containers_type_b` invariant).\n- No dispatch wiring (`build_layout_css` unchanged); pure helper addition, consumed by u5 next round.\n- PR 1 surfaces (`_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.\n- u1/u2/u3 surfaces untouched.\n\nrewind_target: continue_same"
}