Files
llm-wiki/docs/superpowers/specs/2026-06-06-harness-audit-report.md
T

176 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 (검토 전). 외부 파생 금지.