judgments_full32 = V4 결과 32 frame 평가. 현재 max_rank=3 이라 32 중 3 만 검사 = 29 frame 무시.
Fact 4 — catalog 32 frame 모두 등록됨
templates/phase_z2/catalog/frame_contracts.yaml = 32 top-level keys (grep 확인). 즉 catalog 자체는 32 frame 다 등록.
Fact 5 — v4_fallback_policy field 없음 (신규 schema 도입 필요)
grep v4_fallback_policy|fallback_policy 결과 = src/ + templates/ 0 hits.
Fact 6 — judgments_full32 가 V4 결과 schema 명
sec.get("judgments_full32") — V4 결과 yaml (tests/matching/v4_full32_result.yaml) 에 32 frame 평가 포함. fallback 확장 가능 데이터 이미 존재.
Root cause
층
상태
V4 결과 데이터
32 frame 평가 모두 있음 ✅
catalog
32 frame 등록 ✅
lookup_v4_match_with_fallback 의 max_rank
3 hardcoded❌ — rank 4~32 무시
vite env PHASE_Z_MAX_RANK=32
backend 가 무시❌
→ 결과: mdx05-2 같이 V4 rank 1~9 가 catalog 미등록 frame + rank 10 부터 등록 frame 인 case → rank 3 까지만 검사 → 모두 skip → chain_exhausted → unit 생성 X / placeholder zone.
해결 = max_rank 결정 로직을 동적 으로 (옵션 C — usable_count 기반).
IMP-38 의 frontend / backend 분리 (명확화)
영역
영향
frontend FramePanel 표시
IMP-47A 의 TOP_N_FRAMES = 6 그대로. 변경 X (영향 0)
backend max_rank 결정
rank 3 → 동적 (rank 3 또는 32)
→ IMP-38 = backend 의 검사 범위 확대 axis. frontend 표시와 무관.
Scope-lock 제안 (3 units, sequential)
U1 — catalog yaml v4_fallback_policy schema 추가
영역
작업
templates/phase_z2/catalog/frame_contracts.yaml
신규 top-level key v4_fallback_policy: (32 frame entry 와 별도 sibling)
schema
policy_type: dynamic_usable_count_based, usable_threshold: N, default_max_rank: 3, extended_max_rank: 32
로더
src/phase_z2_pipeline.py 의 catalog loader 가 새 key 읽어 module-level constant 로
U2 — lookup_v4_match_with_fallback 동적 max_rank 로직 (옵션 C)
영역
작업
src/phase_z2_pipeline.py:779-
함수 signature 에 usable_threshold: int = N 추가 (또는 yaml 에서 자동 읽기)
logic
(1) judgments_full32 의 rank 1~N 중 usable count 계산 (=use_as_is/light_edit 라벨 + catalog 등록 frame 수)
Root-cause verdict: Claude's diagnosis is substantially correct. The backend currently ignores the frontend PHASE_Z_MAX_RANK=32 env, and the effective V4 fallback search is still capped at rank 3 in the production selector path.
Verified facts:
PHASE_Z_MAX_RANK appears in Front/vite.config.ts, Front_test/Front/vite.config.ts, and Front_test_v515/Front/vite.config.ts, but src/phase_z2_pipeline.py has no read of that env. Backend max-rank behavior is therefore not controlled by the Vite env.
lookup_v4_match_with_fallback(...) still has max_rank: int = 3, and slices judgments_full32 via [:max_rank].
Production fallback call sites still either rely on the default rank 3 or explicitly pass max_rank=3 at the main composition/retry/override lookup points.
frame_contracts.yaml currently contains 32 contracted frame entries, and no v4_fallback_policy / fallback_policy field exists today.
tests/test_phase_z2_v4_fallback.py currently passes: 13 passed.
Important scope corrections before Stage 2:
Do not add v4_fallback_policy as a plain top-level sibling in frame_contracts.yaml unless the loader and all catalog consumers are updated to filter metadata.
Current code assumes load_frame_contracts() is a template-id -> frame-contract dict in several places:
get_contract(template_id) directly indexes the raw YAML dict.
tests/test_catalog_invariant.py asserts every dict entry has template_id and frame_id.
A top-level dict policy would be treated as a fake frame unless filtering is added. Safer Stage 2 options:
Prefer a separate templates/phase_z2/catalog/v4_fallback_policy.yaml; or
introduce a loader API such as load_v4_fallback_policy() plus load_frame_contracts() filtering out a reserved metadata key.
Front_test/Front and Front_test_v515/Front appear to be untracked working-tree clones, while Front/vite.config.ts is tracked. Stage 2 should not require edits to untracked clone dirs unless the operator explicitly wants those trees managed. The source-of-truth cleanup should target the tracked Front/vite.config.ts first.
The usable-count policy should use the same predicates as the actual selection path as much as possible. Counting only label/status + catalog registration may still miss cases where top-3 candidates exist but fail capacity. Recommended effective usability predicate:
status in MVP1_ALLOWED_STATUSES
get_contract(template_id) is not None
if raw_content is supplied, capacity check is acceptable (ok, no_contract, unknown_source_shape) using the existing compute_capacity_fit semantics.
Trace output should distinguish requested/default rank from effective rank. Do not just overwrite trace["max_rank"]; add fields such as policy_applied, usable_count, default_max_rank, extended_max_rank, and effective_max_rank so Stage 4 can prove when/why rank 32 was used.
Frontend FramePanel TOP_N_FRAMES display cap is correctly out of scope. IMP-38 is backend candidate search breadth, not UI display breadth.
Suggested Stage 2 lock shape:
U1: Add a policy loader without polluting the frame-contract dict surface. Prefer separate YAML or filtered metadata loader.
U2: Implement dynamic effective max-rank in lookup_v4_match_with_fallback, including trace fields and capacity-aware usable count.
U3: Remove hardcoded max_rank=3 call-site overrides where dynamic policy should apply, clean the tracked Vite env, and add focused tests for rank-3-preserved and rank-extended cases.
Residual risk:
Current working tree contains unrelated IMP-47B changes, and full pytest -q tests may be noisy due to existing anchor drift. Stage 2/3 verification should be scope-qualified and must not bundle IMP-17 anchor re-pin into IMP-38.
FINAL_CONSENSUS: YES, with the Stage 2 corrections above carried forward.
[Codex #1] Stage 1 problem-review verification - IMP-38 V4 max_rank policy
Root-cause verdict: Claude's diagnosis is substantially correct. The backend currently ignores the frontend `PHASE_Z_MAX_RANK=32` env, and the effective V4 fallback search is still capped at rank 3 in the production selector path.
Verified facts:
- `PHASE_Z_MAX_RANK` appears in `Front/vite.config.ts`, `Front_test/Front/vite.config.ts`, and `Front_test_v515/Front/vite.config.ts`, but `src/phase_z2_pipeline.py` has no read of that env. Backend max-rank behavior is therefore not controlled by the Vite env.
- `lookup_v4_match_with_fallback(...)` still has `max_rank: int = 3`, and slices `judgments_full32` via `[:max_rank]`.
- Production fallback call sites still either rely on the default rank 3 or explicitly pass `max_rank=3` at the main composition/retry/override lookup points.
- `frame_contracts.yaml` currently contains 32 contracted frame entries, and no `v4_fallback_policy` / `fallback_policy` field exists today.
- `tests/test_phase_z2_v4_fallback.py` currently passes: `13 passed`.
Important scope corrections before Stage 2:
1. Do not add `v4_fallback_policy` as a plain top-level sibling in `frame_contracts.yaml` unless the loader and all catalog consumers are updated to filter metadata.
Current code assumes `load_frame_contracts()` is a template-id -> frame-contract dict in several places:
- `get_contract(template_id)` directly indexes the raw YAML dict.
- `_known_contract_ids()` returns `list(load_frame_contracts().keys())`.
- Step 10 debug/artifacts use `len(load_frame_contracts())` and `sorted(load_frame_contracts().keys())`.
- B4 placement passes `frame_contracts=list(load_frame_contracts().values())`.
- `tests/test_catalog_invariant.py` asserts every dict entry has `template_id` and `frame_id`.
A top-level dict policy would be treated as a fake frame unless filtering is added. Safer Stage 2 options:
- Prefer a separate `templates/phase_z2/catalog/v4_fallback_policy.yaml`; or
- introduce a loader API such as `load_v4_fallback_policy()` plus `load_frame_contracts()` filtering out a reserved metadata key.
2. `Front_test/Front` and `Front_test_v515/Front` appear to be untracked working-tree clones, while `Front/vite.config.ts` is tracked. Stage 2 should not require edits to untracked clone dirs unless the operator explicitly wants those trees managed. The source-of-truth cleanup should target the tracked `Front/vite.config.ts` first.
3. The usable-count policy should use the same predicates as the actual selection path as much as possible. Counting only label/status + catalog registration may still miss cases where top-3 candidates exist but fail capacity. Recommended effective usability predicate:
- `status in MVP1_ALLOWED_STATUSES`
- `get_contract(template_id) is not None`
- if `raw_content` is supplied, capacity check is acceptable (`ok`, `no_contract`, `unknown_source_shape`) using the existing `compute_capacity_fit` semantics.
4. Trace output should distinguish requested/default rank from effective rank. Do not just overwrite `trace["max_rank"]`; add fields such as `policy_applied`, `usable_count`, `default_max_rank`, `extended_max_rank`, and `effective_max_rank` so Stage 4 can prove when/why rank 32 was used.
5. Frontend FramePanel `TOP_N_FRAMES` display cap is correctly out of scope. IMP-38 is backend candidate search breadth, not UI display breadth.
Suggested Stage 2 lock shape:
- U1: Add a policy loader without polluting the frame-contract dict surface. Prefer separate YAML or filtered metadata loader.
- U2: Implement dynamic effective max-rank in `lookup_v4_match_with_fallback`, including trace fields and capacity-aware usable count.
- U3: Remove hardcoded `max_rank=3` call-site overrides where dynamic policy should apply, clean the tracked Vite env, and add focused tests for rank-3-preserved and rank-extended cases.
Residual risk:
- Current working tree contains unrelated IMP-47B changes, and full `pytest -q tests` may be noisy due to existing anchor drift. Stage 2/3 verification should be scope-qualified and must not bundle IMP-17 anchor re-pin into IMP-38.
=== EVIDENCE ===
Commands run:
- `rg -n "PHASE_Z_MAX_RANK|lookup_v4_match_with_fallback|max_rank=3|max_rank: int|judgments_full32|MVP1_ALLOWED_STATUSES|APPLICATION_MODE_BY_V4_LABEL" src/phase_z2_pipeline.py Front/vite.config.ts Front_test/Front/vite.config.ts Front_test_v515/Front/vite.config.ts tests -S`
- `rg -c "^ template_id:" templates/phase_z2/catalog/frame_contracts.yaml`
- `rg -n "^v4_fallback_policy:|^ template_id:" templates/phase_z2/catalog/frame_contracts.yaml`
- `Get-Content src/phase_z2_pipeline.py` around `lookup_v4_match_with_fallback`, composition lookup sites, and B4 `load_frame_contracts().values()` usage
- `Get-Content src/phase_z2_mapper.py` for `load_frame_contracts()` / `get_contract()` behavior
- `Get-Content tests/test_catalog_invariant.py` and `tests/test_family_contract_baseline.py`
- `git ls-files Front/vite.config.ts Front_test/Front/vite.config.ts Front_test_v515/Front/vite.config.ts`
- `python -m pytest -q tests/test_phase_z2_v4_fallback.py`
Test result:
- `tests/test_phase_z2_v4_fallback.py`: `13 passed`.
FINAL_CONSENSUS: YES, with the Stage 2 corrections above carried forward.
Verdict: Claude #2 correctly incorporated the Stage 1 correction points and the Stage 2 plan is acceptable, with one clarification required before/while entering Stage 3.
Validated corrections carried forward:
Policy storage was moved out of frame_contracts.yaml into a separate templates/phase_z2/catalog/v4_fallback_policy.yaml, avoiding catalog-loader pollution. This addresses the risk that load_frame_contracts().keys() / .values() / catalog invariant tests would treat a policy dict as a fake frame.
Untracked Front_test/Front and Front_test_v515/Front clone dirs are excluded from the implementation scope. Only tracked Front/vite.config.ts should be cleaned for PHASE_Z_MAX_RANK env removal.
The usable-count predicate is no longer label-only. It is aligned with the real selector path: status in MVP1_ALLOWED_STATUSES, catalog contract exists, and capacity is acceptable when raw_content is available.
Trace schema is split so Stage 4 can prove why a search stayed at rank 3 or expanded to the extended range: requested/default/extended/effective max rank, usable count, and policy applied.
Frontend TOP_N_FRAMES remains out of scope. IMP-38 is backend candidate-search breadth, not candidate display breadth.
One required clarification:
Claude #2 still phrases Q2 as catalog count dynamic (len(judgments_full32)). These are related today but not identical concepts:
len(judgments_full32) = how many V4 judgments exist for the section.
catalog_contract_count = how many frame contracts are registered.
configured extended_max_rank = policy ceiling from v4_fallback_policy.yaml.
If catalog count is also used, name it separately as catalog_contract_count and do not conflate it with V4 judgment length. This prevents future drift where catalog count and V4 judgment count differ.
Implementation-unit review:
u1 is correct: separate policy YAML + loader + loader tests.
u2 is correct: dynamic effective max-rank inside lookup_v4_match_with_fallback, with trace fields and capacity-aware usable predicate.
u3 is correct: remove hardcoded max_rank=3 call-site overrides where dynamic policy should apply, clean tracked Vite env, and add regression coverage.
Verification expectations for Stage 3/4:
Rank-1 normal case should preserve effective max-rank 3.
A case where top ranks are unusable/unregistered should expand to the configured extended ceiling and select a later registered frame.
Trace must show policy_applied, usable_count, and effective_max_rank.
tests/test_phase_z2_v4_fallback.py should remain green.
Any existing IMP-17/IMP-47B anchor drift should be reported as baseline noise, not fixed inside IMP-38.
FINAL_CONSENSUS: YES, with the extended_max_rank semantics clarified as above.
[Codex #2] Stage 2 simulation-plan review - IMP-38 V4 max_rank policy
Verdict: Claude #2 correctly incorporated the Stage 1 correction points and the Stage 2 plan is acceptable, with one clarification required before/while entering Stage 3.
Validated corrections carried forward:
- Policy storage was moved out of `frame_contracts.yaml` into a separate `templates/phase_z2/catalog/v4_fallback_policy.yaml`, avoiding catalog-loader pollution. This addresses the risk that `load_frame_contracts().keys()` / `.values()` / catalog invariant tests would treat a policy dict as a fake frame.
- Untracked `Front_test/Front` and `Front_test_v515/Front` clone dirs are excluded from the implementation scope. Only tracked `Front/vite.config.ts` should be cleaned for `PHASE_Z_MAX_RANK` env removal.
- The usable-count predicate is no longer label-only. It is aligned with the real selector path: `status in MVP1_ALLOWED_STATUSES`, catalog contract exists, and capacity is acceptable when `raw_content` is available.
- Trace schema is split so Stage 4 can prove why a search stayed at rank 3 or expanded to the extended range: requested/default/extended/effective max rank, usable count, and policy applied.
- Frontend `TOP_N_FRAMES` remains out of scope. IMP-38 is backend candidate-search breadth, not candidate display breadth.
One required clarification:
Claude #2 still phrases Q2 as `catalog count dynamic (len(judgments_full32))`. These are related today but not identical concepts:
- `len(judgments_full32)` = how many V4 judgments exist for the section.
- `catalog_contract_count` = how many frame contracts are registered.
- `configured extended_max_rank` = policy ceiling from `v4_fallback_policy.yaml`.
Recommended Stage 3 lock:
```python
effective_extended_ceiling = min(
configured_extended_max_rank,
len(judgments_full32),
)
```
If catalog count is also used, name it separately as `catalog_contract_count` and do not conflate it with V4 judgment length. This prevents future drift where catalog count and V4 judgment count differ.
Implementation-unit review:
- u1 is correct: separate policy YAML + loader + loader tests.
- u2 is correct: dynamic effective max-rank inside `lookup_v4_match_with_fallback`, with trace fields and capacity-aware usable predicate.
- u3 is correct: remove hardcoded `max_rank=3` call-site overrides where dynamic policy should apply, clean tracked Vite env, and add regression coverage.
Verification expectations for Stage 3/4:
- Rank-1 normal case should preserve effective max-rank 3.
- A case where top ranks are unusable/unregistered should expand to the configured extended ceiling and select a later registered frame.
- Trace must show `policy_applied`, `usable_count`, and `effective_max_rank`.
- `tests/test_phase_z2_v4_fallback.py` should remain green.
- Any existing IMP-17/IMP-47B anchor drift should be reported as baseline noise, not fixed inside IMP-38.
FINAL_CONSENSUS: YES, with the `extended_max_rank` semantics clarified as above.
Verified Claude #3 against the prior Codex #2 clarification and local code. Result: the Stage 2 plan is now acceptable and ready for Stage 3.
What was verified:
Claude #3 correctly separates the three concepts that were previously conflated:
configured_extended_max_rank: policy ceiling from v4_fallback_policy.yaml.
len(judgments_full32): number of V4 judgments available for the section.
catalog_contract_count: number of registered frame contracts, useful for trace/reporting but not a safe substitute for V4 judgment count.
The final Stage 2 lock now uses effective_extended_ceiling = min(configured_extended_max_rank, len(judgments_full32)), which is the right behavior. It avoids searching beyond available V4 evidence and avoids silently ignoring the configured policy ceiling.
catalog_contract_count is now trace/reporting-only, not an effective slicing ceiling. This is correct because a catalog entry without a V4 judgment has no rank/confidence basis inside this selector.
The top-level frame_contracts.yaml pollution risk remains resolved by the separate v4_fallback_policy.yaml plan and load_v4_fallback_policy() loader.
The tracked/untracked frontend scope is correct: only tracked Front/vite.config.ts should be edited; untracked Front_test and Front_test_v515 clone dirs stay out of scope.
The 3-tier usable predicate remains aligned with the actual selector path: allowed Phase Z status, catalog contract exists, and optional capacity check when raw_content is present.
Trace expansion to include configured_extended_max_rank, judgments_count, effective_extended_ceiling, effective_max_rank, usable_count, and policy_applied is appropriate for Stage 4 verification.
Existing code still shows lookup_v4_match_with_fallback(..., max_rank: int = 3) and judgments_full32[:max_rank], confirming the implementation target remains real.
load_frame_contracts() still returns the raw catalog dict, confirming why policy must stay out of frame_contracts.yaml unless the loader is explicitly changed.
Stage 3 expectations:
u1 should add the separate policy YAML and loader without changing the shape of load_frame_contracts().
u2 should implement dynamic effective max-rank and the trace fields without disturbing IMP-05 fallback/dedup semantics.
u3 should remove the hardcoded max_rank=3 call-site overrides where dynamic policy is intended, clean tracked Front/vite.config.ts, and add mdx03/mdx05-style regression coverage.
Residual note:
Full-suite pytest may still be noisy because of unrelated IMP-17 / IMP-47B anchor drift. That must stay out of IMP-38 unless a new concrete IMP-38 regression is shown.
FINAL_CONSENSUS: YES - proceed to Stage 3.
[Codex #3] Stage 2 simulation-plan review - IMP-38 post-clarification
Verified Claude #3 against the prior Codex #2 clarification and local code. Result: the Stage 2 plan is now acceptable and ready for Stage 3.
What was verified:
- Claude #3 correctly separates the three concepts that were previously conflated:
- `configured_extended_max_rank`: policy ceiling from `v4_fallback_policy.yaml`.
- `len(judgments_full32)`: number of V4 judgments available for the section.
- `catalog_contract_count`: number of registered frame contracts, useful for trace/reporting but not a safe substitute for V4 judgment count.
- The final Stage 2 lock now uses `effective_extended_ceiling = min(configured_extended_max_rank, len(judgments_full32))`, which is the right behavior. It avoids searching beyond available V4 evidence and avoids silently ignoring the configured policy ceiling.
- `catalog_contract_count` is now trace/reporting-only, not an effective slicing ceiling. This is correct because a catalog entry without a V4 judgment has no rank/confidence basis inside this selector.
- The top-level `frame_contracts.yaml` pollution risk remains resolved by the separate `v4_fallback_policy.yaml` plan and `load_v4_fallback_policy()` loader.
- The tracked/untracked frontend scope is correct: only tracked `Front/vite.config.ts` should be edited; untracked `Front_test` and `Front_test_v515` clone dirs stay out of scope.
- The 3-tier usable predicate remains aligned with the actual selector path: allowed Phase Z status, catalog contract exists, and optional capacity check when `raw_content` is present.
- Trace expansion to include `configured_extended_max_rank`, `judgments_count`, `effective_extended_ceiling`, `effective_max_rank`, `usable_count`, and `policy_applied` is appropriate for Stage 4 verification.
Local verification:
- `pytest -q tests/test_phase_z2_v4_fallback.py tests/test_catalog_invariant.py` -> `15 passed`.
- Existing code still shows `lookup_v4_match_with_fallback(..., max_rank: int = 3)` and `judgments_full32[:max_rank]`, confirming the implementation target remains real.
- `load_frame_contracts()` still returns the raw catalog dict, confirming why policy must stay out of `frame_contracts.yaml` unless the loader is explicitly changed.
Stage 3 expectations:
- u1 should add the separate policy YAML and loader without changing the shape of `load_frame_contracts()`.
- u2 should implement dynamic effective max-rank and the trace fields without disturbing IMP-05 fallback/dedup semantics.
- u3 should remove the hardcoded `max_rank=3` call-site overrides where dynamic policy is intended, clean tracked `Front/vite.config.ts`, and add mdx03/mdx05-style regression coverage.
Residual note:
Full-suite pytest may still be noisy because of unrelated IMP-17 / IMP-47B anchor drift. That must stay out of IMP-38 unless a new concrete IMP-38 regression is shown.
FINAL_CONSENSUS: YES - proceed to Stage 3.
Verified Claude #4's final Stage 2 lock against local code and focused tests. Result: the Stage 2 lock is coherent and ready for Stage 3.
Verification summary:
The implementation target still exists in the current working tree: lookup_v4_match_with_fallback(..., max_rank: int = 3) still slices judgments_full32 with [:max_rank].
The catalog loader still returns the raw frame_contracts.yaml dict (load_frame_contracts()), and existing consumers still use .keys(), .values(), and len(load_frame_contracts()). This re-confirms that policy must not be inserted as a raw top-level dict in frame_contracts.yaml.
Front/vite.config.ts still contains PHASE_Z_MAX_RANK=32, and src/phase_z2_pipeline.py still does not read that env. The env cleanup target remains real.
U1 is correct: separate templates/phase_z2/catalog/v4_fallback_policy.yaml + load_v4_fallback_policy() loader. It must not change the return shape of load_frame_contracts().
U2 is correct: dynamic effective max-rank belongs inside lookup_v4_match_with_fallback, with the final ceiling locked as min(configured_extended_max_rank, len(judgments_full32)).
U2 trace expansion is useful and should be retained. The 8-field trace is a safe superset of the prior Codex requirement because it separates caller/default/policy decisions.
U3 is correct: remove hardcoded max_rank=3 call-site overrides where policy should apply, clean only the tracked Front/vite.config.ts, and add mdx03/mdx05-style regression coverage.
Stage 3 guardrails to preserve:
Do not use catalog_contract_count as the slicing ceiling. It may be logged for diagnostics, but V4 judgment count is the hard upper bound for rank search.
Do not bundle IMP-17/IMP-47B anchor drift into IMP-38. Report it as baseline noise unless a new IMP-38-specific failure appears.
Keep IMP-05 candidate evidence/dedup semantics unchanged. IMP-38 changes search breadth, not the scoring or matching algorithm.
Preserve IMP-30 provisional ordering: dynamic search breadth first, then chain_exhausted, then provisional synthesis when allowed.
Minor observation:
The local working tree already has unrelated changes to src/phase_z2_pipeline.py and tests/test_phase_z2_v4_fallback.py; Stage 3 should keep diffs scoped to the IMP-38 units and avoid absorbing unrelated IMP-47B work.
FINAL_CONSENSUS: YES - proceed to Stage 3.
[Codex #4] Stage 2 final-lock verification - IMP-38
Verified Claude #4's final Stage 2 lock against local code and focused tests. Result: the Stage 2 lock is coherent and ready for Stage 3.
Verification summary:
- The implementation target still exists in the current working tree: `lookup_v4_match_with_fallback(..., max_rank: int = 3)` still slices `judgments_full32` with `[:max_rank]`.
- The catalog loader still returns the raw `frame_contracts.yaml` dict (`load_frame_contracts()`), and existing consumers still use `.keys()`, `.values()`, and `len(load_frame_contracts())`. This re-confirms that policy must not be inserted as a raw top-level dict in `frame_contracts.yaml`.
- `Front/vite.config.ts` still contains `PHASE_Z_MAX_RANK=32`, and `src/phase_z2_pipeline.py` still does not read that env. The env cleanup target remains real.
- Focused baseline is green: `python -m pytest -q tests/test_phase_z2_v4_fallback.py tests/test_catalog_invariant.py` -> `15 passed`.
Claude #4 lock review:
- U1 is correct: separate `templates/phase_z2/catalog/v4_fallback_policy.yaml` + `load_v4_fallback_policy()` loader. It must not change the return shape of `load_frame_contracts()`.
- U2 is correct: dynamic effective max-rank belongs inside `lookup_v4_match_with_fallback`, with the final ceiling locked as `min(configured_extended_max_rank, len(judgments_full32))`.
- U2 trace expansion is useful and should be retained. The 8-field trace is a safe superset of the prior Codex requirement because it separates caller/default/policy decisions.
- U3 is correct: remove hardcoded `max_rank=3` call-site overrides where policy should apply, clean only the tracked `Front/vite.config.ts`, and add mdx03/mdx05-style regression coverage.
Stage 3 guardrails to preserve:
1. Do not use `catalog_contract_count` as the slicing ceiling. It may be logged for diagnostics, but V4 judgment count is the hard upper bound for rank search.
2. Do not bundle IMP-17/IMP-47B anchor drift into IMP-38. Report it as baseline noise unless a new IMP-38-specific failure appears.
3. Keep IMP-05 candidate evidence/dedup semantics unchanged. IMP-38 changes search breadth, not the scoring or matching algorithm.
4. Preserve IMP-30 provisional ordering: dynamic search breadth first, then chain_exhausted, then provisional synthesis when allowed.
Minor observation:
- The local working tree already has unrelated changes to `src/phase_z2_pipeline.py` and `tests/test_phase_z2_v4_fallback.py`; Stage 3 should keep diffs scoped to the IMP-38 units and avoid absorbing unrelated IMP-47B work.
FINAL_CONSENSUS: YES - proceed to Stage 3.
Verified Claude #5's U1 implementation against the Stage 2 lock and the local working tree.
Result: u1 is correct and scope-acceptable.
Checks:
templates/phase_z2/catalog/v4_fallback_policy.yaml exists as a separate policy file. The policy keys are present and match the Stage 2 lock: policy_type=dynamic_usable_count_based, usable_threshold=1, default_max_rank=3, extended_max_rank=32.
src/phase_z2_mapper.py adds V4_FALLBACK_POLICY_PATH, _V4_FALLBACK_POLICY_CACHE, _V4_FALLBACK_POLICY_DEFAULT, and load_v4_fallback_policy() without changing load_frame_contracts() or get_contract().
The fallback default is conservative: if the policy yaml is missing, default_max_rank=3 and extended_max_rank=3, preserving pre-IMP-38 behavior.
The policy remains out of frame_contracts.yaml; load_frame_contracts() still returns the raw template-id catalog shape. This satisfies the prior catalog-pollution correction.
The warning is a pytest cache write permission warning under .pytest_cache; it is not an IMP-38 functional failure.
Scope note:
The u1 line count is larger than the Stage 2 estimate, mainly because the new loader test is explicit. I do not see a functional reason to rewind u1 for that. If the orchestration line budget is enforced mechanically, that can be handled as a process cleanup, but the implementation itself is coherent and safe.
Remaining units: [u2, u3]
rewind_target: continue_same
FINAL_CONSENSUS: NO
[Codex #5] Stage 3 code-edit verification - IMP-38 u1
Verified Claude #5's U1 implementation against the Stage 2 lock and the local working tree.
Result: u1 is correct and scope-acceptable.
Checks:
- `templates/phase_z2/catalog/v4_fallback_policy.yaml` exists as a separate policy file. The policy keys are present and match the Stage 2 lock: `policy_type=dynamic_usable_count_based`, `usable_threshold=1`, `default_max_rank=3`, `extended_max_rank=32`.
- `src/phase_z2_mapper.py` adds `V4_FALLBACK_POLICY_PATH`, `_V4_FALLBACK_POLICY_CACHE`, `_V4_FALLBACK_POLICY_DEFAULT`, and `load_v4_fallback_policy()` without changing `load_frame_contracts()` or `get_contract()`.
- The fallback default is conservative: if the policy yaml is missing, `default_max_rank=3` and `extended_max_rank=3`, preserving pre-IMP-38 behavior.
- The policy remains out of `frame_contracts.yaml`; `load_frame_contracts()` still returns the raw template-id catalog shape. This satisfies the prior catalog-pollution correction.
- `tests/test_v4_fallback_policy_loader.py` covers yaml existence, expected keys/values, cache identity, graceful missing-yaml fallback, catalog shape preservation, and policy-key non-bleed.
- No U1 production edit touches `src/phase_z2_pipeline.py`; the dynamic selector behavior remains correctly deferred to u2.
Test result:
`python -m pytest -q tests/test_v4_fallback_policy_loader.py tests/test_catalog_invariant.py tests/test_phase_z2_v4_fallback.py` -> `21 passed, 1 warning in 0.34s`.
The warning is a pytest cache write permission warning under `.pytest_cache`; it is not an IMP-38 functional failure.
Scope note:
The u1 line count is larger than the Stage 2 estimate, mainly because the new loader test is explicit. I do not see a functional reason to rewind u1 for that. If the orchestration line budget is enforced mechanically, that can be handled as a process cleanup, but the implementation itself is coherent and safe.
Remaining units: [u2, u3]
rewind_target: continue_same
FINAL_CONSENSUS: NO
대응: U2 = lookup_v4_match_with_fallback 함수 본체만 수정. #76 의 변경 영역 (Step 12 reject route = line shift 후 L568/L584/L805/L3264 hunk 등) 과 다른 위치. 함수 단위 분리 = commit 시 merge 안전.
변경 line count
src/phase_z2_pipeline.py: signature 1 line + docstring 8 lines + import 1 line + logic ~55 lines + trace 8 fields + 메시지 1 line = +75 lines, -3 lines (signature/legacy trace 정리)
총 +220 lines, 2 files. plan estimate 48 line / 2 files 보다 큰 이유 = test 가 7 항목 (caller_override + 8 trace + Codex #2 정정 + no_judgments + no_section + chain_exhausted 메시지).
Verified U1 and U2 against the Stage 2 lock and the current local working tree.
Result: u1 and u2 are correct and scope-acceptable. Proceed to u3, with one call-site-count correction noted below.
Checks:
U1 remains correct: templates/phase_z2/catalog/v4_fallback_policy.yaml is separate from frame_contracts.yaml, and src/phase_z2_mapper.py adds load_v4_fallback_policy() without changing load_frame_contracts() / get_contract() shape.
U2 changes lookup_v4_match_with_fallback(..., max_rank: Optional[int] = None) and loads policy through load_v4_fallback_policy().
The three ceiling concepts are separated in code: default_max_rank, configured_extended_max_rank, and judgments_count.
Codex #2 correction is implemented: effective_extended_ceiling = min(configured_extended_max_rank, judgments_count) when judgments exist.
The trace includes the required IMP-38 fields: requested_max_rank, default_max_rank, configured_extended_max_rank, judgments_count, effective_extended_ceiling, effective_max_rank, usable_count, policy_applied, plus legacy alias max_rank.
The caller override compatibility path works: explicit max_rank=3 yields policy_applied="caller_override" and effective_max_rank=3.
The policy path works: with 5 V4 judgments and no usable default-window candidate, max_rank=None yields policy_applied="extended_max_rank", effective_extended_ceiling=5, and effective_max_rank=5.
IMP-05 / IMP-30 backward compatibility is preserved by the focused regression suite: existing tests/test_phase_z2_v4_fallback.py still passes.
The warning is the known .pytest_cache permission warning, not an IMP-38 functional failure.
Important correction for u3:
Claude #6 / the todo summary says "4 call sites all pass max_rank=3". In the current checkout I verified the call sites with rg -n "lookup_v4_match_with_fallback\(" src/phase_z2_pipeline.py tests -S.
Current production call-site shape is:
src/phase_z2_pipeline.py:1996 - no explicit max_rank; already uses policy path.
So u3 should remove the explicit max_rank=3 from the three current call sites, not force a fourth edit. The already-policy call at :1996 should be left as-is unless a new concrete reason appears.
Scope note:
The src/phase_z2_pipeline.py diff is currently mixed with unrelated IMP-47B changes in the same file. This review treats only the IMP-38 selector import/signature/policy/trace logic and tests/test_dynamic_max_rank.py as U2 scope.
Remaining units: [u3]
rewind_target: continue_same
FINAL_CONSENSUS: NO
[Codex #6] Stage 3 code-edit verification - IMP-38 u1+u2
Verified U1 and U2 against the Stage 2 lock and the current local working tree.
Result: u1 and u2 are correct and scope-acceptable. Proceed to u3, with one call-site-count correction noted below.
Checks:
- U1 remains correct: `templates/phase_z2/catalog/v4_fallback_policy.yaml` is separate from `frame_contracts.yaml`, and `src/phase_z2_mapper.py` adds `load_v4_fallback_policy()` without changing `load_frame_contracts()` / `get_contract()` shape.
- U2 changes `lookup_v4_match_with_fallback(..., max_rank: Optional[int] = None)` and loads policy through `load_v4_fallback_policy()`.
- The three ceiling concepts are separated in code: `default_max_rank`, `configured_extended_max_rank`, and `judgments_count`.
- Codex #2 correction is implemented: `effective_extended_ceiling = min(configured_extended_max_rank, judgments_count)` when judgments exist.
- The trace includes the required IMP-38 fields: `requested_max_rank`, `default_max_rank`, `configured_extended_max_rank`, `judgments_count`, `effective_extended_ceiling`, `effective_max_rank`, `usable_count`, `policy_applied`, plus legacy alias `max_rank`.
- The caller override compatibility path works: explicit `max_rank=3` yields `policy_applied="caller_override"` and `effective_max_rank=3`.
- The policy path works: with 5 V4 judgments and no usable default-window candidate, `max_rank=None` yields `policy_applied="extended_max_rank"`, `effective_extended_ceiling=5`, and `effective_max_rank=5`.
- IMP-05 / IMP-30 backward compatibility is preserved by the focused regression suite: existing `tests/test_phase_z2_v4_fallback.py` still passes.
Test result:
`python -m pytest -q tests/test_dynamic_max_rank.py tests/test_v4_fallback_policy_loader.py tests/test_catalog_invariant.py tests/test_phase_z2_v4_fallback.py` -> `28 passed, 1 warning in 0.41s`.
The warning is the known `.pytest_cache` permission warning, not an IMP-38 functional failure.
Important correction for u3:
Claude #6 / the todo summary says "4 call sites all pass max_rank=3". In the current checkout I verified the call sites with `rg -n "lookup_v4_match_with_fallback\(" src/phase_z2_pipeline.py tests -S`.
Current production call-site shape is:
- `src/phase_z2_pipeline.py:1996` - no explicit `max_rank`; already uses policy path.
- `src/phase_z2_pipeline.py:3590` - explicit `max_rank=3`.
- `src/phase_z2_pipeline.py:3661` - explicit `max_rank=3`.
- `src/phase_z2_pipeline.py:3858` - explicit `max_rank=3`.
So u3 should remove the explicit `max_rank=3` from the three current call sites, not force a fourth edit. The already-policy call at `:1996` should be left as-is unless a new concrete reason appears.
Scope note:
The `src/phase_z2_pipeline.py` diff is currently mixed with unrelated IMP-47B changes in the same file. This review treats only the IMP-38 selector import/signature/policy/trace logic and `tests/test_dynamic_max_rank.py` as U2 scope.
Remaining units: [u3]
rewind_target: continue_same
FINAL_CONSENSUS: NO
❌ — L1996 은 이미 default (즉 IMP-38 후 자동 policy path)
"L1863 (override path, default 사용)" 표현
부분 맞음 (default 사용) — 다만 "override" 가 아닌 일반 호출. L1863 → 현 L1996 (line shift)
원인 추정: Stage 1 read 시 L1863 = "default 사용" 으로 인식했지만, 이후 IMP-30 (#39 commit 1efbf67) 또는 IMP-32 (#41 commit c412f1e) 의 helper extraction 에서 explicit max_rank=3 제거됨. 현 working tree 에서는 L1996 = no explicit.
Verified Claude #7's corrected U3 plan against the current local checkout and the prior Codex #6 correction.
Result: corrected U3 plan is accepted. Proceed to U3 execution.
Checks:
Claude #7 correctly accepts the call-site-count correction from Codex #6: the current production cleanup target is 3 explicit max_rank=3 call sites, not 4.
Current production call-site shape re-verified with rg -n "lookup_v4_match_with_fallback\(" src/phase_z2_pipeline.py tests -S:
src/phase_z2_pipeline.py:3590 - explicit max_rank=3; remove in U3.
src/phase_z2_pipeline.py:3661 - explicit max_rank=3; remove in U3.
src/phase_z2_pipeline.py:3858 - explicit max_rank=3; remove in U3.
Front/vite.config.ts still has the stale env surface at lines currently matching PHASE_Z_MAX_RANK: comment around :349 and value around :358. This remains a real U3 cleanup target.
U1/U2 remain accepted from Codex #6: policy YAML/loader, dynamic max-rank logic, 3-variable min ceiling, 8 trace fields, and caller_override backward compatibility are already verified.
Accepted U3 scope:
Remove exactly the three explicit max_rank=3 arguments in src/phase_z2_pipeline.py so those paths use the new policy default.
Leave the already-policy call at src/phase_z2_pipeline.py:1996 untouched.
Remove/retire PHASE_Z_MAX_RANK=32 from tracked Front/vite.config.ts only; do not touch untracked frontend clone directories.
Add focused regression coverage proving normal/default behavior remains rank-3 when usable candidates exist and extends when no usable candidate exists inside the default window.
Guardrails:
Do not alter V4 scoring, candidate evidence schema, L4 dedup semantics, or IMP-30 provisional ordering.
Do not bundle IMP-17/IMP-47B anchor drift or other dirty-tree changes into IMP-38.
Keep the U3 diff scoped to the three call-site argument removals, tracked vite env cleanup, and the new regression test.
FINAL_CONSENSUS: YES - ready for U3 execute.
[Codex #7] Stage 3 U3 plan re-review - IMP-38
Verified Claude #7's corrected U3 plan against the current local checkout and the prior Codex #6 correction.
Result: corrected U3 plan is accepted. Proceed to U3 execution.
Checks:
- Claude #7 correctly accepts the call-site-count correction from Codex #6: the current production cleanup target is 3 explicit `max_rank=3` call sites, not 4.
- Current production call-site shape re-verified with `rg -n "lookup_v4_match_with_fallback\(" src/phase_z2_pipeline.py tests -S`:
- `src/phase_z2_pipeline.py:1996` - no explicit `max_rank`; already uses policy path. Leave unchanged.
- `src/phase_z2_pipeline.py:3590` - explicit `max_rank=3`; remove in U3.
- `src/phase_z2_pipeline.py:3661` - explicit `max_rank=3`; remove in U3.
- `src/phase_z2_pipeline.py:3858` - explicit `max_rank=3`; remove in U3.
- `Front/vite.config.ts` still has the stale env surface at lines currently matching `PHASE_Z_MAX_RANK`: comment around `:349` and value around `:358`. This remains a real U3 cleanup target.
- U1/U2 remain accepted from Codex #6: policy YAML/loader, dynamic max-rank logic, 3-variable min ceiling, 8 trace fields, and caller_override backward compatibility are already verified.
Accepted U3 scope:
- Remove exactly the three explicit `max_rank=3` arguments in `src/phase_z2_pipeline.py` so those paths use the new policy default.
- Leave the already-policy call at `src/phase_z2_pipeline.py:1996` untouched.
- Remove/retire `PHASE_Z_MAX_RANK=32` from tracked `Front/vite.config.ts` only; do not touch untracked frontend clone directories.
- Add focused regression coverage proving normal/default behavior remains rank-3 when usable candidates exist and extends when no usable candidate exists inside the default window.
Guardrails:
- Do not alter V4 scoring, candidate evidence schema, L4 dedup semantics, or IMP-30 provisional ordering.
- Do not bundle IMP-17/IMP-47B anchor drift or other dirty-tree changes into IMP-38.
- Keep the U3 diff scoped to the three call-site argument removals, tracked vite env cleanup, and the new regression test.
FINAL_CONSENSUS: YES - ready for U3 execute.
총 U3 = +115 lines, -4 lines, 3 files. plan estimate 30 line / 3 files 보다 큰 이유 = regression test 4 scenarios (normal / extended / cleanup / override) 가 verify 항목 다수.
Verified Claude #9's U3 implementation against Codex #7's corrected U3 plan and the current local working tree.
Result: u3 is correct and scoped. Stage 3 is complete; proceed to Stage 4 test-verify.
Findings:
The corrected U3 scope was 3 production cleanup sites, not 4. Current source state matches that correction: the three explicit production max_rank=3 overrides have been removed, and the existing default-policy call path remains without an explicit max_rank.
Current production lookup call sites in src/phase_z2_pipeline.py are at 1996, 3590, 3660, and 3856; none of them now pass max_rank=3. The only remaining max_rank=3 references found are regression tests that intentionally prove caller_override backward compatibility.
The provisional/retry behavior is preserved: the allow_provisional=True call paths remain intact while the rank policy is allowed to flow through the default YAML-backed policy path.
Front/vite.config.ts no longer forwards PHASE_Z_MAX_RANK=32. The file now carries a retire comment for that env, while PHASE_Z_ALLOW_RESTRUCTURE and PHASE_Z_ALLOW_REJECT remain unchanged.
tests/test_phase_z2_max_rank_regression.py covers the four U3 scenarios: default policy on normal usable candidates, extended rank when the default window has no usable candidate, default caller no longer reporting caller_override, and explicit max_rank still reporting caller_override.
The IMP-05/IMP-30 fallback baseline remains covered by tests/test_phase_z2_v4_fallback.py as part of the focused run.
Scope note:
src/phase_z2_pipeline.py currently contains unrelated IMP-47B working-tree hunks, so I treated that diff as mixed. This review is limited to the IMP-38 U3 max_rank call-site cleanup and Vite env retirement surfaces.
[Codex #8] Stage 3 code-edit verification - IMP-38 u3
Verified Claude #9's U3 implementation against Codex #7's corrected U3 plan and the current local working tree.
Result: u3 is correct and scoped. Stage 3 is complete; proceed to Stage 4 test-verify.
Findings:
- The corrected U3 scope was 3 production cleanup sites, not 4. Current source state matches that correction: the three explicit production max_rank=3 overrides have been removed, and the existing default-policy call path remains without an explicit max_rank.
- Current production lookup call sites in src/phase_z2_pipeline.py are at 1996, 3590, 3660, and 3856; none of them now pass max_rank=3. The only remaining max_rank=3 references found are regression tests that intentionally prove caller_override backward compatibility.
- The provisional/retry behavior is preserved: the allow_provisional=True call paths remain intact while the rank policy is allowed to flow through the default YAML-backed policy path.
- Front/vite.config.ts no longer forwards PHASE_Z_MAX_RANK=32. The file now carries a retire comment for that env, while PHASE_Z_ALLOW_RESTRUCTURE and PHASE_Z_ALLOW_REJECT remain unchanged.
- tests/test_phase_z2_max_rank_regression.py covers the four U3 scenarios: default policy on normal usable candidates, extended rank when the default window has no usable candidate, default caller no longer reporting caller_override, and explicit max_rank still reporting caller_override.
- The IMP-05/IMP-30 fallback baseline remains covered by tests/test_phase_z2_v4_fallback.py as part of the focused run.
Scope note:
- src/phase_z2_pipeline.py currently contains unrelated IMP-47B working-tree hunks, so I treated that diff as mixed. This review is limited to the IMP-38 U3 max_rank call-site cleanup and Vite env retirement surfaces.
=== EVIDENCE ===
Commands run:
rg -n "lookup_v4_match_with_fallback\(|max_rank=3|PHASE_Z_MAX_RANK|phase_z_max_rank|effective_max_rank|policy_applied" src\phase_z2_pipeline.py Front\vite.config.ts tests\test_phase_z2_max_rank_regression.py tests\test_dynamic_max_rank.py
python -m pytest -q tests/test_phase_z2_max_rank_regression.py tests/test_dynamic_max_rank.py tests/test_v4_fallback_policy_loader.py tests/test_catalog_invariant.py tests/test_phase_z2_v4_fallback.py
Get-Content -LiteralPath 'tests\test_phase_z2_max_rank_regression.py'
Get-Content -LiteralPath 'Front\vite.config.ts' | Select-Object -Skip 340 -First 18
Get-Content -LiteralPath 'src\phase_z2_pipeline.py' | Select-Object -Skip 3582 -First 18
Get-Content -LiteralPath 'src\phase_z2_pipeline.py' | Select-Object -Skip 3652 -First 20
Get-Content -LiteralPath 'src\phase_z2_pipeline.py' | Select-Object -Skip 3848 -First 20
git diff -- src\phase_z2_pipeline.py Front\vite.config.ts tests\test_phase_z2_max_rank_regression.py
Files checked:
src/phase_z2_pipeline.py
Front/vite.config.ts
tests/test_phase_z2_max_rank_regression.py
tests/test_dynamic_max_rank.py
tests/test_v4_fallback_policy_loader.py
tests/test_catalog_invariant.py
tests/test_phase_z2_v4_fallback.py
Test results:
python -m pytest -q tests/test_phase_z2_max_rank_regression.py tests/test_dynamic_max_rank.py tests/test_v4_fallback_policy_loader.py tests/test_catalog_invariant.py tests/test_phase_z2_v4_fallback.py -> 32 passed, 1 warning in 0.46s
The warning is a pytest cache permission warning under .pytest_cache and does not affect the executed tests.
Verified facts:
- No production max_rank=3 override remains in src/phase_z2_pipeline.py.
- Front/vite.config.ts no longer defines PHASE_Z_MAX_RANK.
- The new U3 regression tests exercise the policy-enabled default path and explicit caller_override path.
- Focused IMP-38 regression suite passes.
Remaining units: []
rewind_target: continue_same
FINAL_CONSENSUS: YES - proceed to Stage 4 test-verify.
"src/phase_z2_pipeline.py currently contains unrelated IMP-47B working-tree hunks, so I treated that diff as mixed. This review is limited to the IMP-38 U3 max_rank call-site cleanup and Vite env retirement surfaces."
Reviewed Claude #11 Stage 4 test-verify comment and re-ran the verification locally.
Result: Stage 4 is accepted for the IMP-38 scope. Proceed to Stage 5 commit-push with a scope-qualified/cherry-picked IMP-38 commit set.
Findings:
Focused IMP-38 regression surface is green: the policy loader, dynamic max-rank logic, U3 call-site cleanup regressions, catalog invariant, and existing V4 fallback baseline pass together.
Full pytest is also reproducible as 684 passed / 2 failed when TMP/TEMP are pointed at a writable temp root. The only remaining failures are the known IMP-17 line-anchor tests.
The two failing tests are not behavioral V4 max-rank regressions. They assert fixed physical line numbers in src/phase_z2_pipeline.py and now read line 570 as return _v4_match_from_judgment(section_id, top, rank=1) and line 571 as blank, while the route-hint table is currently at lines 573-581.
I agree with the corrected high-level framing that the anchor drift is cumulative, not an IMP-47B-only story. IMP-38 does contribute to the file's pre-anchor line movement through the added load_v4_fallback_policy import. However, one nuance: the large IMP-38 expansion inside lookup_v4_match_with_fallback is below the route-hint anchor and is not the direct cause of the line 570/571 anchor failure. The direct pre-anchor shift visible in the diff is the combination of +1 IMP-38 import and +6 IMP-47B Step 12 import lines, with IMP-47B also changing the reject-route table content itself.
Therefore the anchor tests should remain out of the IMP-38 Stage 5 commit. Re-pinning or redesigning those brittle physical-line anchor tests belongs in the governance/anchor follow-up, not in the IMP-38 cherry-pick.
Stage 5 should include only the IMP-38 files/hunks: policy YAML, mapper loader, max-rank policy logic/call-site cleanup, Vite env retirement, and IMP-38 tests. It should exclude IMP-47B AI fallback hunks, frontend #76/#70 work, and anchor-test re-pin changes.
A first raw full-suite run hit many tmp_path setup errors because the default Windows temp root was not writable (C:\Users\User\AppData\Local\Temp\pytest-of-User). Re-running with TMP/TEMP set to D:\tmp removed those environment errors and reproduced Claude's 684/2 shape.
Verified facts:
No production max_rank=3 override remains in src/phase_z2_pipeline.py.
Front/vite.config.ts no longer defines PHASE_Z_MAX_RANK.
IMP-38 focused tests pass.
The remaining full-suite failures are brittle physical-line anchor failures, not max-rank policy behavior failures.
rewind_target: continue_same
FINAL_CONSENSUS: YES - proceed to Stage 5 commit-push with IMP-38 scoped cherry-pick.
[Codex #9] Stage 4 test-verify review - IMP-38
Reviewed Claude #11 Stage 4 test-verify comment and re-ran the verification locally.
Result: Stage 4 is accepted for the IMP-38 scope. Proceed to Stage 5 commit-push with a scope-qualified/cherry-picked IMP-38 commit set.
Findings:
- Focused IMP-38 regression surface is green: the policy loader, dynamic max-rank logic, U3 call-site cleanup regressions, catalog invariant, and existing V4 fallback baseline pass together.
- Full pytest is also reproducible as 684 passed / 2 failed when TMP/TEMP are pointed at a writable temp root. The only remaining failures are the known IMP-17 line-anchor tests.
- The two failing tests are not behavioral V4 max-rank regressions. They assert fixed physical line numbers in src/phase_z2_pipeline.py and now read line 570 as `return _v4_match_from_judgment(section_id, top, rank=1)` and line 571 as blank, while the route-hint table is currently at lines 573-581.
- I agree with the corrected high-level framing that the anchor drift is cumulative, not an IMP-47B-only story. IMP-38 does contribute to the file's pre-anchor line movement through the added `load_v4_fallback_policy` import. However, one nuance: the large IMP-38 expansion inside `lookup_v4_match_with_fallback` is below the route-hint anchor and is not the direct cause of the line 570/571 anchor failure. The direct pre-anchor shift visible in the diff is the combination of +1 IMP-38 import and +6 IMP-47B Step 12 import lines, with IMP-47B also changing the reject-route table content itself.
- Therefore the anchor tests should remain out of the IMP-38 Stage 5 commit. Re-pinning or redesigning those brittle physical-line anchor tests belongs in the governance/anchor follow-up, not in the IMP-38 cherry-pick.
- Stage 5 should include only the IMP-38 files/hunks: policy YAML, mapper loader, max-rank policy logic/call-site cleanup, Vite env retirement, and IMP-38 tests. It should exclude IMP-47B AI fallback hunks, frontend #76/#70 work, and anchor-test re-pin changes.
=== EVIDENCE ===
Commands run:
- git status --short
- rg -n "lookup_v4_match_with_fallback\(|max_rank=3|PHASE_Z_MAX_RANK|phase_z_max_rank|effective_max_rank|policy_applied" src\\phase_z2_pipeline.py Front\\vite.config.ts tests\\test_phase_z2_max_rank_regression.py tests\\test_dynamic_max_rank.py tests\\orchestrator_unit\\test_imp17_comment_anchor.py
- python -m pytest -q tests/test_phase_z2_max_rank_regression.py tests/test_dynamic_max_rank.py tests/test_v4_fallback_policy_loader.py tests/test_catalog_invariant.py tests/test_phase_z2_v4_fallback.py
- $env:TMP='D:\\tmp'; $env:TEMP='D:\\tmp'; python -m pytest -q tests
- line-window read of src/phase_z2_pipeline.py:560-590 and HEAD:src/phase_z2_pipeline.py:560-590
- git diff -- src\\phase_z2_pipeline.py | Select-String -Pattern '^@@|IMP-38|load_v4_fallback_policy|V4_FallbackPolicy|max_rank|APPLICATION_MODE|_v4_match_from_judgment|IMP-05 L2' -Context 2,2
- rg -n "load_v4_fallback_policy|V4FallbackPolicy|yaml|APPLICATION_MODE_BY_V4_LABEL|def _v4_match_from_judgment|IMP-05 L2|def lookup_v4_match_with_fallback" src\\phase_z2_pipeline.py src\\phase_z2_mapper.py templates\\phase_z2\\catalog\\v4_fallback_policy.yaml
- Get-Content -LiteralPath 'tests\\orchestrator_unit\\test_imp17_comment_anchor.py' | Select-Object -First 45
Files checked:
- src/phase_z2_pipeline.py
- src/phase_z2_mapper.py
- Front/vite.config.ts
- templates/phase_z2/catalog/v4_fallback_policy.yaml
- tests/test_dynamic_max_rank.py
- tests/test_phase_z2_max_rank_regression.py
- tests/test_v4_fallback_policy_loader.py
- tests/test_phase_z2_v4_fallback.py
- tests/orchestrator_unit/test_imp17_comment_anchor.py
Test results:
- Focused IMP-38 suite: 32 passed, 1 pytest-cache warning in 0.47s.
- Full suite with writable temp root: 684 passed, 2 failed in 52.86s.
- The two failures are:
- 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
Execution note:
- A first raw full-suite run hit many tmp_path setup errors because the default Windows temp root was not writable (`C:\Users\User\AppData\Local\Temp\pytest-of-User`). Re-running with TMP/TEMP set to D:\tmp removed those environment errors and reproduced Claude's 684/2 shape.
Verified facts:
- No production `max_rank=3` override remains in src/phase_z2_pipeline.py.
- Front/vite.config.ts no longer defines PHASE_Z_MAX_RANK.
- IMP-38 focused tests pass.
- The remaining full-suite failures are brittle physical-line anchor failures, not max-rank policy behavior failures.
rewind_target: continue_same
FINAL_CONSENSUS: YES - proceed to Stage 5 commit-push with IMP-38 scoped cherry-pick.
"the large IMP-38 expansion inside lookup_v4_match_with_fallback is below the route-hint anchor and is not the direct cause of the line 570/571 anchor failure. The direct pre-anchor shift visible in the diff is the combination of +1 IMP-38 import and +6 IMP-47B Step 12 import lines"
직접 grep 검증
위치
결과
anchor (L573-581) 기준
route-hint anchor _IMP05_ROUTE_HINTS
L573-581 (sed read 확인)
기준점
IMP-38 load_v4_fallback_policy import
L55 (grep 확인)
anchor 위 → +1 line shift
IMP-38 lookup_v4_match_with_fallback 함수 본체 (+60 lines)
L840 시작 (grep 확인)
anchor 아래 → anchor 영향 0
→ Codex #9 정정 정확. IMP-38 의 anchor drift 기여 = import 1 line 만 (함수 본체 +60 lines 는 anchor 아래라 영향 X).
"A first raw full-suite run hit many tmp_path setup errors because the default Windows temp root was not writable. Re-running with TMP/TEMP set to D:\tmp removed those environment errors and reproduced Claude's 684/2 shape."
→ Codex 가 환경 변수 설정 후 동일한 684/2 reproduce. 환경 차이 X = 결과 신뢰 확인 ✅
Stage 4 is locked after Codex #9 and Claude's follow-up verification. IMP-38 is ready to enter Stage 5 commit-push once the user gives an explicit GO.
Readiness summary:
IMP-38 focused suite is green: 32 passed.
Full suite is reproducible as 684 passed / 2 failed with TMP/TEMP set to D:\tmp.
The 2 failures are the known physical-line anchor drift tests in tests/orchestrator_unit/test_imp17_comment_anchor.py, not behavioral IMP-38 regressions.
Codex #9 nuance is accepted: IMP-38 contributes to the anchor drift only through the pre-anchor load_v4_fallback_policy import line. The large lookup_v4_match_with_fallback body expansion is below the route-hint anchor and is not the direct cause of the line 570/571 anchor failure.
Use hunk-level staging or an equivalent temporary-patch workflow.
Verify git diff --cached before commit.
Re-run the focused IMP-38 suite before committing.
Push only after the staged diff is confirmed to contain IMP-38 scope and no #76/frontend/anchor-repin changes.
Current status: waiting for explicit user GO for commit/push.
FINAL_CONSENSUS: YES - Stage 5 is ready, pending user GO.
[Codex #10] Stage 5 readiness note - IMP-38
Stage 4 is locked after Codex #9 and Claude's follow-up verification. IMP-38 is ready to enter Stage 5 commit-push once the user gives an explicit GO.
Readiness summary:
- IMP-38 focused suite is green: 32 passed.
- Full suite is reproducible as 684 passed / 2 failed with TMP/TEMP set to D:\tmp.
- The 2 failures are the known physical-line anchor drift tests in tests/orchestrator_unit/test_imp17_comment_anchor.py, not behavioral IMP-38 regressions.
- Codex #9 nuance is accepted: IMP-38 contributes to the anchor drift only through the pre-anchor load_v4_fallback_policy import line. The large lookup_v4_match_with_fallback body expansion is below the route-hint anchor and is not the direct cause of the line 570/571 anchor failure.
Stage 5 scope to commit:
New files:
- templates/phase_z2/catalog/v4_fallback_policy.yaml
- tests/test_v4_fallback_policy_loader.py
- tests/test_dynamic_max_rank.py
- tests/test_phase_z2_max_rank_regression.py
Modified files, IMP-38 hunks only:
- src/phase_z2_mapper.py
- src/phase_z2_pipeline.py
- Front/vite.config.ts
Explicitly excluded from the IMP-38 commit:
- src/phase_z2_ai_fallback/*
- src/config.py
- Front/client/*
- tests/orchestrator_unit/test_imp17_comment_anchor.py anchor re-pin
- IMP-47B / #76 hunks mixed into src/phase_z2_pipeline.py
- unrelated orchestrator/front/debug/generated artifacts
Stage 5 execution guard:
- Use hunk-level staging or an equivalent temporary-patch workflow.
- Verify git diff --cached before commit.
- Re-run the focused IMP-38 suite before committing.
- Push only after the staged diff is confirmed to contain IMP-38 scope and no #76/frontend/anchor-repin changes.
Current status: waiting for explicit user GO for commit/push.
FINAL_CONSENSUS: YES - Stage 5 is ready, pending user GO.
Reviewed Claude #13's Stage 5 execution-guard comment and re-checked the current issue state.
Result: Claude #13 is correct. Stage 5 is ready, pending explicit user GO for commit/push.
Verification:
Claude #13 accurately reflects the accepted Stage 4 chain: Claude #11 -> Codex #9 -> Claude #12 -> Codex #10.
The Stage 4 test shape is locked: IMP-38 focused suite = 32 passed; full suite = 684 passed / 2 known anchor-line failures when TMP/TEMP use a writable temp root.
The anchor attribution correction is preserved: IMP-38 contributes only the pre-anchor load_v4_fallback_policy import line; the larger lookup_v4_match_with_fallback body expansion is below the route-hint anchor and is not the direct line-anchor cause.
Claude #13's Stage 5 guard list is appropriate: hunk-level staging or equivalent temp-patch workflow, git diff --cached review, focused IMP-38 suite before commit, then push only after staged scope is confirmed.
The included/excluded file scope is correct for a scope-qualified IMP-38 commit.
IMP-47B/#76 hunks mixed into src/phase_z2_pipeline.py
unrelated generated/orchestrator artifacts
Operational note:
Because src/phase_z2_pipeline.py is mixed with #76 work, Stage 5 must not use a broad git add src/phase_z2_pipeline.py. Use hunk-level staging or a temporary clean patch/cherry-pick workflow and inspect git diff --cached before committing.
commit/push remains a destructive workflow step and should wait for the user's explicit GO.
FINAL_CONSENSUS: YES - Stage 5 ready, awaiting user GO.
[Codex #11] Stage 5 readiness verification - IMP-38
Reviewed Claude #13's Stage 5 execution-guard comment and re-checked the current issue state.
Result: Claude #13 is correct. Stage 5 is ready, pending explicit user GO for commit/push.
Verification:
- Claude #13 accurately reflects the accepted Stage 4 chain: Claude #11 -> Codex #9 -> Claude #12 -> Codex #10.
- The Stage 4 test shape is locked: IMP-38 focused suite = 32 passed; full suite = 684 passed / 2 known anchor-line failures when TMP/TEMP use a writable temp root.
- The anchor attribution correction is preserved: IMP-38 contributes only the pre-anchor load_v4_fallback_policy import line; the larger lookup_v4_match_with_fallback body expansion is below the route-hint anchor and is not the direct line-anchor cause.
- Claude #13's Stage 5 guard list is appropriate: hunk-level staging or equivalent temp-patch workflow, git diff --cached review, focused IMP-38 suite before commit, then push only after staged scope is confirmed.
- The included/excluded file scope is correct for a scope-qualified IMP-38 commit.
Stage 5 commit scope accepted:
Include:
- templates/phase_z2/catalog/v4_fallback_policy.yaml
- tests/test_v4_fallback_policy_loader.py
- tests/test_dynamic_max_rank.py
- tests/test_phase_z2_max_rank_regression.py
- IMP-38 hunks only from src/phase_z2_mapper.py
- IMP-38 hunks only from src/phase_z2_pipeline.py
- IMP-38 hunk from Front/vite.config.ts
Exclude:
- src/phase_z2_ai_fallback/*
- src/config.py
- Front/client/*
- tests/orchestrator_unit/test_imp17_comment_anchor.py
- IMP-47B/#76 hunks mixed into src/phase_z2_pipeline.py
- unrelated generated/orchestrator artifacts
Operational note:
- Because src/phase_z2_pipeline.py is mixed with #76 work, Stage 5 must not use a broad `git add src/phase_z2_pipeline.py`. Use hunk-level staging or a temporary clean patch/cherry-pick workflow and inspect `git diff --cached` before committing.
- commit/push remains a destructive workflow step and should wait for the user's explicit GO.
FINAL_CONSENSUS: YES - Stage 5 ready, awaiting user GO.
"Because src/phase_z2_pipeline.py is mixed with #76 work, Stage 5 must not use a broad git add src/phase_z2_pipeline.py. Use hunk-level staging or a temporary clean patch/cherry-pick workflow and inspect git diff --cached before committing."
→ 동의. git add src/phase_z2_pipeline.py 전체 add 시 IMP-47B hunks 같이 staged 됨. 반드시 hunk-level (git add -p) 또는 stash + selective restore 사용.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
관련 step: Step 5 — V4 매칭 fallback chain
source: #43 I1 (V4 fallback chain max_rank 정책 검토)
roadmap axis: R1 (22 단계 안정화)
wave: 2
priority: 높음
dependency: #5 (IMP-05 V4 fallback) verified, #42 (catalog 32) 완료 후
scope:
PHASE_Z_MAX_RANK=32env toggle → yaml 정식 정책usable ≥ N → 3/ 미만 → 32) — 추천v4_fallback_policyfield 도입lookup_v4_match_with_fallback의 max_rank 결정 pathout of scope:
guardrail / validation:
cross-ref:
review loop:
[Claude #1] Stage 1 problem-review — IMP-38 V4 max_rank 정책 정식화
Verified facts (직접 read 기반)
Fact 1 — env
PHASE_Z_MAX_RANK가 src/ 에서 사용 안 됨 ★ 핵심Front/vite.config.ts:358PHASE_Z_MAX_RANK: "32")src/phase_z2_pipeline.pysrc/phase_z2_pipeline.py:293, 4075, 4112, 4300PHASE_Z_STAGE0_ADAPTER_ENABLED,PHASE_Z_B4_*등)→ vite 가 env 보내지만 backend 가 무시. max_rank 는 hardcoded.
Fact 2 —
lookup_v4_match_with_fallbackdefault + 호출 site 모두max_rank=3src/phase_z2_pipeline.py:784max_rank: int = 3(default)src/phase_z2_pipeline.py:1863src/phase_z2_pipeline.py:3461max_rank=3hardcodedsrc/phase_z2_pipeline.py:3532max_rank=3hardcodedsrc/phase_z2_pipeline.py:3729max_rank=3hardcoded→ 4 곳 모두 rank 1~3 만 검색. rank 4+ frame 도달 못함.
Fact 3 — judgments slicing
[:max_rank]at line 825judgments_full32= V4 결과 32 frame 평가. 현재 max_rank=3 이라 32 중 3 만 검사 = 29 frame 무시.Fact 4 — catalog 32 frame 모두 등록됨
templates/phase_z2/catalog/frame_contracts.yaml= 32 top-level keys (grep 확인). 즉 catalog 자체는 32 frame 다 등록.Fact 5 —
v4_fallback_policyfield 없음 (신규 schema 도입 필요)grep
v4_fallback_policy|fallback_policy결과 = src/ + templates/ 0 hits.Fact 6 —
judgments_full32가 V4 결과 schema 명sec.get("judgments_full32")— V4 결과 yaml (tests/matching/v4_full32_result.yaml) 에 32 frame 평가 포함. fallback 확장 가능 데이터 이미 존재.Root cause
lookup_v4_match_with_fallback의max_rankPHASE_Z_MAX_RANK=32→ 결과: mdx05-2 같이 V4 rank 1~9 가 catalog 미등록 frame + rank 10 부터 등록 frame 인 case → rank 3 까지만 검사 → 모두 skip →
chain_exhausted→ unit 생성 X / placeholder zone.해결 = max_rank 결정 로직을 동적 으로 (옵션 C — usable_count 기반).
IMP-38 의 frontend / backend 분리 (명확화)
TOP_N_FRAMES = 6그대로. 변경 X (영향 0)max_rank결정→ IMP-38 = backend 의 검사 범위 확대 axis. frontend 표시와 무관.
Scope-lock 제안 (3 units, sequential)
U1 — catalog yaml
v4_fallback_policyschema 추가templates/phase_z2/catalog/frame_contracts.yamlv4_fallback_policy:(32 frame entry 와 별도 sibling)policy_type: dynamic_usable_count_based,usable_threshold: N,default_max_rank: 3,extended_max_rank: 32src/phase_z2_pipeline.py의 catalog loader 가 새 key 읽어 module-level constant 로U2 —
lookup_v4_match_with_fallback동적 max_rank 로직 (옵션 C)src/phase_z2_pipeline.py:779-usable_threshold: int = N추가 (또는 yaml 에서 자동 읽기)usable >= threshold→effective_max_rank = default_max_rank(3)usable < threshold→effective_max_rank = extended_max_rank(32)judgments[:effective_max_rank]로 sliceusable_count,effective_max_rank,policy_applied추가 (디버깅)U3 — 4 곳 호출 site 정리 + env 폐기 + 회귀 test
src/phase_z2_pipeline.py:1863/3461/3532/3729max_rank=3제거 → yaml policy 자동 적용 (default 동작)Front/vite.config.ts:349-358PHASE_Z_MAX_RANKline 폐기 + comment 정리Front_test/Front/vite.config.ts:358,Front_test_v515/Front/vite.config.ts:358tests/test_phase_z2_v4_fallback.py또는 신규test_phase_z2_max_rank_policy.py— yaml policy 로딩 + 동적 결정 + judgments slicing 검증Guardrails
allow_provisionalpath 와 일관: 동적 max_rank 확장 후에도 chain_exhausted → provisional 합성 그대로Out of scope
TOP_N_FRAMEScap → IMP-47A scope (영향 0)Codex review 대상 questions (Stage 1 → Stage 2 lock 전 합의 필요)
Q1 —
usable_thresholdN 값?Q2 —
extended_max_rankceiling = 32 (fixed) vs catalog count 기반 (동적)?len(CATALOG)또는judgments_full32길이) — catalog 확장 시 자동 추종Q3 — yaml policy key 위치?
policy.yamlQ4 — env
PHASE_Z_MAX_RANK폐기 시 backward compat?Q5 —
usable count계산 기준?phase_z_status in MVP1_ALLOWED_STATUSESonly / (b) + catalog 등록 frame / (c) v4_label use_as_is/light_edit onlyQ6 — IMP-30
allow_provisionalpath 와 우선순위?allow_provisional=True면 rank 1 provisional 합성Affected files
templates/phase_z2/catalog/frame_contracts.yamlv4_fallback_policy:)src/phase_z2_pipeline.py(L779-865 영역)src/phase_z2_pipeline.py(L1863, L3461, L3532, L3729)max_rank=3제거Front/vite.config.ts:349-358Front_test/Front/vite.config.ts,Front_test_v515/Front/vite.config.tstests/test_phase_z2_max_rank_policy.pytests/test_phase_z2_v4_fallback.pyVerification preview (Stage 2 에서 lock)
pytest -q tests/— 회귀 baseline (mdx03 fall through 확인, IMP-47B anchor test 변동 별 axis)ready-for-stage-2 (Codex review 대상 Q1~Q6 합의 후)
[Codex #1] Stage 1 problem-review verification - IMP-38 V4 max_rank policy
Root-cause verdict: Claude's diagnosis is substantially correct. The backend currently ignores the frontend
PHASE_Z_MAX_RANK=32env, and the effective V4 fallback search is still capped at rank 3 in the production selector path.Verified facts:
PHASE_Z_MAX_RANKappears inFront/vite.config.ts,Front_test/Front/vite.config.ts, andFront_test_v515/Front/vite.config.ts, butsrc/phase_z2_pipeline.pyhas no read of that env. Backend max-rank behavior is therefore not controlled by the Vite env.lookup_v4_match_with_fallback(...)still hasmax_rank: int = 3, and slicesjudgments_full32via[:max_rank].max_rank=3at the main composition/retry/override lookup points.frame_contracts.yamlcurrently contains 32 contracted frame entries, and nov4_fallback_policy/fallback_policyfield exists today.tests/test_phase_z2_v4_fallback.pycurrently passes:13 passed.Important scope corrections before Stage 2:
Do not add
v4_fallback_policyas a plain top-level sibling inframe_contracts.yamlunless the loader and all catalog consumers are updated to filter metadata.Current code assumes
load_frame_contracts()is a template-id -> frame-contract dict in several places:get_contract(template_id)directly indexes the raw YAML dict._known_contract_ids()returnslist(load_frame_contracts().keys()).len(load_frame_contracts())andsorted(load_frame_contracts().keys()).frame_contracts=list(load_frame_contracts().values()).tests/test_catalog_invariant.pyasserts every dict entry hastemplate_idandframe_id.A top-level dict policy would be treated as a fake frame unless filtering is added. Safer Stage 2 options:
templates/phase_z2/catalog/v4_fallback_policy.yaml; orload_v4_fallback_policy()plusload_frame_contracts()filtering out a reserved metadata key.Front_test/FrontandFront_test_v515/Frontappear to be untracked working-tree clones, whileFront/vite.config.tsis tracked. Stage 2 should not require edits to untracked clone dirs unless the operator explicitly wants those trees managed. The source-of-truth cleanup should target the trackedFront/vite.config.tsfirst.The usable-count policy should use the same predicates as the actual selection path as much as possible. Counting only label/status + catalog registration may still miss cases where top-3 candidates exist but fail capacity. Recommended effective usability predicate:
status in MVP1_ALLOWED_STATUSESget_contract(template_id) is not Noneraw_contentis supplied, capacity check is acceptable (ok,no_contract,unknown_source_shape) using the existingcompute_capacity_fitsemantics.Trace output should distinguish requested/default rank from effective rank. Do not just overwrite
trace["max_rank"]; add fields such aspolicy_applied,usable_count,default_max_rank,extended_max_rank, andeffective_max_rankso Stage 4 can prove when/why rank 32 was used.Frontend FramePanel
TOP_N_FRAMESdisplay cap is correctly out of scope. IMP-38 is backend candidate search breadth, not UI display breadth.Suggested Stage 2 lock shape:
lookup_v4_match_with_fallback, including trace fields and capacity-aware usable count.max_rank=3call-site overrides where dynamic policy should apply, clean the tracked Vite env, and add focused tests for rank-3-preserved and rank-extended cases.Residual risk:
pytest -q testsmay be noisy due to existing anchor drift. Stage 2/3 verification should be scope-qualified and must not bundle IMP-17 anchor re-pin into IMP-38.=== EVIDENCE ===
Commands run:
rg -n "PHASE_Z_MAX_RANK|lookup_v4_match_with_fallback|max_rank=3|max_rank: int|judgments_full32|MVP1_ALLOWED_STATUSES|APPLICATION_MODE_BY_V4_LABEL" src/phase_z2_pipeline.py Front/vite.config.ts Front_test/Front/vite.config.ts Front_test_v515/Front/vite.config.ts tests -Srg -c "^ template_id:" templates/phase_z2/catalog/frame_contracts.yamlrg -n "^v4_fallback_policy:|^ template_id:" templates/phase_z2/catalog/frame_contracts.yamlGet-Content src/phase_z2_pipeline.pyaroundlookup_v4_match_with_fallback, composition lookup sites, and B4load_frame_contracts().values()usageGet-Content src/phase_z2_mapper.pyforload_frame_contracts()/get_contract()behaviorGet-Content tests/test_catalog_invariant.pyandtests/test_family_contract_baseline.pygit ls-files Front/vite.config.ts Front_test/Front/vite.config.ts Front_test_v515/Front/vite.config.tspython -m pytest -q tests/test_phase_z2_v4_fallback.pyTest result:
tests/test_phase_z2_v4_fallback.py:13 passed.FINAL_CONSENSUS: YES, with the Stage 2 corrections above carried forward.
[Claude #2] Stage 2 simulation-plan — IMP-38 V4 max_rank 정책 정식화
Codex #1 의 5 corrections 를 직접 read 기반 검증 후 반영. 단순 수용 X.
Codex corrections 검증 결과
src/phase_z2_mapper.py:49-53load_frame_contracts() = yaml.safe_load() 전체 dict;tests/test_catalog_invariant.py:37-43isinstance(entry, dict) + assert template_id→ metadata dict 추가 시 failload_v4_fallback_policy()loadergit ls-files Front_test/Front/vite.config.ts Front_test_v515/Front/vite.config.ts= emptyFront/vite.config.tsonlyraw_content의존 →raw_content없는 경우 status + catalog only fallbacktrace["max_rank"]단일 →requested/default/extended/effective/usable_count/policy_applied분리추가 검증 (Codex 안 짚은 부분)
_CATALOG_CACHEpattern (src/phase_z2_mapper.py:46-53)max_rank=3제거 → default fall through (yaml policy 적용). yaml 파일 없으면extended_max_rank=catalog_count도 graceful — backward compat 확보allow_provisionalbyte-identicalchain_exhausted이후. effective_max_rank 확장 후에도 chain_exhausted → 합성 흐름 그대로tests/test_phase_z2_v4_fallback.py: 13 passed(Codex 확인). IMP-47B anchor drift = 별 axis baseline noise=== IMPLEMENTATION_UNITS ===
=== ISSUE AXIS COVERAGE ===
PHASE_Z_MAX_RANK=32env toggle → yaml 정식 정책v4_fallback_policyfieldlookup_v4_match_with_fallbackmax_rank 결정 path=== GUARDRAILS ===
allow_provisionalbyte-identical (provisional 합성 흐름 변경 X)=== OUT OF SCOPE ===
TOP_N_FRAMEScap → IMP-47A scope=== Q1~Q6 합의 (Codex review 반영) ===
len(judgments_full32))v4_fallback_policy.yaml★Front/vite.config.ts즉시 폐기 + warning emit=== ROLLBACK ===
max_rank=3)max_rank=3fall through=== EVIDENCE ===
Read 기반 검증:
src/phase_z2_mapper.py:46-53load_frame_contracts()정의 +_CATALOG_CACHEpatternsrc/phase_z2_mapper.py:56-57get_contract()= dict.get patternsrc/phase_z2_pipeline.py:2069-2071_known_contract_ids()=list(load_frame_contracts().keys())src/phase_z2_pipeline.py:3338-3339Step 10 debug =len(load_frame_contracts())/sorted(load_frame_contracts().keys())src/phase_z2_pipeline.py:4146B4 =frame_contracts=list(load_frame_contracts().values())tests/test_catalog_invariant.py:37-43invariant =isinstance(entry, dict)+template_id/frame_idassertsrc/phase_z2_pipeline.py:779-865lookup_v4_match_with_fallback정의 + trace dict + judgments slicingsrc/phase_z2_pipeline.py:825judgments = (sec.get("judgments_full32") or [])[:max_rank]git ls-filesFront_test / Front_test_v515 = empty (untracked worktree clones)FINAL_CONSENSUS: YES — Stage 2 plan ready for Codex review
[Codex #2] Stage 2 simulation-plan review - IMP-38 V4 max_rank policy
Verdict: Claude #2 correctly incorporated the Stage 1 correction points and the Stage 2 plan is acceptable, with one clarification required before/while entering Stage 3.
Validated corrections carried forward:
frame_contracts.yamlinto a separatetemplates/phase_z2/catalog/v4_fallback_policy.yaml, avoiding catalog-loader pollution. This addresses the risk thatload_frame_contracts().keys()/.values()/ catalog invariant tests would treat a policy dict as a fake frame.Front_test/FrontandFront_test_v515/Frontclone dirs are excluded from the implementation scope. Only trackedFront/vite.config.tsshould be cleaned forPHASE_Z_MAX_RANKenv removal.status in MVP1_ALLOWED_STATUSES, catalog contract exists, and capacity is acceptable whenraw_contentis available.TOP_N_FRAMESremains out of scope. IMP-38 is backend candidate-search breadth, not candidate display breadth.One required clarification:
Claude #2 still phrases Q2 as
catalog count dynamic (len(judgments_full32)). These are related today but not identical concepts:len(judgments_full32)= how many V4 judgments exist for the section.catalog_contract_count= how many frame contracts are registered.configured extended_max_rank= policy ceiling fromv4_fallback_policy.yaml.Recommended Stage 3 lock:
If catalog count is also used, name it separately as
catalog_contract_countand do not conflate it with V4 judgment length. This prevents future drift where catalog count and V4 judgment count differ.Implementation-unit review:
lookup_v4_match_with_fallback, with trace fields and capacity-aware usable predicate.max_rank=3call-site overrides where dynamic policy should apply, clean tracked Vite env, and add regression coverage.Verification expectations for Stage 3/4:
policy_applied,usable_count, andeffective_max_rank.tests/test_phase_z2_v4_fallback.pyshould remain green.FINAL_CONSENSUS: YES, with the
extended_max_ranksemantics clarified as above.[Claude #3] Stage 2 simulation-plan r2 — IMP-38 V4 max_rank 정책 (Codex #2 clarification 검증 후 반영)
Codex #2 의 Q2 clarification + 5 carried-forward corrections 를 직접 read 기반 검증 후 반영. 단순 동의 X.
Codex #2 의 carried-forward 5 corrections — 일치 확인
v4_fallback_policy.yaml+ loader APIFront/vite.config.tsonly→ Codex 도 "Validated corrections carried forward" 로 확인.
Codex #2 의 Q2 clarification — 검증 결과
Codex 정정 의도:
len(judgments_full32)≠ catalog count ≠ configuredextended_max_rank. 3 개념 분리.3 변수 의미 검증 (직접 read 기반):
configured_extended_max_ranklen(judgments_full32)catalog_contract_count = len(load_frame_contracts())#42IMP-04b 같은 catalog 확장 시 증가오늘은 셋 다 32 동일 → Claude #2 의
len(judgments_full32)표현 OK. 다만 미래 drift 위험 존재:→ Codex 정정 정당. 동의.
보강 의견 (Codex 안 짚은 부분)
judgments_count,effective_extended_ceiling별 trace → Stage 4 verification 시 어느 ceiling 이 effective 인지 명확catalog_contract_count는 ceiling 후보 제외Codex verification expectations — 검증 결과 (5 항목)
tests/test_phase_z2_v4_fallback.pygreen→ 5 모두 plan 과 일치.
=== IMPLEMENTATION_UNITS (r2 정정) ===
=== Q1~Q6 최종 합의 (Codex #2 clarification 반영) ===
min(configured_extended_max_rank, len(judgments_full32))(Codex Q2 정정 반영)v4_fallback_policy.yamlFront/vite.config.ts즉시 폐기 + warning emit=== GUARDRAILS (r2 정정 후) ===
allow_provisionalbyte-identical=== OUT OF SCOPE (변경 없음) ===
TOP_N_FRAMEScap → IMP-47A scope=== ROLLBACK ===
max_rank=3fall through)=== EVIDENCE ===
r2 검증 추가 read:
effective_extended_ceiling = min(configured, len(judgments_full32))— 미래 drift 안전len(judgments_full32)schema 위치: V4 결과 yaml (tests/matching/v4_full32_result.yaml) — section 별 평가 수catalog_contract_count = len(load_frame_contracts())— 현재 32,#42확장 시 증가r1 검증 그대로 (유효):
src/phase_z2_mapper.py:46-57load_frame_contracts/get_contracttests/test_catalog_invariant.py:37-43isinstance + template_id assertsrc/phase_z2_pipeline.py:779-865lookup_v4_match_with_fallbacksrc/phase_z2_pipeline.py:1863/3461/3532/37294 call sitesgit ls-files Front_test/Front_test_v515= empty (untracked)tests/test_phase_z2_v4_fallback.py: 13 passedready-for-stage-3 (Codex #3 review 후 진입)
FINAL_CONSENSUS: YES
[Codex #3] Stage 2 simulation-plan review - IMP-38 post-clarification
Verified Claude #3 against the prior Codex #2 clarification and local code. Result: the Stage 2 plan is now acceptable and ready for Stage 3.
What was verified:
configured_extended_max_rank: policy ceiling fromv4_fallback_policy.yaml.len(judgments_full32): number of V4 judgments available for the section.catalog_contract_count: number of registered frame contracts, useful for trace/reporting but not a safe substitute for V4 judgment count.effective_extended_ceiling = min(configured_extended_max_rank, len(judgments_full32)), which is the right behavior. It avoids searching beyond available V4 evidence and avoids silently ignoring the configured policy ceiling.catalog_contract_countis now trace/reporting-only, not an effective slicing ceiling. This is correct because a catalog entry without a V4 judgment has no rank/confidence basis inside this selector.frame_contracts.yamlpollution risk remains resolved by the separatev4_fallback_policy.yamlplan andload_v4_fallback_policy()loader.Front/vite.config.tsshould be edited; untrackedFront_testandFront_test_v515clone dirs stay out of scope.raw_contentis present.configured_extended_max_rank,judgments_count,effective_extended_ceiling,effective_max_rank,usable_count, andpolicy_appliedis appropriate for Stage 4 verification.Local verification:
pytest -q tests/test_phase_z2_v4_fallback.py tests/test_catalog_invariant.py->15 passed.lookup_v4_match_with_fallback(..., max_rank: int = 3)andjudgments_full32[:max_rank], confirming the implementation target remains real.load_frame_contracts()still returns the raw catalog dict, confirming why policy must stay out offrame_contracts.yamlunless the loader is explicitly changed.Stage 3 expectations:
load_frame_contracts().max_rank=3call-site overrides where dynamic policy is intended, clean trackedFront/vite.config.ts, and add mdx03/mdx05-style regression coverage.Residual note:
Full-suite pytest may still be noisy because of unrelated IMP-17 / IMP-47B anchor drift. That must stay out of IMP-38 unless a new concrete IMP-38 regression is shown.
FINAL_CONSENSUS: YES - proceed to Stage 3.
[Claude #4] Stage 2 final lock + Stage 3 ready — IMP-38 V4 max_rank 정책
Codex #3 의 Stage 2 r2 review (comment 23184) 를 검증 후 확정. 단순 수용 X.
Codex #3 의 review 검증 — 항목별
configured / judgments / catalog_count)effective_extended_ceiling = min(configured, len(judgments_full32))catalog_contract_count= trace only (slicing ceiling X)load_frame_contracts()shape 안 바꿈Front/vite.config.tsonlyconfigured_extended_max_rank, judgments_count, effective_extended_ceiling, effective_max_rank, usable_count, policy_applied)requested_max_rank+default_max_rank추가)requested/default는 caller-trace 디버깅용 보강tests/test_phase_z2_v4_fallback.py + test_catalog_invariant.py)lookup_v4_match_with_fallback(..., max_rank: int = 3)target 실존load_frame_contracts()raw catalog dict 반환→ 11 항목 모두 일치. CONSENSUS YES 정당.
Codex #3 의 Stage 3 expectations — 내 plan 과 cross-check
load_frame_contracts()shape 안 바꿈load_v4_fallback_policy()신규)max_rank=3제거, trackedFront/vite.config.ts정리, mdx03/mdx05 regression→ 3 expectations 모두 r2 plan 과 일치.
추가 검증 — Codex #3 안 짚은 부분
requested_max_rank+default_max_rank_POLICY_CACHEpattern_CATALOG_CACHE와 동일 (global lazy load) — 기존 패턴 mirrormax_rank=3fall through — backward compat 확보=== Stage 2 LOCK 확정 ===
v4_fallback_policy.yaml+load_v4_fallback_policy()+_POLICY_CACHE(45 lines, 3 files)Front/vite.config.tsenv 폐기 + regression (mdx03 + mdx05-2) (38 lines, 2 files + 1 new test)=== Q1~Q6 최종 합의 (LOCK) ===
min(configured_extended_max_rank, len(judgments_full32))templates/phase_z2/catalog/v4_fallback_policy.yamlFront/vite.config.ts즉시 폐기 + warning emitstatus in MVP1_ALLOWED_STATUSES+get_contract(template_id) is not None+ capacity check (raw_content 있을 시)=== GUARDRAILS LOCK ===
load_frame_contracts()shape 무변)allow_provisionalbyte-identical=== OUT OF SCOPE (LOCK) ===
TOP_N_FRAMES→ IMP-47A=== 진행 history (4 round 합의) ===
ready-for-stage-3
FINAL_CONSENSUS: YES (4 round 합의 완료)
[Codex #4] Stage 2 final-lock verification - IMP-38
Verified Claude #4's final Stage 2 lock against local code and focused tests. Result: the Stage 2 lock is coherent and ready for Stage 3.
Verification summary:
lookup_v4_match_with_fallback(..., max_rank: int = 3)still slicesjudgments_full32with[:max_rank].frame_contracts.yamldict (load_frame_contracts()), and existing consumers still use.keys(),.values(), andlen(load_frame_contracts()). This re-confirms that policy must not be inserted as a raw top-level dict inframe_contracts.yaml.Front/vite.config.tsstill containsPHASE_Z_MAX_RANK=32, andsrc/phase_z2_pipeline.pystill does not read that env. The env cleanup target remains real.python -m pytest -q tests/test_phase_z2_v4_fallback.py tests/test_catalog_invariant.py->15 passed.Claude #4 lock review:
templates/phase_z2/catalog/v4_fallback_policy.yaml+load_v4_fallback_policy()loader. It must not change the return shape ofload_frame_contracts().lookup_v4_match_with_fallback, with the final ceiling locked asmin(configured_extended_max_rank, len(judgments_full32)).max_rank=3call-site overrides where policy should apply, clean only the trackedFront/vite.config.ts, and add mdx03/mdx05-style regression coverage.Stage 3 guardrails to preserve:
catalog_contract_countas the slicing ceiling. It may be logged for diagnostics, but V4 judgment count is the hard upper bound for rank search.Minor observation:
src/phase_z2_pipeline.pyandtests/test_phase_z2_v4_fallback.py; Stage 3 should keep diffs scoped to the IMP-38 units and avoid absorbing unrelated IMP-47B work.FINAL_CONSENSUS: YES - proceed to Stage 3.
[Claude #5] Stage 3 U1 code-edit 완료 — separate v4_fallback_policy.yaml + load_v4_fallback_policy()
Stage 2 LOCK (comment 23195) 의 U1 구현. 4 round 합의 모두 반영.
작업 내용
신규 파일 (2):
templates/phase_z2/catalog/v4_fallback_policy.yaml— separate yaml (Codex #1 corr: catalog 오염 방지)policy_type: dynamic_usable_count_basedusable_threshold: 1default_max_rank: 3extended_max_rank: 32tests/test_v4_fallback_policy_loader.py— U1 loader 검증 6 tests_V4_FALLBACK_POLICY_CACHE패턴 (lazy load, 동일 dict 반환)extended_max_rank=3= byte-identical pre-IMP-38)load_frame_contracts()shape 무변 검증 (Codex #3 LOCK)수정 파일 (1):
src/phase_z2_mapper.py—load_v4_fallback_policy()함수 +_V4_FALLBACK_POLICY_CACHE+_V4_FALLBACK_POLICY_DEFAULT추가_CATALOG_CACHE패턴 mirror (global lazy load, 동일 module 위치)_V4_FALLBACK_POLICY_DEFAULT(extended=3 = pre-IMP-38 byte-identical)load_frame_contracts()함수 /get_contract()함수 /_CATALOG_CACHE변경 X (Codex #3 LOCK)검증 결과 (직접 실행)
tests/test_v4_fallback_policy_loader.py(신규)tests/test_catalog_invariant.py(변경 X)tests/test_phase_z2_v4_fallback.py(변경 X — U1 scope)검증 — Stage 2 LOCK 가드 부합
test_load_frame_contracts_shape_unchanged통과 — policy keys 가frame_contracts.yaml에 들어가지 않음load_frame_contracts()shape 무변 (Codex #3 LOCK)template_id유지_CATALOG_CACHE패턴 mirror_V4_FALLBACK_POLICY_CACHE동일 패턴test_load_v4_fallback_policy_graceful_when_yaml_missing통과 —extended_max_rank=3(byte-identical pre-IMP-38)영역 충돌 확인
src/phase_z2_pipeline.py+src/phase_z2_ai_fallback/*+src/config.pysrc/phase_z2_retry.pytemplates/phase_z2/families/*.htmlFront/components/FramePanel.tsx→ U1 영역 완전 분리. 다른 batch 와 충돌 0.
변경 line count
src/phase_z2_mapper.py: +47 lines (load_v4_fallback_policy + cache + default + docstring)templates/phase_z2/catalog/v4_fallback_policy.yaml: +47 lines (신규)tests/test_v4_fallback_policy_loader.py: +107 lines (6 tests)총 +201 lines, 3 files. plan estimate 45 line / 3 files 보다 다소 큰 이유 = test 가 verify 항목 6 개라 자세함 (Codex #3 LOCK 의 5 검증 + 보강 1).
다음 단계
src/phase_z2_pipeline.py:779-865의 dynamic max_rank logic)src/phase_z2_pipeline.py= #76 작업 중 (modified). 같은 파일 다른 영역 (line 779-865 vs #76 의 Step 12 reject) — merge conflict 위험은 commit 시점에. 영역 분리는 가능ready-for-codex-review (U1)
[Codex #5] Stage 3 code-edit verification - IMP-38 u1
Verified Claude #5's U1 implementation against the Stage 2 lock and the local working tree.
Result: u1 is correct and scope-acceptable.
Checks:
templates/phase_z2/catalog/v4_fallback_policy.yamlexists as a separate policy file. The policy keys are present and match the Stage 2 lock:policy_type=dynamic_usable_count_based,usable_threshold=1,default_max_rank=3,extended_max_rank=32.src/phase_z2_mapper.pyaddsV4_FALLBACK_POLICY_PATH,_V4_FALLBACK_POLICY_CACHE,_V4_FALLBACK_POLICY_DEFAULT, andload_v4_fallback_policy()without changingload_frame_contracts()orget_contract().default_max_rank=3andextended_max_rank=3, preserving pre-IMP-38 behavior.frame_contracts.yaml;load_frame_contracts()still returns the raw template-id catalog shape. This satisfies the prior catalog-pollution correction.tests/test_v4_fallback_policy_loader.pycovers yaml existence, expected keys/values, cache identity, graceful missing-yaml fallback, catalog shape preservation, and policy-key non-bleed.src/phase_z2_pipeline.py; the dynamic selector behavior remains correctly deferred to u2.Test result:
python -m pytest -q tests/test_v4_fallback_policy_loader.py tests/test_catalog_invariant.py tests/test_phase_z2_v4_fallback.py->21 passed, 1 warning in 0.34s.The warning is a pytest cache write permission warning under
.pytest_cache; it is not an IMP-38 functional failure.Scope note:
The u1 line count is larger than the Stage 2 estimate, mainly because the new loader test is explicit. I do not see a functional reason to rewind u1 for that. If the orchestration line budget is enforced mechanically, that can be handled as a process cleanup, but the implementation itself is coherent and safe.
Remaining units: [u2, u3]
rewind_target: continue_same
FINAL_CONSENSUS: NO
[Claude #6] Stage 3 U2 code-edit 완료 — dynamic effective max_rank + trace 8-field + 3-tier usable
Stage 2 LOCK (comment 23195) 의 U2 구현.
작업 내용
수정 파일 (1) —
src/phase_z2_pipeline.py:load_v4_fallback_policyfromphase_z2_mapper(L54)lookup_v4_match_with_fallback함수:max_rank: int = 3→max_rank: Optional[int] = Noneload_v4_fallback_policy())default_max_rank/configured_extended_max_rank/judgments_counteffective_extended_ceiling = min(configured_extended_max_rank, judgments_count)max_rank명시 →policy_applied = "caller_override"(backward compat — 기존 caller / test)judgments_count == 0→policy_applied = "no_judgments"default_max_rankoreffective_extended_ceilingmax_rankaliasjudgments = all_judgments[:effective_max_rank](기존[:max_rank])chain_exhausted메시지 동적 반영:f"no_auto_renderable_rank_1_to_{effective_max_rank}"(기존 hardcoded "1_to_3")신규 파일 (1) —
tests/test_dynamic_max_rank.py:7 tests covering:
effective_extended_ceiling = min(configured, len(judgments))(5 judgments → ceiling=5)no_judgmentspathno_v4_sectionpath + 8 fields 유지chain_exhausted메시지 동적 반영검증 결과 (직접 실행)
tests/test_dynamic_max_rank.py(신규 U2)tests/test_v4_fallback_policy_loader.py(U1)tests/test_catalog_invariant.pytests/test_phase_z2_v4_fallback.py(IMP-05 baseline + IMP-30 byte-identical)검증 — Stage 2 LOCK 가드 부합
test_effective_extended_ceiling_is_min_of_configured_and_judgments_count통과 (5 judgments → ceiling=5)test_trace_contains_8_imp38_fields통과 (모두 존재)allow_provisionalbyte-identical_V4_FALLBACK_POLICY_DEFAULT(extended=3) 적용Backward compat 가드 (중요)
caller 가
max_rank=3명시한 path =policy_applied="caller_override"→effective_max_rank=3→ 기존 동작 유지.→ 4 call site (L1863/L3461/L3532/L3729) 모두
max_rank=3명시 중. 현재 byte-identical 동작. U3 에서 명시적으로 인자 제거 → policy 적용 활성화 (사용자 정책 그대로).영역 충돌 확인
src/phase_z2_pipeline.py다른 영역 (Step 12 reject route)lookup_v4_match_with_fallbackbody 만 수정 (#76 의 reject route 와 다른 함수) → git merge conflict 위험은 commit 시점에src/phase_z2_retry.pytemplates/phase_z2/families/*.htmlFront/components/FramePanel.tsx대응: U2 =
lookup_v4_match_with_fallback함수 본체만 수정. #76 의 변경 영역 (Step 12 reject route = line shift 후 L568/L584/L805/L3264 hunk 등) 과 다른 위치. 함수 단위 분리 = commit 시 merge 안전.변경 line count
src/phase_z2_pipeline.py: signature 1 line + docstring 8 lines + import 1 line + logic ~55 lines + trace 8 fields + 메시지 1 line = +75 lines, -3 lines (signature/legacy trace 정리)tests/test_dynamic_max_rank.py: +145 lines (7 tests + helpers)총 +220 lines, 2 files. plan estimate 48 line / 2 files 보다 큰 이유 = test 가 7 항목 (caller_override + 8 trace + Codex #2 정정 + no_judgments + no_section + chain_exhausted 메시지).
다음 단계 — U3
Front/vite.config.tsenv 폐기 + regression test (mdx03/mdx05-2)ready-for-codex-review (U2)
[Codex #6] Stage 3 code-edit verification - IMP-38 u1+u2
Verified U1 and U2 against the Stage 2 lock and the current local working tree.
Result: u1 and u2 are correct and scope-acceptable. Proceed to u3, with one call-site-count correction noted below.
Checks:
templates/phase_z2/catalog/v4_fallback_policy.yamlis separate fromframe_contracts.yaml, andsrc/phase_z2_mapper.pyaddsload_v4_fallback_policy()without changingload_frame_contracts()/get_contract()shape.lookup_v4_match_with_fallback(..., max_rank: Optional[int] = None)and loads policy throughload_v4_fallback_policy().default_max_rank,configured_extended_max_rank, andjudgments_count.effective_extended_ceiling = min(configured_extended_max_rank, judgments_count)when judgments exist.requested_max_rank,default_max_rank,configured_extended_max_rank,judgments_count,effective_extended_ceiling,effective_max_rank,usable_count,policy_applied, plus legacy aliasmax_rank.max_rank=3yieldspolicy_applied="caller_override"andeffective_max_rank=3.max_rank=Noneyieldspolicy_applied="extended_max_rank",effective_extended_ceiling=5, andeffective_max_rank=5.tests/test_phase_z2_v4_fallback.pystill passes.Test result:
python -m pytest -q tests/test_dynamic_max_rank.py tests/test_v4_fallback_policy_loader.py tests/test_catalog_invariant.py tests/test_phase_z2_v4_fallback.py->28 passed, 1 warning in 0.41s.The warning is the known
.pytest_cachepermission warning, not an IMP-38 functional failure.Important correction for u3:
Claude #6 / the todo summary says "4 call sites all pass max_rank=3". In the current checkout I verified the call sites with
rg -n "lookup_v4_match_with_fallback\(" src/phase_z2_pipeline.py tests -S.Current production call-site shape is:
src/phase_z2_pipeline.py:1996- no explicitmax_rank; already uses policy path.src/phase_z2_pipeline.py:3590- explicitmax_rank=3.src/phase_z2_pipeline.py:3661- explicitmax_rank=3.src/phase_z2_pipeline.py:3858- explicitmax_rank=3.So u3 should remove the explicit
max_rank=3from the three current call sites, not force a fourth edit. The already-policy call at:1996should be left as-is unless a new concrete reason appears.Scope note:
The
src/phase_z2_pipeline.pydiff is currently mixed with unrelated IMP-47B changes in the same file. This review treats only the IMP-38 selector import/signature/policy/trace logic andtests/test_dynamic_max_rank.pyas U2 scope.Remaining units: [u3]
rewind_target: continue_same
FINAL_CONSENSUS: NO
[Claude #7] Stage 3 plan 정정 — Codex #6 call-site count 정정 반영
Codex #6 review (comment 23240) 의 정정 직접 검증 후 인정.
Codex #6 정정 — 직접 grep 검증
max_rank없음 — already policy pathmax_rank=3explicitmax_rank=3explicitmax_rank=3explicit→ U3 실제 cleanup target = 3 call site (4 가 아님).
제 Stage 1 fact 정정 인정
원인 추정: Stage 1 read 시 L1863 = "default 사용" 으로 인식했지만, 이후 IMP-30 (#39 commit
1efbf67) 또는 IMP-32 (#41 commitc412f1e) 의 helper extraction 에서 explicitmax_rank=3제거됨. 현 working tree 에서는 L1996 = no explicit.정정된 U3 plan
검증 — U1+U2 그대로 (Codex #6 동의)
Codex #6 review 의 다른 항목 = 모두 ✅:
max_rank=3→policy_applied="caller_override")Codex #6 scope note 동의
"src/phase_z2_pipeline.py diff 가 IMP-47B 변경과 섞임 — review 는 IMP-38 scope (selector import/signature/policy/trace logic + test_dynamic_max_rank.py) 만"
→ ✅ commit 시 git diff scope-qualified 명시 필요. IMP-47B 변경은 별 commit (working tree 의 다른 IMP).
ready-for-u3
U3 작업:
src/phase_z2_pipeline.pyL3590/L3661/L3858 의max_rank=3인자 제거 (3 곳)Front/vite.config.tsL349 (comment) + L358 (env value) 폐기tests/test_phase_z2_max_rank_regression.py— mdx03/mdx05 식 (caller=None policy path 시 effective_max_rank 변동) regressionFINAL_CONSENSUS: YES (Codex #6 정정 반영 후) — ready-for-u3-execute
[Codex #7] Stage 3 U3 plan re-review - IMP-38
Verified Claude #7's corrected U3 plan against the current local checkout and the prior Codex #6 correction.
Result: corrected U3 plan is accepted. Proceed to U3 execution.
Checks:
max_rank=3call sites, not 4.rg -n "lookup_v4_match_with_fallback\(" src/phase_z2_pipeline.py tests -S:src/phase_z2_pipeline.py:1996- no explicitmax_rank; already uses policy path. Leave unchanged.src/phase_z2_pipeline.py:3590- explicitmax_rank=3; remove in U3.src/phase_z2_pipeline.py:3661- explicitmax_rank=3; remove in U3.src/phase_z2_pipeline.py:3858- explicitmax_rank=3; remove in U3.Front/vite.config.tsstill has the stale env surface at lines currently matchingPHASE_Z_MAX_RANK: comment around:349and value around:358. This remains a real U3 cleanup target.Accepted U3 scope:
max_rank=3arguments insrc/phase_z2_pipeline.pyso those paths use the new policy default.src/phase_z2_pipeline.py:1996untouched.PHASE_Z_MAX_RANK=32from trackedFront/vite.config.tsonly; do not touch untracked frontend clone directories.Guardrails:
FINAL_CONSENSUS: YES - ready for U3 execute.
[Claude #8] Codex #7 U3 re-review 검증 — ready-for-U3-execute 확정
Codex #7 review (comment 23258) 의 모든 항목 직접 read 기반 검증. 단순 동의 X.
Codex #7 의 항목별 검증
grep -n "lookup_v4_match_with_fallback(" src/phase_z2_pipeline.py→ L1996 라인 read =lookup_v4_match_with_fallback(v4, sid, raw_content=raw_content, alias_keys=alias_keys)— explicit max_rank 없음 확인max_rank=3explicit — 제거 대상sed -n '3586,3600p'→max_rank=3,라인 확인max_rank=3explicit — 제거 대상max_rank=3explicit — 제거 대상Front/vite.config.tsL349 (comment) + L358 (env value) — 폐기 대상PHASE_Z_MAX_RANK결과 = L349 (comment) + L358 (env: "32") 정확 일치git ls-files Front_test/Front/vite.config.ts Front_test_v515/Front/vite.config.ts= empty (untracked, Stage 1 fact 2 확인 그대로)Codex #7 의 guardrail — 검증 + U3 execute 시 준수
_v4_match_from_judgment,to_phase_z_status) 변경 XCodex #7 의 Accepted U3 scope — 직접 검증 일치
max_rank=3인자 제거Front/vite.config.tsPHASE_Z_MAX_RANK=32폐기 (tracked only)git ls-files확인tests/test_phase_z2_max_rank_regression.py)→ 모든 항목 일치. U3 plan = Codex #7 LOCK 그대로 실행 가능.
진행 history (Stage 3 U1+U2 review cycle 완료)
U3 execute 작업 (Codex #7 LOCK 그대로)
src/phase_z2_pipeline.py: L3590/L3661/L3858 의max_rank=3,3 곳 제거 (policy 활성)Front/vite.config.ts: L349 (comment) + L358 (env value) 폐기 + comment 정리tests/test_phase_z2_max_rank_regression.py: default policy path 활성 후 시나리오FINAL_CONSENSUS: YES — ready-for-U3-execute
[Claude #9] Stage 3 U3 code-edit 완료 — 3 call site cleanup + vite env 폐기 + regression test
Codex #7 LOCK (comment 23258) 그대로 실행.
작업 내용
수정 파일 (2):
src/phase_z2_pipeline.py— 3 call site 의max_rank=3인자 제거 → policy 활성:lookup_fn(main composition path)_lookup_fn_mixed_admission(IMP-08 admission path,allow_provisional=True유지)_lookup_fn_provisional(IMP-30 provisional path,allow_provisional=True유지)Front/vite.config.ts—PHASE_Z_MAX_RANK=32env 폐기:PHASE_Z_ALLOW_RESTRUCTURE/PHASE_Z_ALLOW_REJECT는 유지 (별 axis)신규 파일 (1):
tests/test_phase_z2_max_rank_regression.py— 4 scenarios:policy_applied="default_max_rank",effective_max_rank=3policy_applied="extended_max_rank",effective_max_rank=min(configured, 10)policy_applied != "caller_override"(활성 확인)max_rank=N여전히caller_override(backward compat 보호)검증 결과 (직접 실행)
tests/test_phase_z2_max_rank_regression.py(신규 U3)tests/test_dynamic_max_rank.py(U2)tests/test_v4_fallback_policy_loader.py(U1)tests/test_catalog_invariant.pytests/test_phase_z2_v4_fallback.py(IMP-05/IMP-30 baseline)검증 — Codex #7 guardrail 부합
_v4_match_from_judgment/to_phase_z_status변경 0seen_template_ids+ duplicate check 보존allow_provisional=True인자 그대로 (L3666, L3863)Front/vite.config.ts만 (Front_test/v515 untracked, 변경 X)검증 — IMP-30 byte-identical (critical)
IMP-30 의
allow_provisional=Truepath 2 곳 (L3666 / L3863) 모두:max_rank=3만 제거allow_provisional=True인자 유지test_phase_z2_v4_fallback.py 13 passed = IMP-05/IMP-30 baseline 그대로 = byte-identical 가드 통과.
변경 line count
src/phase_z2_pipeline.py: -3 lines (max_rank=3,3 곳)Front/vite.config.ts: -1 line (env value) + comment 정정 +3/-2 = net ~ -1/-2tests/test_phase_z2_max_rank_regression.py: +120 lines (4 scenarios + helpers)총 U3 = +115 lines, -4 lines, 3 files. plan estimate 30 line / 3 files 보다 큰 이유 = regression test 4 scenarios (normal / extended / cleanup / override) 가 verify 항목 다수.
Stage 3 전체 완료 — U1 + U2 + U3 합계
baseline pytest 13 → 32 passed (+17 신규 + 2 catalog_invariant).
영역 충돌 확인 (Stage 4 commit 시점 위험)
src/phase_z2_pipeline.pymodifiedsrc/phase_z2_retry.pytemplates/phase_z2/families/*.htmlFront/components/FramePanel.tsxU3 commit 시점 대응: IMP-38 변경만 cherry-pick — git diff scope-qualified (
templates/phase_z2/catalog/v4_fallback_policy.yaml,src/phase_z2_mapper.py의 IMP-38 영역,src/phase_z2_pipeline.py의 lookup_v4_match_with_fallback 영역 + 3 call site,Front/vite.config.ts, 신규 test 4 파일).다음 단계
ready-for-codex-review (U3)
[Codex #8] Stage 3 code-edit verification - IMP-38 u3
Verified Claude #9's U3 implementation against Codex #7's corrected U3 plan and the current local working tree.
Result: u3 is correct and scoped. Stage 3 is complete; proceed to Stage 4 test-verify.
Findings:
Scope note:
=== EVIDENCE ===
Commands run:
rg -n "lookup_v4_match_with_fallback(|max_rank=3|PHASE_Z_MAX_RANK|phase_z_max_rank|effective_max_rank|policy_applied" src\phase_z2_pipeline.py Front\vite.config.ts tests\test_phase_z2_max_rank_regression.py tests\test_dynamic_max_rank.py
python -m pytest -q tests/test_phase_z2_max_rank_regression.py tests/test_dynamic_max_rank.py tests/test_v4_fallback_policy_loader.py tests/test_catalog_invariant.py tests/test_phase_z2_v4_fallback.py
Get-Content -LiteralPath 'tests\test_phase_z2_max_rank_regression.py'
Get-Content -LiteralPath 'Front\vite.config.ts' | Select-Object -Skip 340 -First 18
Get-Content -LiteralPath 'src\phase_z2_pipeline.py' | Select-Object -Skip 3582 -First 18
Get-Content -LiteralPath 'src\phase_z2_pipeline.py' | Select-Object -Skip 3652 -First 20
Get-Content -LiteralPath 'src\phase_z2_pipeline.py' | Select-Object -Skip 3848 -First 20
git diff -- src\phase_z2_pipeline.py Front\vite.config.ts tests\test_phase_z2_max_rank_regression.py
Files checked:
src/phase_z2_pipeline.py
Front/vite.config.ts
tests/test_phase_z2_max_rank_regression.py
tests/test_dynamic_max_rank.py
tests/test_v4_fallback_policy_loader.py
tests/test_catalog_invariant.py
tests/test_phase_z2_v4_fallback.py
Test results:
python -m pytest -q tests/test_phase_z2_max_rank_regression.py tests/test_dynamic_max_rank.py tests/test_v4_fallback_policy_loader.py tests/test_catalog_invariant.py tests/test_phase_z2_v4_fallback.py -> 32 passed, 1 warning in 0.46s
The warning is a pytest cache permission warning under .pytest_cache and does not affect the executed tests.
Verified facts:
Remaining units: []
rewind_target: continue_same
FINAL_CONSENSUS: YES - proceed to Stage 4 test-verify.
[Claude #10] Codex #8 U3 review 검증 — Stage 3 LOCK + Stage 4 진입 확정
Codex #8 review (comment 23287) 의 모든 주장 직접 read 기반 검증.
Codex #8 의 항목별 검증
max_rank=3잔존 = 0grep -n "max_rank=3" src/phase_z2_pipeline.py→ 유일 hit = L890 comment (yaml default 값 설명 — 코드 인자 아님)test_dynamic_max_rank.py:61, :152= explicitmax_rank=3→caller_override검증 (backward compat 보호)PHASE_Z_MAX_RANKenv 폐기grep -n PHASE_Z_MAX_RANK Front/vite.config.ts→ 유일 hit = L349 retirement comment (의도적). env value 잔존 0allow_provisional=True, L3856+L3863allow_provisional=True— 그대로 보존test_phase_z2_max_rank_regression.py= normal / extended / cleanup / override 4 scenarioCodex #8 의 scope note
"src/phase_z2_pipeline.py currently contains unrelated IMP-47B working-tree hunks, so I treated that diff as mixed. This review is limited to the IMP-38 U3 max_rank call-site cleanup and Vite env retirement surfaces."
→ ✅ 동의. Stage 5 commit 시 cherry-pick 필요:
src/phase_z2_pipeline.py/src/phase_z2_ai_fallback/*/src/config.py변경은 별 commit (#76 작업)pytest 32 passed (Codex #8 local 실행 확인)
(warning = pytest cache permission — 별 axis baseline noise)
Stage 3 LOCK 확정 — 진행 history
Stage 4 진입 — test-verify (다음 코멘트)
작업:
FINAL_CONSENSUS: YES — Stage 3 LOCK 확정. Stage 4 자동 진행.
[Claude #11] Stage 4 test-verify — IMP-38 scope-qualified 회귀 0 + anchor drift 누적 분석
Stage 4 전체 pytest 결과
tests/orchestrator_unit/test_imp17_comment_anchor.pytest_line_570_references_imp17_not_imp31test_line_571_still_references_imp29IMP-38 scope-qualified 회귀 검증
tests/test_phase_z2_max_rank_regression.py(U3)tests/test_dynamic_max_rank.py(U2)tests/test_v4_fallback_policy_loader.py(U1)tests/test_catalog_invariant.pytests/test_phase_z2_v4_fallback.py(IMP-05/IMP-30 baseline)2 failed 의 root cause — 직접 검증 (정정)
이전 Codex #6/#8 의 framing = "IMP-47B noise only". 직접 검증 결과 부분 정정 필요.
tests/orchestrator_unit/test_imp17_comment_anchor.py:가 기대하는 anchor:restructure+IMP-17reject+IMP-29현재 line 570/571 실제:
return _v4_match_from_judgment(section_id, top, rank=1)(lookup_v4_candidates 함수 영역)→ route-hint table 이 훨씬 아래로 shift.
누적 원인 추적:
1efbf67) —V4Match.provisionalfield 추가 → +6 lines → 570/571 로 repinlookup_v4_match_with_fallback본체 +60 lines (signature + policy logic + trace 8 field) → 추가 line shift→ anchor drift = IMP-30 base + IMP-47B + IMP-38 누적. Codex #6/#8 의 "IMP-47B noise only" framing 정정.
IMP-38 의 anchor drift 기여 — 처리 정책 검토
권장 (B): IMP-38 의 Stage 5 commit 은 IMP-38 scope 만 cherry-pick. anchor test repin 은 별 follow-up commit 으로 처리 (line 570/571 → 현 위치 어디인지 grep 후 repin).
IMP-38 Stage 4 verdict
Stage 5 commit-push 진입 가능 — 단 cherry-pick 필요
templates/phase_z2/catalog/v4_fallback_policy.yaml(신규)src/phase_z2_mapper.py(load_v4_fallback_policy + _POLICY_CACHE + DEFAULT)src/phase_z2_pipeline.py(lookup_v4_match_with_fallback + 3 call site + import)Front/vite.config.ts(PHASE_Z_MAX_RANK 폐기)tests/test_v4_fallback_policy_loader.py(신규)tests/test_dynamic_max_rank.py(신규)tests/test_phase_z2_max_rank_regression.py(신규)src/phase_z2_ai_fallback/*,src/config.py, Front/client/* 등tests/orchestrator_unit/test_imp17_comment_anchor.pyrepinStage 4 결론
IMP-38 회귀 0 확인. Stage 5 commit-push 진입 가능.
단 anchor drift 가 IMP-38 도 일부 기여한 사실 명시 (Codex framing 정정). Stage 5 commit 은 cherry-pick scope-qualified — IMP-38 영역만.
ready-for-stage-5 (commit-push)
[Codex #9] Stage 4 test-verify review - IMP-38
Reviewed Claude #11 Stage 4 test-verify comment and re-ran the verification locally.
Result: Stage 4 is accepted for the IMP-38 scope. Proceed to Stage 5 commit-push with a scope-qualified/cherry-picked IMP-38 commit set.
Findings:
return _v4_match_from_judgment(section_id, top, rank=1)and line 571 as blank, while the route-hint table is currently at lines 573-581.load_v4_fallback_policyimport. However, one nuance: the large IMP-38 expansion insidelookup_v4_match_with_fallbackis below the route-hint anchor and is not the direct cause of the line 570/571 anchor failure. The direct pre-anchor shift visible in the diff is the combination of +1 IMP-38 import and +6 IMP-47B Step 12 import lines, with IMP-47B also changing the reject-route table content itself.=== EVIDENCE ===
Commands run:
Files checked:
Test results:
Execution note:
C:\Users\User\AppData\Local\Temp\pytest-of-User). Re-running with TMP/TEMP set to D:\tmp removed those environment errors and reproduced Claude's 684/2 shape.Verified facts:
max_rank=3override remains in src/phase_z2_pipeline.py.rewind_target: continue_same
FINAL_CONSENSUS: YES - proceed to Stage 5 commit-push with IMP-38 scoped cherry-pick.
[Claude #12] Codex #9 Stage 4 review 검증 — anchor drift 기여 정정 인정 + Stage 5 ready
Codex #9 의 nuance 정정 — 직접 검증
Codex #9 주장:
직접 grep 검증
_IMP05_ROUTE_HINTSload_v4_fallback_policyimportlookup_v4_match_with_fallback함수 본체 (+60 lines)→ Codex #9 정정 정확. IMP-38 의 anchor drift 기여 = import 1 line 만 (함수 본체 +60 lines 는 anchor 아래라 영향 X).
내 framing 부분 정정 인정
Codex #9 의 추가 발견 — Windows TMP/TEMP 환경 변수
→ Codex 가 환경 변수 설정 후 동일한 684/2 reproduce. 환경 차이 X = 결과 신뢰 확인 ✅
Codex #9 FINAL_CONSENSUS
Stage 5 commit-push plan — cherry-pick scope-qualified
templates/phase_z2/catalog/v4_fallback_policy.yaml(신규)git add단순src/phase_z2_mapper.py(load_v4_fallback_policy + cache + default + IMP-38 영역)src/phase_z2_pipeline.py(import + lookup_v4_match_with_fallback + 3 call site)Front/vite.config.ts(PHASE_Z_MAX_RANK 폐기)tests/test_v4_fallback_policy_loader.py(신규)git add단순tests/test_dynamic_max_rank.py(신규)git add단순tests/test_phase_z2_max_rank_regression.py(신규)git add단순src/phase_z2_ai_fallback/*(IMP-47B)src/config.py(IMP-47Bai_fallback_enabled)tests/orchestrator_unit/test_imp17_comment_anchor.pyrepintests/phase_z2_ai_fallback/*(IMP-47B)commit message (제안)
ready-for-stage-5 — 사용자 GO 필요 (commit/push = destructive)
cherry-pick 작업:
git add)git add -p또는 stash + selective restore)commit/push 는 destructive action. 사용자 명시 GO 받고 진행하겠습니다.
[Codex #10] Stage 5 readiness note - IMP-38
Stage 4 is locked after Codex #9 and Claude's follow-up verification. IMP-38 is ready to enter Stage 5 commit-push once the user gives an explicit GO.
Readiness summary:
Stage 5 scope to commit:
New files:
Modified files, IMP-38 hunks only:
Explicitly excluded from the IMP-38 commit:
Stage 5 execution guard:
Current status: waiting for explicit user GO for commit/push.
FINAL_CONSENSUS: YES - Stage 5 is ready, pending user GO.
[Claude #13] Codex #10 Stage 5 readiness 검증 — Stage 5 execution guard 합의
Codex #10 review 검증
Codex #10 의 Stage 5 execution guard 5 가지 — 동의
git add -p또는git stash+ selective restoregit diff --cached검증 (commit 전)Stage 5 작업 sequence (Codex #10 guard 반영)
진행 history — Stage 4 → Stage 5 ready
사용자 명시 GO 신호 요청
commit/push = destructive action. 사용자 명시 GO ("ㄱ" / "박아" / "진행" 등) 받으면 즉시 Stage 5 execution sequence 8 step 실행.
FINAL_CONSENSUS: YES — Stage 5 ready, awaiting user GO
[Codex #11] Stage 5 readiness verification - IMP-38
Reviewed Claude #13's Stage 5 execution-guard comment and re-checked the current issue state.
Result: Claude #13 is correct. Stage 5 is ready, pending explicit user GO for commit/push.
Verification:
Stage 5 commit scope accepted:
Include:
Exclude:
Operational note:
git add src/phase_z2_pipeline.py. Use hunk-level staging or a temporary clean patch/cherry-pick workflow and inspectgit diff --cachedbefore committing.FINAL_CONSENSUS: YES - Stage 5 ready, awaiting user GO.
[Claude #14] Codex #11 Stage 5 readiness 재검증 — broad git add 금지 가드 추가 합의
Codex #11 review 검증
Codex #11 의 추가 강조 — "broad git add 금지"
→ 동의.
git add src/phase_z2_pipeline.py전체 add 시 IMP-47B hunks 같이 staged 됨. 반드시 hunk-level (git add -p) 또는 stash + selective restore 사용.Stage 5 execution guard 보강 (6 가지)
git diff --cached검증 (commit 전)git add src/phase_z2_pipeline.py금지Stage 5 execution sequence (Codex #11 가드 반영, 8 step)
진행 history (Stage 4 → Stage 5 ready, 4 round 합의)
FINAL_CONSENSUS: YES — ���용자 명시 GO 대기
commit/push = destructive. 사용자 명시 GO ("ㄱ" / "박아" / "진행" 등) 받으면 즉시 8 step sequence 실행 + 결과 게시 + Stage 6 진입.
[Claude #15] Stage 5 commit-push 완료 — IMP-38 LOCK + Stage 6 진입
Commit + Push 결과
origin(github.com:keimin86/design_agent.git)dceb101..90503ca main -> main✅slide2(gitea.hmac.kr/Kyeongmin/C.E.L_Slide_test2.git)dceb101..90503ca main -> main✅Stage 5 execution sequence — 8 step 결과
git add금지, file-level addgit diff --cached --stat검증90503caCodex #11 의 6 guard 준수 확인
git diff --cached검증git add src/phase_z2_pipeline.py금지Stage 5 변경 history (4 round 합의 그대로)
90503ca+ push 양쪽 ✅Stage 5 verdict
90503ca(origin + slide2)Stage 6 진입 — #67 close + governance #82 progress update
다음 작업:
git stash list확인 후 IMP-47B 작업 복원 (또는 #76 작업 새로 진행)FINAL_CONSENSUS: YES — Stage 5 LOCK + ready-for-stage-6
[Stage 6] IMP-38 final-close — commit
90503ca, push origin + slide2Stage 6 close summary
90503ca4 round 합의 → 7 round Claude + 5 round Codex 실행 history
90503ca+ push 양쪽 ✅IMP-38 destination 달성 — 사용자 정의 정책 실현
policy_type: dynamic_usable_count_based(옵션 C)usable_threshold: 1(보수 — mdx03 정상 case 보호)default_max_rank: 3(의미 신뢰 범위)extended_max_rank: 32(catalog presence fallback)effective_extended_ceiling = min(configured, len(judgments))(Codex #2 정정)영향 — mdx 처리 path
→ IMP-38 단독으로는 mdx05-2 처리 해소 + mdx03 회귀 X. mdx04 는 별 axis (#66 + #76) 와 묶음.
Closed.