리뷰 두 건에서 나온 지적을 반영한다.
1) 실패 양상이 바뀐 것을 다루지 않았다 (MEDIUM)
`chunk_id` 로 행을 찾던 때는 shadow 정렬이 어긋나도 느릴 뿐 정확했다.
rowid 로 찾으면 어긋난 순간 `chunks_ad` 가 남의 문서 shadow 행을 지우고
아무 오류도 내지 않는다. 즉 이 PR 은 실패 양상을 "느림"에서 "조용한
오삭제"로 바꿨는데, 그 불변식이 눈에 안 보이는 상태였다.
`kebab doctor` 에 `fts_shadow` 점검을 넣었다. 전수 대조는 60만 chunk
에서 33초라 doctor 앞에 둘 수 없어 rowid 범위 앞뒤 200행씩만 본다 —
실측 10 ms 이고, 현실적인 드리프트가 취하는 전면 재번호는 잡는다.
표본이라는 사실을 detail 에 적어 정렬 증명으로 읽히지 않게 했다.
`SqliteStore::fts_shadow_misaligned_sample` 이 질의를 들고 있다.
2) VACUUM 위험을 과장했다 (정정)
초안이 "VACUUM 이 rowid 를 다시 매길 수 있고 그러면 정렬이 깨진다"고
단정했다. 실제로 재보니 다시 매기지 않았다 — 실제 KB 사본(60만 chunk,
문서 3,000건을 지워 rowid 에 구멍을 낸 뒤)과 소형 합성 DB 양쪽에서
VACUUM 후 전수 대조 불일치가 0 이었다 (sqlite 3.53.4). SQLite 문서가
"다시 매길 수 있다"고 적은 것은 보장이 없다는 뜻이지 실제로 그렇게
한다는 뜻이 아니다. 문구를 실측대로 고쳤다.
남는 실제 경로는 앞으로 `chunks` 를 테이블 재작성 방식으로 바꾸는
마이그레이션이다. V016 주석에 "그런 마이그레이션은 repopulate 를 같이
돌려야 한다"는 울타리를 박았다.
3) 같은 실측치를 파일마다 다르게 적었다 (MEDIUM)
삭제 시간이 커밋 메시지·HOTFIXES 는 2.0초, 마이그레이션 주석·테스트
독스트링·설계 문서는 0.73초였다. 0.73초는 손으로 마이그레이션한 사본을
따뜻한 캐시에서 잰 값이고 2.0초는 릴리스 바이너리가 마이그레이션한 새
사본에서 잰 값이다. 보수적인 2.0초로 통일했다. '한국' hit 수도
15,837(문서 200건 삭제 후) 과 15,977(전체 코퍼스) 이 섞여 있어
15,977 로 통일했다.
4) docs/ARCHITECTURE.md 디렉토리 트리가 V001..V015 로 멈춰 있었다 (MEDIUM)
V016 까지로 갱신. README 는 손대지 않는다 — 새 서브커맨드·플래그·config
키·`--json` 필드가 없다.
5) 잔가지 (LOW)
`:=` 검사가 번들 SQLite 의 FTS5 idxStr 인코딩에 기대는 것을 assert
메시지에 적었다 (rusqlite 를 올린 직후 실패하면 거기부터 보라는 뜻).
가상 테이블은 항상 `SCAN` 으로 찍히므로 `SEARCH` 로 대체 검사할 방법이
없다는 것도 독스트링에 남겼다. `kb index --rebuild-fts` 라는 옛 이름 +
존재하지 않는 명령 참조 두 곳을 지웠다.
`fts_v016_shadow_probe_detects_forced_drift` 로 탐지 자체를 시험한다 —
어긋난 shadow 행을 억지로 만들어 점검이 잡는지 본다. 잡지 못하는 점검은
없느니만 못하다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017c9JwQq8ZkGvYjpKXMiDhF
`chunk_id` 는 `chunks_fts` 에서 UNINDEXED 다. 그런데 V002 이래 삭제
트리거가 그 컬럼으로 행을 찾았다 (`DELETE FROM chunks_fts WHERE
chunk_id = old.chunk_id`). FTS5 는 UNINDEXED 컬럼에 색인을 만들지 않으니
이 조건을 만족할 색인이 없고, 삭제가 색인 전체 스캔으로 떨어진다.
chunk 한 건 삭제가 O(색인 전체) 였다.
`chunks` 에서 DELETE 가 나가는 모든 경로가 이 비용을 냈다 —
sweep_deleted_files, reset --orphans-only, 그리고 파일이 수정될 때마다
도는 purge_orphan_at_workspace_path. 즉 정상적인 증분 재색인이 코퍼스가
커질수록 느려지는 형태였다.
V016 이 chunks_fts 를 drop 후 재생성하면서 rowid 를 chunks.rowid 와
맞추고, 세 트리거의 행 지정을 rowid 로 바꾼다. FTS5 는 rowid 로 B-tree
조회를 하므로 O(log n) 이 된다. 컬럼 구성·tokenizer 는 그대로라 검색
경로는 한 줄도 안 바뀐다.
실제 KB 사본 (문서 28,427건 / chunk 600,808건) 에서 문서 200건 삭제:
현행 (chunk_id 로 DELETE) 1590.1초
V016 (rowid 로 DELETE) 2.0초
약 800배. 삭제 후 남은 chunks 와 chunks_fts 행 수가 양쪽 다 595,741 로
같다. 마이그레이션 자체는 60만 chunk 기준 32초이고 재색인은 필요 없다.
검색 결과는 불변이다. '한국'(15,977) / 'database'(1,067) / '서울
지하철'(230) / 'kebab'(3) 네 질의의 상위 20건을 chunk_id·bm25
점수·snippet 까지 해시로 비교했고 전후가 동일했다. 그래서 corpus_revision
을 올리지 않는다 — 어휘 검색 정렬이 `ORDER BY score, f.chunk_id` 라
rowid 와 무관하므로 미결 pagination cursor 를 무효화할 이유가 없다.
tokenizer 가 바뀐 V009 와는 다른 경우다.
이슈가 제안한 external-content(`content='chunks'`) 는 택하지 않았다.
V009 트리거가 색인하는 값이 `tokenized_korean_text || ' ' || text` 라
chunks 의 어느 컬럼과도 일치하지 않아, generated column 신설 + FTS
테이블에서 chunk_id/doc_id 제거 + 검색 경로의 rowid join 전환이 딸려온다.
삭제 비용은 rowid 정렬만으로 같은 복잡도로 내려가므로 본문 그림자
(chunks_fts_content, 실측 550 MB) 회수는 별 건으로 남긴다.
rebuild_chunks_fts 도 같이 고쳤다. rowid 를 명시하지 않으면 FTS5 가 자기
번호를 매겨 정렬이 깨지고 그 뒤의 모든 삭제가 조용히 아무것도 안 하게
된다. 이 함수에는 별개의 잠복 결함도 있었다 — V009 가 색인하는 한국어
형태소 접두를 빠뜨리고 raw text 만 넣고 있어서, 재구축을 돌리면 2자
한국어 질의가 다음 재색인 때까지 안 맞았다. 트리거와 같은 CASE 로 맞췄다.
전제: chunks 는 chunk_id TEXT PRIMARY KEY 라 INTEGER PRIMARY KEY 가 없고,
SQLite 의 VACUUM 은 그런 테이블의 rowid 를 다시 매길 수 있다. kebab 은
VACUUM 을 실행하지 않으며(코드베이스 전체에 없음) 사용자가 직접 돌렸다면
rebuild_chunks_fts 가 복구 경로다. external-content 도 같은 전제를 깔고
있어 이 위험은 선택지 간 차이가 아니다.
design §5.5 verbatim block 을 rowid 트리거로 갱신하고 CI diff-check 를
V009 에서 V016 으로 재조준했다 (V007 → V009 때와 같은 방식).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017c9JwQq8ZkGvYjpKXMiDhF
여러 세션 누적으로 ~268개로 불어난 문서를 3 병렬 explorer 로 전수 특성화한 뒤
재정리 설계. 코어(living+frozen)는 건강하고 SoT 도 territory 별로 정의돼 있으나
단일 지도 부재 + historical 아티팩트(plans 64·handoffs 8) 가 트리에 섞임.
설계(방안 B): 3구역 분리(living/frozen/archive) + root DOCS.md SoT 지도 신설 +
오펀 stub 7 삭제 + release-notes 9→CHANGELOG.md 통합 + historical 을
docs/archive/ 로 git mv + stale 컴포넌트 README 2건 수정 + CLAUDE.md §docs 갱신.
frozen specs/tasks 는 거버넌스 앵커라 무변경(이동 전 frozen→대상 링크 grep gate).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
(a) embed_with_cache 를 image/pdf/code 핸들러에 배선(markdown 전용→전미디어) — PR1.
(b) ocr/caption derivation_cache 네임스페이스로 OCR/caption 중간 산출물 캐싱 —
버전-캐스케이드 분리 + ollama-vision 비결정성 박제 — PR2.
리서치로 해소: provenance 는 wire/게이트에 없어 캐시 히트 시 재현 불필요(byte-identical
유지) · payload 는 full 구조체 serde(block 레벨까지 byte-identical) · derivation_cache_key_bytes
신설 · paddle ONNX 자산은 이미 레포 번들(다운로드 불필요) → 결정적 게이트 가능.
열린 항목: paddle 게이트용 text-bearing OCR 코퍼스 생성(R2, plan 에서 해소).
사용자 필수 제약 반영: 각 수정 단위(PR/phase)마다 코어 기능(md ingest·검색·
single-hop RAG+citation)의 도그푸딩 품질이 직전 baseline 과 반드시 비슷해야 하며
회귀 시 머지 금지. baseline 동결 → per-unit `kebab eval compare` + ingest 출력
byte-diff=0 게이트. 이 게이트가 PR 분할 경계와 완료 정의를 지배.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
기능 추가로 산만해진 표면·동작·내부를 강하게 단순화하는 설계. 단일 사용자·pre-1.0
이점으로 능력은 (거의) 유지하되 조작 표면·코드 구조를 절반 이하로.
핵심:
- Cuts: TUI, multi-turn 세션, legacy RAG v1/v2, search LRU 캐시, candle 임베더,
ingest API 5변종→1 (crate 24→22).
- 스파인 4 crate 재작성: config god-struct→타입 슬라이스, ingest 4193줄 모놀리스→
stage 파이프라인, chunk 중앙 selector, rag 2633줄→합성 stage(순수 query→Answer).
- 표면: config 109→~30, env 97→~25, search 20플래그→~6(--filter).
- 불변식: ingest 출력 byte-identical(재색인 없음) + wire 출력 계약 불변(MCP/스킬 무영향).
brainstorming 7개 결정 기록. frozen contract 부분 supersede.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
증분 skip 이 OCR/caption·청킹·코드 등 ingest 산출 영향 설정 변경을 못 잡아 재색인
안 되던 갭 일반화 수정. per-asset-type "ingest config signature" 를 effective
parser_version 에 폴딩 → 영향 설정 변경 시 영향 자산만 자동 재색인. patch(0.26.2).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
arctic 도그푸딩에서 OCR/caption 켜진 Obsidian 볼트가 중간부터 느려졌으나
TTY 진행바가 파일명·phase·모델·경과시간을 안 보여 "멈춤"처럼 보인 문제.
파일명 표시 + 느린 phase(OCR/caption/embed)+모델 실시간 + asset 경과 heartbeat
+ 종료 시 최장 소요 top-N 요약. additive wire(asset_phase, ocr_ms/caption_ms).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
연구문서(2026-06-03) 결론 따라 별칭 기능 제거 설계. 유지(Metadata.aliases,
AssetChunked/AssetTimings, embedding 캐시)/제거(Chunk.aliases, expansion.rs,
ExpansionCfg, chunk_aliases_fts, run_alias_query, ExpansionProgress) 경계 +
신규 DROP 마이그레이션 + wire expansion_progress 제거 결정. 구현 plan 8 task.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- commit track-spec + meta-spec/plan into branch (HIGH: dangling `amends:` ref)
- inline parity evidence (cosine 1.0, max_abs_diff 2.01e-7) into HOTFIXES +
release notes; drop refs to deleted IMPL_REPORT/SPIKE_REPORT (MEDIUM)
- model guard: reject non-e5-large `model` before the 2GB download so
model_id() can't mislabel vectors (MEDIUM) + unit test
- parity test now covers BOTH query: and passage: prefixes (MEDIUM)
- guard encodings.first() index; document zero-attention/pooling invariant;
clarify embed_batch prefixing doc (LOW)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
kickoff 인계(#197)의 brainstorm 결과를 확정한 spec. 트리거=명시 명령
`kebab config migrate`+doctor 안내, 주석 보존=toml_edit 부분 편집,
메커니즘=reconciliation(additive)+step 체인(non-additive) 하이브리드.
init/migrate 가 주석 달린 default 문서를 공유. 안전 3축(멱등·백업·dry-run)
+ atomic write. wire schema config_migration.v1 신설.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PR #195 구현(e9b5202) 기준으로 빠졌던 디테일 보강:
- chunk_id(위치 기반 벡터 식별자) vs cache_key(내용 해시 조회 키) 구분 callout
- §7 호환성/마이그레이션 신설: 본문 재색인 불필요, V012 가산이나 binary 교체 필요,
별칭 sentinel 묶음→개별 변경의 기존 KB 영향(레거시 호환)
- version_key 에 kind 토큰("doc|") 반영, orphan sentinel cleanup(LIKE prefix) 명시
- embed_with_cache 순서 보존 불변, 별칭 개별 벡터 근거(희석 13/18→16/18)
- 정정: derivation_cache_gc 는 메서드만 존재하고 미연결(캐시 현재 무한 누적, 후속)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
별칭을 줄별 개별 dense 벡터(sentinel `{chunk}#alias#N`)로 색인하고
boilerplate 청크는 별칭 생성을 skip. 묶음 1벡터 방식은 평균화로 특정
표현이 희석돼 오히려 회귀(13/18)했던 것을 폐기. 변형 일관성 14/18 →
16/18, mean_spread@10 0.222 → 0.111 (나무위키 ~1000 문서 CS corpus).
`kebab-core::strip_alias_suffix` 가 suffix 형과 per-alias 형 둘 다 처리.
파생물 캐시(V012): embedding 벡터 + 별칭 LLM 결과를 청크 내용 해시
키로 캐싱해 재색인 시 내용 불변 청크의 재계산을 skip. cache_key =
blake3(kind ‖ text_blake3 ‖ version_key)[:32], version_key 에
model/prompt/dimensions 포함 → §9 cascade 와 정합(버전 bump 시 자동
miss). 측정: 정답 3개 cold 1879s → warm 13s ≈ 145배. 순수 가산이라
corpus_revision bump 없음. search/ask 는 kebab.sqlite+lancedb 만으로
동작 → 외부 서버 색인 후 DB 만 복사하는 이식 워크플로 가능.
V012 schema migration + 신규 surface 로 workspace version 0.20.2 →
0.21.0 (minor) bump. README/HANDOFF/ARCHITECTURE/HOTFIXES sync.
known limitation: stack·svm 설명형 2개 잔존 + grounded 판정이 부분
인용을 grounded 로 오분류(후속 후보).
측정 상세: docs/superpowers/handoffs/2026-05-31-namu-wiki-alias-cache-study.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 6 리뷰 MINOR-1: spec 본문이 단일 UNION ALL+GROUP BY 로 기술됐으나
shipped = 2-query(run_query+run_alias_query) + Rust merge_body_alias(body 우선).
서로 다른 FTS 테이블 bm25 절대값 비교가 무의미해 body-우선 merge 가 더 깨끗.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
brainstorm 확정: 청크당 별칭 생성(같은언어+한↔영 번역), additive+수동
재색인, 1차 단순 품질제어. 별도 FTS5 aliases 채널 → RRF 3채널 융합.
flag off 기본, kebab eval variants 로 on/off 측정.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
목표 재정의: 한/영 overlap → 같은 의미의 다양한 표현(동의어·다른 어휘·풀어쓴
문장·한영)에서 일관된 답변 품질. 지난 reranker 실험이 overlap 프록시 최적화로
헛돈 교훈 반영 — 처방 전 진짜 지표(변형 일관성)를 직접 재는 평가부터.
Phase 1(본 spec 구현): kebab-eval golden suite에 변형 그룹(intent group) +
변형 일관성 메트릭(recall_spread, answer_consistency) + recall@pool vs recall@k로
(A)순위출렁/(B)어휘격차 자동 판별. Phase 2(처방)는 측정 결과 게이트 뒤 조건부.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Incorporates all critic (opus) round-1 findings into the dogfood
search-quality eval design spec:
BLOCKER-1: §4.4 execution commands now use --config /build/dogfood/config.toml
(Task A facade-rule patch makes this the canonical path). §5.1 re-titled
from "(후속 패치)" to "Task A로 적용됨 — 권장 운영 경로"; XDG workarounds
demoted to "패치 전 fallback". Intro paragraph updated accordingly.
MAJOR-1: §3 Non-Goals gains an explicit bullet: lang/media/code_lang
SearchFilters validation is out of scope for this harness (runner uses
SearchFilters::default(), runner.rs:151). §4.1 "code 검색" row no longer
claims code_lang filter coverage.
MINOR-1: §4.3 step 3 now names kebab inspect doc <id> as the primary
chunk-selection path (breaks chunk-level curation loop); search hits
demoted to "보조 확인용".
MINOR-2: §4.1 golden category table gains two new rows — 한국어 N-gram
fallback query (복합어/신조어 coverage) and 영어 whole-token exact query
(separates substring artefacts).
MINOR-3: §4.1 YAML header note added: record corpus_revision in golden
file so stale-bail root cause is immediately traceable.
NIT: §9 References line numbers corrected (runner.rs:31, metrics.rs:116/144);
runner.rs:151 SearchFilters::default() reference added.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
line 899: V1만 legacy → V1/V2 둘 다 legacy, v0.20.2 부터 rag-v3 default 선언.
line 1349 (★): config 예시 default rag-v2 → rag-v3.
line 1533 (★): §9 cascade table 코드 상수 rag-v2 → rag-v3.
line 287 이후: answer.v1 예시 블록에 historical snapshot 주석 추가 (n1 — model+ptv stale, 값 변경 안 함).
task spec grep 판단: tasks/p9/p9-fb-15 의 rag-v2 언급 2줄은 rag-v2 도입 시점 historical 기술 → frozen 유지.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.20.1 전체 도그푸딩에서 발견된 8 todo (Ask 응답언어 rag-v3 / doc.lang
docs / bulk input / list title / fusion_score·score_kind / schema
index_version / Ollama hint) 를 단일 patch release 로 설계.
writer worker 초안 → opus critic round-1 리뷰 반영:
- B1: top-level score placeholder → 확정 (score_kind 가 의미 선언, search.rs:95-99)
- M1: 이미지 caption 언어 강제 out-of-scope 명시
- M2: config default 테스트(lib.rs:1316) 갱신 필요 명시
- M3: bulk input 전체 필드 (query/mode/k/trust_min/ingested_after/media/tag/lang)
- M4: rag-v3 의 eval_runs.config_snapshot_json cascade 영향
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
V007 trigram tokenizer 의 한국어 2자 query 0-hit 한계 (Bug #8) 해소를
위한 V009 migration 추가. unicode61 tokenizer 로 환원 + 한국어 형태소
분해 결과를 별 column `tokenized_korean_text` 에 pre-fill 하는 방식.
- migrations/V009__fts_korean_morphological.sql 신규: column ADD,
chunks_fts DROP+재정의, 3 trigger CASE expression, backfill INSERT,
corpus_revision bump.
- design §5.5 갱신: trigram → unicode61 + 형태소 column. CASE
expression trigger 본문.
- crates/kebab-store-sqlite/tests/fts.rs: V007 verbatim test 를
V009 source-of-truth 로 rename. v009_bumps_corpus_revision unit
test 추가.
- store.rs: clippy bool_to_int_with_if + cast_lossless 기존 경고 수정
(pdf_ocr_events 관련 코드, S1 작업 중 발견).
영어 substring 매칭은 V002 (whole-token only) 로 회귀 — spec §3
Non-Goals + 후속 release notes (v0.20.1) 에서 정직히 기술.
Spec: docs/superpowers/specs/2026-05-28-v0.20.x-korean-morphological-tokenizer-spec.md
Plan: docs/superpowers/plans/2026-05-28-v0.20.x-korean-morphological-tokenizer-plan.md (S1)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3-round dogfood-driven fix cycle 의 산출물:
- bugfix1 (Bug #2/#3/#4): spec 964 line + plan 848 line
- bugfix2 (Bug #6/#7, #8 falsified): spec 308 line + plan 388 line
- bugfix3 (Bug #9/#10/#11/#13/#14, #12 falsified): spec 410 line + plan 1043 line
- docs/DOGFOOD.md: 전방위 dogfood checklist 의 전체 (§0 environment ~ §13 reference corpus)
각 round 의 spec/plan 가 critic + verifier round 2 closure ACCEPT 후 frozen. dogfood-driven evidence 기반.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug #11 (이전 commit `fix(config): pdf.ocr.request_timeout_secs default 600 → 60`)
의 frozen-spec deviation handoff.
- tasks/HOTFIXES.md: 2026-05-27 dated subsection — Discovered / Symptom / Root cause /
Fix / Amends 5-field 포맷 (기존 entries 와 일치).
- docs/superpowers/specs/2026-05-27-pdf-scanned-ocr-spec.md: PDF OCR config block
line 1000 (default value) + OQ-1 line 1628 에 inline HTML 주석 2 줄 cross-link.
prose 변경 0 — parent spec frozen contract 보존, HTML 주석은 markdown render 시 invisible.
HOTFIXES entry 가 live source of truth (CLAUDE.md "Spec contract" 규칙).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Step 1 (Group A) of v0.20.0 sub-item 1 (scanned PDF OCR) implementation plan.
A1 — spec §4.2 line 740 prose pseudo-code fix: `app.pdf_ocr_engine.as_ref()`
→ local `pdf_ocr_engine: Option<OllamaVisionOcr>` built in
`ingest_with_config_opts` (정합 with §4.4 eager init, App field 도입 0).
A2 — Cargo.toml dep invariant verified (image crate 미도입 — H-3 DCTDecode-only
v1 invariant 보존; kebab-parse-pdf + kebab-parse-image 가 kebab-app 의 기존
dep). description 갱신은 Step 3 (module 추가 후) 으로 이연.
A3 — cargo tree baseline 캡처 — K5 row #9/#10 의 ground-truth
(.omc/state/pdf-ocr-{app-parse,parse-pdf}-deps.baseline.txt). 본 sub-item
의 다른 step 의 dep graph 변경 0 invariant 의 verifier 의 baseline.
Note: .omc/ 는 .gitignore 대상 — baseline files 는 로컬 파일로 존재.
spec: docs/superpowers/specs/2026-05-27-pdf-scanned-ocr-spec.md
plan: docs/superpowers/plans/2026-05-27-pdf-scanned-ocr-plan.md (round 1c ACCEPT)
contract: §9 (additive minor wire bump — 후속 step)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v0.19.0 release 후 다음 session 인계용 handoff 문서 + 사후 backfill.
- docs/superpowers/handoffs/2026-05-26-v0.20-image-pdf-normalize-handoff.md (540 lines, 9 section)
- sub-item 1/2/3 머지 결과 + 도그푸딩 baseline (1781 doc / 9050 chunks) + user memory + OMC workflow + 빌드 환경
- 현재 구현 상태 (v0.19.0, image+pdf) — 정확한 file:line + struct/fn signature + flow
- 8 TODO 상세 (problem + scope + affected files + risk + trigger 조건)
- 우선순위 + sequencing 권장 + 새 session 첫 단계 제안
- docs/superpowers/specs/2026-05-26-extractor-dispatch-unification-spec.md (sub-item 3 spec)
- docs/superpowers/plans/2026-05-26-extractor-dispatch-unification-plan.md (sub-item 3 plan)
PR #187 머지 시 source code 만 들어가고 spec/plan 누락 — 동일 PR 의 reference link 가 main 에서 404. 본 commit 으로 backfill.
Assisted-by: Claude Code
v0.18.0 cut PR. fb-41 multi-hop RAG + NLI verification 의 user-visible surface (PR #176-180) + post-PR9 cleanup/refactor (PR #181) ship 마무리.
## 변경 사항
### Version
- workspace `Cargo.toml`: 0.17.2 → 0.18.0. Cargo.lock 자동 cascade (24 kebab-* crate 모두 0.18.0).
### Frozen design contract
- `docs/superpowers/specs/2026-04-27-kebab-final-form-design.md`:
- §3.8 RAG types — RefusalReason 에 NliVerificationFailed + NliModelUnavailable + MultiHopDecomposeFailed 추가 + Multi-hop RAG + NLI verification 의 ask_multi_hop facade + step 8.5 NLI hook + HopRecord / VerificationSummary 명시.
- §9 versioning rules 표 — nli_model_version row 신규 (선택 — v0.19+ second adapter 시 wire surface candidate).
### Status transitions
- `docs/superpowers/specs/2026-05-25-p9-fb-41-finalize-spec.md`: status approved-by-team → completed.
- `docs/superpowers/plans/2026-05-25-p9-fb-41-finalize-plan.md`: status approved-by-team → completed (spec_status 도).
### User-facing docs
- `README.md`: 명령 표의 `kebab ask` row 에 `--multi-hop` flag + NLI 옵션 안내 한 단락 (mDeBERTa-v3 XNLI 280 MB 자동 다운로드 / RAM peak ~7-8 GB / threshold tuning 0.5 prod / 0.0 disable).
- `docs/SMOKE.md`: `[rag] nli_threshold = 0.0` config 예시 + 활성화 절차 + first-run download + RAM 권장 inline 안내.
### Handoff + dashboard
- `HANDOFF.md`: 한 줄 요약 의 현재 version 0.17.2 → 0.18.0. v0.18.0 cut entry 추가 (fb-41 multi-hop + NLI + cleanup ship). Component 카운트 단락에 fb-41 PR-9 의 kebab-nli + ask_multi_hop 추가 명시. 머지 후 결정 절 맨 위에 v0.18.0 fb-41 entry 신규.
- `tasks/INDEX.md`: p9-fb-41 ⏳ → ✅ 머지 (v0.18.0). v0.18.0 subsection 신규 — PR #176-181 의 6 sub-PR + cleanup 각 한 줄 요약.
## 비범위 / 별 작업
- HOTFIXES.md 의 fb-41 entry 는 이미 PR #180 (PR-9d closure) 에서 작성 완료 — 본 cut PR 에서 추가 anchor 불필요.
- SKILL.md 의 v0.18+ NLI 안내는 이미 PR-9c-2 에서 inline 추가 완료.
## 검증
- `cargo check --workspace -j 1` 통과 (모든 24 crate v0.18.0 확인).
- frozen design 의 RefusalReason enum 확장이 kebab-core 의 production code 와 정합 (PR-9c-1 시점부터 동일 variants 있음).
Wire 영향: 없음 (additive minor 는 PR-9c-1 에서 이미 ship, 본 commit 은 documentation cascade only).
Behavior 영향: 없음.
머지 후 `gitea-release v0.18.0` 으로 tag + release notes 작성.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR-1 of fb-41 multi-hop RAG (spec: docs/superpowers/specs/2026-05-25-
p9-fb-41-multi-hop-rag-design.md, plan: docs/superpowers/plans/2026-
05-25-p9-fb-41-multi-hop-rag.md).
XL 작업의 첫 PR — baseline 측정 anchor 만 추가. RAG pipeline 미변경,
fixture file + parse 회귀 핀.
사용자 결정 4 axis (2026-05-25):
- approach: query decomposition (LLM 서브-질문)
- trigger: explicit `--multi-hop` flag
- MVP scope: dynamic N-hop (LLM 이 depth 결정, decompose seed +
ReAct-style decide loop hybrid)
- eval: multi-hop golden set 먼저 (본 PR)
본 PR:
- `fixtures/multi_hop_golden.yaml` 신규. 15 question (5 cross-doc +
5 intra-doc + 5 single-fact negative). 기존 `GoldenQuery` struct
그대로 사용 — 별 loader / type 변경 없음. `expected_chunk_ids`
비어 있어 curator 가 `kebab ingest` 후 채울 수 있는 template
형태. `must_contain` 으로 baseline 측정 가능 (P5-2 metric).
- `crates/kebab-eval/tests/loader.rs::loads_multi_hop_golden_fixture`
신규 회귀 핀. fixture parse OK + 15 question + 5/5/5 bucket
분포 + 모든 question 에 must_contain 최소 1 개.
baseline 측정 protocol (별 run, commit 에 artifact 안 포함):
1. v0.17.2 binary 로 single-pass `kebab eval run --fixture
multi_hop_golden.yaml` 실행
2. P@5, P@10, must_contain pass rate, citation_coverage 캡처
3. PR-3 (dynamic iter 머지) 후 동일 fixture + `multi_hop=true` 로
재실행 → Δ 비교
PR 분할 6 단계 (plan 참조): PR-1 (본 PR — fixture only), PR-2
(RagPipeline::ask_multi_hop fixed depth=2), PR-3 (dynamic iter),
PR-4 (CLI flag + wire), PR-5 (MCP + SKILL.md), PR-6 (TUI toggle +
trace render). 마지막 PR 후 v0.18.0 cut.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>