docs(release): v0.20.1 release notes draft + spec/plan dogfood cross-link
#1 (사용자 요청): release notes draft 작성 + spec/plan 의 dogfood evidence cross-link 보강. docs/release-notes/v0.20.1-draft.md (신규): - 4 단락 본문 (한국어 2자 query 지원 + 영어 substring 회귀 + V007→V009 자동 backfill + ingest 성능 영향). - Migration cascade table (lexical_index_version, corpus_revision, wire schema shape preservation). - API + dependency 변경 (lindera v3, lindera-ko-dic v3, retired short_query_hint helper, 새 facade APIs). - Breaking changes 명시 (영어 substring 회귀, 첫 부팅 latency, DB/ binary 크기 증가). - Upgrade 절차 + Known limitation + 14 dogfood scenario reference. spec Appendix B (segmentation evidence): - "Empirical verification (2026-05-28 dogfood — post-merge update)" subsection 신규. prior-knowledge 가정 vs 실측 결과 table. Scenario 1-4 모두 verified 표시. ko-dic 의 '서울특별시' → '[서울, 특별시]' 분해 증거 명시. plan Changelog: - post-implementation entry: 22 commit on branch, S3 blockers, S7 cascade, S11 sanity regression updates, opus PR review 4 finding fixes. - dogfood evidence entry: 14 scenario verify pass, ko-dic 분해 evidence, HOTFIXES + spec Appendix B cross-link. Spec: …spec…md Appendix B Plan: …plan…md (post-implementation + dogfood evidence Changelog) Release notes: docs/release-notes/v0.20.1-draft.md
This commit is contained in:
152
docs/release-notes/v0.20.1-draft.md
Normal file
152
docs/release-notes/v0.20.1-draft.md
Normal file
@@ -0,0 +1,152 @@
|
||||
---
|
||||
title: kebab v0.20.1 release notes (draft)
|
||||
created: 2026-05-28
|
||||
status: draft
|
||||
release_trigger:
|
||||
- 사용자 도그푸딩 필요 (Bug #8 — 한국어 2자 query 0-hit 해소)
|
||||
- frozen design contract 변경 (design §5.5 chunks_fts: trigram → unicode61 + 형태소 column)
|
||||
---
|
||||
|
||||
# kebab v0.20.1 — 한국어 형태소 검색 + 영어 substring 회귀
|
||||
|
||||
v0.20.0 (sub-item 1 scanned PDF OCR via qwen2.5vl:3b, 2026-05-27) 후속의 patch release. v0.20.x 라인의 두 누적 enhancement (logging round 2 + 한국어 morphological tokenizer) 를 하나로 묶어 cut.
|
||||
|
||||
## 핵심 변경
|
||||
|
||||
### 1. 한국어 2자 query 지원 — Bug #8 해소
|
||||
|
||||
이전 V007 (v0.17.0+) 의 trigram FTS5 tokenizer 는 3-character gram 최소 인덱싱이라 `'한국'` / `'서울'` / `'지하철'` 같은 1-3자 한국어 단어 query 가 종종 0-hit 였습니다. 사용자 도그푸딩 round 3/4 의 가장 큰 search experience surface.
|
||||
|
||||
**v0.20.1 의 해결책** — `chunks_fts` tokenizer 를 `unicode61` 로 환원 + lindera ko-dic 형태소 분석기로 한국어 chunk text 를 분해해 별 column `tokenized_korean_text` 에 pre-fill. FTS5 trigger 의 CASE expression 이 raw text 앞에 prepend 해 단일 query 로 두 column 모두 매칭.
|
||||
|
||||
```bash
|
||||
# v0.17.0 (V007 trigram): 0 hit
|
||||
# v0.20.1 (V009 morphological): hit (chunk 의 ko-dic 분해 결과에 '한국' morpheme 가 존재)
|
||||
kebab search '한국'
|
||||
kebab search '서울'
|
||||
```
|
||||
|
||||
**Dogfood evidence** (`tasks/HOTFIXES.md` 2026-05-28 entry + `docs/DOGFOOD.md` §2.1bis 의 reference fixture 14 scenario):
|
||||
|
||||
| Query | v0.17.0 (V007 trigram) | v0.20.1 (V009) |
|
||||
|---|---|---|
|
||||
| `'한국'` 2자 | 0 hit | hit |
|
||||
| `'서울'` 2자 | 0 hit | hit |
|
||||
| `'지하철'` 3자 | substring (제한적) | morpheme + raw token 모두 매칭 |
|
||||
| `'서울특별시'` compound | substring | ko-dic 분해 `[서울, 특별시]` |
|
||||
|
||||
**Known limitation**: ko-dic 가 compound noun (예: `한국정부`) 을 단일 token 으로 저장하는 경우 그 chunk 의 `'한국'` 단독 query 는 hit X. KB 가 영어/code 위주면 한국어 token 자체 부재로 lexical 0-hit 자연. N-gram supplement (sub-token 추가 emit) 는 v0.21.x P9 follow-up.
|
||||
|
||||
### 2. 영어 substring 매칭 회귀 (V002 동작 환원)
|
||||
|
||||
V007 trigram 의 ad-hoc 부산물이었던 영어 substring 매칭 (`'token'` query 가 `'tokenizer'` chunk 도 hit) 은 V009 의 unicode61 transition 으로 **사라집니다**. V002 (pre-v0.17.0) 의 whole-token 매칭으로 환원.
|
||||
|
||||
```bash
|
||||
# v0.17.0: 'token' query → 'tokenizer' chunk hit (substring recall ↑, 단어 경계 정밀도 ↓)
|
||||
# v0.20.1: 'token' query → 'token' 토큰만 hit, 'tokenizer' 는 다른 token
|
||||
kebab search --mode lexical 'token' # 다른 결과 가능
|
||||
kebab search --mode lexical 'tokenizer' # 정확한 token 매칭
|
||||
```
|
||||
|
||||
substring recall 이 필요한 시나리오는 **vector** 또는 **hybrid** mode 권장 (RRF 가 lexical + semantic 결합). spec §3 Non-Goals Path A 의 설계 결정 — lexical 의 단어 경계 정밀도 vs substring recall trade-off 에서 후자 포기.
|
||||
|
||||
### 3. V007 → V009 자동 backfill (재-ingest 불필요)
|
||||
|
||||
기존 V007 KB 를 v0.20.1 binary 로 첫 부팅할 때 `App::open_with_config` 의 first-boot hook 이 자동 실행:
|
||||
|
||||
1. V009 migration apply (schema 변경: `tokenized_korean_text TEXT` column ADD + chunks_fts re-create with unicode61 + CASE expression triggers).
|
||||
2. `backfill_tokenized_korean_text` API 호출 — chunks 의 NULL `tokenized_korean_text` row 를 lindera 로 분해 후 UPDATE. 1000-row batch transaction + progress callback.
|
||||
3. chunks_au trigger 가 chunks_fts 를 자동 재-index.
|
||||
|
||||
KB 크기 비례 latency:
|
||||
- 1만 chunk → ~30-60초 (lindera tokenize + UPDATE + trigger overhead).
|
||||
- 10만 chunk → ~5-10분.
|
||||
- stderr 의 `tracing::info!` progress log (`korean tokenizer backfill: 500/10000`) 발화. 사용자 hang 인지 방지.
|
||||
- 부분 완료 (Ctrl-C) 후 재실행 시 IS NULL filter 로 idempotent 이어 처리.
|
||||
|
||||
backfill 실패 시 (예: lindera dict load fail) `App::open_with_config` 은 success 반환 + warn log. vector/hybrid mode 정상 사용 가능.
|
||||
|
||||
### 4. Ingest 성능 영향
|
||||
|
||||
새 chunk ingest 시 chunker (`kebab-chunk::md_heading_v1`, `code_*_ast_v1`, `pdf_page_v1`, `tier2_shared`) 가 chunk emit 직전에 `tokenize_korean_morphological(text)` 호출. OnceLock 캐시로 dictionary load 가 process-lifetime 동안 1회 — amortized cost 낮음.
|
||||
|
||||
도그푸딩 측정 (1781 markdown / 9050 chunk):
|
||||
- v0.20.0 ingest baseline: ~700초.
|
||||
- v0.20.1 ingest: ~671초 (실측 — overhead 거의 무의미, chunk text 길이 비례 +5-10% 예상).
|
||||
|
||||
`kebab.sqlite` 파일 크기 영향:
|
||||
- `tokenized_korean_text` column 추가 (한국어 chunk 비례 +20-30% column data).
|
||||
- chunks_fts shadow 의 indexed text 가 raw + tokenized 합 (Korean-heavy KB 에서 ~2배 chunks_fts 크기).
|
||||
- 영어/code 위주 KB 에서는 `tokenized_korean_text` 가 NULL 또는 short → 영향 minimal.
|
||||
|
||||
binary 크기:
|
||||
- v0.20.0: ~270 MB (release).
|
||||
- v0.20.1: ~390 MB (lindera-ko-dic embedded dict 의 ~120 MB 추가).
|
||||
|
||||
## Migration cascade
|
||||
|
||||
| Version field | v0.20.0 | v0.20.1 |
|
||||
|---|---|---|
|
||||
| `lexical_index_version` | `lex:{chunker}` | `lex:{chunker}:fts5-v009-korean-morphological` |
|
||||
| `corpus_revision` | V004 seed = 0 | V009 migration tail 의 +1 (post-migration baseline = 1) |
|
||||
| `chunker_version` | unchanged | unchanged |
|
||||
| `parser_version` | unchanged | unchanged |
|
||||
| `embedding_version` | unchanged | unchanged |
|
||||
| Wire schema (`search_response.v1`, `answer.v1`) | shape unchanged | shape unchanged (hit ordering / snippet content 만 변화) |
|
||||
|
||||
eval runner 의 `config_snapshot_json` 가 자동으로 `lexical_index_version` 의 V009 suffix 를 picks up — `crates/kebab-eval/tests/fixtures/eval/run-1.json` 의 V009 baseline 으로 regenerate 됨.
|
||||
|
||||
## API + dependency 변경
|
||||
|
||||
신규 workspace dependencies:
|
||||
- `lindera = "3"` (MIT/Apache-2.0)
|
||||
- `lindera-ko-dic = "3"` (Apache-2.0, MeCab-ko-dic upstream)
|
||||
|
||||
신규 facade-level API (`kebab-app` 의 `*_with_config` 패턴 따라):
|
||||
- `kebab_chunk::tokenize_korean_morphological(text: &str) -> Option<String>`
|
||||
- `kebab_store_sqlite::SqliteStore::backfill_tokenized_korean_text<F, T>(progress, tokenize) -> Result<u64>`
|
||||
|
||||
retired:
|
||||
- `kebab_app::short_query_hint()` helper — V007 시절 advisory. V009 의 2-char Korean query hit 으로 obsolete. `SearchResponse.hint` struct field + wire schema `hint` field 는 forward-compat 차원에서 보존 (항상 None).
|
||||
|
||||
## Logging round 2 (v0.20.x sub-item 1 후속, PR #190 머지)
|
||||
|
||||
- PDF OCR raster image dimension capture (PR #189 의 round 1 null 결함 fix).
|
||||
- V008 SQLite mirror — historical OCR query table.
|
||||
- CLI `kebab inspect ocr-stats` + `kebab inspect ocr-failures`.
|
||||
- Log retention policy — `keep_recent_runs` + `retention_days`.
|
||||
|
||||
자세한 내용 = HANDOFF.md "v0.20.x ingest log r2" entry + spec `docs/superpowers/specs/2026-05-28-v0.20.x-logging-r2-spec.md`.
|
||||
|
||||
## Breaking changes / 사용자 영향
|
||||
|
||||
- **영어 lexical 의 substring 매칭이 사라짐** — 사용자가 `'token'` query 로 `'tokenizer'` 도 찾는 패턴 의존 시 vector/hybrid mode 로 mode-switch 권장. release notes 만 보고 mode 변경 안 한 사용자는 자기 KB 의 검색 결과 변화 인지.
|
||||
- **첫 부팅 latency** — 큰 KB 의 사용자는 v0.20.1 binary 의 첫 호출이 30초~10분 hang. stderr progress log 확인.
|
||||
- **DB / binary 크기 증가** — `kebab.sqlite` +20-30% (Korean-heavy), binary +120MB (lindera ko-dic embedded). `/build/cache/tmp` 또는 별 디스크에 KB 둔 사용자는 영향 minimal.
|
||||
|
||||
## Upgrade 절차
|
||||
|
||||
```bash
|
||||
# 1. binary 교체 (release tag v0.20.1)
|
||||
git fetch && git checkout v0.20.1 && cargo build --release -p kebab-cli
|
||||
|
||||
# 2. 첫 호출 — V009 migration + eager backfill 자동
|
||||
kebab search '한국' # stderr 의 backfill progress log 확인
|
||||
|
||||
# 3. 새 검색 패턴 확인
|
||||
kebab search '서울' # 2자 query hit
|
||||
kebab search 'tokenizer' # whole-token (substring recall 회귀)
|
||||
```
|
||||
|
||||
회귀 발견 시 `tasks/HOTFIXES.md` 또는 GitHub issue 보고.
|
||||
|
||||
## References
|
||||
|
||||
- Spec: [`docs/superpowers/specs/2026-05-28-v0.20.x-korean-morphological-tokenizer-spec.md`](../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`](../superpowers/plans/2026-05-28-v0.20.x-korean-morphological-tokenizer-plan.md)
|
||||
- HOTFIXES entry: [`tasks/HOTFIXES.md`](../../tasks/HOTFIXES.md) 2026-05-28
|
||||
- DOGFOOD scenarios: [`docs/DOGFOOD.md`](../DOGFOOD.md) §2.1 + §2.1bis
|
||||
- Design contract: [`docs/superpowers/specs/2026-04-27-kebab-final-form-design.md`](../superpowers/specs/2026-04-27-kebab-final-form-design.md) §5.5 + §9
|
||||
- V009 migration: [`migrations/V009__fts_korean_morphological.sql`](../../migrations/V009__fts_korean_morphological.sql)
|
||||
- lindera: https://github.com/lindera-morphology/lindera (MIT/Apache-2.0)
|
||||
- lindera-ko-dic: https://github.com/lindera-morphology/lindera-dictionary (Apache-2.0)
|
||||
@@ -740,6 +740,8 @@ commit_count: 10
|
||||
|
||||
## Changelog
|
||||
- 2026-05-28 closure-r1-mp: plan closure verifier (sonnet) ACCEPT verdict + 9 micro-patches 적용 (MP-1: feature flag, MP-2~MP-7: AC actionability, MP-8: parallel safety, MP-9: hybrid/english regression checks).
|
||||
- 2026-05-28 post-implementation: 11 step + 10 follow-up commit 모두 머지 — total 17 implementation commit + 4 docs polish + 1 spec/plan archive = 22 commit on `feat/korean-morphological-tokenizer`. S3 spec compliance reviewer 가 2 blocker (get_chunk read path + 9 AST chunker cascade) 발견 → fix commit. S7 implementer 가 `MIT_QUERY_CHARS` 3→2 cascade 필수 발견 (정당한 scope expansion). S11 sanity 단계에서 V007 trigram-specific test 3 + corpus_revision test baseline + chunk snapshot 10 의 의도된 회귀 update. PR-level final review (opus) 의 4 minor docs finding (README english regression, HOTFIXES paths, hint schema, SKILL.md) 정정.
|
||||
- 2026-05-28 dogfood evidence: reference fixture (korea-overview.md + korea-compound.md, DOGFOOD.md §2.1bis) 의 14 scenario verify pass — '한국' 4 hit, '서울' 2 hit, '지하철' 2 hit, '서울특별시' 1 hit (ko-dic 분해 `[서울, 특별시]` 증거). spec Appendix B 의 prior-knowledge 가정이 실측에서 일치. HOTFIXES 2026-05-28 entry 의 dogfood verification 표 + spec Appendix B 의 Empirical verification subsection 으로 cross-link.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -583,6 +583,30 @@ lindera-dict-ko-dic 은 MeCab-ko-dic 기반이며, 다음과 같은 segmentation
|
||||
|
||||
본 spec 의 AC §9.1 과 lindera ko-dic 의 실제 동작이 **일반적으로 일치**하나, 고유명사 / 복합명사 정책에 따라 variation 가능. Implementation 단계에서 dogfood corpus 에 대한 실측 검증 필수.
|
||||
|
||||
### Empirical verification (2026-05-28 dogfood — post-merge update)
|
||||
|
||||
V009 implementation 머지 직전 reference corpus (korea-overview.md + korea-compound.md, DOGFOOD.md §2.1bis) 로 실측 verify:
|
||||
|
||||
| Fixture chunk (실제 corpus) | Query | Hit count | ko-dic 분해 evidence (snippet) |
|
||||
|---|---|---|---|
|
||||
| "한국 은 동아시아 의 반도 국가다" | `'한국'` 2자 | 4 | "한국 은 동아시아 의 반 도 국가 다" — `[한국, 은, 동아시아, 의, 반도, 국가, 다]` |
|
||||
| "서울 의 지하철 시스템" | `'서울'` 2자 | 2 | "서울 의 지하철 은 1974 년 1 호 선 개통 후" — `[서울, 의, 지하철, 은, ...]` |
|
||||
| "지하철 은 시민 의 가장 중요한 교통수단" | `'지하철'` 3자 | 2 | 단일 morpheme `지하철` 매칭 |
|
||||
| "한국어 학습 자료" | `'한국어'` 3자 | 1 | ko-dic compound noun `한국어` 단일 token |
|
||||
| "한국문화 의 핵심 은 정" | `'한국문화'` compound | 1 | 단일 token (compound) |
|
||||
| "서울특별시 와 부산광역시 는 한국 의 양대 도시" | `'서울특별시'` compound | 1 | ko-dic 가 `서울특별시` → `[서울, 특별시]` 분해 → `'서울'` 단독 query 도 hit |
|
||||
| `'키'` 1자 | — | 0 | `build_match_string` 의 `MIN_QUERY_CHARS = 2` filter |
|
||||
|
||||
**검증된 핵심 동작**:
|
||||
- ✅ Scenario 1 (`'한국'` query → `'한국어'` chunk hit): explicit 공백 분리된 corpus 에서는 보장 hit. compound noun (`한국어`, `한국문화`) 가 단일 token 일 때는 hit X.
|
||||
- ✅ Scenario 2 (`'서울'` query → `'서울특별시'` chunk hit): **실측에서 hit 확인** — ko-dic 가 `서울특별시` 를 compound 으로 등록 안 하고 `[서울, 특별시]` 로 분해함. spec 의 "Option α (고유명사 미등록 시 hit)" 결정과 정확히 부합.
|
||||
- ✅ Scenario 3 (영어 + 3자 이상 한국어): 보장 hit.
|
||||
- ✅ Scenario 4 (영어 query 'pipeline'): whole-token 매칭으로 회귀 (V002 동작 = spec §3 Non-Goals Path A).
|
||||
|
||||
**Known gap** (사용자 KnowledgeBase 같은 영어/code 위주 KB):
|
||||
- 한국어 token 자체 부재 → lexical 0-hit 자연 (vector/hybrid mode 로 우회).
|
||||
- 실측 사례: 1781 markdown / 9050 chunk 의 React/Cargo docs 위주 KB 에서 `'한국'` / `'서울'` 모두 0-hit (해당 단어가 corpus 에 없음 — V007 vs V009 의 차이 아님). corpus 가 한국어 content 를 가져야 V009 의 benefit 발현.
|
||||
|
||||
---
|
||||
|
||||
## Appendix C: Storage / binary / ingest cost evidence
|
||||
|
||||
Reference in New Issue
Block a user