--- title: LLM Wiki 하네스 설계 감사 — deep-research 기준 33파일 × 8원칙 source_type: llm-generated status: draft confidence: medium tags: [harness, claude-code, audit, automation, design] last_reviewed: 2026-06-06 --- # 하네스 설계 감사 보고서 (read-only) > **⚠️ 시점 스냅샷 (2026-06-06 감사 당시):** 본 보고서는 *구현 전* 상태를 기술한다. 이후 **Spec A 가 G1·G5·G6 을, Spec B 가 G3·G7 을 해소**했다. 특히 §3 G1("`--hook` 이 항상 exit 0")·G6("claim_gate 테스트 0개")는 **현재 코드에서 더 이상 사실이 아니다**(`--hook` fix-up 시 exit 2, `test_wiki_claim_gate.py` 존재). 우선순위 판단 시 [[docs/superpowers/README|하네스 경화 로드맵]]의 Spec A/B = DONE 상태를 함께 보라. 남은 미해소: **G2(Spec D, 보류)**, **G4(Spec C, 진행 예정)**. **기준선:** Claude Code 내장 `deep-research` Workflow 스크립트 (CLI 바이너리에서 추출, 350행 — `/tmp/deep_research_script.js`). **대상:** `.claude/` 하네스 33파일 (agents 10 · commands 22 · hooks 3 · settings 2 · skill 1 — 일부는 commands에 포함, 실제 채점 파일 33개). **방법:** 5개 read-only 감사 에이전트를 병렬 dispatch, 동일한 8원칙 루브릭 + 고정 output-spec 적용 → 본 문서로 합성. **파일 변경 없음.** > 메타: 이 감사 자체가 deep-research 패턴(fan-out readers → 고정 schema → synthesize)으로 수행됨. dogfooding. --- ## §0. deep-research가 "잘 설계된 하네스"인 8가지 이유 (루브릭) | # | 원칙 | deep-research 구현 | 한 줄 정의 | |---|---|---|---| | **P1** | 강제된 output-spec | `agent()` 마다 JSON `schema` (required/enum/minItems), tool-layer 검증 + 모델 재시도 | 구조화 출력이 **경계에서 검증**되는가, 산문 희망인가 | | **P2** | 결정론적 오케스트레이션 | `pipeline()`/`[barrier]`가 JS 코드, 각 barrier에 *왜 막는지* 주석 | fan-out/순서가 **코드**인가 controller-LLM 판단인가 | | **P3** | 적대적 정족수 검증 | 3표/claim, ≥2 refute면 kill, "불확실하면 refuted=true", 기권≠통과 | 독립 회의론자 + **기계적 kill 임계값** vs 단일심사/자문 | | **P4** | 우아한 저하 + funnel stats | 모든 early-return이 유효한 PARTIAL 리포트 + `stats` funnel | 부분결과 경로 + **깔때기 통계**를 내는가 | | **P5** | 무삭제 보장 | `budgetDropped[]`/`dupes[]` 추적·노출, 캡이 보임 | 캡/탈락/스킵을 **명시 보고**하는가 silent인가 | | **P6** | 증거 + grep 자가검증 | claim마다 verbatim 인용 | 원문 인용 + grep 검증 | | **P7** | 최소권한 + 단일직무 | 작은 단일목적 에이전트 | scoped `tools:`, 하나의 일 | | **P8** | 명명된 상수 | `MAX_FETCH`, `VOTES_PER_CLAIM` 상단 | 임계값이 한 곳에 명명 vs 산문에 흩어진 매직넘버 | 채점: `MATCH` / `PARTIAL` / `GAP` / `N-A`. --- ## §1. 33파일 × 8원칙 매트릭스 ### 1-A. agents (10) — 대부분 GATE(read-only judge) + 3 WORKER + 1 ORCHESTRATOR | File | P1 | P2 | P3 | P4 | P5 | P6 | P7 | P8 | |---|---|---|---|---|---|---|---|---| | branch-depth-auditor | PARTIAL | N-A | PARTIAL no quorum | GAP | N-A | MATCH | MATCH | MATCH | | coverage-auditor | PARTIAL | PARTIAL | GAP single judge | GAP | N-A | MATCH | PARTIAL | MATCH | | project-readiness-auditor | PARTIAL | N-A | PARTIAL no vote | GAP | N-A | MATCH | MATCH | MATCH | | wiki-adversarial-reviewer | MATCH | N-A | **PARTIAL no N-vote/default-refute** | PARTIAL | N-A | MATCH | MATCH | PARTIAL | | wiki-decision-researcher | PARTIAL | PARTIAL | N-A | PARTIAL | PARTIAL | MATCH | MATCH | PARTIAL | | wiki-diagram-reviewer | **MATCH** | N-A | **MATCH** measured+HARD-STOP | GAP | MATCH | MATCH | MATCH | **MATCH** | | wiki-doc-author | MATCH | N-A | N-A | PARTIAL | GAP | PARTIAL | MATCH | PARTIAL | | wiki-link-verifier | MATCH | N-A | N-A | PARTIAL | MATCH | MATCH | MATCH | PARTIAL | | wiki-research-lane | MATCH | N-A | PARTIAL | MATCH | PARTIAL | MATCH | MATCH | MATCH | | wiki-source-summarizer | MATCH | N-A | N-A | PARTIAL | PARTIAL | MATCH | MATCH | MATCH | ### 1-B. capture commands (5) — 2 ORCHESTRATOR + 3 SCAFFOLD | File | P1 | P2 | P3 | P4 | P5 | P6 | P7 | P8 | |---|---|---|---|---|---|---|---|---| | daily | N-A | N-A | N-A | PARTIAL | N-A | N-A | MATCH | N-A | | branch | PARTIAL | N-A | GAP | PARTIAL | N-A | PARTIAL | MATCH | N-A | | branch-spec | PARTIAL | **PARTIAL prose-advisory** | PARTIAL LLM-judge | PARTIAL | MATCH | PARTIAL | MATCH | GAP | | project | PARTIAL | N-A | N-A | PARTIAL | N-A | N-A | MATCH | N-A | | project-spec | PARTIAL | **PARTIAL prose loop** | PARTIAL LLM-judge | PARTIAL | MATCH | PARTIAL | MATCH | GAP | ### 1-C. transform/quality commands (7) — 2 TRANSFORM + 3 GATE + 1 QUERY + 1 MIGRATION | File | P1 | P2 | P3 | P4 | P5 | P6 | P7 | P8 | |---|---|---|---|---|---|---|---|---| | ingest | GAP | N-A | N-A | GAP | PARTIAL | MATCH | MATCH | N-A | | tag | GAP | N-A | N-A | GAP | N-A | N-A | MATCH | N-A | | lint | PARTIAL | PARTIAL (post-hoc) | PARTIAL advisory | PARTIAL | MATCH | PARTIAL | MATCH | PARTIAL | | query | GAP | N-A | PARTIAL | N-A | N-A | MATCH | MATCH | N-A | | depth | PARTIAL | **MATCH** linter→LLM gate | PARTIAL single auditor | PARTIAL | N-A | N-A | MATCH | PARTIAL | | coverage | PARTIAL | **MATCH** gate ordering | PARTIAL single auditor | PARTIAL | N-A | N-A | MATCH | PARTIAL | | migrate-claims | PARTIAL | **MATCH** Phase0-4 interlock | PARTIAL default-UNSUPPORTED | **MATCH** | MATCH | MATCH | MATCH | PARTIAL | ### 1-D. output + invest commands (10) — 4 DERIVE + RESEARCH/LEDGER/PLAN/REVIEW | File | P1 | P2 | P3 | P4 | P5 | P6 | P7 | P8 | |---|---|---|---|---|---|---|---|---| | projectize | PARTIAL | N-A | N-A | GAP | N-A | PARTIAL | MATCH | GAP | | interviewize | PARTIAL | PARTIAL gate steps | N-A | GAP | N-A | PARTIAL | MATCH | GAP | | blogify | MATCH | PARTIAL gate steps | N-A | GAP | N-A | PARTIAL | MATCH | GAP | | explain | PARTIAL | N-A | N-A | GAP | N-A | PARTIAL | MATCH | N-A | | invest-daily | PARTIAL | N-A (delegates DR) | N-A | GAP | **GAP** | PARTIAL no grep | MATCH | GAP | | invest-decide | PARTIAL | PARTIAL gate steps | **PARTIAL soft-block** | GAP | N-A | GAP no grep | MATCH | GAP | | invest-ingest | PARTIAL | PARTIAL | PARTIAL REJECT-excl | GAP | N-A | PARTIAL | MATCH | N-A | | invest-plan | PARTIAL | PARTIAL | N-A | GAP | N-A | PARTIAL | MATCH | PARTIAL | | invest-research | PARTIAL | N-A (delegates DR) | PARTIAL no quorum | GAP | N-A | MATCH self-grep | MATCH | N-A | | invest-review | PARTIAL | PARTIAL | N-A | GAP | N-A | GAP no grep | MATCH | PARTIAL | ### 1-E. infra: hooks + settings + skill (6) — 결정론 backbone | File | P1 | P2 | P3 | P4 | P5 | P6 | P7 | P8 | |---|---|---|---|---|---|---|---|---| | wiki_structure_lint.py | PARTIAL | **PARTIAL post-hoc, exit0 항상** | N-A | **MATCH** fail_by_type | PARTIAL hook[:10] | PARTIAL | MATCH | **MATCH** templates SSOT | | wiki_claim_gate.py | **MATCH** | **MATCH** PreToolUse exit-2 | PARTIAL keyword block | PARTIAL | GAP | PARTIAL | MATCH | **GAP** inline 하드코딩 | | test_wiki_structure_lint.py | PARTIAL | MATCH | N-A | PARTIAL | PARTIAL | PARTIAL | MATCH | PARTIAL | | settings.json | PARTIAL | PARTIAL Post=after-fact | N-A | N-A | N-A | N-A | MATCH | N-A | | settings.local.json | N-A | N-A | N-A | N-A | N-A | N-A | **GAP blanket Bash** | N-A | | wiki-workflow/SKILL.md | PARTIAL | **GAP advisory dispatch** | PARTIAL | PARTIAL | N-A | MATCH | MATCH | PARTIAL re-list | --- ## §2. 강점 — deep-research 수준 이상 (건드리지 말 것) 1. **P6 증거 규율이 최강.** self-grep verbatim 검증이 `wiki-source-summarizer`·`wiki-research-lane`·`wiki-diagram-reviewer`·`/migrate-claims`·`invest-research`에서 **기계적으로** 명세됨(`grep -nF`/`sed -n`). deep-research보다 강함. 2. **P7 최소권한·단일직무가 깨끗.** 9/10 에이전트가 scoped `tools:` + "Shortcut Trap" 반-합리화 가드. (유일 예외: `settings.local.json` blanket `Bash`.) 3. **결정론 layer가 존재.** deep-research에는 없는 `wiki_structure_lint.py`(582행, 이진 PASS/FAIL) + `wiki_claim_gate.py`(exit-2 block) — 문서-쓰기 경계 게이트. 4. **두 개의 "골드 표준형" 파일이 이미 존재 → 내부 템플릿으로 승격 가능:** - **`wiki-diagram-reviewer.md`** — P1·P3·P5·P8 동시 MATCH. 측정된 카운트 + HARD-STOP→0 + 명명 상수(≤10/≤8/≤1/≥95). 다른 judge 에이전트의 본보기. - **`/migrate-claims.md`** — P2·P4 MATCH. Phase0-4 hard interlock(Phase2는 Phase1 미완 시 BLOCKED), default-refute(`UNSUPPORTED_DECISION`), >10파일→슬라이스, COMPLETE/PARTIAL/BLOCKED verdict. 다른 오케스트레이터의 본보기. --- ## §3. 횡단 체계적 갭 (deep-research가 더 체계적인 지점) — 영향순 ### G1 — 결정론 backbone이 실제로 막지 않는다 ⭐ 가장 구체적·고위험 `wiki_structure_lint.py`의 `--hook` 모드는 **findings가 있어도 항상 `sys.exit(0)`** (line 531) → 풍부한 린터가 **경고 프린터**일 뿐 게이트가 아님. 게다가 PostToolUse라 **사후**. 유일한 실제 차단은 `wiki_claim_gate.py`(PreToolUse + SubagentStop)인데 **5개 path-prefix만 커버** → `wiki/projects`, 모든 `invest-*`, 파생 `wiki/interview|portfolio|blog`, `raw/errors`는 **쓰기-시점 강제 전혀 없음**. (블라스트: H / 레버리지: H) ### G2 — 오케스트레이션이 산문-자문이지 코드가 아니다 `branch-spec`·`project-spec`은 fan-out/barrier를 번호 매긴 LLM 지시("통과 시 dispatch", "8c 루프백 반복")로 표현. 비순응 controller가 단계를 건너뛰어도 **탐지 가능한 위반이 없음**. deep-research의 `[barrier]`(코드)와 대비. (단 `/depth`·`/coverage`·`/migrate-claims`은 P2 MATCH — 이미 깔끔한 linter→LLM 순서.) (블라스트: H / 레버리지: H, 단 Workflow는 Claude 전용) ### G3 — 검증에 verdict 라벨은 있으나 정족수/kill 기계장치가 없다 모든 judge가 **단일 패스**(adversarial-reviewer, depth/coverage/readiness auditor, invest-research KEEP/REJECT). N-vote·default-refute·기권≠통과 없음. **역설:** `wiki-adversarial-reviewer`의 `INSUFFICIENT_CONTEXT`는 불확실성을 PASS 쪽으로 — deep-research(불확실→refute)의 **정반대**. `invest-decide`는 규칙 위반을 soft-flag(설계상 사용자 주권이나 P3 PARTIAL). (블라스트: M / 레버리지: H) ### G4 — funnel stats가 거의 어디에도 없다 `wiki_structure_lint.py`의 `fail_by_type/fail_by_rule`만 유일. 어떤 judge·command도 deep-research funnel(후보→처리→탈락→확정)을 안 냄. **`/ingest`가 promotable 항목을 silent 누락했는지 알 수 없음**; `coverage-auditor`가 관심사를 몇 개 열거했는지 보이지 않음. (블라스트: M / 레버리지: M) ### G5 — P8 SSOT 분열(split-brain) `structure_lint`은 `templates/`에서 매핑을 도출(강함). 그러나 `claim_gate`는 섹션명·컬럼을 inline 하드코딩, `SKILL.md`는 source_type를 재나열 → **택소노미 사본 3개**, drift 위험. 임계값(90/30/14일, cap 6, ≥5 findings, ≥95)도 산문에 분산. (블라스트: M / 레버리지: M) ### G6 — 차단 훅에 테스트가 없다 유일한 exit-2 차단기 `claim_gate.py`는 **유닛 테스트 0개**; 비차단 린터는 잘 테스트됨. 고-블라스트 컴포넌트가 덜 테스트됨(비대칭). (블라스트: M / 레버리지: L) ### G7 — 에이전트 *출력*은 어디에서도 schema 검증되지 않는다 모든 에이전트가 산문("첫 글자 `#` + 고정 Output 블록 + STOP 체크리스트")을 반환, **희망으로 검증**. deep-research의 초능력(tool-layer JSON schema + 재시도)이 부재. 단 — 이건 Claude Code가 `Agent` 호출에 schema를 강제하는 native 수단이 없어서(현재 `Workflow`의 `agent({schema})`만 가능) **부분적으로 플랫폼 제약**. (블라스트: M / 레버리지: M) --- ## §4. 우선순위 (레버리지 × 블라스트, 낮은 아키텍처 위험 우선) | 순위 | 항목 | 갭 | 레버리지 | 블라스트 | 아키텍처 위험 | 3-플랫폼? | |---|---|---|---|---|---|---| | 1 | structure_lint `--hook` 차단화(CRITICAL/WARN 티어) + claim_gate 커버리지 확장 + SSOT 중앙화 | G1·G5 | H | H | **낮음**(기존 강화) | ✅ 공유 hook | | 2 | judge 5종에 return-schema 강제 + adversarial-reviewer에 N-vote/default-refute/기권≠통과 | G3·G7 | H | M | 낮음 | ✅ | | 3 | reporting-standards에 funnel-stats 계약 + ingest/coverage/depth/decision-researcher 출력에 stats 블록 | G4 | M | M | 낮음 | ✅ | | 4 | claim_gate 유닛 테스트 + diagram-reviewer/migrate-claims를 "골드형 템플릿"으로 문서화 | G6 | M | L | 낮음 | ✅ | | 5 | invest-daily/decide 강화: 숫자당 grep self-verify, 규칙 위반 hard-block 옵션 | G3·부분 | M | M | 낮음 | Claude 전용 | | 6 | branch-spec/project-spec를 결정론 Workflow 스크립트로 변환 | G2 | H | H | **높음** | ❌ Claude 전용 | > 6번이 "딥 재아키텍처"(앞서 보류). 1~4번이 *기존 아키텍처를 deep-research 메커니즘으로 경화*하는 안전한 길. --- ## §5. spec 분할 권고 (각각 자체 spec→plan 사이클) - **Spec A — "결정론 backbone을 진짜 게이트로"** ⭐ 첫 슬라이스 후보. (순위 1) - `wiki_structure_lint.py --hook`: CRITICAL(broken-link/missing-required-section)에 exit-2, WARN은 exit-0 유지 → 사후 경고를 사전 차단으로. - `wiki_claim_gate.py`: path-prefix 커버리지를 `wiki/projects`·`invest-*`·파생 산출물로 확장. - SSOT 중앙화: 섹션명·컬럼·source_type 매핑을 단일 모듈로(두 훅 + SKILL이 소비). - `--hook` findings 잘림(`[:10]`) 시 "N more suppressed" 명시(무삭제). - **왜 먼저:** 다른 모든 spec이 "backbone이 막는다"를 전제. 위험 최저, 블라스트 최고(비차단-린터는 사실상 잠재 결함). - **Spec B — "judge 에이전트 schema + quorum"** (순위 2): depth/coverage/readiness/adversarial/diagram에 return-schema; adversarial-reviewer·게이트에 선택적 N-vote + default-refute + 기권≠통과. P1+P3 동시 상승. - **Spec C — "funnel stats + 무삭제 계약"** (순위 3): reporting-standards + 핵심 명령 출력에 stats 블록. - **Spec D (보류, 딥) — "branch-spec/project-spec 결정론 오케스트레이션"** (순위 6): Workflow 변환, Claude 전용, 최고 레버리지·최고 위험. 사용자 명시 opt-in 필요. --- ## §6. claim traceability 검사 (감사 메타) 본 감사는 5개 슬라이스 매트릭스에서 파일별 evidence(file:line)를 근거로 했고, 미적용 원칙은 `N-A(이유)`로 분리했다. 임의 보강 없이 갭은 `GAP`으로, 단정 불가는 `PARTIAL`로 표기했다. 이 보고서는 변경을 가하지 않는 read-only 산출물이며, 다음 단계는 사용자가 §5의 첫 슬라이스(Spec A)를 승인할 때 spec→plan으로 진행한다. **상태:** draft (검토 전). 외부 파생 금지.