실패 모드 하든(harden) 프로세스
실패 모드 하든(harden) 프로세스
ADR 0059 failure classifier + supply 2 대시보드를 닫힌 에러 루프(closed error loop) 로 바꾸는 monotone-harden 워크플로: 감사(audit)가 표면화하는 모든 실패 모드는 카테고리, eval 예제, 래칫(ratcheting) 천장을 얻는다 — 그래서 같은 회귀가 조용히 재발할 수 없다.
이것은 Phase 5 감사(#992)의 항목 3 이다. 계약은 ADR 0062 다.
Historical scope note. 이 문서의 legacy
reports/real100/*,make real-eval,n=221,kordoc계열 언급은 ADR 0062 당시 v1 private-eval ratchet 를 설명하는 archive-only 운영 기록이다. 현재 새 작업·PR·claim 의 private eval 근거는real100_v2표면만 사용해야 하며, 이 프로세스를 재활성화하려면 먼저real100_v2aggregate/gate 로 재검증해야 한다.
루프
audit surfaces a failure mode (e.g. #1005 retrieval_miss, #1020 verifier_false_negative)
│
▼
(a) category exists in failure_classifier.py? ──no──▶ add category + lock test
│ yes (tests/test_failure_classifier.py)
▼
(b) ≥5 representative examples in real-eval set? ──no──▶ add hardcase examples
│ yes (eval/real_config.local.yaml)
▼
(c) ceiling set in test_failure_rate_regression.py? ──no──▶ set ceiling = current rate + margin
│ yes
▼
(d) supply 2 dashboard renders it (scripts/render_failure_distribution.py)
│
▼
fix lands ──▶ lower committed rate ──▶ TIGHTEN ceiling in same PR ──▶ loop closes tighter
래칫은 한 방향으로만 돌아간다: 천장은 fix 가 landing 되면 내려가며,
명시적 [ALLOW_REGRESSION] 정당화 없이는 절대 올라가지 않는다.
이 방향성은 이제 CI 가 강제한다 — branch-and-issue-check.yml 의
ceiling-ratchet gate (scripts/check_branch_and_issue.py
--check-ceiling-ratchet) 가 base 브랜치 대비 ceiling 상향/제거를 PR 본문의
[ALLOW_REGRESSION: <category> ...] 토큰 없이는 차단한다 (issue #1150).
in-test test_ceilings_are_monotone_sane 은 역전(천장 < 현재 rate)만 가드하므로,
상향 차단은 이 CI gate 가 담당한다.
각 표면의 역할
| surface | file | role |
|---|---|---|
| classifier | eval/scorers/failure_classifier.py |
7-category first-match-wins 라벨 (ADR 0059) |
| classifier lock | tests/test_failure_classifier.py |
ordering 을 고정해 Finding #1 이 verifier_false_negative 로 유지되게 함 |
| dashboard | scripts/render_failure_distribution.py |
distribution + ADR 0059 계약 ✓ 렌더 (Markdown/aggregate JSON + local HTML board) |
| regression gate | tests/test_failure_rate_regression.py |
커밋된 baseline 의 래칫 천장 (ADR 0062) |
| ratchet gate | scripts/check_branch_and_issue.py --check-ceiling-ratchet |
base 대비 ceiling 상향/제거를 [ALLOW_REGRESSION] 없이 차단 (CI, issue #1150) |
| baseline | reports/real100/baseline.aggregate.json |
historical v1 archive-only aggregate. 현재 게이트/claim 에 재사용하려면 real100_v2 aggregate 로 재검증해야 함 (ADR 0005 경계) |
새로 표면화된 실패 모드 추가하기
-
카테고리를 확정한다. 모드가 기존 7-category 라벨에 맞으면 단계 3 으로 건너뛴다. 진정으로 새것이라면
failure_classifier.py의FAILURE_CATEGORIES와classify_failure()에 추가하되, first-match-wins ordering 을 존중한다(ADR 0059).tests/test_failure_classifier.py에 유닛 테스트를 추가한다. -
≥5 개 예제를 추가한다. 그 모드를 보이는 대표적인 hardcase 쿼리를 최소 5개
eval/real_config.local.yaml(gitignore, ADR 0005)에 추가한다. 이는 rate 에 안정적인 분모 신호를 준다. -
천장을 설정한다. historical v1 절차는 baseline 을 regen(
make real-eval+make real-eval-baseline-update STRICT=1)한 뒤 새 카테고리의 커밋된 rate 를 읽고,tests/test_failure_rate_regression.py의CEILING_RATE_BY_CATEGORY에current_rate + margin을 추가했다. 이 명령 조합과 v1 rate 는 현재 새 작업에서는 archive-only 이며, 현재 천장/claim 을 갱신하려면 먼저real100_v2aggregate/gate 로 등가 표면을 확정해야 한다. margin 은 variance 감사(#1025)가 측정한 cross-HEAD variance 를 흡수했다 — 최초 도입 시에는 넉넉히 설정한 뒤 조였다(tighten). -
대시보드가 렌더하는지 검증한다. historical v1 절차에서는
scripts/render_failure_distribution.py를 재실행해 새 카테고리가reports/real100/failure_distribution.md와 local-onlyreports/real100/failure_distribution.html에 나타나는지 확인했다. 현재 표면에서 같은 주장을 하려면real100_v2aggregate 기반 출력으로 먼저 대체해야 한다.
fix 이후 천장 조이기
historical v1 fix PR 이 게이트된 rate 를 낮출 때의 절차는 다음과 같았다:
- fix 의 HEAD 에서 v1 baseline 을 regen 한다.
- 카테고리의
CEILING_RATE_BY_CATEGORY항목을 새current_rate + small_margin으로 같은 PR 에서 낮춘다. test_ceilings_are_monotone_sane은 천장을 현재 rate 아래로 설정하는 것(역전된 래칫)을 가드한다.
현재 real100_v2 표면에서 같은 래칫을 운용하려면 v2 baseline/gate 를 먼저 확정한
뒤 그 표면의 rate 에만 적용해야 한다.
의도적으로 천장을 상향해야 한다면 (예: ADR 0058 같은 cross-HEAD 변경으로
variance 가 커진 경우), PR 본문에 [ALLOW_REGRESSION: <category> 0.X→0.Y 사유]
토큰을 추가한다 — CI ceiling-ratchet gate 가 이를 확인하고, 없으면 PR 을
차단한다.
절대 카운트가 아닌 rate 인 이유
variance 감사(#1025)는 절대 카운트가 HEAD 별로는 결정론적이지만
cross-HEAD 로는 변동함을 발견했다(verifier_false_negative 가
PR #1001 / #1004 / #1018 전반에 49 ↔ 65 ↔ 76 으로 변동, 반면 same-HEAD N=3
실행은 byte-identical 이었다). 문서화된 margin 이 있는 rate 는 그 cross-HEAD
variance 를 흡수한다; 히스토리컬 variance 를 넘어선 진짜 회귀는 여전히
게이트를 발화시킨다. fix 의 전/후는 항상 같은 커밋에서 비교하라 — cross-HEAD
비교는 fix 를 그 사이의 변경(예: ADR 0058
hybrid 전환)과 혼동시킨다.
워크 예제 (이 루프를 먹인 historical v1 감사들)
| audit | mode | committed rate | ceiling |
|---|---|---|---|
| #1020 | verifier_false_negative |
0.344 (76/221) | 0.40 |
| #1005 | retrieval_miss |
0.290 (64/221) | 0.34 |
| — | total failures | 0.814 (180/221) | 0.86 |
각 감사의 follow-up fix(Issue F verifier 하든, Issue A top_k
ablation, …)는 이 historical v1 rate 를 낮춘 뒤 천장을 조이는 것을 목표로 했다.
현재 작업에서 같은 결론을 주장하려면 real100_v2 evidence 로 다시 측정해야 한다.