Executing unit: u1 [Claude #3] Stage 3 Round #1 — u1 (backend axis 1) **unit_executed**: `u1` **files_changed**: - `src/phase_z2_pipeline.py` **diff_summary**: - Inserted a new deterministic loop above `application_plan_units.append({...})` (line 3972~3988) that builds `v4_all_judgments_list` for the current unit. Each entry now carries `min_height_px` alongside the existing `catalog_registered` field. Lookup is bound once per candidate (`_contract = get_contract(c.template_id)`), so `catalog_registered` and `min_height_px` share the same contract read (no extra cost vs prior single call). - `min_height_px` source = `(_contract or {}).get("visual_hints", {}).get("min_height_px")` → matches the zone-scope pattern at `src/phase_z2_pipeline.py:3771` and the catalog field at `templates/phase_z2/catalog/frame_contracts.yaml:40,89,218,280,351,417,481,544,601,668`. Returns `None` when the contract is unregistered or when `visual_hints` is absent → frontend tolerates `undefined`. - Replaced the original inline list comprehension at the dict literal (`"v4_all_judgments": [...]`) with `"v4_all_judgments": v4_all_judgments_list,` so the dict literal stays clean and the lookup happens exactly once per candidate. Comment block above the field updated to mention IMP-11 D-2 (u1) addition (`per-candidate min_height_px added (None when unregistered)`). - Additive only — no field rename, no removal, no semantic change to existing keys (`template_id` / `frame_id` / `frame_number` / `v4_rank` / `confidence` / `label` / `catalog_registered` unchanged in shape and value). Other Step 9 fields untouched. Zone-scope `zone_region_plans[].min_height_px` (line 3771) untouched. `v4_candidates[]` (non-reject path, line 4008~4018) untouched — minHeightPx will be `undefined` on legacy path per Stage 1 scope_lock guardrail. - No AI call added. No catalog mutation. No new import. `get_contract` already imported (line 53). **scope_check**: - Stage 1 axis_1_backend_field: ✅ `min_height_px` added to each v4_all_judgments entry. - Stage 1 guardrail "reuse same lookup (no extra cost)": ✅ single `_contract` binding per candidate, used twice in dict literal. - Stage 1 guardrail "graceful when contract missing": ✅ `(_contract or {})` short-circuit returns `None`, never raises. - Stage 1 guardrail "no catalog write": ✅ `get_contract` is read-only. - Stage 1 out_of_scope "Step 8 zone_region_plans output": ✅ untouched (still at line 3771). - Stage 2 estimate_lines: 10 → actual diff = +17 / −12 (net +5) ≈ aligned; minor overhead from loop expansion vs comprehension for readability and single-lookup. **remaining_units**: `u2`, `u3`, `u4`, `u5` **follow_up_issue_candidates**: none discovered. All scope confined to Stage 1 axis_1_backend_field. **rewind_target**: `continue_same` (u1 complete; u2~u5 remain per Stage 2 plan) === EVIDENCE === - Edited file: `src/phase_z2_pipeline.py:3972-3988` (new `v4_all_judgments_list` builder) and `:4025-4026` (dict literal now references the prebuilt list). - Source of `min_height_px`: `templates/phase_z2/catalog/frame_contracts.yaml:40` (`visual_hints.min_height_px: 230`) and 9 other contracts at lines 89/218/280/351/417/481/544/601/668. - `get_contract` signature: `src/phase_z2_mapper.py:56-57` → returns `dict | None`. - Zone-scope precedent confirming `.get("visual_hints", {}).get("min_height_px")` shape: `src/phase_z2_pipeline.py:3771`. - No new imports required; `get_contract` already in scope at `src/phase_z2_pipeline.py:53`. - No commits or pushes performed per Stage 3 directive ("DO NOT commit or push"). STOP — awaiting Codex review for u1.