ADR 0071 — Committed README metric snapshot as parity source of truth
ADR 0071 — Committed README metric snapshot as parity source of truth
- Status: Superseded
- Superseded by: ADR 0005 (public fixture smoke / private internal eval boundary)
- Date: 2026-05-23
- Authors: Hyunsoo Kim
- Related: ADR 0005 (eval split public-synthetic/private-local), ADR 0001 (naive baseline 보존), CLAUDE.md “PR 설명” §5b, PR #1116 (docs 한국어화)
- Issue: #792
- Supersedes-attempt: PR #739 / PR #751 (revert
58b9835) — CI README-metric sync 게이트의 source-mismatch 실패
Context
README 의 핵심 성능표 (<!-- METRICS_TABLE:START --> … END) 는 공개 합성 eval 산출물이다. 그 수치를 CI 에서 강제하려던 첫 시도 (PR #739/#751, pr-eval.yml 에 update_readme_metrics.py --check step 추가) 는 source mismatch 로 항상 실패해 revert 됐다:
- CI eval-delta job 은
eval/config.ci.yaml+embedding-backend: hashing로reports/eval_summary.json을 생성한다. - README 표는 풀
eval/config.yaml로 로컬 생성된다 (make smoke= full config + hashing backend, REPORT_DIR=reports → write+check). reports/eval_summary.json은.gitignore(reports/*) 로 ignored → committed source of truth 부재.- 따라서 CI 의
--checkstep 은 PR 시점에 재측정한 divergent eval 을 README 와 비교 → 구조적으로 불일치. - 자동 fix (CI 에서 README regen) 는 production 수치 손실 (citation precision +18pp → +0pp).
두 번째 (이전엔 표면화 안 된) 원인: README metric 블록은 더 이상 순수 기계 생성물이 아니다. PR #1116 (한국어화) 이 <details><summary> 와 하단 caption 을 한국어 prose (ADR 링크 · <strong> 통계 포함) 로 손수 편집했으나 update_readme_metrics.py 는 여전히 옛 영어 문자열을 emit 한다. 즉 render(eval_summary) ≠ 현재 README 블록 — snapshot 을 아무리 정확히 만들어도 “render == README” 전체 비교 게이트는 통과 불가.
Decision
-
reports/eval_summary.snapshot.json을 committed source of truth 로 한다..gitignore에!reports/eval_summary.snapshot.json예외 1줄. 공개 합성 eval 산출물이므로 ADR 0005 비공개 경계 밖 (reports/real*/미해당,EVAL_PRIVACY_ARTIFACT_GLOBS미해당). -
CI 게이트는 재측정하지 않는다.
pr-eval.yml의 step 은update_readme_metrics.py --report reports/eval_summary.snapshot.json --readme README.md --check만 수행 — README 의 metric 행이 committed snapshot 과 일치하는지 검증. eval 재실행 없음 → #739/#751 의 source mismatch 제거. -
게이트 범위는 metric 행 (숫자) 한정.
--check와 write 경로 모두 marker 블록 안의|-delimited 행만 비교/치환하고 (splice_table_rows),<details>/<summary>+ caption 의 손수 큐레이션 prose 는 in-place 보존한다. table header/separator 행은 renderer 와 README 가 byte-identical 이라 round-trip clean. prose 의 i18n drift 는 #1116 의 별개 관심사로 decouple. -
갱신 절차는
make snapshot-update. operator 가make smoke(fulleval/config.yaml, hashing backend) 로reports/eval_summary.json재생성 후make snapshot-update실행 → snapshot 갱신 + README metric 행 in-place sync (prose 보존).reports/eval_summary.snapshot.json+README.md동반 commit.
Why these specific choices
| 결정 | 근거 |
|---|---|
| committed snapshot (옵션 A) vs 별 workflow (B) / CI 자동 push (C) | A 는 Makefile 1 target + gitignore 1줄 + CI 1 step. B 는 eval 시간을 PR CI 에 부담, C 는 CI push 보안 우려. issue #792 §권고 A. |
| 재측정 안 함 | 재측정이 #739/#751 실패의 직접 원인. snapshot 비교는 backend/config 무관하게 결정론적. |
| 숫자 행만 비교 (rows-only) | render 가 prose 를 소유하지 않으므로 (PR #1116 이후) 전체-블록 비교는 영구 false-fail. 게이트의 본래 목적은 README 숫자 ↔ snapshot 정합 강제이며, prose 큐레이션은 그와 직교. |
make smoke 가 SSoT 생성 경로 |
이미 full eval/config.yaml + hashing 으로 README 를 write/check 하는 기존 경로. 신규 측정 표면 도입 아님 — 기존 산출물을 committed 화. |
Consequences
- Positive: CI 가 README 숫자 drift 를 머지 전 차단 (regression test
test_repo_readme_rows_match_snapshot가 항상-on sentinel). snapshot 이 reviewer 가 의존할 측정-provenance 계약 고정.make governance-check의check가 committed snapshot 대상으로 동작 (이전엔 gitignoredreports/eval_summary.json부재 시 exit 2). - Negative: snapshot 갱신은 수동 (
make snapshot-update) — 측정 numbers 가 움직였는데 snapshot 미갱신 시 CI red. 이는 의도 (drift fast-fail). - 불변 보존: ADR 0001 (naive baseline) ranking 무변경 (코드 경로 미수정, 측정 산출물만 commit). ADR 0005 경계 (공개 합성 한정). ADR 0003 답변 계약 무관.
Verification
자기-검증 (본문이 약속한 parity 강제가 실제로 작동하는지):
python3 scripts/update_readme_metrics.py --report reports/eval_summary.snapshot.json --readme README.md --check가[OK]이면 README ↔ snapshot 정합.- snapshot 의 한 metric 을 임의 변경하면
--check와test_repo_readme_rows_match_snapshot이 fail 해야 한다 (게이트가 살아있다는 증거).