docs(config): v3 재편 surface 동기화 + minor version bump 0.27.0→0.28.0

README Configuration([ingest.*] 레이아웃 + migrate 안내), SMOKE config 예시,
HOTFIXES dated entry(rename 매핑 + 3 불변식), 선행 마이그레이션 spec 교차링크.
인터페이스 변경(config 레이아웃 rename + env 추가) = minor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 13:03:07 +00:00
parent 15e6918cef
commit 90812e981f
6 changed files with 66 additions and 35 deletions

48
Cargo.lock generated
View File

@@ -4751,7 +4751,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-app" name = "kebab-app"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64 0.22.1", "base64 0.22.1",
@@ -4799,7 +4799,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-chunk" name = "kebab-chunk"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"blake3", "blake3",
@@ -4817,7 +4817,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-cli" name = "kebab-cli"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@@ -4838,7 +4838,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-config" name = "kebab-config"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"dirs 5.0.1", "dirs 5.0.1",
@@ -4854,7 +4854,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-core" name = "kebab-core"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"blake3", "blake3",
@@ -4868,7 +4868,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-embed" name = "kebab-embed"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"blake3", "blake3",
@@ -4882,7 +4882,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-embed-candle" name = "kebab-embed-candle"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"candle-core", "candle-core",
@@ -4902,7 +4902,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-embed-local" name = "kebab-embed-local"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"fastembed", "fastembed",
@@ -4915,7 +4915,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-embed-ollama" name = "kebab-embed-ollama"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"kebab-config", "kebab-config",
@@ -4930,7 +4930,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-eval" name = "kebab-eval"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"kebab-app", "kebab-app",
@@ -4949,7 +4949,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-llm" name = "kebab-llm"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"kebab-core", "kebab-core",
@@ -4958,7 +4958,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-llm-local" name = "kebab-llm-local"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"kebab-config", "kebab-config",
@@ -4975,7 +4975,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-mcp" name = "kebab-mcp"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"kebab-app", "kebab-app",
@@ -4993,7 +4993,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-nli" name = "kebab-nli"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"hf-hub", "hf-hub",
@@ -5008,7 +5008,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-parse-code" name = "kebab-parse-code"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"gix", "gix",
@@ -5031,7 +5031,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-parse-image" name = "kebab-parse-image"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"ab_glyph", "ab_glyph",
"anyhow", "anyhow",
@@ -5059,7 +5059,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-parse-md" name = "kebab-parse-md"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"kebab-core", "kebab-core",
@@ -5076,7 +5076,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-parse-pdf" name = "kebab-parse-pdf"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"blake3", "blake3",
@@ -5091,7 +5091,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-rag" name = "kebab-rag"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"blake3", "blake3",
@@ -5113,7 +5113,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-search" name = "kebab-search"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"globset", "globset",
@@ -5132,7 +5132,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-source-fs" name = "kebab-source-fs"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"blake3", "blake3",
@@ -5150,7 +5150,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-store-sqlite" name = "kebab-store-sqlite"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"blake3", "blake3",
@@ -5170,7 +5170,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-store-vector" name = "kebab-store-vector"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arrow", "arrow",
@@ -5194,7 +5194,7 @@ dependencies = [
[[package]] [[package]]
name = "kebab-tui" name = "kebab-tui"
version = "0.27.0" version = "0.28.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"crossterm", "crossterm",

View File

@@ -32,7 +32,7 @@ edition = "2024"
rust-version = "1.85" rust-version = "1.85"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
repository = "https://github.com/altair823/kebab" repository = "https://github.com/altair823/kebab"
version = "0.27.0" # v0.27.0 — PP-OCRv5 ONNX Rust 네이티브 OCR 엔진: `[image.ocr] engine = "paddle-onnx"` (default 여전히 "ollama-vision") 로 in-process 검출+인식(`ort` =2.0.0-rc.9, Python 런타임 0). DBNet det + CTC rec, 후처리(min-area rect/unclip)는 pure-Rust. e2e CER 0.005(synthetic 한/영, PoC 0.024 대비 우수), 큰 페이지 CPU <4초(Ollama vision ~50초 대비). 신규 config `det_model`/`rec_model`/`dict`/`score_thresh`/`unclip_ratio`/`max_boxes` + `KEBAB_IMAGE_OCR_*` env. ingest 서명 `|ocr:1:{engine}:{engine_version}` 로 engine/모델 변경 시 자동 재색인. 신규 인터페이스(engine 값/config 키) → minor. — CLAUDE.md §Release version = "0.28.0" # v0.28.0 — config 스키마 v2→v3 재편: 미디어 형식 설정을 `[ingest.*]` 우산으로 통합(`[indexing]`→`[ingest]` 스칼라, `[chunking]`/`[image.ocr]`/`[image.caption]`/`[pdf.ocr]`→`[ingest.*]`). 기존 v2 파일은 load 시 메모리 자동 변환(디스크 미변경), 파일 갱신은 `kebab config migrate`(값·주석 보존). env 이름(LHS) 100% 보존 + RHS 만 새 경로, 신규 `KEBAB_PDF_OCR_{DET_MODEL,REC_MODEL,DICT,SCORE_THRESH,UNCLIP_RATIO,MAX_BOXES}`. `ingest_config_signature` 바이트 불변(재색인 0). PdfOcrCfg paddle 대칭 키. 신규 인터페이스(config 레이아웃 rename + env 추가) → minor. — CLAUDE.md §Release
# pre-v0.18 workspace-wide cleanup: enable clippy::pedantic group with # pre-v0.18 workspace-wide cleanup: enable clippy::pedantic group with
# intentional allow-list. The allowed lints are either cosmetic (doc style), # intentional allow-list. The allowed lints are either cosmetic (doc style),

View File

@@ -68,7 +68,7 @@ rsync -a <src-data_dir>/lancedb/ user@server:<dst-data_dir>/lancedb/
### 멀티미디어 색인 ### 멀티미디어 색인
Markdown · PDF · 이미지(OCR + caption) · 소스코드(Rust/Python/TS/JS/Go/Java/Kotlin/C/C++ AST) · 리소스(YAML/Dockerfile/TOML/JSON/XML 등)를 확장자에 따라 자동으로 적절한 chunker 에 라우팅한다. embedded text 가 없는 scanned PDF 는 `[pdf.ocr]` 로 page-단위 OCR (opt-in). 전체 확장자→chunker 매핑은 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md). Markdown · PDF · 이미지(OCR + caption) · 소스코드(Rust/Python/TS/JS/Go/Java/Kotlin/C/C++ AST) · 리소스(YAML/Dockerfile/TOML/JSON/XML 등)를 확장자에 따라 자동으로 적절한 chunker 에 라우팅한다. embedded text 가 없는 scanned PDF 는 `[ingest.pdf.ocr]` 로 page-단위 OCR (opt-in). 전체 확장자→chunker 매핑은 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
### RAG (근거 인용 + 거절) ### RAG (근거 인용 + 거절)
@@ -182,10 +182,11 @@ prompt_template_version = "rag-v3" # 답변 언어 = 질문 언어. rag-v1/v2
nli_threshold = 0.0 # >0 (예: 0.5) 면 mDeBERTa XNLI groundedness 검증. nli_threshold = 0.0 # >0 (예: 0.5) 면 mDeBERTa XNLI groundedness 검증.
``` ```
- **`[ingest]`** (v0.28.0) — 모든 형식 ingest 설정의 우산. 병렬도(`max_parallel_extractors`/`max_parallel_embeddings`/`watch_filesystem`, ← 옛 `[indexing]`)와 형식별 하위 절(`[ingest.chunking]` ← 옛 `[chunking]`, `[ingest.code]`, `[ingest.image.ocr]` ← 옛 `[image.ocr]`, `[ingest.pdf.ocr]` ← 옛 `[pdf.ocr]`)이 전부 이 아래로 모인다. 기존 v2 `config.toml` 은 그대로 둬도 로드 시 메모리에서 자동 변환되며, 파일을 새 레이아웃으로 갱신하려면 `kebab config migrate` (값·주석 보존).
- **파생물 캐시** — embedding 결과를 내용 해시로 자동 캐싱한다 (위 「핵심 기능」 참고). 설정 항목 없음. - **파생물 캐시** — embedding 결과를 내용 해시로 자동 캐싱한다 (위 「핵심 기능」 참고). 설정 항목 없음.
- **`[ingest.code]`** — code ingest 의 skip 정책 (`skip_generated_header`, `max_file_bytes`, `extra_skip_globs`). `.gitignore` 자동 honor, `.kebabignore` 는 추가 layer. - **`[ingest.code]`** — code ingest 의 skip 정책 (`skip_generated_header`, `max_file_bytes`, `extra_skip_globs`). `.gitignore` 자동 honor, `.kebabignore` 는 추가 layer.
- **`[image.ocr]`** — 이미지 OCR (default off / opt-in). `engine` 으로 백엔드 선택: `"ollama-vision"` (default, 원격 vision LM) 또는 `"paddle-onnx"` (v0.27.0 신규 — PP-OCRv5 ONNX 를 in-process 로 실행, Python 런타임 불필요, 큰 페이지 CPU <4초, 오프라인). `paddle-onnx` 는 워크스페이스에 번들된 모델을 쓰며 `det_model`/`rec_model`/`dict` 로 경로 override, `score_thresh`(0.3)/`unclip_ratio`(1.5)/`max_boxes`(1000) 로 검출 튜닝 가능 (`KEBAB_IMAGE_OCR_*` env 동일 지원). engine 또는 모델을 바꾸면 영향 이미지가 자동 재색인된다. - **`[ingest.image.ocr]`** — 이미지 OCR (default off / opt-in). `engine` 으로 백엔드 선택: `"ollama-vision"` (default, 원격 vision LM) 또는 `"paddle-onnx"` (PP-OCRv5 ONNX 를 in-process 로 실행, Python 런타임 불필요, 큰 페이지 CPU <4초, 오프라인). `paddle-onnx` 는 워크스페이스에 번들된 모델을 쓰며 `det_model`/`rec_model`/`dict` 로 경로 override, `score_thresh`(0.3)/`unclip_ratio`(1.5)/`max_boxes`(1000) 로 검출 튜닝 가능 (`KEBAB_IMAGE_OCR_*` env 동일 지원 — env 이름은 v3 에서도 불변). engine 또는 모델을 바꾸면 영향 이미지가 자동 재색인된다.
- **`[pdf.ocr]`** — scanned PDF 의 page-단위 OCR (default off / opt-in, page 당 ~수십 초 cost). `engine``[image.ocr]` 과 동일하게 `"ollama-vision"`/`"paddle-onnx"` 선택. 활성화 후 v0.19 시절 색인분은 `kebab ingest --force-reingest` 로 재처리. - **`[ingest.pdf.ocr]`** — scanned PDF 의 page-단위 OCR (default off / opt-in, page 당 ~수십 초 cost). `engine``[ingest.image.ocr]` 과 동일하게 `"ollama-vision"`/`"paddle-onnx"` 선택. v3 에서 paddle 모델 경로 키(`det_model`/`rec_model`/`dict`/`score_thresh`/`unclip_ratio`/`max_boxes`)를 PDF 자체적으로 가질 수 있다(`KEBAB_PDF_OCR_*` env 동일). 활성화 후 옛 색인분은 `kebab ingest --force-reingest` 로 재처리.
- **`--config <path>`** — 임시 워크스페이스 / 격리 테스트용 (CLI · TUI 모두 honor). - **`--config <path>`** — 임시 워크스페이스 / 격리 테스트용 (CLI · TUI 모두 honor).
- **`kebab config migrate`** — 새 버전에서 추가된 config 섹션을 기존 `config.toml` 에 설명 주석과 함께 채워 넣는다 (사용자가 손본 값·주석·순서는 보존, 멱등, 변경 시 자동 `.bak` 백업). `--dry-run` 으로 변경 미리보기. `kebab doctor` 가 갱신 필요 시 안내한다. `kebab init` 으로 새로 생성되는 config.toml 도 섹션별 주석을 포함한다. - **`kebab config migrate`** — 새 버전에서 추가된 config 섹션을 기존 `config.toml` 에 설명 주석과 함께 채워 넣는다 (사용자가 손본 값·주석·순서는 보존, 멱등, 변경 시 자동 `.bak` 백업). `--dry-run` 으로 변경 미리보기. `kebab doctor` 가 갱신 필요 시 안내한다. `kebab init` 으로 새로 생성되는 config.toml 도 섹션별 주석을 포함한다.
- **`KEBAB_*` env** — 일부 키 override (`KEBAB_RAG_SCORE_GATE`, `KEBAB_EVAL_GOLDEN` 등). - **`KEBAB_*` env** — 일부 키 override (`KEBAB_RAG_SCORE_GATE`, `KEBAB_EVAL_GOLDEN` 등).

View File

@@ -95,12 +95,14 @@ model_dir = "{data_dir}/models"
runs_dir = "{data_dir}/runs" runs_dir = "{data_dir}/runs"
copy_threshold_mb = 100 copy_threshold_mb = 100
[indexing] # v0.28.0: 모든 형식 ingest 설정의 우산. 병렬도(← 옛 [indexing])는 [ingest] 스칼라로,
# chunking/code/image/pdf 는 [ingest.*] 하위로 통합. 옛 v2 파일은 로드 시 자동 변환됨.
[ingest]
max_parallel_extractors = 2 max_parallel_extractors = 2
max_parallel_embeddings = 1 max_parallel_embeddings = 1
watch_filesystem = false watch_filesystem = false
[chunking] [ingest.chunking]
target_tokens = 500 target_tokens = 500
overlap_tokens = 80 overlap_tokens = 80
respect_markdown_headings = true respect_markdown_headings = true
@@ -329,7 +331,7 @@ MCP tool 동등:
[workspace] [workspace]
include = ["**/*.md", "**/*.png", "**/*.jpg"] include = ["**/*.md", "**/*.png", "**/*.jpg"]
[image.ocr] [ingest.image.ocr]
enabled = true # vision LM 으로 이미지 안 텍스트 전사 enabled = true # vision LM 으로 이미지 안 텍스트 전사
engine = "ollama-vision" engine = "ollama-vision"
model = "gemma4:e4b" # 사용자 환경의 비전 모델 model = "gemma4:e4b" # 사용자 환경의 비전 모델
@@ -337,12 +339,12 @@ endpoint = "http://192.168.0.47:11434" # 비우면 models.llm.endpoint fallback
languages = ["eng", "kor"] languages = ["eng", "kor"]
max_pixels = 1600 # long-edge cap max_pixels = 1600 # long-edge cap
[image.caption] [ingest.image.caption]
enabled = true # vision LM 으로 한 문장 객관 설명 생성 enabled = true # vision LM 으로 한 문장 객관 설명 생성
max_pixels = 768 max_pixels = 768
prompt_template_version = "caption-v1" prompt_template_version = "caption-v1"
[pdf.ocr] [ingest.pdf.ocr]
enabled = true # smoke test 의 OCR path 활성화 (manual invoke) enabled = true # smoke test 의 OCR path 활성화 (manual invoke)
always_on = false always_on = false
engine = "ollama-vision" engine = "ollama-vision"
@@ -358,13 +360,13 @@ lang_hint = "kor"
이미지 자산 한 장당 OCR 1 호출 + Caption 1 호출 → ~3-6초 (`gemma4:e4b` 기준). 다이어그램 / 카메라 사진 / 스크린샷 위주 워크스페이스에 권장. 책 / 스캔본은 P7 PDF 라인으로. 이미지 자산 한 장당 OCR 1 호출 + Caption 1 호출 → ~3-6초 (`gemma4:e4b` 기준). 다이어그램 / 카메라 사진 / 스크린샷 위주 워크스페이스에 권장. 책 / 스캔본은 P7 PDF 라인으로.
**v0.27.0 — paddle-onnx 엔진 (오프라인, Ollama 불필요).** `[image.ocr] engine = "paddle-onnx"` 로 바꾸면 PP-OCRv5 ONNX 를 in-process 로 실행한다 (원격 vision LM 불필요, 큰 페이지 CPU <4초). embedding 까지 끄려면 `[models.embedding] provider = "none"` (lexical-only) 로 두면 Ollama 없이 OCR→FTS5 검색 전체 경로를 스모크할 수 있다: **v0.27.0 — paddle-onnx 엔진 (오프라인, Ollama 불필요).** `[ingest.image.ocr] engine = "paddle-onnx"` 로 바꾸면 PP-OCRv5 ONNX 를 in-process 로 실행한다 (원격 vision LM 불필요, 큰 페이지 CPU <4초). embedding 까지 끄려면 `[models.embedding] provider = "none"` (lexical-only) 로 두면 Ollama 없이 OCR→FTS5 검색 전체 경로를 스모크할 수 있다:
```toml ```toml
[models.embedding] [models.embedding]
provider = "none" # lexical-only — Ollama 불필요 provider = "none" # lexical-only — Ollama 불필요
[image.ocr] [ingest.image.ocr]
enabled = true enabled = true
engine = "paddle-onnx" # PP-OCRv5 ONNX in-process (Python/원격 0) engine = "paddle-onnx" # PP-OCRv5 ONNX in-process (Python/원격 0)
model = "ppocrv5-mobile-kor" model = "ppocrv5-mobile-kor"

View File

@@ -266,3 +266,6 @@ config load 체크 직후 `config_migration` 체크 1개 추가:
- kickoff 인계 문서와의 차이: kickoff §4.2 는 "버전별 변환 함수 체인"만 제안했으나, - kickoff 인계 문서와의 차이: kickoff §4.2 는 "버전별 변환 함수 체인"만 제안했으나,
kebab 의 serde-default 특성상 additive 변경은 step 으로 표현하기 부적절(버전 무관) → kebab 의 serde-default 특성상 additive 변경은 step 으로 표현하기 부적절(버전 무관) →
**reconciliation 을 1급 메커니즘으로 승격**하고 step 은 non-additive 전용으로 한정. **reconciliation 을 1급 메커니즘으로 승격**하고 step 은 non-additive 전용으로 한정.
- 2026-06-04 v3 재편(첫 non-additive rename)에서 `step_2_to_3`(미디어 테이블
`[ingest.*]` relocation) + `Config::from_file` load 시 메모리 자동변환 추가 —
`docs/superpowers/specs/2026-06-04-config-schema-reorg-design.md`.

View File

@@ -14,6 +14,31 @@ historical contract that was implemented; this file accumulates the
deltas so phase 5+ readers can find the live behavior without diffing deltas so phase 5+ readers can find the live behavior without diffing
git history. git history.
## 2026-06-04 — config 스키마 v2→v3 재편: 미디어 ingest 통합 (v0.28.0)
**무엇을 바꿨나.** `config.toml` 의 미디어 형식 설정을 `[ingest.*]` 우산 아래로 통합했다. 첫 non-additive rename 마이그레이션.
rename 매핑:
| v2 (top-level) | v3 (`[ingest.*]`) |
|---|---|
| `[indexing]` (스칼라 키) | `[ingest]` 스칼라 (`max_parallel_extractors`/`max_parallel_embeddings`/`watch_filesystem`) |
| `[chunking]` | `[ingest.chunking]` |
| `[image.ocr]` | `[ingest.image.ocr]` |
| `[image.caption]` | `[ingest.image.caption]` |
| `[pdf.ocr]` | `[ingest.pdf.ocr]` |
| `[ingest.code]` | `[ingest.code]` (불변) |
**보장한 3가지 불변식.**
1. **signature 바이트 불변**`ingest_config_signature` 출력은 값 기반이라 struct 경로 재편 후에도 v2 와 바이트 동일. 업그레이드 시 전체 재색인 발생 안 함. paddle 경로(det/rec/dict)는 미디어별로 호출자가 넘기도록 인자화(`ocr_engine_version_for_sig` + `engine_version_for_paths`); v2 의 "pdf 가 image paddle 을 빌려쓰던" 비대칭은 `step_2_to_3` 의 값 복사(`copy_image_paddle_to_pdf`)로 보존.
2. **env override 이름 100% 보존**`apply_env` whitelist 의 키 문자열(LHS, 예 `KEBAB_CHUNKING_TARGET_TOKENS`/`KEBAB_INDEXING_MAX_PARALLEL_EXTRACTORS`)은 불변, 대입 대상(RHS)만 `self.ingest.*` 로. 기존 `KEBAB_*` 스크립트 무파손. 신규 `KEBAB_PDF_OCR_{DET_MODEL,REC_MODEL,DICT,SCORE_THRESH,UNCLIP_RATIO,MAX_BOXES}` 6키 추가(image.ocr paddle 대칭).
3. **load 시 메모리 내 자동 변환**`Config::from_file``schema_version < 3` 파일을 디스크 미변경으로 메모리에서 v3 변환(`migrate_document` 경유). 미변환 v2 파일도 설정 유실 0. 파일 갱신은 `kebab config migrate` (값·주석·대안 줄 보존, 멱등).
`PdfOcrCfg` 에 paddle 대칭 6키 추가. `ser_f32_clean` 으로 f32 직렬화 정리(`0.30000001192092896``0.3`). per-option 인라인 주석(`key_comment`)을 init/migrate 산출 config 에 부착.
**도그푸딩.** (T10 후 채움)
## 2026-06-04 — PP-OCRv5 ONNX Rust 네이티브 OCR 엔진 (v0.27.0) ## 2026-06-04 — PP-OCRv5 ONNX Rust 네이티브 OCR 엔진 (v0.27.0)
**무엇을 추가했나.** 이미지 OCR 에 두 번째 백엔드 `paddle-onnx` 를 붙였다. 기존 `ollama-vision` **무엇을 추가했나.** 이미지 OCR 에 두 번째 백엔드 `paddle-onnx` 를 붙였다. 기존 `ollama-vision`