﻿[Codex #2] Stage 3 plan review - round 2

Verification scope: Issue #9 Stage 3 Round 2 plan only. I did not modify files. Current worktree has unrelated dirty/untracked files, ignored for this review.

Verified against actual code:
- `D:\ad-hoc\kei\design_agent\src\phase_z2_pipeline.py`: `build_layout_css()` is still at lines 841-902 for override handling, with only `horizontal-2` h-ratio and `vertical-2` w-ratio branches.
- `D:\ad-hoc\kei\design_agent\src\phase_z2_pipeline.py`: current `debug_zones` height injection is the row-only zip at lines 3067-3070.
- `D:\ad-hoc\kei\design_agent\src\phase_z2_pipeline.py`: Step 8 currently writes only `zone_heights_px_planned` and `zone_ratios_planned` at lines 3255-3256.
- `D:\ad-hoc\kei\design_agent\src\phase_z2_pipeline.py`: retry planning starts after the proposed-action gate returns at line 1322; the callsite is later at line 3746.
- `D:\ad-hoc\kei\design_agent\src\phase_z2_composition.py`: `select_layout_preset(units: list[CompositionUnit])` is at line 709, so Claude's correction of the test signature is valid.
- `D:\ad-hoc\kei\design_agent\templates\phase_z2\layouts\layouts.yaml`: the 8 preset topologies and `css_areas` match the plan's topology list.
- `D:\ad-hoc\kei\design_agent\src\phase_z2_retry.py`: `apply_retry_to_layout_css()` is still rows-only at lines 198-215 and does not mutate `widths_px`, `cols`, `width_ratios`, or `dynamic_cols`.

Blocking findings:

1. The proposed `_compute_per_zone_geometry` 1-D fallback is not safe for PR 1 as written. It maps `heights_px[i]` and `widths_px[i]` by zone index whenever `rows_grid is None`. That is only valid when the relevant track list has the same cardinality as zones. For `horizontal-2`, both zones share one column, so a single `widths_px=[SLIDE_BODY_WIDTH]` would make the bottom zone width `None`. For `vertical-2`, both zones share one row, so a single `heights_px=[SLIDE_BODY_HEIGHT]` would make the right zone height `None`. If the plan instead intends to duplicate full-width/full-height values per zone, that must be stated separately because those duplicated arrays would no longer be pure grid-track arrays. The safer general rule is to parse `css_areas` for all presets, including rows/cols/single, and aggregate tracks per zone consistently.

2. The proposed `_override_to_grid_tracks` snippet does not enforce the strict override contract it claims. It does not check extra override positions outside `css_areas`; it does not enforce spanning consistency such as `top-1-bottom-2` top `w` equaling the total two-column span; and it silently overwrites conflicting non-span track values, for example `bottom-left.h=0.4` and `bottom-right.h=0.6` both write row 1 with the last value winning. This means rule 2 and parts of rules 1/3 are not implemented by the snippet. The plan needs explicit conflict checks before Stage 4.

3. PR scope is internally inconsistent for override migration. The table says `_override_to_grid_tracks` general rule is PR 2, but also says `_override_to_grid_tracks` h/w default `1.0` is PR 1. The text then says PR 1 keeps legacy h-only/w-only branches and PR 2 introduces the general helper. Those cannot all be true. Lock either a PR 1 legacy-only override path or a PR 1 general helper subset, not both.

4. The rollback trigger references `run_visual_check.py`, but I could not find that script under `D:\ad-hoc\kei\design_agent` with `rg`. The actual surface I verified is the pipeline Step 14 `visual_check` artifact and internal visual runtime check path in `src\phase_z2_pipeline.py`. The rollback criterion should name the real command/path that will be run.

Non-blocking confirmations:
- Gap 1, Gap 3, and Gap 4 from Codex #1 are correctly accepted in principle.
- The split between direct 8-preset dynamic tests and `select_layout_preset()` reachable-default tests is a good correction.
- Keeping Step 8 `step_status="complete"` out of scope is consistent with the region-level partial status lock.

Verification commands run:
- `python -m py_compile src\phase_z2_pipeline.py src\phase_z2_composition.py src\phase_z2_retry.py src\space_allocator.py src\pipeline.py` passed.
- `python -m pytest -q tests` passed: 42 passed in 7.05s.
- `rg "def run_visual_check|run_visual_check.py|visual_check" -n scripts src tests` found visual-check references but no `run_visual_check.py`.

Conclusion: I cannot ack the 6 axes yet. The direction still matches Issue #9 and the Phase Z general-pipeline rule, but the plan needs one more revision to make per-zone geometry aggregation and override strictness mathematically consistent before implementation handoff.
FINAL_CONSENSUS: NO
