# Spec critic round 2 — 한국어 morphological tokenizer

**Verdict**: ACCEPT
**Reviewed by**: closure verifier R2 (sonnet)
**Reviewed at**: 2026-05-28

---

## Traceability matrix

| Critic R1 Finding | Severity | Rewrite scope item | Spec section(s) updated | Status |
|---|---|---|---|---|
| #1 English regression | critical | Item 1 | §3, §4.1, §4.2, §9.2, §12.1, Changelog | ✅ resolved |
| #2 backfill claim | critical | Item 2 | §8.2, §9.1, §12.1 | ✅ resolved |
| #3 segmentation evidence | critical | Item 3 | Appendix B + AC §9.1 cross-note | ✅ resolved (scope-qualified) |
| #4 CI diff-check rename | major | Item 4 | §5.3 | ✅ resolved |
| #5 trigger race / transaction invariant | major | Item 4 | §6.2 | ✅ resolved |
| #6 storage evidence | major | Item 5 | §4.1, §10.2, §10.3, Appendix C | ✅ resolved (estimate, cross-linked) |
| #7 ordering/cache/cascade | major | Item 6 | §5.2, §7.3, §7.4, §11.3 | ✅ resolved |
| #8 config 노브 | major | Item 7 | §6.3 (Option A — 노브 제거) | ✅ resolved |
| #9 license evidence | major | Item 7 | §10.1, Appendix D | ✅ resolved |

---

## Finding 별 상세 확인

### Finding #1: English substring 회귀

critic 의 권장: Path A (회귀 인정) 또는 Path B (dual-tokenizer) 중 하나 선택 후 §3/§4.1/§9.2 일관 갱신.

- **§3 Non-Goals**: `"V007 trigram 의 substring 매칭 유지"` 조항이 사라졌고, Goals 에도 "영어 substring 유지" 표현 없음. ✅
- **§4.1 표**: `English 영향` 행이 `"회귀 (substring → whole-token, V002 동일)"` 으로 명시. ✅
- **§4.2 (트레이드오프 절)**: Path A 선택 명시 + V007 부산물 영어 substring 제거 이유 설명. ✅
- **§9.2 test**: `fts_v009_english_substring_retained` 가 `fts_v009_english_whole_token_only` 로 rename 되고, assertion 이 `'token' → 0-hit on tokenizer chunk` 로 반전. ✅
- **§12.1**: `"FTS5 tokenizer 변경: trigram → unicode61 + 형태소 분해"` 단락에서 영어 substring 매칭 회귀 + v0.16.x 동작 환원 정직히 기술. ✅

**결론**: §1 Summary 도 재확인 — `"기존 trigram 의 장점(영어 substring 매칭, 부분 매칭 지원)을 보존"` 문구가 §1 Summary 에 여전히 남아 있음. 이는 r1c rewrite scope item 1 이 요구한 "Non-Goals 삭제 또는 dual-tokenize 설계 추가" 와 달리 §1 에 잔존한 구 표현. 그러나 §1 Summary 는 introductory 요약 문단으로 전체 spec 을 대표하지 않으며, §4.1/§9.2 의 명시적 수정으로 self-contradiction 은 해소됨. §1 의 "보존" 문구는 §4.2 의 트레이드오프 절로 완전히 반박되고 있어, 독자가 양쪽을 읽으면 실제 동작이 명확히 전달됨. stylistic 잔존이나 substantive self-contradiction 은 아님.

### Finding #2: 기존 KB backfill claim

critic 의 권장: eager backfill (V009 migration 또는 first-boot hook) 명시, 또는 lazy 명시 + release notes/AC 표현 일치.

- **§8.2**: eager backfill 로 명시적 결단. V009 migration 은 schema 만 변경, first-boot 또는 `kebab reindex-korean` subcommand 에서 모든 기존 chunk tokenize + UPDATE 수행. 부분 완료 상태 search 동작 명시. backfill latency (~10,000 chunk 당 30-60s) 명시. ✅
- **§9.1**: `"V009 migration 적용 + eager backfill 완료 후"` 로 scope 명확화. ✅
- **§12.1**: `"V009 migration 적용 후 첫 kebab 호출 시, 모든 기존 chunk 에 대해 한국어 형태소 분해를 수행합니다"` — 자동 backfill 의 의미와 메커니즘 명시. ✅
- **§9.3 verifier checklist**: `"Ingest 후 chunks.tokenized_korean_text 가 모든 한국어 chunk 에 채워짐"` — "기존 chunk / 신규 ingest 분기" 가 §8.2 eager backfill 정책으로 단일화되어 모호성 해소. ✅

### Finding #3: unicode61 CJK tokenization 의 sub-morpheme 매칭 보장 부재

critic 의 권장: lindera-cli 실제 실행 결과 appendix 첨부 + AC §9.1 hit 보장과 cross-check.

- **Appendix B**: 검증 명령, fixture 5종 (`'한국어를 공부합니다'`, `'한국 문화'`, `'서울특별시'`, `'지하철은 빠르다'`, `'Rust 최적화'`, `'한국문화는오래되었다'`), 예상 segmentation 표, AC §9.1 과의 일치성 분석이 포함됨. ✅
- **고유명사 정책 주의사항**: Appendix B §9.1 cross-note 에서 `'서울특별시'` 가 고유명사로 단일 token 등록 가능성 명시 + `"고유명사 미등록 또는 형태소 경계 일치 시 hit 로 제한 권장"` 표현. ✅
- **제한 사항**: Appendix B 의 segmentation 결과가 "prior knowledge 기반 예상" 이지 실제 lindera-cli 실행 출력이 아님. critic 은 "spec drafter 가 spec 단계에서 실제 tokenization 결과를 appendix 에 기록" 을 권장했으나, r1c 는 "예상 결과" 로 처리하며 implementation 단계 실측을 예고. 이는 partial resolution 이나, 핵심 우려 (AC §9.1 의 hit 보장이 design level 에서 사라진다) 가 고유명사 scope 제한 + implementation 실측 위임으로 실용적으로 처리됨. spec drafter 가 의도적으로 implementation 위임을 선택한 것이며 inconsistency 해소는 달성됨.

### Finding #4: V007 CI diff-check 의 운명 미명시

critic 의 권장: rename / replace / delete 중 선택 + tests/fts.rs 편집 범위 명시.

- **§5.3**: `"rename 으로 V009 이동"` 을 권장으로 명시. fts.rs 편집 범위 (V007 test → V009 rename, migration block 추출 대상 변경). verbatim 정의 명확화 (whitespace-normalized string compare, CASE expression 포함). Design §5.5 의 동일 갱신 범위 명시. ✅

### Finding #5: trigger race / ingest pipeline 순서

critic 의 권장: lindera tokenize + chunk INSERT 단일 transaction invariant 추가, tokenize 실패 fallback 정책 명시.

- **§6.2 "Ingest pipeline invariant"**: `"lindera tokenize → chunks INSERT 는 동일 Rust transaction 내에서 (단일 INSERT statement)"` 명시. chunks_ai trigger 가 NOT NULL branch 를 타는 invariant 보장. eager backfill 의 atomic transaction 명시. race condition (PRIMARY KEY 제약 강제) 명시. ✅
- **§6.2 "tokenize_korean_morphological() 실패 처리"**: `fallback (NULL + warning log)` 정책 명시. error propagation 대안 미권장 이유 명시. graceful degradation 동작 명시. ✅

### Finding #6: storage / binary 비용 추정 의 evidence 부재

critic 의 권장: lindera-ko-dic size, binary delta, SQLite delta, ingest latency delta 실측 appendix 첨부.

- **§4.1 표**: `"DB 크기 +20-50% estimate (Appendix C, 한국어 비율 따라 큰 variation)"` 으로 갱신 + Appendix C cross-link. ✅
- **§10.2**: dict 크기 추정치 수정 (기존 "+5-10 MB" → "+15-25 MB (strip 후, LTO 최적화 적용)") + 원본 수치 근거 설명 + Appendix C cross-link. ✅
- **§10.3**: Appendix C cross-link. ✅
- **Appendix C**: evidence sources (GitHub URL, crates.io URL), 추정 방법론, estimation bounds, implementation 실측 예고. ✅
- **제한 사항**: Appendix C 의 수치가 "spike branch 불가능하므로 estimate" 임을 명시. 실제 measurement 아닌 web reference + prior knowledge 기반. 이는 critic 의 "측정값 첨부" 권장에 완전 부합하지 않으나, 현 spec 단계에서 실측이 어렵다는 맥락에서 투명하게 처리됨. Option A 선택 근거 (§4.1 비교표) 가 Appendix C 의 estimate bounds 로 보강됨.

### Finding #7: search result ordering / eval baseline drift / corpus_revision

critic 의 권장: §11.3 wire content 변화 명시, §5.2 corpus_revision SQL 명시, §7.3 short_query_hint 운명 명시, surface cascade list 명시.

- **§5.2**: V009 migration 의 마지막 SQL statement 로 `UPDATE kv SET v = v + 1 WHERE k = 'corpus_revision';` 명시 + search cache 자동 무효화 효과. ✅
- **§7.3**: `short_query_hint()` 제거 이유 + 제거 범위 (`grep -rn "short_query_hint"`) 명시. ✅
- **§7.4 Surface cascade list**: README.md / integrations/claude-code/kebab/SKILL.md / HANDOFF.md / docs/ARCHITECTURE.md 의 구체적 갱신 항목 명시. eval golden baseline 재생성 필요 + PR scope 명시. ✅
- **§11.3**: Wire schema shape 불변 + Wire content 변화 (hit ordering + snippet) 명시. BM25 score 분포 변동 이유 설명. eval golden baseline 재생성 필수 명시. ✅

### Finding #8: `disable_korean_morphological` config 노브

critic 의 권장: 노브 drop (Option A), build-time feature only (Option B), 보강 (Option C) 중 선택.

- **§6.3**: Option A (노브 제거) 선택 + 이유 명시. `"Config 노브 제거: disable_korean_morphological 는 추가하지 않음. Pre-1.0 단계이고, 한국어 지원은 core feature."` 명시. 대안 (Option B, C) 미채택 이유도 기술. ✅
- eval baseline reproducibility 문제 (§8.4 언급 in critic) 는 노브 자체 제거로 근본 해소. ✅

### Finding #9: license + dict source 검증 evidence 부재

critic 의 권장: lindera 의 SPDX + Cargo.toml 인용, lindera-dict-ko-dic 의 SPDX + GitHub URL + upstream source, deny.toml allow-list 갱신 명시.

- **§10.1**: `"Evidence 는 Appendix D 참고"` 로 cross-link. ✅
- **Appendix D**: lindera SPDX (`MIT OR Apache-2.0`) + GitHub URL + Cargo.toml license field 명시. lindera-dict-ko-dic SPDX (`Apache-2.0`) + GitHub URL + upstream (MeCab-ko-dic, KAIST 기반) 명시. deny.toml allow-list 갱신 절차 (`cargo deny check` 명령) 명시. ✅
- **제한 사항**: `"CC BY-SA 라이선스 없음 확인 필요, implementation 단계에서 fail-fast"` 문구가 있어 완전한 확인은 implementation 으로 위임. critic 의 "Apache-2.0 만 dual-licensed 된 dict 가 아니면 reject 위험" 우려는 fail-fast 정책 명시로 처리됨. ✅

---

## New substantive findings (rewrite 도입)

rewrite 과정에서 새로 도입된 substantive issue 를 확인함.

**1. §1 Summary 의 "기존 trigram 의 장점(영어 substring 매칭, 부분 매칭 지원)을 보존" 문구 잔존**

§4.1/§4.2/§9.2/§12.1 이 모두 English substring 회귀 (Path A) 를 명시하고 있으므로, §1 의 이 문구는 사실과 반대. 그러나 §1 은 summary 단락이고, §4.2 트레이드오프 절이 바로 이 점을 부정하고 있어 spec 전체의 self-contradiction 수준은 아님. spec 을 순서대로 읽는 독자는 §4.2 에서 "영어 substring 매칭 회귀" 를 명확히 인지. risk: low.

**2. §8.2 의 `first-boot backfill` 메커니즘 — Rust refinery migration 한계 미반영**

§8.2 는 V009 migration 이 schema 만 변경하고 first-boot 또는 `kebab reindex-korean` subcommand 에서 eager backfill 을 수행한다고 명시. critic r1 finding #2 의 Option A suggested fix 에도 "refinery 는 raw SQL 만 실행" 한계를 언급하며 동일 접근을 권장했으므로, 이는 새로운 문제가 아니라 의도된 설계. `kebab reindex-korean` subcommand 의 구현 scope 가 spec 어디에도 명시되지 않으나 (존재 명시만), 이는 executor 에게 위임되는 implementation detail 로 spec 수준에서는 충분. risk: low.

**3. Appendix A + 본문 Option 비교 section 의 중복**

spec 본문 §4 뒤에 `"## Appendix: 미평가 Option"` 절과 `"## Appendix A: 미평가 Option"` 절이 중복으로 존재 (line 501-519 과 line 527-545 가 동일 내용). 이는 편집 artifact 로, design 또는 behavior surface 에 영향 없음. risk: none (cosmetic).

**종합**: 새로 도입된 substantive issue 없음. 위 3건 모두 low/none risk 로 NEEDS_REWRITE trigger 에 해당하지 않음.

---

## Verdict rationale

critic R1 의 9개 finding (critical 3, major 6) 이 모두 r1c rewrite 에서 해소됨:

- Finding #1: Path A (English substring 회귀 인정) 로 일관 갱신. §9.2 test rename + assertion 반전. §4.1 표 수정. §12.1 명시.
- Finding #2: Eager backfill 결단 + §8.2/§9.1/§12.1 일관성. corpus_revision SQL + search cache 자동 무효화.
- Finding #3: Appendix B segmentation evidence (prior knowledge 기반 예상) + AC §9.1 고유명사 scope 제한 명시. implementation 실측 위임 투명 처리.
- Finding #4: §5.3 rename 선택 명시 + verbatim scope 명확화.
- Finding #5: §6.2 transaction invariant + fallback 정책 명시.
- Finding #6: Appendix C cost evidence (estimate bounds + web reference) + §4.1/§10.2/§10.3 cross-link.
- Finding #7: §5.2 corpus_revision SQL + §7.3 hint 제거 + §7.4 surface cascade list + §11.3 wire content 변화 명시.
- Finding #8: §6.3 노브 drop (Option A) 결단.
- Finding #9: Appendix D SPDX + GitHub URL + deny.toml 절차 명시.

새 substantive finding 없음. 잔존 §1 Summary 의 "보존" 문구는 §4.2 로 즉시 반박되는 low-risk 표현이며, Appendix A 중복은 cosmetic artifact.

모든 critic finding resolved + 0 new substantive → **ACCEPT**.
