md-heading-v2(PR #209)의 oversize 분할을 PDF 청커에도 적용. v1.1 의 chunk_page 는 문장/문단 경계로만 잘라서 경계 없는 거대 페이지(빽빽한 scanned page 한 줄 OCR)가 통째로 한 청크 → strict 임베더 실패. v1.2 는 2-tier: tier-1(문장/문단 greedy + overlap) 후 segment 가 max_chunk_tokens 초과면 tier-2 가 공유 text_pieces 로 재분할 → 모든 PDF 청크 ≤ 예산. - 신규 공유 모듈 crate::oversize (text_pieces/char_pieces/BYTES_PER_TOKEN) — md 와 PDF 가 공유(단일 진실 공급원). md-heading-v2 는 호출만, 출력 byte-identical(md 라벨·동작 불변, parity 테스트 전부 통과). - PdfPageV1Chunker { max_chunk_tokens } + policy_hash budget fold(md 동형) + pdf_chunker_from_config(kebab-app). 신규 config 키 없음. - 분할 조각 chunk_id 는 #c{segment_start}s{i}(미분할 단일 segment 는 bare #c{segment_start} 유지 → 공통 경우 v1.1 동일). - Page span: 분할 조각은 부모 segment 의 char 범위를 그대로 가짐(segment-granular, md 동형). per-piece narrowing 은 text_pieces 의 줄 구분자 소실로 drift 하는 버그라 코드 리뷰 후 제거 — 회귀 테스트 oversize_pdf_page_with_newlines_splits_without_span_drift 로 잠금. - chunker_version v1.1→v1.2 → 다음 ingest 에서 PDF 1회 자동 재청크(md/code 무영향). 검증: kebab-chunk lib 93 pass(span 회귀 포함), kebab-app green, clippy 0. 도그푸딩 (실험 KB, scanned PDF + arctic@Lemonade, budget 200): 625/625 errors=0, scanned_page1 1→3 청크·scanned_page2 3→7 청크(둘 다 pdf-page-v1.2), 전 코퍼스 10215 청크 전부 ≤200. 버전 bump 은 follow-up 들과 함께 배치 릴리스에서 일괄. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
Components
책임 단위 그룹별 contributor 향 상세. 사용자 향 grand picture 는 README.md, 상위 crate 의존 그래프 + 디렉토리 구조 + locked-in 결정은 docs/ARCHITECTURE.md, 진척도는 HANDOFF.md, per-task spec 은 tasks/INDEX.md.
각 그룹 페이지는 동일 템플릿: 구성 crate / 구조 다이어그램 / data flow 다이어그램 / 주요 type / 외부 의존 / 핵심 결정 (HOTFIXES + spec 의 "왜") / 관련 spec / HOTFIXES.
그룹 wiring
12 그룹 간 호출/의존 흐름. 점선 = Foundation 이 모두에 의존. UI 는 App facade 만 통해 다른 그룹 도달.
flowchart TB
subgraph Surfaces ["UI surface"]
UI["UI<br/>(cli + tui)"]
end
subgraph Orchestration ["orchestration"]
AppFacade["App facade<br/>(kebab-app)"]
RAG["RAG"]
Eval["Eval"]
end
subgraph IngestPipe ["ingest pipeline"]
Source["Source"]
Parse["Parse"]
NormChunk["Normalize+Chunk"]
end
subgraph IndexQuery ["index + retrieval"]
Embed["Embed"]
Store["Store"]
Search["Search"]
end
subgraph Generation ["generation"]
LLM["LLM"]
end
Foundation["Foundation<br/>(core + parse-types + config)"]
UI --> AppFacade
AppFacade --> Source --> Parse --> NormChunk
NormChunk --> Store
NormChunk --> Embed --> Store
AppFacade --> Embed
AppFacade --> Search
Search --> Store
Search --> Embed
AppFacade --> RAG
RAG --> Search
RAG --> LLM
RAG --> Store
AppFacade --> Eval
Eval --> AppFacade
Eval --> Store
Foundation -.-> Source
Foundation -.-> Parse
Foundation -.-> NormChunk
Foundation -.-> Embed
Foundation -.-> Store
Foundation -.-> Search
Foundation -.-> LLM
Foundation -.-> RAG
Foundation -.-> AppFacade
Foundation -.-> UI
Foundation -.-> Eval
그룹 목록
| 그룹 | 역할 | 페이지 |
|---|---|---|
| Foundation | 도메인 type + 설정 + parser IR. 모든 crate 의 zero-dep 토대. | foundation/ |
| Source | 워크스페이스 walk + .kebabignore + BLAKE3 checksum → RawAsset. |
source/ |
| Parse | bytes → ParsedBlock (md) 또는 CanonicalDocument (pdf/image). OCR + caption 어댑터. |
parse/ |
| Normalize+Chunk | ParsedBlock → CanonicalDocument lift (markdown only) + 모든 미디어 → Vec<Chunk> (md/pdf 변종 chunker). |
normalize-chunk/ |
| Store | SQLite (V001-V005, FTS5, jobs, chat sessions) + LanceDB (per-model vector 테이블) two-phase write. | store/ |
| Embed | Embedder trait + fastembed-rs 어댑터 (multilingual-e5-small 384d). |
embed/ |
| Search | lexical (FTS5 BM25) + vector (ANN) + hybrid (RRF) — Retriever trait 3 변종. |
search/ |
| LLM | LanguageModel trait + Ollama HTTP 어댑터 (gemma4:e4b default). streaming + cancel-safe. |
llm/ |
| RAG | retrieve → gate → pack → generate → cite-validate → persist 9 stage pipeline. multi-turn 지원. | rag/ |
| App facade | kebab-app — 모든 UI binary 의 유일한 진입점. *_with_config companion 패턴. |
app-facade/ |
| UI | kebab-cli (--json wire envelope) + kebab-tui (4 패널 + Mode machine + cheatsheet). |
ui/ |
| Eval | golden query 회귀 평가 + run-vs-run compare. must_contain rule-based. |
eval/ |
진입 가이드
처음 읽는다면 (의존성 따라 bottom-up):
- Foundation — 다른 모든 페이지가 참조하는 type 정의.
AssetId/DocumentId/Chunk/Citation/ 5 version 등. - Source → Parse → Normalize+Chunk → Store — ingest pipeline 흐름.
- Embed → Search — retrieval.
- LLM → RAG — generation.
- App facade — 위 전부 wiring.
- UI — facade 위.
- Eval — 독립.
특정 작업 별 진입:
- 새 미디어 타입 추가 (예: epub) — Parse → Normalize+Chunk → Store (chunker_version) → App facade (라우팅).
- 새 retrieval 모드 — Search → App facade (mode dispatch) → UI (--mode flag).
- 새 LLM 어댑터 — LLM (trait crate, 새 type 금지) + 새
kebab-llm-<provider>crate → App facade (config provider switch). - TUI 신규 pane — UI 만. Mode + Theme + InputBuffer 재사용.
다이어그램 제약
각 그룹 페이지의 다이어그램은 mermaid (Gitea / GitHub 자동 렌더). 페이지 별 최소 2개 — 구조 (type/trait/struct 관계) + data flow (입출력 흐름). 실제 코드와 시그니처 일치 — 작성 시 crates/kebab-<name>/src/lib.rs 직접 읽음 (추측 금지).