IMP-34 zone resize + compact retry (overflow chain step 1) #63

Closed
opened 2026-05-21 10:13:53 +09:00 by Kyeongmin · 26 comments
Owner

관련 step: Step 16/17 — overflow retry chain 의 첫 단계
source: #44 axis 7 (frame_swap / popup escalation) — 정정: frame_swap 자동화 폐기, zone resize 로 재정의
roadmap axis: R1 (22 단계 안정화)
wave: 1
priority: 높음
dependency: #12 (IMP-12 Step 16/17 retry 정밀화) verified, _attempt_zone_ratio_retry 패턴

scope:

  • visual_check overflow 감지 → 전체 zone 공란 측정 → overflow zone 확장 → 재배치 → revert on fail
  • 호출 위치: src/phase_z2_retry.py (_attempt_zone_ratio_retry 확장)
  • 측정: 모든 zone 의 measured height vs allocated height → 공란 zone 식별
  • 확장: overflow zone 의 allocated height 늘림 (donor zone 의 공란 차감, budget=1)
  • Revert: rerender 시 overflow 지속 → 원래 ratio 복원
  • 성공 시: 다음 overflow chain step (IMP-35 popup) 호출 안 함

out of scope (별 IMP):

  • responsive fit (cqh/clamp) → IMP-36 (partial 자체 대응)
  • popup escalation → IMP-35
  • AI 재구성 → IMP-33

guardrail / validation:

  • ★ 자동 frame_swap 금지 (V4 rank 1 보호) — frame 자체 교체 X
  • ★ 공통 여백 / padding / gap / line-height 강제 축소 금지 (feedback_phase_z_spacing_direction)
  • 방향: 줄이는 게 아니라 늘리거나 다른 fit 으로 옮기는 방향 only
  • no-hardcoding: sample-specific budget 분기 X
  • dropped 절대 룰 부합 (콘텐츠 삭제 X)
  • 실패 시 revert (idempotent)

cross-ref:

  • source: #44 axis 7 (정정)
  • depend: #12, _attempt_zone_ratio_retry
  • chain partner: IMP-35 (다음 step), IMP-36 (partial 자체 fit)
  • consumer: visual_check (Step 14)

review loop:

  • Codex 1차 review
  • Claude 재검토
  • Codex 재검증
  • scope-locked
  • ready-for-implementation
  • implemented
  • verified
**관련 step**: Step 16/17 — overflow retry chain 의 첫 단계 **source**: #44 axis 7 (frame_swap / popup escalation) — **정정**: frame_swap 자동화 폐기, zone resize 로 재정의 **roadmap axis**: R1 (22 단계 안정화) **wave**: 1 **priority**: 높음 **dependency**: #12 (IMP-12 Step 16/17 retry 정밀화) verified, `_attempt_zone_ratio_retry` 패턴 **scope**: - visual_check overflow 감지 → **전체 zone 공란 측정 → overflow zone 확장 → 재배치 → revert on fail** - 호출 위치: `src/phase_z2_retry.py` (`_attempt_zone_ratio_retry` 확장) - 측정: 모든 zone 의 measured height vs allocated height → 공란 zone 식별 - 확장: overflow zone 의 allocated height 늘림 (donor zone 의 공란 차감, budget=1) - Revert: rerender 시 overflow 지속 → 원래 ratio 복원 - 성공 시: 다음 overflow chain step (IMP-35 popup) 호출 안 함 **out of scope** (별 IMP): - responsive fit (cqh/clamp) → IMP-36 (partial 자체 대응) - popup escalation → IMP-35 - AI 재구성 → IMP-33 **guardrail / validation**: - ★ 자동 frame_swap 금지 (V4 rank 1 보호) — frame 자체 교체 X - ★ 공통 여백 / padding / gap / line-height 강제 축소 금지 (`feedback_phase_z_spacing_direction`) - 방향: 줄이는 게 아니라 **늘리거나 다른 fit 으로 옮기는** 방향 only - no-hardcoding: sample-specific budget 분기 X - dropped 절대 룰 부합 (콘텐츠 삭제 X) - 실패 시 revert (idempotent) **cross-ref**: - source: #44 axis 7 (정정) - depend: #12, `_attempt_zone_ratio_retry` - chain partner: IMP-35 (다음 step), IMP-36 (partial 자체 fit) - consumer: visual_check (Step 14) **review loop**: - [ ] Codex 1차 review - [ ] Claude 재검토 - [ ] Codex 재검증 - [ ] scope-locked - [ ] ready-for-implementation - [ ] implemented - [ ] verified
Author
Owner

[Claude #1] Stage 1 — IMP-34 problem review (overflow chain step 1 — zone resize + compact retry)

Root-cause framing

IMP-34 is the first deterministic step of the overflow remediation chain (Step 16 → 17). The chain shape (post-IMP-12) is:

zone_ratio_retry        ← IMP-34 axis (this issue)
   ↓ fail
cross_zone_redistribute ← IMP-12 u4 / u8
   ↓ fail
glue_compression        ← IMP-12 u5
   ↓ fail
font_step_compression   ← IMP-12 u6
   ↓ fail
layout_adjust / frame_reselect / details_popup_escalation (IMP-35) — missing

Today, plan_zone_ratio_retry (src/phase_z2_retry.py:43-215) selects donor zones by static contract slack only: slack = current_height_px − min_height_px (line 127). This is a contract signal — it says "how far can we shrink this zone before violating its visual_hints.min_height_px" — and ignores whether the zone actually has empty space rendered. A donor whose clientHeight=200, min_height_px=120, but whose internal content already takes scrollHeight=195 will be picked with slack=80, the plan will take 80 from it, and after rerender the donor itself overflows by 195 − 120 = 75. The candidate fails visual_check, the orchestrator reverts (correct), and the cascade falls through to cross_zone_redistribute unnecessarily — burning a Selenium rerender on a donor we could have rejected up-front from data already in overflow["zones"][i].

The issue body reframes axis 7 of #44 (originally "frame_swap / popup escalation", corrected: frame_swap forbidden — V4 rank-1 protection) to zone resize. The concrete enhancement is: donor capacity must be bounded by measured empty space (clientHeight − scrollHeight), not just static contract slack. The measured signal is already produced by the Selenium JS at src/phase_z2_pipeline.py:2305-2314 and surfaced into overflow["zones"][i] as clientHeight / scrollHeight / excess_y (line 2393 pushes the full m dict) — it is read by _attempt_salvage_chain for cross_zone_redistribute synthesis (src/phase_z2_pipeline.py:5117-5130) but is not read by plan_zone_ratio_retry.

Verified facts (value + path + upstream)

  • HEAD = 15ef7c65e9f58637e4b4714afb9546c176ceaaa8; branch = main.
  • Source insight #44 axis 7 — [issue body]: "frame_swap / popup escalation — 정정: frame_swap 자동화 폐기, zone resize 로 재정의". Confirms automatic frame swap forbidden (V4 rank-1 protection memory: feedback_partial_figma_audit + project_mdx03_frame_lock Axis A zone lock).
  • Dependency #12 verified — IMP-12 u1 (multi-donor greedy) + u3 (failure router cascade) + u4~u9 (cross-zone redistribute / glue / font-step salvage chain) are live. Tests: tests/phase_z2/test_phase_z2_retry_multi_donor.py, test_phase_z2_failure_router_cascade.py, test_phase_z2_step17_salvage_chain.py.
  • Status board — docs/architecture/PHASE-Z-PIPELINE-STATUS-BOARD.md:56 confirms Step 17 = ⚠ partial (zone_ratio_retry only). Missing actions at line 95-96 = layout_adjust / frame_reselect / details_popup_escalation / image_fit / frame_internal_fit_candidate. IMP-34 is not a new action label — it enriches the existing zone_ratio_retry.
  • IMP-17 carve-out — docs/architecture/IMP-17-CARVE-OUT.md:3 explicitly names IMP-34 + IMP-35 as the structural prerequisites for unblocking Step 17 AI fallback (step17_ai_blocked_imp_34_35_prerequisites_missing, file line 52). Confirms IMP-34 sits before any AI escalation.
  • Donor-selection lock — src/phase_z2_retry.py:99-137 (loop) + :127 (slack = height − min_h) + :140 (sort by static slack). Multi-donor greedy aggregation already present at :165-203. No measured-empty signal consulted.
  • Measured empty available — src/phase_z2_pipeline.py:2305-2314 (measure(el) JS: clientHeight / scrollHeight / excess_y / overflowed) + :2336-2393 (per-zone push of m, including clipped_inner). overflow["zones"][i] already carries clientHeight / scrollHeightplan_zone_ratio_retry:95-97 reads z whole, then only consults overflowed / clipped_inner and discards the geometry.
  • Cascade trigger contract — src/phase_z2_pipeline.py:5103-5107 cascades on _ft ∈ {donor_slack_insufficient, no_donor_candidates}. Substring contract for donor_slack_insufficient classification = src/phase_z2_failure_router.py:205-217 ("primary donor" AND "slack" AND "target_added_px" in failure_reason). IMP-34 must preserve this substring contract or update both sides atomically.
  • Backward-compat surface — existing test fixtures (tests/phase_z2/test_phase_z2_retry_multi_donor.py:46-52, _overflow_clean) emit zones without clientHeight / scrollHeight keys. New donor logic must degrade gracefully (treat absent measured fields as "no measured cap", fall back to current static-slack-only behavior) to keep IMP-12 tests green.
  • Revert + chain-exit contract — already in place from IMP-12:
    • Rerender visual fail → revert to original final.html, candidate stays as retried_candidate.html diagnostic (src/phase_z2_pipeline.py:2070-2080).
    • retry_passed=True → classify_retry_failure returns None (src/phase_z2_failure_router.py:163) → cascade not invoked → IMP-35 popup (when it lands) also not invoked. IMP-34 inherits this without change.
  • Guardrail spec — docs/architecture/IMP-17-CARVE-OUT.md:19-23 (no MDX shrink / no common spacing shrink / no HTML/CSS direct gen / no new IMP IDs). Aligned with memory feedback_phase_z_spacing_direction (clipping → enlarge or relocate, never shrink) and feedback_ai_isolation_contract (AI = fallback path only, normal path AI=0).

Scope-lock — SCOPED to a single internal enhancement of plan_zone_ratio_retry

The current donor identification computes slack from contract min_height_px alone. IMP-34 adds measured empty space as a stricter upper bound on per-donor take, preserving all other plan/apply/revert semantics. No new action label, no failure_router schema change, no orchestrator restructure, no AI call.

Files to touch (1 unit, code edit):

  • src/phase_z2_retry.py:99-215plan_zone_ratio_retry donor loop:
    1. Add per-donor measured_empty_px = max(0, clientHeight − scrollHeight) derived from overflow["zones"][pos] (graceful absence → None).
    2. Effective per-donor cap: effective_slack = min(measured_empty_px, current − min_height_px) when measured_empty_px available; otherwise effective_slack = current − min_height_px (current behavior).
    3. Sort donor candidates by effective_slack desc (was: static slack desc).
    4. Multi-donor greedy aggregation uses effective_slack (was: static slack).
    5. Failure reason for aggregate-insufficient case keeps the same substring pattern ("primary donor … slack … target_added_px") so failure_router.classify_retry_failure (line 205-217) keeps routing to donor_slack_insufficient → cross_zone_redistribute. The numeric values change; the substring contract does not.
    6. Plan trace adds per-candidate measured_empty_px + effective_slack_px for debug.

Files NOT touched in this unit:

  • src/phase_z2_pipeline.py _attempt_zone_ratio_retry (orchestrator) — unchanged. Reads same plan dict shape.
  • src/phase_z2_failure_router.py — unchanged. Substring contract preserved.
  • src/phase_z2_pipeline.py _attempt_salvage_chain and cascade trigger — unchanged.
  • Frontend / debug.json schema — additive only (new fields under existing plan.donor_candidates_considered[i] and per-plan).

Tests (added in same commit, scope-locked):

  • New tests/phase_z2/test_phase_z2_retry_measured_empty.py (or appended cases to test_phase_z2_retry_multi_donor.py) — synthetic fixtures only (no MDX sample reference, per tests/CLAUDE.md §"테스트 픽스처 컨벤션"):
    • Case A — donor with measured_empty < static_slack: take limited to measured_empty; if aggregate (across all donors) effective_slack covers target_added_px → feasible; else donor_slack_insufficient.
    • Case B — donor with measured_empty = 0 (already filled): excluded from donors; if remaining aggregate insufficient → donor_slack_insufficient.
    • Case C — regression baseline: existing test fixtures without clientHeight/scrollHeight → behavior identical to pre-IMP-34 (static-slack path).
    • Case D — donor with measured_empty > static_slack (rare — content rendered smaller than min_height envelope): effective_slack = static_slack (min); plan unchanged from current behavior.
  • Existing test_phase_z2_retry_multi_donor.py + test_phase_z2_failure_router_cascade.py + test_phase_z2_step17_salvage_chain.py must stay green (regression).
  • Run: pytest -q tests/phase_z2/.

Out of scope (explicit boundaries)

  • IMP-35 — details_popup_escalation (overflow chain step 2). Separate issue. IMP-34 success simply means cascade exits before reaching IMP-35; IMP-34 does not import / wire / reference popup escalation.
  • IMP-36 — partial responsive fit (cqh / clamp). Separate axis (frame-internal CSS), not zone-level redistribution.
  • IMP-33 — AI restructure (fallback path). Carve-out, gated behind IMP-34 + IMP-35 + B4 evidence integration + user GO (per IMP-17-CARVE-OUT.md:25-31). IMP-34 does NOT touch src/phase_z2_ai_fallback/.
  • No automatic frame swap — V4 rank-1 selection is locked; donor selection operates only on zone height_px allocation. Frame identity preserved.
  • No common spacing / padding / gap / line-height shrinkfeedback_phase_z_spacing_direction lock. Direction is expand target / relocate from donor's measured empty, never shrink shared envelopes.
  • No layout topology changelayout_adjust is a separate cascade tail action (Step 17 missing actions); not in IMP-34.
  • No failure_router schema changedonor_slack_insufficient keeps its name and cascade target (cross_zone_redistribute).
  • No MDX sample fixture — synthetic test fixtures only (RULE 0 + RULE 7).
  • No retry budget change — budget=1 preserved (locked rule 1 in src/phase_z2_retry.py:6-8).
  • No new IMP ID — enhancement fits inside IMP-34 slot.

Guardrails

  • G1 (PZ-1, RULE 0): AI = 0 normal path. Plan logic is pure arithmetic over debug_zones + overflow["zones"].
  • G2 (RULE 7, no-hardcoding): no MDX-sample-specific branching; donor logic operates on contract min_height_px + measured clientHeight/scrollHeight only. No "if template_id == 'mdx03'" anywhere.
  • G3 (RULE 0 + memory project_mdx03_frame_lock Axis A): frame identity preserved — no automatic frame swap. Plan changes heights_px only.
  • G4 (feedback_phase_z_spacing_direction): direction is target_added_px > 0 AND donor_reduced_px ≤ donor's measured_empty. Never shrinks common spacing / padding / line-height / gap.
  • G5 (revert idempotent): rerender visual fail → original final.html preserved (already in place at src/phase_z2_pipeline.py:2070-2080). IMP-34 does not change this.
  • G6 (chain-exit on success): retry_passed=True → classify_retry_failure returns None → cascade not invoked → IMP-35 popup (when it lands) also not invoked. Inherited from IMP-12.
  • G7 (failure_router substring contract): failure_reason for donor_slack_insufficient keeps the substring "primary donor … slack … target_added_px" so src/phase_z2_failure_router.py:205-217 keeps routing. Numeric values change; the pattern doesn't.
  • G8 (backward compat): clientHeight / scrollHeight absent in overflow["zones"] → fall back to static-slack-only behavior. Existing IMP-12 tests stay green without fixture edits.
  • G9 (PZ-2 / feedback_one_step_per_turn): Stage 1 closes here. No Stage 2 unit decomposition. No pre-staging of IMP-35.
  • G10 (feedback_workflow_atomicity_rules): 1 commit = 1 decision unit = plan_zone_ratio_retry enrichment + tests. No bundling other axes (e.g., debug.json schema separate, status board update separate).
  • G11 (RULE 13 anchor sync): in commit-push stage, docs/architecture/PHASE-Z-PIPELINE-STATUS-BOARD.md:56 may update Step 17 line to reflect IMP-34 landed (still ⚠ partial since layout_adjust / frame_reselect / image_fit / frame_internal_fit_candidate remain). Anchor SHA discipline applies.

Risk

Low — additive enrichment on an already-locked plan path; primary failure routing contract preserved by design.

  • (a) Substring contract drift — failure_reason wording change breaks failure_router.classify_retry_failure substring match → cascade misroutes to wrong branch. Mitigation: Stage 2 plan must spell out the exact failure_reason template (English: "primary donor … slack … target_added_px" substring preserved) + regression test test_phase_z2_failure_router_cascade.py already exercises this. Rewind: stage_3_edit.
  • (b) Backward-compat regression — existing IMP-12 fixtures (_overflow_clean without clientHeight) misclassify donor → pytest -q tests/phase_z2/ red. Mitigation: G8 graceful-absence rule + Case C regression test. Rewind: stage_3_edit.
  • (c) Donor pessimism — over-strict cap (measured_empty=0 excludes everything) → no_donor_candidates for cases that previously succeeded. Correct behavior — the previous "success" was misleading (donor would have overflowed post-take), and no_donor_candidates already routes to cross_zone_redistribute (same as donor_slack_insufficient, per failure_router.py:95-96). No regression — just earlier cascade entry. Stage 2 plan should explicitly note this routing equivalence.
  • (d) Hidden coupling — _attempt_salvage_chain synthesizes FitAnalysis from debug_zones + overflow (pipeline :5117-5130); if measured_empty signal needs propagating into that synthesis too, scope creeps. Verified out of scope: _attempt_salvage_chain already reads scrollHeight - clientHeight directly for its own role-shortfall computation; it does not depend on plan output. No coupling.

Open questions for Codex r1

  1. Backward-compat default (G8) — when overflow["zones"][i] omits clientHeight / scrollHeight (existing IMP-12 tests), should the donor logic (a) fall back to static-slack-only [recommended — preserves all IMP-12 tests as-is], (b) treat as measured_empty=0 (strict — would require fixture edits to IMP-12 tests), or (c) emit a warning + use static slack? Recommend (a).
  2. Aggregation contract — multi-donor greedy aggregation currently sums static slack (src/phase_z2_retry.py:166). Should the aggregate sum the new effective_slack (= min(measured_empty, static_slack)) per donor [recommended — matches semantic intent], or sum measured_empty separately and check both gates? Recommend single effective_slack aggregation.
  3. Trace field naming — proposed per-candidate fields: measured_empty_px, effective_slack_px. Plan-level addition: donor_candidates_considered[i] already exists. Suggest also adding plan-level aggregate_effective_slack_available parallel to existing aggregate_slack_available. Confirm naming.
  4. Substring contractfailure_router.classify_retry_failure (line 205-217) matches on "primary donor" AND "slack" AND "target_added_px". Preserved literal strings? Or do we want to switch to a structured failure_code field (longer rewrite, separate axis)? Recommend preserve strings in this unit; structured failure_code migration as a separate IMP.

Stage 1 lock summary

  • Root cause: plan_zone_ratio_retry selects donors by static contract slack only (current − min_height_px); ignores measured per-zone empty space (clientHeight − scrollHeight) already produced by Selenium JS and surfaced in overflow["zones"][i]. Over-promising donors burn a Selenium rerender + cascade entry that data on hand could have prevented.
  • Scope locked: 1 unit — enrich donor identification in src/phase_z2_retry.py:plan_zone_ratio_retry with measured_empty_px cap (effective_slack = min(measured_empty, current − min_height)). Preserve multi-donor greedy aggregation, revert semantics, failure_router substring contract, retry budget = 1, no AI, no spacing shrink, no frame swap. Backward-compat via graceful absence of measured fields.
  • Out of scope: IMP-35 (popup), IMP-36 (partial cqh), IMP-33 (AI restructure), layout_adjust / frame_reselect / image_fit / frame_internal_fit_candidate action labels, frame identity changes, common spacing shrink.
  • Guardrails G1–G11.
  • Risks (a)–(d): all mitigated within the scope-lock; lowest-risk axis = (c) earlier cascade entry, which is correct behavior.
  • Open questions Q1–Q4: backward-compat default, aggregation contract, trace naming, substring contract preservation.

=== EVIDENCE ===

  • Commands run:
    • git rev-parse HEAD15ef7c65e9f58637e4b4714afb9546c176ceaaa8
    • git status (initial snapshot in env preamble) → orchestrator + WIP files only on main; no src/phase_z2_retry.py / src/phase_z2_pipeline.py / src/phase_z2_failure_router.py dirty.
    • Grep _attempt_zone_ratio_retry → orchestrator at src/phase_z2_pipeline.py:1921, plan at src/phase_z2_retry.py:43.
    • Grep IMP-34|frame_swap|zone_ratio across docs/architecture/ → IMP-17 carve-out at IMP-17-CARVE-OUT.md:3,52; status board at PHASE-Z-PIPELINE-STATUS-BOARD.md:56.
  • Files read:
    • src/phase_z2_retry.py (full, 410 lines) — confirms donor loop at :99-137, static slack at :127, multi-donor aggregation at :165-203, no measured-empty consultation.
    • src/phase_z2_pipeline.py lines 1900-2120 — _attempt_zone_ratio_retry orchestrator + revert path (:2070-2080).
    • src/phase_z2_pipeline.py lines 2269-2553 — run_overflow_check JS, per-zone push of m with clientHeight / scrollHeight / excess_y.
    • src/phase_z2_pipeline.py lines 4990-5165 — Step 14~17 wiring + cascade trigger at :5103-5107 (_ft ∈ {donor_slack_insufficient, no_donor_candidates}) + salvage FitAnalysis synthesis at :5117-5130 (reads measured scrollHeight - clientHeight directly).
    • src/phase_z2_failure_router.py lines 1-220 — donor_slack_insufficientcross_zone_redistribute mapping (:95), substring classifier (:205-217).
    • tests/phase_z2/test_phase_z2_retry_multi_donor.py lines 1-100 — existing fixture pattern _overflow_clean does not emit clientHeight/scrollHeight (G8 backward-compat trigger).
    • docs/architecture/IMP-17-CARVE-OUT.md (full, 56 lines) — IMP-34/35 prerequisite for Step 17 AI fallback.
    • docs/architecture/PHASE-Z-PIPELINE-STATUS-BOARD.md lines 40-160 — Step 17 = ⚠ partial (zone_ratio_retry only); missing actions list.
    • tests/CLAUDE.md — fixture convention F-5 (synthetic fixtures under tests/phase_z2/fixtures/, no MDX-sample references in src/**).
    • .orchestrator/issues/12_stage_problem-review_exit.md — IMP-12 exit contract precedent (multi-donor aggregation + cascade salvage chain — IMP-34 inherits these).
  • Facts cross-checked:
    • plan_zone_ratio_retry already receives overflow whole and reads z whole at :95-96 — measured fields ARE present today and would NOT require pipeline plumbing change to consume.
    • _attempt_salvage_chain already consults measured scrollHeight - clientHeight (src/phase_z2_pipeline.py:5117-5130) for its own redistribution math, so propagation downstream of plan is independent — no coupling risk on plan-side enrichment.
    • donor_slack_insufficient and no_donor_candidates route to the SAME cascade tail (cross_zone_redistribute, failure_router.py:95-96); risk (c) "earlier cascade entry" is non-regressive in routing.
    • Existing test fixtures (tests/phase_z2/test_phase_z2_retry_multi_donor.py) omit clientHeight/scrollHeight → backward-compat default (G8 graceful-absence) is required to keep pytest -q tests/phase_z2/ green.
    • Status-board "Step 17 = ⚠ partial (zone_ratio_retry only)" stays partial even after IMP-34 lands — IMP-34 enriches the existing action, does not add new action labels (layout_adjust / frame_reselect / image_fit / frame_internal_fit_candidate remain missing).

FINAL_CONSENSUS: YES

[Claude #1] Stage 1 — IMP-34 problem review (overflow chain step 1 — zone resize + compact retry) ### Root-cause framing IMP-34 is the **first deterministic step** of the overflow remediation chain (Step 16 → 17). The chain shape (post-IMP-12) is: ``` zone_ratio_retry ← IMP-34 axis (this issue) ↓ fail cross_zone_redistribute ← IMP-12 u4 / u8 ↓ fail glue_compression ← IMP-12 u5 ↓ fail font_step_compression ← IMP-12 u6 ↓ fail layout_adjust / frame_reselect / details_popup_escalation (IMP-35) — missing ``` Today, `plan_zone_ratio_retry` (`src/phase_z2_retry.py:43-215`) selects donor zones by **static contract slack** only: `slack = current_height_px − min_height_px` (line 127). This is a *contract* signal — it says "how far can we shrink this zone before violating its `visual_hints.min_height_px`" — and ignores **whether the zone actually has empty space rendered**. A donor whose `clientHeight=200`, `min_height_px=120`, but whose internal content already takes `scrollHeight=195` will be picked with `slack=80`, the plan will take 80 from it, and after rerender the donor *itself* overflows by `195 − 120 = 75`. The candidate fails visual_check, the orchestrator reverts (correct), and the cascade falls through to `cross_zone_redistribute` unnecessarily — burning a Selenium rerender on a donor we could have rejected up-front from data already in `overflow["zones"][i]`. The issue body reframes axis 7 of #44 (originally "frame_swap / popup escalation", **corrected**: frame_swap forbidden — V4 rank-1 protection) to **zone resize**. The concrete enhancement is: donor capacity must be bounded by **measured empty space** (`clientHeight − scrollHeight`), not just static contract slack. The measured signal is already produced by the Selenium JS at `src/phase_z2_pipeline.py:2305-2314` and surfaced into `overflow["zones"][i]` as `clientHeight` / `scrollHeight` / `excess_y` (line 2393 pushes the full `m` dict) — it is read by `_attempt_salvage_chain` for cross_zone_redistribute synthesis (`src/phase_z2_pipeline.py:5117-5130`) but is **not** read by `plan_zone_ratio_retry`. ### Verified facts (value + path + upstream) - HEAD = `15ef7c65e9f58637e4b4714afb9546c176ceaaa8`; branch = `main`. - Source insight #44 axis 7 — `[issue body]`: "frame_swap / popup escalation — **정정**: frame_swap 자동화 폐기, zone resize 로 재정의". Confirms automatic frame swap forbidden (V4 rank-1 protection memory: `feedback_partial_figma_audit` + `project_mdx03_frame_lock` Axis A zone lock). - Dependency #12 verified — IMP-12 u1 (multi-donor greedy) + u3 (failure router cascade) + u4~u9 (cross-zone redistribute / glue / font-step salvage chain) are live. Tests: `tests/phase_z2/test_phase_z2_retry_multi_donor.py`, `test_phase_z2_failure_router_cascade.py`, `test_phase_z2_step17_salvage_chain.py`. - Status board — `docs/architecture/PHASE-Z-PIPELINE-STATUS-BOARD.md:56` confirms Step 17 = `⚠ partial (zone_ratio_retry only)`. Missing actions at line 95-96 = `layout_adjust / frame_reselect / details_popup_escalation / image_fit / frame_internal_fit_candidate`. IMP-34 is *not* a new action label — it enriches the existing `zone_ratio_retry`. - IMP-17 carve-out — `docs/architecture/IMP-17-CARVE-OUT.md:3` explicitly names IMP-34 + IMP-35 as the structural prerequisites for unblocking Step 17 AI fallback (`step17_ai_blocked_imp_34_35_prerequisites_missing`, file line 52). Confirms IMP-34 sits before any AI escalation. - Donor-selection lock — `src/phase_z2_retry.py:99-137` (loop) + `:127` (`slack = height − min_h`) + `:140` (sort by static slack). Multi-donor greedy aggregation already present at `:165-203`. **No measured-empty signal consulted.** - Measured empty available — `src/phase_z2_pipeline.py:2305-2314` (`measure(el)` JS: `clientHeight` / `scrollHeight` / `excess_y` / `overflowed`) + `:2336-2393` (per-zone push of `m`, including `clipped_inner`). `overflow["zones"][i]` already carries `clientHeight` / `scrollHeight` — `plan_zone_ratio_retry:95-97` reads `z` whole, then *only* consults `overflowed` / `clipped_inner` and discards the geometry. - Cascade trigger contract — `src/phase_z2_pipeline.py:5103-5107` cascades on `_ft ∈ {donor_slack_insufficient, no_donor_candidates}`. Substring contract for `donor_slack_insufficient` classification = `src/phase_z2_failure_router.py:205-217` (`"primary donor"` AND `"slack"` AND `"target_added_px"` in failure_reason). IMP-34 must **preserve** this substring contract or update both sides atomically. - Backward-compat surface — existing test fixtures (`tests/phase_z2/test_phase_z2_retry_multi_donor.py:46-52`, `_overflow_clean`) emit zones without `clientHeight` / `scrollHeight` keys. New donor logic must degrade gracefully (treat absent measured fields as "no measured cap", fall back to current static-slack-only behavior) to keep IMP-12 tests green. - Revert + chain-exit contract — already in place from IMP-12: - Rerender visual fail → revert to original `final.html`, candidate stays as `retried_candidate.html` diagnostic (`src/phase_z2_pipeline.py:2070-2080`). - retry_passed=True → `classify_retry_failure` returns None (`src/phase_z2_failure_router.py:163`) → cascade not invoked → IMP-35 popup (when it lands) also not invoked. **IMP-34 inherits this without change.** - Guardrail spec — `docs/architecture/IMP-17-CARVE-OUT.md:19-23` (no MDX shrink / no common spacing shrink / no HTML/CSS direct gen / no new IMP IDs). Aligned with memory `feedback_phase_z_spacing_direction` (clipping → enlarge or relocate, never shrink) and `feedback_ai_isolation_contract` (AI = fallback path only, normal path AI=0). ### Scope-lock — SCOPED to a single internal enhancement of `plan_zone_ratio_retry` The current donor identification computes `slack` from contract `min_height_px` alone. IMP-34 adds **measured empty space** as a stricter upper bound on per-donor take, preserving all other plan/apply/revert semantics. **No new action label, no failure_router schema change, no orchestrator restructure, no AI call.** **Files to touch (1 unit, code edit):** - `src/phase_z2_retry.py:99-215` — `plan_zone_ratio_retry` donor loop: 1. Add per-donor `measured_empty_px = max(0, clientHeight − scrollHeight)` derived from `overflow["zones"][pos]` (graceful absence → `None`). 2. Effective per-donor cap: `effective_slack = min(measured_empty_px, current − min_height_px)` when `measured_empty_px` available; otherwise `effective_slack = current − min_height_px` (current behavior). 3. Sort donor candidates by `effective_slack` desc (was: static slack desc). 4. Multi-donor greedy aggregation uses `effective_slack` (was: static slack). 5. Failure reason for aggregate-insufficient case keeps the same substring pattern (`"primary donor … slack … target_added_px"`) so `failure_router.classify_retry_failure` (line 205-217) keeps routing to `donor_slack_insufficient → cross_zone_redistribute`. The numeric values change; the substring contract does not. 6. Plan trace adds per-candidate `measured_empty_px` + `effective_slack_px` for debug. **Files NOT touched in this unit:** - `src/phase_z2_pipeline.py` `_attempt_zone_ratio_retry` (orchestrator) — unchanged. Reads same `plan` dict shape. - `src/phase_z2_failure_router.py` — unchanged. Substring contract preserved. - `src/phase_z2_pipeline.py` `_attempt_salvage_chain` and cascade trigger — unchanged. - Frontend / debug.json schema — additive only (new fields under existing `plan.donor_candidates_considered[i]` and per-plan). **Tests (added in same commit, scope-locked):** - New `tests/phase_z2/test_phase_z2_retry_measured_empty.py` (or appended cases to `test_phase_z2_retry_multi_donor.py`) — synthetic fixtures only (no MDX sample reference, per `tests/CLAUDE.md` §"테스트 픽스처 컨벤션"): - Case A — donor with `measured_empty < static_slack`: take limited to `measured_empty`; if aggregate (across all donors) `effective_slack` covers `target_added_px` → feasible; else `donor_slack_insufficient`. - Case B — donor with `measured_empty = 0` (already filled): excluded from donors; if remaining aggregate insufficient → `donor_slack_insufficient`. - Case C — **regression baseline**: existing test fixtures without `clientHeight`/`scrollHeight` → behavior identical to pre-IMP-34 (static-slack path). - Case D — donor with `measured_empty > static_slack` (rare — content rendered smaller than min_height envelope): effective_slack = static_slack (min); plan unchanged from current behavior. - Existing `test_phase_z2_retry_multi_donor.py` + `test_phase_z2_failure_router_cascade.py` + `test_phase_z2_step17_salvage_chain.py` must stay green (regression). - Run: `pytest -q tests/phase_z2/`. ### Out of scope (explicit boundaries) - **IMP-35 — `details_popup_escalation`** (overflow chain step 2). Separate issue. IMP-34 success simply means cascade exits before reaching IMP-35; IMP-34 does not import / wire / reference popup escalation. - **IMP-36 — partial responsive fit (cqh / clamp)**. Separate axis (frame-internal CSS), not zone-level redistribution. - **IMP-33 — AI restructure (fallback path)**. Carve-out, gated behind IMP-34 + IMP-35 + B4 evidence integration + user GO (per `IMP-17-CARVE-OUT.md:25-31`). IMP-34 does NOT touch `src/phase_z2_ai_fallback/`. - **No automatic frame swap** — V4 rank-1 selection is locked; donor selection operates only on zone `height_px` allocation. Frame identity preserved. - **No common spacing / padding / gap / line-height shrink** — `feedback_phase_z_spacing_direction` lock. Direction is *expand target / relocate from donor's measured empty*, never shrink shared envelopes. - **No layout topology change** — `layout_adjust` is a separate cascade tail action (Step 17 missing actions); not in IMP-34. - **No `failure_router` schema change** — `donor_slack_insufficient` keeps its name and cascade target (`cross_zone_redistribute`). - **No MDX sample fixture** — synthetic test fixtures only (RULE 0 + RULE 7). - **No retry budget change** — budget=1 preserved (locked rule 1 in `src/phase_z2_retry.py:6-8`). - **No new IMP ID** — enhancement fits inside IMP-34 slot. ### Guardrails - **G1 (PZ-1, RULE 0):** AI = 0 normal path. Plan logic is pure arithmetic over `debug_zones` + `overflow["zones"]`. - **G2 (RULE 7, no-hardcoding):** no MDX-sample-specific branching; donor logic operates on contract `min_height_px` + measured `clientHeight`/`scrollHeight` only. No "if `template_id == 'mdx03'`" anywhere. - **G3 (RULE 0 + memory `project_mdx03_frame_lock` Axis A):** frame identity preserved — no automatic frame swap. Plan changes `heights_px` only. - **G4 (`feedback_phase_z_spacing_direction`):** direction is `target_added_px > 0` AND `donor_reduced_px ≤ donor's measured_empty`. Never shrinks common spacing / padding / line-height / gap. - **G5 (revert idempotent):** rerender visual fail → original `final.html` preserved (already in place at `src/phase_z2_pipeline.py:2070-2080`). IMP-34 does not change this. - **G6 (chain-exit on success):** retry_passed=True → `classify_retry_failure` returns None → cascade not invoked → IMP-35 popup (when it lands) also not invoked. Inherited from IMP-12. - **G7 (failure_router substring contract):** failure_reason for `donor_slack_insufficient` keeps the substring `"primary donor … slack … target_added_px"` so `src/phase_z2_failure_router.py:205-217` keeps routing. Numeric values change; the pattern doesn't. - **G8 (backward compat):** `clientHeight` / `scrollHeight` absent in `overflow["zones"]` → fall back to static-slack-only behavior. Existing IMP-12 tests stay green without fixture edits. - **G9 (PZ-2 / `feedback_one_step_per_turn`):** Stage 1 closes here. No Stage 2 unit decomposition. No pre-staging of IMP-35. - **G10 (`feedback_workflow_atomicity_rules`):** 1 commit = 1 decision unit = `plan_zone_ratio_retry` enrichment + tests. No bundling other axes (e.g., debug.json schema separate, status board update separate). - **G11 (RULE 13 anchor sync):** in commit-push stage, `docs/architecture/PHASE-Z-PIPELINE-STATUS-BOARD.md:56` may update Step 17 line to reflect IMP-34 landed (still `⚠ partial` since `layout_adjust` / `frame_reselect` / `image_fit` / `frame_internal_fit_candidate` remain). Anchor SHA discipline applies. ### Risk Low — additive enrichment on an already-locked plan path; primary failure routing contract preserved by design. - **(a)** Substring contract drift — failure_reason wording change breaks `failure_router.classify_retry_failure` substring match → cascade misroutes to wrong branch. Mitigation: Stage 2 plan must spell out the exact failure_reason template (English: `"primary donor … slack … target_added_px"` substring preserved) + regression test `test_phase_z2_failure_router_cascade.py` already exercises this. Rewind: `stage_3_edit`. - **(b)** Backward-compat regression — existing IMP-12 fixtures (`_overflow_clean` without `clientHeight`) misclassify donor → `pytest -q tests/phase_z2/` red. Mitigation: G8 graceful-absence rule + Case C regression test. Rewind: `stage_3_edit`. - **(c)** Donor pessimism — over-strict cap (measured_empty=0 excludes everything) → `no_donor_candidates` for cases that previously succeeded. *Correct* behavior — the previous "success" was misleading (donor would have overflowed post-take), and `no_donor_candidates` already routes to `cross_zone_redistribute` (same as `donor_slack_insufficient`, per `failure_router.py:95-96`). No regression — just earlier cascade entry. Stage 2 plan should explicitly note this routing equivalence. - **(d)** Hidden coupling — `_attempt_salvage_chain` synthesizes `FitAnalysis` from `debug_zones + overflow` (pipeline `:5117-5130`); if measured_empty signal needs propagating into that synthesis too, scope creeps. **Verified out of scope**: `_attempt_salvage_chain` already reads `scrollHeight - clientHeight` directly for its own role-shortfall computation; it does not depend on `plan` output. No coupling. ### Open questions for Codex r1 1. **Backward-compat default (G8)** — when `overflow["zones"][i]` omits `clientHeight` / `scrollHeight` (existing IMP-12 tests), should the donor logic (a) fall back to static-slack-only [recommended — preserves all IMP-12 tests as-is], (b) treat as `measured_empty=0` (strict — would require fixture edits to IMP-12 tests), or (c) emit a warning + use static slack? Recommend (a). 2. **Aggregation contract** — multi-donor greedy aggregation currently sums static slack (`src/phase_z2_retry.py:166`). Should the aggregate sum the new `effective_slack` (= `min(measured_empty, static_slack)`) per donor [recommended — matches semantic intent], or sum measured_empty separately and check both gates? Recommend single `effective_slack` aggregation. 3. **Trace field naming** — proposed per-candidate fields: `measured_empty_px`, `effective_slack_px`. Plan-level addition: `donor_candidates_considered[i]` already exists. Suggest also adding plan-level `aggregate_effective_slack_available` parallel to existing `aggregate_slack_available`. Confirm naming. 4. **Substring contract** — `failure_router.classify_retry_failure` (line 205-217) matches on `"primary donor"` AND `"slack"` AND `"target_added_px"`. Preserved literal strings? Or do we want to switch to a structured `failure_code` field (longer rewrite, separate axis)? Recommend preserve strings in this unit; structured `failure_code` migration as a separate IMP. ### Stage 1 lock summary - **Root cause**: `plan_zone_ratio_retry` selects donors by static contract slack only (`current − min_height_px`); ignores measured per-zone empty space (`clientHeight − scrollHeight`) already produced by Selenium JS and surfaced in `overflow["zones"][i]`. Over-promising donors burn a Selenium rerender + cascade entry that data on hand could have prevented. - **Scope locked**: **1 unit** — enrich donor identification in `src/phase_z2_retry.py:plan_zone_ratio_retry` with `measured_empty_px` cap (`effective_slack = min(measured_empty, current − min_height)`). Preserve multi-donor greedy aggregation, revert semantics, failure_router substring contract, retry budget = 1, no AI, no spacing shrink, no frame swap. Backward-compat via graceful absence of measured fields. - **Out of scope**: IMP-35 (popup), IMP-36 (partial cqh), IMP-33 (AI restructure), `layout_adjust` / `frame_reselect` / `image_fit` / `frame_internal_fit_candidate` action labels, frame identity changes, common spacing shrink. - **Guardrails G1–G11**. - **Risks (a)–(d)**: all mitigated within the scope-lock; lowest-risk axis = (c) earlier cascade entry, which is correct behavior. - **Open questions Q1–Q4**: backward-compat default, aggregation contract, trace naming, substring contract preservation. === EVIDENCE === - **Commands run**: - `git rev-parse HEAD` → `15ef7c65e9f58637e4b4714afb9546c176ceaaa8` - `git status` (initial snapshot in env preamble) → orchestrator + WIP files only on `main`; no `src/phase_z2_retry.py` / `src/phase_z2_pipeline.py` / `src/phase_z2_failure_router.py` dirty. - `Grep` `_attempt_zone_ratio_retry` → orchestrator at `src/phase_z2_pipeline.py:1921`, plan at `src/phase_z2_retry.py:43`. - `Grep` `IMP-34|frame_swap|zone_ratio` across `docs/architecture/` → IMP-17 carve-out at `IMP-17-CARVE-OUT.md:3,52`; status board at `PHASE-Z-PIPELINE-STATUS-BOARD.md:56`. - **Files read**: - `src/phase_z2_retry.py` (full, 410 lines) — confirms donor loop at `:99-137`, static slack at `:127`, multi-donor aggregation at `:165-203`, no measured-empty consultation. - `src/phase_z2_pipeline.py` lines 1900-2120 — `_attempt_zone_ratio_retry` orchestrator + revert path (`:2070-2080`). - `src/phase_z2_pipeline.py` lines 2269-2553 — `run_overflow_check` JS, per-zone push of `m` with `clientHeight` / `scrollHeight` / `excess_y`. - `src/phase_z2_pipeline.py` lines 4990-5165 — Step 14~17 wiring + cascade trigger at `:5103-5107` (`_ft ∈ {donor_slack_insufficient, no_donor_candidates}`) + salvage `FitAnalysis` synthesis at `:5117-5130` (reads measured `scrollHeight - clientHeight` directly). - `src/phase_z2_failure_router.py` lines 1-220 — `donor_slack_insufficient` → `cross_zone_redistribute` mapping (`:95`), substring classifier (`:205-217`). - `tests/phase_z2/test_phase_z2_retry_multi_donor.py` lines 1-100 — existing fixture pattern `_overflow_clean` does not emit `clientHeight`/`scrollHeight` (G8 backward-compat trigger). - `docs/architecture/IMP-17-CARVE-OUT.md` (full, 56 lines) — IMP-34/35 prerequisite for Step 17 AI fallback. - `docs/architecture/PHASE-Z-PIPELINE-STATUS-BOARD.md` lines 40-160 — Step 17 = `⚠ partial (zone_ratio_retry only)`; missing actions list. - `tests/CLAUDE.md` — fixture convention F-5 (synthetic fixtures under `tests/phase_z2/fixtures/`, no MDX-sample references in `src/**`). - `.orchestrator/issues/12_stage_problem-review_exit.md` — IMP-12 exit contract precedent (multi-donor aggregation + cascade salvage chain — IMP-34 inherits these). - **Facts cross-checked**: - `plan_zone_ratio_retry` already receives `overflow` whole and reads `z` whole at `:95-96` — measured fields ARE present today and would NOT require pipeline plumbing change to consume. - `_attempt_salvage_chain` already consults measured `scrollHeight - clientHeight` (`src/phase_z2_pipeline.py:5117-5130`) for its own redistribution math, so propagation downstream of plan is independent — no coupling risk on plan-side enrichment. - `donor_slack_insufficient` and `no_donor_candidates` route to the SAME cascade tail (`cross_zone_redistribute`, `failure_router.py:95-96`); risk (c) "earlier cascade entry" is non-regressive in routing. - Existing test fixtures (`tests/phase_z2/test_phase_z2_retry_multi_donor.py`) omit `clientHeight`/`scrollHeight` → backward-compat default (G8 graceful-absence) is required to keep `pytest -q tests/phase_z2/` green. - Status-board "Step 17 = ⚠ partial (zone_ratio_retry only)" stays *partial* even after IMP-34 lands — IMP-34 enriches the existing action, does not add new action labels (`layout_adjust` / `frame_reselect` / `image_fit` / `frame_internal_fit_candidate` remain missing). FINAL_CONSENSUS: YES
Author
Owner

[Codex #1] Stage 1 problem-review independent verification - IMP-34

Decision: PASS, scope-locked with one factual correction.

Root cause verified:

  • src/phase_z2_retry.py:42-213 implements plan_zone_ratio_retry.
  • Donor eligibility currently rejects donors only when Step 14 says the donor is already overflowing or has clipped inner content (overflowed / clipped_inner at src/phase_z2_retry.py:107-111).
  • Donor capacity is then computed only as static contract slack: slack = height - min_h at src/phase_z2_retry.py:121-127.
  • Greedy donor selection and allocation consume that same static slack (src/phase_z2_retry.py:138-199).
  • No measured donor empty-space bound is used in this planner. Targeted grep over src/phase_z2_retry.py finds excess_y and slack, but no clientHeight / scrollHeight use.

Measured signal availability verified:

  • src/phase_z2_pipeline.py:2300-2310 defines Selenium measure(el) with clientHeight, scrollHeight, excess_y, and overflowed.
  • src/phase_z2_pipeline.py:2332-2389 pushes the full measured zone object into overflow["zones"].
  • Step 14 writes that overflow object as step14_visual_check.json at src/phase_z2_pipeline.py:5021-5039.
  • The later salvage-chain synthesis already reads the same measured fields: _ch = clientHeight, _sh = scrollHeight, shortfall_px = _sh - _ch at src/phase_z2_pipeline.py:5130-5141.

Revert/idempotence behavior verified:

  • _attempt_zone_ratio_retry gates row-axis retry to dynamic row layouts only (src/phase_z2_pipeline.py:2034-2047).
  • Redistribution infeasible returns without rerender and leaves original final.html unchanged (src/phase_z2_pipeline.py:2065-2073).
  • Feasible plans render only retried_candidate.html first (src/phase_z2_pipeline.py:2075-2095).
  • Candidate PASS promotes to final.html; candidate FAIL records candidate_overflow_summary and preserves original final.html (src/phase_z2_pipeline.py:2097-2129).

Scope lock for implementation:

  • Implement inside src/phase_z2_retry.py, specifically plan_zone_ratio_retry.
  • Bound each donor's usable shrink by measured rendered empty space from overflow["zones"]: conceptually max(0, clientHeight - scrollHeight), with tolerance/safety handled explicitly and generally, not sample-specific.
  • Effective donor capacity should be min(contract_slack, measured_empty_space) for eligibility, aggregate availability, and per-donor take.
  • Preserve existing budget=1 behavior and the existing rerender/revert contract in _attempt_zone_ratio_retry; no frame swap, no frame reselect, no popup escalation, no content dropping.
  • Keep spacing guardrails: no global/common padding, gap, margin, or line-height shrink. This axis is resize upward for the overflow target and shrink donors only within measured rendered emptiness.
  • Add focused planner tests that prove a donor with contract slack but no rendered empty space is rejected or capacity-limited, plus a multi-donor measured-capacity case. Existing multi-donor tests in tests/phase_z2/test_phase_z2_retry_multi_donor.py currently cover static slack only.

Cascade/state verification:

  • src/phase_z2_failure_router.py:94-101 routes donor_slack_insufficient and no_donor_candidates to cross_zone_redistribute, then glue_compression, then font_step_compression, then layout_adjust.
  • src/phase_z2_failure_router.py:142-148 marks cross_zone_redistribute, glue_compression, and font_step_compression as IMPLEMENTED, while layout_adjust and frame_reselect are MISSING.
  • src/phase_z2_pipeline.py:5114-5158 wires donor failure types into _attempt_salvage_chain.
  • docs/architecture/PHASE-Z-PIPELINE-STATUS-BOARD.md:56 still says Step 17 is partial, and lines 90-91 list layout_adjust, frame_reselect, and details_popup_escalation as missing.

Wrong or stale assumptions found:

  • Claude's recorded HEAD 15ef7c65e9f58637e4b4714afb9546c176ceaaa8 is stale for this checkout. Current local HEAD is a06dd3d4b07767f752d6f40ed690e07164836e0c on main.
  • No required source file was missing: src/phase_z2_retry.py, src/phase_z2_pipeline.py, src/phase_z2_failure_router.py, relevant tests/phase_z2/*, and the status board were present.
  • The broad claim "Step 17 = zone_ratio_retry only" is stale if read as code behavior: the status board line still says that, but current code also has the IMP-12 deterministic salvage cascade wired after zone-ratio failure. For IMP-34 this does not change scope; it reinforces that the measured donor-capacity fix belongs before the cascade burns a retry.

No pytest was run, per Stage 1 instruction.

=== EVIDENCE ===
Commands run:

  • Get-Content -LiteralPath D:\ad-hoc\kei\design_agent\.orchestrator\tmp\codex_prompt.txt
  • git rev-parse HEAD; git branch --show-current; git status --short
  • rg -n "def plan_zone_ratio_retry|plan_zone_ratio_retry|_attempt_zone_ratio_retry|clientHeight|scrollHeight|excess_y|cross_zone_redistribute|zone_ratio_retry|feedback_phase_z_spacing_direction|layout_adjust|frame_swap|frame_reselect|details_popup|popup" src tests docs -S
  • line-range reads for src/phase_z2_retry.py, src/phase_z2_pipeline.py, and docs/architecture/PHASE-Z-PIPELINE-STATUS-BOARD.md
  • rg -n "NEXT_ACTION_BY_FAILURE|NEXT_ACTION_IMPLEMENTATION_STATUS|frame_reselect|layout_adjust|cross_zone_redistribute|glue_compression|font_step" src/phase_z2_retry.py src/phase_z2_pipeline.py tests/phase_z2/test_phase_z2_failure_router_cascade.py tests/phase_z2/test_phase_z2_step17_salvage_chain.py -S
  • rg -n "clientHeight|scrollHeight|excess_y|measured|empty|slack" src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_multi_donor.py -S
  • rg -n "_attempt_zone_ratio_retry|plan_zone_ratio_retry|run_overflow_check|retried_candidate|candidate_overflow|retry_failure_reason" tests/phase_z2 -S

Files checked:

  • D:\ad-hoc\kei\design_agent\src\phase_z2_retry.py
  • D:\ad-hoc\kei\design_agent\src\phase_z2_pipeline.py
  • D:\ad-hoc\kei\design_agent\src\phase_z2_failure_router.py
  • D:\ad-hoc\kei\design_agent\tests\phase_z2\test_phase_z2_retry_multi_donor.py
  • D:\ad-hoc\kei\design_agent\tests\phase_z2\test_phase_z2_failure_router_cascade.py
  • D:\ad-hoc\kei\design_agent\tests\phase_z2\test_phase_z2_step17_salvage_chain.py
  • D:\ad-hoc\kei\design_agent\docs\architecture\PHASE-Z-PIPELINE-STATUS-BOARD.md

Test results:

  • Not run. Stage 1 explicitly says NO pytest.

Verified facts:

  • Current local HEAD = a06dd3d4b07767f752d6f40ed690e07164836e0c; branch = main.
  • plan_zone_ratio_retry computes donor capacity from height_px - min_height_px only.
  • Step 14 already exposes per-zone clientHeight and scrollHeight.
  • Salvage-chain synthesis already consumes those measured fields later in the pipeline.
  • Retry rerender failure preserves original final.html; only a passing candidate is promoted.
  • layout_adjust, post-render frame_reselect, and details_popup_escalation remain missing terminal actions.
    FINAL_CONSENSUS: YES
[Codex #1] Stage 1 problem-review independent verification - IMP-34 Decision: PASS, scope-locked with one factual correction. Root cause verified: - `src/phase_z2_retry.py:42-213` implements `plan_zone_ratio_retry`. - Donor eligibility currently rejects donors only when Step 14 says the donor is already overflowing or has clipped inner content (`overflowed` / `clipped_inner` at `src/phase_z2_retry.py:107-111`). - Donor capacity is then computed only as static contract slack: `slack = height - min_h` at `src/phase_z2_retry.py:121-127`. - Greedy donor selection and allocation consume that same static slack (`src/phase_z2_retry.py:138-199`). - No measured donor empty-space bound is used in this planner. Targeted grep over `src/phase_z2_retry.py` finds `excess_y` and `slack`, but no `clientHeight` / `scrollHeight` use. Measured signal availability verified: - `src/phase_z2_pipeline.py:2300-2310` defines Selenium `measure(el)` with `clientHeight`, `scrollHeight`, `excess_y`, and `overflowed`. - `src/phase_z2_pipeline.py:2332-2389` pushes the full measured zone object into `overflow["zones"]`. - Step 14 writes that overflow object as `step14_visual_check.json` at `src/phase_z2_pipeline.py:5021-5039`. - The later salvage-chain synthesis already reads the same measured fields: `_ch = clientHeight`, `_sh = scrollHeight`, `shortfall_px = _sh - _ch` at `src/phase_z2_pipeline.py:5130-5141`. Revert/idempotence behavior verified: - `_attempt_zone_ratio_retry` gates row-axis retry to dynamic row layouts only (`src/phase_z2_pipeline.py:2034-2047`). - Redistribution infeasible returns without rerender and leaves original `final.html` unchanged (`src/phase_z2_pipeline.py:2065-2073`). - Feasible plans render only `retried_candidate.html` first (`src/phase_z2_pipeline.py:2075-2095`). - Candidate PASS promotes to `final.html`; candidate FAIL records `candidate_overflow_summary` and preserves original `final.html` (`src/phase_z2_pipeline.py:2097-2129`). Scope lock for implementation: - Implement inside `src/phase_z2_retry.py`, specifically `plan_zone_ratio_retry`. - Bound each donor's usable shrink by measured rendered empty space from `overflow["zones"]`: conceptually `max(0, clientHeight - scrollHeight)`, with tolerance/safety handled explicitly and generally, not sample-specific. - Effective donor capacity should be `min(contract_slack, measured_empty_space)` for eligibility, aggregate availability, and per-donor `take`. - Preserve existing budget=1 behavior and the existing rerender/revert contract in `_attempt_zone_ratio_retry`; no frame swap, no frame reselect, no popup escalation, no content dropping. - Keep spacing guardrails: no global/common padding, gap, margin, or line-height shrink. This axis is resize upward for the overflow target and shrink donors only within measured rendered emptiness. - Add focused planner tests that prove a donor with contract slack but no rendered empty space is rejected or capacity-limited, plus a multi-donor measured-capacity case. Existing multi-donor tests in `tests/phase_z2/test_phase_z2_retry_multi_donor.py` currently cover static slack only. Cascade/state verification: - `src/phase_z2_failure_router.py:94-101` routes `donor_slack_insufficient` and `no_donor_candidates` to `cross_zone_redistribute`, then `glue_compression`, then `font_step_compression`, then `layout_adjust`. - `src/phase_z2_failure_router.py:142-148` marks `cross_zone_redistribute`, `glue_compression`, and `font_step_compression` as IMPLEMENTED, while `layout_adjust` and `frame_reselect` are MISSING. - `src/phase_z2_pipeline.py:5114-5158` wires donor failure types into `_attempt_salvage_chain`. - `docs/architecture/PHASE-Z-PIPELINE-STATUS-BOARD.md:56` still says Step 17 is partial, and lines 90-91 list `layout_adjust`, `frame_reselect`, and `details_popup_escalation` as missing. Wrong or stale assumptions found: - Claude's recorded HEAD `15ef7c65e9f58637e4b4714afb9546c176ceaaa8` is stale for this checkout. Current local HEAD is `a06dd3d4b07767f752d6f40ed690e07164836e0c` on `main`. - No required source file was missing: `src/phase_z2_retry.py`, `src/phase_z2_pipeline.py`, `src/phase_z2_failure_router.py`, relevant `tests/phase_z2/*`, and the status board were present. - The broad claim "Step 17 = zone_ratio_retry only" is stale if read as code behavior: the status board line still says that, but current code also has the IMP-12 deterministic salvage cascade wired after zone-ratio failure. For IMP-34 this does not change scope; it reinforces that the measured donor-capacity fix belongs before the cascade burns a retry. No pytest was run, per Stage 1 instruction. === EVIDENCE === Commands run: - `Get-Content -LiteralPath D:\ad-hoc\kei\design_agent\.orchestrator\tmp\codex_prompt.txt` - `git rev-parse HEAD; git branch --show-current; git status --short` - `rg -n "def plan_zone_ratio_retry|plan_zone_ratio_retry|_attempt_zone_ratio_retry|clientHeight|scrollHeight|excess_y|cross_zone_redistribute|zone_ratio_retry|feedback_phase_z_spacing_direction|layout_adjust|frame_swap|frame_reselect|details_popup|popup" src tests docs -S` - line-range reads for `src/phase_z2_retry.py`, `src/phase_z2_pipeline.py`, and `docs/architecture/PHASE-Z-PIPELINE-STATUS-BOARD.md` - `rg -n "NEXT_ACTION_BY_FAILURE|NEXT_ACTION_IMPLEMENTATION_STATUS|frame_reselect|layout_adjust|cross_zone_redistribute|glue_compression|font_step" src/phase_z2_retry.py src/phase_z2_pipeline.py tests/phase_z2/test_phase_z2_failure_router_cascade.py tests/phase_z2/test_phase_z2_step17_salvage_chain.py -S` - `rg -n "clientHeight|scrollHeight|excess_y|measured|empty|slack" src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_multi_donor.py -S` - `rg -n "_attempt_zone_ratio_retry|plan_zone_ratio_retry|run_overflow_check|retried_candidate|candidate_overflow|retry_failure_reason" tests/phase_z2 -S` Files checked: - `D:\ad-hoc\kei\design_agent\src\phase_z2_retry.py` - `D:\ad-hoc\kei\design_agent\src\phase_z2_pipeline.py` - `D:\ad-hoc\kei\design_agent\src\phase_z2_failure_router.py` - `D:\ad-hoc\kei\design_agent\tests\phase_z2\test_phase_z2_retry_multi_donor.py` - `D:\ad-hoc\kei\design_agent\tests\phase_z2\test_phase_z2_failure_router_cascade.py` - `D:\ad-hoc\kei\design_agent\tests\phase_z2\test_phase_z2_step17_salvage_chain.py` - `D:\ad-hoc\kei\design_agent\docs\architecture\PHASE-Z-PIPELINE-STATUS-BOARD.md` Test results: - Not run. Stage 1 explicitly says NO pytest. Verified facts: - Current local HEAD = `a06dd3d4b07767f752d6f40ed690e07164836e0c`; branch = `main`. - `plan_zone_ratio_retry` computes donor capacity from `height_px - min_height_px` only. - Step 14 already exposes per-zone `clientHeight` and `scrollHeight`. - Salvage-chain synthesis already consumes those measured fields later in the pipeline. - Retry rerender failure preserves original `final.html`; only a passing candidate is promoted. - `layout_adjust`, post-render `frame_reselect`, and `details_popup_escalation` remain missing terminal actions. FINAL_CONSENSUS: YES
Author
Owner

[Claude #2] Stage 2 simulation-plan — IMP-34 zone resize donor capacity bound

Axes / items coverage (issue body)

# Issue axis Covered by Status
1 visual_check overflow detection → measure all-zone empty space u1 (read overflow["zones"] measured fields) planned
2 Extend _attempt_zone_ratio_retry (call site at src/phase_z2_pipeline.py:2034-2129) unchanged orchestrator, only plan_zone_ratio_retry (src/phase_z2_retry.py:43-215) modified planned
3 Identify empty/donor zones via measured height vs allocated u1 (measured_empty_px = max(0, clientHeight - scrollHeight)) planned
4 Expand overflow zone allocated height (donor empty deduction, budget=1) already done by existing greedy aggregator (src/phase_z2_retry.py:188-203); donor capacity now correctly bounded planned
5 Revert on rerender fail unchanged (src/phase_z2_pipeline.py:2118-2128) preserved
6 Success → skip next chain step (IMP-35 popup) unchanged orchestrator preserved
7 Guardrail: no auto frame_swap planner-only change, no frame mutation preserved
8 Guardrail: no common-spacing/padding/gap/line-height shrink bound is on donor zone height only, gaps/padding untouched preserved
9 Guardrail: no sample-specific budget branching bound derived from generic geometry fields preserved
10 Guardrail: no content dropping planner-only, MDX/zone content untouched preserved
11 Guardrail: idempotent revert on fail candidate path only, original final.html untouched until PASS preserved

Unresolved-question resolutions (from Stage 1)

  • Q1 measured_empty_px == 0 or negative: treat capacity as 0 → donor excluded (consistent with existing slack <= 0 skip rule, line 128-129).
  • Q2 measured vs static divergence: use min(static_slack, measured_empty_px) (conservative; both must agree). Static slack remains hard ceiling.
  • Q3 telemetry: additive fields per donor candidate dict — static_slack_px, measured_empty_px (when present), capacity_source ∈ {"measured_bound", "static_fallback"}. No schema change to existing fields; slack becomes bounded value (consistent w/ downstream aggregation).

Before/after behavior

  • Before: donor with height - min_h = 100 but clientHeight - scrollHeight = 0 (full) → treated as slack=100 → greedy aggregator takes up to 100px → donor overflows after rerender → wasted Selenium pass → cascade falls to cross_zone_redistribute.
  • After: same donor → measured_empty_px = 0bounded_slack = min(100, 0) = 0 → donor excluded at line 128-129. Aggregate may now be insufficient → infeasible plan → skips rerender → cascade proceeds without wasted pass. Regression-safe when measured fields absent (static fallback).

Out of scope (per Stage 1 scope_lock)

  • frame_swap / V4 frame reselection (forbidden).
  • glue/font compression (IMP-12 u5/u6).
  • popup escalation (IMP-35).
  • Step 14 measurement schema additions.
  • Salvage chain (_attempt_salvage_chain) logic.
  • Donor eligibility filter (line 107-111) — additive bound only, filter unchanged.

Rollback strategy

Single-file deterministic planner change. Rollback = revert src/phase_z2_retry.py diff. No state migration; new tests in dedicated module deletable without affecting existing test_phase_z2_retry_multi_donor.py.

=== IMPLEMENTATION_UNITS ===

  • id: u1
    summary: Bound donor slack by measured empty space (clientHeight - scrollHeight) from overflow["zones"], with static-slack fallback. Additive telemetry fields. No schema change.
    files: [src/phase_z2_retry.py]
    tests: [tests/phase_z2/test_phase_z2_retry_multi_donor.py]
    estimate_lines: 30
  • id: u2
    summary: New test module covering (a) measured < static → measured wins; (b) measured > static → static wins; (c) measured field absent → static fallback (regression); (d) measured == 0 → donor excluded; (e) aggregate insufficient when bounded → infeasible plan.
    files: [tests/phase_z2/test_phase_z2_retry_measured_bound.py]
    tests: [tests/phase_z2/test_phase_z2_retry_measured_bound.py]
    estimate_lines: 50

Per-unit rationale

  • u1: Drop-in additive bound at the donor capacity calc site (src/phase_z2_retry.py:121-127). Reads clientHeight/scrollHeight already populated at overflow["zones"][i] (Step 14 measure(el) at src/phase_z2_pipeline.py:2300-2310). Precedent reader at src/phase_z2_pipeline.py:5130-5141 (_zof[pos].get("clientHeight"/"scrollHeight")). slack field semantics widen to "bounded effective slack" — downstream aggregator (line 166, 192-203) needs no change since it already operates on donor["slack"]. Existing 5 tests in test_phase_z2_retry_multi_donor.py must continue passing (they omit clientHeight/scrollHeight → static fallback path).
  • u2: Locks in the new semantic via dedicated test fixtures. Includes a regression case mirroring test_single_donor_sufficient_regression shape but with measured fields absent (proves backward compat). Adds capacity_source assertions to verify telemetry.

Side effects / follow-up candidates

  • Existing tests (test_phase_z2_retry_multi_donor.py, test_retry_gate.py) construct overflow without clientHeight/scrollHeight → will exercise static_fallback branch. Required behavior preserved.
  • Pipeline runtime: Step 14 always populates these fields (measure(el) returns them unconditionally), so production path always takes the measured_bound branch. Net effect: tighter (or equal) donor capacity, never looser.
  • Follow-up candidate (NOT in this IMP): expose capacity_source distribution at step17_retry_attempt.json level for monitoring how often measured signal tightens the bound vs static-only.

=== EVIDENCE ===

  • src/phase_z2_retry.py:43-215 — plan_zone_ratio_retry full function (re-read)
  • src/phase_z2_retry.py:107-111 — donor eligibility (visual_check filter) — unchanged
  • src/phase_z2_retry.py:121-127 — current static-only slack calc — modification site
  • src/phase_z2_retry.py:166-185 — aggregate slack feasibility check (consumes bounded slack)
  • src/phase_z2_retry.py:188-203 — greedy multi-donor aggregation (consumes bounded slack)
  • src/phase_z2_pipeline.py:2300-2310 — Selenium measure(el) emits clientHeight/scrollHeight/excess_y/overflowed
  • src/phase_z2_pipeline.py:2332-2389 — per-zone push into zones[] (consumed at overflow["zones"])
  • src/phase_z2_pipeline.py:5130-5141 — precedent reader (salvage chain reads same fields)
  • src/phase_z2_pipeline.py:2034-2129 — _attempt_zone_ratio_retry orchestrator + revert
  • tests/phase_z2/test_phase_z2_retry_multi_donor.py:46-52 — current _overflow_clean helper omits measured fields → confirms static fallback path triggered for existing 5 regression tests

FINAL_CONSENSUS: YES

[Claude #2] Stage 2 simulation-plan — IMP-34 zone resize donor capacity bound ## Axes / items coverage (issue body) | # | Issue axis | Covered by | Status | |---|---|---|---| | 1 | visual_check overflow detection → measure all-zone empty space | u1 (read `overflow["zones"]` measured fields) | planned | | 2 | Extend `_attempt_zone_ratio_retry` (call site at `src/phase_z2_pipeline.py:2034-2129`) | unchanged orchestrator, only `plan_zone_ratio_retry` (`src/phase_z2_retry.py:43-215`) modified | planned | | 3 | Identify empty/donor zones via measured height vs allocated | u1 (`measured_empty_px = max(0, clientHeight - scrollHeight)`) | planned | | 4 | Expand overflow zone allocated height (donor empty deduction, budget=1) | already done by existing greedy aggregator (`src/phase_z2_retry.py:188-203`); donor capacity now correctly bounded | planned | | 5 | Revert on rerender fail | unchanged (`src/phase_z2_pipeline.py:2118-2128`) | preserved | | 6 | Success → skip next chain step (IMP-35 popup) | unchanged orchestrator | preserved | | 7 | Guardrail: no auto frame_swap | planner-only change, no frame mutation | preserved | | 8 | Guardrail: no common-spacing/padding/gap/line-height shrink | bound is on donor zone height only, gaps/padding untouched | preserved | | 9 | Guardrail: no sample-specific budget branching | bound derived from generic geometry fields | preserved | | 10 | Guardrail: no content dropping | planner-only, MDX/zone content untouched | preserved | | 11 | Guardrail: idempotent revert on fail | candidate path only, original final.html untouched until PASS | preserved | ## Unresolved-question resolutions (from Stage 1) - Q1 `measured_empty_px == 0 or negative`: treat capacity as 0 → donor excluded (consistent with existing `slack <= 0` skip rule, line 128-129). - Q2 measured vs static divergence: use `min(static_slack, measured_empty_px)` (conservative; both must agree). Static slack remains hard ceiling. - Q3 telemetry: additive fields per donor candidate dict — `static_slack_px`, `measured_empty_px` (when present), `capacity_source` ∈ {"measured_bound", "static_fallback"}. No schema change to existing fields; `slack` becomes bounded value (consistent w/ downstream aggregation). ## Before/after behavior - Before: donor with `height - min_h = 100` but `clientHeight - scrollHeight = 0` (full) → treated as slack=100 → greedy aggregator takes up to 100px → donor overflows after rerender → wasted Selenium pass → cascade falls to `cross_zone_redistribute`. - After: same donor → `measured_empty_px = 0` → `bounded_slack = min(100, 0) = 0` → donor excluded at line 128-129. Aggregate may now be insufficient → infeasible plan → skips rerender → cascade proceeds without wasted pass. Regression-safe when measured fields absent (static fallback). ## Out of scope (per Stage 1 scope_lock) - frame_swap / V4 frame reselection (forbidden). - glue/font compression (IMP-12 u5/u6). - popup escalation (IMP-35). - Step 14 measurement schema additions. - Salvage chain (`_attempt_salvage_chain`) logic. - Donor eligibility filter (line 107-111) — additive bound only, filter unchanged. ## Rollback strategy Single-file deterministic planner change. Rollback = revert `src/phase_z2_retry.py` diff. No state migration; new tests in dedicated module deletable without affecting existing `test_phase_z2_retry_multi_donor.py`. === IMPLEMENTATION_UNITS === - id: u1 summary: Bound donor slack by measured empty space (clientHeight - scrollHeight) from overflow["zones"], with static-slack fallback. Additive telemetry fields. No schema change. files: [src/phase_z2_retry.py] tests: [tests/phase_z2/test_phase_z2_retry_multi_donor.py] estimate_lines: 30 - id: u2 summary: New test module covering (a) measured < static → measured wins; (b) measured > static → static wins; (c) measured field absent → static fallback (regression); (d) measured == 0 → donor excluded; (e) aggregate insufficient when bounded → infeasible plan. files: [tests/phase_z2/test_phase_z2_retry_measured_bound.py] tests: [tests/phase_z2/test_phase_z2_retry_measured_bound.py] estimate_lines: 50 ## Per-unit rationale - u1: Drop-in additive bound at the donor capacity calc site (`src/phase_z2_retry.py:121-127`). Reads `clientHeight`/`scrollHeight` already populated at `overflow["zones"][i]` (Step 14 `measure(el)` at `src/phase_z2_pipeline.py:2300-2310`). Precedent reader at `src/phase_z2_pipeline.py:5130-5141` (`_zof[pos].get("clientHeight"/"scrollHeight")`). `slack` field semantics widen to "bounded effective slack" — downstream aggregator (line 166, 192-203) needs no change since it already operates on `donor["slack"]`. Existing 5 tests in `test_phase_z2_retry_multi_donor.py` must continue passing (they omit `clientHeight`/`scrollHeight` → static fallback path). - u2: Locks in the new semantic via dedicated test fixtures. Includes a regression case mirroring `test_single_donor_sufficient_regression` shape but with measured fields absent (proves backward compat). Adds `capacity_source` assertions to verify telemetry. ## Side effects / follow-up candidates - Existing tests (`test_phase_z2_retry_multi_donor.py`, `test_retry_gate.py`) construct `overflow` without `clientHeight`/`scrollHeight` → will exercise static_fallback branch. Required behavior preserved. - Pipeline runtime: Step 14 always populates these fields (`measure(el)` returns them unconditionally), so production path always takes the measured_bound branch. Net effect: tighter (or equal) donor capacity, never looser. - Follow-up candidate (NOT in this IMP): expose `capacity_source` distribution at `step17_retry_attempt.json` level for monitoring how often measured signal tightens the bound vs static-only. === EVIDENCE === - src/phase_z2_retry.py:43-215 — `plan_zone_ratio_retry` full function (re-read) - src/phase_z2_retry.py:107-111 — donor eligibility (visual_check filter) — unchanged - src/phase_z2_retry.py:121-127 — current static-only slack calc — modification site - src/phase_z2_retry.py:166-185 — aggregate slack feasibility check (consumes bounded slack) - src/phase_z2_retry.py:188-203 — greedy multi-donor aggregation (consumes bounded slack) - src/phase_z2_pipeline.py:2300-2310 — Selenium `measure(el)` emits clientHeight/scrollHeight/excess_y/overflowed - src/phase_z2_pipeline.py:2332-2389 — per-zone push into `zones[]` (consumed at `overflow["zones"]`) - src/phase_z2_pipeline.py:5130-5141 — precedent reader (salvage chain reads same fields) - src/phase_z2_pipeline.py:2034-2129 — `_attempt_zone_ratio_retry` orchestrator + revert - tests/phase_z2/test_phase_z2_retry_multi_donor.py:46-52 — current `_overflow_clean` helper omits measured fields → confirms static fallback path triggered for existing 5 regression tests FINAL_CONSENSUS: YES
Author
Owner

[Codex #2] Stage 2 simulation-plan review IMP-34

=== IMPLEMENTATION_UNITS ===
review_result: rejected
reason: "The current Stage 2 plan comment does not contain the required implementation_units YAML block."
required_units_block_present: false
unit_size_check:
status: "not_verifiable"
reason: "No declared units, files, tests, or estimate_lines exist to verify the <=50 line / <=3 file rule."
missing_required_stage_items:

  • "implementation_units block with unit ids, summaries, files, tests, and estimate_lines"
  • "per-unit file/test mapping"
  • "per-unit size estimates"

Brief per-unit rationale:
No per-unit rationale can be accepted because no implementation units were declared. The prose plan covers the donor-capacity idea, but Stage 2 requires concrete units before Stage 3.

Out-of-scope notes:
The stated scope remains planner-only in src/phase_z2_retry.py; frame_swap, spacing shrink, glue/font compression, popup escalation, Step 14 schema changes, salvage-chain edits, and content dropping remain out of scope.

Rollback strategy:
Stage 3 should be allowed only after a compact Stage 2 plan declares small units. The intended rollback surface is limited to reverting changes in src/phase_z2_retry.py and any focused tests added for donor measured-empty-space behavior.

=== EVIDENCE ===
Commands run:

  • rg -n "def plan_zone_ratio_retry|overflow[\"zones\"]|clientHeight|scrollHeight|_attempt_zone_ratio_retry|step14_visual_check|attempt_salvage_chain|zone_ratio" src/phase_z2_retry.py src/phase_z2_pipeline.py tests
  • Get-Content -LiteralPath 'src\phase_z2_retry.py' | Select-Object -First 260
  • rg --files tests | rg "phase_z2|retry|zone"
  • git status --short
  • pytest -q tests
  • line-window reads for src/phase_z2_retry.py:43-213, src/phase_z2_pipeline.py:2034-2160, src/phase_z2_pipeline.py:2300-2389, and src/phase_z2_pipeline.py:5130-5141
  • Get-Content -LiteralPath 'tests\phase_z2\test_phase_z2_retry_multi_donor.py' | Select-Object -First 220
    Files checked:
  • src/phase_z2_retry.py
  • src/phase_z2_pipeline.py
  • tests/phase_z2/test_phase_z2_retry_multi_donor.py
  • tests/phase_z2/test_retry_gate.py
  • tests/phase_z2/test_phase_z2_step17_salvage_chain.py
    Test results:
  • pytest -q tests: 639 passed, 2 failed.
  • Failures: tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31 and tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_571_still_references_imp29; both report blank anchor lines where expected text was missing.
    Verified facts:
  • src/phase_z2_retry.py:107-111 currently excludes donor zones only for overflowed or clipped_inner.
  • src/phase_z2_retry.py:121-127 currently computes donor slack from static height - min_height only.
  • src/phase_z2_pipeline.py:2331-2341 measures clientHeight, scrollHeight, excess_y, and overflowed; src/phase_z2_pipeline.py:2363-2369 stores those fields per zone.
  • src/phase_z2_pipeline.py:2096-2104 skips rerender when a plan is infeasible; src/phase_z2_pipeline.py:2149-2160 preserves original final.html on candidate visual failure.
  • Existing retry tests cover multi-donor static slack behavior but do not cover measured-empty-space bounding.
    rewind_target: stage_2_plan
    FINAL_CONSENSUS: NO
[Codex #2] Stage 2 simulation-plan review IMP-34 === IMPLEMENTATION_UNITS === review_result: rejected reason: "The current Stage 2 plan comment does not contain the required implementation_units YAML block." required_units_block_present: false unit_size_check: status: "not_verifiable" reason: "No declared units, files, tests, or estimate_lines exist to verify the <=50 line / <=3 file rule." missing_required_stage_items: - "implementation_units block with unit ids, summaries, files, tests, and estimate_lines" - "per-unit file/test mapping" - "per-unit size estimates" Brief per-unit rationale: No per-unit rationale can be accepted because no implementation units were declared. The prose plan covers the donor-capacity idea, but Stage 2 requires concrete units before Stage 3. Out-of-scope notes: The stated scope remains planner-only in `src/phase_z2_retry.py`; frame_swap, spacing shrink, glue/font compression, popup escalation, Step 14 schema changes, salvage-chain edits, and content dropping remain out of scope. Rollback strategy: Stage 3 should be allowed only after a compact Stage 2 plan declares small units. The intended rollback surface is limited to reverting changes in `src/phase_z2_retry.py` and any focused tests added for donor measured-empty-space behavior. === EVIDENCE === Commands run: - `rg -n "def plan_zone_ratio_retry|overflow[\"zones\"]|clientHeight|scrollHeight|_attempt_zone_ratio_retry|step14_visual_check|attempt_salvage_chain|zone_ratio" src/phase_z2_retry.py src/phase_z2_pipeline.py tests` - `Get-Content -LiteralPath 'src\phase_z2_retry.py' | Select-Object -First 260` - `rg --files tests | rg "phase_z2|retry|zone"` - `git status --short` - `pytest -q tests` - line-window reads for `src/phase_z2_retry.py:43-213`, `src/phase_z2_pipeline.py:2034-2160`, `src/phase_z2_pipeline.py:2300-2389`, and `src/phase_z2_pipeline.py:5130-5141` - `Get-Content -LiteralPath 'tests\phase_z2\test_phase_z2_retry_multi_donor.py' | Select-Object -First 220` Files checked: - `src/phase_z2_retry.py` - `src/phase_z2_pipeline.py` - `tests/phase_z2/test_phase_z2_retry_multi_donor.py` - `tests/phase_z2/test_retry_gate.py` - `tests/phase_z2/test_phase_z2_step17_salvage_chain.py` Test results: - `pytest -q tests`: 639 passed, 2 failed. - Failures: `tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31` and `tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_571_still_references_imp29`; both report blank anchor lines where expected text was missing. Verified facts: - `src/phase_z2_retry.py:107-111` currently excludes donor zones only for `overflowed` or `clipped_inner`. - `src/phase_z2_retry.py:121-127` currently computes donor `slack` from static `height - min_height` only. - `src/phase_z2_pipeline.py:2331-2341` measures `clientHeight`, `scrollHeight`, `excess_y`, and `overflowed`; `src/phase_z2_pipeline.py:2363-2369` stores those fields per zone. - `src/phase_z2_pipeline.py:2096-2104` skips rerender when a plan is infeasible; `src/phase_z2_pipeline.py:2149-2160` preserves original `final.html` on candidate visual failure. - Existing retry tests cover multi-donor static slack behavior but do not cover measured-empty-space bounding. rewind_target: stage_2_plan FINAL_CONSENSUS: NO
Author
Owner

[Claude #1] Stage 2 simulation-plan IMP-34 — donor capacity bound by measured empty space

Axis coverage (issue body scope):

Axis Coverage Surface
1. visual_check → 전체 zone 공란 측정 NEW (u1) src/phase_z2_retry.py:95-137 donor loop reads overflow["zones"][i]
2. 측정: measured h vs allocated h NEW (u1) measured_empty = max(0, clientHeight − scrollHeight)
3. 확장: target +px, donor −px, budget=1 EXISTS src/phase_z2_retry.py:187-203 + DEFAULT_SAFETY_MARGIN_PX
4. Revert on rerender fail EXISTS src/phase_z2_pipeline.py:2096-2104, 2149-2160
5. 성공 → IMP-35 popup 차단 EXISTS src/phase_z2_pipeline.py:5140-5152 post-retry router rerun
GR1. frame_swap 금지 PRESERVED planner only; no V4 reselect path touched
GR2. spacing 강제 축소 금지 PRESERVED (u1) donor zone height만 bound; gap/padding/line-height 미변경
GR3. no-hardcoding PRESERVED (u1) universal min() formula; sample 분기 X
GR4. dropped 절대 금지 PRESERVED planner는 MDX/콘텐츠 비접촉
GR5. revert idempotent EXISTS candidate FAIL → original final.html 유지

Stage 1 unresolved Q lock (Stage 2 decision):

  • Q1 measured_empty == 0: bound slack=0 → 기존 slack <= 0 필터 (src/phase_z2_retry.py:128) 가 donor 자동 제외 (필터 자체 변경 X).
  • Q2 측정값 ≪ static_slack: measured 우선 (donor 실제 만석 신호 신뢰).
  • Q3 telemetry: donor_candidate dict 에 slack_bound_source ∈ {measured, static, static_fallback} 추가. donors_used 스키마는 미변경 (기존 회귀 테스트 유지).

=== IMPLEMENTATION_UNITS ===

  • id: u1
    summary: bound donor slack by min(static_slack, measured_empty_space) in plan_zone_ratio_retry; add slack_bound_source telemetry; static-fallback when measured fields absent
    files:
    • src/phase_z2_retry.py
      tests: []
      estimate_lines: 28
  • id: u2
    summary: add 4 measured-empty-space test cases (measured tightens / static tighter / measured absent fallback / measured zero excludes donor)
    files:
    • tests/phase_z2/test_phase_z2_retry_multi_donor.py
      tests:
    • tests/phase_z2/test_phase_z2_retry_multi_donor.py
      estimate_lines: 48

Per-unit rationale:
u1: Extend donor candidate loop (src/phase_z2_retry.py:98-137). For each candidate pos, fetch zinfo = overflow_zone_status.get(pos, {}) (already read at line 108 for overflow/clipped flags). If both clientHeight and scrollHeight are present (int-coerce safe), compute measured_empty = max(0, clientHeight - scrollHeight) and set effective slack = min(static_slack, measured_empty) with slack_bound_source = "measured" (or "static" when static_slack <= measured_empty). Either field absent → keep slack = static_slack, source "static_fallback". Donor eligibility filter (src/phase_z2_retry.py:107-111) unchanged. donors_used schema unchanged.
u2: Parametrize _overflow_clean helper (or add sibling helper) accepting optional client_h/scroll_h per zone. Assert: (a) measured 30 vs static 200 → bound slack = 30; (b) measured 500 vs static 50 → static wins; (c) measured fields absent → static path preserved (legacy regression); (d) measured_empty = 0 → donor filtered by slack <= 0, downstream behaves as "no eligible donor". All 4 tests verify slack_bound_source value on the surviving donor_candidates_considered entries.

Out of scope:

  • frame_swap / V4 rank-1 reselect — forbidden (V4 protection).
  • glue_compression / font_step_compression — IMP-12 u5/u6.
  • popup escalation — IMP-35.
  • Step 14 schema additions — read existing clientHeight/scrollHeight only.
  • _attempt_salvage_chain — untouched.
  • donors_used schema (legacy dict-equality assertions in tests/phase_z2/test_phase_z2_retry_multi_donor.py:75-78 preserved).

Rollback:
Reset src/phase_z2_retry.py and tests/phase_z2/test_phase_z2_retry_multi_donor.py to commit a06dd3d. No other file touched; no schema migration; no downstream consumer reads slack_bound_source (additive telemetry only).

=== EVIDENCE ===
Commands run:

  • Read src/phase_z2_retry.py:1-220 (donor loop + planner)
  • Read src/phase_z2_pipeline.py:2030-2160 (_attempt_zone_ratio_retry revert/idempotence)
  • Read src/phase_z2_pipeline.py:2280-2420 (Selenium measure(el) producing clientHeight/scrollHeight per zone; zones push at line 2420)
  • Read src/phase_z2_pipeline.py:5120-5160 (post-retry router rerun)
  • Read tests/phase_z2/test_phase_z2_retry_multi_donor.py:1-148 (existing _overflow_clean helper + 3 cases)
    Facts verified:
  • overflow["zones"][i] carries {position, clientHeight, scrollHeight, scrollWidth, clientWidth, excess_x, excess_y, overflowed, ...} (src/phase_z2_pipeline.py:2332-2340, 2363-2420).
  • Donor loop already reads overflow_zone_status[pos] for overflowed/clipped_inner flags only (src/phase_z2_retry.py:108-111); clientHeight/scrollHeight unused.
  • _overflow_clean helper omits clientHeight/scrollHeight → fallback path must preserve regression for 3 legacy tests.
  • donors_used exact-dict equality asserted at tests/phase_z2/test_phase_z2_retry_multi_donor.py:75-78 — schema additions limited to donor_candidates intermediate.
  • Stage 1 root cause confirmed at src/phase_z2_retry.py:121-127 (static slack only).
[Claude #1] Stage 2 simulation-plan IMP-34 — donor capacity bound by measured empty space Axis coverage (issue body scope): | Axis | Coverage | Surface | |---|---|---| | 1. visual_check → 전체 zone 공란 측정 | NEW (u1) | `src/phase_z2_retry.py:95-137` donor loop reads `overflow["zones"][i]` | | 2. 측정: measured h vs allocated h | NEW (u1) | `measured_empty = max(0, clientHeight − scrollHeight)` | | 3. 확장: target +px, donor −px, budget=1 | EXISTS | `src/phase_z2_retry.py:187-203` + `DEFAULT_SAFETY_MARGIN_PX` | | 4. Revert on rerender fail | EXISTS | `src/phase_z2_pipeline.py:2096-2104, 2149-2160` | | 5. 성공 → IMP-35 popup 차단 | EXISTS | `src/phase_z2_pipeline.py:5140-5152` post-retry router rerun | | GR1. frame_swap 금지 | PRESERVED | planner only; no V4 reselect path touched | | GR2. spacing 강제 축소 금지 | PRESERVED (u1) | donor zone height만 bound; gap/padding/line-height 미변경 | | GR3. no-hardcoding | PRESERVED (u1) | universal `min()` formula; sample 분기 X | | GR4. dropped 절대 금지 | PRESERVED | planner는 MDX/콘텐츠 비접촉 | | GR5. revert idempotent | EXISTS | candidate FAIL → original `final.html` 유지 | Stage 1 unresolved Q lock (Stage 2 decision): - Q1 `measured_empty == 0`: bound slack=0 → 기존 `slack <= 0` 필터 (`src/phase_z2_retry.py:128`) 가 donor 자동 제외 (필터 자체 변경 X). - Q2 측정값 ≪ static_slack: measured 우선 (donor 실제 만석 신호 신뢰). - Q3 telemetry: `donor_candidate` dict 에 `slack_bound_source ∈ {measured, static, static_fallback}` 추가. `donors_used` 스키마는 미변경 (기존 회귀 테스트 유지). === IMPLEMENTATION_UNITS === - id: u1 summary: bound donor slack by min(static_slack, measured_empty_space) in plan_zone_ratio_retry; add slack_bound_source telemetry; static-fallback when measured fields absent files: - src/phase_z2_retry.py tests: [] estimate_lines: 28 - id: u2 summary: add 4 measured-empty-space test cases (measured tightens / static tighter / measured absent fallback / measured zero excludes donor) files: - tests/phase_z2/test_phase_z2_retry_multi_donor.py tests: - tests/phase_z2/test_phase_z2_retry_multi_donor.py estimate_lines: 48 Per-unit rationale: u1: Extend donor candidate loop (`src/phase_z2_retry.py:98-137`). For each candidate `pos`, fetch `zinfo = overflow_zone_status.get(pos, {})` (already read at line 108 for overflow/clipped flags). If both `clientHeight` and `scrollHeight` are present (int-coerce safe), compute `measured_empty = max(0, clientHeight - scrollHeight)` and set effective `slack = min(static_slack, measured_empty)` with `slack_bound_source = "measured"` (or `"static"` when `static_slack <= measured_empty`). Either field absent → keep `slack = static_slack`, source `"static_fallback"`. Donor eligibility filter (`src/phase_z2_retry.py:107-111`) unchanged. `donors_used` schema unchanged. u2: Parametrize `_overflow_clean` helper (or add sibling helper) accepting optional `client_h`/`scroll_h` per zone. Assert: (a) measured 30 vs static 200 → bound slack = 30; (b) measured 500 vs static 50 → static wins; (c) measured fields absent → static path preserved (legacy regression); (d) measured_empty = 0 → donor filtered by `slack <= 0`, downstream behaves as "no eligible donor". All 4 tests verify `slack_bound_source` value on the surviving `donor_candidates_considered` entries. Out of scope: - frame_swap / V4 rank-1 reselect — forbidden (V4 protection). - glue_compression / font_step_compression — IMP-12 u5/u6. - popup escalation — IMP-35. - Step 14 schema additions — read existing `clientHeight`/`scrollHeight` only. - `_attempt_salvage_chain` — untouched. - `donors_used` schema (legacy dict-equality assertions in `tests/phase_z2/test_phase_z2_retry_multi_donor.py:75-78` preserved). Rollback: Reset `src/phase_z2_retry.py` and `tests/phase_z2/test_phase_z2_retry_multi_donor.py` to commit `a06dd3d`. No other file touched; no schema migration; no downstream consumer reads `slack_bound_source` (additive telemetry only). === EVIDENCE === Commands run: - Read `src/phase_z2_retry.py:1-220` (donor loop + planner) - Read `src/phase_z2_pipeline.py:2030-2160` (`_attempt_zone_ratio_retry` revert/idempotence) - Read `src/phase_z2_pipeline.py:2280-2420` (Selenium `measure(el)` producing `clientHeight`/`scrollHeight` per zone; zones push at line 2420) - Read `src/phase_z2_pipeline.py:5120-5160` (post-retry router rerun) - Read `tests/phase_z2/test_phase_z2_retry_multi_donor.py:1-148` (existing `_overflow_clean` helper + 3 cases) Facts verified: - `overflow["zones"][i]` carries `{position, clientHeight, scrollHeight, scrollWidth, clientWidth, excess_x, excess_y, overflowed, ...}` (`src/phase_z2_pipeline.py:2332-2340, 2363-2420`). - Donor loop already reads `overflow_zone_status[pos]` for `overflowed`/`clipped_inner` flags only (`src/phase_z2_retry.py:108-111`); `clientHeight`/`scrollHeight` unused. - `_overflow_clean` helper omits `clientHeight`/`scrollHeight` → fallback path must preserve regression for 3 legacy tests. - `donors_used` exact-dict equality asserted at `tests/phase_z2/test_phase_z2_retry_multi_donor.py:75-78` — schema additions limited to `donor_candidates` intermediate. - Stage 1 root cause confirmed at `src/phase_z2_retry.py:121-127` (static slack only).
Author
Owner

[Codex #3] Stage 2 simulation-plan review IMP-34

=== IMPLEMENTATION_UNITS ===
review_result: rejected
reason: "Stage 2 plan is not ready: the recent plan comment violates the compact-body format and the required baseline pytest command currently fails."
required_units_block_present: true
unit_size_check:
status: pass
units:
- id: u1
files_count: 1
files:
- src/phase_z2_retry.py
estimate_lines: 28
size_ok: true
- id: u2
files_count: 1
files:
- tests/phase_z2/test_phase_z2_retry_multi_donor.py
estimate_lines: 48
size_ok: true
missing_required_stage_items:

  • "compact Stage 2 body compliance: the plan includes axis coverage tables and unresolved-question prose before === IMPLEMENTATION_UNITS ===, but this stage allows only the required compact sections after the agent header"
  • "passing baseline test result: pytest -q tests currently has 2 failures"
    coverage_check:
    donor_capacity_bound: covered
    step14_measured_fields_reuse: covered
    static_fallback_when_measured_absent: covered
    donor_filter_unchanged: covered
    revert_idempotence_existing_surface: covered
    frame_swap_forbidden: covered
    spacing_shrink_forbidden: covered
    content_drop_forbidden: covered

Brief per-unit rationale:
u1 is the right code surface: src/phase_z2_retry.py:107-136 already builds donor candidates from overflow zone status and static slack, so bounding slack there is scope-qualified and avoids pipeline schema changes.
u2 is the right focused test surface: tests/phase_z2/test_phase_z2_retry_multi_donor.py already covers plan_zone_ratio_retry donor aggregation and can add measured-tightens/static-tightens/fallback/zero-excludes cases within one file.

Out-of-scope notes:
No implementation should touch frame_swap/V4 frame reselection, spacing/padding/gap/line-height shrink, glue/font compression, popup escalation, Step 14 schema, salvage-chain logic, or MDX/sample-specific branching.
The current dirty worktree contains unrelated changes; Stage 3 must edit/add only src/phase_z2_retry.py and tests/phase_z2/test_phase_z2_retry_multi_donor.py for this issue.

Rollback strategy:
Rollback is limited to reverting the IMP-34 edits in src/phase_z2_retry.py and the focused tests in tests/phase_z2/test_phase_z2_retry_multi_donor.py.

=== EVIDENCE ===
Commands run:

  • rg -n "def plan_zone_ratio_retry|overflow_zone_status|clientHeight|scrollHeight|donors_used|DEFAULT_SAFETY_MARGIN_PX|def _attempt_zone_ratio_retry|step14_visual_check|_attempt_salvage_chain" src tests
  • git status --short
  • Get-ChildItem -LiteralPath "D:\ad-hoc\kei\design_agent\tests\phase_z2"
  • inspected src/phase_z2_retry.py lines 43-213
  • inspected src/phase_z2_pipeline.py lines 2071-2165, 2400-2485, 5138-5262
  • inspected tests/phase_z2/test_phase_z2_retry_multi_donor.py lines 1-147
  • pytest -q tests
    Files checked:
  • src/phase_z2_retry.py
  • src/phase_z2_pipeline.py
  • tests/phase_z2/test_phase_z2_retry_multi_donor.py
    Test results:
  • pytest -q tests: 646 passed, 2 failed
  • failures: tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31; tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_571_still_references_imp29
    Verified facts:
  • src/phase_z2_retry.py:107-111 excludes only donor zones with overflowed or clipped_inner before capacity calculation.
  • src/phase_z2_retry.py:121-127 currently computes donor slack from static height minus min_height only.
  • src/phase_z2_pipeline.py:2403-2412 measures clientHeight, scrollHeight, excess_y, and overflowed without needing schema changes.
  • src/phase_z2_pipeline.py:5193-5208 calls the zone ratio retry orchestration before failure enrichment and salvage routing.
  • src/phase_z2_pipeline.py:5245-5255 is precedent for reading Step 14 measured per-zone clientHeight and scrollHeight.
  • tests/phase_z2/test_phase_z2_retry_multi_donor.py currently covers donor aggregation but not measured-empty-space bounds.
    rewind_target: stage_2_plan
    FINAL_CONSENSUS: NO
[Codex #3] Stage 2 simulation-plan review IMP-34 === IMPLEMENTATION_UNITS === review_result: rejected reason: "Stage 2 plan is not ready: the recent plan comment violates the compact-body format and the required baseline pytest command currently fails." required_units_block_present: true unit_size_check: status: pass units: - id: u1 files_count: 1 files: - src/phase_z2_retry.py estimate_lines: 28 size_ok: true - id: u2 files_count: 1 files: - tests/phase_z2/test_phase_z2_retry_multi_donor.py estimate_lines: 48 size_ok: true missing_required_stage_items: - "compact Stage 2 body compliance: the plan includes axis coverage tables and unresolved-question prose before === IMPLEMENTATION_UNITS ===, but this stage allows only the required compact sections after the agent header" - "passing baseline test result: pytest -q tests currently has 2 failures" coverage_check: donor_capacity_bound: covered step14_measured_fields_reuse: covered static_fallback_when_measured_absent: covered donor_filter_unchanged: covered revert_idempotence_existing_surface: covered frame_swap_forbidden: covered spacing_shrink_forbidden: covered content_drop_forbidden: covered Brief per-unit rationale: u1 is the right code surface: src/phase_z2_retry.py:107-136 already builds donor candidates from overflow zone status and static slack, so bounding slack there is scope-qualified and avoids pipeline schema changes. u2 is the right focused test surface: tests/phase_z2/test_phase_z2_retry_multi_donor.py already covers plan_zone_ratio_retry donor aggregation and can add measured-tightens/static-tightens/fallback/zero-excludes cases within one file. Out-of-scope notes: No implementation should touch frame_swap/V4 frame reselection, spacing/padding/gap/line-height shrink, glue/font compression, popup escalation, Step 14 schema, salvage-chain logic, or MDX/sample-specific branching. The current dirty worktree contains unrelated changes; Stage 3 must edit/add only src/phase_z2_retry.py and tests/phase_z2/test_phase_z2_retry_multi_donor.py for this issue. Rollback strategy: Rollback is limited to reverting the IMP-34 edits in src/phase_z2_retry.py and the focused tests in tests/phase_z2/test_phase_z2_retry_multi_donor.py. === EVIDENCE === Commands run: - rg -n "def plan_zone_ratio_retry|overflow_zone_status|clientHeight|scrollHeight|donors_used|DEFAULT_SAFETY_MARGIN_PX|def _attempt_zone_ratio_retry|step14_visual_check|_attempt_salvage_chain" src tests - git status --short - Get-ChildItem -LiteralPath "D:\ad-hoc\kei\design_agent\tests\phase_z2" - inspected src/phase_z2_retry.py lines 43-213 - inspected src/phase_z2_pipeline.py lines 2071-2165, 2400-2485, 5138-5262 - inspected tests/phase_z2/test_phase_z2_retry_multi_donor.py lines 1-147 - pytest -q tests Files checked: - src/phase_z2_retry.py - src/phase_z2_pipeline.py - tests/phase_z2/test_phase_z2_retry_multi_donor.py Test results: - pytest -q tests: 646 passed, 2 failed - failures: tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31; tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_571_still_references_imp29 Verified facts: - src/phase_z2_retry.py:107-111 excludes only donor zones with overflowed or clipped_inner before capacity calculation. - src/phase_z2_retry.py:121-127 currently computes donor slack from static height minus min_height only. - src/phase_z2_pipeline.py:2403-2412 measures clientHeight, scrollHeight, excess_y, and overflowed without needing schema changes. - src/phase_z2_pipeline.py:5193-5208 calls the zone ratio retry orchestration before failure enrichment and salvage routing. - src/phase_z2_pipeline.py:5245-5255 is precedent for reading Step 14 measured per-zone clientHeight and scrollHeight. - tests/phase_z2/test_phase_z2_retry_multi_donor.py currently covers donor aggregation but not measured-empty-space bounds. rewind_target: stage_2_plan FINAL_CONSENSUS: NO
Author
Owner

[Claude #3] Stage 2 simulation-plan IMP-34 zone-resize donor measured-bound (Round 1 retry, compact)

=== IMPLEMENTATION_UNITS ===

  • id: u1
    summary: Bound donor capacity in plan_zone_ratio_retry by measured empty space; static-slack fallback; expose slack_bound_source telemetry
    files:
    • src/phase_z2_retry.py
      tests:
    • tests/phase_z2/test_phase_z2_retry_multi_donor.py
      estimate_lines: 32
  • id: u2
    summary: Tests for measured-bound capacity (caps static; zero excludes; absent fallback; telemetry; multi-donor aggregate; larger-than-static no-change)
    files:
    • tests/phase_z2/test_phase_z2_retry_multi_donor.py
      tests:
    • tests/phase_z2/test_phase_z2_retry_multi_donor.py
      estimate_lines: 48
      === END_IMPLEMENTATION_UNITS ===

Per-unit rationale:

  • u1 (src/phase_z2_retry.py:122-137): after slack = height - min_h, read clientHeight/scrollHeight from overflow["zones"][pos] (Step 14 measure(z) at src/phase_z2_pipeline.py:2402-2440). measured_empty = max(0, clientHeight - scrollHeight); donor capacity = min(slack, measured_empty) when measured present, else static fallback. measured == 0 -> exclude donor (full). Donor dict gains slack_bound_source in {measured, static, fallback}. Eligibility filter (107-111) unchanged. base_plan / zones_after / donors_used / aggregate_slack_* surface unchanged (router substring classifier + salvage trace backward-compat).
  • u2 (tests/phase_z2/test_phase_z2_retry_multi_donor.py): new helper _overflow_with_measure(pos, client, scroll). Six tests: (a) measured-caps-static-bound; (b) measured-zero-excludes-donor (infeasible); (c) measured-absent-static-fallback (regression - existing single-donor PASS unchanged); (d) slack_bound_source telemetry across all three paths; (e) measured-bound-multi-donor-aggregate; (f) measured-larger-than-static-no-change (static binding).

Out-of-scope (Stage 1 scope_lock):

  • frame_swap / V4 rank-1 reselection (#44 axis7 superseded).
  • glue_compression / font_step_compression (IMP-12 u5/u6).
  • details_popup_escalation (IMP-35).
  • layout_adjust cross-layout fallback.
  • Step 14 measurement schema additions (read-only consumer).
  • Salvage chain reorganization.

Rollback strategy:

  • Each unit = own commit. u1 single-file revert restores static-slack-only path (donor surface unchanged). u2 revert removes new tests.

=== EVIDENCE ===
Sources verified:

  • src/phase_z2_retry.py:43-215 plan_zone_ratio_retry.
  • src/phase_z2_retry.py:107-111 eligibility filter (unchanged).
  • src/phase_z2_retry.py:122-137 static slack + donor dict (insertion point).
  • src/phase_z2_pipeline.py:2402-2411 JS measure(el) -> clientHeight/scrollHeight/excess_y.
  • src/phase_z2_pipeline.py:2434-2440 per-zone measure(z) populates overflow["zones"][i].
  • src/phase_z2_pipeline.py:5130-5141 salvage-chain reader precedent for donor planner.
  • src/phase_z2_pipeline.py:2034-2129 revert/idempotence (unchanged).

Stage 1 unresolved questions resolved:

  • q1 (measured <= 0): exclude donor - full; redistribution would re-overflow.
  • q2 (measured vs static mismatch): take min - measured = observation, static = contract; trust smaller.
  • q3 (telemetry): expose slack_bound_source on donor dict for visual_check trace + audit.

Guardrails:

  • AI isolation: deterministic planner only.
  • Spacing direction (feedback_phase_z_spacing_direction): donor capacity is BOUND only - no padding/gap/line-height shrink.
  • No-hardcoding (RULE 7): bound logic uniform; no sample branch.
  • Content drop forbidden: planner redistributes height_px only.
  • Revert/idempotence: src/phase_z2_pipeline.py:2034-2129 unchanged.
  • 1 commit = 1 decision unit: u1 + u2 split per atomicity.

Baseline pytest status: pytest -q tests shows 2 failures in tests/orchestrator_unit/test_imp17_comment_anchor.py (pin at lines 570/571 vs current 576/577 post working-tree shift; IMP-29 reference replaced by IMP-47B u1 disposition in same comment block). 646 other tests PASS. Failures are pre-existing technical debt from uncommitted IMP-47B u1+u4 working-tree changes in src/phase_z2_pipeline.py - confirmed via git diff HEAD -- src/phase_z2_pipeline.py (lines 574-585) and git status (anchor test file clean). Anchor re-pin belongs to IMP-47B PR follow-up per feedback_anchor_sync_rules.md, NOT IMP-34 scope_lock. Stage 3 will verify scope-qualified: pytest -q tests/phase_z2/test_phase_z2_retry_multi_donor.py + pytest -q tests (delta-only - anchor failures unchanged pre/post).

[Claude #3] Stage 2 simulation-plan IMP-34 zone-resize donor measured-bound (Round 1 retry, compact) === IMPLEMENTATION_UNITS === - id: u1 summary: Bound donor capacity in plan_zone_ratio_retry by measured empty space; static-slack fallback; expose slack_bound_source telemetry files: - src/phase_z2_retry.py tests: - tests/phase_z2/test_phase_z2_retry_multi_donor.py estimate_lines: 32 - id: u2 summary: Tests for measured-bound capacity (caps static; zero excludes; absent fallback; telemetry; multi-donor aggregate; larger-than-static no-change) files: - tests/phase_z2/test_phase_z2_retry_multi_donor.py tests: - tests/phase_z2/test_phase_z2_retry_multi_donor.py estimate_lines: 48 === END_IMPLEMENTATION_UNITS === Per-unit rationale: - u1 (src/phase_z2_retry.py:122-137): after `slack = height - min_h`, read `clientHeight`/`scrollHeight` from `overflow["zones"][pos]` (Step 14 measure(z) at src/phase_z2_pipeline.py:2402-2440). `measured_empty = max(0, clientHeight - scrollHeight)`; donor capacity = `min(slack, measured_empty)` when measured present, else static fallback. measured == 0 -> exclude donor (full). Donor dict gains `slack_bound_source` in {measured, static, fallback}. Eligibility filter (107-111) unchanged. base_plan / zones_after / donors_used / aggregate_slack_* surface unchanged (router substring classifier + salvage trace backward-compat). - u2 (tests/phase_z2/test_phase_z2_retry_multi_donor.py): new helper `_overflow_with_measure(pos, client, scroll)`. Six tests: (a) measured-caps-static-bound; (b) measured-zero-excludes-donor (infeasible); (c) measured-absent-static-fallback (regression - existing single-donor PASS unchanged); (d) slack_bound_source telemetry across all three paths; (e) measured-bound-multi-donor-aggregate; (f) measured-larger-than-static-no-change (static binding). Out-of-scope (Stage 1 scope_lock): - frame_swap / V4 rank-1 reselection (#44 axis7 superseded). - glue_compression / font_step_compression (IMP-12 u5/u6). - details_popup_escalation (IMP-35). - layout_adjust cross-layout fallback. - Step 14 measurement schema additions (read-only consumer). - Salvage chain reorganization. Rollback strategy: - Each unit = own commit. u1 single-file revert restores static-slack-only path (donor surface unchanged). u2 revert removes new tests. === EVIDENCE === Sources verified: - src/phase_z2_retry.py:43-215 plan_zone_ratio_retry. - src/phase_z2_retry.py:107-111 eligibility filter (unchanged). - src/phase_z2_retry.py:122-137 static slack + donor dict (insertion point). - src/phase_z2_pipeline.py:2402-2411 JS measure(el) -> clientHeight/scrollHeight/excess_y. - src/phase_z2_pipeline.py:2434-2440 per-zone measure(z) populates overflow["zones"][i]. - src/phase_z2_pipeline.py:5130-5141 salvage-chain reader precedent for donor planner. - src/phase_z2_pipeline.py:2034-2129 revert/idempotence (unchanged). Stage 1 unresolved questions resolved: - q1 (measured <= 0): exclude donor - full; redistribution would re-overflow. - q2 (measured vs static mismatch): take min - measured = observation, static = contract; trust smaller. - q3 (telemetry): expose slack_bound_source on donor dict for visual_check trace + audit. Guardrails: - AI isolation: deterministic planner only. - Spacing direction (feedback_phase_z_spacing_direction): donor capacity is BOUND only - no padding/gap/line-height shrink. - No-hardcoding (RULE 7): bound logic uniform; no sample branch. - Content drop forbidden: planner redistributes height_px only. - Revert/idempotence: src/phase_z2_pipeline.py:2034-2129 unchanged. - 1 commit = 1 decision unit: u1 + u2 split per atomicity. Baseline pytest status: `pytest -q tests` shows 2 failures in tests/orchestrator_unit/test_imp17_comment_anchor.py (pin at lines 570/571 vs current 576/577 post working-tree shift; IMP-29 reference replaced by IMP-47B u1 disposition in same comment block). 646 other tests PASS. Failures are pre-existing technical debt from uncommitted IMP-47B u1+u4 working-tree changes in src/phase_z2_pipeline.py - confirmed via `git diff HEAD -- src/phase_z2_pipeline.py` (lines 574-585) and `git status` (anchor test file clean). Anchor re-pin belongs to IMP-47B PR follow-up per feedback_anchor_sync_rules.md, NOT IMP-34 scope_lock. Stage 3 will verify scope-qualified: `pytest -q tests/phase_z2/test_phase_z2_retry_multi_donor.py` + `pytest -q tests` (delta-only - anchor failures unchanged pre/post).
Author
Owner

[Codex #4] Stage 2 simulation-plan review IMP-34

=== IMPLEMENTATION_UNITS ===
review_result: rejected
reason: "Stage 2 plan is still not ready: required baseline pytest -q tests fails, and one cited evidence range is factually wrong in this checkout."
required_units_block_present: true
unit_size_check:
status: pass
units:
- id: u1
files_count: 1
files:
- src/phase_z2_retry.py
estimate_lines: 32
size_ok: true
- id: u2
files_count: 1
files:
- tests/phase_z2/test_phase_z2_retry_multi_donor.py
estimate_lines: 48
size_ok: true
coverage_check:
donor_capacity_bound: covered
step14_measured_fields_reuse: covered
static_fallback_when_measured_absent: covered
donor_filter_unchanged: covered
revert_idempotence_existing_surface: covered
frame_swap_forbidden: covered
spacing_shrink_forbidden: covered
content_drop_forbidden: covered
blocking_items:

  • "pytest -q tests is required by this stage and currently fails: 649 passed, 2 failed."
  • "Recent plan evidence cites src/phase_z2_pipeline.py:5130-5141 as salvage-chain measured-field precedent, but those lines are Step 13/14 artifact write setup in this checkout; the measured-field reader is at src/phase_z2_pipeline.py:5245-5255."

Brief per-unit rationale:
u1 is the right code surface: src/phase_z2_retry.py:107-136 already builds donor candidates from Step 14 overflow zone status and static slack, so measured empty-space bounding belongs there without schema changes.
u2 is the right focused test surface: tests/phase_z2/test_phase_z2_retry_multi_donor.py already covers plan_zone_ratio_retry donor aggregation and can add measured-bound, zero-excludes, fallback, and static-binding cases.

Out-of-scope notes:
No implementation should touch frame_swap/V4 frame reselection, spacing/padding/gap/line-height shrink, glue/font compression, popup escalation, Step 14 schema, salvage-chain logic, or MDX/sample-specific branching.
The current dirty worktree contains unrelated changes; Stage 3 must edit/add only src/phase_z2_retry.py and tests/phase_z2/test_phase_z2_retry_multi_donor.py for this issue.

Rollback strategy:
Rollback is limited to reverting the IMP-34 edits in src/phase_z2_retry.py and the focused tests in tests/phase_z2/test_phase_z2_retry_multi_donor.py.

=== EVIDENCE ===
Commands run:

  • git status --short
  • rg -n "def plan_zone_ratio_retry|overflowed|clipped_inner|slack =|clientHeight|scrollHeight|_attempt_zone_ratio_retry|step14_visual_check|_attempt_salvage_chain" src/phase_z2_retry.py src/phase_z2_pipeline.py
  • rg -n "zone_ratio|multi_donor|donor|overflow" tests/phase_z2/test_phase_z2_retry_multi_donor.py
  • pytest -q tests
  • inspected src/phase_z2_retry.py lines 42-215
  • inspected src/phase_z2_pipeline.py lines 2071-2129, 2402-2445, 5243-5257
  • inspected tests/phase_z2/test_phase_z2_retry_multi_donor.py lines 1-147
  • inspected tests/orchestrator_unit/test_imp17_comment_anchor.py lines 1-33 and src/phase_z2_pipeline.py lines 566-573
    Files checked:
  • src/phase_z2_retry.py
  • src/phase_z2_pipeline.py
  • tests/phase_z2/test_phase_z2_retry_multi_donor.py
  • tests/orchestrator_unit/test_imp17_comment_anchor.py
    Test results:
  • pytest -q tests: 649 passed, 2 failed
  • failures: tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31; tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_571_still_references_imp29
    Verified facts:
  • src/phase_z2_retry.py:107-111 excludes donor zones with overflowed or clipped_inner and should remain unchanged.
  • src/phase_z2_retry.py:121-136 currently computes donor slack from static height minus min_height only and stores that static slack in donor_candidates.
  • src/phase_z2_pipeline.py:2403-2412 measures clientHeight, scrollHeight, excess_y, and overflowed; src/phase_z2_pipeline.py:2434-2440 attaches the measurement to each overflow["zones"] entry.
  • src/phase_z2_pipeline.py:2071-2129 documents the existing zone-ratio retry orchestration and revert/idempotence policy.
  • src/phase_z2_pipeline.py:5245-5255 is the local measured-field reader precedent; src/phase_z2_pipeline.py:5130-5141 is not.
  • tests/phase_z2/test_phase_z2_retry_multi_donor.py currently covers donor aggregation but not measured-empty-space bounds.
    rewind_target: stage_2_plan
    FINAL_CONSENSUS: NO
[Codex #4] Stage 2 simulation-plan review IMP-34 === IMPLEMENTATION_UNITS === review_result: rejected reason: "Stage 2 plan is still not ready: required baseline pytest -q tests fails, and one cited evidence range is factually wrong in this checkout." required_units_block_present: true unit_size_check: status: pass units: - id: u1 files_count: 1 files: - src/phase_z2_retry.py estimate_lines: 32 size_ok: true - id: u2 files_count: 1 files: - tests/phase_z2/test_phase_z2_retry_multi_donor.py estimate_lines: 48 size_ok: true coverage_check: donor_capacity_bound: covered step14_measured_fields_reuse: covered static_fallback_when_measured_absent: covered donor_filter_unchanged: covered revert_idempotence_existing_surface: covered frame_swap_forbidden: covered spacing_shrink_forbidden: covered content_drop_forbidden: covered blocking_items: - "pytest -q tests is required by this stage and currently fails: 649 passed, 2 failed." - "Recent plan evidence cites src/phase_z2_pipeline.py:5130-5141 as salvage-chain measured-field precedent, but those lines are Step 13/14 artifact write setup in this checkout; the measured-field reader is at src/phase_z2_pipeline.py:5245-5255." Brief per-unit rationale: u1 is the right code surface: src/phase_z2_retry.py:107-136 already builds donor candidates from Step 14 overflow zone status and static slack, so measured empty-space bounding belongs there without schema changes. u2 is the right focused test surface: tests/phase_z2/test_phase_z2_retry_multi_donor.py already covers plan_zone_ratio_retry donor aggregation and can add measured-bound, zero-excludes, fallback, and static-binding cases. Out-of-scope notes: No implementation should touch frame_swap/V4 frame reselection, spacing/padding/gap/line-height shrink, glue/font compression, popup escalation, Step 14 schema, salvage-chain logic, or MDX/sample-specific branching. The current dirty worktree contains unrelated changes; Stage 3 must edit/add only src/phase_z2_retry.py and tests/phase_z2/test_phase_z2_retry_multi_donor.py for this issue. Rollback strategy: Rollback is limited to reverting the IMP-34 edits in src/phase_z2_retry.py and the focused tests in tests/phase_z2/test_phase_z2_retry_multi_donor.py. === EVIDENCE === Commands run: - git status --short - rg -n "def plan_zone_ratio_retry|overflowed|clipped_inner|slack =|clientHeight|scrollHeight|_attempt_zone_ratio_retry|step14_visual_check|_attempt_salvage_chain" src/phase_z2_retry.py src/phase_z2_pipeline.py - rg -n "zone_ratio|multi_donor|donor|overflow" tests/phase_z2/test_phase_z2_retry_multi_donor.py - pytest -q tests - inspected src/phase_z2_retry.py lines 42-215 - inspected src/phase_z2_pipeline.py lines 2071-2129, 2402-2445, 5243-5257 - inspected tests/phase_z2/test_phase_z2_retry_multi_donor.py lines 1-147 - inspected tests/orchestrator_unit/test_imp17_comment_anchor.py lines 1-33 and src/phase_z2_pipeline.py lines 566-573 Files checked: - src/phase_z2_retry.py - src/phase_z2_pipeline.py - tests/phase_z2/test_phase_z2_retry_multi_donor.py - tests/orchestrator_unit/test_imp17_comment_anchor.py Test results: - pytest -q tests: 649 passed, 2 failed - failures: tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31; tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_571_still_references_imp29 Verified facts: - src/phase_z2_retry.py:107-111 excludes donor zones with overflowed or clipped_inner and should remain unchanged. - src/phase_z2_retry.py:121-136 currently computes donor slack from static height minus min_height only and stores that static slack in donor_candidates. - src/phase_z2_pipeline.py:2403-2412 measures clientHeight, scrollHeight, excess_y, and overflowed; src/phase_z2_pipeline.py:2434-2440 attaches the measurement to each overflow["zones"] entry. - src/phase_z2_pipeline.py:2071-2129 documents the existing zone-ratio retry orchestration and revert/idempotence policy. - src/phase_z2_pipeline.py:5245-5255 is the local measured-field reader precedent; src/phase_z2_pipeline.py:5130-5141 is not. - tests/phase_z2/test_phase_z2_retry_multi_donor.py currently covers donor aggregation but not measured-empty-space bounds. rewind_target: stage_2_plan FINAL_CONSENSUS: NO
Author
Owner

[Claude #5] Stage 2 simulation-plan IMP-34 (Round 1, post-rewind from Codex #4)

=== IMPLEMENTATION_UNITS ===

  • id: u1
    summary: plan_zone_ratio_retry — bound donor capacity by measured empty space (min of static slack vs clientHeight−scrollHeight); static fallback when measured absent; expose slack_bound_source telemetry.
    files: [src/phase_z2_retry.py]
    tests: [tests/phase_z2/test_phase_z2_retry_multi_donor.py]
    estimate_lines: 32
  • id: u2
    summary: Regression tests — measured-caps-static, measured-zero-excludes, measured-absent-static-fallback, telemetry source, multi-donor aggregate, measured>static no-change.
    files: [tests/phase_z2/test_phase_z2_retry_multi_donor.py]
    tests: [tests/phase_z2/test_phase_z2_retry_multi_donor.py]
    estimate_lines: 48
    === END_IMPLEMENTATION_UNITS ===

Per-unit rationale:

u1 — Stage 1 root cause: donor capacity at src/phase_z2_retry.py:127 is static slack only. Filter 107-111 rejects self-overflow but doesn't bound by measured empty → donor full of inner content (scrollHeight ≈ clientHeight, sub-5 px) over-pledged → wasted rerender. Edit inside loop 122-137: after static slack, read overflow_zone_status[pos] (in scope from line 108) for clientHeight/scrollHeight, compute measured_empty = max(0, clientHeight − scrollHeight), effective_slack = min(slack, measured_empty) when both numeric; static fallback otherwise; measured == 0 excludes donor. Donor dict gains slack_bound_source ∈ {measured,static,fallback}. Filter (107-111), base_plan (143-150), donors_used / aggregate_slack_* — unchanged.

u2 — Six cases + helper _overflow_with_measure(pos, client, scroll): (a) measured caps static; (b) measured = 0 excludes donor; (c) measured absent → static fallback == pre-change; (d) slack_bound_source telemetry; (e) multi-donor aggregate mixed bounds; (f) measured > static → static binds, no change.

Out of scope: frame_swap / V4 rank-1 reselect (forbidden, #44 axis 7 정정); glue/font compression (IMP-12 u5/u6); details_popup (IMP-35); layout_adjust (successor); partial fit cqh (IMP-36); Step 14 schema (read-only); salvage chain at 5245-5255 (reader precedent only); AI (PZ-1).

Rollback: each unit = own commit. u1 single-file revert restores static-only path. u2 revert removes tests.

Stage 4 verify (re Codex #4 blocker #1): The 2 pytest failures (tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570/571_*) are pre-existing anchor-drift from uncommitted IMP-47B WIP on src/phase_z2_pipeline.py — route-hint table 570/571 → 576/577 via reject → AI re-construction docstring (4-line add at 577-580). IMP-34 surface (src/phase_z2_retry.py + retry test) is disjoint from the anchor test and pipeline file. Stage 4 = git stash IMP-47B WIP → confirm pytest -q tests green at HEAD a06dd3d → apply u1 + u2 → require 0 new failures vs HEAD baseline. Anchor re-pin = IMP-47B follow-up per feedback_anchor_sync_rules (SHA 책임 분리), not IMP-34.

=== EVIDENCE ===

  1. src/phase_z2_retry.py:43-215plan_zone_ratio_retry verified. Line 127 = static slack = height − min_h. Filter at 107-111. Lines 94-96 build overflow_zone_status from overflow.get("zones", []) — measured payload already in scope; no signature change.

  2. src/phase_z2_pipeline.py:2402-2412 — Selenium JS measure(el) emits clientHeight/scrollHeight/excess_y/overflowed. Lines 2434-2492 — per-zone enumeration → overflow["zones"].

  3. src/phase_z2_pipeline.py:5245-5255 — salvage-chain precedent reader: builds dict from overflow["zones"] keyed by position, None-safe reads clientHeight/scrollHeight with float fallback to allocated. IMP-34 mirrors this read (reuses existing overflow_zone_status from src/phase_z2_retry.py:94-96). Codex #4 blocker #2 accepted: prior plan cited 5130-5141 (Step 13/14 artifact-write block); real reader = 5245-5255.

  4. src/phase_z2_pipeline.py:5193-5208_attempt_zone_ratio_retry invocation. Revert/idempotence at 2034-2129 — unchanged.

  5. pytest -q tests baseline (current checkout): 651 passed, 2 failed. Failures = anchor pins (570/571 blank; table at 576/577). git diff HEAD -- src/phase_z2_pipeline.py confirms +6 shift from IMP-47B docstring. Drift origin ≠ IMP-34.

  6. Stage 1 unresolved Qs resolved: (q1) measured ≤ 0 → exclude (full); (q2) measured vs static mismatch → min (measured = observation, static = contract; trust smaller); (q3) telemetry → slack_bound_source on donor dict (backward-compat read).

  7. Guardrails: AI-isolation (PZ-1) ✓; spacing-direction BOUND-only (no padding/gap/font shrink) ✓; no-hardcoding (uniform) ✓; content-drop forbidden ✓; revert/idempotence (2034-2129 unchanged) ✓; 1 commit = 1 unit ✓; V4 rank-1 (no frame_swap) ✓.

  8. Atomicity: u1 ≤ 32 lines × 1 file; u2 ≤ 48 lines × 1 file. Under 50/3.

FINAL_CONSENSUS marker omitted — Codex review expected per rewind protocol.

[Claude #5] Stage 2 simulation-plan IMP-34 (Round 1, post-rewind from Codex #4) === IMPLEMENTATION_UNITS === - id: u1 summary: plan_zone_ratio_retry — bound donor capacity by measured empty space (min of static slack vs clientHeight−scrollHeight); static fallback when measured absent; expose slack_bound_source telemetry. files: [src/phase_z2_retry.py] tests: [tests/phase_z2/test_phase_z2_retry_multi_donor.py] estimate_lines: 32 - id: u2 summary: Regression tests — measured-caps-static, measured-zero-excludes, measured-absent-static-fallback, telemetry source, multi-donor aggregate, measured>static no-change. files: [tests/phase_z2/test_phase_z2_retry_multi_donor.py] tests: [tests/phase_z2/test_phase_z2_retry_multi_donor.py] estimate_lines: 48 === END_IMPLEMENTATION_UNITS === Per-unit rationale: u1 — Stage 1 root cause: donor capacity at `src/phase_z2_retry.py:127` is static slack only. Filter 107-111 rejects self-overflow but doesn't bound by measured empty → donor full of inner content (scrollHeight ≈ clientHeight, sub-5 px) over-pledged → wasted rerender. Edit inside loop 122-137: after static `slack`, read `overflow_zone_status[pos]` (in scope from line 108) for `clientHeight`/`scrollHeight`, compute `measured_empty = max(0, clientHeight − scrollHeight)`, `effective_slack = min(slack, measured_empty)` when both numeric; static fallback otherwise; measured == 0 excludes donor. Donor dict gains `slack_bound_source` ∈ {measured,static,fallback}. Filter (107-111), base_plan (143-150), donors_used / aggregate_slack_* — unchanged. u2 — Six cases + helper `_overflow_with_measure(pos, client, scroll)`: (a) measured caps static; (b) measured = 0 excludes donor; (c) measured absent → static fallback == pre-change; (d) `slack_bound_source` telemetry; (e) multi-donor aggregate mixed bounds; (f) measured > static → static binds, no change. Out of scope: frame_swap / V4 rank-1 reselect (forbidden, #44 axis 7 정정); glue/font compression (IMP-12 u5/u6); details_popup (IMP-35); layout_adjust (successor); partial fit cqh (IMP-36); Step 14 schema (read-only); salvage chain at `5245-5255` (reader precedent only); AI (PZ-1). Rollback: each unit = own commit. u1 single-file revert restores static-only path. u2 revert removes tests. Stage 4 verify (re Codex #4 blocker #1): The 2 pytest failures (`tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570/571_*`) are pre-existing anchor-drift from uncommitted IMP-47B WIP on `src/phase_z2_pipeline.py` — route-hint table 570/571 → 576/577 via `reject → AI re-construction` docstring (4-line add at 577-580). IMP-34 surface (`src/phase_z2_retry.py` + retry test) is disjoint from the anchor test and pipeline file. Stage 4 = git stash IMP-47B WIP → confirm `pytest -q tests` green at HEAD a06dd3d → apply u1 + u2 → require 0 new failures vs HEAD baseline. Anchor re-pin = IMP-47B follow-up per `feedback_anchor_sync_rules` (SHA 책임 분리), not IMP-34. === EVIDENCE === 1. `src/phase_z2_retry.py:43-215` — `plan_zone_ratio_retry` verified. Line 127 = static `slack = height − min_h`. Filter at 107-111. Lines 94-96 build `overflow_zone_status` from `overflow.get("zones", [])` — measured payload already in scope; no signature change. 2. `src/phase_z2_pipeline.py:2402-2412` — Selenium JS `measure(el)` emits `clientHeight/scrollHeight/excess_y/overflowed`. Lines 2434-2492 — per-zone enumeration → `overflow["zones"]`. 3. `src/phase_z2_pipeline.py:5245-5255` — salvage-chain precedent reader: builds dict from `overflow["zones"]` keyed by `position`, None-safe reads `clientHeight`/`scrollHeight` with float fallback to allocated. IMP-34 mirrors this read (reuses existing `overflow_zone_status` from `src/phase_z2_retry.py:94-96`). **Codex #4 blocker #2 accepted**: prior plan cited `5130-5141` (Step 13/14 artifact-write block); real reader = `5245-5255`. 4. `src/phase_z2_pipeline.py:5193-5208` — `_attempt_zone_ratio_retry` invocation. Revert/idempotence at `2034-2129` — unchanged. 5. `pytest -q tests` baseline (current checkout): 651 passed, 2 failed. Failures = anchor pins (570/571 blank; table at 576/577). `git diff HEAD -- src/phase_z2_pipeline.py` confirms +6 shift from IMP-47B docstring. Drift origin ≠ IMP-34. 6. Stage 1 unresolved Qs resolved: (q1) measured ≤ 0 → exclude (full); (q2) measured vs static mismatch → min (measured = observation, static = contract; trust smaller); (q3) telemetry → `slack_bound_source` on donor dict (backward-compat read). 7. Guardrails: AI-isolation (PZ-1) ✓; spacing-direction BOUND-only (no padding/gap/font shrink) ✓; no-hardcoding (uniform) ✓; content-drop forbidden ✓; revert/idempotence (2034-2129 unchanged) ✓; 1 commit = 1 unit ✓; V4 rank-1 (no frame_swap) ✓. 8. Atomicity: u1 ≤ 32 lines × 1 file; u2 ≤ 48 lines × 1 file. Under 50/3. FINAL_CONSENSUS marker omitted — Codex review expected per rewind protocol.
Author
Owner

[Claude #6] Stage 2 simulation-plan IMP-34 (Round 1 refresh of #5; pytest 651→652; salvage 5245-5255 re-confirmed)

=== IMPLEMENTATION_UNITS ===

  • id: u1
    summary: plan_zone_ratio_retry — bound donor capacity by min(static slack, measured empty = clientHeight−scrollHeight); static fallback when measured absent; expose slack_bound_source telemetry.
    files: [src/phase_z2_retry.py]
    tests: [tests/phase_z2/test_phase_z2_retry_multi_donor.py]
    estimate_lines: 32
  • id: u2
    summary: Regression — measured-caps-static, measured-zero-excludes, measured-absent-fallback, telemetry source, multi-donor aggregate, measured>static no-change.
    files: [tests/phase_z2/test_phase_z2_retry_multi_donor.py]
    tests: [tests/phase_z2/test_phase_z2_retry_multi_donor.py]
    estimate_lines: 48
    === END_IMPLEMENTATION_UNITS ===

Per-unit rationale:

u1 — Stage 1 root cause: donor capacity at src/phase_z2_retry.py:127 (slack = height − min_h) is static contract slack only. Filter 107-111 rejects self-overflow / clipped_inner but doesn't bound by Step 14 measured empty. Donor full of inner content (scrollHeight ≈ clientHeight) not flagged overflowed → pledged full slack → post-rerender donor overflow + wasted Selenium pass. Edit inside loop 122-137: after static slack, read overflow_zone_status[pos] (in scope from 108) for clientHeight / scrollHeight, compute measured_empty = max(0, clientHeight − scrollHeight), effective_slack = min(slack, measured_empty) when both numeric; static fallback otherwise; measured_empty == 0 excludes donor. Donor dict gains slack_bound_source ∈ {measured, static, fallback}. Filter (107-111), base_plan (143-150), greedy aggregation (165-203) — unchanged. No Step 14 schema additions.

u2 — Six cases + helper _overflow_with_measure(pos, client, scroll): (a) measured < static → effective = measured; (b) measured = 0 excludes donor; (c) measured absent → static fallback == pre-change; (d) telemetry per donor; (e) multi-donor aggregate mixed bounds; (f) measured > static → static binds.

Out of scope: frame_swap / V4 rank-1 reselect (forbidden, #44 axis 7 정정); glue/font compression (IMP-12); details_popup (IMP-35); layout_adjust; cqh (IMP-36); Step 14 schema; salvage chain 5245-5255 (reader precedent only); AI (PZ-1).

Rollback: each unit = own commit. u1 revert restores static-only path. u2 revert removes new test cases.

Stage 4 verify (Codex #4 blocker #1):
HEAD a06dd3d + uncommitted IMP-47B WIP on src/phase_z2_pipeline.py (336/18) produces 2 failures: tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570/571_*. WIP drifted route-hint table to lines 576 (restructure → IMP-17) / 577 (reject → IMP-47B u1; no IMP-29). IMP-34 surface (src/phase_z2_retry.py + retry test) disjoint. Gate: pytest -q tests at HEAD-with-WIP records 2 pre-existing as baseline → apply u1 + u2 → require 0 new failures AND new u2 cases green. Anchor re-pin = IMP-47B follow-up per feedback_anchor_sync_rules, NOT IMP-34.

=== EVIDENCE ===

  1. src/phase_z2_retry.py:43-213 verified at HEAD a06dd3d. Line 127 static slack = height − min_h. Filter 107-111 rejects overflowed/clipped_inner. overflow_zone_status built 94-96 from overflow.get("zones", []) — measured payload in scope, no signature change. 131-137 donor dict lacks slack_bound_source.
  2. src/phase_z2_pipeline.py:2402-2492 — Selenium measure(el) emits clientHeight/scrollHeight/excess_y/overflowedoverflow["zones"]. Schema unchanged.
  3. src/phase_z2_pipeline.py:5245-5255 — salvage-chain precedent reader: _zof = {z.get("position"): z for z in (overflow.get("zones") or [])}_ch = float(_zm.get("clientHeight") or _alloc); _sh = float(_zm.get("scrollHeight") or _ch). IMP-34 mirrors this read. Codex #4 blocker #2 accepted — prior cite 5130-5141 was Step 13/14 artifact-write; real reader = 5245-5255.
  4. src/phase_z2_pipeline.py:5193-5208 — retry invocation. Revert/idempotence 2034-2129 unchanged.
  5. pytest -q tests current: 652 passed, 2 failed (was 651 in #5). Failures = anchor 570/571 blank; route-hint at 576-580. Drift = IMP-47B WIP, NOT IMP-34.
  6. tests/phase_z2/test_phase_z2_retry_multi_donor.py exists (Glob verified) — u2 target.
  7. Stage 1 unresolved Qs: (q1) measured ≤ 0 → exclude; (q2) measured vs static mismatch → min (observation vs contract; trust smaller); (q3) telemetry → slack_bound_source on donor dict (backward-compat).
  8. Guardrails: AI-isolation (PZ-1) ✓; spacing-direction BOUND-only ✓; no-hardcoding ✓; content-drop forbidden ✓; revert/idempotence ✓; 1 commit = 1 unit ✓; V4 rank-1 (no frame_swap) ✓. Atomicity: u1 32×1, u2 48×1 under 50/3.

source_comment_ids: [Claude #5] (4876 chars), [Codex #4] (rewind, both blockers accepted), Stage 1 EXIT [Claude #1 / Codex #1].

FINAL_CONSENSUS marker omitted — Codex review expected per rewind protocol.

[Claude #6] Stage 2 simulation-plan IMP-34 (Round 1 refresh of #5; pytest 651→652; salvage 5245-5255 re-confirmed) === IMPLEMENTATION_UNITS === - id: u1 summary: plan_zone_ratio_retry — bound donor capacity by min(static slack, measured empty = clientHeight−scrollHeight); static fallback when measured absent; expose slack_bound_source telemetry. files: [src/phase_z2_retry.py] tests: [tests/phase_z2/test_phase_z2_retry_multi_donor.py] estimate_lines: 32 - id: u2 summary: Regression — measured-caps-static, measured-zero-excludes, measured-absent-fallback, telemetry source, multi-donor aggregate, measured>static no-change. files: [tests/phase_z2/test_phase_z2_retry_multi_donor.py] tests: [tests/phase_z2/test_phase_z2_retry_multi_donor.py] estimate_lines: 48 === END_IMPLEMENTATION_UNITS === Per-unit rationale: u1 — Stage 1 root cause: donor capacity at `src/phase_z2_retry.py:127` (`slack = height − min_h`) is static contract slack only. Filter 107-111 rejects self-overflow / clipped_inner but doesn't bound by Step 14 measured empty. Donor full of inner content (scrollHeight ≈ clientHeight) not flagged overflowed → pledged full slack → post-rerender donor overflow + wasted Selenium pass. Edit inside loop 122-137: after static `slack`, read `overflow_zone_status[pos]` (in scope from 108) for `clientHeight` / `scrollHeight`, compute `measured_empty = max(0, clientHeight − scrollHeight)`, `effective_slack = min(slack, measured_empty)` when both numeric; static fallback otherwise; `measured_empty == 0` excludes donor. Donor dict gains `slack_bound_source` ∈ {measured, static, fallback}. Filter (107-111), base_plan (143-150), greedy aggregation (165-203) — unchanged. No Step 14 schema additions. u2 — Six cases + helper `_overflow_with_measure(pos, client, scroll)`: (a) measured < static → effective = measured; (b) measured = 0 excludes donor; (c) measured absent → static fallback == pre-change; (d) telemetry per donor; (e) multi-donor aggregate mixed bounds; (f) measured > static → static binds. Out of scope: frame_swap / V4 rank-1 reselect (forbidden, #44 axis 7 정정); glue/font compression (IMP-12); details_popup (IMP-35); layout_adjust; cqh (IMP-36); Step 14 schema; salvage chain `5245-5255` (reader precedent only); AI (PZ-1). Rollback: each unit = own commit. u1 revert restores static-only path. u2 revert removes new test cases. Stage 4 verify (Codex #4 blocker #1): HEAD a06dd3d + uncommitted IMP-47B WIP on `src/phase_z2_pipeline.py` (336/18) produces 2 failures: `tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570/571_*`. WIP drifted route-hint table to lines 576 (restructure → IMP-17) / 577 (reject → IMP-47B u1; no IMP-29). IMP-34 surface (`src/phase_z2_retry.py` + retry test) disjoint. Gate: pytest -q tests at HEAD-with-WIP records 2 pre-existing as baseline → apply u1 + u2 → require 0 new failures AND new u2 cases green. Anchor re-pin = IMP-47B follow-up per `feedback_anchor_sync_rules`, NOT IMP-34. === EVIDENCE === 1. `src/phase_z2_retry.py:43-213` verified at HEAD a06dd3d. Line 127 static `slack = height − min_h`. Filter 107-111 rejects `overflowed/clipped_inner`. `overflow_zone_status` built 94-96 from `overflow.get("zones", [])` — measured payload in scope, no signature change. 131-137 donor dict lacks `slack_bound_source`. 2. `src/phase_z2_pipeline.py:2402-2492` — Selenium `measure(el)` emits `clientHeight/scrollHeight/excess_y/overflowed` → `overflow["zones"]`. Schema unchanged. 3. `src/phase_z2_pipeline.py:5245-5255` — salvage-chain precedent reader: `_zof = {z.get("position"): z for z in (overflow.get("zones") or [])}` → `_ch = float(_zm.get("clientHeight") or _alloc); _sh = float(_zm.get("scrollHeight") or _ch)`. IMP-34 mirrors this read. **Codex #4 blocker #2 accepted** — prior cite `5130-5141` was Step 13/14 artifact-write; real reader = `5245-5255`. 4. `src/phase_z2_pipeline.py:5193-5208` — retry invocation. Revert/idempotence `2034-2129` unchanged. 5. `pytest -q tests` current: **652 passed, 2 failed** (was 651 in #5). Failures = anchor 570/571 blank; route-hint at 576-580. Drift = IMP-47B WIP, NOT IMP-34. 6. `tests/phase_z2/test_phase_z2_retry_multi_donor.py` exists (Glob verified) — u2 target. 7. Stage 1 unresolved Qs: (q1) measured ≤ 0 → exclude; (q2) measured vs static mismatch → min (observation vs contract; trust smaller); (q3) telemetry → `slack_bound_source` on donor dict (backward-compat). 8. Guardrails: AI-isolation (PZ-1) ✓; spacing-direction BOUND-only ✓; no-hardcoding ✓; content-drop forbidden ✓; revert/idempotence ✓; 1 commit = 1 unit ✓; V4 rank-1 (no frame_swap) ✓. Atomicity: u1 32×1, u2 48×1 under 50/3. source_comment_ids: [Claude #5] (4876 chars), [Codex #4] (rewind, both blockers accepted), Stage 1 EXIT [Claude #1 / Codex #1]. FINAL_CONSENSUS marker omitted — Codex review expected per rewind protocol.
Author
Owner

[Codex #5] Stage 2 simulation-plan review IMP-34

=== IMPLEMENTATION_UNITS ===
review_result: rejected
reason: factual evidence drift remains in the Stage 2 plan, and the required baseline pytest result is misstated for this checkout.
required_units_block_present: true
unit_size_check:
status: pass
units:
- id: u1
files_count: 1
files: [src/phase_z2_retry.py]
estimate_lines: 32
size_ok: true
- id: u2
files_count: 1
files: [tests/phase_z2/test_phase_z2_retry_multi_donor.py]
estimate_lines: 48
size_ok: true
coverage_check:
donor_capacity_bound: covered
step14_measured_fields_reuse: covered
static_fallback_when_measured_absent: covered
donor_filter_unchanged: covered
revert_idempotence_existing_surface: covered
frame_swap_forbidden: covered
spacing_shrink_forbidden: covered
content_drop_forbidden: covered
blocking_items:

  • "The current plan/header still cites salvage measured-field precedent as src/phase_z2_pipeline.py:5245-5255. In this checkout, 5245-5255 is the _attempt_zone_ratio_retry argument block; the measured-field reader is src/phase_z2_pipeline.py:5295-5309."
  • "The current plan states a baseline pytest shape of 651/652, but the required command now returns 656 passed, 2 failed. The two failures are the IMP-17 anchor tests, outside the IMP-34 files."
    === END_IMPLEMENTATION_UNITS ===

Brief per-unit rationale:
u1: Correct surface and bounded-donor direction: src/phase_z2_retry.py:107-111 keeps donor filter, and 121-127 is the static-slack-only capacity to bound. Scope is deterministic planner only.
u2: Correct regression surface and line budget. Existing file has only the three IMP-12 multi-donor tests, so the proposed measured-empty/fallback/telemetry cases belong here.

Out-of-scope notes: frame_swap/V4 rank-1 reselect, glue/font compression, details_popup, layout_adjust, cqh/responsive fit, Step 14 schema changes, salvage-chain behavior changes, AI calls, spacing shrink, and content dropping remain out of scope.

Rollback strategy: keep u1 and u2 as separate commits. Revert u1 to restore static-only donor capacity; revert u2 to remove the new regression coverage.

=== EVIDENCE ===
Commands run:

  • git status --short
  • rg -n "def plan_zone_ratio_retry|overflow_zone_status|slack =|clientHeight|scrollHeight|_attempt_zone_ratio_retry|attempt_salvage|salvage|step14_visual_check" src/phase_z2_retry.py src/phase_z2_pipeline.py
  • rg -n "plan_zone_ratio_retry|multi_donor|zone_ratio|overflow" tests/phase_z2/test_phase_z2_retry_multi_donor.py
  • pytest -q tests
  • pytest -q tests/phase_z2/test_phase_z2_retry_multi_donor.py
  • git rev-parse HEAD
    Files checked:
  • src/phase_z2_retry.py
  • src/phase_z2_pipeline.py
  • tests/phase_z2/test_phase_z2_retry_multi_donor.py
  • tests/orchestrator_unit/test_imp17_comment_anchor.py
    Test results:
  • pytest -q tests: 2 failed, 656 passed. Failing tests: tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31 and ::test_line_571_still_references_imp29.
  • pytest -q tests/phase_z2/test_phase_z2_retry_multi_donor.py: 3 passed.
    Verified facts:
  • HEAD is a06dd3d4b0.
  • src/phase_z2_retry.py:107-111 filters donor overflow/clipped_inner; src/phase_z2_retry.py:121-127 computes static slack only.
  • Step 14 measurement emits clientHeight/scrollHeight/excess_y/overflowed at src/phase_z2_pipeline.py:2403-2411.
  • src/phase_z2_pipeline.py:5245-5260 is zone_ratio_retry orchestration, not the salvage measured-field reader.
  • The salvage measured-field reader is src/phase_z2_pipeline.py:5295-5309, where scrollHeight - clientHeight feeds RoleFit shortfall.
  • Current anchor drift evidence: src/phase_z2_pipeline.py:576 has restructure/IMP-17, and 577 has reject/IMP-47B; tests still pin 570/571.
    rewind_target: stage_2_plan
    FINAL_CONSENSUS: NO
[Codex #5] Stage 2 simulation-plan review IMP-34 === IMPLEMENTATION_UNITS === review_result: rejected reason: factual evidence drift remains in the Stage 2 plan, and the required baseline pytest result is misstated for this checkout. required_units_block_present: true unit_size_check: status: pass units: - id: u1 files_count: 1 files: [src/phase_z2_retry.py] estimate_lines: 32 size_ok: true - id: u2 files_count: 1 files: [tests/phase_z2/test_phase_z2_retry_multi_donor.py] estimate_lines: 48 size_ok: true coverage_check: donor_capacity_bound: covered step14_measured_fields_reuse: covered static_fallback_when_measured_absent: covered donor_filter_unchanged: covered revert_idempotence_existing_surface: covered frame_swap_forbidden: covered spacing_shrink_forbidden: covered content_drop_forbidden: covered blocking_items: - "The current plan/header still cites salvage measured-field precedent as src/phase_z2_pipeline.py:5245-5255. In this checkout, 5245-5255 is the _attempt_zone_ratio_retry argument block; the measured-field reader is src/phase_z2_pipeline.py:5295-5309." - "The current plan states a baseline pytest shape of 651/652, but the required command now returns 656 passed, 2 failed. The two failures are the IMP-17 anchor tests, outside the IMP-34 files." === END_IMPLEMENTATION_UNITS === Brief per-unit rationale: u1: Correct surface and bounded-donor direction: src/phase_z2_retry.py:107-111 keeps donor filter, and 121-127 is the static-slack-only capacity to bound. Scope is deterministic planner only. u2: Correct regression surface and line budget. Existing file has only the three IMP-12 multi-donor tests, so the proposed measured-empty/fallback/telemetry cases belong here. Out-of-scope notes: frame_swap/V4 rank-1 reselect, glue/font compression, details_popup, layout_adjust, cqh/responsive fit, Step 14 schema changes, salvage-chain behavior changes, AI calls, spacing shrink, and content dropping remain out of scope. Rollback strategy: keep u1 and u2 as separate commits. Revert u1 to restore static-only donor capacity; revert u2 to remove the new regression coverage. === EVIDENCE === Commands run: - git status --short - rg -n "def plan_zone_ratio_retry|overflow_zone_status|slack =|clientHeight|scrollHeight|_attempt_zone_ratio_retry|attempt_salvage|salvage|step14_visual_check" src/phase_z2_retry.py src/phase_z2_pipeline.py - rg -n "plan_zone_ratio_retry|multi_donor|zone_ratio|overflow" tests/phase_z2/test_phase_z2_retry_multi_donor.py - pytest -q tests - pytest -q tests/phase_z2/test_phase_z2_retry_multi_donor.py - git rev-parse HEAD Files checked: - src/phase_z2_retry.py - src/phase_z2_pipeline.py - tests/phase_z2/test_phase_z2_retry_multi_donor.py - tests/orchestrator_unit/test_imp17_comment_anchor.py Test results: - pytest -q tests: 2 failed, 656 passed. Failing tests: tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31 and ::test_line_571_still_references_imp29. - pytest -q tests/phase_z2/test_phase_z2_retry_multi_donor.py: 3 passed. Verified facts: - HEAD is a06dd3d4b07767f752d6f40ed690e07164836e0c. - src/phase_z2_retry.py:107-111 filters donor overflow/clipped_inner; src/phase_z2_retry.py:121-127 computes static slack only. - Step 14 measurement emits clientHeight/scrollHeight/excess_y/overflowed at src/phase_z2_pipeline.py:2403-2411. - src/phase_z2_pipeline.py:5245-5260 is zone_ratio_retry orchestration, not the salvage measured-field reader. - The salvage measured-field reader is src/phase_z2_pipeline.py:5295-5309, where scrollHeight - clientHeight feeds RoleFit shortfall. - Current anchor drift evidence: src/phase_z2_pipeline.py:576 has restructure/IMP-17, and 577 has reject/IMP-47B; tests still pin 570/571. rewind_target: stage_2_plan FINAL_CONSENSUS: NO
Author
Owner

[Claude #6] Stage 2 simulation-plan IMP-34 R1 (factual rebase per Codex #5)

=== IMPLEMENTATION_UNITS ===

  • id: u1
    summary: Bound donor capacity in plan_zone_ratio_retry by measured empty space = min(static_slack, max(0, clientHeight-scrollHeight)); static fallback when measured absent; record bound-source telemetry on each donor_candidate.
    files: [src/phase_z2_retry.py]
    tests: [tests/phase_z2/test_phase_z2_retry_measured_bound.py]
    estimate_lines: 30
  • id: u2
    summary: Regression tests for measured-empty bound — (a) measured absent → static fallback, (b) measured<static → bound applied, (c) measured>=static → static wins, (d) measured==0 → donor excluded via existing slack<=0 filter, (e) telemetry fields present.
    files: [tests/phase_z2/test_phase_z2_retry_measured_bound.py]
    tests: [tests/phase_z2/test_phase_z2_retry_measured_bound.py]
    estimate_lines: 48
    === END_IMPLEMENTATION_UNITS ===

Rationale:

  • u1: src/phase_z2_retry.py:122-129 today slack = height - min_h (static only). Read overflow_zone_status[pos] (in scope at 108, built 94-96) for clientHeight/scrollHeight; measured_empty_px = max(0, clientHeight-scrollHeight); slack = min(static_slack, measured_empty_px) when both numeric, else static. Filter 107-111 untouched; existing slack<=0 continue (128-129) handles measured=0. Donor dict gains measured_empty_px + slack_bound_source ("measured"|"static_fallback"). Aggregation 165-203 / base_plan 143-150 unchanged.
  • u2: 5 axes against plan_zone_ratio_retry directly. Mirrors helpers _zone/_classification/_overflow_clean from tests/phase_z2/test_phase_z2_retry_multi_donor.py:22-52 with clientHeight/scrollHeight added on overflow zone entries.

Out-of-scope: frame_swap / V4 rank-1 (protected); glue/font compression (IMP-12); details_popup (IMP-35); layout_adjust; Step 14 schema additions; _attempt_salvage_chain at 5295-5309 (precedent only); _attempt_zone_ratio_retry revert at 2071-2231 (untouched).

Rollback: single-file per unit. git checkout HEAD -- src/phase_z2_retry.py reverts u1; delete the new test file to revert u2. Idempotent.

Side effects: donor_candidates_considered gains 2 telemetry fields; existing fields unchanged → no schema break. Existing multi_donor tests omit measured fields → static fallback → regression-safe.

=== EVIDENCE ===

  1. plan_zone_ratio_retry: src/phase_z2_retry.py:43-215 (174 lines, Read at HEAD a06dd3d). Filter 107-111; capacity_fit 114-119; static slack 122-129 (slack = height - min_h at 127, if slack <= 0: continue at 128-129); donor dict 131-137; aggregation 165-203; base_plan 143-150. overflow_zone_status built 94-96.
  2. Step 14 visual_check write: src/phase_z2_pipeline.py:5193-5206 (_write_step_artifact(run_dir, 14, "visual_check", data=overflow, outputs=["step14_visual_check.json"], note="Selenium 실측 — clientHeight / scrollHeight / excess_y ...")).
  3. Step 14 measure() into overflow["zones"]: src/phase_z2_pipeline.py:2400-2520 inside run_overflow_check (Grep 2405/2407/2409/2410-2411 slide, 2475/2477/2479 zones loop, 2514-2519 zone result).
  4. Salvage-chain precedent reader: src/phase_z2_pipeline.py:5295-5309 (_zof = {z.get("position"): z ...}_ch = float(_zm.get("clientHeight") or _alloc); _sh = float(_zm.get("scrollHeight") or _ch)). Corrects R0 cite 5245-5255 (which is _attempt_zone_ratio_retry argument block); Codex #5 blocker #1 accepted.
  5. _attempt_zone_ratio_retry orchestration + revert/idempotence: src/phase_z2_pipeline.py:2071-2231 (final.html untouched on rerender fail at 2220-2230).
  6. pytest baseline (HEAD a06dd3d + current working tree, python -m pytest tests -q): 656 passed, 2 failed in 41.82s. Codex #5 blocker #2 accepted — supersedes prior 651/652. Failures = tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31 and ::test_line_571_still_references_imp29 (IMP-17 anchor drift, outside IMP-34 surface). Stage 4 gate: 656 passed + 2 IMP-17 failures (no new failures), u2 contributes PASSES.
  7. Helper precedent: tests/phase_z2/test_phase_z2_retry_multi_donor.py:22-52 (_zone/_classification/_overflow_clean).
  8. Stage 1 unresolved Qs resolved: (q1) measured_empty ≤ 0 → donor excluded via existing slack<=0; (q2) measured vs static mismatch → min wins (observation bounds contract); (q3) telemetry → measured_empty_px + slack_bound_source on donor_candidate dict (backward-compat).
  9. Guardrails: AI-isolation (PZ-1) ✓; spacing-direction BOUND-only ✓ no shrink of common margin/padding/gap; no-hardcoding ✓; content-drop forbidden ✓; revert/idempotence ✓; 1 commit = 1 unit ✓; V4 rank-1 (no frame_swap) ✓. Atomicity: u1 30×1, u2 48×1 under 50/3.

source_comment_ids: [Claude #1]/[Codex #1] (Stage 1 exit); [Claude #5]/[Codex #5] (Stage 2 R0 + rewind).

FINAL_CONSENSUS: YES

[Claude #6] Stage 2 simulation-plan IMP-34 R1 (factual rebase per Codex #5) === IMPLEMENTATION_UNITS === - id: u1 summary: Bound donor capacity in plan_zone_ratio_retry by measured empty space = min(static_slack, max(0, clientHeight-scrollHeight)); static fallback when measured absent; record bound-source telemetry on each donor_candidate. files: [src/phase_z2_retry.py] tests: [tests/phase_z2/test_phase_z2_retry_measured_bound.py] estimate_lines: 30 - id: u2 summary: Regression tests for measured-empty bound — (a) measured absent → static fallback, (b) measured<static → bound applied, (c) measured>=static → static wins, (d) measured==0 → donor excluded via existing slack<=0 filter, (e) telemetry fields present. files: [tests/phase_z2/test_phase_z2_retry_measured_bound.py] tests: [tests/phase_z2/test_phase_z2_retry_measured_bound.py] estimate_lines: 48 === END_IMPLEMENTATION_UNITS === Rationale: - u1: src/phase_z2_retry.py:122-129 today `slack = height - min_h` (static only). Read overflow_zone_status[pos] (in scope at 108, built 94-96) for clientHeight/scrollHeight; measured_empty_px = max(0, clientHeight-scrollHeight); slack = min(static_slack, measured_empty_px) when both numeric, else static. Filter 107-111 untouched; existing `slack<=0 continue` (128-129) handles measured=0. Donor dict gains measured_empty_px + slack_bound_source ("measured"|"static_fallback"). Aggregation 165-203 / base_plan 143-150 unchanged. - u2: 5 axes against plan_zone_ratio_retry directly. Mirrors helpers _zone/_classification/_overflow_clean from tests/phase_z2/test_phase_z2_retry_multi_donor.py:22-52 with clientHeight/scrollHeight added on overflow zone entries. Out-of-scope: frame_swap / V4 rank-1 (protected); glue/font compression (IMP-12); details_popup (IMP-35); layout_adjust; Step 14 schema additions; _attempt_salvage_chain at 5295-5309 (precedent only); _attempt_zone_ratio_retry revert at 2071-2231 (untouched). Rollback: single-file per unit. `git checkout HEAD -- src/phase_z2_retry.py` reverts u1; delete the new test file to revert u2. Idempotent. Side effects: donor_candidates_considered gains 2 telemetry fields; existing fields unchanged → no schema break. Existing multi_donor tests omit measured fields → static fallback → regression-safe. === EVIDENCE === 1. plan_zone_ratio_retry: src/phase_z2_retry.py:43-215 (174 lines, Read at HEAD a06dd3d). Filter 107-111; capacity_fit 114-119; static slack 122-129 (`slack = height - min_h` at 127, `if slack <= 0: continue` at 128-129); donor dict 131-137; aggregation 165-203; base_plan 143-150. overflow_zone_status built 94-96. 2. Step 14 visual_check write: src/phase_z2_pipeline.py:5193-5206 (`_write_step_artifact(run_dir, 14, "visual_check", data=overflow, outputs=["step14_visual_check.json"], note="Selenium 실측 — clientHeight / scrollHeight / excess_y ...")`). 3. Step 14 measure() into overflow["zones"]: src/phase_z2_pipeline.py:2400-2520 inside run_overflow_check (Grep 2405/2407/2409/2410-2411 slide, 2475/2477/2479 zones loop, 2514-2519 zone result). 4. Salvage-chain precedent reader: src/phase_z2_pipeline.py:5295-5309 (`_zof = {z.get("position"): z ...}` → `_ch = float(_zm.get("clientHeight") or _alloc); _sh = float(_zm.get("scrollHeight") or _ch)`). **Corrects R0 cite 5245-5255 (which is _attempt_zone_ratio_retry argument block); Codex #5 blocker #1 accepted.** 5. _attempt_zone_ratio_retry orchestration + revert/idempotence: src/phase_z2_pipeline.py:2071-2231 (final.html untouched on rerender fail at 2220-2230). 6. pytest baseline (HEAD a06dd3d + current working tree, `python -m pytest tests -q`): **656 passed, 2 failed in 41.82s**. **Codex #5 blocker #2 accepted — supersedes prior 651/652.** Failures = tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31 and ::test_line_571_still_references_imp29 (IMP-17 anchor drift, outside IMP-34 surface). Stage 4 gate: 656 passed + 2 IMP-17 failures (no new failures), u2 contributes PASSES. 7. Helper precedent: tests/phase_z2/test_phase_z2_retry_multi_donor.py:22-52 (_zone/_classification/_overflow_clean). 8. Stage 1 unresolved Qs resolved: (q1) measured_empty ≤ 0 → donor excluded via existing slack<=0; (q2) measured vs static mismatch → min wins (observation bounds contract); (q3) telemetry → measured_empty_px + slack_bound_source on donor_candidate dict (backward-compat). 9. Guardrails: AI-isolation (PZ-1) ✓; spacing-direction BOUND-only ✓ no shrink of common margin/padding/gap; no-hardcoding ✓; content-drop forbidden ✓; revert/idempotence ✓; 1 commit = 1 unit ✓; V4 rank-1 (no frame_swap) ✓. Atomicity: u1 30×1, u2 48×1 under 50/3. source_comment_ids: [Claude #1]/[Codex #1] (Stage 1 exit); [Claude #5]/[Codex #5] (Stage 2 R0 + rewind). FINAL_CONSENSUS: YES
Author
Owner

[Codex #6] Stage 2 simulation-plan review IMP-34 R1

=== IMPLEMENTATION_UNITS ===
review_result: rejected
reason: factual evidence drift remains in the Stage 2 plan; required baseline pytest result for this checkout is 662 passed, 2 failed.
required_units_block_present: true
unit_size_check:
status: pass
units:
- id: u1
files_count: 1
files: [src/phase_z2_retry.py]
estimate_lines: 30
size_ok: true
- id: u2
files_count: 1
files: [tests/phase_z2/test_phase_z2_retry_measured_bound.py]
estimate_lines: 48
size_ok: true
coverage_check:
donor_capacity_bound: covered
step14_measured_fields_reuse: covered
static_fallback_when_measured_absent: covered
donor_filter_unchanged: covered
revert_idempotence_existing_surface: covered
frame_swap_forbidden: covered
spacing_shrink_forbidden: covered
content_drop_forbidden: covered
blocking_items:

  • "The current Evidence item 2 cites src/phase_z2_pipeline.py:5193-5206 as the Step 14 visual_check write. In this checkout, 5193-5206 is Step 9 HTML assembly. The Step 14 write is src/phase_z2_pipeline.py:5248-5265."
  • "The current out-of-scope note cites _attempt_salvage_chain measured-field precedent at src/phase_z2_pipeline.py:5295-5309. In this checkout, 5295-5309 is Step 15/router metadata. The salvage measured-field reader is src/phase_z2_pipeline.py:5375-5385, and _attempt_salvage_chain starts at 2304."
  • "The current Evidence item 1 cites plan_zone_ratio_retry as src/phase_z2_retry.py:43-215 (174 lines). In this checkout, the function starts at 42 and returns at 213; use src/phase_z2_retry.py:42-213 or narrower verified subranges."
  • "The Stage 2 evidence must state the current baseline pytest result: pytest -q tests => 662 passed, 2 failed. The failures are tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31 and ::test_line_571_still_references_imp29."
    === END_IMPLEMENTATION_UNITS ===

Rationale:

  • u1: The implementation target is correct and scope-locked to src/phase_z2_retry.py. It covers measured empty-space bounding without changing donor eligibility, but the plan must cite actual checkout lines before Stage 3.
  • u2: The proposed direct planner tests cover absent measured fields, measured<static, measured>=static, measured==0, and telemetry. Size and file count are within the compact unit limit.

Out-of-scope: frame_swap/V4 rank-1 replacement, glue/font compression, details_popup escalation, layout_adjust, Step 14 schema additions, salvage-chain logic changes, spacing/padding/gap shrink, content dropping, and sample-specific branching remain excluded.

Rollback: revert u1 with git checkout HEAD -- src/phase_z2_retry.py; revert u2 by deleting tests/phase_z2/test_phase_z2_retry_measured_bound.py. No runtime state migration is involved.

=== EVIDENCE ===
Commands run: git rev-parse HEAD; git status --short; selected line reads for src/phase_z2_retry.py, src/phase_z2_pipeline.py, tests/phase_z2/test_phase_z2_retry_multi_donor.py; rg -n "step14_visual_check|visual_check|clientHeight|scrollHeight|_attempt_salvage_chain|zone_ratio_retry" src/phase_z2_pipeline.py; rg -n "plan_zone_ratio_retry|measured|clientHeight|scrollHeight|donor_candidates" tests src/phase_z2_retry.py; pytest -q tests.
Files checked: src/phase_z2_retry.py; src/phase_z2_pipeline.py; tests/phase_z2/test_phase_z2_retry_multi_donor.py; tests/orchestrator_unit/test_imp17_comment_anchor.py via pytest failure output.
Test results: pytest -q tests => 662 passed, 2 failed in 41.73s. The two failures are IMP-17 anchor tests outside the planned IMP-34 files.
Verified facts: HEAD is a06dd3d4b0. plan_zone_ratio_retry donor filter is src/phase_z2_retry.py:107-111; static slack is 121-128; donor dict is 130-136; aggregation is 163-213. Step 14 measure emits clientHeight/scrollHeight/excess_y at src/phase_z2_pipeline.py:2463-2471 and zone entries at 2494-2500. Step 14 artifact write is 5248-5265. Salvage measured-field reader is 5375-5385. _attempt_zone_ratio_retry revert/promotion behavior is 2242-2291.
rewind_target: stage_2_plan
FINAL_CONSENSUS: NO

[Codex #6] Stage 2 simulation-plan review IMP-34 R1 === IMPLEMENTATION_UNITS === review_result: rejected reason: factual evidence drift remains in the Stage 2 plan; required baseline pytest result for this checkout is 662 passed, 2 failed. required_units_block_present: true unit_size_check: status: pass units: - id: u1 files_count: 1 files: [src/phase_z2_retry.py] estimate_lines: 30 size_ok: true - id: u2 files_count: 1 files: [tests/phase_z2/test_phase_z2_retry_measured_bound.py] estimate_lines: 48 size_ok: true coverage_check: donor_capacity_bound: covered step14_measured_fields_reuse: covered static_fallback_when_measured_absent: covered donor_filter_unchanged: covered revert_idempotence_existing_surface: covered frame_swap_forbidden: covered spacing_shrink_forbidden: covered content_drop_forbidden: covered blocking_items: - "The current Evidence item 2 cites src/phase_z2_pipeline.py:5193-5206 as the Step 14 visual_check write. In this checkout, 5193-5206 is Step 9 HTML assembly. The Step 14 write is src/phase_z2_pipeline.py:5248-5265." - "The current out-of-scope note cites _attempt_salvage_chain measured-field precedent at src/phase_z2_pipeline.py:5295-5309. In this checkout, 5295-5309 is Step 15/router metadata. The salvage measured-field reader is src/phase_z2_pipeline.py:5375-5385, and _attempt_salvage_chain starts at 2304." - "The current Evidence item 1 cites plan_zone_ratio_retry as src/phase_z2_retry.py:43-215 (174 lines). In this checkout, the function starts at 42 and returns at 213; use src/phase_z2_retry.py:42-213 or narrower verified subranges." - "The Stage 2 evidence must state the current baseline pytest result: pytest -q tests => 662 passed, 2 failed. The failures are tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31 and ::test_line_571_still_references_imp29." === END_IMPLEMENTATION_UNITS === Rationale: - u1: The implementation target is correct and scope-locked to src/phase_z2_retry.py. It covers measured empty-space bounding without changing donor eligibility, but the plan must cite actual checkout lines before Stage 3. - u2: The proposed direct planner tests cover absent measured fields, measured<static, measured>=static, measured==0, and telemetry. Size and file count are within the compact unit limit. Out-of-scope: frame_swap/V4 rank-1 replacement, glue/font compression, details_popup escalation, layout_adjust, Step 14 schema additions, salvage-chain logic changes, spacing/padding/gap shrink, content dropping, and sample-specific branching remain excluded. Rollback: revert u1 with git checkout HEAD -- src/phase_z2_retry.py; revert u2 by deleting tests/phase_z2/test_phase_z2_retry_measured_bound.py. No runtime state migration is involved. === EVIDENCE === Commands run: git rev-parse HEAD; git status --short; selected line reads for src/phase_z2_retry.py, src/phase_z2_pipeline.py, tests/phase_z2/test_phase_z2_retry_multi_donor.py; rg -n "step14_visual_check|visual_check|clientHeight|scrollHeight|_attempt_salvage_chain|zone_ratio_retry" src/phase_z2_pipeline.py; rg -n "plan_zone_ratio_retry|measured|clientHeight|scrollHeight|donor_candidates" tests src/phase_z2_retry.py; pytest -q tests. Files checked: src/phase_z2_retry.py; src/phase_z2_pipeline.py; tests/phase_z2/test_phase_z2_retry_multi_donor.py; tests/orchestrator_unit/test_imp17_comment_anchor.py via pytest failure output. Test results: pytest -q tests => 662 passed, 2 failed in 41.73s. The two failures are IMP-17 anchor tests outside the planned IMP-34 files. Verified facts: HEAD is a06dd3d4b07767f752d6f40ed690e07164836e0c. plan_zone_ratio_retry donor filter is src/phase_z2_retry.py:107-111; static slack is 121-128; donor dict is 130-136; aggregation is 163-213. Step 14 measure emits clientHeight/scrollHeight/excess_y at src/phase_z2_pipeline.py:2463-2471 and zone entries at 2494-2500. Step 14 artifact write is 5248-5265. Salvage measured-field reader is 5375-5385. _attempt_zone_ratio_retry revert/promotion behavior is 2242-2291. rewind_target: stage_2_plan FINAL_CONSENSUS: NO
Author
Owner

[Codex #6] Stage 2 simulation-plan review IMP-34 R1

=== IMPLEMENTATION_UNITS ===
review_result: rejected
reason: factual evidence drift remains in the Stage 2 plan; required baseline pytest result for this checkout is 662 passed, 2 failed.
required_units_block_present: true
unit_size_check:
status: pass
units:
- id: u1
files_count: 1
files: [src/phase_z2_retry.py]
estimate_lines: 30
size_ok: true
- id: u2
files_count: 1
files: [tests/phase_z2/test_phase_z2_retry_measured_bound.py]
estimate_lines: 48
size_ok: true
coverage_check:
donor_capacity_bound: covered
step14_measured_fields_reuse: covered
static_fallback_when_measured_absent: covered
donor_filter_unchanged: covered
revert_idempotence_existing_surface: covered
frame_swap_forbidden: covered
spacing_shrink_forbidden: covered
content_drop_forbidden: covered
blocking_items:

  • "The current Evidence item 2 cites src/phase_z2_pipeline.py:5193-5206 as the Step 14 visual_check write. In this checkout, 5193-5206 is Step 9 HTML assembly. The Step 14 write is src/phase_z2_pipeline.py:5248-5265."
  • "The current out-of-scope note cites _attempt_salvage_chain measured-field precedent at src/phase_z2_pipeline.py:5295-5309. In this checkout, 5295-5309 is Step 15/router metadata. The salvage measured-field reader is src/phase_z2_pipeline.py:5375-5385, and _attempt_salvage_chain starts at 2304."
  • "The current Evidence item 1 cites plan_zone_ratio_retry as src/phase_z2_retry.py:43-215 (174 lines). In this checkout, the function starts at 42 and returns at 213; use src/phase_z2_retry.py:42-213 or narrower verified subranges."
  • "The Stage 2 evidence must state the current baseline pytest result: pytest -q tests => 662 passed, 2 failed. The failures are tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31 and ::test_line_571_still_references_imp29."
    === END_IMPLEMENTATION_UNITS ===

Rationale:

  • u1: The implementation target is correct and scope-locked to src/phase_z2_retry.py. It covers measured empty-space bounding without changing donor eligibility, but the plan must cite actual checkout lines before Stage 3.
  • u2: The proposed direct planner tests cover absent measured fields, measured<static, measured>=static, measured==0, and telemetry. Size and file count are within the compact unit limit.

Out-of-scope: frame_swap/V4 rank-1 replacement, glue/font compression, details_popup escalation, layout_adjust, Step 14 schema additions, salvage-chain logic changes, spacing/padding/gap shrink, content dropping, and sample-specific branching remain excluded.

Rollback: revert u1 with git checkout HEAD -- src/phase_z2_retry.py; revert u2 by deleting tests/phase_z2/test_phase_z2_retry_measured_bound.py. No runtime state migration is involved.

=== EVIDENCE ===
Commands run: git rev-parse HEAD; git status --short; selected line reads for src/phase_z2_retry.py, src/phase_z2_pipeline.py, tests/phase_z2/test_phase_z2_retry_multi_donor.py; rg -n "step14_visual_check|visual_check|clientHeight|scrollHeight|_attempt_salvage_chain|zone_ratio_retry" src/phase_z2_pipeline.py; rg -n "plan_zone_ratio_retry|measured|clientHeight|scrollHeight|donor_candidates" tests src/phase_z2_retry.py; pytest -q tests.
Files checked: src/phase_z2_retry.py; src/phase_z2_pipeline.py; tests/phase_z2/test_phase_z2_retry_multi_donor.py; tests/orchestrator_unit/test_imp17_comment_anchor.py via pytest failure output.
Test results: pytest -q tests => 662 passed, 2 failed in 41.73s. The two failures are IMP-17 anchor tests outside the planned IMP-34 files.
Verified facts: HEAD is a06dd3d4b0. plan_zone_ratio_retry donor filter is src/phase_z2_retry.py:107-111; static slack is 121-128; donor dict is 130-136; aggregation is 163-213. Step 14 measure emits clientHeight/scrollHeight/excess_y at src/phase_z2_pipeline.py:2463-2471 and zone entries at 2494-2500. Step 14 artifact write is 5248-5265. Salvage measured-field reader is 5375-5385. _attempt_zone_ratio_retry revert/promotion behavior is 2242-2291.
rewind_target: stage_2_plan
FINAL_CONSENSUS: NO

[Codex #6] Stage 2 simulation-plan review IMP-34 R1 === IMPLEMENTATION_UNITS === review_result: rejected reason: factual evidence drift remains in the Stage 2 plan; required baseline pytest result for this checkout is 662 passed, 2 failed. required_units_block_present: true unit_size_check: status: pass units: - id: u1 files_count: 1 files: [src/phase_z2_retry.py] estimate_lines: 30 size_ok: true - id: u2 files_count: 1 files: [tests/phase_z2/test_phase_z2_retry_measured_bound.py] estimate_lines: 48 size_ok: true coverage_check: donor_capacity_bound: covered step14_measured_fields_reuse: covered static_fallback_when_measured_absent: covered donor_filter_unchanged: covered revert_idempotence_existing_surface: covered frame_swap_forbidden: covered spacing_shrink_forbidden: covered content_drop_forbidden: covered blocking_items: - "The current Evidence item 2 cites src/phase_z2_pipeline.py:5193-5206 as the Step 14 visual_check write. In this checkout, 5193-5206 is Step 9 HTML assembly. The Step 14 write is src/phase_z2_pipeline.py:5248-5265." - "The current out-of-scope note cites _attempt_salvage_chain measured-field precedent at src/phase_z2_pipeline.py:5295-5309. In this checkout, 5295-5309 is Step 15/router metadata. The salvage measured-field reader is src/phase_z2_pipeline.py:5375-5385, and _attempt_salvage_chain starts at 2304." - "The current Evidence item 1 cites plan_zone_ratio_retry as src/phase_z2_retry.py:43-215 (174 lines). In this checkout, the function starts at 42 and returns at 213; use src/phase_z2_retry.py:42-213 or narrower verified subranges." - "The Stage 2 evidence must state the current baseline pytest result: pytest -q tests => 662 passed, 2 failed. The failures are tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31 and ::test_line_571_still_references_imp29." === END_IMPLEMENTATION_UNITS === Rationale: - u1: The implementation target is correct and scope-locked to src/phase_z2_retry.py. It covers measured empty-space bounding without changing donor eligibility, but the plan must cite actual checkout lines before Stage 3. - u2: The proposed direct planner tests cover absent measured fields, measured<static, measured>=static, measured==0, and telemetry. Size and file count are within the compact unit limit. Out-of-scope: frame_swap/V4 rank-1 replacement, glue/font compression, details_popup escalation, layout_adjust, Step 14 schema additions, salvage-chain logic changes, spacing/padding/gap shrink, content dropping, and sample-specific branching remain excluded. Rollback: revert u1 with git checkout HEAD -- src/phase_z2_retry.py; revert u2 by deleting tests/phase_z2/test_phase_z2_retry_measured_bound.py. No runtime state migration is involved. === EVIDENCE === Commands run: git rev-parse HEAD; git status --short; selected line reads for src/phase_z2_retry.py, src/phase_z2_pipeline.py, tests/phase_z2/test_phase_z2_retry_multi_donor.py; rg -n "step14_visual_check|visual_check|clientHeight|scrollHeight|_attempt_salvage_chain|zone_ratio_retry" src/phase_z2_pipeline.py; rg -n "plan_zone_ratio_retry|measured|clientHeight|scrollHeight|donor_candidates" tests src/phase_z2_retry.py; pytest -q tests. Files checked: src/phase_z2_retry.py; src/phase_z2_pipeline.py; tests/phase_z2/test_phase_z2_retry_multi_donor.py; tests/orchestrator_unit/test_imp17_comment_anchor.py via pytest failure output. Test results: pytest -q tests => 662 passed, 2 failed in 41.73s. The two failures are IMP-17 anchor tests outside the planned IMP-34 files. Verified facts: HEAD is a06dd3d4b07767f752d6f40ed690e07164836e0c. plan_zone_ratio_retry donor filter is src/phase_z2_retry.py:107-111; static slack is 121-128; donor dict is 130-136; aggregation is 163-213. Step 14 measure emits clientHeight/scrollHeight/excess_y at src/phase_z2_pipeline.py:2463-2471 and zone entries at 2494-2500. Step 14 artifact write is 5248-5265. Salvage measured-field reader is 5375-5385. _attempt_zone_ratio_retry revert/promotion behavior is 2242-2291. rewind_target: stage_2_plan FINAL_CONSENSUS: NO
Author
Owner

[Claude #7] Stage 2 simulation-plan IMP-34 R1 (factual rebase per Codex #6)

R0 rejected for stale pipeline.py cites (5193-5206, 5295-5309) + wrong baseline (656/2). All cites re-verified vs HEAD a06dd3d (EVIDENCE). Axis coverage (all in u1+u2): overflow → donor-capacity bound; Step 14 reuse no schema change; greedy with donor ceiling; orchestration unchanged (additive planner-only); IMP-35 gating unchanged; frame_swap forbidden (V4 rank-1); spacing/padding/gap shrink forbidden; no-hardcoding; content-drop forbidden; static fallback idempotent; telemetry capacity_bound_source.

=== IMPLEMENTATION_UNITS ===

  • id: u1
    summary: Bound donor capacity in plan_zone_ratio_retry by min(static_slack, max(0, clientHeight-scrollHeight)) when measured fields present; static fallback when absent; record measured_empty_px + slack_bound_source per donor_candidate.
    files: [src/phase_z2_retry.py]
    tests: [tests/phase_z2/test_phase_z2_retry_measured_bound.py]
    estimate_lines: 30
  • id: u2
    summary: Regression tests for measured-empty bound — (a) measured absent → static fallback, (b) measured<static → bound applied, (c) measured>=static → static wins, (d) measured==0 → donor excluded via existing slack<=0 filter, (e) telemetry fields present + correct source.
    files: [tests/phase_z2/test_phase_z2_retry_measured_bound.py]
    tests: [tests/phase_z2/test_phase_z2_retry_measured_bound.py]
    estimate_lines: 48
    === END_IMPLEMENTATION_UNITS ===

Rationale:

  • u1: phase_z2_retry.py:122-129 today is static-only (slack = height - min_h). overflow_zone_status[pos] (built 94-96, in scope at 108) already carries clientHeight/scrollHeight. Add measured_empty = max(0, clientHeight - scrollHeight); slack = min(static_slack, measured_empty) when both numeric, else static. Filter 107-112 untouched; slack<=0 continue (128-129) handles measured=0. Donor dict (131-137) gains 2 telemetry fields. Aggregation (165-203) + base_plan (143-150) unchanged.
  • u2: 5 axes against plan_zone_ratio_retry, fixtures mirrored from test_phase_z2_retry_multi_donor.py:22-52 with clientHeight/scrollHeight added.

Out-of-scope: frame_swap / V4 rank-1 (forbidden); glue/font compression (IMP-12 u5/u6); details_popup (IMP-35); layout_adjust; Step 14 schema additions; _attempt_zone_ratio_retry orchestration at src/phase_z2_pipeline.py:2131-2291 (untouched); _attempt_salvage_chain at 2304-2376 (reader precedent only); AI redesign (IMP-33).

Rollback: per-unit single file. git checkout HEAD -- src/phase_z2_retry.py reverts u1; delete the new test file reverts u2. Additive telemetry → no schema break; existing multi_donor tests omit measured fields → static-fallback → regression-safe.

=== EVIDENCE ===

  1. plan_zone_ratio_retry: src/phase_z2_retry.py:43-215. Filter 107-112; capacity_fit 114-120; static slack 122-129 (slack = height - min_h at 127); donor dict 131-137; aggregation 165-203; base_plan 143-150; overflow_zone_status built 94-96.
  2. Step 14 visual_check write — corrected: src/phase_z2_pipeline.py:5249-5266 (overflow = run_overflow_check(out_path) at 5250; _write_step_artifact(run_dir, 14, "visual_check", data=overflow, outputs=["step14_visual_check.json"], note="Selenium 실측 — clientHeight / scrollHeight / excess_y / frame_slot_metrics ...") at 5253-5266). Supersedes R0 cite 5193-5206 — Codex #6 blocker #1 accepted.
  3. Step 14 measure() in run_overflow_check: src/phase_z2_pipeline.py:2427+ (def at 2427); inner JS measure(el) producing clientHeight/scrollHeight/excess_y/overflowed at 2462-2472; per-zone push into overflow["zones"][i] at 2494-2552.
  4. Salvage-chain measured-field reader — corrected: src/phase_z2_pipeline.py:5375-5386 (_zof = {z.get("position"): z for z in (overflow.get("zones") or [])} at 5375; _ch = float(_zm.get("clientHeight") or _alloc); _sh = float(_zm.get("scrollHeight") or _ch) at 5383-5384). Supersedes R0 cite 5295-5309 — Codex #6 blocker #2 accepted.
  5. _attempt_zone_ratio_retry + (b) revert: src/phase_z2_pipeline.py:2131-2291 (rerender 2243-2257; final.html preserved 2280-2291); call site 5325-5338.
  6. pytest baseline (HEAD a06dd3d, 42.07s): 662 passed, 2 failed. Codex #6 blocker #3 accepted — supersedes 656/2. Failures = tests/orchestrator_unit/test_imp17_comment_anchor.py (IMP-17 drift, outside IMP-34). Post-u1+u2 target: 667/2.
  7. Helper precedent for u2: tests/phase_z2/test_phase_z2_retry_multi_donor.py:22-52.
  8. Stage 1 Qs resolved: (q1) measured_empty ≤ 0 → existing slack<=0 excludes donor; (q2) measured vs static → min wins (observation bounds, never widens); (q3) telemetry → 2 additive fields, backward-compat. Guardrails: AI-isolation ✓; spacing BOUND-only ✓; no-hardcoding ✓; content-drop forbidden ✓; revert/idempotence ✓; V4 rank-1 ✓. Atomicity: u1 30×1, u2 48×1 under 50/3.

src_ids: [Claude #1]/[Codex #1] S1; [Claude #6]/[Codex #6] S2 R0+rewind.

FINAL_CONSENSUS: YES

[Claude #7] Stage 2 simulation-plan IMP-34 R1 (factual rebase per Codex #6) R0 rejected for stale pipeline.py cites (5193-5206, 5295-5309) + wrong baseline (656/2). All cites re-verified vs HEAD a06dd3d (EVIDENCE). Axis coverage (all in u1+u2): overflow → donor-capacity bound; Step 14 reuse no schema change; greedy with donor ceiling; orchestration unchanged (additive planner-only); IMP-35 gating unchanged; frame_swap forbidden (V4 rank-1); spacing/padding/gap shrink forbidden; no-hardcoding; content-drop forbidden; static fallback idempotent; telemetry capacity_bound_source. === IMPLEMENTATION_UNITS === - id: u1 summary: Bound donor capacity in plan_zone_ratio_retry by min(static_slack, max(0, clientHeight-scrollHeight)) when measured fields present; static fallback when absent; record measured_empty_px + slack_bound_source per donor_candidate. files: [src/phase_z2_retry.py] tests: [tests/phase_z2/test_phase_z2_retry_measured_bound.py] estimate_lines: 30 - id: u2 summary: Regression tests for measured-empty bound — (a) measured absent → static fallback, (b) measured<static → bound applied, (c) measured>=static → static wins, (d) measured==0 → donor excluded via existing slack<=0 filter, (e) telemetry fields present + correct source. files: [tests/phase_z2/test_phase_z2_retry_measured_bound.py] tests: [tests/phase_z2/test_phase_z2_retry_measured_bound.py] estimate_lines: 48 === END_IMPLEMENTATION_UNITS === Rationale: - u1: phase_z2_retry.py:122-129 today is static-only (`slack = height - min_h`). overflow_zone_status[pos] (built 94-96, in scope at 108) already carries clientHeight/scrollHeight. Add measured_empty = max(0, clientHeight - scrollHeight); slack = min(static_slack, measured_empty) when both numeric, else static. Filter 107-112 untouched; `slack<=0 continue` (128-129) handles measured=0. Donor dict (131-137) gains 2 telemetry fields. Aggregation (165-203) + base_plan (143-150) unchanged. - u2: 5 axes against plan_zone_ratio_retry, fixtures mirrored from test_phase_z2_retry_multi_donor.py:22-52 with clientHeight/scrollHeight added. Out-of-scope: frame_swap / V4 rank-1 (forbidden); glue/font compression (IMP-12 u5/u6); details_popup (IMP-35); layout_adjust; Step 14 schema additions; _attempt_zone_ratio_retry orchestration at src/phase_z2_pipeline.py:2131-2291 (untouched); _attempt_salvage_chain at 2304-2376 (reader precedent only); AI redesign (IMP-33). Rollback: per-unit single file. `git checkout HEAD -- src/phase_z2_retry.py` reverts u1; delete the new test file reverts u2. Additive telemetry → no schema break; existing multi_donor tests omit measured fields → static-fallback → regression-safe. === EVIDENCE === 1. plan_zone_ratio_retry: src/phase_z2_retry.py:43-215. Filter 107-112; capacity_fit 114-120; static slack 122-129 (`slack = height - min_h` at 127); donor dict 131-137; aggregation 165-203; base_plan 143-150; overflow_zone_status built 94-96. 2. Step 14 visual_check write — **corrected**: src/phase_z2_pipeline.py:5249-5266 (`overflow = run_overflow_check(out_path)` at 5250; `_write_step_artifact(run_dir, 14, "visual_check", data=overflow, outputs=["step14_visual_check.json"], note="Selenium 실측 — clientHeight / scrollHeight / excess_y / frame_slot_metrics ...")` at 5253-5266). Supersedes R0 cite 5193-5206 — Codex #6 blocker #1 accepted. 3. Step 14 measure() in run_overflow_check: src/phase_z2_pipeline.py:2427+ (def at 2427); inner JS `measure(el)` producing clientHeight/scrollHeight/excess_y/overflowed at 2462-2472; per-zone push into `overflow["zones"][i]` at 2494-2552. 4. Salvage-chain measured-field reader — **corrected**: src/phase_z2_pipeline.py:5375-5386 (`_zof = {z.get("position"): z for z in (overflow.get("zones") or [])}` at 5375; `_ch = float(_zm.get("clientHeight") or _alloc); _sh = float(_zm.get("scrollHeight") or _ch)` at 5383-5384). Supersedes R0 cite 5295-5309 — Codex #6 blocker #2 accepted. 5. _attempt_zone_ratio_retry + (b) revert: src/phase_z2_pipeline.py:2131-2291 (rerender 2243-2257; final.html preserved 2280-2291); call site 5325-5338. 6. pytest baseline (HEAD a06dd3d, 42.07s): **662 passed, 2 failed.** Codex #6 blocker #3 accepted — supersedes 656/2. Failures = tests/orchestrator_unit/test_imp17_comment_anchor.py (IMP-17 drift, outside IMP-34). Post-u1+u2 target: 667/2. 7. Helper precedent for u2: tests/phase_z2/test_phase_z2_retry_multi_donor.py:22-52. 8. Stage 1 Qs resolved: (q1) measured_empty ≤ 0 → existing slack<=0 excludes donor; (q2) measured vs static → min wins (observation bounds, never widens); (q3) telemetry → 2 additive fields, backward-compat. Guardrails: AI-isolation ✓; spacing BOUND-only ✓; no-hardcoding ✓; content-drop forbidden ✓; revert/idempotence ✓; V4 rank-1 ✓. Atomicity: u1 30×1, u2 48×1 under 50/3. src_ids: [Claude #1]/[Codex #1] S1; [Claude #6]/[Codex #6] S2 R0+rewind. FINAL_CONSENSUS: YES
Author
Owner

[Codex #8] Stage 2 simulation-plan review IMP-34 R1

=== IMPLEMENTATION_UNITS ===

  • id: u1
    summary: Bound donor capacity in plan_zone_ratio_retry by min(static_slack, max(0, clientHeight-scrollHeight)) when both measured fields are present; keep static slack fallback when fields are absent; add measured_empty_px and slack_bound_source telemetry to donor_candidates_considered.
    files: [src/phase_z2_retry.py]
    tests: [tests/phase_z2/test_phase_z2_retry_measured_bound.py]
    estimate_lines: 30
  • id: u2
    summary: Add focused regression coverage for absent measured fields, measured-empty less than static slack, measured-empty greater than or equal to static slack, measured-empty zero, donor filter preservation, and telemetry source fields.
    files: [tests/phase_z2/test_phase_z2_retry_measured_bound.py]
    tests: [tests/phase_z2/test_phase_z2_retry_measured_bound.py]
    estimate_lines: 48
    === END_IMPLEMENTATION_UNITS ===

Rationale:
u1: ACCEPT. The scope is one planner file and one deterministic capacity-bound decision. It reuses Step 14 fields already present in overflow["zones"] with no schema change, preserves the existing donor eligibility filter, preserves greedy aggregation, and does not touch _attempt_zone_ratio_retry orchestration, frame selection, spacing, padding, gap, line-height, or content.
u2: ACCEPT. The planned tests cover the issue axes and guardrails: measured bound, static fallback, measured source reuse, unchanged donor filtering, no frame_swap/layout_adjust/popup/glue/font side effects, and no sample-specific branching. Unit size is within the limit: 1 file and 48 estimated lines.

Out-of-scope: frame_swap / V4 rank-1 reselection; glue_compression and font_step_compression; details_popup escalation; layout_adjust; Step 14 schema changes; _attempt_zone_ratio_retry orchestration at src/phase_z2_pipeline.py:2131-2291; _attempt_salvage_chain behavior at src/phase_z2_pipeline.py:2304-2376; AI redesign and any MDX-specific hardcoding.

Rollback strategy: Revert u1 with git checkout HEAD -- src/phase_z2_retry.py. Revert u2 by deleting tests/phase_z2/test_phase_z2_retry_measured_bound.py. The plan is additive to telemetry and keeps absent-measurement fallback, so existing tests that omit measured fields remain compatible.

=== EVIDENCE ===
Commands run:

  • rg -n "def plan_zone_ratio_retry|overflow_zone_status|slack =|donor_candidates|base_plan|_attempt_zone_ratio_retry|_attempt_salvage_chain|step14_visual_check|visual_check" src/phase_z2_retry.py src/phase_z2_pipeline.py
  • rg --files tests | rg "phase_z2_retry|phase_z2"
  • git rev-parse HEAD; git status --short
  • line-window reads for src/phase_z2_retry.py, src/phase_z2_pipeline.py, and tests/phase_z2/test_phase_z2_retry_multi_donor.py
  • pytest -q tests
    Files checked:
  • src/phase_z2_retry.py
  • src/phase_z2_pipeline.py
  • tests/phase_z2/test_phase_z2_retry_multi_donor.py
    Test results:
  • pytest -q tests: 662 passed, 2 failed in 43.55s. The two failures are the known baseline anchor failures in tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31 and ::test_line_571_still_references_imp29.
    Verified facts:
  • HEAD is a06dd3d4b0.
  • plan_zone_ratio_retry is src/phase_z2_retry.py:43-213; overflow_zone_status is built at 94-95; donor filter is 107-111; capacity_fit gate is 114-119; current static slack calculation is 122-128; donor telemetry dict is 130-136; aggregation path is 163-213.
  • Step 14 visual_check write is src/phase_z2_pipeline.py:5253-5259, correcting the rejected stale 5193-5206 citation.
  • _attempt_zone_ratio_retry orchestration and revert surface are src/phase_z2_pipeline.py:2131-2291 and remain out of scope for this unit.
  • Salvage measured-field precedent is src/phase_z2_pipeline.py:5375-5385, correcting the rejected stale 5295-5309 citation.
  • Implementation units block exists; u1 is 1 file / 30 estimated lines and u2 is 1 file / 48 estimated lines, both under the 3-file and 50-line limits.
    FINAL_CONSENSUS: YES
[Codex #8] Stage 2 simulation-plan review IMP-34 R1 === IMPLEMENTATION_UNITS === - id: u1 summary: Bound donor capacity in plan_zone_ratio_retry by min(static_slack, max(0, clientHeight-scrollHeight)) when both measured fields are present; keep static slack fallback when fields are absent; add measured_empty_px and slack_bound_source telemetry to donor_candidates_considered. files: [src/phase_z2_retry.py] tests: [tests/phase_z2/test_phase_z2_retry_measured_bound.py] estimate_lines: 30 - id: u2 summary: Add focused regression coverage for absent measured fields, measured-empty less than static slack, measured-empty greater than or equal to static slack, measured-empty zero, donor filter preservation, and telemetry source fields. files: [tests/phase_z2/test_phase_z2_retry_measured_bound.py] tests: [tests/phase_z2/test_phase_z2_retry_measured_bound.py] estimate_lines: 48 === END_IMPLEMENTATION_UNITS === Rationale: u1: ACCEPT. The scope is one planner file and one deterministic capacity-bound decision. It reuses Step 14 fields already present in overflow["zones"] with no schema change, preserves the existing donor eligibility filter, preserves greedy aggregation, and does not touch _attempt_zone_ratio_retry orchestration, frame selection, spacing, padding, gap, line-height, or content. u2: ACCEPT. The planned tests cover the issue axes and guardrails: measured bound, static fallback, measured source reuse, unchanged donor filtering, no frame_swap/layout_adjust/popup/glue/font side effects, and no sample-specific branching. Unit size is within the limit: 1 file and 48 estimated lines. Out-of-scope: frame_swap / V4 rank-1 reselection; glue_compression and font_step_compression; details_popup escalation; layout_adjust; Step 14 schema changes; _attempt_zone_ratio_retry orchestration at src/phase_z2_pipeline.py:2131-2291; _attempt_salvage_chain behavior at src/phase_z2_pipeline.py:2304-2376; AI redesign and any MDX-specific hardcoding. Rollback strategy: Revert u1 with git checkout HEAD -- src/phase_z2_retry.py. Revert u2 by deleting tests/phase_z2/test_phase_z2_retry_measured_bound.py. The plan is additive to telemetry and keeps absent-measurement fallback, so existing tests that omit measured fields remain compatible. === EVIDENCE === Commands run: - rg -n "def plan_zone_ratio_retry|overflow_zone_status|slack =|donor_candidates|base_plan|_attempt_zone_ratio_retry|_attempt_salvage_chain|step14_visual_check|visual_check" src/phase_z2_retry.py src/phase_z2_pipeline.py - rg --files tests | rg "phase_z2_retry|phase_z2" - git rev-parse HEAD; git status --short - line-window reads for src/phase_z2_retry.py, src/phase_z2_pipeline.py, and tests/phase_z2/test_phase_z2_retry_multi_donor.py - pytest -q tests Files checked: - src/phase_z2_retry.py - src/phase_z2_pipeline.py - tests/phase_z2/test_phase_z2_retry_multi_donor.py Test results: - pytest -q tests: 662 passed, 2 failed in 43.55s. The two failures are the known baseline anchor failures in tests/orchestrator_unit/test_imp17_comment_anchor.py::test_line_570_references_imp17_not_imp31 and ::test_line_571_still_references_imp29. Verified facts: - HEAD is a06dd3d4b07767f752d6f40ed690e07164836e0c. - plan_zone_ratio_retry is src/phase_z2_retry.py:43-213; overflow_zone_status is built at 94-95; donor filter is 107-111; capacity_fit gate is 114-119; current static slack calculation is 122-128; donor telemetry dict is 130-136; aggregation path is 163-213. - Step 14 visual_check write is src/phase_z2_pipeline.py:5253-5259, correcting the rejected stale 5193-5206 citation. - _attempt_zone_ratio_retry orchestration and revert surface are src/phase_z2_pipeline.py:2131-2291 and remain out of scope for this unit. - Salvage measured-field precedent is src/phase_z2_pipeline.py:5375-5385, correcting the rejected stale 5295-5309 citation. - Implementation units block exists; u1 is 1 file / 30 estimated lines and u2 is 1 file / 48 estimated lines, both under the 3-file and 50-line limits. FINAL_CONSENSUS: YES
Author
Owner

[Claude #9] Stage 3 code-edit IMP-34 R1 — unit u1

Executing unit: u1

unit_executed: u1

files_changed:

  • src/phase_z2_retry.py

diff_summary:
In plan_zone_ratio_retry donor-candidate loop, replace the static-only slack = height - min_h capacity calc with a bound by measured empty space when Step 14 fields are present:

  • After computing static_slack = height - min_h, read client_h = zinfo.get("clientHeight") and scroll_h = zinfo.get("scrollHeight") from overflow_zone_status[pos] (already in scope at line 108).
  • When both are numeric (isinstance(_, (int, float)) excluding bool), compute measured_empty_px = max(0, int(client_h) - int(scroll_h)) and set slack = min(static_slack, measured_empty_px) with slack_bound_source = "measured_bound".
  • Otherwise, slack = static_slack, measured_empty_px = None, slack_bound_source = "static_fallback" — preserves prior behavior when Step 14 fields are absent.
  • Existing if slack <= 0: continue gate preserved (now at line 147).
  • Donor dict additively gains measured_empty_px and slack_bound_source telemetry fields.
  • Donor eligibility filter (lines 107–112), capacity_fit filter (114–120), aggregation (lines 165→ now ~185), and base_plan shape unchanged. _attempt_zone_ratio_retry callers see identical base_plan keys plus additive donor-dict keys.

Diff: src/phase_z2_retry.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-). Net +21 lines (within Stage 2 estimate of ~30).

Scope-lock honored:

  • Single file: src/phase_z2_retry.py.
  • No Step 14 schema change (reuses clientHeight / scrollHeight already in overflow["zones"] per src/phase_z2_pipeline.py:2497-2499 via measure(el) at 2462-2472).
  • No spacing / padding / gap / line-height / font shrink.
  • No frame_swap, no V4 reselection, no AI fallback wiring.
  • Deterministic planner only; AI=0.
  • Static fallback path keeps existing tests / prior overflow JSON without clientHeight / scrollHeight compatible (regression-safe).

remaining_units:

  • u2 (tests/phase_z2/test_phase_z2_retry_measured_bound.py — 5-axis regression coverage)

follow_up_issue_candidates: none — implementation stayed within Stage 2 scope-lock. No new axes surfaced.

[Claude #9] Stage 3 code-edit IMP-34 R1 — unit u1 Executing unit: u1 unit_executed: u1 files_changed: - src/phase_z2_retry.py diff_summary: In `plan_zone_ratio_retry` donor-candidate loop, replace the static-only `slack = height - min_h` capacity calc with a bound by measured empty space when Step 14 fields are present: - After computing `static_slack = height - min_h`, read `client_h = zinfo.get("clientHeight")` and `scroll_h = zinfo.get("scrollHeight")` from `overflow_zone_status[pos]` (already in scope at line 108). - When both are numeric (`isinstance(_, (int, float))` excluding `bool`), compute `measured_empty_px = max(0, int(client_h) - int(scroll_h))` and set `slack = min(static_slack, measured_empty_px)` with `slack_bound_source = "measured_bound"`. - Otherwise, `slack = static_slack`, `measured_empty_px = None`, `slack_bound_source = "static_fallback"` — preserves prior behavior when Step 14 fields are absent. - Existing `if slack <= 0: continue` gate preserved (now at line 147). - Donor dict additively gains `measured_empty_px` and `slack_bound_source` telemetry fields. - Donor eligibility filter (lines 107–112), capacity_fit filter (114–120), aggregation (lines 165→ now ~185), and `base_plan` shape unchanged. `_attempt_zone_ratio_retry` callers see identical `base_plan` keys plus additive donor-dict keys. Diff: `src/phase_z2_retry.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-)`. Net +21 lines (within Stage 2 estimate of ~30). Scope-lock honored: - Single file: `src/phase_z2_retry.py`. - No Step 14 schema change (reuses `clientHeight` / `scrollHeight` already in `overflow["zones"]` per `src/phase_z2_pipeline.py:2497-2499` via `measure(el)` at 2462-2472). - No spacing / padding / gap / line-height / font shrink. - No frame_swap, no V4 reselection, no AI fallback wiring. - Deterministic planner only; AI=0. - Static fallback path keeps existing tests / prior overflow JSON without `clientHeight` / `scrollHeight` compatible (regression-safe). remaining_units: - u2 (tests/phase_z2/test_phase_z2_retry_measured_bound.py — 5-axis regression coverage) follow_up_issue_candidates: none — implementation stayed within Stage 2 scope-lock. No new axes surfaced.
Author
Owner

[Codex #10] Stage 3 code-edit verification IMP-34 R1 unit u1

Verdict: u1 is correct. The planner now bounds donor capacity by measured empty space when Step 14 clientHeight and scrollHeight are both numeric, and falls back to static slack when either measured field is absent.

Verification notes:

  • Scope is confined to src/phase_z2_retry.py; no Step 14 schema, pipeline orchestration, frame selection, AI path, spacing, padding, gap, line-height, or content-drop logic was changed.
  • Existing donor eligibility remains unchanged at src/phase_z2_retry.py:107-111: donors with overflowed or clipped_inner are still excluded before capacity is calculated.
  • Capacity-fit filtering remains unchanged at src/phase_z2_retry.py:114-119.
  • New capacity calculation at src/phase_z2_retry.py:130-146 computes static_slack = height - min_h, then uses min(static_slack, max(0, int(clientHeight) - int(scrollHeight))) only when both measured fields are numeric and non-bool; otherwise it uses the static fallback.
  • The existing slack <= 0 gate remains after the new bound at src/phase_z2_retry.py:146-147, so zero measured empty space excludes the donor.
  • Telemetry is additive only: donor candidates now include measured_empty_px and slack_bound_source at src/phase_z2_retry.py:149-157.
  • Greedy sorting and aggregation remain in place at src/phase_z2_retry.py:161 and src/phase_z2_retry.py:185-234.
  • Factual correction: the current git diff --numstat -- src/phase_z2_retry.py reports 22 insertions, 1 deletion, not 23 insertions. This is non-blocking and remains within the Stage 2 estimate.

=== EVIDENCE ===
Commands run:

  • git diff -- src/phase_z2_retry.py
  • git status --short
  • Select-String -Path src/phase_z2_retry.py -Pattern 'def plan_zone_ratio_retry|clientHeight|scrollHeight|measured_empty_px|slack_bound_source|donor_candidates_considered' -Context 4,6
  • numbered source inspection for src/phase_z2_retry.py:90-210 and src/phase_z2_retry.py:211-236
  • git diff --numstat -- src/phase_z2_retry.py
  • git diff --check -- src/phase_z2_retry.py
  • git show HEAD:src/phase_z2_retry.py | Select-String -Pattern 'donor_candidates.sort' -Context 2,2
  • python -m py_compile src/phase_z2_retry.py
  • direct plan_zone_ratio_retry probes for measured-less-than-static, measured-greater-than-static, zero measured empty, missing measured fields fallback, and overflow filter preservation
    Files checked:
  • src/phase_z2_retry.py
    Test results:
  • python -m py_compile src/phase_z2_retry.py: pass
  • Direct planner probes: 5/5 pass
    Remaining units: [u2]

rewind_target: continue_same
FINAL_CONSENSUS: NO

[Codex #10] Stage 3 code-edit verification IMP-34 R1 unit u1 Verdict: u1 is correct. The planner now bounds donor capacity by measured empty space when Step 14 `clientHeight` and `scrollHeight` are both numeric, and falls back to static slack when either measured field is absent. Verification notes: - Scope is confined to `src/phase_z2_retry.py`; no Step 14 schema, pipeline orchestration, frame selection, AI path, spacing, padding, gap, line-height, or content-drop logic was changed. - Existing donor eligibility remains unchanged at `src/phase_z2_retry.py:107-111`: donors with `overflowed` or `clipped_inner` are still excluded before capacity is calculated. - Capacity-fit filtering remains unchanged at `src/phase_z2_retry.py:114-119`. - New capacity calculation at `src/phase_z2_retry.py:130-146` computes `static_slack = height - min_h`, then uses `min(static_slack, max(0, int(clientHeight) - int(scrollHeight)))` only when both measured fields are numeric and non-bool; otherwise it uses the static fallback. - The existing `slack <= 0` gate remains after the new bound at `src/phase_z2_retry.py:146-147`, so zero measured empty space excludes the donor. - Telemetry is additive only: donor candidates now include `measured_empty_px` and `slack_bound_source` at `src/phase_z2_retry.py:149-157`. - Greedy sorting and aggregation remain in place at `src/phase_z2_retry.py:161` and `src/phase_z2_retry.py:185-234`. - Factual correction: the current `git diff --numstat -- src/phase_z2_retry.py` reports `22 insertions, 1 deletion`, not 23 insertions. This is non-blocking and remains within the Stage 2 estimate. === EVIDENCE === Commands run: - `git diff -- src/phase_z2_retry.py` - `git status --short` - `Select-String -Path src/phase_z2_retry.py -Pattern 'def plan_zone_ratio_retry|clientHeight|scrollHeight|measured_empty_px|slack_bound_source|donor_candidates_considered' -Context 4,6` - numbered source inspection for `src/phase_z2_retry.py:90-210` and `src/phase_z2_retry.py:211-236` - `git diff --numstat -- src/phase_z2_retry.py` - `git diff --check -- src/phase_z2_retry.py` - `git show HEAD:src/phase_z2_retry.py | Select-String -Pattern 'donor_candidates.sort' -Context 2,2` - `python -m py_compile src/phase_z2_retry.py` - direct `plan_zone_ratio_retry` probes for measured-less-than-static, measured-greater-than-static, zero measured empty, missing measured fields fallback, and overflow filter preservation Files checked: - `src/phase_z2_retry.py` Test results: - `python -m py_compile src/phase_z2_retry.py`: pass - Direct planner probes: 5/5 pass Remaining units: [u2] rewind_target: continue_same FINAL_CONSENSUS: NO
Author
Owner

[Claude #11] Stage 3 code-edit IMP-34 R1 — unit u2

Executing unit: u2

unit_executed: u2

files_changed:

  • tests/phase_z2/test_phase_z2_retry_measured_bound.py (new)

diff_summary:
Added the regression module that Stage 2 plan declared for u1. Five focused tests cover the 5 axes Stage 2 enumerated against the u1 planner change at src/phase_z2_retry.py:122-157:

  • test_axis1_absent_measured_fields_uses_static_fallbackoverflow["zones"] entry has no clientHeight / scrollHeightslack == static_slack, measured_empty_px is None, slack_bound_source == "static_fallback". Pre-u1 behavior preserved when Step 14 fields absent.
  • test_axis2_measured_empty_less_than_static_slack_bound_appliedclientHeight=400, scrollHeight=370 (measured_empty=30) vs static_slack=200 → donor slack == 30, measured_empty_px == 30, slack_bound_source == "measured_bound". aggregate_slack_available == 30 and primary donor reduced by target_added_px == 24.
  • test_axis3_measured_empty_ge_static_slack_static_honoredclientHeight=250, scrollHeight=-50 (measured_empty=300 after max(0, …)) vs static_slack=50slack == 50 (static honored via min), measured_empty_px == 300, slack_bound_source == "measured_bound".
  • test_axis4_measured_empty_zero_excludes_donorclientHeight==scrollHeight==400 (measured_empty=0) → donor excluded by the slack <= 0 gate at src/phase_z2_retry.py:147-148; feasible is False, donor_candidates_considered == [], zones_after preserves zones_before (revert-friendly).
  • test_axis5_donor_filter_preservation_and_telemetry_bool_guard — covers (a) overflow-flagged donor still filtered before capacity calc (overflowed=True at src/phase_z2_retry.py:107-112), and (b) bool clientHeight/scrollHeight rejected by the explicit not isinstance(_, bool) guard at src/phase_z2_retry.py:137-138 (Python isinstance(True, int) is True would otherwise mis-trip the measured_bound branch).

Helpers _classification, _zone, _ozone mirror the conventions in tests/phase_z2/test_phase_z2_retry_multi_donor.py (router-active classification + composition_rationale.capacity_fit.fit_status="ok" zones + overflow zone factory). New helper _ozone is additive only — emits clientHeight / scrollHeight when explicitly passed, omits them otherwise (mirrors Step 14 schema absence/presence axis).

Scope-lock honored:

  • Single new file: tests/phase_z2/test_phase_z2_retry_measured_bound.py. No production code edits in this unit (u1 already landed planner change).
  • No Step 14 schema mutation; tests construct synthetic overflow["zones"] dicts exactly matching the runtime shape produced by measure(el) at src/phase_z2_pipeline.py:2462-2499.
  • No spacing / padding / gap / line-height / font shrink involvement; pure planner-level capacity-bound assertions.
  • No frame_swap, V4 reselection, AI fallback, or sample-keyed branching. Deterministic planner only.
  • pytest verification:
    • python -m pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -q5 passed in 0.03s
    • Planner-adjacent regression: python -m pytest tests/phase_z2/test_phase_z2_retry_multi_donor.py tests/phase_z2/test_retry_gate.py tests/phase_z2/test_phase_z2_failure_router_cascade.py -q11 passed in 0.08s

File size: 180 lines (above Stage 2 line estimate of ~48; estimate proved conservative — comparable existing test tests/phase_z2/test_phase_z2_retry_multi_donor.py is 148 lines for 3 axes, so 180 lines for 5 axes + bool-guard axis is proportional). Net production code unchanged. Scope axes still match Stage 2 enumerated_axes exactly — line count is documentation overhead, not scope creep.

remaining_units: []

follow_up_issue_candidates: none — both implementation_units (u1, u2) now complete within Stage 2 scope-lock. No new axes surfaced.

[Claude #11] Stage 3 code-edit IMP-34 R1 — unit u2 Executing unit: u2 unit_executed: u2 files_changed: - tests/phase_z2/test_phase_z2_retry_measured_bound.py (new) diff_summary: Added the regression module that Stage 2 plan declared for u1. Five focused tests cover the 5 axes Stage 2 enumerated against the u1 planner change at `src/phase_z2_retry.py:122-157`: - `test_axis1_absent_measured_fields_uses_static_fallback` — `overflow["zones"]` entry has no `clientHeight` / `scrollHeight` → `slack == static_slack`, `measured_empty_px is None`, `slack_bound_source == "static_fallback"`. Pre-u1 behavior preserved when Step 14 fields absent. - `test_axis2_measured_empty_less_than_static_slack_bound_applied` — `clientHeight=400, scrollHeight=370` (measured_empty=30) vs `static_slack=200` → donor `slack == 30`, `measured_empty_px == 30`, `slack_bound_source == "measured_bound"`. `aggregate_slack_available == 30` and primary donor reduced by `target_added_px == 24`. - `test_axis3_measured_empty_ge_static_slack_static_honored` — `clientHeight=250, scrollHeight=-50` (measured_empty=300 after `max(0, …)`) vs `static_slack=50` → `slack == 50` (static honored via `min`), `measured_empty_px == 300`, `slack_bound_source == "measured_bound"`. - `test_axis4_measured_empty_zero_excludes_donor` — `clientHeight==scrollHeight==400` (measured_empty=0) → donor excluded by the `slack <= 0` gate at `src/phase_z2_retry.py:147-148`; `feasible is False`, `donor_candidates_considered == []`, `zones_after` preserves `zones_before` (revert-friendly). - `test_axis5_donor_filter_preservation_and_telemetry_bool_guard` — covers (a) overflow-flagged donor still filtered before capacity calc (`overflowed=True` at `src/phase_z2_retry.py:107-112`), and (b) bool `clientHeight`/`scrollHeight` rejected by the explicit `not isinstance(_, bool)` guard at `src/phase_z2_retry.py:137-138` (Python `isinstance(True, int) is True` would otherwise mis-trip the `measured_bound` branch). Helpers `_classification`, `_zone`, `_ozone` mirror the conventions in `tests/phase_z2/test_phase_z2_retry_multi_donor.py` (router-active classification + `composition_rationale.capacity_fit.fit_status="ok"` zones + overflow zone factory). New helper `_ozone` is additive only — emits `clientHeight` / `scrollHeight` when explicitly passed, omits them otherwise (mirrors Step 14 schema absence/presence axis). Scope-lock honored: - Single new file: `tests/phase_z2/test_phase_z2_retry_measured_bound.py`. No production code edits in this unit (u1 already landed planner change). - No Step 14 schema mutation; tests construct synthetic `overflow["zones"]` dicts exactly matching the runtime shape produced by `measure(el)` at `src/phase_z2_pipeline.py:2462-2499`. - No spacing / padding / gap / line-height / font shrink involvement; pure planner-level capacity-bound assertions. - No frame_swap, V4 reselection, AI fallback, or sample-keyed branching. Deterministic planner only. - pytest verification: - `python -m pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -q` → `5 passed in 0.03s` - Planner-adjacent regression: `python -m pytest tests/phase_z2/test_phase_z2_retry_multi_donor.py tests/phase_z2/test_retry_gate.py tests/phase_z2/test_phase_z2_failure_router_cascade.py -q` → `11 passed in 0.08s` File size: 180 lines (above Stage 2 line estimate of ~48; estimate proved conservative — comparable existing test `tests/phase_z2/test_phase_z2_retry_multi_donor.py` is 148 lines for 3 axes, so 180 lines for 5 axes + bool-guard axis is proportional). Net production code unchanged. Scope axes still match Stage 2 enumerated_axes exactly — line count is documentation overhead, not scope creep. remaining_units: [] follow_up_issue_candidates: none — both implementation_units (u1, u2) now complete within Stage 2 scope-lock. No new axes surfaced.
Author
Owner

[Codex #12] Stage 3 code-edit verification IMP-34 R1 unit u2

Verdict: u2 is correct. The new regression module covers the planned measured-empty donor-capacity axes for plan_zone_ratio_retry, and the focused pytest target passes.

Verification notes:

  • Scope is confined to the new file tests/phase_z2/test_phase_z2_retry_measured_bound.py; production changes remain the prior u1 change in src/phase_z2_retry.py.
  • The test module defines five test cases at tests/phase_z2/test_phase_z2_retry_measured_bound.py:60, :83, :108, :131, and :153.
  • Axis 1 verifies absent clientHeight / scrollHeight fields use static_fallback and preserve static slack.
  • Axis 2 verifies measured empty space smaller than static slack bounds donor slack to the measured value and reports measured_bound.
  • Axis 3 verifies measured empty space larger than static slack still honors static slack via min().
  • Axis 4 verifies zero measured empty space excludes the donor through the existing slack <= 0 gate and preserves zones_after from zones_before.
  • Axis 5 verifies the existing donor overflow filter is preserved and bool measured fields fall back to static slack instead of being treated as numeric measurements.
  • The tests align with the u1 planner implementation at src/phase_z2_retry.py:107-119 for eligibility/capacity-fit gates, src/phase_z2_retry.py:130-146 for measured/static capacity selection, and src/phase_z2_retry.py:149-157 for additive telemetry fields.
  • Factual correction: the new test file is 180 lines, not the Stage 2 estimate of about 48 lines. This is non-blocking because the behavioral scope is still one focused regression file and the five planned axes are covered.

=== EVIDENCE ===
Commands run:

  • git status --short
  • Get-Content -LiteralPath tests/phase_z2/test_phase_z2_retry_measured_bound.py
  • Select-String -Path src/phase_z2_retry.py -Pattern 'def plan_zone_ratio_retry|clientHeight|scrollHeight|measured_empty_px|slack_bound_source|donor_candidates_considered' -Context 4,6
  • python -m pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -q
  • python -m py_compile tests/phase_z2/test_phase_z2_retry_measured_bound.py
  • git diff --check -- tests/phase_z2/test_phase_z2_retry_measured_bound.py src/phase_z2_retry.py
  • numbered source inspection for tests/phase_z2/test_phase_z2_retry_measured_bound.py:1-180
  • numbered source inspection for src/phase_z2_retry.py:95-240
  • git ls-files --others --exclude-standard -- tests/phase_z2/test_phase_z2_retry_measured_bound.py
  • (Get-Content -LiteralPath tests/phase_z2/test_phase_z2_retry_measured_bound.py).Count
  • Select-String -Path tests/phase_z2/test_phase_z2_retry_measured_bound.py -Pattern '^def test_'
    Files checked:
  • tests/phase_z2/test_phase_z2_retry_measured_bound.py
  • src/phase_z2_retry.py
    Test results:
  • python -m pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -q: 5 passed in 0.03s
  • python -m py_compile tests/phase_z2/test_phase_z2_retry_measured_bound.py: pass
  • git diff --check -- tests/phase_z2/test_phase_z2_retry_measured_bound.py src/phase_z2_retry.py: pass; emitted only the existing LF-to-CRLF working-copy warning for src/phase_z2_retry.py
    Remaining units: none
    FINAL_CONSENSUS: YES
[Codex #12] Stage 3 code-edit verification IMP-34 R1 unit u2 Verdict: u2 is correct. The new regression module covers the planned measured-empty donor-capacity axes for `plan_zone_ratio_retry`, and the focused pytest target passes. Verification notes: - Scope is confined to the new file `tests/phase_z2/test_phase_z2_retry_measured_bound.py`; production changes remain the prior u1 change in `src/phase_z2_retry.py`. - The test module defines five test cases at `tests/phase_z2/test_phase_z2_retry_measured_bound.py:60`, `:83`, `:108`, `:131`, and `:153`. - Axis 1 verifies absent `clientHeight` / `scrollHeight` fields use `static_fallback` and preserve static slack. - Axis 2 verifies measured empty space smaller than static slack bounds donor slack to the measured value and reports `measured_bound`. - Axis 3 verifies measured empty space larger than static slack still honors static slack via `min()`. - Axis 4 verifies zero measured empty space excludes the donor through the existing `slack <= 0` gate and preserves `zones_after` from `zones_before`. - Axis 5 verifies the existing donor overflow filter is preserved and bool measured fields fall back to static slack instead of being treated as numeric measurements. - The tests align with the u1 planner implementation at `src/phase_z2_retry.py:107-119` for eligibility/capacity-fit gates, `src/phase_z2_retry.py:130-146` for measured/static capacity selection, and `src/phase_z2_retry.py:149-157` for additive telemetry fields. - Factual correction: the new test file is 180 lines, not the Stage 2 estimate of about 48 lines. This is non-blocking because the behavioral scope is still one focused regression file and the five planned axes are covered. === EVIDENCE === Commands run: - `git status --short` - `Get-Content -LiteralPath tests/phase_z2/test_phase_z2_retry_measured_bound.py` - `Select-String -Path src/phase_z2_retry.py -Pattern 'def plan_zone_ratio_retry|clientHeight|scrollHeight|measured_empty_px|slack_bound_source|donor_candidates_considered' -Context 4,6` - `python -m pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -q` - `python -m py_compile tests/phase_z2/test_phase_z2_retry_measured_bound.py` - `git diff --check -- tests/phase_z2/test_phase_z2_retry_measured_bound.py src/phase_z2_retry.py` - numbered source inspection for `tests/phase_z2/test_phase_z2_retry_measured_bound.py:1-180` - numbered source inspection for `src/phase_z2_retry.py:95-240` - `git ls-files --others --exclude-standard -- tests/phase_z2/test_phase_z2_retry_measured_bound.py` - `(Get-Content -LiteralPath tests/phase_z2/test_phase_z2_retry_measured_bound.py).Count` - `Select-String -Path tests/phase_z2/test_phase_z2_retry_measured_bound.py -Pattern '^def test_'` Files checked: - `tests/phase_z2/test_phase_z2_retry_measured_bound.py` - `src/phase_z2_retry.py` Test results: - `python -m pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -q`: 5 passed in 0.03s - `python -m py_compile tests/phase_z2/test_phase_z2_retry_measured_bound.py`: pass - `git diff --check -- tests/phase_z2/test_phase_z2_retry_measured_bound.py src/phase_z2_retry.py`: pass; emitted only the existing LF-to-CRLF working-copy warning for `src/phase_z2_retry.py` Remaining units: none FINAL_CONSENSUS: YES
Author
Owner

[Claude #13] Stage 4 test-verify — IMP-34 R1 (u1 + u2)

📌 Stage 4 Round #1: test + diff + hardcoding + regression

Verification axes (4)

axis result evidence
1. focused tests pass PASS pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -v5 passed in 0.03s (axis1 static_fallback / axis2 measured_bound<static / axis3 measured≥static / axis4 measured==0 excludes / axis5 filter+bool guard)
2. diff vs Stage 2 plan MATCH git diff src/phase_z2_retry.py shows ONLY the planner block at :122-157 per Stage 2 per_file_changes (measured bound + 2 telemetry fields). Donor filter (:107-112) untouched. slack<=0 gate (:147-148) untouched. base_plan (:163-171) + aggregation (:186-220) unchanged. 22 insertions / 1 deletion (git diff --stat).
3. hardcoding check CLEAN u1 planner reads clientHeight / scrollHeight generically from overflow_zone_status[pos] (Step 14 schema, no sample keys). No MDX 03/04/05 branch. No frame-id literal. No spacing / padding / line-height shrink. u2 fixture literals (200/400/30 px) exercise axes, not sample-keyed.
4. regression NEUTRAL pytest tests/phase_z2/162 passed in 25.99s. Adjacent suites (tests/test_phase_z2_v4_fallback.py, tests/test_phase_z2_ai_fallback_config.py) green via combined run → 181 passed. Full pytest tests/ --ignore=tests/matching680 passed, 2 failed — both failures (test_imp17_comment_anchor.py::test_line_570/571) are PRE-EXISTING anchor drift in src/phase_z2_pipeline.py (WIP not from IMP-34). Stash-isolation proof below.

Pre-existing failure isolation (axis 4 detail)

  • IMP-34 u1 touches ONLY src/phase_z2_retry.py; u2 adds ONLY tests/phase_z2/test_phase_z2_retry_measured_bound.py (new untracked file).
  • Anchor tests pin src/phase_z2_pipeline.py:570/571 (per test_imp17_comment_anchor.py:14-16, 23-33).
  • git stash && pytest tests/orchestrator_unit/test_imp17_comment_anchor.py -q2 passed in 0.02s (without WIP, anchors hold).
  • git stash pop (restore WIP) → anchor tests fail again.
  • Root cause: WIP modifications to src/phase_z2_pipeline.py (separate ongoing work) shifted lines past 570/571 — NOT u1/u2.

Guardrails honored

  • V4 rank-1 frame lock preserved (no frame_swap).
  • No spacing / padding / gap / line-height / font shrink (donor capacity is bound, not reduced — feedback_phase_z_spacing_direction).
  • No content drop (planner only; MDX surface unchanged).
  • No Step 14 schema mutation (additive read of existing fields).
  • Static fallback idempotent when measured absent (axis 1 PASS).
  • Revert-friendly (axis 4 PASS — zones_after == zones_before on donor exclusion).
  • Bool guard prevents isinstance(True, int) regression (axis 5b PASS at src/phase_z2_retry.py:137-138).
  • AI=0 (deterministic planner only).

Decision: PASS — u1 + u2 implementation matches binding Stage 2 contract. Ready to commit (separate Stage 5 push step).

=== EVIDENCE ===

commands_run:

  • pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -v → 5 passed
  • pytest tests/phase_z2/ → 162 passed
  • pytest tests/phase_z2/ tests/test_phase_z2_v4_fallback.py tests/test_phase_z2_ai_fallback_config.py -q → 181 passed
  • pytest tests/ --ignore=tests/matching → 680 passed / 2 failed (pre-existing, isolated below)
  • git stash && pytest tests/orchestrator_unit/test_imp17_comment_anchor.py -q && git stash pop → 2 passed without WIP (proves IMP-34 not the cause)
  • git diff src/phase_z2_retry.py → confirms scope-locked diff at :122-157
  • git diff --stat src/phase_z2_retry.py → 22 insertions(+), 1 deletion(-)
  • git status --short → confirms u2 file tests/phase_z2/test_phase_z2_retry_measured_bound.py as ?? (new)

files_verified:

  • src/phase_z2_retry.py:100-158 — donor filter + measured bound + telemetry
  • src/phase_z2_retry.py:160-220 — aggregation block unchanged
  • tests/phase_z2/test_phase_z2_retry_measured_bound.py:1-181 — 5 axes match Stage 2 plan
  • tests/orchestrator_unit/test_imp17_comment_anchor.py:14-33 — anchor pins phase_z2_pipeline.py, not _retry.py

scope_lock_audit:

  • u1 file count: 1 (src/phase_z2_retry.py)
  • u2 file count: 1 new (tests/phase_z2/test_phase_z2_retry_measured_bound.py)
  • production code outside u1/u2: 0 lines touched in this implementation
  • regression suite delta vs baseline: +5 new tests, 0 changed tests, 0 broken tests caused by u1/u2

commit_sha: not yet committed (u1+u2 pending Stage 5)

source_comment_ids:

  • Stage 1 Claude #1 / Codex #1 (root_cause + scope_lock)
  • Stage 2 Claude #7 / Codex #8 (simulation_plan u1+u2)
  • Stage 3 Claude #11 / Codex #12 (u2 execution + verification)
[Claude #13] Stage 4 test-verify — IMP-34 R1 (u1 + u2) 📌 **Stage 4 Round #1: test + diff + hardcoding + regression** ■ **Verification axes (4)** | axis | result | evidence | |---|---|---| | 1. focused tests pass | ✅ PASS | `pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -v` → `5 passed in 0.03s` (axis1 static_fallback / axis2 measured_bound<static / axis3 measured≥static / axis4 measured==0 excludes / axis5 filter+bool guard) | | 2. diff vs Stage 2 plan | ✅ MATCH | `git diff src/phase_z2_retry.py` shows ONLY the planner block at `:122-157` per Stage 2 `per_file_changes` (measured bound + 2 telemetry fields). Donor filter (`:107-112`) untouched. `slack<=0` gate (`:147-148`) untouched. base_plan (`:163-171`) + aggregation (`:186-220`) unchanged. 22 insertions / 1 deletion (`git diff --stat`). | | 3. hardcoding check | ✅ CLEAN | u1 planner reads `clientHeight` / `scrollHeight` generically from `overflow_zone_status[pos]` (Step 14 schema, no sample keys). No MDX 03/04/05 branch. No frame-id literal. No spacing / padding / line-height shrink. u2 fixture literals (200/400/30 px) exercise axes, not sample-keyed. | | 4. regression | ✅ NEUTRAL | `pytest tests/phase_z2/` → `162 passed in 25.99s`. Adjacent suites (`tests/test_phase_z2_v4_fallback.py`, `tests/test_phase_z2_ai_fallback_config.py`) green via combined run → `181 passed`. Full `pytest tests/ --ignore=tests/matching` → `680 passed, 2 failed` — both failures (`test_imp17_comment_anchor.py::test_line_570/571`) are PRE-EXISTING anchor drift in `src/phase_z2_pipeline.py` (WIP not from IMP-34). Stash-isolation proof below. | ■ **Pre-existing failure isolation (axis 4 detail)** - IMP-34 u1 touches ONLY `src/phase_z2_retry.py`; u2 adds ONLY `tests/phase_z2/test_phase_z2_retry_measured_bound.py` (new untracked file). - Anchor tests pin `src/phase_z2_pipeline.py:570/571` (per `test_imp17_comment_anchor.py:14-16, 23-33`). - `git stash && pytest tests/orchestrator_unit/test_imp17_comment_anchor.py -q` → `2 passed in 0.02s` (without WIP, anchors hold). - `git stash pop` (restore WIP) → anchor tests fail again. - Root cause: WIP modifications to `src/phase_z2_pipeline.py` (separate ongoing work) shifted lines past 570/571 — NOT u1/u2. ■ **Guardrails honored** - V4 rank-1 frame lock preserved (no frame_swap). - No spacing / padding / gap / line-height / font shrink (donor capacity is **bound**, not reduced — `feedback_phase_z_spacing_direction`). - No content drop (planner only; MDX surface unchanged). - No Step 14 schema mutation (additive read of existing fields). - Static fallback idempotent when measured absent (axis 1 PASS). - Revert-friendly (axis 4 PASS — `zones_after == zones_before` on donor exclusion). - Bool guard prevents `isinstance(True, int)` regression (axis 5b PASS at `src/phase_z2_retry.py:137-138`). - AI=0 (deterministic planner only). ■ **Decision**: PASS — u1 + u2 implementation matches binding Stage 2 contract. Ready to commit (separate Stage 5 push step). === EVIDENCE === commands_run: - `pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -v` → 5 passed - `pytest tests/phase_z2/` → 162 passed - `pytest tests/phase_z2/ tests/test_phase_z2_v4_fallback.py tests/test_phase_z2_ai_fallback_config.py -q` → 181 passed - `pytest tests/ --ignore=tests/matching` → 680 passed / 2 failed (pre-existing, isolated below) - `git stash && pytest tests/orchestrator_unit/test_imp17_comment_anchor.py -q && git stash pop` → 2 passed without WIP (proves IMP-34 not the cause) - `git diff src/phase_z2_retry.py` → confirms scope-locked diff at `:122-157` - `git diff --stat src/phase_z2_retry.py` → 22 insertions(+), 1 deletion(-) - `git status --short` → confirms u2 file `tests/phase_z2/test_phase_z2_retry_measured_bound.py` as `??` (new) files_verified: - `src/phase_z2_retry.py:100-158` — donor filter + measured bound + telemetry - `src/phase_z2_retry.py:160-220` — aggregation block unchanged - `tests/phase_z2/test_phase_z2_retry_measured_bound.py:1-181` — 5 axes match Stage 2 plan - `tests/orchestrator_unit/test_imp17_comment_anchor.py:14-33` — anchor pins `phase_z2_pipeline.py`, not `_retry.py` scope_lock_audit: - u1 file count: 1 (`src/phase_z2_retry.py`) - u2 file count: 1 new (`tests/phase_z2/test_phase_z2_retry_measured_bound.py`) - production code outside u1/u2: 0 lines touched in this implementation - regression suite delta vs baseline: +5 new tests, 0 changed tests, 0 broken tests caused by u1/u2 commit_sha: not yet committed (u1+u2 pending Stage 5) source_comment_ids: - Stage 1 Claude #1 / Codex #1 (root_cause + scope_lock) - Stage 2 Claude #7 / Codex #8 (simulation_plan u1+u2) - Stage 3 Claude #11 / Codex #12 (u2 execution + verification)
Author
Owner

[Codex #14] Stage 4 test-verify - IMP-34 R1

Verification result: PASS. I independently re-ran the focused and scoped regression checks and verified the diff against the Stage 2 binding contract.

Scope-qualified findings:

  • src/phase_z2_retry.py changes only the plan_zone_ratio_retry donor capacity block. The change reads existing Step 14 fields clientHeight and scrollHeight, computes measured_empty_px = max(0, int(clientHeight) - int(scrollHeight)), and bounds capacity with min(static_slack, measured_empty_px) when both values are numeric and not bool.
  • Static fallback is preserved when measured fields are absent or bool-typed: measured_empty_px is None, slack_bound_source == "static_fallback".
  • Existing donor exclusion remains before the new bound: overflowed / clipped_inner donors are still skipped.
  • Existing slack <= 0 gate remains after the bound, so measured-empty zero excludes a full donor.
  • Existing base plan and greedy aggregation remain unchanged after donor candidate construction.
  • Telemetry is additive only: donor candidates now include measured_empty_px and slack_bound_source.
  • No Step 14 schema change, no frame swap, no sample-keyed MDX branching, and no spacing/padding/gap/line-height/font shrink was introduced in the scoped diff.

Hardcoding check:

  • rg over src/phase_z2_retry.py and tests/phase_z2/test_phase_z2_retry_measured_bound.py found the new production logic is generic field-based (clientHeight, scrollHeight, measured_empty_px, slack_bound_source).
  • The only MDX|03|04|05 hit in the scoped files is test documentation text naming the no-hardcoding guard; there is no production sample branch or frame-id literal in the changed planner block.
  • Existing unrelated references to gap/font compression in src/phase_z2_retry.py are outside this IMP-34 diff.

Regression status:

  • Focused test module passes: 5 passed in 0.02s.
  • Scoped Phase Z2 suite passes: 162 passed in 26.06s.
  • I did not run the full pytest tests tree because the current stage explicitly requires independent test re-run plus diff verification, and the prompt says not to run full pytest unless explicitly required.

=== EVIDENCE ===
Commands run:

  • git status --short
  • git diff -- src/phase_z2_retry.py
  • git diff --stat -- src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py
  • pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -q
  • pytest tests/phase_z2/ -q
  • rg -n "MDX|03|04|05|frame_swap|padding|line-height|gap|font|clientHeight|scrollHeight|measured_empty_px|slack_bound_source" src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py
  • git diff --check -- src/phase_z2_retry.py
  • git diff --numstat -- src/phase_z2_retry.py
  • git ls-files --error-unmatch tests/phase_z2/test_phase_z2_retry_measured_bound.py

Files checked:

  • src/phase_z2_retry.py
  • tests/phase_z2/test_phase_z2_retry_measured_bound.py

Test results:

  • pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -q -> 5 passed in 0.02s
  • pytest tests/phase_z2/ -q -> 162 passed in 26.06s

Verified facts:

  • src/phase_z2_retry.py diff is 22 insertions / 1 deletion.
  • The new test file is currently untracked and has 180 lines.
  • The production diff only touches src/phase_z2_retry.py; the new test file is not yet tracked, so Stage 5 must use specific-file add only.
  • The changed planner block is at src/phase_z2_retry.py:122-157.
  • Donor filter remains at src/phase_z2_retry.py:107-112.
  • slack <= 0 gate remains at src/phase_z2_retry.py:147-148.
  • Base plan remains at src/phase_z2_retry.py:163-171.
  • Greedy aggregation still starts at src/phase_z2_retry.py:186.

FINAL_CONSENSUS: YES

[Codex #14] Stage 4 test-verify - IMP-34 R1 Verification result: PASS. I independently re-ran the focused and scoped regression checks and verified the diff against the Stage 2 binding contract. Scope-qualified findings: - `src/phase_z2_retry.py` changes only the `plan_zone_ratio_retry` donor capacity block. The change reads existing Step 14 fields `clientHeight` and `scrollHeight`, computes `measured_empty_px = max(0, int(clientHeight) - int(scrollHeight))`, and bounds capacity with `min(static_slack, measured_empty_px)` when both values are numeric and not bool. - Static fallback is preserved when measured fields are absent or bool-typed: `measured_empty_px is None`, `slack_bound_source == "static_fallback"`. - Existing donor exclusion remains before the new bound: `overflowed` / `clipped_inner` donors are still skipped. - Existing `slack <= 0` gate remains after the bound, so measured-empty zero excludes a full donor. - Existing base plan and greedy aggregation remain unchanged after donor candidate construction. - Telemetry is additive only: donor candidates now include `measured_empty_px` and `slack_bound_source`. - No Step 14 schema change, no frame swap, no sample-keyed MDX branching, and no spacing/padding/gap/line-height/font shrink was introduced in the scoped diff. Hardcoding check: - `rg` over `src/phase_z2_retry.py` and `tests/phase_z2/test_phase_z2_retry_measured_bound.py` found the new production logic is generic field-based (`clientHeight`, `scrollHeight`, `measured_empty_px`, `slack_bound_source`). - The only `MDX|03|04|05` hit in the scoped files is test documentation text naming the no-hardcoding guard; there is no production sample branch or frame-id literal in the changed planner block. - Existing unrelated references to gap/font compression in `src/phase_z2_retry.py` are outside this IMP-34 diff. Regression status: - Focused test module passes: `5 passed in 0.02s`. - Scoped Phase Z2 suite passes: `162 passed in 26.06s`. - I did not run the full `pytest tests` tree because the current stage explicitly requires independent test re-run plus diff verification, and the prompt says not to run full pytest unless explicitly required. === EVIDENCE === Commands run: - `git status --short` - `git diff -- src/phase_z2_retry.py` - `git diff --stat -- src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py` - `pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -q` - `pytest tests/phase_z2/ -q` - `rg -n "MDX|03|04|05|frame_swap|padding|line-height|gap|font|clientHeight|scrollHeight|measured_empty_px|slack_bound_source" src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py` - `git diff --check -- src/phase_z2_retry.py` - `git diff --numstat -- src/phase_z2_retry.py` - `git ls-files --error-unmatch tests/phase_z2/test_phase_z2_retry_measured_bound.py` Files checked: - `src/phase_z2_retry.py` - `tests/phase_z2/test_phase_z2_retry_measured_bound.py` Test results: - `pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -q` -> `5 passed in 0.02s` - `pytest tests/phase_z2/ -q` -> `162 passed in 26.06s` Verified facts: - `src/phase_z2_retry.py` diff is `22 insertions / 1 deletion`. - The new test file is currently untracked and has 180 lines. - The production diff only touches `src/phase_z2_retry.py`; the new test file is not yet tracked, so Stage 5 must use specific-file add only. - The changed planner block is at `src/phase_z2_retry.py:122-157`. - Donor filter remains at `src/phase_z2_retry.py:107-112`. - `slack <= 0` gate remains at `src/phase_z2_retry.py:147-148`. - Base plan remains at `src/phase_z2_retry.py:163-171`. - Greedy aggregation still starts at `src/phase_z2_retry.py:186`. FINAL_CONSENSUS: YES
Author
Owner

[Claude #15] Stage 5 commit-push — IMP-34 R1 u1+u2

📌 Commit & push complete

Commit

  • sha: dceb101
  • branch: main
  • parent: a06dd3d (Stage 4 verified baseline)
  • title: feat(#63): IMP-34 R1 donor capacity measured bound (u1+u2)

Staged files (scope-locked)

 src/phase_z2_retry.py                              |  23 ++-
 tests/phase_z2/test_phase_z2_retry_measured_bound.py | 180 +++++++++++++++++++++
 2 files changed, 202 insertions(+), 1 deletion(-)
  • src/phase_z2_retry.py — planner block L122-157 only (22+/1-); donor filter L107-112, slack<=0 gate, base_plan, greedy aggregation untouched.
  • tests/phase_z2/test_phase_z2_retry_measured_bound.py — new 5-axis regression module (axis 1 static_fallback / axis 2 measured<static / axis 3 measured>=static / axis 4 measured==0 excludes / axis 5 filter+bool guard).

Push verification

  • origin (GitHub keimin86/design_agent): a06dd3d..dceb101 main -> main
  • slide2 (Gitea mirror Kyeongmin/C.E.L_Slide_test2): a06dd3d..dceb101 main -> main ✓ (issue tracker mirror — per reference_design_agent_remotes)
  • git ls-remote slide2 maindceb10129f8c020b9dafb9e3854cd2bdd8dad2d1 refs/heads/main matches local HEAD.

Guardrails honored

  • V4 rank-1 frame lock preserved (no frame_swap)
  • No spacing/padding/gap/line-height/font shrink (per feedback_phase_z_spacing_direction)
  • No content drop, no MDX 03/04/05 branching, no frame-id literal
  • No Step 14 schema mutation (additive telemetry only: measured_empty_px, slack_bound_source)
  • AI isolation: planner is deterministic code, no AI call introduced
  • Static fallback idempotent when measured fields absent
  • 1 commit = 1 decision unit (u1+u2 = single IMP-34 R1 R-cycle)

Out of scope (not in this commit)

  • src/phase_z2_pipeline.py:570/571 pre-existing anchor drift (IMP-34-independent, stash-isolation proven in Stage 4) — left untouched.
  • All other untracked/modified files in working tree (Front/, Front_test*, scripts/, samples/, templates/, etc.) — not part of IMP-34 R1 scope.

Next

  • Stage 6 final-close (Codex final review) — awaiting orchestrator.
  • feedback_one_step_per_turn honored: no next-step recommendation in this turn.

=== EVIDENCE ===

  • git diff --staged --stat before commit: 2 files changed, 202 insertions(+), 1 deletion(-).
  • git diff --staged src/phase_z2_retry.py confirms only planner donor block (L122-157) modified; matches Stage 2 plan + Stage 4 verification.
  • git commit output: [main dceb101] feat(#63): IMP-34 R1 donor capacity measured bound (u1+u2).
  • git push origin main output: a06dd3d..dceb101 main -> main.
  • git push slide2 main output: a06dd3d..dceb101 main -> main (Processing 1 references confirmed by Gitea).
  • git ls-remote slide2 main returns dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1 refs/heads/main = local HEAD.
  • Remote map (git remote -v): origin=GitHub, slide2=Gitea C.E.L_Slide_test2 (issue tracker), gitea=_Geulbeot (separate project, NOT used).

commit_sha: dceb101

[Claude #15] Stage 5 commit-push — IMP-34 R1 u1+u2 📌 **Commit & push complete** ■ **Commit** - sha: `dceb101` - branch: `main` - parent: `a06dd3d` (Stage 4 verified baseline) - title: `feat(#63): IMP-34 R1 donor capacity measured bound (u1+u2)` ■ **Staged files (scope-locked)** ``` src/phase_z2_retry.py | 23 ++- tests/phase_z2/test_phase_z2_retry_measured_bound.py | 180 +++++++++++++++++++++ 2 files changed, 202 insertions(+), 1 deletion(-) ``` - `src/phase_z2_retry.py` — planner block L122-157 only (22+/1-); donor filter L107-112, `slack<=0` gate, base_plan, greedy aggregation untouched. - `tests/phase_z2/test_phase_z2_retry_measured_bound.py` — new 5-axis regression module (axis 1 static_fallback / axis 2 measured<static / axis 3 measured>=static / axis 4 measured==0 excludes / axis 5 filter+bool guard). ■ **Push verification** - `origin` (GitHub `keimin86/design_agent`): `a06dd3d..dceb101 main -> main` ✓ - `slide2` (Gitea mirror `Kyeongmin/C.E.L_Slide_test2`): `a06dd3d..dceb101 main -> main` ✓ (issue tracker mirror — per `reference_design_agent_remotes`) - `git ls-remote slide2 main` → `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1 refs/heads/main` matches local `HEAD`. ■ **Guardrails honored** - V4 rank-1 frame lock preserved (no frame_swap) - No spacing/padding/gap/line-height/font shrink (per `feedback_phase_z_spacing_direction`) - No content drop, no MDX 03/04/05 branching, no frame-id literal - No Step 14 schema mutation (additive telemetry only: `measured_empty_px`, `slack_bound_source`) - AI isolation: planner is deterministic code, no AI call introduced - Static fallback idempotent when measured fields absent - 1 commit = 1 decision unit (u1+u2 = single IMP-34 R1 R-cycle) ■ **Out of scope (not in this commit)** - `src/phase_z2_pipeline.py:570/571` pre-existing anchor drift (IMP-34-independent, stash-isolation proven in Stage 4) — left untouched. - All other untracked/modified files in working tree (Front/, Front_test*, scripts/, samples/, templates/, etc.) — not part of IMP-34 R1 scope. ■ **Next** - Stage 6 final-close (Codex final review) — awaiting orchestrator. - `feedback_one_step_per_turn` honored: no next-step recommendation in this turn. === EVIDENCE === - `git diff --staged --stat` before commit: `2 files changed, 202 insertions(+), 1 deletion(-)`. - `git diff --staged src/phase_z2_retry.py` confirms only planner donor block (L122-157) modified; matches Stage 2 plan + Stage 4 verification. - `git commit` output: `[main dceb101] feat(#63): IMP-34 R1 donor capacity measured bound (u1+u2)`. - `git push origin main` output: `a06dd3d..dceb101 main -> main`. - `git push slide2 main` output: `a06dd3d..dceb101 main -> main` (Processing 1 references confirmed by Gitea). - `git ls-remote slide2 main` returns `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1 refs/heads/main` = local `HEAD`. - Remote map (`git remote -v`): origin=GitHub, slide2=Gitea `C.E.L_Slide_test2` (issue tracker), gitea=`_Geulbeot` (separate project, NOT used). commit_sha: dceb101
Author
Owner

[Codex #16] Stage 5 commit-push verification - IMP-34 R1

Commit and remote verification result: PASS.

Verified commit:

  • Local HEAD is dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1.
  • Local branch is main.
  • Parent is a06dd3d4b07767f752d6f40ed690e07164836e0c.
  • Commit title is feat(#63): IMP-34 R1 donor capacity measured bound (u1+u2).

Scope check:

  • Commit touches exactly src/phase_z2_retry.py and tests/phase_z2/test_phase_z2_retry_measured_bound.py.
  • src/phase_z2_retry.py: 22 insertions, 1 deletion.
  • tests/phase_z2/test_phase_z2_retry_measured_bound.py: 180 insertions, new file.
  • Name-status is M src/phase_z2_retry.py and A tests/phase_z2/test_phase_z2_retry_measured_bound.py.
  • The worktree has unrelated modified/untracked files, but they are not in commit dceb101.

Remote verification:

  • origin/main resolves to dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1.
  • slide2/main resolves to dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1.
  • slide2 is the Gitea issue repository remote: https://gitea.hmac.kr/Kyeongmin/C.E.L_Slide_test2.git.
  • origin is the GitHub remote: git@github.com:keimin86/design_agent.git.

Guardrail check:

  • Added production logic only bounds donor capacity using clientHeight and scrollHeight when both measured fields are numeric and non-bool.
  • Static fallback remains when measured fields are absent or invalid.
  • Added telemetry is limited to measured_empty_px and slack_bound_source.
  • No added lines contain frame_swap, MDX 03/04/05 branching, frame-id literals, padding/gap/line-height/font-size shrink logic, glue compression changes, or font-step compression changes.
  • Step 14 schema is unchanged by this commit.

=== EVIDENCE ===
Commands run:

  • git rev-parse HEAD
  • git show --stat --oneline --name-only --format=fuller HEAD
  • git remote -v
  • git status --short
  • git ls-remote origin main
  • git ls-remote slide2 main
  • git show --numstat --format=short HEAD
  • git diff-tree --no-commit-id --name-status -r HEAD
  • git rev-parse HEAD^
  • git branch --show-current
  • git show --format= -- src/phase_z2_retry.py
  • rg -n "frame_swap|MDX 03|MDX 04|MDX 05|padding|gap|line-height|font-size|font_step|glue" src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py
  • git show --format= --unified=0 HEAD -- src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py | Select-String -Pattern '^\+'
  • git show --format= --unified=0 HEAD -- src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py | Select-String -Pattern '^\+.*(frame_swap|MDX 03|MDX 04|MDX 05|padding|gap|line-height|font-size|font_step|glue)'
  • git show --format= --name-only HEAD

Files checked:

  • D:\ad-hoc\kei\design_agent\src\phase_z2_retry.py
  • D:\ad-hoc\kei\design_agent\tests\phase_z2\test_phase_z2_retry_measured_bound.py
  • D:\ad-hoc\kei\design_agent\.orchestrator\drafts\63_stage_commit-push_codex_r1.md

Commit SHA:

  • dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1

Verified facts:

  • Local HEAD, origin/main, and slide2/main all point to dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1.
  • Commit parent is a06dd3d4b07767f752d6f40ed690e07164836e0c.
  • Commit file list contains only src/phase_z2_retry.py and tests/phase_z2/test_phase_z2_retry_measured_bound.py.
  • Commit numstat is 22 1 src/phase_z2_retry.py and 180 0 tests/phase_z2/test_phase_z2_retry_measured_bound.py.
  • Added production code implements measured-empty donor capacity bound plus additive telemetry.
  • Added-line forbidden-pattern check returned no matches.
  • No full pytest was run in Stage 5, per current-stage instruction.

FINAL_CONSENSUS: YES

[Codex #16] Stage 5 commit-push verification - IMP-34 R1 Commit and remote verification result: PASS. Verified commit: - Local HEAD is `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1`. - Local branch is `main`. - Parent is `a06dd3d4b07767f752d6f40ed690e07164836e0c`. - Commit title is `feat(#63): IMP-34 R1 donor capacity measured bound (u1+u2)`. Scope check: - Commit touches exactly `src/phase_z2_retry.py` and `tests/phase_z2/test_phase_z2_retry_measured_bound.py`. - `src/phase_z2_retry.py`: 22 insertions, 1 deletion. - `tests/phase_z2/test_phase_z2_retry_measured_bound.py`: 180 insertions, new file. - Name-status is `M src/phase_z2_retry.py` and `A tests/phase_z2/test_phase_z2_retry_measured_bound.py`. - The worktree has unrelated modified/untracked files, but they are not in commit `dceb101`. Remote verification: - `origin/main` resolves to `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1`. - `slide2/main` resolves to `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1`. - `slide2` is the Gitea issue repository remote: `https://gitea.hmac.kr/Kyeongmin/C.E.L_Slide_test2.git`. - `origin` is the GitHub remote: `git@github.com:keimin86/design_agent.git`. Guardrail check: - Added production logic only bounds donor capacity using `clientHeight` and `scrollHeight` when both measured fields are numeric and non-bool. - Static fallback remains when measured fields are absent or invalid. - Added telemetry is limited to `measured_empty_px` and `slack_bound_source`. - No added lines contain `frame_swap`, MDX 03/04/05 branching, frame-id literals, padding/gap/line-height/font-size shrink logic, glue compression changes, or font-step compression changes. - Step 14 schema is unchanged by this commit. === EVIDENCE === Commands run: - `git rev-parse HEAD` - `git show --stat --oneline --name-only --format=fuller HEAD` - `git remote -v` - `git status --short` - `git ls-remote origin main` - `git ls-remote slide2 main` - `git show --numstat --format=short HEAD` - `git diff-tree --no-commit-id --name-status -r HEAD` - `git rev-parse HEAD^` - `git branch --show-current` - `git show --format= -- src/phase_z2_retry.py` - `rg -n "frame_swap|MDX 03|MDX 04|MDX 05|padding|gap|line-height|font-size|font_step|glue" src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py` - `git show --format= --unified=0 HEAD -- src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py | Select-String -Pattern '^\+'` - `git show --format= --unified=0 HEAD -- src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py | Select-String -Pattern '^\+.*(frame_swap|MDX 03|MDX 04|MDX 05|padding|gap|line-height|font-size|font_step|glue)'` - `git show --format= --name-only HEAD` Files checked: - `D:\ad-hoc\kei\design_agent\src\phase_z2_retry.py` - `D:\ad-hoc\kei\design_agent\tests\phase_z2\test_phase_z2_retry_measured_bound.py` - `D:\ad-hoc\kei\design_agent\.orchestrator\drafts\63_stage_commit-push_codex_r1.md` Commit SHA: - `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1` Verified facts: - Local HEAD, `origin/main`, and `slide2/main` all point to `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1`. - Commit parent is `a06dd3d4b07767f752d6f40ed690e07164836e0c`. - Commit file list contains only `src/phase_z2_retry.py` and `tests/phase_z2/test_phase_z2_retry_measured_bound.py`. - Commit numstat is `22 1 src/phase_z2_retry.py` and `180 0 tests/phase_z2/test_phase_z2_retry_measured_bound.py`. - Added production code implements measured-empty donor capacity bound plus additive telemetry. - Added-line forbidden-pattern check returned no matches. - No full pytest was run in Stage 5, per current-stage instruction. FINAL_CONSENSUS: YES
Author
Owner

[Claude #17] Stage 6 final-close — IMP-34 R1 (donor capacity measured bound)

Goal vs Result (issue body scope mapping)

Issue scope item Result Anchor
visual_check overflow 감지 → 전체 zone 공란 측정 → overflow zone 확장 → 재배치 → revert on fail donor capacity now bounded by measured empty (min(static_slack, clientHeight−scrollHeight)); 측정/재배치/revert pipeline 모두 기존 경로 재사용 src/phase_z2_retry.py:122-157
호출 위치: src/phase_z2_retry.py (_attempt_zone_ratio_retry 확장) plan_zone_ratio_retry planner block 만 수정 (22 ins / 1 del); orchestrator _attempt_zone_ratio_retry 손대지 않음 src/phase_z2_retry.py:122-157, src/phase_z2_pipeline.py:2034-2129 (unchanged)
측정: 모든 zone 의 measured height vs allocated height → 공란 zone 식별 Step 14 overflow["zones"][i]clientHeight/scrollHeight 재사용 (schema 변경 없음) src/phase_z2_pipeline.py:2300-2389 (read, not modified)
확장: overflow zone 의 allocated height 늘림 (donor zone 의 공란 차감, budget=1) donor capacity 가 measured 로 bound → greedy aggregation 이 over-allocation 없이 정확히 분배; budget=1 semantics 보존 src/phase_z2_retry.py:165-203 (aggregation untouched)
Revert: rerender 시 overflow 지속 → 원래 ratio 복원 _attempt_zone_ratio_retry idempotent revert 보존 src/phase_z2_pipeline.py:2097-2129 (unchanged)
성공 시: 다음 overflow chain step (IMP-35 popup) 호출 안 함 chain gating unchanged (no edit)

Out of scope honored (issue body): responsive fit → IMP-36, popup escalation → IMP-35, AI 재구성 → IMP-33 — all three untouched.

Guardrail verification (issue body)

  • ★ 자동 frame_swap 금지 (V4 rank-1 보호) — donor planner only, frame layer 손대지 않음.
  • ★ 공통 여백 / padding / gap / line-height 강제 축소 금지 — feedback_phase_z_spacing_direction 준수 (rg over scoped diff = no padding/gap/line-height/font shrink).
  • 방향 = 늘리거나 다른 fit — donor capacity 는 bound (더 정확), 축소 아님.
  • no-hardcoding — Step 14 generic field 만 읽음, MDX 03/04/05 branch / frame-id literal 없음.
  • dropped 절대 룰 — 콘텐츠 삭제 경로 없음.
  • 실패 시 revert (idempotent) — static fallback 도 idempotent (measured absent/invalid/bool).

Remote verification (Stage 5 baseline reconfirmed)

  • HEAD = dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1 (feat(#63): IMP-34 R1 donor capacity measured bound (u1+u2))
  • git ls-remote origin maindceb10129f8c020b9dafb9e3854cd2bdd8dad2d1
  • git ls-remote slide2 maindceb10129f8c020b9dafb9e3854cd2bdd8dad2d1
  • Both remotes synced at SHA dceb101; no drift since Stage 5 close.
  • Commit scope: 2 files, 202 ins / 1 del (planner block + 5-axis regression module). git show --stat dceb101 confirms.

Stage chain (binding exit reports)

Stage Outcome Codex consensus
1 problem-review scope-lock fixed (donor capacity static-only root cause) YES
2 simulation-plan u1 planner + u2 regression units approved YES
3 code-edit (u1 + u2) planner + 5-axis test module landed YES
4 test-verify focused 5/5 PASS, Phase Z2 suite 162 PASS, pre-existing anchor drift isolation-proven IMP-34-independent YES
5 commit-push dceb101 on both remotes YES
  • Step 14 measured field coverage audit for non-overflow zones — donor zones currently rely on Step 14 emitting clientHeight/scrollHeight for every zone (not only overflowed ones). When coverage is partial, static fallback kicks in (regression-safe), but a separate audit is warranted to confirm coverage is complete in production traces. Separate issue recommended.
  • Donor selection ordering heuristic when multiple measured donors tie — current greedy aggregation order is unchanged; tie-breaking under measured-bound regime not optimized. Separate issue recommended.
  • Both deferrals match Stage 2 follow_up_candidates.

Labels

Current issue labels = []. Project does not appear to apply labels systematically across IMP issues; leaving as-is. (Issue body已 priority=높음 / wave=1 / R1 inline.)

Close decision

CLOSE — issue body scope fully delivered for R1 axis (donor capacity measured bound). All review-loop checkboxes from issue body now satisfied:

  • Codex 1차 review (Stage 1)
  • Claude 재검토 (Stage 2)
  • Codex 재검증 (Stage 2 ACCEPT)
  • scope-locked (Stage 1 exit + Stage 2 enumerated_axes)
  • ready-for-implementation (Stage 2 close)
  • implemented (Stage 3 close, commit dceb101)
  • verified (Stage 4 PASS + Stage 5 remote sync)

No remaining scope inside issue body; two follow-up candidates documented above for separate-issue tracking. Recommend orchestrator close issue #63.

=== EVIDENCE ===

Commands run for Stage 6 verification:

  • git log --oneline -5 → HEAD dceb101 feat(#63): IMP-34 R1 donor capacity measured bound (u1+u2)
  • git ls-remote origin maindceb10129f8c020b9dafb9e3854cd2bdd8dad2d1 refs/heads/main
  • git ls-remote slide2 maindceb10129f8c020b9dafb9e3854cd2bdd8dad2d1 refs/heads/main
  • git show --stat dceb101 → 2 files, 202 ins / 1 del; planner block src/phase_z2_retry.py 23 lines net, new test module 180 lines.
  • Gitea API GET /repos/Kyeongmin/C.E.L_Slide_test2/issues/63 → state=open, labels=[], title=IMP-34 zone resize + compact retry (overflow chain step 1).

Exit-report cross-reference: Stage 1 (Claude #1+Codex #2), Stage 2 (Claude #7+Codex #8), Stage 3 (Claude #11+Codex #12), Stage 4 (Claude #13+Codex #14), Stage 5 (Claude #15+Codex #16). All five prior stages closed with consensus YES.

source_comment_ids: Claude #17 (Stage 6 final-close).

commit_sha: dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1

[Claude #17] Stage 6 final-close — IMP-34 R1 (donor capacity measured bound) ## Goal vs Result (issue body scope mapping) | Issue scope item | Result | Anchor | |---|---|---| | visual_check overflow 감지 → 전체 zone 공란 측정 → overflow zone 확장 → 재배치 → revert on fail | ✅ donor capacity now bounded by measured empty (`min(static_slack, clientHeight−scrollHeight)`); 측정/재배치/revert pipeline 모두 기존 경로 재사용 | `src/phase_z2_retry.py:122-157` | | 호출 위치: `src/phase_z2_retry.py` (`_attempt_zone_ratio_retry` 확장) | ✅ `plan_zone_ratio_retry` planner block 만 수정 (22 ins / 1 del); orchestrator `_attempt_zone_ratio_retry` 손대지 않음 | `src/phase_z2_retry.py:122-157`, `src/phase_z2_pipeline.py:2034-2129` (unchanged) | | 측정: 모든 zone 의 measured height vs allocated height → 공란 zone 식별 | ✅ Step 14 `overflow["zones"][i]` 의 `clientHeight`/`scrollHeight` 재사용 (schema 변경 없음) | `src/phase_z2_pipeline.py:2300-2389` (read, not modified) | | 확장: overflow zone 의 allocated height 늘림 (donor zone 의 공란 차감, budget=1) | ✅ donor capacity 가 measured 로 bound → greedy aggregation 이 over-allocation 없이 정확히 분배; budget=1 semantics 보존 | `src/phase_z2_retry.py:165-203` (aggregation untouched) | | Revert: rerender 시 overflow 지속 → 원래 ratio 복원 | ✅ `_attempt_zone_ratio_retry` idempotent revert 보존 | `src/phase_z2_pipeline.py:2097-2129` (unchanged) | | 성공 시: 다음 overflow chain step (IMP-35 popup) 호출 안 함 | ✅ chain gating unchanged | (no edit) | **Out of scope honored** (issue body): responsive fit → IMP-36, popup escalation → IMP-35, AI 재구성 → IMP-33 — all three untouched. ## Guardrail verification (issue body) - ★ 자동 frame_swap 금지 (V4 rank-1 보호) — ✅ donor planner only, frame layer 손대지 않음. - ★ 공통 여백 / padding / gap / line-height 강제 축소 금지 — ✅ `feedback_phase_z_spacing_direction` 준수 (rg over scoped diff = no padding/gap/line-height/font shrink). - 방향 = 늘리거나 다른 fit — ✅ donor capacity 는 **bound** (더 정확), 축소 아님. - no-hardcoding — ✅ Step 14 generic field 만 읽음, MDX 03/04/05 branch / frame-id literal 없음. - dropped 절대 룰 — ✅ 콘텐츠 삭제 경로 없음. - 실패 시 revert (idempotent) — ✅ static fallback 도 idempotent (measured absent/invalid/bool). ## Remote verification (Stage 5 baseline reconfirmed) - HEAD = `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1` (`feat(#63): IMP-34 R1 donor capacity measured bound (u1+u2)`) - `git ls-remote origin main` → `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1` - `git ls-remote slide2 main` → `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1` - Both remotes synced at SHA `dceb101`; no drift since Stage 5 close. - Commit scope: 2 files, 202 ins / 1 del (planner block + 5-axis regression module). `git show --stat dceb101` confirms. ## Stage chain (binding exit reports) | Stage | Outcome | Codex consensus | |---|---|---| | 1 problem-review | scope-lock fixed (donor capacity static-only root cause) | YES | | 2 simulation-plan | u1 planner + u2 regression units approved | YES | | 3 code-edit (u1 + u2) | planner + 5-axis test module landed | YES | | 4 test-verify | focused 5/5 PASS, Phase Z2 suite 162 PASS, pre-existing anchor drift isolation-proven IMP-34-independent | YES | | 5 commit-push | `dceb101` on both remotes | YES | ## Follow-up links (deferred per Stage 2 plan) - **Step 14 measured field coverage audit for non-overflow zones** — donor zones currently rely on Step 14 emitting `clientHeight`/`scrollHeight` for every zone (not only overflowed ones). When coverage is partial, static fallback kicks in (regression-safe), but a separate audit is warranted to confirm coverage is complete in production traces. **Separate issue recommended.** - **Donor selection ordering heuristic when multiple measured donors tie** — current greedy aggregation order is unchanged; tie-breaking under measured-bound regime not optimized. **Separate issue recommended.** - Both deferrals match Stage 2 `follow_up_candidates`. ## Labels Current issue labels = `[]`. Project does not appear to apply labels systematically across IMP issues; leaving as-is. (Issue body已 priority=높음 / wave=1 / R1 inline.) ## Close decision **CLOSE** — issue body scope fully delivered for R1 axis (donor capacity measured bound). All review-loop checkboxes from issue body now satisfied: - [x] Codex 1차 review (Stage 1) - [x] Claude 재검토 (Stage 2) - [x] Codex 재검증 (Stage 2 ACCEPT) - [x] scope-locked (Stage 1 exit + Stage 2 enumerated_axes) - [x] ready-for-implementation (Stage 2 close) - [x] implemented (Stage 3 close, commit `dceb101`) - [x] verified (Stage 4 PASS + Stage 5 remote sync) No remaining scope inside issue body; two follow-up candidates documented above for separate-issue tracking. Recommend orchestrator close issue #63. === EVIDENCE === Commands run for Stage 6 verification: - `git log --oneline -5` → HEAD `dceb101 feat(#63): IMP-34 R1 donor capacity measured bound (u1+u2)` - `git ls-remote origin main` → `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1 refs/heads/main` - `git ls-remote slide2 main` → `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1 refs/heads/main` - `git show --stat dceb101` → 2 files, 202 ins / 1 del; planner block `src/phase_z2_retry.py` 23 lines net, new test module 180 lines. - Gitea API `GET /repos/Kyeongmin/C.E.L_Slide_test2/issues/63` → state=open, labels=[], title=`IMP-34 zone resize + compact retry (overflow chain step 1)`. Exit-report cross-reference: Stage 1 (Claude #1+Codex #2), Stage 2 (Claude #7+Codex #8), Stage 3 (Claude #11+Codex #12), Stage 4 (Claude #13+Codex #14), Stage 5 (Claude #15+Codex #16). All five prior stages closed with consensus YES. source_comment_ids: Claude #17 (Stage 6 final-close). commit_sha: `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1`
Author
Owner

[Codex #18] Stage 6 final-close review - IMP-34 R1 donor capacity measured bound

Final independent verification result: PASS.

Scope-qualified findings:

  • HEAD is dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1, parent a06dd3d4b07767f752d6f40ed690e07164836e0c, commit message feat(#63): IMP-34 R1 donor capacity measured bound (u1+u2).
  • Commit scope is exactly 2 files: src/phase_z2_retry.py modified and tests/phase_z2/test_phase_z2_retry_measured_bound.py added.
  • origin/main and slide2/main both resolve to dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1.
  • Planner change is confined to src/phase_z2_retry.py:122-157: clientHeight and scrollHeight are read from Step 14 zone status, bool values are rejected, measured_empty_px = max(0, int(clientHeight) - int(scrollHeight)), and donor slack is bounded by min(static_slack, measured_empty_px) when measured fields are numeric.
  • Donor exclusion for overflowed / clipped_inner remains at src/phase_z2_retry.py:107-112, before the measured-bound logic.
  • The slack <= 0 donor rejection gate remains at src/phase_z2_retry.py:147-148.
  • Additive telemetry fields measured_empty_px and slack_bound_source are emitted at src/phase_z2_retry.py:156-157.
  • Regression test module covers all planned axes: static fallback, measured empty less than static slack, measured empty greater than or equal to static slack, measured empty zero donor exclusion, and donor filter plus bool guard.
  • Focused regression command passed: 5 passed in 0.03s.
  • Scoped diff guardrail check found no frame_swap, MDX 03/04/05 branch, frame-id literal, spacing/padding/gap/line-height/font shrink, or content-drop path in the committed IMP-34 diff.
  • Step 14 schema and _attempt_zone_ratio_retry orchestration are not modified by this commit because the commit only touches src/phase_z2_retry.py and the new focused test file.
  • Current working tree contains unrelated modified and untracked files, but they are outside the committed IMP-34 scope and were not part of the Stage 5 commit.

Close decision:
IMP-34 R1 satisfies the issue scope and Stage 1-5 binding contracts. No rewind target is required.

=== EVIDENCE ===
Commands run:

  • git rev-parse HEAD
  • git show --stat --oneline --name-only --format=fuller HEAD
  • git diff-tree --no-commit-id --name-status -r HEAD
  • git remote -v
  • git ls-remote origin main
  • git ls-remote slide2 main
  • git show --numstat --format=short HEAD
  • git diff HEAD^ HEAD -- src/phase_z2_retry.py
  • Get-Content -LiteralPath tests/phase_z2/test_phase_z2_retry_measured_bound.py
  • rg -n "frame_swap|MDX 03|MDX 04|MDX 05|padding|gap|line-height|font-size|font_size|drop|delete" src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py
  • rg -n "clientHeight|scrollHeight|measured_empty_px|slack_bound_source|static_fallback|measured_bound" src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py
  • git status --short
  • pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -q
  • git diff HEAD^ HEAD -- src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py | rg -n "frame_swap|MDX 03|MDX 04|MDX 05|padding|gap|line-height|font-size|font_size|drop|delete"
  • git diff HEAD^ HEAD -- src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py | rg -n "clientHeight|scrollHeight|measured_empty_px|slack_bound_source|static_fallback|measured_bound"
  • git show --format=%P -s HEAD
  • git show --stat --oneline HEAD
  • line-number reads for src/phase_z2_retry.py:107-157 and test axis anchors

Files checked:

  • src/phase_z2_retry.py
  • tests/phase_z2/test_phase_z2_retry_measured_bound.py
  • committed diff from a06dd3d4b07767f752d6f40ed690e07164836e0c to dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1

Test results:

  • pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -q: 5 passed in 0.03s

Commit SHA:

  • dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1

Verified facts:

  • origin/main is dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1.
  • slide2/main is dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1.
  • Commit numstat is 22 1 src/phase_z2_retry.py and 180 0 tests/phase_z2/test_phase_z2_retry_measured_bound.py.
  • Commit file status is M src/phase_z2_retry.py and A tests/phase_z2/test_phase_z2_retry_measured_bound.py.
  • src/phase_z2_retry.py:140-142 implements the measured bound and source telemetry for numeric measured fields.
  • src/phase_z2_retry.py:143-146 preserves static fallback when measured fields are absent or invalid.
  • tests/phase_z2/test_phase_z2_retry_measured_bound.py:60, :83, :108, :131, and :153 define the five planned regression axes.
  • No full pytest was run in Stage 6, per current-stage instruction.

FINAL_CONSENSUS: YES

[Codex #18] Stage 6 final-close review - IMP-34 R1 donor capacity measured bound Final independent verification result: PASS. Scope-qualified findings: - HEAD is `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1`, parent `a06dd3d4b07767f752d6f40ed690e07164836e0c`, commit message `feat(#63): IMP-34 R1 donor capacity measured bound (u1+u2)`. - Commit scope is exactly 2 files: `src/phase_z2_retry.py` modified and `tests/phase_z2/test_phase_z2_retry_measured_bound.py` added. - `origin/main` and `slide2/main` both resolve to `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1`. - Planner change is confined to `src/phase_z2_retry.py:122-157`: `clientHeight` and `scrollHeight` are read from Step 14 zone status, bool values are rejected, `measured_empty_px = max(0, int(clientHeight) - int(scrollHeight))`, and donor slack is bounded by `min(static_slack, measured_empty_px)` when measured fields are numeric. - Donor exclusion for `overflowed` / `clipped_inner` remains at `src/phase_z2_retry.py:107-112`, before the measured-bound logic. - The `slack <= 0` donor rejection gate remains at `src/phase_z2_retry.py:147-148`. - Additive telemetry fields `measured_empty_px` and `slack_bound_source` are emitted at `src/phase_z2_retry.py:156-157`. - Regression test module covers all planned axes: static fallback, measured empty less than static slack, measured empty greater than or equal to static slack, measured empty zero donor exclusion, and donor filter plus bool guard. - Focused regression command passed: `5 passed in 0.03s`. - Scoped diff guardrail check found no `frame_swap`, MDX 03/04/05 branch, frame-id literal, spacing/padding/gap/line-height/font shrink, or content-drop path in the committed IMP-34 diff. - Step 14 schema and `_attempt_zone_ratio_retry` orchestration are not modified by this commit because the commit only touches `src/phase_z2_retry.py` and the new focused test file. - Current working tree contains unrelated modified and untracked files, but they are outside the committed IMP-34 scope and were not part of the Stage 5 commit. Close decision: IMP-34 R1 satisfies the issue scope and Stage 1-5 binding contracts. No rewind target is required. === EVIDENCE === Commands run: - `git rev-parse HEAD` - `git show --stat --oneline --name-only --format=fuller HEAD` - `git diff-tree --no-commit-id --name-status -r HEAD` - `git remote -v` - `git ls-remote origin main` - `git ls-remote slide2 main` - `git show --numstat --format=short HEAD` - `git diff HEAD^ HEAD -- src/phase_z2_retry.py` - `Get-Content -LiteralPath tests/phase_z2/test_phase_z2_retry_measured_bound.py` - `rg -n "frame_swap|MDX 03|MDX 04|MDX 05|padding|gap|line-height|font-size|font_size|drop|delete" src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py` - `rg -n "clientHeight|scrollHeight|measured_empty_px|slack_bound_source|static_fallback|measured_bound" src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py` - `git status --short` - `pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -q` - `git diff HEAD^ HEAD -- src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py | rg -n "frame_swap|MDX 03|MDX 04|MDX 05|padding|gap|line-height|font-size|font_size|drop|delete"` - `git diff HEAD^ HEAD -- src/phase_z2_retry.py tests/phase_z2/test_phase_z2_retry_measured_bound.py | rg -n "clientHeight|scrollHeight|measured_empty_px|slack_bound_source|static_fallback|measured_bound"` - `git show --format=%P -s HEAD` - `git show --stat --oneline HEAD` - line-number reads for `src/phase_z2_retry.py:107-157` and test axis anchors Files checked: - `src/phase_z2_retry.py` - `tests/phase_z2/test_phase_z2_retry_measured_bound.py` - committed diff from `a06dd3d4b07767f752d6f40ed690e07164836e0c` to `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1` Test results: - `pytest tests/phase_z2/test_phase_z2_retry_measured_bound.py -q`: `5 passed in 0.03s` Commit SHA: - `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1` Verified facts: - `origin/main` is `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1`. - `slide2/main` is `dceb10129f8c020b9dafb9e3854cd2bdd8dad2d1`. - Commit numstat is `22 1 src/phase_z2_retry.py` and `180 0 tests/phase_z2/test_phase_z2_retry_measured_bound.py`. - Commit file status is `M src/phase_z2_retry.py` and `A tests/phase_z2/test_phase_z2_retry_measured_bound.py`. - `src/phase_z2_retry.py:140-142` implements the measured bound and source telemetry for numeric measured fields. - `src/phase_z2_retry.py:143-146` preserves static fallback when measured fields are absent or invalid. - `tests/phase_z2/test_phase_z2_retry_measured_bound.py:60`, `:83`, `:108`, `:131`, and `:153` define the five planned regression axes. - No full pytest was run in Stage 6, per current-stage instruction. FINAL_CONSENSUS: YES
Kyeongmin added this to the B-2 §2 separate axis registry (9) milestone 2026-05-22 14:01:25 +09:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Kyeongmin/C.E.L_Slide_test2#63