feat(#68): IMP-39 u1~u8 ranking_sort_policy single-source + backend↔frontend label-priority mirror
Some checks failed
Multi-MDX Regression (IMP-91) / multi-mdx-regression (push) Failing after 23s

u1: templates/phase_z2/catalog/ranking_sort_policy.yaml — single-source policy
    (label_priority asc {use_as_is:0, light_edit:1, restructure:2, reject:3}
    + confidence desc + v4_rank asc tie-break).
u2: src/phase_z2_pipeline.py — apply_ranking_sort helper + lookup_v4_match_with_fallback
    applies policy AFTER IMP-38 raw-window selection (raw default_window + usable_count
    preserved on RAW all_judgments).
u3: src/phase_z2_pipeline.py — _build_application_plan_unit forwards ranking_sort_policy
    + sorted_candidate_evidence into Step 9 payload.
u4: Front/client/src/services/designAgentApi.ts — frame_candidates builder reads
    unit.sorted_candidate_evidence + unit.ranking_sort_policy first; local LABEL_PRIORITY
    retained only on warn-fallback path.
u5: tests/test_ranking_sort_policy.py — pure permutation coverage (sample-agnostic).
u6: tests/phase_z2/test_label_priority_synthetic.py + fixtures/ranking_sort_policy/
    synthetic_divergence.yaml — low-conf use_as_is behind high-conf restructure.
u7: tests/phase_z2/test_imp39_mdx04_env_toggle_e2e.py — samples/mdx_batch/04.mdx with
    AI_FALLBACK_ENABLED=off; backend selected_v4_rank == frontend frame_candidates[0].
u8: tests/phase_z2/test_imp39_corpus_audit.py — real corpus sweep over
    tests/matching/v4_full32_result.yaml (10 MDX sections); section IDs loaded
    dynamically (RULE 0 / RULE 7 sample-agnostic).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-24 17:12:07 +09:00
parent 2e3747c5ab
commit 028042aaa9
8 changed files with 1536 additions and 12 deletions

View File

@@ -0,0 +1,50 @@
# IMP-39 single-source ranking sort policy — backend ↔ frontend mirror.
#
# 도입 배경 (issue #68):
# Backend `lookup_v4_match_with_fallback` 는 V4 raw confidence-desc 순서로
# first-eligible 선택 (label_priority 무시). Frontend `designAgentApi.ts` 는
# 동일 source 를 (label_priority asc, confidence desc) 로 재정렬 후 slice.
# 결과: 낮은-confidence 높은-priority label 이 raw 상 뒤에 있을 때
# backend "rank 1 selected" ≠ frontend `frame_candidates[0]` divergence.
#
# 정책 결정 (Stage 1~2 LOCK, 4 round 합의):
# - 단일 source 위치 = 본 yaml (catalog hot-reload + frontend mirror 가능)
# - frame_contracts.yaml / v4_fallback_policy.yaml 오염 회피 (분리 파일)
# - 정렬 axes = (label_priority asc, confidence desc, v4_rank asc)
# - tie-break = 원본 v4_rank 보존 (frontend LABEL_PRIORITY 와 1:1)
#
# 적용 path:
# - backend: src/phase_z2_pipeline.py `apply_ranking_sort` (helper, u1)
# + `lookup_v4_match_with_fallback` selector loop (u2)
# + `_build_application_plan_unit` Step 9 payload (u3)
# - frontend: Front/client/src/services/designAgentApi.ts (u4)
# → unit.ranking_sort_policy + unit.sorted_candidate_evidence 우선 read
# → local LABEL_PRIORITY 는 warn-fallback only
policy_type: deterministic_label_priority_then_confidence
# label_priority:
# lower value = higher priority (use_as_is 가 첫 후보)
# sort key = (label_priority asc, confidence desc, v4_rank asc)
label_priority:
use_as_is: 0
light_edit: 1
restructure: 2
reject: 3
# unknown_label_priority:
# label 이 위 매트릭스에 없을 시 부여되는 우선순위 (최하위 push).
# frontend `LABEL_PRIORITY[label] ?? 99` 와 1:1.
unknown_label_priority: 99
# tie_break_axes:
# 동일 label_priority 시 적용 순서 — frontend mirror 와 1:1.
# confidence_desc: 큰 confidence 가 앞
# v4_rank_asc: 동일 confidence 시 raw v4 rank (1, 2, 3 ...) 작은 게 앞
tie_break_axes:
- confidence_desc
- v4_rank_asc
# graceful fallback (yaml 없을 시):
# loader 가 default policy_type=deterministic_label_priority_then_confidence
# + 위 label_priority 매트릭스 로 fall through (backward compat / boot-safe).