Files
llm-wiki/docs/superpowers/specs/2026-06-01-wiki-structure-lint-design.md
T

108 lines
7.3 KiB
Markdown

# Design: wiki-structure-lint — 결정론적 문서 구조 린터
- 날짜: 2026-06-01
- 대상: `.claude/hooks/wiki_structure_lint.py` (신규), `/depth` 연동, (2차) PostToolUse hook
- 목적: 위키 문서가 **자기 `source_type` 템플릿의 양식을 따르는지** 결정론적으로(파이썬) 검증. "templates대로 안 된 문서"를 코퍼스 전수로 색출하고, P3(옵시디언 링크 문법)와 depth 게이트의 기계적 사전체크를 한 도구로 묶는다.
---
## 0. 배경 / 왜 결정론적 레이어인가
`branch-depth-gate`(LLM 의미 게이트, 별도 스펙)를 설계하다, 검증을 결정론(파이썬) vs 의미(LLM)로 나누는 게 자연스럽다는 결론. 파이썬은 **틀·양식 + 기계적으로 판별 가능한 것**(섹션 존재, 링크 문법, 앵커 실재)만 보고, 의미(claim 깊이 L0/L1, 조건의 진위)는 LLM 감사기가 본다.
여기에 사용자 관찰이 더해짐: **"지금 문서들이 templates대로 안 되어 있는 게 매우 많다."** → 결정론 레이어를 *먼저* 짓는 게 즉시 가치(비적합 문서 색출) + 싼 게이트 우선.
기존 자산과의 경계:
- `wiki_claim_gate.py` (PreToolUse hook, 유지): 특정 표(Claims Extracted 등) 존재를 write 시점에 강제. 본 린터는 이를 일반화(전 source_type · 전 섹션).
- `/lint` (LLM, 유지): 의미 품질(과장·canonical 우회 등). 본 린터는 결정론 구조만.
## 1. 검증 3군 (전부 이진 PASS/FAIL — 심각도 단계 없음)
문서당 결과는 `PASS` 또는 `FAIL + 사유 목록`. 사용자 결정: "양식대로 안 되어있으면 불통."
### C1. 템플릿 적합성
문서는 자기 `source_type` 템플릿의 **필수 + 무마커 섹션 전부** + **frontmatter 키 전부**를 가져야 PASS.
- `(있다면)`·`(있을 때)`·`(전용)`·`(infra 전용)` 등 **optional 마커 섹션**은 없어도 PASS.
- **무마커 섹션 = 필수**로 간주(양식이므로).
- 템플릿에 없는 **추가 섹션은 허용**(불통 아님).
- `source_type`이 어느 템플릿과도 매칭 안 되면 → 불통(`UNMAPPED_SOURCE_TYPE`).
### C2. 옵시디언 링크 문법 (= P3 흡수)
- `` `[[...]]` `` 인라인 코드/백틱에 싸인 위키링크 → 불통(옵시디언이 링크로 렌더 안 함).
- `[[경로]]` 타깃 파일 부재 → 불통(`BROKEN_LINK`).
- `[[파일#앵커]]` 의 앵커가 대상에 부재 → 불통(`DANGLING_ANCHOR`, best-effort: 헤딩 또는 Claim ID 토큰 검색).
- fenced code block(```` ``` ````) 내부의 `[[...]]` 는 예시이므로 **스킵**(false positive 방지). 인라인 백틱은 검출.
### C3. depth 구조 사전체크 (branch-note 만)
- Decision Evidence Map 의 `선택 조건` 셀 비어 있음 → 불통.
- `## 엣지·실패·의존` 섹션 없음/내용 없음 → 불통.
- (신규 템플릿 적용 브랜치에 발동. 기존 80개는 C1에서 "섹션 없음"으로 먼저 걸림.)
## 2. source_type → 템플릿 매핑 (거의 자동 도출)
해석 순서 (2026-06-01 정리 후 — **모든 템플릿이 frontmatter source_type 를 직접 선언**, fallback 제거됨):
1. 각 템플릿 frontmatter `source_type:` 파싱. **다중값은 `|`/`,` 구분**`raw-source-template: official-doc | company-tech-blog | personal-blog`.
2. `daily-task` 는 문서 frontmatter `track:`(develop/infra)으로 분기.
3. (보조) `## source_type 허용값` 섹션 파싱은 backward 호환용으로 유지.
4. 위 어디에도 안 걸리는 source_type → `UNMAPPED_SOURCE_TYPE` 불통.
명시 결과 (빈 source_type 4종 해소): `concept-template ← llm-generated`, `raw-source-template ← official-doc|company-tech-blog|personal-blog`, `interview-template ← interview`, `source-summary-template ← source-summary`(둘 다 wiki/concepts 층이라 구분 필수). 매핑 SSOT 가 전부 템플릿 frontmatter 안에 있음(스크립트 상수 fallback 0).
**nav/hub/meta 제외**: layer 최상위 직속 파일(`wiki/llm-wiki.md`·`wiki/log.md`) + `README.md`/`log.md`/`index.md` 는 템플릿 콘텐츠가 아니므로 린트 스킵. frontmatter 없는 스텁은 섹션 누락 도배 대신 `NO_FRONTMATTER` 1건으로 표면화.
## 3. 헤더 정규화 매칭
번역·괄호주석·순서 차이를 흡수:
- `## Parent / 부모 (필수)` → 선두 `## ` 제거 → 괄호 `(...)` 제거 → `/` 분리 → 각 토큰 strip+소문자 → `{parent, 부모}`.
- 문서 헤더도 같게 정규화. **순서 무관 집합 비교.** 한쪽(한글/영문)만 있어도 매칭.
- optional 판정: 원본 헤더에 optional 마커 정규식(`있다면|있을 때|있으면|전용|optional`) 포함 여부.
## 4. CLI / 출력 / 통합
- **표준 라이브러리만** (claim_gate.py 스타일, 의존성 0). frontmatter 는 라인 파싱(`^key:`).
- 호출:
- `python3 .claude/hooks/wiki_structure_lint.py --file <path>` — 단일. `/depth` 사전체크·hook용.
- `python3 .claude/hooks/wiki_structure_lint.py --all``raw/`·`wiki/` 전수 스윕.
- 출력: 문서별 `PASS` / `FAIL` + 사유(rule code · line · 설명). `--all` 은 끝에 요약(전체 N, FAIL M, source_type별 집계).
- exit code: FAIL 있으면 ≠ 0 (hook/CI 게이트용).
- `/depth` 연동: 먼저 `--file` 결정론 검사 → 구조 불통이면 그것부터 보고, 통과 시 LLM `branch-depth-auditor` 로.
- (2차) PostToolUse hook: 저장 시 C2 자동 검출(P3 목적). 1차는 CLI만.
## 5. 검증 / 수용 기준
- `--all` 이 정상 종료하고 source_type별 집계를 출력.
- 알려진 비적합(`personal-blog`·`meta`·`error` source_type)이 `UNMAPPED_SOURCE_TYPE` 로 잡힘.
- 의도적으로 백틱 래핑한 테스트 링크가 C2 로 잡힘.
- 잘 정비된 문서 1개(예: 최근 branch-note)가 PASS.
- false positive 점검: fenced code block 내 예시 `[[...]]` 가 BROKEN 으로 안 잡힘.
## 6. 자동수정(autofix) — 백틱 래핑만
사용자 결정으로 **안전한 한 종류만** 자동수정 도입: 순수 `` `[[...]]` `` 래핑 → `[[...]]`.
- `--fix` (기본 dry-run, 변경 미리보기) / `--fix --apply` (실제 기록).
- fenced code block 내부·혼합 코드 스팬(`` `foo [[x]] bar` ``)은 **건드리지 않음**(보수적).
- git 미사용 환경이므로 `--apply` 전 **폴더 백업 필수**.
- 2026-06-01 1회 실행 결과: 388파일/3117곳 수정, 백업 `llm-wiki-backup-20260601-214319`.
비목표:
- 그 외 autofix 없음(누락 섹션·frontmatter 는 내용 생성 불가 → 마이그레이션 영역).
- 의미 품질 판정 없음 — LLM `/lint`·`branch-depth-auditor` 담당.
- 멀티 CLI 전파(Codex/Gemini) 범위 밖.
## 6.1 알려진 한계 (구현 중 보정)
- 링크 타깃 해석: `[[x.md]]` 의 `.md` suffix 허용, templates/ 도 유효 타깃으로 인덱싱(초기 FP 2종 수정 완료).
- placeholder 링크(`[[X]]`·`[[URL]]`·`[[double bracket]]` 등 예시 텍스트)가 BROKEN_LINK 로 잡힘 — 향후 예시 라인 스킵 규칙으로 보정 여지.
- 템플릿 드리프트(MISSING_SECTION 571·MISSING_FRONTMATTER 89)는 본 린터의 검출 대상이되, 해소는 **문서→템플릿 마이그레이션**(별도 사이클). 린터가 그 진척도 측정기 역할.
## 7. 산출물
| 파일 | 작업 |
|---|---|
| `.claude/hooks/wiki_structure_lint.py` | 신규 (C1+C2+C3, CLI 2모드) |
| `/depth` (depth 게이트 구현 시) | 사전체크로 본 린터 호출 |
| (2차) `.claude/settings.json` | PostToolUse hook 등록 (C2 자동) |