7.3 KiB
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 제거됨):
- 각 템플릿 frontmatter
source_type:파싱. 다중값은|/,구분 →raw-source-template: official-doc | company-tech-blog | personal-blog. daily-task는 문서 frontmattertrack:(develop/infra)으로 분기.- (보조)
## source_type 허용값섹션 파싱은 backward 호환용으로 유지. - 위 어디에도 안 걸리는 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결정론 검사 → 구조 불통이면 그것부터 보고, 통과 시 LLMbranch-depth-auditor로.- (2차) PostToolUse hook: 저장 시 C2 자동 검출(P3 목적). 1차는 CLI만.
5. 검증 / 수용 기준
--all이 정상 종료하고 source_type별 집계를 출력.- 알려진 비적합(
personal-blog·meta·errorsource_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]]의.mdsuffix 허용, 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 자동) |