9.8 KiB
9.8 KiB
title, source_type, status, confidence, tags, last_reviewed
| title | source_type | status | confidence | tags | last_reviewed | |||||
|---|---|---|---|---|---|---|---|---|---|---|
| Spec A — 결정론 backbone을 진짜 게이트로 (하이브리드 PreToolUse/PostToolUse) | llm-generated | draft | medium |
|
2026-06-06 |
Spec A — 결정론 backbone을 진짜 게이트로
상위 감사: 2026-06-06-harness-audit-report §3 G1·G5·G6. 본 spec은 그 첫 슬라이스. 두 설계 결정 확정: (1) 하이브리드 게이트, (2) Lean & safe 범위/SSOT.
1. 문제 (감사에서)
- G1:
wiki_structure_lint.py의--hook경로가 findings가 있어도 항상sys.exit(0)(line 531) — 582행짜리 결정론 린터가 경고 프린터일 뿐 게이트가 아니다. 게다가 PostToolUse라 사후. 유일한 실제 차단(wiki_claim_gate.py)은 5개 path-prefix만 커버 →wiki/projects·invest-*·파생 산출물·raw/errors는 쓰기-시점 강제 0. - G5: SSOT 분열 —
structure_lint는templates/에서 도출(좋음), 그러나claim_gate는 섹션명·컬럼을 inline 하드코딩,SKILL.md는 source_type 재나열 → 택소노미 사본 3개, drift 위험. - G6: 유일한 exit-2 차단 훅
claim_gate.py에 유닛 테스트 0개(비차단 린터는 잘 테스트됨 — 비대칭).
2. 비목표 (이 spec 범위 밖)
- judge 에이전트 return-schema / N-vote quorum → Spec B.
- funnel stats 계약 → Spec C.
- 누락 카테고리의 새 의미 규칙(invest source+timestamp, 파생물 canonical-source+status, wiki/projects claim-backed) → 후속(Spec B/C 또는 A.2). 단, 섹션/frontmatter 존재 검사는 C1이 template-derived이므로 게이트 배선만으로 전 카테고리 자동 확장됨.
- Antigravity 훅 포팅 → 기존 follow-up(
docs/superpowers/notes/2026-06-04-phase2-antigravity-hook-coverage.md). - branch-spec/project-spec의 Workflow 변환 → Spec D(보류).
3. 핵심 설계 결정 (확정)
DD1 — 하이브리드 게이트 지점·의미
PostToolUse exit-2는 쓰기를 되돌리지 못한다(파일은 이미 디스크, 모델에 "고쳐라"만 전달). 따라서:
- 항상-틀린 검사(C2 깨진 링크) → PreToolUse로 옮겨
projected_content에 검사, exit 2로 쓰기 자체 차단(ghost가 디스크에 안 닿음). - 완성성 검사(C1 섹션/frontmatter, C3 선택조건) → 기존
is_completeness_checkable철학 유지: '완성 선언' 문서에만 PostToolUse exit-2 fix-up.
기존 코드 철학(C2-항상 / C1·C3-완성시)과 정확히 일치한다.
DD2 — Lean & safe 범위 + SSOT
- claim_gate의 기존 5-prefix 의미 규칙은 그대로. 새 의미 규칙 없음.
- SSOT: claim_gate의 하드코딩 prefix→요구 맵 + 공유 이벤트 파싱을 새
wiki_rules.py모듈로 추출(두 훅이 import). 템플릿-테이블 파싱(Deep SSOT)은 채택 안 함 — fragile.
4. 아키텍처
.claude/hooks/
wiki_rules.py ← NEW. 공유 SSOT 모듈 (stdlib only)
wiki_claim_gate.py ← MOD. wiki_rules import (projected_content/파싱/PREFIX_REQUIREMENTS 이관)
wiki_structure_lint.py ← MOD. --pre 모드 신설 + --hook fix-up 티어링
test_wiki_structure_lint.py ← 기존
test_wiki_claim_gate.py ← NEW
4.1 wiki_rules.py (NEW 공유 모듈)
- 이관(claim_gate→여기):
tool_name(),tool_input(),target_path(),write_content(),projected_content(),command_string(),rel_to_root(),has_table(). - 추출(하드코딩→dict, SSOT):
(claim_gate의
PREFIX_REQUIREMENTS = { "raw/official-docs/": {"tables": [("## Claims Extracted", [...6 cols])], "sections": ["## Usage Boundaries"]}, "raw/company-tech-blogs/": {...동일...}, "raw/branch-notes/": {"tables": [("## Decision Evidence Map", [...5 cols])], "section_regex": [r"^## .*\bClaims To Verify\b"], "semantic": ["officially_supported_needs_strength"]}, "wiki/concepts/": {"tables": [("## Claim-backed Knowledge", [...4 cols])]}, "docs/superpowers/specs/*-report.md": {"semantic": ["complete_needs_traceability"]}, }check_markdown_write로직이 이 dict를 순회하도록 리팩터 — 동작 동일, 출처가 dict로.) - 상수:
CRITICAL_CODES,FIXUP_CODES,WARN_CODES(structure_lint이 import).CRITICAL_CODES = {"BROKEN_LINK", "BROKEN_MD_LINK"} # PreToolUse block FIXUP_CODES = {"MISSING_SECTION","MISSING_FRONTMATTER", "EMPTY_SELECTION_CRITERION","DANGLING_ANCHOR", "PROJECT_NO_DIAGRAM","PROJECT_NO_BRANCH_TABLE", "UNMAPPED_SOURCE_TYPE"} # PostToolUse exit-2 (완성시) # 그 외(NO_FRONTMATTER 등) → WARN exit 0
4.2 wiki_structure_lint.py --pre (NEW 모드)
- stdin JSON 이벤트 →
wiki_rules.target_path()+projected_content()로 쓰기 후 예상 본문 계산. raw/또는wiki/의.md가 아니면 exit 0.build_vault_index(root)+check_c2(projected_doc, ...)실행.code in CRITICAL_CODES인 finding이 있으면 stderr로 사유 출력 +sys.exit(2)(쓰기 차단).- 백틱 placeholder(
`[[future]]`)는_code_spans가 이미 면제 → forward-ref 정상. - 차단 메시지에 탈출구 명시: "미존재 타깃은 백틱 코드(
`[[slug]]`)로 표기하거나 타깃을 먼저 생성."
- 백틱 placeholder(
- CRITICAL 없으면 exit 0(DANGLING_ANCHOR 등은 PostToolUse가 처리).
- 성능: PreToolUse는 매 쓰기마다 발동 →
build_vault_index(rglob) 비용. 30s timeout 내 여유지만, 캐시 불가(이벤트마다 새 프로세스). projected 본문에서 추출된 위키링크가 0개면 인덱스 빌드 스킵하는 early-exit 추가.
4.3 wiki_structure_lint.py --hook (MOD, fix-up 티어링)
현재: C2 항상 + (완성시) C1/C3 → 전부 stderr 출력 → 항상 exit 0. 변경:
- C2 broken-link는 이미 PreToolUse에서 차단됨 →
--hook에서는 중복 차단 안 함(여전히 출력은 하되 exit 코드엔 미반영). DANGLING_ANCHOR(타깃 존재, 앵커만 부재)는 여기 fix-up 대상. is_completeness_checkable(doc)이고findings중code in FIXUP_CODES가 있으면 → stderr 사유 +sys.exit(2)(fix-up 루프; 파일은 디스크에 있으나 모델이 고침).- 그 외 → 기존처럼 WARN 출력 +
sys.exit(0). - 무삭제:
findings[:10]잘림 시… 외 {len-10}건 (suppressed)1줄 추가(--hook·--pre양쪽).
4.4 settings.json 배선
"PreToolUse": [
{ "matcher": "*", "hooks": [ /* wiki_claim_gate.py (기존) */ ] },
{ "matcher": "Write|Edit|MultiEdit", "hooks": [
{ "type": "command",
"command": "python3 \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/wiki_structure_lint.py --pre",
"timeout": 30 } ] }
],
"PostToolUse": [
{ "matcher": "Write|Edit|MultiEdit", "hooks": [ /* wiki_structure_lint.py --hook (기존, 동작 변경) */ ] }
]
SubagentStart/SubagentStop(claim_gate)는 불변.
5. 영향 분석 / 위험
| 위험 | 완화 |
|---|---|
| PreToolUse C2-block이 클러스터 작성 중 형제 forward-ref를 막음 | 백틱 placeholder(코드가 이미 면제). 차단 메시지에 탈출구 명시. C1/C3는 PreToolUse 차단 안 함(완성시 PostToolUse fix-up). |
매 쓰기 build_vault_index 비용 |
projected 본문에 위키링크 0개면 인덱스 빌드 스킵. 30s timeout. |
| PostToolUse exit-2가 무한 fix-up 루프 | claim_gate가 쓰는 stop_hook_active 가드 패턴 동일 적용 — 재진입 시 통과. |
wiki_rules.py import가 Codex에서 깨짐 |
같은 디렉터리 stdlib import → Codex 동일 스크립트 실행이라 동작. --check(sync_automation)와 무관(훅은 SSOT 1벌). |
| claim_gate 리팩터가 기존 동작 변경 | dict-주도로 바꾸되 동작 동치. 신규 test_wiki_claim_gate.py가 5-prefix 케이스 회귀 고정. |
6. 수용 기준 (검증 가능)
--pre: 미존재 타깃[[ghost]]를 본문에 포함한 Write 이벤트(stdin JSON) → exit 2 + stderr에 사유. 백틱`[[ghost]]`은 exit 0.--pre: 위키링크 0개 본문 →build_vault_index미호출(스킵) + exit 0.--hook:status: verified문서가 필수 섹션 누락 → exit 2.status: draft동일 문서 → exit 0(WARN만).--hook/--pre: findings 11건 → 10건 출력 +… 외 1건 (suppressed).claim_gate: 리팩터 후 기존 5-prefix 차단 케이스 5종 + 통과 케이스가test_wiki_claim_gate.py에서 green.python3 .claude/hooks/test_wiki_structure_lint.py+test_wiki_claim_gate.py전부 통과.wiki_structure_lint.py --all회귀: 본 변경 전후 FAIL 집합 동일(게이트 배선은--all에 영향 없음).- 실제 wiki 문서 1개 정상 작성(완성 frontmatter + 유효 링크) → Pre/Post 훅 모두 통과(exit 0), 정상 저장.
7. 구현 순서 (writing-plans에서 단계화)
wiki_rules.py생성 — claim_gate에서 공유 헬퍼 이관 +PREFIX_REQUIREMENTSdict + severity 상수.wiki_claim_gate.py—wiki_rulesimport,check_markdown_write를 dict-주도로 리팩터(동작 동치).test_wiki_claim_gate.py— 5-prefix 차단/통과 회귀 고정(2 전에 작성 = TDD).wiki_structure_lint.py—--pre모드 +--hookfix-up 티어링 + 무삭제 1줄.test_wiki_structure_lint.py—--pre/fix-up 케이스 추가.settings.json— PreToolUse에--pre배선.- 수동 스모크: §6 수용 기준 1-8 실행.
8. 메모
- 본 spec은 기존 메커니즘 강화(저위험)이며 새 아키텍처를 도입하지 않는다. Workflow/quorum 같은 고위험 변경은 Spec B/D로 분리.
claim_gate와structure_lint의 책임 분리 유지: claim_gate=claim 의미, structure_lint=구조/링크.wiki_rules.py는 공유 기계장치만(정책 아님).