feat(#67): IMP-38 V4 max_rank policy formalization (u1~u3, 4 round consensus)
- u1: separate templates/phase_z2/catalog/v4_fallback_policy.yaml + load_v4_fallback_policy() loader (catalog pollution prevention — Codex #1 correction) - u2: dynamic effective max_rank in lookup_v4_match_with_fallback (3-variable ceiling min, Codex #2 correction: min(configured, len(judgments_full32))) + 3-tier usable predicate (status + catalog + optional capacity) + trace 8 fields (requested/default/configured_extended/ judgments_count/effective_extended_ceiling/effective_max_rank/usable_count/policy_applied) - u3: 2 production call site cleanup (max_rank=3 removed, HEAD baseline) + tracked Front/vite.config.ts PHASE_Z_MAX_RANK env retired + 4 regression scenarios verified: 32 passed (IMP-38 focused scope) — IMP-05 L4 dedup / L2 schema preserved, IMP-30 allow_provisional byte-identical, caller_override backward compat (tests) Stage cycle (#67, 7 round Claude + 5 round Codex): - Stage 1: Claude #1 -> Codex #1 YES + 5 corrections - Stage 2 r1+r2: Claude #2-#4 -> Codex #2 Q2 -> Codex #3 YES (4 round consensus LOCK 23195) - Stage 3 U1+U2+U3: Claude #5-#9 -> Codex #6 NO 4to3 correction -> Codex #7 YES -> Codex #8 YES - Stage 4: Claude #11 -> Codex #9 (anchor attribution nuance) -> Codex #10 readiness -> Codex #11 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,7 @@ import yaml
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
CATALOG_PATH = PROJECT_ROOT / "templates" / "phase_z2" / "catalog" / "frame_contracts.yaml"
|
||||
V4_FALLBACK_POLICY_PATH = PROJECT_ROOT / "templates" / "phase_z2" / "catalog" / "v4_fallback_policy.yaml"
|
||||
|
||||
|
||||
class FitError(Exception):
|
||||
@@ -57,6 +58,44 @@ def get_contract(template_id: str) -> dict | None:
|
||||
return load_frame_contracts().get(template_id)
|
||||
|
||||
|
||||
# ─── V4 fallback policy loading (IMP-38) ──────────────────────────
|
||||
|
||||
_V4_FALLBACK_POLICY_CACHE: dict | None = None
|
||||
|
||||
_V4_FALLBACK_POLICY_DEFAULT: dict = {
|
||||
"policy_type": "static",
|
||||
"usable_threshold": 1,
|
||||
"default_max_rank": 3,
|
||||
"extended_max_rank": 3, # graceful: yaml 없을 시 확장 X (byte-identical to pre-IMP-38)
|
||||
}
|
||||
|
||||
|
||||
def load_v4_fallback_policy() -> dict:
|
||||
"""IMP-38 V4 fallback policy loader (separate yaml, catalog 오염 방지).
|
||||
|
||||
Returns dict with keys: policy_type, usable_threshold, default_max_rank, extended_max_rank.
|
||||
|
||||
Codex #1 권장: frame_contracts.yaml top-level 오염 회피 (별 yaml).
|
||||
Codex #3 LOCK: load_frame_contracts() shape 변경 X (이 함수는 별 cache).
|
||||
|
||||
Graceful fallback:
|
||||
yaml 파일 없을 시 → _V4_FALLBACK_POLICY_DEFAULT (default_max_rank=3, extended=3)
|
||||
→ backward compat byte-identical to pre-IMP-38 behavior.
|
||||
|
||||
Returns:
|
||||
dict — 정책 키 (정책 yaml 의 superset 가능, 알 수 없는 키는 무시 권장).
|
||||
"""
|
||||
global _V4_FALLBACK_POLICY_CACHE
|
||||
if _V4_FALLBACK_POLICY_CACHE is None:
|
||||
if V4_FALLBACK_POLICY_PATH.exists():
|
||||
loaded = yaml.safe_load(V4_FALLBACK_POLICY_PATH.read_text(encoding="utf-8")) or {}
|
||||
# merge with default (yaml 키 부분 누락 시 default 로 fall through)
|
||||
_V4_FALLBACK_POLICY_CACHE = {**_V4_FALLBACK_POLICY_DEFAULT, **loaded}
|
||||
else:
|
||||
_V4_FALLBACK_POLICY_CACHE = dict(_V4_FALLBACK_POLICY_DEFAULT)
|
||||
return _V4_FALLBACK_POLICY_CACHE
|
||||
|
||||
|
||||
# ─── Source-shape splitters ──────────────────────────────────────
|
||||
|
||||
def _split_top_bullets(content: str) -> list[tuple[str, list[str]]]:
|
||||
|
||||
Reference in New Issue
Block a user