Generated banner for LLM Wiki — Obsidian 볼트를 LLM이 유지보수하는 지식 베이스로 운영하기
Generated logo for LLM Wiki — Obsidian 볼트를 LLM이 유지보수하는 지식 베이스로 운영하기

LLM Wiki — Obsidian 볼트를 LLM이 유지보수하는 지식 베이스로 운영하기

LLM wiki kit

First committed by cookyman74

Published in cookyman74/llmwiki-harness. Publisher page

46 GitHub stars

What it is

llmwiki-harness
— the description by cookyman74

AutoGineer overview, from the brain’s own files

llmwiki-harness is a framework designed to turn an Obsidian vault into a living, self-maintaining knowledge base. Unlike standard RAG systems that re-process information on every query, this harness uses LLMs to incrementally ingest, synthesize, and cross-reference information into a structured wiki. It implements a 4-tier memory model (Working, Episodic, Semantic, Procedural) to manage information decay, confidence scoring, and knowledge promotion. The system is built for Claude Code, utilizing a suite of specialized agents and skills to handle wiki operations, linting, and MoC (Map of Content) maintenance, ensuring the knowledge base grows in value over time.

How it works

Memory model: LLM wiki

AutoGineer overview, from the brain’s own files

The system organizes data into three storage layers: raw sources, the wiki, and the schema. It uses a 4-tier memory hierarchy where information is promoted from L1-working (temporary session scratchpad) to L2-episodic (session summaries), L3-semantic (facts and entities with confidence scores), and L4-procedural (repeated methods). The harness uses specialized agents for tasks like ingestion, synthesis, and linting, triggered by specific skills. It maintains an index.md for cataloging and a log.md for chronological tracking, while using frontmatter metadata to manage status, confidence, and decay.

Folders
FolderRoleFiles
.claudeagent-config33
.githubagent-config1
rawraw-sources7
templatestemplates11
teststests3
toolsscripts166
wikiwiki6
develop_docsfolder44
metricsfolder2

The agent writes

Requirements

Databases: PostgreSQL

Works with: Claude Code, OpenCode

Set it up

  1. This brain runs code automatically: 2 hooks (claude-code). Review .claude/settings.json before opening the folder in Claude Code. No step below turns a hook on for you.

    • .claude/settings.json: python3 "$CLAUDE_PROJECT_DIR"/.claude/skills/wiki-lint/scripts/wiki-status-check.py
    • .claude/settings.json: python3 "$CLAUDE_PROJECT_DIR"/.claude/skills/wiki-lint/scripts/stamp-updated.py
  2. Download the brain as a ZIP and unzip it where you keep your notes or project: https://mp-staging.autogineer.com/agent-brains/cookyman74/llmwiki-harness/download
  3. Claude Code reads CLAUDE.md from this folder automatically.

  4. cd llmwiki-harness && claude

  5. AutoGineer scanned these files at commit 139bc84.

  6. First committed by cookyman74. Published in cookyman74/llmwiki-harness. Licence: MIT License.

Usage

AutoGineer overview, from the brain’s own files

  • Ingesting new research papers or meeting notes into the wiki
  • Querying the wiki for synthesized answers based on accumulated knowledge
  • Running automated linting to identify stale information or missing links
  • Maintaining Map of Content (MoC) pages for navigation
  • Compressing session scratchpads into episodic memory
  • Calculating confidence scores for facts based on source evidence
“Ingest the notes in raw/meetings/project-kickoff.md into the wiki.”
The agent extracts key facts, updates relevant entity pages, links the source, and updates the index.md.
“Run a wiki-lint to check for stale information.”
The agent identifies pages with low confidence or faded decay classes and marks them as archive candidates.

Documentation by cookyman74

LLM Wiki — Obsidian 볼트를 LLM이 유지보수하는 지식 베이스로 운영하기

Image: CI Image: Platform Image: Python Image: License: MIT Image: Obsidian Image: Claude Code Image: PRs Welcome

Obsidian을 IDE로, LLM을 프로그래머로, 위키를 코드베이스로. 사용자는 소스를 큐레이션하고 질문하고, LLM이 요약·교차참조·정리·부기(bookkeeping)를 전부 한다.

👉 처음이라면 INTRO.md 부터 — 이게 뭐고 왜 쓰는지 3분 소개.

이 문서 하나만 따라 하면 빈 Obsidian 볼트에서 시작해 자기 유지보수(self-maintaining) LLM 위키 하네스를 그대로 재현할 수 있다.

  • 대상 도구: Claude Code (CLI/데스크톱/IDE)

  • 언어: 노트 본문 한국어 / 파일명 영문 kebab-case

  • 버전: v2.1 (통합 계층 기억 + lazy 인제스트·모델 라우팅) — v1(평평 구조) → v2(4계층·신뢰도·망각) → v2.1(비용 최적화)


목차

  1. 핵심 아이디어

  2. 아키텍처 — 3계층 저장 + 4계층 기억

  3. 디렉토리 구조 전체

  4. 처음부터 재현하기 (Setup)

  5. 하네스 구성요소 — 에이전트·스킬·스크립트

  6. 사용법 — 5가지 오퍼레이션 6.5. 일상 응용 시나리오

  7. v2 메커니즘 상세 7.5. 질의 리트리벌 최적화 (v0.8.4~v0.9.0)

  8. Frontmatter 스키마 레퍼런스

  9. 스크립트 레퍼런스

  10. Obsidian 설정 팁

  11. 하네스 진화·확장

  12. 트러블슈팅

  13. 설계 결정 요약


1. 핵심 아이디어

대부분의 "LLM + 문서" 경험은 RAG다: 파일을 올리면 LLM이 질문마다 관련 조각을 검색해 답을 만든다. 문제는 누적이 없다는 것 — 매 질문마다 지식을 처음부터 재발견한다.

LLM Wiki는 다르다. LLM이 원본 소스와 당신 사이에 영속적인 위키(구조화·상호링크된 마크다운 파일 모음)를 점진적으로 만들고 유지한다. 새 소스를 넣으면 LLM은:

  1. 소스를 읽고 핵심 정보를 추출

  2. 기존 위키에 통합 — 엔티티 페이지 갱신, 요약 수정, 새 데이터가 옛 주장과 충돌하면 표시

  3. 교차참조·모순 표시·종합을 한 번 컴파일해두고 계속 최신화

즉 위키는 복리로 쌓이는 산출물이다. 교차참조는 이미 걸려 있고, 모순은 이미 표시돼 있고, 종합은 이미 읽은 모든 것을 반영한다.

역할 분담:

  • 사람: 소스 큐레이션, 탐색 방향 결정, 좋은 질문

  • LLM: 요약·교차참조·정리·부기 — 위키를 지치지 않고 유지 (한 번에 15개 파일 갱신)

관련 정신: Vannevar Bush의 Memex(1945) — 개인이 큐레이션하는 지식 저장소, 문서 간 연상 경로. 그가 못 푼 것 = "누가 유지보수하나?". LLM이 그 답이다.


2. 아키텍처

2-1. 3계층 저장 (Storage)
계층위치소유규칙
Raw sourcesraw/사용자본문 immutable — 내용 수정 금지, 진실의 원천. 단 인제스트 완료 스탬프(ingested/wiki_source/ingest_status)는 맨 위 메타 블록만 예외(본문 무변경).
The wikiwiki/LLMLLM이 전부 생성·유지. 사용자는 읽기만.
The schemaCLAUDE.md공동구조·규약·워크플로우의 정본. 함께 진화.
2-2. 4계층 기억 (Memory Tiers) — v2 핵심

v1의 한계(모든 정보가 영원히 동등·평평한 구조·품질 통제 없음)를 풀기 위해, 인간 기억 구조를 본떠 정보에 등급·시간·신뢰도를 부여한다. 정보는 아래층(휘발·구체)에서 위층(영속·추상)으로 반복·시간·신뢰도에 따라 승격한다.

raw/  ──▶  L1-working  ──▶  L2-episodic  ──▶  L3-semantic  ──▶  L4-procedural
소스        작업기억         일화기억           의미기억            절차기억
(불변)      세션 스크래치     세션·소스 증거      사실·엔티티(신뢰도)   방법 steps
            세션종료시 비움    "언제 무엇을"       증류된 지식          반복된 절차
계층폴더담는 것대표 파일
L1 작업기억wiki/L1-working/현재 세션 관찰·스크래치(임시, 세션 종료 시 비움)
L2 일화기억wiki/L2-episodic/세션 요약, 소스 증거session-YYYY-MM-DD.md, source-*.md
L3 의미기억wiki/L3-semantic/사실·엔티티·개념 (신뢰도 부여)fact-*.md, entity-*.md, concept-*.md
L4 절차기억wiki/L4-procedural/반복 관찰된 방법 (steps)procedure-*.md
MoC (네비)wiki/moc/큐레이션 주제 지도home-moc.md, {topic}-moc.md

승격 규칙:

  • L1 → L2 (세션 종료 시): L1 관찰을 3–5줄로 압축 → session-*.md 저장 → L1 비움

  • L2 → L3 (lint 시): 같은 주장이 3회 이상 등장 → fact-*.md로 승격 (모순 있으면 보류)

  • L3 → L4 (lint 시): 같은 절차가 2회 이상 관찰 → procedure-*.md로 추출

2-3. 특수 파일 두 개
  • index.md — 내용 지향. 위키 전체 카탈로그(페이지 + 한 줄 요약). 인제스트마다 자동 갱신. 질의 시 여기부터 읽어 관련 페이지를 찾는다.

  • log.md — 시간 지향. append-only 기록. 각 항목 ## [YYYY-MM-DD] <op> | <제목> 프리픽스 고정 → grep "^## \[" log.md | tail -5로 최근 이력 조회.

index.md vs MoC 차이: index는 "빠짐없이"(전수·기계적·평면), MoC는 "이해되게"(선별·큐레이션·계층). 둘 다 유지하되 역할을 섞지 않는다.


3. 디렉토리 구조 전체

llmwiki.obsidian/
├── CLAUDE.md                  # ★ 스키마 — 매 세션 로딩되는 정본 (규약·신뢰도·망각·관계 규칙)
├── CLAUDE.local.md            # 개인 프로필 (gitignore — 공유 안 됨)
├── README.md                  # 이 문서
├── index.md                   # 전수 카탈로그 (자동 갱신)
├── log.md                     # 시간순 append-only 로그
│
├── raw/                       # 원본 소스 (본문 immutable, 인제스트 스탬프만 예외)
│   ├── assets/                # 다운로드한 이미지
│   ├── meetings/              # 회의록 (verbal 소스)
│   ├── notes/                 # 데일리 노트 (스크래치 — pending 스캔 제외)
│   └── ...                    # 자유 분류 (lecture/·article/·books/ 등)
│
├── wiki/                      # LLM 소유 — 4계층 기억
│   ├── L1-working/            # 세션 스크래치 (종료 시 비움)
│   ├── L2-episodic/           # session-*.md, source-*.md
│   ├── L3-semantic/           # fact-*.md, entity-*.md, concept-*.md (신뢰도)
│   ├── L4-procedural/         # procedure-*.md (steps)
│   └── moc/                   # 네비게이션 지도 (home-moc.md 현관 + {topic}-moc.md)
│
├── templates/                 # Obsidian 템플릿
│   ├── meeting.md             # 회의록 (→ raw/meetings/)
│   ├── daily.md               # 오늘의 노트 (→ raw/notes/)
│   └── seeds/                 # 빈 시드 (index·log·home-moc — 배포본 초기화용)
│
├── metrics/                   # 비용대비효과 측정
│   ├── ingest-cost.csv        # 문서당 토큰·페이지·모델 원장 (gitignore)
│   └── cost-report.py         # 모델가중 비용 리포트
│
└── .claude/                   # 하네스
    ├── settings.json          # 훅 (SessionStart 자동 점검 + PostToolUse updated 스탬프)
    ├── agents/                # 전문가 정의 (누가) — 6개
    │   ├── wiki-ingestor.md        (model: sonnet)
    │   ├── wiki-synthesizer.md
    │   ├── wiki-linter.md
    │   ├── wiki-cartographer.md
    │   ├── wiki-consolidator.md
    │   └── meeting-scribe.md       (model: sonnet)
    └── skills/                # 워크플로우 정의 (어떻게)
        ├── wiki-ops/          # ★ 오케스트레이터 (진입점)
        ├── wiki-ingest/       # 인제스트 절차 (lazy)
        │   └── scripts/       # concept-index.py
        ├── wiki-query/        # 질의 절차
        ├── wiki-lint/         # 점검 절차
        │   └── scripts/       # link-audit.py, decay.py, lint-due.py, search.py,
        │                      # ingest-status.py, wiki-status-check.py
        ├── wiki-moc/          # MoC 절차
        ├── wiki-consolidate/  # 통합·배치병합 절차
        │   └── scripts/       # confidence.py, list-claims.py
        ├── weekly-review/     # 주간 리뷰 자동화
        └── meeting-minutes/   # 녹음 → 전사 → 회의록 (로컬/OpenAI 이중 백엔드)
            ├── scripts/       # transcribe.py, apikey.py
            └── references/    # backend-setup.md

4. 처음부터 재현하기 (Setup)

세 갈래가 있다. 하네스 repo를 받았다면 방법 C가 가장 빠르다.

방법 C — git clone로 하네스 배포본 받기 (가장 빠름)

누군가 공유한 하네스 repo(구조·스킬만, 콘텐츠 제외)를 받아 바로 운영을 시작한다.

git clone <하네스-repo-URL> my-wiki && cd my-wiki
  1. 개인 프로필 작성 — CLAUDE.local.md를 만들어 자기 소개를 적는다(gitignore라 공유 안 됨). Claude가 세션마다 로딩해 설명 톤을 맞춘다:

    # CLAUDE.local.md
    ## 목적 / 사용자 프로필
    - 역할: (백엔드 개발자 / 데이터 사이언티스트 / …)
    - 스택: (언어·프레임워크·인프라)
    - 관심/학습: (현재 파고드는 주제)
  2. 시드 복사 — 빈 색인·로그·현관 MoC를 제자리로:

    cp templates/seeds/index.md templates/seeds/log.md .
    cp templates/seeds/home-moc.md wiki/moc/
  3. Claude Code 실행 → /hooks 한 번 열기(훅 로드) → Obsidian에서 볼트 열기.

  4. raw/에 첫 소스를 넣고 "인제스트해줘" → 위키가 자란다.

업데이트 받기: git pull. 하네스(스킬·에이전트·스크립트)만 갱신되고 내 콘텐츠는 그대로다. 내가 하네스를 고쳐 재배포: git add .claude/ CLAUDE.md README.md templates/ && git commit && git push — 콘텐츠·프로필은 .gitignore라 안 올라간다. 볼트 밖(다른 프로젝트·Codex·Cursor 등)에서도 위키를 쓰려면: MCP 서버를 등록한다 — 설치·사용 가이드. 볼트에는 아무것도 설치하지 않는다(5-5 참고).

방법 A — myharness 팩토리로 자동 생성 (권장)

이 하네스는 myharness 플러그인(에이전트 팀 + 스킬 자동 구성 메타 스킬)으로 만들어졌다.

  1. Obsidian 볼트 생성 — Obsidian에서 새 볼트를 만든다 (예: llmwiki.obsidian).

  2. Claude Code를 볼트 디렉토리에서 실행 — cd <볼트경로> && claude.

  3. /init 실행 (선택) — 기본 CLAUDE.md·규약 스캐폴딩.

  4. myharness 실행 — LLM Wiki 아이디어 문서를 인자로 넘겨 하네스 구성 요청:

    /myharness:myharness <LLM Wiki 패턴 설명 + "MoC 네비게이션 포함" 요청>
  5. myharness가 Phase 0~7로 에이전트·스킬·스크립트·CLAUDE.md·훅을 생성한다.

  6. v2 업그레이드 — 개선안(신뢰도·덮어쓰기·망각·4계층·엔티티·자동화)을 다시 myharness에 넘겨 아키텍처 변경을 요청하면 이 문서의 v2 구조로 확장된다.

방법 B — 수동 재현 (구조를 직접 만들기)

myharness 없이 파일을 직접 만들 때. 볼트 루트에서:

1) 디렉토리 스캐폴딩

mkdir -p raw/assets
mkdir -p wiki/L1-working wiki/L2-episodic wiki/L3-semantic wiki/L4-procedural wiki/moc
mkdir -p .claude/agents
mkdir -p .claude/skills/wiki-ops
mkdir -p .claude/skills/wiki-ingest
mkdir -p .claude/skills/wiki-query
mkdir -p .claude/skills/wiki-lint/scripts
mkdir -p .claude/skills/wiki-moc
mkdir -p .claude/skills/wiki-consolidate/scripts
# 빈 폴더가 git/OneDrive에서 유지되도록
find raw wiki -type d -exec touch {}/.gitkeep \;

2) 파일 작성 — 아래 5장·8장·9장의 내용을 각 경로에 만든다:

  • CLAUDE.md (스키마 — 8장 frontmatter + 7장 규칙)

  • .claude/agents/*.md (5개 전문가 — 5-1장)

  • .claude/skills/*/SKILL.md (6개 스킬 — 5-2장)

  • 스크립트 10개 (9장) — 작성 후 chmod +x

  • .claude/settings.json (훅 — 7-6장)

  • index.md, log.md, wiki/moc/home-moc.md (시드)

3) 활성화

# 스크립트 실행권한
chmod +x .claude/skills/wiki-lint/scripts/*.sh .claude/skills/wiki-lint/scripts/*.py
chmod +x .claude/skills/wiki-consolidate/scripts/*.sh .claude/skills/wiki-consolidate/scripts/*.py
chmod +x .claude/skills/wiki-ingest/scripts/*.sh metrics/cost-report.py
  • Claude Code에서 /hooks를 한 번 열어 새 훅을 로드한다 (세션 중 추가된 훅은 설정 watcher가 즉시 못 잡는다 — /hooks 열기 또는 재시작 필요).

사전 요구사항
  • Claude Code 설치

  • python3 (모든 결정적 스크립트 + 훅 — 표준 라이브러리만 사용)

  • (선택) Obsidian — 위키를 사람이 브라우징. 그래프 뷰로 위키 형태를 본다.

크로스플랫폼: 모든 스크립트·훅이 python3 전용이다(bash·grep·awk·jq·rg 불필요). 따라서:

  • macOS·Linux — 그대로 작동 (python3 기본/쉬운 설치).

  • Windows — 네이티브로 작동 (python3만 PATH에 있으면 됨; WSL·Git Bash 불필요). 단 훅 명령이 python3를 부르므로 Windows에서 python만 있으면 python3 별칭을 잡아주거나 PATH에 python3 확보.

  • 줄바꿈은 .gitattributes가 LF로 정규화(Windows CRLF 손상 방지).


5. 하네스 구성요소

핵심 설계: 에이전트(누가) + 스킬(어떻게) 분리. 에이전트는 역할·원칙·프로토콜을, 스킬은 절차·템플릿을 담는다.

실행 모드: 전문가 풀 (서브 에이전트 디스패치). 오퍼레이션마다 전문가 1명을 호출한다 — 동시 협업 팀이 아니다. 한 소스 인제스트는 교차참조·신뢰도 일관성을 위해 단일 에이전트가 통째로 처리한다(분할하면 페이지 충돌).

5-1. 에이전트 (6명)
에이전트역할사용 스킬모델
wiki-ingestor소스 인제스트 (lazy) — L2 증거 + 신규 L3 초안만. 병합·신뢰도·덮어쓰기·관계는 lint 배치로 지연wiki-ingest, wiki-mocsonnet
wiki-synthesizer질의 응답 — index→MoC→L3 탐색, 신뢰도·최신성 반영 인용 답변, 환류wiki-query, wiki-mocopus
wiki-linter건강 검진 — 링크·망각·신뢰도 감사, 승격 후보, 모순 검사wiki-lint, wiki-consolidate, wiki-mocopus
wiki-cartographerMoC 네비게이션 — 허브 생성·갱신, L3/L4 편입, 도달성 점검wiki-mocopus
wiki-consolidator기억 통합 — L1→L2 압축, L2→L3→L4 승격, 신뢰도 재계산 + lazy 후속 배치 병합(기존 페이지 통합·관계 구조화·중복 초안 정리)wiki-consolidate, wiki-mocopus
meeting-scribe전사문 → 회의록 — 결정·액션·미결 분리, 근거 있을 때만 발언 귀속. 2만 자 전사문을 격리 컨텍스트에서 소비meeting-minutessonnet

모델 라우팅(v2.1): 인제스트는 추출·파일쓰기 위주라 sonnet(빈번한 경로를 싸게). 병합 판단·종합·감사 같은 고추론은 나머지 4개 에이전트가 opus로. 측정상 인제스트 페이지당 실질비용 ~4.6×↓·지연 ~3×↓.

각 에이전트 정의 파일(.claude/agents/{name}.md)의 frontmatter는 연결 계약을 담는다:

---
name: wiki-ingestor
description: <언제 이 에이전트를 쓰나 — pushy하게>
tools: Read, Write, Edit, Glob, Grep, Bash, WebFetch
model: sonnet   # 인제스트=sonnet, 나머지 에이전트=opus (모델 라우팅)
skills: [wiki-ingest, wiki-moc]   # 이 에이전트가 쓰는 스킬 (구성 자기평가용)
---

본문 섹션: 핵심 역할 · 작업 원칙 · 입력/출력 프로토콜 · 이전 산출물이 있을 때 · 에러 핸들링 · 협업.

5-2. 스킬 (8개)
스킬유형하는 일
wiki-ops오케스트레이터 (진입점)Phase 0 컨텍스트 확인 → 라우팅 → 오퍼레이션 흐름 조율
wiki-ingest워크플로우(lazy) 소스 → L2 증거 + 신규 L3 초안 (병합·신뢰도·관계·승격은 lint 지연)
wiki-query워크플로우탐색 → 신뢰도 반영 인용 답변 → 환류
wiki-lint워크플로우스크립트 검사 → 망각·신뢰도·supersession 감사 → 배치 승격 → 추론 검사
wiki-moc워크플로우MoC 허브 구조·계층·편입·도달성
wiki-consolidate워크플로우4계층 승격 (L1→L2→L3→L4) + lazy 후속 배치 병합(신뢰도·관계·기존 통합)
weekly-review워크플로우주간 리뷰 자동 생성 — 완료·미결·stale·다음주 우선순위 → query 페이지 파일링
meeting-minutes워크플로우녹음 → 전사 → 회의록. 회의 민감도로 백엔드 분기(로컬 온디바이스 / OpenAI API)

오케스트레이터 frontmatter는 orchestrates: 배열로 조율하는 에이전트를 선언:

---
name: wiki-ops
description: <pushy — 인제스트·질의·린트·MoC·통합 + 후속 키워드>
orchestrates: [wiki-ingestor, wiki-synthesizer, wiki-linter, wiki-cartographer, wiki-consolidator]
---
5-3. 스크립트 (10개, 결정적 — LLM 토큰 0)

계산은 스크립트가, 의미 판단만 LLM이. 상세는 9장.

스크립트위치용도
confidence.pywiki-consolidate/scripts소스 수·유형 → 신뢰도 점수
list-claims.pywiki-consolidate/scriptsL2 주장(claim::) 수집 → 배치 병합·승격
concept-index.pywiki-ingest/scripts기존 L3/L4 카탈로그 (본문 Read 없이 신규/기존 판별 — lazy의 핵심)
decay.pywiki-lint/scriptsEbbinghaus 망각 보존율 계산·스캔
link-audit.pywiki-lint/scripts끊긴 wikilink + 고아 + 누락 타겟(stub_targets)
lint-due.pywiki-lint/scripts마지막 lint 경과일 + L1 미압축 수
ingest-status.pywiki-lint/scriptsraw 인제스트 상태 (done/pending)
wiki-status-check.pywiki-lint/scriptsSessionStart 훅 — lint 경과·L1·미인제스트 알림
search.pywiki-lint/scriptsgrep 기반 위키 본문 검색
cost-report.pymetrics/문서당 토큰·모델가중 비용 리포트
transcribe.pymeeting-minutes/scripts녹음 전사 (로컬/OpenAI) — ffmpeg 정규화·청크·타임스탬프
apikey.pymeeting-minutes/scriptsOpenAI 키 해석·키체인 저장·인증 테스트
5-4. 회의록 파이프라인 — 외부 전송을 코드로 막는다

meeting-minutes는 다른 스킬과 성격이 하나 다르다. 오디오를 외부로 보낼지 말지라는 되돌릴 수 없는 결정을 포함한다.

판정은 회의 성격으로 한다 — 인사·평가·심사·계약·개인정보·보안·고객사 기밀은 로컬 온디바이스(mlx-whisper), 일반 업무회의·기술 논의·일정 조율만 OpenAI API. 애매하면 로컬이다. 오분류 비용이 비대칭이기 때문이다: 로컬 오분류는 시간만 더 들지만, OpenAI 오분류는 되돌릴 수 없다(삭제해도 캐시·색인이 남을 수 있다).

이 규칙을 SKILL.md에만 적으면 잊힌다. 그래서 스크립트가 --confirm-upload 없이는 openai 백엔드를 실행 자체를 거부한다. 플래그를 붙이는 행위가 곧 판정을 마쳤다는 기록이 된다.

$ transcribe.py --backend openai --input 회의.m4a
openai 백엔드는 오디오를 외부 서버로 전송한다.
민감 회의(인사·평가·심사·계약·개인정보)라면 --backend local 을 쓰라.
exit=1     # 네트워크 호출 0회

산출물은 둘로 나뉜다. 전사 원문은 raw/meetings/transcripts/에 근거로 보존하고, 회의록만 raw/meetings/에 남겨 위키로 인제스트한다. 63분 녹음은 2만 자를 넘어 회의록의 가독성을 파괴하고 인제스트 토큰을 낭비한다. 회의록 작성은 meeting-scribe에 위임해 메인 컨텍스트가 전사문으로 오염되지 않게 한다.

화자 분리는 지원하지 않는다. Whisper 계열은 "누가 말했는지"를 출력하지 않는다. meeting-scribe는 근거(호명·자기소개·역할 명시)가 있을 때만 발언을 귀속하고, 나머지는 화자 없이 기록한다. 심사·평가 회의에서 잘못된 발언 귀속은 내용 누락보다 해롭기 때문이다.

5-5. MCP 서버 (tools/llmwiki-mcp/) — 볼트 밖에서 위키 쓰기

지금까지의 구성요소는 모두 볼트 안에서 도는 Claude Code를 전제한다. 그런데 위키가 가장 아쉬운 순간은 대개 볼트 밖이다 — 다른 저장소에서 코드를 고치는 중, Codex CLI로 작업하는 중, Cursor에서 문서를 쓰는 중. 그때 위키를 열어 읽는 대신 에이전트가 위키에서 답을 찾게 하는 것이 이 서버다.

설치·사용법은 별도 문서에 있다 → tools/llmwiki-mcp/docs/install-and-usage.ko.md (준비물, 3분 빠른 시작, 클라이언트 8종 등록, 사용법, 환경변수, 문제 해결, 업데이트·제거)

obsidian-llmwiki-mcp는 볼트를 읽기 전용으로 여는 MCP 서버다(Node ≥20, 사전 설치물은 Node 하나). 7.5장의 리트리벌(렉시컬 seed → 관계 1홉 확장 → MoC 멤버 → 옵션 BM25 rerank → claims 팩)을 도구 4개로 노출한다 — wiki_expand(진입점) · wiki_pack(주장·신뢰도 팩) · wiki_read_page(전문) · wiki_search(fallback). Claude Code(다른 프로젝트)·Codex·Gemini·agy·Cursor·Windsurf·Claude Desktop·VS Code 8종이 같은 서버를 등록해 쓴다. 등록은 클라이언트마다 한 줄이고, 스니펫은 서버의 print-config 가 만들어 준다.

볼트 안에서는 아무것도 바뀌지 않는다. wiki-query·wiki-ops는 지금처럼 search.py·scope-expand.py를 직접 부른다. 서버는 같은 규칙을 옮긴 것이고, 두 경로가 같은 결과를 낸다는 것을 패리티 테스트가 지킨다 — 볼트 안이든 밖이든 같은 답을 받는다.

위키는 읽기만 한다. 이 서버에는 파일을 쓰는 코드가 없고, 네트워크 연결도 LLM 호출도 하지 않는다. 인제스트·파일링·린트는 계속 볼트 하네스의 일이다. 그래서 설치·제거가 볼트에 아무 흔적을 남기지 않는다.

영문 상세 레퍼런스(도구 계약·상한·보안 경계)는 tools/llmwiki-mcp/README.md, 서버를 고칠 때 지켜야 할 규칙과 설계·검토 기록은 CLAUDE.md의 동기 규칙과 develop_docs/v0.8.6/에 있다.


6. 사용법 — 5가지 오퍼레이션

모든 작업의 진입점은 wiki-ops 오케스트레이터다. 위키 관련 요청을 하면 자동 트리거되고, 알맞은 전문가로 라우팅한다. 자연어로 요청하면 된다.

6-1. Ingest (소스 인제스트)

언제: 새 소스를 위키에 넣을 때.

1. raw/ 에 소스 파일을 넣는다 (예: raw/attention-is-all-you-need.md)
   — Obsidian Web Clipper로 웹 기사를 markdown으로 저장하면 편하다.
2. Claude Code에서: "raw/attention-is-all-you-need.md 인제스트해줘"

LLM이 하는 일 (v2.1 lazy — 싸고 빠르게):

  1. 소스 읽기 (본문 immutable) + 유형 판별 (공식/코드/일반/구두) + 중복 스탬프 체크

  2. 기존 개념은 concept-index.py 카탈로그로만 파악 (페이지 본문 Read 안 함 — 위키가 커도 O(1))

  3. wiki/L2-episodic/source-*.md 증거 페이지 — 핵심 사실 전부 claim::로 (재등장 신호)

  4. 신규 개념만 wiki/L3-semantic/ 초안 생성 (confidence 0.6 고정). 기존과 겹치면 손대지 않음

  5. 신규 L3만 MoC에 가볍게 편입 + index.md·log.md + raw 맨 위 스탬프

  6. 신규 페이지·claim 수 보고 + "병합·신뢰도·관계·승격은 다음 lint 배치" 명시

왜 lazy? v2는 인제스트마다 기존 페이지를 전수 Read하고 병합·관계까지 해서 위키가 클수록 토큰이 O(n)로 폭증했다. v2.1은 인제스트를 증거 남기기로 가볍게 하고, 병합은 본질적으로 효율적인 누적 후 lint 배치로 옮겼다(→ §6-5, §7).

6-2. Query (질의)

언제: 위키에 질문하거나 비교·분석이 필요할 때.

"우리 스택에서 PostgreSQL을 왜 쓰지? 근거랑 신뢰도까지"
"transformer와 RNN 비교표 만들어줘"

LLM이 하는 일:

  1. index.md → 관련 MoC → L3 페이지 탐색 (없으면 search.py)

  2. confidence·status·last_confirmed 확인

  3. stale 페이지는 주근거로 안 씀 (superseded_by 따라 현재 페이지로)

  4. 각 주장에 신뢰도 + 출처 인용 병기

  5. 재사용 가치 있는 답은 위키에 환류 제안 → 승인 시 L3에 저장 (탐색이 복리로 쌓임)

6-3. Lint (건강 검진)

언제: 주기적으로, 또는 SessionStart 훅이 "3일+ 경과" 알릴 때.

"위키 점검해줘"  /  "위키 린트"

LLM이 하는 일 (6단계):

  1. 스크립트: link-audit.py(끊긴 링크·고아) + decay.py(망각 보존율)

  2. 망각 처리: faded(R<0.3) 페이지 → 재확인 또는 아카이브 후보 (자동 삭제 금지)

  3. 신뢰도·supersession 감사: confidence 정합, stale 짝 링크 확인

  4. 통합 승격: L2→L3, L3→L4 후보 제안

  5. 추론 검사: 모순, 낡은 주장, 누락 개념, 빠진 관계

  6. 보고: 심각도 정렬 수리 목록 + 새 질문·소스 제안

lint 후 반드시 ## [날짜] lint | ...를 log에 남긴다 — 자동화 훅이 경과일을 이 항목으로 계산한다.

6-4. MoC (네비게이션 정리)

언제: 주제 지도를 만들거나 갱신할 때.

"llmops 관련 MoC 만들어줘"  /  "네비게이션 정리해줘"

LLM이 하는 일: wiki/moc/{topic}-moc.md 허브 생성/갱신, home-moc 계층 유지, 새 L3/L4 페이지 편입, 모든 L3/L4 페이지가 ≥1 MoC에서 도달 가능한지 점검(고아 방지).

6-5. Consolidate (기억 통합)

언제: 세션 마무리 시(L1→L2), 또는 lint 중(L2→L3→L4).

"세션 마무리하자"  /  "L1 압축해줘"  /  "통합해줘"

LLM이 하는 일:

  • L1→L2: L1 관찰 3–5줄 압축 → session-YYYY-MM-DD.md → L1 비움

  • L2→L3: 같은 주장 3회+ → fact-*.md 승격 (모순 시 보류)

  • L3→L4: 같은 절차 2회+ → procedure-*.md 추출

  • 배치 병합 (lazy 인제스트 후속, v2.1): lazy가 미룬 일을 lint에서 일괄 — 신규 개념 ↔ 기존 페이지 병합, sources: 추가·신뢰도 재계산(claim 재등장 카운트), ## 관계 구조화, 중복 초안 정리. 병합은 여러 소스가 쌓인 뒤 한 번에 보는 게 효율적(소스마다 전수 스캔 회피).


6.5 일상 응용 시나리오 (단순 Q&A를 넘어)

단순 질문은 이 위키의 일부만 쓴다. 핵심 가치는 인제스트→축적→복리. 하루 업무 3블록 + 주간 리뷰에 매핑한 응용방안(외부 PKM/LLM-wiki 사례 리서치 기반).

① 회의 → 회의록 파이프라인
  • 회의 직후 인제스트 → action item·결정사항을 claim으로 자동 추출.

  • 회의 전 3분 브리핑 — "이 안건 지난 논의 뭐였지?" → 질의유형 라우팅으로 즉답(빠른 사실브리핑).

  • 결정 로그(ADR) — 결정 번복 시 supersession(status: stale + superseded_by)으로 "왜 바꿨나" 이력 보존.

② 학습 → 지식 복리 + 자동 복습
  • 아티클/강의 인제스트 → concept 페이지 축적(흩어진 북마크 대신 연결된 지식).

  • 신뢰도(confidence)로 이해도 추적 — 1소스 0.6="얕게 봄", 3소스 0.95="여러 각도 확인".

  • 망각(forgetting)이 복습 엔진 — 보존율 하락 시 lint가 "복습 대상" 표시(Ebbinghaus). 일반 PKM엔 없는 기능.

③ 개발 → 운영지식·트러블슈팅 축적
  • 트러블슈팅 로그 — 장애·해결 인제스트 → 같은 절차 2회+ 시 L4 procedure 자동 승격(런북화).

  • 기술결정 기록 — "왜 X 대신 Y" concept + supersession.

  • 운영지식(llmops·모니터링) — entity/procedure로 축적.

④ 주간 리뷰 (최고 ROI)

완료·미결(open loops)·stale·다음주 우선순위를 한 장으로 종합 → "몇 시간→몇 분". weekly-review 스킬로 자동화(/weekly-review, 금요일). 회고·계획·상급자 보고 초안을 한 번에.

이 위키만의 차별점 (일반 Notion+AI 대비)
기능일상 용도
질의유형 라우팅회의 전 3분 브리핑(빠름)
confidence보고 신뢰도·이해도 추적
forgetting자동 복습 큐(학습)
supersession결정 번복 이력(ADR)
L3→L4 승격반복 절차→런북
session 압축매일 작업 로그 자동

참고: Karpathy LLM Wiki(지식=컴파일된 코드) · AI Second Brain(회의노트→action item·주간리뷰 자동화) · living knowledge base. 상세 매핑은 위키 페이지 concept-wiki-daily-usage.


7. v2 메커니즘 상세

v2.1 타이밍 주의: 아래 신뢰도·덮어쓰기·관계는 개념이고, v2.1에서는 인제스트가 아니라 lint 배치 시점에 계산·구조화된다(lazy). 인제스트는 신규 개념에 confidence 0.6만 고정으로 붙이고, 재계산·병합·관계는 미룬다.

7-1. 신뢰도 점수 (Confidence)

주장의 확실성을 0.0~1.0으로. 소스 수·유형에서 결정적으로 계산 (confidence.py).

조건점수
소스 1개0.60
소스 2개0.85
소스 3개 이상0.95
소스 0개 (미확인)0.30
+ 공식 문서/코드 포함+0.10
− 구두 정보(slack/회의록)−0.10

최종값은 [0.0, 1.0]로 클램프. L3/L4 페이지 frontmatter confidence:에 기록하고, 질의 답변에 병기한다.

python3 .claude/skills/wiki-consolidate/scripts/confidence.py --count 3 --official   # → 1.0
python3 .claude/skills/wiki-consolidate/scripts/confidence.py --kinds official,normal,verbal   # → 0.95
7-2. 덮어쓰기 (Supersession)

새 정보가 옛 정보와 다르면 삭제하지 않고 명시 대체:

  • 옛 페이지: status: stale + superseded_by: [[새-페이지]]

  • 새 페이지: supersedes: [[옛-페이지]]

  • 서로 링크, 타임스탬프 기록 → 이력 보존

어느 쪽이 맞는지 불명한 단순 상충은 대체가 아니라 > [!warning] 상충 콜아웃으로 두 출처를 병기한다.

7-3. 망각 (Forgetting) — Ebbinghaus 곡선

시간이 지나면 정보의 보존율이 지수적으로 감쇠한다. 재확인하면 리셋.

보존율 R = exp(−Δt / S) — Δt = 마지막 확인 후 경과일, S = 안정성(decay_class가 결정).

decay_classS(일)성격
architecture365핵심 설계 결정 — 천천히 망각
procedure365L4 절차 — 오래 감
concept / semantic120의미 개념
entity90엔티티
episodic21세션·소스 기록 — 바램
transient7일시적 버그·일회 관찰 — 빠르게 망각
(default)90

보존율 밴드: R≥0.5 fresh / 0.3≤R<0.5 aging(재확인 권장) / R<0.3 faded(아카이브 후보 — 자동 삭제 금지).

리셋: 재확인 시 last_confirmed: 오늘 → Δt=0 → R=1.0.

lint가 decay.py --scan으로 faded 페이지를 표시한다.

7-4. 통합 계층 (Consolidation Tiers)

2-2장 참조. 요지: 반복·시간·신뢰도로 정보를 차등. 자주 확인되는 것은 위로 올라가 오래 살고(L4 절차), 한 번 스친 것은 아래에서 망각된다(L1/transient). 사람 기억이 그렇게 작동한다.

7-5. 엔티티 관계 (Entity Relations)

엔티티 페이지 ## 관계 섹션에 구조화된 관계를 기록 (Obsidian 인라인 필드 :: — Dataview 질의 가능):

## 관계
- caused :: [[incident-x]] (sources: 3, confidence: 0.9)
- depends_on :: [[postgres]] (sources: 1, confidence: 0.6)
- fixed :: [[bug-123]] (sources: 2, confidence: 0.85)

관계 유형: uses · depends_on · contradicts · caused · fixed · supersedes. 각 관계에 소스 수·신뢰도 병기.

7-6. 자동화 (Automation)

정직한 한계: Claude Code 훅은 셸만 실행 — LLM lint를 스스로 못 돌린다. 그래서 훅은 조건 검사 + 알림까지만 한다.

두 개의 훅 (.claude/settings.json):

  1. SessionStart 훅 — wiki-status-check.py

    • 세션 시작 시 1회 실행 (Stop 훅은 매 턴 발화 = 스팸이라 회피)

    • lint-due.py로 마지막 lint 3일+ 경과 또는 L1 미압축 감지

    • 해당 시 모델 컨텍스트에 알림 주입 → Claude가 능동으로 lint/consolidate 제안

    • 아무것도 안 걸리면 {} (조용)

  2. PostToolUse 훅 — updated: 스탬프

    • Write|Edit로 .md 파일을 건드리면 frontmatter updated:를 오늘로 자동 갱신

진짜 자율 백그라운드 lint를 원하면 → Claude Code /schedule (클라우드 크론 에이전트)로 별도 배선. 예: "매주 월요일 위키 lint 돌려줘".


7.5 질의 리트리벌 최적화 (v0.8.4~v0.9.0)

질의 응답이 질의당 52~62k 토큰을 쓰던 문제(index 통독 + 광역 MoC 스윕)를 결정적 그래프 스코프 + 질의유형 라우팅으로 최적화했다. A/B 9라운드 실측·매 릴리즈 외부감사(agy). 상세: develop_docs/v0.8.3/TECH-SUMMARY.md, develop_docs/v0.8.5/COMBO.md, 실측 원장 metrics/ab-results.csv.

핵심 이동
[구식]  질의 → index(218줄) 통독 → MoC 다수 → 페이지 12~19 full-read → 종합
[신식]  질의 → scope-expand.py(0토큰): lexical seed → 관계 1홉 확장 → (rerank) → claims 팩
       → synthesizer: 팩 1-read → 부족분만 full-read → 종합
적용 기술과 근거자료
기술근거우리 구현
그래프 확장 스코프GraphRAG — 지식그래프 엣지 탐색scope-expand.py expand: [[링크]] 파싱, lexical seed→관계 1홉(아웃·인·MoC멤버). 순수 lexical이 못 닿는 관계연결 페이지 도달
claims 컨텍스트 팩Parent-Document Retriever(small-to-big)팩(claims)=small-side, 페이지 full=parent. 팩 1-read로 다중 페이지 대체
질의유형 라우팅Adaptive-RAG(질의복잡도 라우팅)조회=seed·사실브리핑=rerank+팩·절차=full-read. 하나의 전략은 만능 아님
BM25 rerankAnthropic Contextual Retrieval(reranking 실패 49→67%↓)임베딩 없이 BM25(0토큰)로 확장 pool을 질의관련도 top-N 축소
팩에 관계 포함 (v0.8.6)관계질의=관계 실린 사실브리핑pack이 ## 관계(uses/depends_on) 줄도 추출(+300토큰). 관계타겟(connector 등)을 새 라우트 없이 커버

요약: 우리 claims 팩 = parent-retriever의 small-side였고, rerank·라우팅이 빠진 조각이었다. Adaptive-RAG로 질의유형을 나누고, Contextual Retrieval의 reranking을 BM25로 얹어 완성.

질의유형별 라우팅 (파라미터)
유형스코프 명령전달
단일조회scope-expand.py expand … --max 6seed 목록
사실브리핑·비교… --max 20 --rerank 11claims 팩
절차·how-to… --max 8(rerank 안 함)후보 full-read(팩 없음)
실측 결과 (동일조건 A/B)
질의유형구식(baseline)신식개선
사실브리핑50.6k / 14 tool22.7k / 1 tool토큰 −55%·tool −93%
절차·how-to39.5k / 8~38k / 10동률(절차는 스코핑 이득 없음 — 본질적으로 다페이지 세부 필요)
교훈 (실측 검증)
  • 관계 도달은 lexical로 안 됨 → 그래프 1홉 확장 필수.

  • 팩은 사실질의에만 이득 → 절차질의는 full-read 라우팅.

  • rerank는 최대 레버지만 다이얼 → 조이면 싸지고 recall 준다(11이 균형).

  • recall의 진짜 레버는 --max가 아니라 키워드 품질(엔티티 포함).

  • 절차질의 비용은 근본적 → 스코핑으로 못 줄임. 반복비용은 환류 캐시(query 페이지)로만.

  • 관계질의는 새 유형 아님 → 팩에 관계 실으면 사실브리핑 라우트로 커버.

전체 근거자료·구현 상세·A/B 원장: develop_docs/v0.8.3/TECH-SUMMARY.md. 관련 리서치 — GraphRAG · Parent-Document Retriever · Adaptive-RAG · Anthropic Contextual Retrieval · Contextual Compression · Self-RAG · Stop-RAG · AB-RAG.


8. Frontmatter 스키마 레퍼런스

모든 위키 페이지 상단에 YAML frontmatter. 파일이 type:을 가져야 위키 페이지로 인식된다(스크립트가 CLAUDE.md 같은 비-페이지를 걸러냄).

---
type: working | episodic | session | source | fact | entity | concept | procedure | moc | index | log | query
title: <한국어 제목>
aliases: []                  # 영문 파일명의 한국어 라벨/별칭
tags: []                     # 소문자, 예: [llm, training]
created: YYYY-MM-DD
updated: 2026-09-09

# --- L3/L4 전용 (신뢰도·망각) ---
confidence: 0.0~1.0          # confidence.py로 계산
sources: [소스-슬러그, …]     # 근거 소스 (개수가 신뢰도 결정)
last_confirmed: YYYY-MM-DD    # 재확인마다 갱신 → 망각 리셋
decay_class: architecture | procedure | concept | entity | episodic | transient
status: active | stale        # 대체된 정보는 stale (삭제 안 함)
superseded_by: [[새-페이지]]   # (옛 페이지에) 대체됨
supersedes: [[옛-페이지]]      # (새 페이지에) 대체함

# --- source 페이지 전용 ---
source_url: <URL 또는 raw 경로>
source_kind: official | code | normal | verbal
---

raw 파일 인제스트 스탬프 (raw/ 원본 맨 위에만 추가 — 본문 무변경, 완료 표시·중복 방지):

---
ingested: YYYY-MM-DD
wiki_source: [[source-<slug>]]   # 생성된 L2 증거 페이지
ingest_status: done
---

raw 본문은 immutable이지만 이 상태 스탬프는 예외. ingest-status.py가 이 표시로 done/pending을 판별한다.

규약 요약:

  • 본문 한국어, 파일명 영문 kebab-case ([[wikilinks]] 깔끔)

  • [[wikilinks]] 양방향, 없는 페이지 링크는 스텁으로 남김

  • 모든 L3/L4 페이지는 ≥1 MoC 도달 가능 (고아 방지)

  • 소스 immutable, 충돌은 병기 또는 supersession

  • 파일 이동·이름변경은 승인 후 (wikilink 깨짐)


9. 스크립트 레퍼런스

confidence.py
# 소스 수 + 유형으로 신뢰도 계산
python3 .claude/skills/wiki-consolidate/scripts/confidence.py --count 2                    # 0.85
python3 .claude/skills/wiki-consolidate/scripts/confidence.py --count 1 --official         # 0.7
python3 .claude/skills/wiki-consolidate/scripts/confidence.py --kinds official,normal      # count=2, +official → 0.95
python3 .claude/skills/wiki-consolidate/scripts/confidence.py --count 3 --json             # {"score":0.95,...}

소스 유형: official|code(+0.1) · verbal(−0.1) · normal.

decay.py
# 단일 페이지 보존율
python3 .claude/skills/wiki-lint/scripts/decay.py --class architecture --last 2026-01-01   # "0.57 fresh"
python3 .claude/skills/wiki-lint/scripts/decay.py --class transient --last 2026-07-01       # "0.04 faded"
# 위키 전체 스캔 — faded/aging 페이지 나열
python3 .claude/skills/wiki-lint/scripts/decay.py --scan . --json

--today YYYY-MM-DD로 기준일 지정 가능(기본 오늘). last_confirmed 없으면 updated: 사용.

python3 .claude/skills/wiki-lint/scripts/link-audit.py .           # 사람이 읽는 리포트
python3 .claude/skills/wiki-lint/scripts/link-audit.py . --json    # {broken, orphans, ...}
  • broken: 대상 페이지가 없는 [[wikilink]] ((page,target) 중복 제거)

  • orphans: inbound 링크 0인 페이지 (단, index/log/working/episodic/session/source 유형은 제외 — 정상적으로 inbound 0)

  • stub_targets: 없는 페이지를 가리키는 링크를 참조 페이지 수(inbound)순으로 집계 → 여러 페이지가 가리키는 스텁 = 누락 개념 최우선 작성 후보. (의도된 스텁과 오타 broken을 구분하는 신호)

  • HTML 주석·코드펜스 안의 링크는 무시. type: 있는 파일만 페이지로 취급.

lint-due.py
python3 .claude/skills/wiki-lint/scripts/lint-due.py . 3      # 임계 3일
# 출력: "DUE 5" | "OK 2" | "NEVER"  +  "L1:<미압축 페이지 수>"

log.md의 마지막 ## [날짜] lint 항목으로 경과일 계산.

wiki-status-check.py

SessionStart 훅이 호출. lint-due.py 결과로 알림 JSON을 만든다. DUE거나 L1>0이면 {"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"..."}}, 아니면 {}.

search.py
python3 .claude/skills/wiki-lint/scripts/search.py "transformer"      # wiki/ 전체 grep (rg 미의존)
list-claims.py
python3 .claude/skills/wiki-consolidate/scripts/list-claims.py .      # L2-episodic의 claim:: 라인 수집

같은 주장이 3회+ 등장하는지 LLM이 클러스터링하는 입력(배치 병합·승격).

concept-index.py
python3 .claude/skills/wiki-ingest/scripts/concept-index.py .         # 기존 L3/L4 카탈로그
# 출력: slug | title | aliases | confidence  (frontmatter만, 한 줄씩)

lazy 인제스트의 핵심 — 45개 페이지 본문을 Read하는 대신 이 한 출력만 보고 개념이 기존인지 신규인지 판별한다(토큰 O(1) 유지).

cost-report.py
python3 metrics/cost-report.py                # 문서당 토큰·페이지 효율 표
python3 metrics/cost-report.py --by-model     # opus vs sonnet 비교 (모델가중 wcost)

metrics/ingest-cost.csv(오케스트레이터가 인제스트마다 append)를 읽어 tok/page·모델가중 비용을 낸다. v2/v2.1 비용대비효과 추적용. MODEL_PRICE 상대단가 opus 5·sonnet 1·haiku 0.25.

ingest-status.py
python3 .claude/skills/wiki-lint/scripts/ingest-status.py .           # raw/ 인제스트 상태
# 출력: DONE <date> <file> | PENDING <file>  +  "ingested: N   pending: M"

raw 파일 맨 위 스탬프(ingest_status: done)를 읽어 인제스트 완료/대기를 판별. raw 본문은 immutable — 스탬프는 맨 위 메타 블록만 추가(본문 무변경). Obsidian에서 각 소스에 완료 표시가 보이고, 중복 인제스트를 막는다. wiki_source 필드는 생성된 L2 증거 페이지로 클릭 이동. 미인제스트 소스는 SessionStart 훅도 알린다.

환경 주의: 스크립트는 grep·python3만 의존한다. rg(ripgrep)는 비로그인 셸 PATH에 없는 경우가 많아 쓰지 않는다.


10. Obsidian 설정 팁

  • Web Clipper — 브라우저 확장. 웹 기사를 markdown으로 변환해 raw/에 빠르게 저장.

  • 이미지 로컬 다운로드 — Settings → Files and links → Attachment folder path를 raw/assets/로. Settings → Hotkeys에서 "Download attachments for current file"을 단축키(예: Ctrl+Shift+D)로 바인딩. 클리핑 후 눌러 이미지 로컬 저장 → LLM이 이미지를 직접 참조 가능(끊기는 URL 대신).

    • 단, LLM은 인라인 이미지가 섞인 md를 한 번에 못 읽는다 — 텍스트 먼저 읽고, 필요한 이미지만 별도로 본다.

  • 그래프 뷰 — 위키 형태(허브·고아)를 보는 최고의 도구.

  • Dataview 플러그인 — frontmatter(confidence·tags·sources·관계 ::)로 동적 테이블·리스트 생성.

  • Marp 플러그인 — 위키 내용에서 슬라이드 덱 직접 생성.

  • git — 위키는 결국 markdown git 레포. 버전 이력·브랜치·협업 무료.

템플릿 (회의록·오늘의 노트)

templates/에 두 템플릿이 있다. Obsidian 코어 플러그인으로 연결한다.

회의록 — templates/meeting.md → raw/meetings/

  • 성격: 회의는 구두 소스(source_kind: verbal) — 인제스트 시 신뢰도 −0.1.

  • 섹션: 안건 / 논의·결정 / 액션. frontmatter=메타(date·attendees), 본문=내용, 파일명=제목(중복 없음).

  • 연결: 설정 → 코어 플러그인 Templates 켜기 → 템플릿 폴더 templates → 새 노트에서 Cmd+P → "Insert template" → meeting.

  • 흐름: 회의 중 기록 → raw/meetings/에 저장 → "인제스트해줘" → 결정·액션이 위키에 통합(ingest-status가 자동으로 pending 감지).

오늘의 노트 — templates/daily.md → raw/notes/

  • 성격: 개인 스크래치(할 일·배운 것). 지식이 아니라 타임라인 → 인제스트해도 L2-episodic 기록만, L3 개념 생성 안 함.

  • 섹션: 할 일 / 한 일·배운 것 / 메모. 파일명=날짜.

  • 연결: 설정 → 코어 플러그인 Daily notes 켜기 → New file location raw/notes · Template file location templates/daily · Date format YYYY-MM-DD → 달력 아이콘/"Open today".

  • 주의: raw/notes/는 인제스트 pending 스캔에서 제외돼 있다(매일 노트가 알림을 시끄럽게 안 만들게). 특정 날 노트를 위키에 넣고 싶으면 수동 인제스트.

동적 템플릿: 날짜 자동·참석자 입력이 필요하면 Templater 커뮤니티 플러그인. {{date}} → <% tp.date.now("YYYY-MM-DD") %>, 입력받기 <% tp.system.prompt("제목") %>. 폴더 템플릿으로 raw/meetings/·raw/notes/에 새 노트 만들 때 자동 적용도 가능.


11. 하네스 진화·확장

하네스는 정적 산출물이 아니라 진화하는 시스템이다.

  • 피드백 반영 경로:

    피드백 유형수정 대상
    결과물 품질해당 에이전트의 스킬 (.claude/skills/*/SKILL.md)
    에이전트 역할에이전트 정의 (.claude/agents/*.md)
    워크플로우 순서오케스트레이터 (wiki-ops/SKILL.md)
    트리거 누락스킬 description
    신뢰도 규칙confidence.py (base_for·모디파이어)
    망각 속도decay.py STABILITY 딕셔너리
    승격 임계wiki-consolidate/SKILL.md (3회/2회)
  • 모든 변경은 CHANGELOG.md(하네스 변경 이력)에 기록 — 하네스가 어떻게 진화했는지 추적, 퇴행 방지. (2026-09-11 에 CLAUDE.md 에서 분리 — CLAUDE.md 는 현재 규칙만 둔다)

  • 재-myharness: 큰 아키텍처 변경은 /myharness:myharness에 개선안을 넘겨 자동 확장. update 인자로 팩토리 정본 변경을 빌드된 하네스에 재전파(사용자 수정 보존).

튜닝 예시 — 망각을 더 느리게:

# .claude/skills/wiki-lint/scripts/decay.py 의 STABILITY 수정
STABILITY = { "architecture": 730, ... }   # 365 → 730일

12. 트러블슈팅

증상원인해결
SessionStart 알림이 안 뜬다세션 중 추가된 훅을 watcher가 미로드Claude Code에서 /hooks 한 번 열기 또는 재시작
updated: 자동 스탬프 안 됨위와 동일 (훅 미로드)/hooks 열기
스크립트 "command not found: rg"비로그인 셸 PATH에 rg 없음이미 grep 기반이라 무관 — 커스텀 스크립트 추가 시 rg 대신 grep 사용
link-audit.py가 CLAUDE.md를 고아로 잡음구버전최신 버전은 type: 없는 파일 제외 — 스크립트 갱신
볼트가 비어 보임 (파일 없음)OneDrive 클라우드 미동기Finder에서 "이 기기에 항상 유지" 또는 OneDrive 동기 강제
위키 페이지가 검색 안 됨frontmatter type: 누락모든 페이지에 type: 부여 (스크립트가 페이지 판별 기준으로 씀)
decay.py가 페이지를 스캔 안 함last_confirmed·updated 둘 다 없음frontmatter에 날짜 필드 추가

검증 명령 모음:

# 링크 정합
python3 .claude/skills/wiki-lint/scripts/link-audit.py .
# 스크립트 문법
for s in $(find .claude/skills -name '*.sh'); do bash -n "$s"; done
for p in $(find .claude/skills -name '*.py'); do python3 -m py_compile "$p"; done
# 훅 JSON 유효성
jq -e '.hooks' .claude/settings.json

13. 설계 결정 요약

결정이유
전문가 풀(서브 에이전트), 팀 아님오퍼레이션은 서로 다른 시점에 1명씩 실행 — 동시 협업 불필요. 인제스트는 단일 에이전트가 통째로(교차참조 일관성)
계산은 스크립트, 판단은 LLM링크·신뢰도·망각은 결정적 → 스크립트가 공짜로. LLM 예산은 의미 검사(모순·승격 동치)에만
grep 기반 스크립트rg는 비로그인 셸 PATH에 없는 경우가 많음
SessionStart 훅 (Stop 아님)Stop은 매 턴 발화=스팸. SessionStart는 세션당 1회
훅은 알림까지만훅은 셸만 실행 — LLM lint 자동 실행 불가. 진짜 자율은 /schedule
stale 마킹, 삭제 안 함이력 보존 — 언제 무엇이 왜 바뀌었는지 추적
단일 런타임 (Claude Code)Codex 미사용 — AGENTS.md/.codex 중복 동기 부담 회피
index.md + MoC 병존index=전수(기계적), MoC=큐레이션(계층). 임베딩 RAG 없이 수백 페이지 항해
lazy 인제스트 (v2.1)인제스트는 증거+신규초안만, 병합·신뢰도·관계·승격은 lint 배치로. eager는 위키 클수록 O(n) 토큰 폭증 — 병합은 누적 후 일괄이 효율적
모델 라우팅 (v2.1)인제스트=sonnet(기계적·빈번), 나머지=opus(고추론). 측정 페이지당 실질비용 ~4.6×↓·지연 ~3×↓
비용 원장 (v2.1)metrics/ — 문서당 토큰·모델가중 비용 추적. 최적화 효과를 숫자로 검증
raw/notes 스캔 제외데일리 노트는 스크래치 — 매일 pending 알림 스팸 방지, 넣고 싶은 날만 수동 인제스트

부록 — 빠른 시작 (TL;DR)

1. Obsidian 볼트 생성, Claude Code를 볼트에서 실행
2. /hooks 한 번 열어 훅 로드
3. raw/ 에 첫 소스 넣기 (Web Clipper 추천)
4. "이 소스 인제스트해줘"  → L2 증거 + L3 사실(신뢰도) 생성
5. "llmops MoC 만들어줘"   → 네비게이션 지도
6. "위키에 ~ 질문"         → 신뢰도 포함 인용 답변
7. "위키 린트"             → 망각·모순·승격 점검
8. "세션 마무리"           → L1→L2 압축
9. Obsidian 그래프 뷰로 위키 형태 브라우징

현재 상태는 CLAUDE.md(스키마), 하네스 변경 이력은 CHANGELOG.md, 위키 타임라인은 log.md 가 정본이다.

develop_docs/v0.8.6/todo/P4-cache-followup.md

P4 — (후속) 프로세스 내 mtime 캐시 v1.1

status: done               # not-started | in-progress | review | done — 캐시 구현·검증 완료(P4-02~P4-10·P4-13+), **외부리뷰 3라운드 — 3차에서 codex·agy 모두 잔여 BLOCKER 0**.
                           # **P4-11(npm 0.2.0 publish)만 P3-08 의 0.1.0 publish 승인에 막혀 있다.**
                           # 착수 판정(P4-01)은 트리거 미관측이었으나 사용자 지시로 캐시를 열었다 — 그 경위를 P4-01 에 남긴다.
started: 2026-09-10
completed: 2026-09-10
external_review: 1차 done → review/P4-agy-2026-09-10.md(병합 불가, BLOCKER 2·MAJOR 3·MINOR 2) · review/P4-codex-2026-09-10.md(병합 불가, BLOCKER 2·MAJOR 6·MINOR 2) — 전건 판정·반영 · 2차 codex → review/P4-codex-r2-2026-09-10.md(병합 불가, BLOCKER 3·MAJOR 3·MINOR 2) — 전건 판정·반영 · 2차 agy **미수행**(agy CLI 인증 확인 실패 — 계정 프로필 조회 오류, 코드와 무관. 3차에 포함) · 3차 codex → review/P4-codex-r3-2026-09-10.md(BLOCKER 0·MAJOR 2·MINOR 2) · 3차 agy → review/P4-agy-r3-2026-09-10.md(**병합 가능**, BLOCKER 0·MAJOR 0·MINOR 2) — 전건 판정·반영 → **done**
branch: feat/mcp-p4-lex-index (base: feat/mcp-p3-release, PR #22 위에 스택) — 캐시 착수 시 별도 브랜치
  • 근거: ../DESIGN.md §6(성능), ../PRD.md §4 비목표("캐시는 v1 에서 안 함 → v1.1")

  • 선행 조건: P3 done (v0.11.0 릴리즈) 그리고 다음 중 하나가 관측될 때만 착수 — ① 실볼트 1,000 페이지 근접 ② 호출당 buildGraph 가 1s 초과 ③ 사용자가 지연을 체감. 관측 없으면 이 단계는 열지 않는다(YAGNI).

    • 2026-09-10 판정: ①②③ 전부 미해당(근거 todo/baseline/P4-trigger.txt). 그럼에도 사용자 지시로 착수했다 — 게이트를 '측정으로 확인한 뒤 사람이 연다'로 운용한 것이고, 판정 기록은 그대로 남긴다(교훈 3: 미달을 지우지 않는다).

  • 완료 표시 방법: 00-README.md 규칙

P0~P3 교훈 반영 (이 단계에 적용하는 규칙)

앞 단계에서 실제로 대가를 치르고 얻은 것만 적는다. 캐시를 열 때 이 규칙을 먼저 읽는다.

  1. 문자열 리뷰는 실행을 대신하지 못한다. P3 에서 codex 등록 명령이 리뷰 3라운드를 통과하고 실행에서 죽었다(-c 조합 → invalid transport). → 캐시 항목은 "코드가 그렇게 보인다"가 아니라 파일을 실제로 바꾸고 다음 호출 결과를 확인하는 테스트로만 완료 표시한다(P4-07).

  2. 측정 조건을 지표 문구와 일치시킨다. P3 라우팅은 지표가 "규칙 스니펫 없는 상태"인데 15회 측정은 전부 스니펫 설치 상태여서 지표를 입증하지 못했다. → P4 의 모든 측정은 on/off·페이지 수·runs·warmup·측정 지점(프로세스 wall vs 인프로세스)을 근거 파일 첫 줄에 적는다.

  3. 미달을 [x] 로 적지 않는다. P3 외부리뷰 BLOCKER 5건 중 3건이 이 유형이었다. → 조건 미충족이면 [ ] ⏸ 사유, 판정이 바뀌면 근거 파일 경로를 함께 남긴다.

  4. "미달"의 원인이 대상이 아니라 실행 조건일 수 있다. Codex 라우팅 미달의 실제 원인은 codex exec 의 기본 approval: never 였다. → 캐시 성능이 기대만 못 하면 캐시 로직을 고치기 전에 측정 하네스(warmup·프로세스 경계·OS 파일캐시)부터 의심한다. 실볼트 --rerank 0 구간에서 12ms 절감이 프로세스 노이즈(±30ms)에 묻힌 것이 이번 실례다.

  5. 정본과의 구조 분기는 패리티로만 지탱된다. P4-12+ 로 TS 의 lex 경로가 Python 정본과 구조가 달라졌다(2패스→1패스). → 리트리벌 규칙을 바꾸는 변경은 CLAUDE.md 동기 규칙대로 Python·TS·픽스처 3점을 한 PR 에서 고치고, 구조만 다르고 출력이 같은 변경은 픽스처·퍼징·실볼트 digest 3중으로 증명한다(P4-16+).

  6. 보안·상한 가드는 최적화의 사각지대다. P2 에서 읽기 전용 가드가 별칭 import 로 우회됐고(3차 BLOCKER), TOCTOU·심볼릭 링크 경계가 두 번 뚫렸다. → 캐시는 walkMd 경계 검사·O_NOFOLLOW 뒤에 놓이며, 캐시 적중 경로가 이 검사들을 건너뛰지 않는지 별도 테스트(P4-10)로 고정한다.

  7. 환경 제약은 계획 시점에 확인한다. P3 는 필수 5종 중 2종을 환경 부재로 검증하지 못한 채 지표를 세웠다. → P4 의 OneDrive·FAT/exFAT mtime 정밀도 항목은 해당 환경을 실제로 만들 수 있는지 먼저 확인하고, 불가하면 착수 전에 항목을 조정한다.

체크리스트

A. 착수 판정
  • [x] P4-01 착수 사유 기록(위 ①②③ 중 무엇, 측정값) — todo/baseline/P4-trigger.txt ✅ 2026-09-10 — 판정: 미착수(트리거 미관측). ① 309/1,000 페이지(30.9%) ② buildGraph median 18.6ms(인프로세스)·28ms(--selftest) — 목표 1s 대비 ~35배 여유 ③ 지연 체감 보고 없음. 합성 1,000p 에서도 호출 합계 183ms(목표 <3s 의 6%). 근거 todo/baseline/P4-trigger.txt

B. 설계
  • [x] P4-02 캐시 키 = 전 파일 (relpath, mtimeMs, size) 스냅샷. 호출 시 readdir+stat 스캔으로 변경 감지 ✅ 2026-09-10 — src/cache.ts snapshot(): walkMd(경계 검사 포함) 결과에 lstat(링크 미추적)을 병렬로 붙여 볼트 realpath + 파일별 (경로, dev, ino, mode, size, mtimeNs, ctimeNs) 를 이어붙인 키(외부리뷰 1차로 (relpath, mtimeMs, size) 에서 확장 — P4-19+). stat 실패·비정규 파일은 ? 로 남아 항상 미스가 된다. 스캔 비용 실측 309p 5.2ms·1,000p 17.2ms

  • [x] P4-03 변경 파일만 재파싱, 그래프(인링크 포함)는 통째 재구성 — 부분 갱신 금지(정확성 우선) ✅ 2026-09-10 — readTexts() 가 안정 스냅샷에서 신원(FileId)이 같은 파일의 본문만 재사용하고(불안정 스냅샷이면 전량 재독 — 2차 반영) 나머지는 read()(O_NOFOLLOW 유지). 그래프는 assemble() 로 통째 재구성 — 링크 정규화·인링크는 부분 갱신하지 않는다. 누적 바이트 예산(MAX_TOTAL_BYTES)은 캐시 적중분까지 세어 캐시 유무로 상한 판정이 달라지지 않게 했다

  • [x] P4-04 캐시 무효화 조건 — 파일 추가·삭제·mtime·size 변화, --root 변경. 캐시 크기 상한(페이지 수 기준) 없음, 프로세스 종료 시 소멸(디스크 캐시 없음) ✅ 2026-09-10 — 추가·삭제는 키의 파일 목록이 달라져 미스(삭제 파일 텍스트는 c.texts 대입 교체로 함께 사라짐), --root 는 root 별 캐시(Map, 최대 4개, LRU — 1·2차 반영)라 전환해도 서로 섞이지 않는다. 디스크 캐시 없음. 추가 규칙: 스냅샷에 [-5s, 2s) 창 안 수정 파일이 있으면 '불안정' — 저장도 기존 캐시 사용도 하지 않는다(1·2차 반영) — FAT/exFAT·클라우드 동기 폴더의 타임스탬프 해상도 안에서 크기까지 같은 수정은 스냅샷으로 구분할 수 없기 때문. 파생 메모는 누적 128MiB(DERIVED_BUDGET_BYTES) 상한

  • [x] P4-05 LLMWIKI_CACHE=0 으로 끌 수 있음(패리티·디버그) ✅ 2026-09-10 — 끄면 stat 스캔조차 하지 않고 v1 경로(readAll)로 간다. 파생 메모도 저장하지 않는다. README(Env 줄·CLI 절·한계)와 DESIGN §6 에 기재 · 배선 점검(2026-09-10): 미지정 시 서버는 on·--once/--selftest 는 off, =1 로 강제 on 추가(P4-25+)

C. 구현·테스트
  • [x] [x] P4-06 src/cache.ts 구현, buildGraph 앞단에 투명 삽입 ✅ 2026-09-10 — buildGraph 는 walkMd → snapshot → 적중 시 같은 Graph 객체 반환, 미스 시 assemble(). search.ts filesMode 도 같은 텍스트 캐시를 쓴다. 호출부(once.ts·read.ts·cli.ts·tools.ts)는 무변경 — 캐시는 전적으로 투명하다

  • [x] [x] P4-07 단위 — 파일 수정/추가/삭제 후 다음 호출에서 반영, OneDrive 지연(mtime 만 변경·내용 동일) 케이스 ✅ 2026-09-10 — test/unit/p4-cache.test.ts 25건(최초 10 + 1차 회귀 6 + 2차 회귀 5 + 3차 회귀 4): 적중 시 동일 객체 · 수정 반영 · 추가/삭제 반영(인링크 재생성·삭제 파일 캐시 제거) · mtime 만 변경(내용 동일)은 재구성하되 결과 동일 · 타임스탬프 해상도 안의 수정(같은 크기·같은 mtime 으로 내용 교체)에서도 새 내용을 본다 · --root 전환 · on/off 동일

  • [x] [x] P4-08 패리티 — tests/parity.py 를 캐시 on/off 양쪽으로 실행, 전건 동일. 연속 호출(같은 프로세스) 시나리오 추가 ✅ 2026-09-10 — 픽스처 0/56 FAIL·DRIFT 0 (on·off 양쪽) · 퍼징 200/200 (on·off) · 실볼트 0/20 FAIL, digest 9bcb72a0a91edc08(on 2회·off 1회 전부 동일 — P1-67+ 의 캐시 이전 값과도 동일). 연속 호출은 CLI 로는 잴 수 없어(프로세스마다 캐시가 새로 생김) 인프로세스 테스트로 넣었다(P4-07 의 on/off 케이스가 같은 프로세스에서 3라운드 반복)

  • [x] [x] P4-09 성능 — 1,000 페이지 합성 볼트에서 2회차 호출 <100ms, stat 스캔 비용 기록 todo/baseline/P4-perf.txt ✅ 2026-09-10 — test/perf/cache-bench.mjs(인프로세스 median of 10): 최신 — 1,000p 정확히 2회차 26.6ms(3차 반영 후, off 164.3 / 2차 반영 후 24.5ms)(목표 <100ms 충족, -85%) · 309p off 42.5 → 2회차 8.3ms. warm 의 고정 비용은 walk+lstat 스캔(309p 5.2 · 1,000p 17.2ms). 이력: 최초 기록 24.0ms 는 'warm 반복 median' 이었고 1차 리뷰(codex M5)로 '정확히 2회차' 측정으로 바꿨다 — warm 시간의 70~75%가 스캔이다. identical_output=true

  • [x] [x] P4-10 심볼릭 링크 스킵 규칙(P2-17) 이 캐시 경로에서도 유지되는지 테스트 ✅ 2026-09-10 — 설계상 순회는 캐시하지 않는다(캐시는 walkMd 뒤에 붙는다). 테스트 2건: 캐시 적중 상태에서 경계 밖 파일로 링크를 넣어도 노드가 늘지 않고 카나리 문자열이 그래프에 없다 · 실제 파일을 링크로 교체하면 목록이 바뀌어 재구성되고 외부 내용이 실리지 않는다. 링크 생성 권한이 없는 환경에서는 사유를 남기고 건너뛴다

  • [x] [x] P4-12+ (P1 코덱스 리뷰 #5 이월) lexScore 중복 계산 제거 — lexicalSeeds 가 distinct 캐시를 반환해 expand 가 재사용, BM25 scoreText·cpLen 도 그래프 빌드 시 1회 계산. 출력 무변경(패리티 전건 유지가 조건). Python 정본은 그대로 둔다 ✅ 2026-09-10 — src/expand.ts 에 LexIndex·lexIndex(G, terms) 신설, lexicalSeeds/expand 가 선택 인자 idx 로 재사용(미전달 시 기존 동작 그대로, 부분 인덱스면 누락 노드만 그 자리에서 계산). src/once.ts expandData 가 호출당 1회 생성해 둘에 전달. lex 단계 −49.8%(309p 24.72→12.42ms · 1,000p 125.26→62.86ms · 2,000p 254.61→128.19ms). 출력 무변경 3중 검증: 픽스처 패리티 0/56 FAIL·골든 DRIFT 0 · 퍼징 200/200×2 seed · 실볼트 0/20 FAIL 3회 반복 digest 9bcb72a0a91edc08 = P1-67+ 가 기록한 변경 이전 값과 동일. npm run check 40파일 551건 통과, npm pack 19파일 무변동. 근거 todo/baseline/P4-perf.txt ↳ BM25 부분은 이미 충족돼 코드 변경이 없다(추측 아닌 소스 확인): bm25Rank 는 호출당 scoreText·cpLen 을 노드별 1회만 돌리고(texts·dlOf 맵, P1-69+ 에서 처리), 프로덕션 경로에서 bm25Rank 는 요청당 1회만 불린다(src/once.ts:99 가 유일한 호출부). 호출을 넘는 재사용은 캐시(P4-13+) 없이는 불가능하다

  • [x] [x] P4-14+ 단계별 계측 하네스 test/perf/stage-bench.mjs — buildGraph·lex(legacy/shared)·bm25 를 인프로세스로 분리 측정(median of 30, warmup 5)하고 두 경로의 결과 동일성까지 출력. 숫자만 출력해 baseline 에 그대로 붙여도 콘텐츠가 새지 않는다 ✅ 2026-09-10 — 프로세스 wall(bench.py)은 node 부팅 ~145ms·OneDrive IO 에 신호가 묻혀 12ms 절감을 판정할 수 없었다(교훈 4). eslint 에 test/perf/**/*.mjs Node 전역 블록 추가

  • [x] [x] P4-15+ 등가성 회귀 테스트 test/unit/p4-12-lex-index.test.ts 6건 — lexIndex 값 = 노드별 lexScore, lexicalSeeds/expand 가 idx 유무와 무관하게 동일, 렌더 텍스트 바이트 동일, 부분 인덱스·빈 인덱스 경계. term 집합 8종(무매치·빈 목록·중복·비ASCII·alias 포함) ✅ 2026-09-10 — 골든·패리티는 실행 경로 하나만 보므로 두 경로 대조는 별도 테스트가 필요하다

  • [x] [x] P4-16+ 정본과의 구조 분기 기록 — Python scope-expand.py 는 lexical_seeds·expand 가 각자 lex_score 를 돌리고(2패스), TS 는 1패스다. 정본은 수정하지 않는다(P1 리뷰 판정 유지). 분기를 src/expand.ts 주석과 CLAUDE.md 변경 이력에 남기고, 보증은 패리티 3종이 진다 ✅ 2026-09-10

  • [x] [x] P4-13+ (P4-01 측정 결과 반영) 캐시 범위 재정의 — 현행 P4-02~P4-04 는 buildGraph 만 캐시하지만, 1,000 페이지 실측 분해에서 buildGraph 는 호출 비용의 29%(52ms/183ms)뿐이다(lex 34% + bm25 37%). 노드별 파생 산출물 중 term 과 무관한 것(lex haystack pyLower(text+"\n"+aliases), bm25 scoreText, dl)을 캐시 엔트리에 함께 실어야 ROI 가 난다. 착수 시 P4-02·P4-03 을 이 범위로 고쳐 쓴다 — 단, 파생값은 원본 파일 변경 시 함께 무효화되어야 하므로 무효화 단위를 파일→(노드+파생)으로 정의할 것 ✅ 2026-09-10 — cache.ts derived(node, kind, compute) 로 구현: 파생값을 노드 객체에 WeakMap 으로 매다는 방식이라 그래프가 교체되면 파생도 함께 사라진다(무효화 단위가 자동으로 노드). 적용 지점은 expand.lexHay·bm25.scoreText·bm25 dl 3곳. 누적 128MiB 상한. 효과: warm 호출이 lex·bm25 재계산을 통째로 건너뛴다(1,000p 190→24ms — 그래프만 캐시했다면 ~130ms 에 그쳤을 몫)

  • [x] [x] P4-18+ 착수 트리거 감시 — 위키 lint·주간리뷰 때 node tools/llmwiki-mcp/dist/cli.js --selftest --root <VAULT> 의 pages·buildGraph_ms 를 확인해 ①② 근접 여부만 본다(현재 309p·28ms). 근접하면 P4-01 을 갱신하고 캐시를 연다 ✅ 2026-09-10 — 캐시를 열었으므로 원래 목적(착수 시점 판단)은 소멸. 감시 대상을 바꿔 남긴다: warm 호출은 walk+stat 스캔이 지배하므로(1,000p 17.4ms = warm 의 73%), 다음 병목은 스캔 비용이다. 페이지 수가 3,000 을 넘거나 warm 호출이 100ms 를 넘으면 스캔 자체를 줄이는 방법(디렉터리 mtime 프루닝·watch API)을 새 항목으로 연다

D. 배포
  • [x] P4-19+ (외부리뷰 1차 반영) 캐시 키 강화 — stat→lstat, (dev, ino, mode, size, mtimeNs, ctimeNs) + 볼트 realpath, isFile() 아니면 저장 금지 ✅ 2026-09-10 — codex B1·B2 / agy M2 를 문서화가 아니라 설계 변경으로 막았다. cp -p 재현 테스트 통과

  • [x] P4-20+ (외부리뷰 1차 반영) 예산·수명 수정 — 파생 예산을 그래프 조립 시 리셋, 저장 전 예산 확인, has() 로 undefined 인정, 텍스트 캐시 256MiB 상한, root 캐시 Map(최대 4) ✅ 2026-09-10 — agy B2·MINOR / codex M2·m1

  • [x] P4-21+ (외부리뷰 1차 반영) CI 에 캐시 off 패리티 + 퍼징 단계 추가 ✅ 2026-09-10 — .github/workflows/ci.yml: LLMWIKI_CACHE=0 python tests/parity.py·python tests/parity_fuzz.py. DoD 를 CI 가 지키게 됐다(agy M1·codex M3·M6)

  • [x] P4-22+ (외부리뷰 1차 반영) 측정 하네스 정정 — stage-bench 는 캐시 off 강제, cache-bench 는 정확히 2회차 측정·표준 median ✅ 2026-09-10 — agy M3·codex M5. 재측정: 309p 48.4→9.2ms · 1,000p 195.4→26.4ms · 2,000p 379.5→48.9ms

  • [x] P4-23+ (외부리뷰 2차 반영) 불안정 스냅샷의 적중·재사용 금지, 텍스트 예산 초과 시 그래프 미저장, 파생 예산을 전 root 합으로, root LRU, Windows ctime 서술 정정 ✅ 2026-09-10 — codex r2 B1·B3·M1·M3·m1·m2. 회귀 5건 추가(p4-cache 21건). 재검증: 41파일 572건 · 픽스처 0/56(on·off) · 퍼징 200/200 · 실볼트 0/20(on·off) digest 9bcb72a0a91edc08 불변 · 1,000p 정확히 2회차 24.5ms

  • [x] P4-24+ (외부리뷰 3차 반영) 누적 상한 즉시 판정, 전 root 텍스트 합 상한(LRU 축출), 스냅샷 lstat 동시성 풀, 축출 시 참조 해제 ✅ 2026-09-10 — codex r3 M1·M2·m3·m4 / agy r3 m1·m2. 회귀 4건 추가(p4-cache 25건). 재검증: 41파일 576건 · 픽스처 0/56(on·off) · 퍼징 200/200 · 실볼트 0/20(on·off) digest 9bcb72a0a91edc08 불변 · 1,000p 정확히 2회차 26.6ms · pack 20파일(dist/cache.js 추가분)

  • [x] P4-25+ (배선 점검 — 사용자 요청 "배선도 누락 재검토") 캐시·P4 변경이 코드·CLI·서버·CI·문서·릴리즈 계획에 빠짐없이 연결됐는지 소스로 전수 확인하고 누락을 고쳤다 ✅ 2026-09-10 ↳ ① CI 읽기 전용 가드 실패(실제 결함) — src/cache.ts 가 import fs from "node:fs/promises"(default import, 가드가 금지)였다. P4 브랜치를 한 번도 푸시하지 않아 CI 가 돌지 않았고 가드는 ci.yml heredoc 에만 있어 로컬 npm run check 로는 잡히지 않았다 → import { promises as fs } from "node:fs"(vault.ts 와 동일)로 수정. 재발 방지: 가드 본문을 tools/llmwiki-mcp/scripts/readonly-guard.py 로 옮기고 CI 는 그 파일을 호출(규칙 무변경) — 로컬에서 python3 scripts/readonly-guard.py 로 같은 판정. P0~P3 교훈 1("문자열 리뷰는 실행을 대신 못 한다")의 CI 판: CI 전용 검사는 로컬에서 돌릴 수 있어야 한다 ↳ ② 1회성 모드에 캐시가 순수 비용으로 배선됨 — --once·--selftest 는 프로세스당 1회 호출이라 적중 기회가 없는데 stat 스캔만 더해져 1,000p cold 가 off 대비 +7.7%(164.3→176.9ms)였고, --selftest 의 buildGraph_ms(P4-18+ 감시 지표·Codex 타임아웃 근거) 의미가 조용히 바뀌었다 → setCacheDefault(false) 를 cli 의 두 분기에 배선, LLMWIKI_CACHE=1 로 강제 on 가능. 서버 기본값은 on 그대로. CI 패리티 ON 단계는 LLMWIKI_CACHE=1 을 명시해 캐시 경로 커버리지를 유지. 실측: --selftest buildGraph_ms 25ms(v1 의미 복원) ↳ ③ --help 와 README 불일치 — README 의 help 블록에만 LLMWIKI_CACHE 가 있고 실제 cli.ts HELP 에는 없었다 → HELP 에 추가, --help 출력을 검사하는 테스트 추가 ↳ ④ 문서 배선 — PRD §4("캐시는 v1 에서 안 함")·위험 표에 P4 구현 사실과 버전 경계 주석, 릴리즈 노트(v0.11.0)에 "P4 캐시는 이 태그에 포함되지 않음·publish 시점 커밋 확인" 명시, 릴리즈 노트 단위 테스트 수를 P3 끝 실측값(39파일 545건)으로 정정 ↳ ⑤ P3 체크리스트 배선(첫 리뷰에서 지적했던 누락) — 본문·헤더가 참조만 하던 P3-31+·P3-32+·P3-34+ 를 실체화(31+·34+ 는 반영 근거 확인 후 [x], 32+ 는 환경 제약 [ ] ⏸), 헤더의 P3-33+ 를 본문과 같은 P3-32+ 로 통일, DoD pack 수(18→19)·P3-02 줄 수(15→17) 오기 정정 ↳ 확인했으나 문제없음: 호출부 5곳(once.ts expand·read.ts read_page·cli.ts selftest·search.ts·server.ts 경유) 모두 buildGraph/readTexts 를 거침, pack.ts 는 선택 페이지만 read()(캐시 불필요), pack 가드 20파일·금지 경로 0, dump-tools-list --check·templates/clients/build.py --check 드리프트 0, eslint test/perf/*.mjs 블록 존재 ↳ 재검증: 42파일 580건 · 가드 OK · 패리티 기본/=1/=0 전부 0/56 · 퍼징 200/200 · 실볼트 기본·=1 0/20 digest 9bcb72a0a91edc08 · bench.py selftest 파싱 정상

  • [x] P4-26+ Claude Code 실클라이언트 E2E — tarball 설치본을 claude -p --mcp-config --strict-mcp-config 로 주입(사용자 설정 무변경), 실볼트 사본에서 수행 ✅ 2026-09-10 — A 사실브리핑(on): expand(rerank=11)→pack·read_page 0·신뢰도 병기 PASS · B 세션 도중 변조(같은 크기·mtime 복원, ctime 만 변화)(on): search·expand 모두 새 내용 반영 PASS, 2회차 expand 39→13ms · C 동일 절차(off): PASS, 도구 결과 5건 B 와 문자열 동일 · 프로토콜 직접 구동 on/off: stdout 비JSON 0줄·출력 동일 · 볼트 하네스 Python 경로 정상 · 실볼트 무변경(diff 0). 근거 todo/evidence/P4-e2e-claude-code.md

  • [x] P4-27+ (E2E 에서 발견) Claude Code 가 structuredContent 를 모델에 넘기고, 그 안에 text 와 rows/pages 가 같은 정보를 이중으로 담아 모델 입력이 텍스트 대비 2.06~4.24배(pack 9,715B → 20,015B) — 팩의 토큰 절감 설계를 상쇄한다. 2026-09-10 사용자 결정: 2안(기본 텍스트만, LLMWIKI_STRUCTURED=1 opt-in) ✅ 2026-09-10 — 구현·측정·외부리뷰(codex·agy, 둘 다 BLOCKER 0) 반영 완료 ↳ 구현: server.ts 가 ServerOptions.structured(기본 false)일 때 tools/list 에서 outputSchema 를 빼고 응답에서 structuredContent 를 뺀다(스펙상 둘은 함께). callTool(…, structured=true) 는 전체 계약 경로 그대로(outputSchema fail-closed 검증·envelope 예산) — 기존 P2/P3 테스트는 p2-helpers.connect() 기본 structured: true 로 opt-in 경로를 계속 검사. cli.ts 는 LLMWIKI_STRUCTURED=1 만 켠다('true' 등 다른 값은 off). 텍스트 전용 경로의 상한은 capText(read_page 는 read.ts 에서 이미 capText)로 유지 ↳ 테스트 p4-27-text-only-default.test.ts 4건: 기본 서버 outputSchema·structuredContent 없음·오류 없음 / 두 모드 텍스트 바이트 동일(suggested_next 줄 포함) / 기본 모드 응답이 더 작음 / 실제 CLI stdio 서버의 env 배선(미지정·'1'·'true'). 전체 43파일 584건 통과 ↳ 재측정(Claude Code, 실볼트 읽기 전용, 스니펫 없음): 사실 3/3·절차 2/2 준수, JSON 으로 감싼 payload 0건, F1 모델 입력 expand 1,470→411B·pack 20,015→9,715B(텍스트와 동일). 근거 todo/evidence/P4-e2e-claude-code.md ↳ 외부리뷰 반영(codex: BLOCKER 0·MAJOR 4·MINOR 3 / agy: BLOCKER 0·MAJOR 3·MINOR 4 — 지적이 거의 겹침): ① 텍스트 전용 경로도 응답 JSON 전체 200KB 예산(JSON 이스케이프로 부푼 envelope 재측정·축소) ② p2-helpers.connect() 기본을 실제 서버와 같은 텍스트 전용으로 뒤집고, structuredContent 계약 테스트 12파일·직접 callTool 호출 21곳은 opt-in 을 명시 ③ callTool 의 structured 인자를 필수로(서버 기본과 반대인 암묵값 제거) ④ 기본 모드 보안·상한 테스트 신설 p4-27-default-mode-guards.test.ts 17건(경로 탈출·입력 상한·오류 문구 두 모드 동일 12종, 심볼릭 링크 카나리, 이스케이프 대형 페이지 envelope, 큰 응답의 의도된 텍스트 차이) ⑤ README 도구 표·DESIGN §3·릴리즈 노트(0.2.0 호환성 변경 예고)·스펙 SHOULD(TextContent=JSON 직렬화) 의도적 예외 명시. print-config --structured 는 기각 — JSON 설정 env 블록·CLI --env 로 이미 켤 수 있어 README 예시로 대체 ↳ 반영 중 드러난 P2 결함: opt-in 경로의 envelope 예산이 structuredContent 만 재고 content[0].text 에 같은 텍스트가 한 번 더 실리는 몫을 세지 않았다 — 이스케이프 많은 대형 read_page 가 341,496B(상한 204,800B)로 나갔다. P2-23 테스트는 이 잘못된 측정을 '정확히 절반'으로 고정하고 있었다 → 전송 응답 전체(content+structuredContent)를 재도록 고치고, 축소를 '남은 예산의 절반에서 시작 + 실측 비율로 한 번에 보정'으로 바꿔 과절단 제거(고정 비율로는 25.6KB 까지 버렸다 → ASCII 본문 기준 절반 예산을 거의 다 씀). P2-23 단언을 올바른 불변식(전송 전체 ≤ 상한, 텍스트 ≤ 절반, 과절단 금지)으로 정정 ↳ 재검증: 44파일 600건 · 가드 OK · tools-list 덤프 무드리프트 · 패리티 기본/LLMWIKI_CACHE=1 0/56 · 퍼징 200/200 · 실볼트 0/20 digest 9bcb72a0a91edc08. 리뷰 원문 review/P4-27-{codex,agy}-2026-09-10.md

  • [ ] P4-11 npm 0.2.0 publish, README 성능 절 갱신, CLAUDE.md 변경 이력 행 — 선행: P3-08 의 0.1.0 publish(승인 대기). 0.1.0 이 나가기 전에는 0.2.0 을 논하지 않는다 ⏸ 2026-09-10 — 문서 몫은 선반영: README(Env 줄·LLMWIKI_CACHE 설명·한계 2줄 교체)·DESIGN §6·CLAUDE.md 이력 행 완료. 남은 것은 publish 뿐이며 P3-08 승인에 막혀 있다 ⏸ 2026-09-10 — 사용자 결정: PR #19~#23 main 머지, 태그·npm publish 는 보류(버전 전략 미정 — 한 번에 0.1.0 vs 계획대로 0.1.0→0.2.0). 릴리즈 노트 초안은 유지 ✖ 2026-09-11 기각 — P4 가 첫 공개 [email protected] 에 포함되어 별도 0.2.0 이 필요 없어졌다(사용자 결정: 첫 공개에 전부 포함). README 성능 절·CLAUDE.md 이력은 이미 반영됨

  • [x] P4-17+ (P4-12+ 몫) CLAUDE.md 변경 이력 행 추가 — 패키지 배포 없이 저장소에만 반영되는 변경이므로 P4-11 과 분리해 지금 기록 ✅ 2026-09-10

산출물

  • src/cache.ts, 테스트, P4-trigger.txt, P4-perf.txt, npm 0.2.0

  • (2026-09-10 실제 산출) 캐시: src/cache.ts(신설)·src/graph.ts(assemble 분리·캐시 삽입)·src/search.ts·src/expand.ts(lexHay)·src/bm25.ts(파생 메모), test/unit/p4-cache.test.ts(10건), test/perf/cache-bench.mjs, README·DESIGN §6 갱신

  • (2026-09-10 실제 산출) 이월분: src/expand.ts(lexIndex)·src/once.ts, test/unit/p4-12-lex-index.test.ts, test/perf/stage-bench.mjs, todo/baseline/P4-trigger.txt·P4-perf.txt

완료 기준 (DoD)

  • [x] 캐시 on/off 패리티 전건 동일 ✅ 2026-09-10 — 픽스처 56 on/off · 퍼징 200 on/off · 실볼트 20 on/off, digest 전부 9bcb72a0a91edc08. 단위 42파일 580건 통과(배선 점검 후)

  • [x] 성능 목표 달성 기록 ✅ 2026-09-10 — 1,000p 정확히 2회차 26.6ms(목표 <100ms, 3차 반영 후 재측정), stat 스캔 비용까지 todo/baseline/P4-perf.txt 에 기록

  • [x] 외부리뷰 완료 ✅ 2026-09-10 — 3라운드: 1차 codex·agy(BLOCKER 4) → 2차 codex(BLOCKER 3, agy 는 CLI 인증 오류로 미수행) → 3차 codex·agy 모두 잔여 BLOCKER 0(agy '병합 가능'). 3차 MAJOR 2·MINOR 4 는 00-README 5항대로 같은 단계 안에서 반영·재검증

  • [x] (이월분) P4-12+ 출력 무변경 완료 ✅ 2026-09-10 — 픽스처 패리티·퍼징 2 seed·실볼트 digest 동일·단위 551건, 세 볼트에서 identical_output=true

외부리뷰 (단계 종료 시 필수)

  • 대상: src/cache.ts, 관련 테스트, 성능 기록

  • 관점: ① 무효화 누락(stale 결과 반환) 경로 ② OneDrive 온디맨드 파일·mtime 정밀도(FAT/exFAT 2s) 함정 ③ 메모리 상한 부재의 위험 ④ 캐시가 보안 규칙(심볼릭 링크·경로 제한)을 우회하는 경로

  • 관점(추가, 2026-09-10) ⑤ 정본 구조 분기(P4-12+)가 패리티만으로 충분히 지탱되는가 — 픽스처가 닿지 않는 입력(부분 인덱스·빈 terms·중복 slug 볼트)에서 두 경로가 갈릴 여지 ⑥ 파생값 캐시(P4-13+)가 원본 무효화와 어긋나 term 무관 파생만 낡는 경로

  • 절차: 00-README.md 규칙. 프롬프트 review/P4-prompt.txt, 결과 review/P4-agy-YYYY-MM-DD.md

외부리뷰 반영
#심각도지적 요지판정(수용/기각/상신)반영 위치
1차 agy 2026-09-10 (review/P4-agy-2026-09-10.md, BLOCKER 2·MAJOR 3·MINOR 2) — 병합 불가
p4-aB1BLOCKER미래 mtime 파일이 있으면 now - mtimeMs 가 음수 → 항상 "방금 수정" 판정 → 그 볼트 캐시가 영구 무력화되고 매 호출 캐시를 비움수용(실증) — FUTURE_SKEW_MS(5s) 도입: 창은 [-5s, 2s). 그보다 더 미래인 mtime 은 시계 차이로 본다. 단순히 ageMs >= 0 로 막으면 Date.now() 의 ms 절삭 때문에 갓 쓴 파일을 놓치는 것을 자체 테스트가 검출src/cache.ts FUTURE_SKEW_MS, p4-cache.test.ts "[agy B1]"
p4-aB2BLOCKERderivedBytes 가 전역이라 같은 볼트에서 수정이 누적되면 128MiB 를 넘어 파생 캐시가 영구 정지수용 — resetDerivedBudget() 를 그래프 조립 시점(buildGraph)에 호출. 12회 반복 수정 테스트로 고정src/cache.ts·src/graph.ts, p4-cache.test.ts "[agy B2]"
p4-aM1MAJORCI 에 캐시 off 패리티·퍼징 단계가 없어 DoD 를 CI 가 지키지 못함수용 — .github/workflows/ci.yml 에 LLMWIKI_CACHE=0 패리티 + parity_fuzz.py 단계 추가ci.yml
p4-aM2MAJOR2s 창 밖에서 mtime·size 를 보존한 채 내용이 바뀌면 낡은 적중(메타데이터 캐시의 한계)수용(설계 변경) — 문서화로 끝내지 않고 키에 ctime·ino·dev·mode 를 넣어 실제로 막았다. cp -p 시나리오를 테스트로 재현src/cache.ts snapshot, p4-cache.test.ts "[codex B1·agy M2]"
p4-aM3MAJORstage-bench.mjs 가 캐시·파생 메모를 타서 단계 비용을 왜곡수용 — 벤치가 LLMWIKI_CACHE=0 을 강제하고 출력에 표기. 재측정치로 근거 파일 갱신test/perf/stage-bench.mjs, baseline/P4-perf.txt
p4-am1MINOR단일 슬롯 root 캐시 — 두 볼트 교차 조회 시 매번 폐기수용 — Map<string, RootCache>(MAX_ROOTS 4)로 전환, 교차 조회 적중 테스트 추가src/cache.ts, p4-cache.test.ts "[리뷰 MINOR]"
p4-am2MINOR누락 테스트(미래 mtime·장기 수정·search↔graph 상호작용·상한 혼합)수용 — 6건 추가(위 항목들 + search→graph 일관성)p4-cache.test.ts "외부리뷰 반영 회귀"
1차 codex 2026-09-10 (review/P4-codex-2026-09-10.md, BLOCKER 2·MAJOR 6·MINOR 2) — 병합 불가
p4-cB1BLOCKER(path, mtime, size) 만으로는 낡은 본문을 판별할 수 없음(mtime 복원·시계 보정·볼트 realpath 변경)수용 — 키를 realpath(vault) + (경로, dev, ino, mode, size, mtimeNs, ctimeNs) 로 확장. ctime 은 userland 가 되돌릴 수 없어 복원 시나리오를 잡는다src/cache.ts snapshot
p4-cB2BLOCKER캐시 적중 시 read() 를 건너뛰어 O_NOFOLLOW·realpath 검증이 빠짐. stat 은 링크를 따라감수용 — lstat 으로 교체(링크 미추적) + isFile() 아니면 저장 금지 + ino/mode 키 포함 → 파일이 링크·다른 파일로 바뀌면 반드시 미스가 되어 read()(O_NOFOLLOW) 로 돌아간다. 순회·경계 검사는 애초에 호출마다 수행src/cache.ts snapshot, p4-cache.test.ts P4-10
p4-cM1MAJOR권한 변경(chmod)이 키에 없어 접근성과 결과가 어긋남수용 — mode·ctimeNs 가 키에 포함되므로 chmod 는 미스. 테스트 추가p4-cache.test.ts "[codex M3]"
p4-cM2MAJOR파생 예산이 실제 메모리 상한이 아님(문자열만 계수·마지막 하나가 초과·undefined 재계산)수용 — 값 저장 전 예산 확인, 숫자도 8B 로 계수, has() 로 undefined 캐시 인정, 텍스트 캐시에 TEXT_BUDGET_BYTES 256MiB 추가src/cache.ts derived·readTexts
p4-cM3MAJOR정본 구조 분기의 CI 증명 부족(캐시 on/off·퍼징 미실행)수용 — CI 에 두 단계 추가(p4-aM1 과 같은 조치)ci.yml
p4-cM4MAJOR필수 무효화 케이스 누락(mtime 복원·미래 mtime·chmod·교차 root·stat 실패 복귀 등)수용(부분) — mtime 복원·미래 mtime·chmod·교차 root·search↔graph 6건 추가. walk 와 snapshot 사이의 링크 교체(TOCTOU)는 결정론적 재현이 어려워 미추가 — 대신 lstat+ino/mode 키로 그 창에서도 미스가 되게 만들었고, 상위 디렉터리 교체 경쟁은 P2-42+ 가 이미 "로컬 단일 사용자 범위에서 수용" 으로 판정한 잔여 위험이다(DESIGN §5)p4-cache.test.ts, DESIGN §5·§6
p4-cM5MAJOR성능 주장이 측정 범위보다 넓음(rerank 경로만·warm 정의·median 계산)수용 — cache-bench.mjs 에 정확히 2회차 측정 추가, 표준 median(짝수 평균)으로 수정, 라벨을 "warm(반복 median)" 으로 정정test/perf/cache-bench.mjs, baseline/P4-perf.txt
p4-cM6MAJOR리뷰 환경에서 npm test·parity 를 재현하지 못해 체크리스트 수치를 독립 검증할 수 없었음(EPERM·dist 없음)수용(절차) — 캐시 on/off 패리티·퍼징을 CI 필수 단계로 승격해 3-OS 로그가 근거가 되게 했다. 로컬 재현 절차(npm ci && npm run check 후 python3 tests/parity.py)를 근거 파일에 명시ci.yml, baseline/P4-perf.txt
p4-cm1MINORroot 캐시 1개만 유지수용 — p4-am1 과 동일 조치src/cache.ts
p4-cm2MINOR외부리뷰 미완료 상태에서 status: done 으로 바꾸지 말 것수용 — status: review 유지, DoD 외부리뷰 항목도 [ ] 로 남긴다이 문서 상단
2차 codex 2026-09-10 (review/P4-codex-r2-2026-09-10.md, BLOCKER 3·MAJOR 3·MINOR 2) — 병합 불가. agy 2차는 CLI 인증 오류로 미수행
p4-r2B1BLOCKER'방금 수정' 창이 저장만 막고 기존 캐시 적중·텍스트 재사용은 허용수용(실증) — 불안정 스냅샷이면 getGraph 가 null, readTexts 는 전량 재독. 옛 신원을 든 스냅샷으로 재사용 경로를 직접 두드리는 회귀 테스트src/cache.ts getGraph·readTexts, p4-cache.test.ts "[codex r2 B1]"
p4-r2B2BLOCKER상위 디렉터리 교체 경쟁으로 realpath 경계 우회 → 외부 본문이 캐시될 수 있음기각(근거) — 캐시가 노출을 늘리지 않는다: 외부 본문은 외부 파일의 dev/ino 로 키가 잡혀, 다음 호출에서 교체가 유지되면 walkMd 가 그 파일을 빼고 원복되면 신원 불일치로 미스 → 캐시된 외부 본문은 재제공되지 않는다. 노출은 캐시 없는 한 호출과 동일하고, 그 잔여 위험은 P2-42+ 에서 수용됨. Node 에 openat 계열이 없어 구성요소 고정 순회는 불가DESIGN §6
p4-r2B3BLOCKERWindows ctimeNs 가 생성 시각이라 방어 불성립부분 기각 + 정정 — 전제가 틀렸다: Node ctime 은 POSIX·NTFS 모두 상태 변경 시각(생성 시각은 birthtime, Node v24 공식 문서 "Stat time values"). 이 전제는 내 1차 반영 주석의 오류였고 리뷰어가 그대로 인용했다 → 주석·README·DESIGN 정정. 실제로 방어가 약한 FAT/exFAT·일부 네트워크 FS 는 README 한계 + LLMWIKI_CACHE=0 권고src/cache.ts 헤더, README, DESIGN §6
p4-r2M1MAJOR텍스트 예산 초과 시에도 그래프가 저장돼 상한 우회 / 파생 카운터가 root 여러 개의 합을 못 셈수용 — 예산 초과면 snap.storable=false 로 그래프도 미저장. 파생은 root 별 계수 + 전 root 합으로 예산 판정, 저장된 그래프 노드에만 세대 번호로 매단다src/cache.ts, p4-cache.test.ts "[codex r2 M1]" ×2
p4-r2M2MAJORCI 가 캐시 적중·무효화 패리티(같은 프로세스 연속 호출)를 검증하지 않음기각(사실 오류) — CI 의 npm run check 가 vitest 전체를 3-OS 에서 돌리고, 여기에 p4-cache.test.ts 의 build→hit→mutate→rebuild·on/off 비교(21건)가 포함된다. parity.py·퍼징이 매 호출 새 프로세스라는 지적 자체는 맞으나 연속 호출 검증은 vitest 가 맡는다. 단 Windows 에서 의미가 다른 chmod 케이스는 skipIf(win32) 로 명시ci.yml(npm run check), p4-cache.test.ts
p4-r2M3MAJOR체크리스트 서술이 현재 구현·측정과 불일치(P4-02/03/04/07/09)수용 — 5개 항목 서술을 최신 구현(lstat·FileId·불안정 스냅샷·LRU)과 최신 측정(정확히 2회차 24.5ms)으로 교체, 옛 값은 '이력'으로 남김이 문서 P4-02~P4-09·DoD
p4-r2m1MINORroot 축출이 LRU 가 아니라 FIFO수용 — 접근 시 Map 순서 갱신. LRU 테스트 추가src/cache.ts rootCache, p4-cache.test.ts "[codex r2 m1]"
p4-r2m2MINOR필수 회귀 누락(기존 캐시+창 진입·FUTURE_SKEW 경계·stat 실패 복구·상한 혼합·Windows ctime·상위 디렉터리 경쟁)수용(부분) — 창 진입(B1)·FUTURE_SKEW 경계·텍스트/파생 상한 3건 추가. stat 실패 복구는 추가·삭제 테스트가 같은 경로를 탄다. Windows ctime·상위 디렉터리 경쟁은 이 환경(darwin)에서 결정론적 재현이 불가 — 전자는 B3 정정으로 전제가 사라졌고 후자는 B2 근거로 기각p4-cache.test.ts "외부리뷰 2차 반영 회귀"
3차 codex 2026-09-10 (review/P4-codex-r3-2026-09-10.md) — BLOCKER 0, r2B2·r2B3 기각을 "타당" 으로 확인
p4-r3M1MAJORreadTexts 가 전부 읽은 뒤 누적 상한을 검사 — 상한을 크게 넘는 볼트도 한때 전량 메모리 적재수용(소스 확인: readAll 은 읽을 때마다, readTexts 는 사후 검사였다 — agy 3차가 "완전히 동일" 이라 본 것은 틀림) — 적중분 포함 읽는 즉시 계수, 초과 시 다른 워커도 새 파일을 읽지 않음. 적중 텍스트도 size ≤ MAX_FILE_BYTES 재확인src/cache.ts readTexts, p4-cache.test.ts "[codex r3 M1]"
p4-r3M2MAJOR캐시 메모리 상한이 전역 하드캡이 아님(root 당 256MiB × 4)수용 — 전 root 텍스트 합 512MiB(TOTAL_TEXT_BUDGET_BYTES), 넘으면 LRU 로 다른 root 를 비우고 혼자 넘으면 미저장. README 에 "payload 기준 논리 상한, RSS 아님" 명시src/cache.ts, README, p4-cache.test.ts "[codex r3 M2]"
p4-r3m3MINORrename(slug 변경)·stat 실패 후 복구 회귀 누락수용 — 2건 추가p4-cache.test.ts "[codex r3 m3]" ×2
p4-r3m4MINOR리뷰 환경에서 vitest 가 임시 ssr 디렉터리 EPERM 으로 0건 실행수용(절차) — 재현 절차에 "쓰기 가능한 임시 디렉터리 필요(샌드박스 read-only 에서는 vitest 불가)" 명시. 코드 결함 아님(리뷰어 본인 판단과 동일)baseline/P4-perf.txt
3차 agy 2026-09-10 (review/P4-agy-r3-2026-09-10.md) — 병합 가능, BLOCKER 0·MAJOR 0
p4-r3am1MINORsnapshot() 이 파일 수만큼 lstat 을 한꺼번에 던짐수용 — 동시성 64 풀(SNAPSHOT_CONCURRENCY)src/cache.ts snapshot
p4-r3am2MINOR축출된 root 의 그래프 참조가 GC 전까지 남음수용 — evict() 가 graph·texts 참조를 명시적으로 끊는다src/cache.ts evict
P4-27+ codex 2026-09-10 (review/P4-27-codex-2026-09-10.md) — 병합 불가, BLOCKER 0 · agy (review/P4-27-agy-2026-09-10.md) — 조건부 보류, BLOCKER 0
p427-M1MAJOR큰 응답에서 두 모드 텍스트가 달라 '바이트 동일' 주장이 거짓수용 — 주장을 "작은 응답은 동일, envelope 초과 시 opt-in 이 더 짧음(의도)" 으로 정정하고 테스트로 고정server.ts 주석, README, DESIGN §3, p4-27-default-mode-guards
p427-M2MAJOR기본 경로가 envelope 예산을 건너뜀(JSON 이스케이프로 초과 가능)수용 — 텍스트 전용 envelope 재측정·축소server.ts, 테스트
p427-M3MAJOR헬퍼 기본 opt-in 이라 실제 기본 경로의 보안·상한이 미검사수용 — 헬퍼 기본 텍스트 전용, 계약 테스트는 opt-in 명시, 기본 모드 보안·상한 17건 신설p2-helpers.ts, 테스트 15파일
p427-M4MAJOR(codex) / MINOR(agy)structuredContent 의존 클라이언트 회귀, print-config 에 opt-in 없음부분 수용 — README opt-in 예시·릴리즈 노트 0.2.0 호환성 변경 예고. print-config --structured 는 기각(env 블록·--env 로 이미 가능, CLI 표면 확대 회피). 0.1.0 은 미공개라 기존 사용자 없음README, RELEASE-NOTES
p427-m1MINORcallTool 기본 true 가 서버 기본 false 와 반대수용 — 인자 필수화server.ts
p427-m2MINORDESIGN·README 에 조건 없는 outputSchema/JSON 서술 잔존수용DESIGN §3, README 표·suggested_next 문장
p427-m3MINORopt-in TextContent 가 JSON 직렬화가 아님(스펙 SHOULD)수용(문서) — 의도적 예외로 명시(텍스트 클라이언트·패리티 의존)README, DESIGN §3
p427-P2(반영 중 자체 검출)opt-in envelope 이 structuredContent 만 재 전송량이 상한 초과(341,496B)수정 — 전송 전체 측정 + 비례 보정 축소, P2-23 단언 정정server.ts, p2-11-23
282 files

Advantages

  • Provides persistent, cumulative knowledge storage rather than re-discovering information on every query. evidence: README.md
  • Implements a structured memory hierarchy with confidence scoring and decay management. evidence: CLAUDE.md

Limitations

  • Requires manual curation of raw sources and adherence to specific frontmatter schemas. evidence: CLAUDE.md
  • The system relies on specific agent-based workflows that require Claude Code to function as intended. evidence: README.md

Security

Risk level: Low risk

This brain runs

Hooks (2)

  • .claude/settings.json (SessionStart): python3 "$CLAUDE_PROJECT_DIR"/.claude/skills/wiki-lint/scripts/wiki-status-check.py
  • .claude/settings.json (PostToolUse): python3 "$CLAUDE_PROJECT_DIR"/.claude/skills/wiki-lint/scripts/stamp-updated.py

Agent instructions that run or fetch something (22)

  • .claude/skills/meeting-minutes/SKILL.md: python3 .claude/skills/meeting-minutes/scripts/pending-recordings.py .
  • .claude/skills/meeting-minutes/SKILL.md: `python3 .claude/skills/meeting-minutes/scripts/transcribe.py --check` 로 백엔드 가용성을 먼저 본다.
  • .claude/skills/meeting-minutes/SKILL.md: python3 $S/transcribe.py --backend local \
  • .claude/skills/meeting-minutes/SKILL.md: python3 $S/transcribe.py --backend openai --confirm-upload \
  • .claude/skills/meeting-minutes/SKILL.md: python3 .claude/skills/meeting-minutes/scripts/apikey.py status # 현재 출처 확인
  • .claude/skills/meeting-minutes/SKILL.md: python3 .claude/skills/meeting-minutes/scripts/apikey.py set-keychain # 키체인에 저장
  • .claude/skills/meeting-minutes/SKILL.md: python3 .claude/skills/meeting-minutes/scripts/apikey.py migrate # 레거시 파일 → 키체인
  • .claude/skills/meeting-minutes/SKILL.md: | 증상 | 원인 | 대응 | |---|---|---| | `ffmpeg 없음` | 미설치 | `brew install ffmpeg` | | `로컬 전사 엔진이 없다` | mlx-whisper 미설치 | `python3 -m pip install mlx-whisper` | | openai 백엔드가 거부됨 | `--confirm-upload` 누락 | 판정을 다시 하라.
  • .claude/skills/weekly-review/SKILL.md: python3 .claude/skills/wiki-lint/scripts/scope-expand.py expand \
  • .claude/skills/weekly-review/SKILL.md: python3 .claude/skills/wiki-lint/scripts/scope-expand.py pack $(cat /tmp/wr_cands.txt | cut -f3) > /tmp/wr_pack.md
  • .claude/skills/wiki-consolidate/SKILL.md: `python3 .claude/skills/wiki-consolidate/scripts/list-claims.py .` — L2 전체의 `claim::` 라인 수집.
  • .claude/skills/wiki-consolidate/SKILL.md: `python3 .claude/skills/wiki-consolidate/scripts/list-claims.py .`로 L2 전체 claim 수집 + `python3 .claude/skills/wiki-ingest/scripts/concept-index.py .`로 기존 L3 카탈로그.
  • .claude/skills/wiki-consolidate/SKILL.md: 승격·재확인 시 `python3 .claude/skills/wiki-consolidate/scripts/confidence.py --kinds <official,normal,verbal,…>` (또는 `--count N --official/--verbal`)로 점수를 구해 frontmatter `confidence:`에 기록.
  • .claude/skills/wiki-ingest/SKILL.md: python3 .claude/skills/wiki-ingest/scripts/concept-index.py .
  • .claude/skills/wiki-lint/SKILL.md: python3 .claude/skills/wiki-lint/scripts/validate-pages.py . --json
  • .claude/skills/wiki-lint/SKILL.md: python3 .claude/skills/wiki-lint/scripts/link-audit.py . --json
  • .claude/skills/wiki-lint/SKILL.md: python3 .claude/skills/wiki-lint/scripts/decay.py --scan . --json
  • .claude/skills/wiki-lint/SKILL.md: python3 .claude/skills/wiki-lint/scripts/search.py "<query>"
  • .claude/skills/wiki-ops/SKILL.md: python3 .claude/skills/wiki-lint/scripts/lint-due.py . 3
  • .claude/skills/wiki-ops/SKILL.md: python3 .claude/skills/wiki-lint/scripts/ingest-status.py . # 미인제스트 raw 소스 (pending)
  • .claude/skills/wiki-ops/SKILL.md: 리포트: `python3 metrics/cost-report.py` — 페이지당·소스100줄당 토큰, 낭비율, 총계.
  • CLAUDE.md: 미인제스트 raw 조회: `python3 .claude/skills/wiki-lint/scripts/ingest-status.py`.
  • Low risk: network-instructions (.claude/skills/meeting-minutes/SKILL.md:130)
  • Low risk: scripts-present (.claude/skills/meeting-minutes/scripts/apikey.py)
  • Low risk: scripts-present (.claude/skills/meeting-minutes/scripts/pending-recordings.py)
  • Low risk: scripts-present (.claude/skills/wiki-ingest/scripts/concept-index.py)
  • Low risk: scripts-present (.claude/skills/wiki-lint/scripts/decay.py)
  • Low risk: scripts-present (.claude/skills/wiki-lint/scripts/ingest-status.py)
  • Low risk: scripts-present (.claude/skills/wiki-lint/scripts/link-audit.py)
  • Low risk: scripts-present (.claude/skills/wiki-lint/scripts/lint-due.py)
  • Low risk: scripts-present (.claude/skills/wiki-lint/scripts/scope-expand.py)
  • Low risk: scripts-present (.claude/skills/wiki-lint/scripts/search.py)
  • Low risk: scripts-present (.claude/skills/wiki-lint/scripts/stamp-updated.py)
  • Low risk: scripts-present (.claude/skills/wiki-lint/scripts/validate-pages.py)
  • Low risk: scripts-present (.claude/skills/wiki-lint/scripts/wiki-status-check.py)

Attribution and licence

First committed by
GitHub avatar of cookyman74cookyman74
Publisher
Justin (Community)
Source
cookyman74/llmwiki-harness · commit 139bc84
Licence
MIT License (files served here)
  • ATTRIBUTION.md (written by AutoGineer)
Synced
Contributors
cookyman74

AutoGineer is not affiliated with or endorsed by cookyman74.

Download

The ZIP holds the files AutoGineer serves, with ATTRIBUTION.md and the licence. Download llmwiki-harness.zip