엔지니어링 거버넌스
엔지니어링 거버넌스
엔지니어링 작업이 본 저장소를 어떻게 흘러가는지 안내하는 단일 진입점. 규칙서 (CLAUDE.md), 결정 기록 (docs/adr/), 테스트, 평가, reviewer 문서를 묶는다. 신규 기여자 또는 reviewer 온보딩은 여기서 시작.
무엇이 어디에 있나
| 관심사 | 단일 출처 | 비고 |
|---|---|---|
| 코딩 & 리뷰 규칙 | CLAUDE.md |
Pre-PR 체크리스트, 금지 shortcut, 성능 기대치 |
| AI-agent 운영 모델 | docs/operations/ai-engineering-operating-system.md, tasks/queue.md |
역할(role), persistent task queue, plan doc, review/eval handoff |
| Multi-agent 조율 | docs/multi-agent-ownership.md |
7-way 소유권, rag_core.py lock holder, 병행 작업 충돌 해결 |
| Load-bearing 결정 | docs/adr/ |
결정 당 짧은 파일 1개, status 추적 |
| 동작 계약 | ADR 0003, docs/agentic/answer-policy.md |
답변 JSON shape, schema_version, status 값 |
| Eval 표면 | ADR 0005, docs/evaluation/surface-map.md, eval/config.yaml, eval/*.example.yaml |
공개 fixture smoke는 commit 가능, private/internal eval은 local-only |
| Reviewer 메트릭 | reports/eval_summary.json, private/internal aggregate docs |
PR eval 델타 워크플로가 fixture smoke + latency diff를 PR 코멘트에 upsert |
| 실패 분석 | docs/real-data/real-data-failure-taxonomy.md, docs/real-data/failure-cases.md |
우선순위 백로그의 원천 |
| API 데모 | docs/operations/api-demo.md, api/main.py |
reviewer 놀이터, 측정 기준 아님 |
| Issue/PR triage | 본 페이지 “Milestones & 이슈 lifecycle” | 마일스톤, stale 정책, 현재 카테고리 스냅샷 |
변경 lifecycle
non-trivial 변경의 체크리스트 (사람·AI 공용):
- Issue 열기 또는 픽업 (필수, ADR 0007). 실패 taxonomy + 우선순위 백로그가 1차 source.
.github/ISSUE_TEMPLATE/사용. 브랜치+PR 은 이 issue 번호 참조 필수 — 컨벤션 체크 (CI) 가 merge 차단 - ADR 필요한지 판단.
docs/adr/README.md기준 사용. 대부분 불필요, 모호하면 issue 에 질문 - Task queue / plan 판단. multi-session, load-bearing, eval/benchmark, 또는 >1 파일/>50 LOC 작업은
tasks/queue.md에 task를 남기고docs/plans/TEMPLATE.md기반 plan doc를 작성 - 기존 코드 점검. 읽은 파일, 재사용 함수, 놀란 점 명시
- Branch + worktree (병렬 시).
<type>/issue-<N>[-<slug>](ADR 0007) — 예:feat/issue-79-hybrid-retrieval. Claude Code 기본 worktree 명 (claude/<auto>) 은 PR 전 rename (git branch -m feat/issue-<N>-<slug>). Codex, Claude Code, 외부git worktree는 같은 coordination surface 이므로 편집 전에overlap-preflight를 실행한다:python3 scripts/agent_loop.py overlap-preflight --issue <N> --branch <type>/issue-<N>-<slug> - 변경 + 테스트. 재사용 우선, one concern per PR. 동작 변경 무 테스트 = 사고. 회귀는
tests/test_*_regression.py - Eval 로컬 실행 (해당 시).
make eval공개 fixture smoke. 실제 성능 변경은 private/internal eval aggregate와 비교. Claim boundary는docs/evaluation/surface-map.md기준 - Review checklist 선택. 일반 review 외에 load-bearing은 deep review, eval/benchmark claim은 benchmark auditor checklist (
docs/reviews/ai-review-checklists.md)를 적용 - Push + PR. PR body 는
.github/pull_request_template.md채움 - CI 검증. 3개 체크 (모든 PR):
Pytest+Eval delta vs base+Validate branch name + issue link(ADR 0007, required status check) - 리뷰 응답. 리뷰 중 scope 추가 금지 — follow-up issue
- Merge. Squash-merge, 브랜치 삭제, worktree 정리
- 문서/queue 갱신 (reviewer 가 알아야 할 변경 시): task status, 평가 경계, private/internal aggregate evidence, ADR status, taxonomy entry
Milestones & 이슈 lifecycle
Open issue 는 마일스톤으로 그룹화 — 백로그가 깨끗이 스캔되고 계획 vs 보류 구분 가능. 마일스톤은 GitHub 에서 수동 관리; 아래 스냅샷은 예시, GitHub 마일스톤 페이지가 authoritative.
마일스톤
| 마일스톤 | 목적 | 일반적 issue 종류 |
|---|---|---|
v3-release |
RAG 스택 차기 릴리즈 작업 — ingestion v3, 검색·순위 변경, 신규 코어 유틸 | 동작 ship 하는 feat, fix |
portfolio-review-readiness |
Reviewer 폴리시: README 명확성, 케이스 스터디, 배포 산출물, 구조화 출력 docs, 분석 변형 시각화 | 포트폴리오 reviewer 대상 docs, chore, eval |
real-data-evaluation |
비공개 100-doc real-data eval 건강성: docs/real-data/real-data-failure-taxonomy.md 관측 실패, 한국어 축, 보류 회귀 |
측정 가능한 real-data 델타 ship 하는 eval, fix |
Meta/parent issue (예: #118 포트폴리오, #187 phase 향상 백로그) 는 마일스톤 미할당 — 자식 issue 가 마일스톤 보유.
Stale 정책
- 60일 무활동 →
stale라벨 + “still planned? close or rescope” 코멘트. 주간 triage stale후 90일 무활동 → 마일스톤 포인터와 함께 close. 작업 재개 시 reopen- Auto-close 절대 금지 — 종결은 사람 결정, 라벨이 자동화 친화 신호
라벨/마일스톤 현재 수동 관리, GitHub Action 미연결.
스냅샷 (2026-05-11)
| 마일스톤 | Open issue |
|---|---|
v3-release |
#121, #167, #168, #170 |
portfolio-review-readiness |
#122, #123, #124, #125, #127, #128, #164, #172 |
real-data-evaluation |
#126 |
분류되지 않은 issue 는 마일스톤 없이 유지.
문서들의 상호 강화
┌───────────────────────────┐
│ CLAUDE.md │ (규칙)
└─────────────┬─────────────┘
│
┌───────────────┼────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ ADR │ │ Test │ │ Eval │
│ (왜) │ │ (가드) │ │ (증명) │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└───────────────┼────────────────┘
▼
┌───────────────────────────┐
│ Reviewer 문서 │
│ (README, docs/*, PR diff)│
└───────────────────────────┘
- CLAUDE.md = 모든 변경이 만족할 규칙
- ADR = load-bearing 선택의 왜, 향후 무의식적 반전 방지
- Test = 규칙·결정의 silent rot 방지
- Eval = 규칙·결정을 reviewer 가 읽을 수치로 변환
- Reviewer 문서 (README, design docs, PR description, 분석 변형 report) 가 위를 가리켜 작성자 DM 없이 end-to-end 이해 가능
본 거버넌스가 막는 안티패턴
- Silent 계약 drift — 답변 필드가 사라져도 테스트가 못 잡음. 방지: ADR 0003 +
score_answer_format(eval/run_eval.py) - Headline 메트릭 인플레이션 — README 가 공개 fixture 숫자를 성능 주장처럼 사용. 방지: 공개 fixture smoke / private internal eval 분리 (ADR 0005) + private aggregate-only evidence 정책
- 기준선 rot — naive_baseline 이 import 되지만 아무도 안 돌림. 방지:
naive_baseline=eval/config.yaml의 named 분석 변형, 매 eval run 마다 보고 - 결정 세탁 — load-bearing 선택이 리팩터 PR 에 묻힘. 방지: CLAUDE.md Core principles 의 ADR 임계값 +
docs/adr/README.md; PR 템플릿이 질문 강제 - 리뷰 중 scope 증가 — “while I was here” fix 가 PR 비대화. 방지: CLAUDE.md “one PR, one concern”; follow-up issue spawn
Governance saves: 실제 막은 인시던트
위 목록은 설계 — 규칙과 가드. 이 섹션은 증거 — 실제 발생한 인시던트와 사후 추가된 hook/ADR/규칙. 거버넌스가 있다 가 아니라 rent 를 냈다 가 reviewer 의 30초 질문. 각 항목 = rent 1회.
-
#69 의도된 보류 회귀 — smoke CI 의 real-data 사각. 공개 fixture smoke 델타는 녹색이었으나 비공개 100-doc real-eval 에서 근거 불충분 시 의도된 보류 손실. eval 분리 규율 (ADR 0005) 은 이미 있었지만 PR 시점 gate 가 advisory. 사후 추가: PR 템플릿 5b (real-data 델타) 필수 CI 체크 (
scripts/check_branch_and_issue.py --check-5b,scripts/_governance.pyload-bearing 경로 리스트 경유 강제). 후속: 이 §5b 강제 게이트는 ADR 0084 로 폐지 (유지보수자가 첨부 중단 결정) —make real-eval-delta측정 도구 +LOAD_BEARING_PATHSawareness 는 유지, real-data aggregate 첨부는 이제 권장(강제 아님) -
Stacked-PR child auto-close on
--delete-branchmerge. base 브랜치를gh pr merge --delete-branch로 머지하면서 stacked dependent PR 이 여전히 그 브랜치를 target 으로 함 → GitHub 기본 동작이 dependent PR auto-close, 진행 중 리뷰 상태 손실. 사후 추가:.claude/settings.json의PreToolUseBash matcher 가gh pr list --base <this-PR-head> --state open --json number비어있지 않을 때gh pr merge --delete-branch거부. 명령이 GitHub 에 도달 전 차단. 규칙 텍스트는CLAUDE.md > Prohibited에 살아남도록 명시 -
gh pr merge --delete-branch멀티 worktree 로컬 abort (issue #1283). gh 의--delete-branch는 원격 삭제를 로컬 checkout-to-default + 로컬 브랜치 삭제와 한 명령에 묶는다. 상시 20~30 worktree 가동 환경에서main이 다른 worktree 에 체크아웃돼 있으면 gh 의 로컬 checkout 이 실패하며 원격 삭제 전에 명령 전체를 abort — 서버 squash 머지는 성공하지만 원격 브랜치가 남아 매번git push origin --delete수동 정리 (memoryfeedback_merge_admin_gate§2, PR #1210 재발). 사후 추가: 두 ship 표면 모두--delete-branch대신 머지 후git push origin --delete <branch>(순수 원격, 로컬 체크아웃 불필요) 사용 —scripts/claude-hooks/stop-ship.shStage 5 +.claude/skills/ship-pr/SKILL.md.git push origin --delete도 child auto-close 를 유발하므로 위 bash-guard 의 stacked 보호를 이 형태(Branch (3))로 확장 -
ADR 번호 worktree 충돌. 관측 페어 3개 — 0022→0023, 0023→0025, 0029→0030 — 두 worktree 가 독립적으로
ls docs/adr/에서 같은 ADR 번호 예약 후 머지 시점 충돌. 수정은 procedural (번호는 공유 자원). 사후 추가:CLAUDE.md > Core principles > "Reserve ADR numbers up front"가 dual check (ls docs/adr/+gh pr list --search "ADR" --state open) 강제 + 사용자 확인 요구 (worktree 간 직렬화) -
docs cross-reference dead-link 재발 (≥6회). docs/ batch 재구성마다 상대경로 링크가 깨짐 — 파일 이동 시 그 파일을 가리키는 다른(미수정) 파일의 inbound 링크가 끊기는데 수동 grep + 사후 cleanup PR 에만 의존 (커밋 627a63b “80 links”, 9754f69, da80073 “restore ADR 0020 to fix broken links” 등). 기존 ADR↔README 번호 parity 가드 (
scripts/_governance.py) 는 ADR 번호 만 검사, 일반 cross-doc 링크는 미검사. 사후 추가:scripts/check_doc_links.py— 모든 tracked*.md의 상대경로 링크 + proseADR NNNN참조가 실재 파일을 가리키는지 검사 (외부 URL / Jekyll permalink 제외, stdlib-only). pre-commit 훅 (shift-left,.mdstaged 시 전체 트리 스캔) +tests/test_doc_links.pyreal-repo sentinel (canonical CI gate, post-push) +make check-doc-links3곳에서 강제 (issue #1060). 도입 시 기존 broken link 101개 동시 cleanup (대부분 docs// 의../→../../off-by-one + renumber 된 ADR slug)
각 인시던트 = 1회 지불된 실제 비용. 신규 인시던트 (거버넌스 갭 → 수정 → 무재발) 는 여기 추가.
온보딩 shortcut
신규 기여자 reading order:
CLAUDE.md— 규칙- 본 파일 — 규칙들의 연결
docs/adr/README.md+ 현재 ADR 6개 훑기 — load-bearing 결정docs/real-data/real-data-failure-taxonomy.md— 백로그 원천
10분 짜리 reviewer 는 step 3 부터.
훅 설정
Git 훅 (opt-in, 개발자당 1회)
활성화:
make install-hooks
# 또는:
git config core.hooksPath .githooks
make install-hooks 는 hooksPath 외에 공유 object-store corruption 가드 (issue #1680)도
설정한다: 20~30 worktree 가 .git/objects 하나를 공유하므로 자동 git gc/repack/prune 이
in-flight git add/commit 과 race 해 아직 커밋 안 된 worktree index 만 참조하는 blob 을
삭제(fatal: unable to read <blob> / invalid cache-tree)할 수 있다. 이를 막으려고
gc.auto 0 / gc.autoDetach false / maintenance.auto false / fetch.writeCommitGraph false
로 자동 pruner 를 전면 비활성하고, core.fsync loose-object + core.fsyncMethod batch
로 loose object durability 를 보장한다. 트레이드오프: loose object 가 쌓이므로 활성 세션이
0 일 때만 수동으로 git gc --no-detach 를 돌려 pack 한다 (worktree 가 dirty/in-flight commit
중일 때 gc 금지).
.githooks/ 의 2개 훅 활성:
-
pre-commit— eval 분리 (ADR 0005) 비공개측 파일 포함 commit 을 hard-block..gitignore정렬,git add -f+ force-path 잡음.git commit --no-verify우회는 훅 allowlist 가 놓친 aggregate 산출물 의도 commit 시만 + 같은 변경에서 allowlist 수정 -
pre-push— 2개 체크:- 브랜치 + 이슈 컨벤션 (ADR 0007) — 현 브랜치가
<type>/issue-<N>[-<slug>]미매치 시 hard-fail. CI 체크의 미러, push round-trip 전 위반 표면화.gh설치+인증 시 issue #N 존재 확인 추가 - Real-data eval 리마인더 — 검색/검증기/eval/api 경로 touch 시 soft-warn, PR 템플릿 5b 의
make real-eval-delta첨부 reminder. Exit 0, 차단 안 함
git push --no-verify우회는 문서화된 사유 (예: 측정된 PR 의 doc-only follow-up) 시만 - 브랜치 + 이슈 컨벤션 (ADR 0007) — 현 브랜치가
Claude Code 훅 (자동 로드)
.claude/settings.json commit 되어 Claude Code 자동 로드. Edit/MultiEdit/Write 의 PreToolUse 훅 등록 — Claude 가 load-bearing 파일 (rag_core.py, ingestion.py, visual_ingestion.py, eval/, api/, docs/adr/) 수정 직전 stderr awareness reminder. 고려할 ADR 리스트 + PR 템플릿 5b 요구사항 noting.
훅 스크립트: scripts/claude-hooks/pretooluse-loadbearing.sh. 차단 안 함 — 순수 awareness layer.
Claude Code 훅 (opt-in, user-global) — plan-slug race detector
병행 worktree 의 Claude Code 세션들이 user-global 디렉터리 (~/.claude/plans/<random-slug>.md) 에 plan 파일 write. slug 공간은 크지만 10+ 동시 worktree 에서 충돌 0 아님 (2026-05-15 관측, issue #779).
scripts/claude-hooks/plan-slug-race.sh = user-global PreToolUse 훅. plan 파일 Write 차단 조건:
- 대상 파일 존재
- mtime 이 최근 5 min 내 (
PLAN_SLUG_RACE_THRESHOLD, 기본 300 s) - 처음 200 바이트가 caller cwd 와 다른 worktree slug 선언
writer 측 컨벤션: 모든 plan 파일 처음 200 자에 본 plan은 worktree `<slug>` 의 deliverable. 같은 마커 포함 → 훅이 race 검출. 마커 없는 plan 은 차단 안 함 (false-positive 회피).
Override (다른 worktree plan 의도적 덮어쓰기): PLAN_SLUG_RACE_THRESHOLD=0.
훅은 자동 등록 안 됨 (Claude Code 세션이 여러 repo 걸칠 수 있음). ~/.claude/settings.json 에 1회 wire:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "<절대경로>/scripts/claude-hooks/plan-slug-race.sh"
}
]
}
]
}
}
회귀 커버리지: tests/test_plan_slug_race_hook_regression.py.