Files
kebab/CLAUDE.md
altair823 93e69c9bec docs: 재조정 후속 — 정책 문구 정합 + 코드 코멘트 lag 수정
spec 재조정(이전 commit)으로 생긴 정책↔실제 모순 + 검증이 발견한 코드 lag 정리.

- CLAUDE.md / DOCS.md: "frozen 편집 금지" → "reconciled baseline" 으로 정합.
  원칙 동결이되, 누적 drift/삭제로 문서가 현실과 모순되면 주기적 dated 재조정
  (✂/⟳/⚠)을 허용·기대. HOTFIXES 는 재조정 사이 fine-grained live deviation log.
- kebab-rag/src/pipeline.rs:580: doc-comment rag-multi-hop-v1 → v2
  (실제 const PROMPT_TEMPLATE_VERSION_MULTI_HOP 와 일치 — 검증 패스 발견).

코멘트 전용 변경이라 빌드 영향 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C5r6DDehC3ECcC2BxLsPHu
2026-06-27 23:09:28 +00:00

22 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project

Single-user local-first knowledge base + RAG. Rust 2024 workspace, 20 crates, single binary (kebab). All inference is local (Ollama + fastembed + whisper.cpp).

The repo's documentation is split by audience + zone — don't duplicate across them. DOCS.md is the doc map (what's the Source-of-Truth for what); start there if unsure where something belongs.

zones: living (현재 진실, 갱신 필수) · 설계 계약 (설계 의도 baseline — docs/superpowers/specs/2026-04-27-…-design.md 하나; 원칙 동결이되 코드와 크게 어긋나면 dated 재조정 주석(✂제거/⟳갱신/⚠모순수정)으로 정합, 2026-06-27 재조정 선례) · 증거 (rust_report, dogfood/v0.18.0). 그 외 historical 문서(옛 plans/handoffs/task specs/feature specs)는 2026-06-27 doc-reorg 에서 삭제 — git history 에만. 새 plan/handoff/per-feature spec 문서를 쌓지 않는다(그게 268개로 불어난 원인): 결정·deviation 은 HOTFIXES, 구조·불변식은 ARCHITECTURE, 릴리스는 CHANGELOG 에 직접.

  • README.md — first stop for an end user. Quick start, command table, one Mermaid logical-architecture diagram, configuration pointers, license. Stays narrow. (living)
  • DOCS.md — 문서 지도 / Source-of-Truth 인덱스. "어느 문서가 현재 진실인지" 한눈에. (living)
  • HANDOFF.md — phase-level progress dashboard. Phase status table, "next task candidates", short milestone pointers. The README never duplicates this. (living)
  • tasks/INDEX.md — per-component status dashboard. (living)
  • docs/ARCHITECTURE.md — internal structure: crate dependency graph, directory tree, locked-in technical decisions, 핵심 구현 불변식. (living)
  • tasks/HOTFIXES.md — dated post-merge deviation log; live source of truth where behavior and the contract disagree. (living)
  • CHANGELOG.md — release 변경 이력 인덱스. (living)
  • docs/superpowers/specs/2026-04-27-kebab-final-form-design.md — frozen design contract (의도 baseline).

Build / test / lint

cargo test -p <crate>                          # preferred — per-crate, parallel-safe
cargo test -p <crate> <test_name>              # single test (substring match)
cargo test --workspace --no-fail-fast          # full suite
cargo clippy --workspace --all-targets -- -D warnings   # CI gate
cargo build --release                          # produces target/release/kebab

The dev/test profile is already trimmed (debug = "line-tables-only", split-debuginfo = "unpacked" — see workspace Cargo.toml), but target/ still balloons across task cycles (incremental artifacts pile on top of every test-binary's debug info). Run cargo clean routinely after each merged PR — recovery is cheap (one re-link per crate; backtraces still resolve). Disk-layout + cleanup policy lives in the global ~/.claude/CLAUDE.md; keep the build/target dir under large_data per that file.

The facade rule

kebab-app is the only crate UI binaries (kebab-cli, future kebab-desktop) may touch. Every user-facing entry has a *_with_config(cfg, …) companion that takes an explicit Config:

  • kebab-cli calls the *_with_config form so --config <path> is honored.
  • The bare kebab_app::ingest(...) / search(...) / ask(...) form re-loads Config::load(None) (XDG default) and silently bypasses any explicit path. Two regressions of exactly this shape are recorded in tasks/HOTFIXES.md (P3-5 + P4-3 follow-ups). When wiring a new CLI subcommand, always thread the Config through.

*_with_config is #[doc(hidden)] pub fn but it's the official config-explicit API, not a test seam.

Spec contract

docs/superpowers/specs/2026-04-27-kebab-final-form-design.md (12 sections) is the single frozen design contract for the whole workspace — "무엇을 의도했나"의 baseline. (The per-component task specs tasks/p<N>/ + per-feature design specs that referenced it were removed in the 2026-06-27 doc-reorg; the shipped code is the implementation truth, the design contract is the intent baseline (periodically dated-reconciled to current code — see the doc's 2026-06-27 banner), and live deviations live in HOTFIXES between reconciliations.)

  • The design contract is the intent baseline, kept stable — avoid free-form rewriting to chase every code change. BUT when accumulated drift/removals make it describe nonexistent features, a periodic dated reconciliation pass (✂ removed / ⟳ updated / ⚠ contradiction-fix — see the 2026-06-27 banner in the doc) is permitted and expected. HOTFIXES remains the fine-grained live deviation log between reconciliations.
  • Live deviations from the contract go in tasks/HOTFIXES.md as dated entries. Treat HOTFIXES.md as the live source of truth when behavior and the contract disagree. Don't recreate per-feature spec/plan/handoff files — record decisions in HOTFIXES, structure/invariants in docs/ARCHITECTURE.md, releases in CHANGELOG.md.

tasks/INDEX.md is the per-component status dashboard; HANDOFF.md is the phase-level dashboard. Update them when a component/phase status changes. The full doc map (what's the SoT for what) is DOCS.md.

Allowed / forbidden deps

Crate-boundary rules per design §8 (consolidated here — the per-task specs that originally held these were removed in the 2026-06-27 doc-reorg; this table + design §8 are the reference). New crates inherit the boundary rules of their category (e.g. a new embedder → same forbidden list as kebab-embed-local). Verify against the crate's Cargo.toml before adding an import.

Crate / group Forbidden imports Why
kebab-core all kebab-* 도메인 타입+trait 만. 구현 없음 → 순환 의존 0.
kebab-config 모든 상위 crate (parsers/stores/embed/search/llm/rag/app/UI) platform-agnostic. kebab-core 만 의존.
kebab-parse-* (md/pdf/image/code), kebab-chunk kebab-store-*, kebab-embed-*, kebab-search, kebab-llm-*, kebab-rag, UI CanonicalDocument/Chunk 만 trait 로 산출. 검색/임베딩/저장과 런타임 결합 없음.
kebab-search concrete kebab-embed-*, kebab-llm-*, kebab-rag, UI 검색은 임베더 provider 무관. adapter 는 app 에서 주입.
kebab-embed-local/ollama, kebab-llm-local parsers, chunk, stores, search, 다른 embed/llm, rag, UI provider 구현은 교체 가능한 trait. 파이프라인 결합 없음.
kebab-rag parsers, chunk, concrete kebab-embed-*/kebab-llm-*, UI trait object 를 app 이 주입. adapter 직접 의존 없음.
kebab-eval (특히 metrics/compare) parsers, chunk, kebab-store-vector, embed, search(직접), llm, rag, UI runner 는 kebab-app facade + SQLite store 만. metrics/compare 는 런타임과 분리.
UI crates (kebab-cli, kebab-mcp, future kebab-desktop) kebab-store-*, kebab-parse-*, kebab-chunk, kebab-embed-*, kebab-search(직접), kebab-llm-*, kebab-rag 모든 진입은 kebab-app facade 만 (design §8).

Wire schema v1

All --json output carries a schema_version field. Current schemas: ingest_report.v1, ingest_progress.v1, search_hit.v1, answer.v1, doctor.v1, reset_report.v1, schema.v1, error.v1, chunk_inspection.v1, citation.v1, doc_summary.v1. Schemas live in docs/wire-schema/v1/. The wire shape is the contract for external integrations (Claude Code skills, MCP, etc.); breaking it requires a *.v2 major bump and parallel-running both for one phase. In --json mode, fatal errors emit error.v1 to stderr as ndjson (non---json mode keeps plain stderr text); exit codes 0/1/2/3 are unchanged — error.v1.code provides fine-grained agent branching.

In-tree integration packages live under integrations/<host>/ — currently integrations/claude-code/kebab/ (a Claude Code skill that calls kebab search --json / kebab ask --json). Any wire schema major bump (v1→v2) MUST update each shipped integration in the same PR, same as the version-cascade rule below. Per-user trigger keywords (team / system / acronym) belong in the user's local copy of the skill, not in the repo-shipped frontmatter — keep integrations/claude-code/kebab/SKILL.md's description generic.

Versioning cascade

parser_version / chunker_version / embedding_version / prompt_template_version / index_version follow the cascade rule in design §9. Changing any of these invalidates downstream records (chunks, embeddings, eval runs, …). When changing a version: either ship a re-process job or treat it as a breaking schema bump. The eval runner snapshots all five into eval_runs.config_snapshot_json.

Release / binary version bump

Workspace Cargo.tomlversion 은 binary release 의 정체성. 다음 트리거 중 하나 발생 시 bump + 새 release 컷:

  • 사용자가 새 바이너리로 도그푸딩 또는 실사용 을 할 필요가 있다고 명시.
  • breaking schema change (V00X migration / wire schema major bump v1→v2 등) 가 머지된 후 — 이전 릴리즈 binary 가 새 DB / 새 wire 와 호환 안 됨. wire 의 additive minor 변경 (예: IngestReport.unchanged 같은 필드 추가) 은 backward-compat 이라 본 트리거에 해당 안 됨.
  • frozen design contract 변경 (design §X 갱신) 이 머지된 후.

Bump 자체는 단순 minor / patch 한 줄 수정 (Cargo.toml workspace version) — 이미 모든 kebab-* crate 가 version = { workspace = true } 라 자동 cascade. 동시에 Cargo.lock 자동 갱신.

Release 절차:

  1. gitea-release v<X.Y.Z> (gitea-ops skill) 으로 tag + push + release notes.
  2. release notes 는 사용자 도그푸딩에 영향이 가는 surface 변경을 위주로 — wire schema 추가, CLI flag 신규, TUI 키 변경, V00X migration 등 — 다룬다. 이때 추가된 기능과 변경사항은 유저가 이해할 수 있도록 친절하고 자세하게 풀어서 설명해야 하며, 단순히 commit subject 를 나열하는 형태로 끝내면 안 된다. 필요하다면 도그푸딩이나 테스트 결과도 함께 적어 둔다.
  3. 프리-1.0 (0.x.y) 단계 bump 규칙 — 기능(behavior) 또는 인터페이스(interface) 변경 여부로 판정:
    • minor bump (0.x.0): 기능 또는 인터페이스에 실질적 변경이 있을 때. 인터페이스 = 신규/변경/삭제된 CLI subcommand·flag, config 키, wire schema 의 breaking 변경, 임베딩/검색/RAG 등 사용자가 받는 결과·동작의 변화, V00X migration, frozen 설계 변경. 기능 = 새 source 형식·검색 모드·백엔드 등 할 수 있는 일의 추가/변경.
    • patch bump (0.x.y): 기능·인터페이스 변경이 없을 때. bug fix, 내부 refactor, 성능 개선, 로깅/진행표시 등 관측성(observability) 개선, additive-only wire 변경(backward-compat 신규 필드/이벤트라 기존 소비자 무영향), 문서. ← 즉 "결과가 같고 새 명령/플래그/config 도 없으면 patch".
    • 경계 예: 진행 로그에 phase/파일명 추가 + additive wire 이벤트(asset_phase) = patch (검색·색인 결과 불변, 새 명령/플래그/config 없음). arctic 임베더 provider + 신규 config 키 = minor (인터페이스 추가). 별칭 기능 제거 + migration = minor (동작·인터페이스 변경).

bump 시점 = release 시점 같은 commit. 즉 commit chore: bump version 0.x → 0.y 직후 같은 commit 에 tag. v0.1.0 (2319206) 처럼 bump 없이 tag 만 찍는 패턴은 후속 release 가 대상 commit 을 헷갈리게 함 — pre-release snapshot 은 SHA reference 로 충분.

Dogfood trigger

도그푸딩 = 새 binary 를 실제 KB / 실제 query 로 돌려보고 user-visible 동작이 spec 의 의도와 일치하는지 확인하는 종단 검증. unit / integration test 가 못 잡는 회귀 (UX 어색함, performance regression, 의외의 token 처리, embedding drift, RAG hallucination) 를 catch 함. PR 머지 전 또는 머지 직후 release notes 작성 전에 실시.

도그푸딩이 필요한 시점

다음 트리거 중 하나라도 hit 시 도그푸딩 필수. 모두 release-level 또는 user-visible behavior 변경 임.

Schema / migration:

  • 신규 V00X migration (예: V007 trigram, V008 OCR mirror, V009 morphological) — corpus_revision cascade + auto-backfill 정책의 사용자 경험 확인.
  • frozen design contract 변경 (docs/superpowers/specs/2026-04-27-kebab-final-form-design.md §X 갱신) — verbatim CI diff-check 외의 user-visible side effect 확인.

Wire schema / CLI surface:

  • 신규 --json 필드, exit code 변경, 또는 schema major bump (v1 → v2) — agent / external integration 의 호환성 검증.
  • kebab 의 subcommand 또는 flag 추가/삭제/rename — agent skill / muscle memory 영향.

Search / RAG behavior:

  • FTS5 tokenizer / chunker / embedder 모델 / RAG prompt template 변경 — 같은 query 의 hit ordering, snippet, RAG citation 패턴이 자연스럽게 변화하는지.
  • score gate, RRF fusion ratio, NLI threshold 같은 ranking 파라미터 default 변경.

Performance:

  • ingest / search / ask latency 의 의도된 변화 (예: lindera tokenize, OCR 추가, multi-hop RAG) — actual wall-clock 측정 + release notes 에 명시.
  • 대용량 KB (수천 doc / 만 chunk) 의 first-boot eager backfill 시간이 사용자 hang 인지에 영향 안 가는지.

Language / locale:

  • 한국어 / 일본어 / 중국어 lexical 동작 변경 (V007 trigram, V009 morphological, future N-gram).
  • 영어 substring 매칭 같은 ad-hoc 부산물의 회귀.

File / asset surface:

  • 신규 source 형식 (PDF OCR, audio, video) — extractor / chunker 의 실제 corpus 동작.
  • .kebabignore / _external/ 같은 workspace 정책 변경.

Release-level: 위 트리거 중 하나가 hit 되어 Cargo.toml workspace version bump 가 필요하면, bump commit 이전에 도그푸딩 evidence 가 HOTFIXES + release notes 에 명시 되어 있어야 함. evidence 없는 release 는 사용자가 "왜 bump 했는지" 추적 불가.

도그푸딩 데이터 보관소

모든 도그푸딩 source 문서 + KB state + 로그는 머신-로컬 도그푸딩 store 한 디렉토리에 누적 보관한다. 머신별 실제 경로(이 머신은 large_data/out/ 아래)는 global ~/.claude/CLAUDE.md §디스크 + 프로젝트 메모리에 기록 — repo 안이나 XDG 기본 경로(~/.config/kebab, ~/.local/share/kebab/tmp 산발 위치는 도그푸딩 store 로 쓰지 않는다. 분류는 문서 의미 / 종류 / 형식 기준만 — version·생성 시점·scenario 이름 prefix 금지 (v0.20.1-dogfood/ 같은 디렉토리 신설 X). store 하위 레이아웃:

  • corpus/ — source 문서 (read-only). format 별 분류 (markdown/, code/, html/, images/, pdf/, …) + 각 format 내 category 별 (예: markdown/{korean,english,bilingual,tech-docs,notes,edge-cases}, code/{rust,python,…}). 새 fixture 는 적절한 category subdir 에 추가.
  • kb/ (또는 xdg/) — 도그푸딩 run 의 KB 출력 (SQLite + LanceDB + assets + models). 매 run 마다 reset 가능. 별 KB 디렉토리 신설 X.
  • logs/ — 누적 실행 로그 (ndjson + stderr + summary).
  • config.toml — canonical 도그푸딩 config (없으면 kebab init 후 path override).
  • _archive/ — regeneratable stale state (이전 run 의 sqlite/lancedb, XDG snapshot). 디스크 압박 시 wipe 가능.

ad-hoc fixture 가 필요하면 store 의 corpus/<format>/<category>/ 에 추가 — 새 산발 디렉토리 신설 금지.

도그푸딩 결과 기록

도그푸딩 evidence 는 두 곳에 cascade:

  1. tasks/HOTFIXES.md 의 dated entry — 시나리오 별 hit count 표 + snippet evidence + known limitation. 미래에 spec drift 의심 시 git history 외 immediate reference 가 됨.
  2. docs/release-notes/v<X.Y.Z>-draft.md (또는 gitea release body) — 사용자 도그푸딩 영향에 영향이 가는 surface 변경을 4 단락 (변경 사실 / trade-off / mitigation / upgrade 절차) 으로 풀어서 설명. evidence link.

도그푸딩 단계에서 발견된 bug (spec 과 실제 동작의 mismatch, performance regression, UX 어색함) 는 즉시 fix → re-dogfood. fix 가 별 PR 으로 빠지면 머지 후 HOTFIXES 에 dated entry.

DOGFOOD scenario catalog (§1~§13) 는 docs/DOGFOOD.md. 신규 release 마다 §관련 section 의 scenario list 갱신 + 신규 scenario 추가.

Naming + paths

  • Crate prefix: kebab- (kebab-case package, kebab_ snake_case in Rust modules).
  • Binary: kebab.
  • Env var prefix: KEBAB_* (e.g. KEBAB_RAG_SCORE_GATE, KEBAB_EVAL_GOLDEN, KEBAB_COMMIT_HASH).
  • XDG paths: ~/.config/kebab/, ~/.local/share/kebab/, ~/.cache/kebab/, ~/.local/state/kebab/.
  • SQLite filename: kebab.sqlite (under data_dir).
  • Workspace ignore: .kebabignore (per directory).
  • _external/ (under workspace.root): single-file / stdin ingest 가 외부 file 을 deterministic 명명 (<blake3-12>.<ext>) 으로 copy. 첫 생성 시 .kebabignore 자동 append.

The migration from the old kb name lives in commits 911fb49 / f1a448d / f9714aa. If you spot a leftover kb reference, treat it as a leftover and fix it (the rename PR sweep covered crates/, docs/, tasks/, README, design doc, fixtures — but workspace root Cargo.toml comments needed a follow-up; assume similar misses are possible).

Smoke + integration

docs/SMOKE.md walks through running the full pipeline against an isolated TempDir KB via --config /tmp/kebab-smoke/config.toml. Use this instead of touching ~/.local/share/kebab/ when verifying a fresh clone or a CLI flag change. Most CLI regressions surface here, not in unit tests (see HOTFIXES.md).

User-facing docs (README + HANDOFF + ARCHITECTURE)

Three sibling docs split the audience. Every implementation PR (feat/*) keeps them in sync; spec PRs (spec/*) don't touch any of the three.

README 최신성은 hard requirement. README 는 항상 현재 코드베이스를 반영해야 한다 — 기능이 추가·변경·삭제되거나 사용자가 닿는 표면(CLI 서브커맨드/플래그/--json 필드/config 키/기본값/XDG 경로)이 바뀌면 그 PR 에서 README 를 함께 갱신한다 (제거된 surface·플래그는 README 에서도 제거 — 옛 surface 가 남아 있으면 안 됨). 단, README 에 담는 깊이는 "사용자가 바로 쓰는 설명서" 수준(무엇을·어떻게: 명령·플래그·config·Quick start·기능이 해주는 일)이지 아키텍처 설명이 아니다. 내부 구조·crate 그래프·기술 결정·구현 디테일은 README 에 쓰지 말고 docs/ARCHITECTURE.md 에 반영한다. 경계 = "사용법은 README, 구조는 ARCHITECTURE". 같은 변경이 둘 다 건드리면 양쪽을 각자의 altitude 로 갱신한다 (예: 새 source 형식 → README 는 "이제 X 도 색인됨" 한 줄, ARCHITECTURE 는 extractor/chunker 배선 상세).

README.md — end user. Stays narrow. The surfaces a user touches:

  • CLI — new kebab <subcommand>, flag, --json field, or exit-code change. Update the 명령 table and the Quick start block if the new flow needs a different invocation. 삭제된 서브커맨드/플래그는 표·예시에서 제거.
  • UI surface — 현재 사용자 UI 는 kebab CLI 가 유일하다 (kebab tui + kebab-tui crate 는 v0.31.0 에서 제거, desktop 은 P9-5 미착수). 새 외부 UI surface(예: desktop) 가 실제로 land 하면 명령 표 + Mermaid 다이어그램 갱신.
  • Configuration — new config.toml field, KEBAB_* env, default change, or XDG path. Update the Configuration section AND the config example block in docs/SMOKE.md.

The Mermaid logical-architecture diagram stays the only diagram in the README. If a new media type / external service / store crosses the diagram boundary, update it; otherwise leave it alone.

The README does NOT carry: phase status, component count, post-merge deviations, crate dependency graph, directory tree, locked-in technical decisions. Those live in HANDOFF or ARCHITECTURE.

HANDOFF.md — handing off. Phase-level progress + next-task candidates. Flip the relevant phase row from to when a phase epic completes. Add a one-line entry under "머지 후 발견된 버그 / 결정 (요약)" when a HOTFIXES entry lands that's load-bearing for someone picking up the project. Per-component progress lives in tasks/INDEX.md, not here.

docs/ARCHITECTURE.md — implementation detail. Crate dependency graph, directory tree, locked-in technical decisions. Update when:

  • A new crate is added — extend the graph + directory tree.
  • A locked-in decision flips (e.g. OCR engine default changes per a HOTFIXES entry) — update the table and link the HOTFIXES entry.
  • A directory moves — update the tree.

Out of scope for all three: HOTFIXES detail (tasks/HOTFIXES.md), version cascade mechanics (CLAUDE.md §Versioning cascade), the frozen design contract's section detail (docs/superpowers/specs/2026-04-27-…-design.md).

If a feature ships behind a flag that's off-by-default, mention the flag explicitly in the README so a user reading only the README knows the surface exists but is gated.

Remote

Git remote is Gitea: https://gitea.altair823.xyz/altair823-org/kebab.git. PRs are created via the Gitea REST API (POST /repos/altair823-org/kebab/pulls) — gh CLI does not work against this host. Auth uses ~/.netrc (populated via git credential fill).