Commit Graph

1236 Commits

Author SHA1 Message Date
24ec86c515 feat(rag): rag-v4 — RAG provenance 라벨 (source/trust + 신뢰도 우선 지시)
RAG 프롬프트의 각 [근거] 청크 머리에 출처/trust 라벨을 붙이고
(`[#n] source=jira trust=secondary doc=…`), system prompt 에 "저신뢰 출처를 권위
출처와 충돌 시 discount 하고 [#번호]로 귀속" 2규칙을 더한다. 출처 필터
(`--source`/`--trust-min`)가 못 잡는 생성 측 실패 — 저신뢰(jira) 청크가 권위(wiki)
청크를 답변에서 덮어쓰는 것 — 를 다룬다.

- SearchHit 에 source_id/trust_level(additive optional). lexical/vector build_hit
  가 documents 조인에서 채움(both 동일: trust_level lowercase TEXT →
  serde lowercase round-trip, doc_summary read-back 과 동형). hybrid fusion 전파.
- pack_context 라벨 렌더(버전 무관 항상). SYSTEM_PROMPT_RAG_V4 = rag-v3 8규칙
  verbatim + 2규칙. config 기본 rag-v3→rag-v4. multi-hop synth 도 2규칙 →
  rag-multi-hop-v1→v2(prompt 변경 = 버전 bump, design §9).
- wire: search_hit.v1 에 두 필드 optional additive(required 아님,
  skip_serializing_if=None → 구 소비자 무영향, v2 bump 아님).
- source_id 는 RAG 헤더에 렌더되므로 validate_sources 에 [A-Za-z0-9._-] char 검증.
- opt-out: rag-v3 핀 = v3 system prompt 선택(discount 지시 빠짐, 라벨은 무해히 잔존).

검증: kebab-core/search/rag/config/eval 전 테스트 green(25 바이너리), clippy 0.
독립 코드 리뷰 APPROVE(7위험 PASS — trust round-trip 실 DB 확인; MEDIUM rag-v3
opt-out doc + multi-hop 버전 / LOW source_id 검증 반영). 도그푸딩: 라벨 메커니즘
end-to-end 검증(search --json 이 competing 쿼리에 wiki/primary + jira/secondary 둘
다 정확 라벨로 노출). LLM-judge(답변 비교)는 instruction LLM 부재로 보류(.2/.47
다운 + lemonade /api/generate it-model template 미적용) — 인프라, .2 복구 시 측정.
버전 bump 은 follow-up 들과 배치 릴리스에서 일괄.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
2026-06-24 05:48:34 +00:00
3573cceb4e Merge pull request 'feat(chunk): pdf-page-v1.2 — PDF 페이지 oversize 분할 + 공유 oversize 모듈' (#212) from feat/pdf-oversize-split into main 2026-06-24 05:47:46 +00:00
ed8ab7cdbe feat(chunk): pdf-page-v1.2 — PDF 페이지 oversize 분할 + 공유 oversize 모듈
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
2026-06-24 05:47:09 +00:00
5c45c384a0 Merge pull request 'fix(config): [ingest.chunking] budget floor 검증' (#211) from fix/config-chunking-floor-validation into main 2026-06-24 05:44:42 +00:00
22478a1126 Merge pull request 'docs(claude): 머신 용량/성능 + stale /build 경로를 전역·메모리로 이전' (#210) from docs/claude-md-machine-cleanup into main 2026-06-24 05:44:31 +00:00
727648d21d fix(config): [ingest.chunking] budget floor 검증 (validate_chunking)
`Config::from_file` 에 `validate_chunking()` 추가 — 청킹 budget 의 명백히 깨진
조합을 load 시점에 reject(`validate_sources` 와 동일 패턴, `ConfigInvalid`):

- `target_tokens ≥ 16` (`MIN_CHUNK_TOKENS`)
- `overlap_tokens < target_tokens`
- `max_chunk_tokens ≥ target_tokens`

동기: md-heading-v2(PR #209)의 `max_chunk_tokens` 는 검증이 없어 `0` 같은
오설정이 청커 내부 `budget.max(1)` 클램프에 흡수돼 3-byte 청크 폭주(인덱스
bloat, 무에러)를 냈다 — reviewer 지적. 기존 `target_tokens`/`overlap_tokens`
도 미검증이라 세 필드를 한 번에 floor + 상호 제약으로 막는다.

valid config 무영향(동작·결과 불변), 깨진 config 만 명확한 메시지로 load 실패.
새 config 키·migration·동작 변경 없음 → patch-level. 테스트 6종(defaults pass +
reject 4 + e2e from_file).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
2026-06-24 02:12:03 +00:00
b453881086 docs(claude): 머신 용량/성능 + stale /build 경로를 전역·프로젝트 메모리로 이전
repo CLAUDE.md 에 섞여 있던 머신-specific 내용을 정리:

- 머신 용량/성능(target balloon GB 수치, "disk space is tight", cargo clean
  루틴, `-j1` 의 RAM 고갈 사유)을 머신-레벨 global `~/.claude/CLAUDE.md`
  (codex/gemini 심링크)로 일반 규칙화해 이전. repo 에는 프로젝트-specific
  사실(무거운 dep 링크 → `-j1`, dev/test 프로파일 트림, 머지 후 cargo clean)만
  남기고 머신 제약은 global 포인터로 위임.
- stale `/build/dogfood/` 데이터 보관소 섹션 제거 — 이 머신에 `/build` 는
  존재하지 않았다(가공 경로). 실제 도그푸딩 store(`large_data/out/kebab-dogfood`,
  `kebab-ab`)는 머신-로컬이라 global §디스크 + 프로젝트 메모리로 이전하고,
  repo 에는 경로-무관 분류 정책(corpus/<format>/<category> 등)만 유지. 옛
  username 경로(`/home/altair823/KnowledgeBase`) 등 stale forbidden 목록 삭제.
- crate 수 표기 정정: "~21"/"18 crates" → 실제 24.

repo CLAUDE.md 는 이제 머신 경로/용량 수치를 하드코딩하지 않는다(전부 global +
프로젝트 메모리 참조). global 은 프로젝트-무관 유지(kebab 미언급).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
2026-06-24 01:44:25 +00:00
565ee9ef35 Merge pull request 'feat(chunk): md-heading-v2 — 예산 초과 청크 일반 분할' (#209) from feat/md-heading-v2-oversize-split into main
Reviewed-on: #209
v0.30.0
2026-06-24 01:26:51 +00:00
58386c4445 feat(chunk): md-heading-v2 — 예산 초과 청크 일반 분할 (oversize-chunk split)
v1 의 "블록 미분할" 한계를 일반화. 거대 list/code/table/paragraph 가 한 청크로
임베더 컨텍스트를 초과해 임베딩이 통째로 실패하던 문제를 해소한다. v2 는 v1 과
모든 출력이 동일하되, 청크의 실제 임베드 text 크기(`text.len()/3`)가
`max_chunk_tokens`(신규 config, byte/3, default 4000)를 넘는 청크만 줄(`\n`)
경계로, 단일 거대 줄은 UTF-8 char 경계로 잘라 각 조각이 예산 이하가 되게 한다.

- 판정 기준은 저장 `token_estimate` 가 아니라 실제 `text` 길이: ImageRef/AudioRef
  청크는 image-only 규약으로 token_estimate=0 이지만 OCR/caption text 는 클 수
  있다(빽빽한 스크린샷). 도그푸딩 일치 재테스트에서 이 image-OCR 구멍 발견·수정.
- 분할 조각 chunk_id 는 동일 block_ids 를 공유하므로 id-input 해시에 `#seg{i}`
  접미사로 충돌 회피(저장 policy_hash 는 bare — pdf-page-v1 의 `#L` 레시피 동형).
- `max_chunk_tokens` 는 v2 의 policy_hash 에만 fold(공유 ChunkPolicy 미변경 →
  코드/PDF 청커 cascade 무영향). 값 변경 시 markdown 만 재청크.
- 미분할 청크는 v1 과 byte-identical. `chunker_version` v1→v2 → 다음 plain
  `kebab ingest` 에서 markdown 1회 자동 재청크(--force 불필요, 코드/PDF 무영향).

동기: strict 임베더(AMD Lemonade `/api/embed`)는 oversize 입력을 truncate 아닌
거부(`500 too large`) — ollama 가 조용히 truncate 하던 걸 청커가 애초에 안 만들게.

검증(실험 KB, arctic-embed-l-v2 @ Lemonade): v2 전 620 중 2 doc(거대 jira list
블록) 임베드 실패 → v2 후 620/620 errors=0, 7114 청크 전부 ≤ 4000. 사용자 실
config 일치 재테스트(이미지 OCR + PDF OCR paddle-onnx ON)에서 image-OCR 구멍
발견·수정 후 dense 이미지 OCR 텍스트가 budget 초과 시 분할(token_estimate=0 →
1청크였던 것이 실제 text 기준 다중 청크로) 실증. frozen 설계 doc / frozen p1-5
spec 미변경(설계 §9 가 md-heading-v2 라벨 bump 를 변경 메커니즘으로 명시).

known limitation: PDF 는 별도 청커 pdf-page-v1.1 이라 이 split 미적용(후속 후보).

Cargo.toml 0.29.0 → 0.30.0 (신규 config 키 + 청커 동작 변경 = pre-1.0 minor +
도그푸딩 트리거). docs cascade: HOTFIXES / release-notes-v0.30.0-draft /
plan(2026-06-24) / normalize-chunk README / README / SMOKE / HANDOFF.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
2026-06-24 01:10:53 +00:00
cda6c2d820 Merge pull request 'feat(search): provenance 출처 필터 — [[workspace.sources]] 멀티소스 + --source/--source-type' (#208) from feat/source-type-filter into main
Reviewed-on: #208
2026-06-23 15:52:29 +00:00
58ac62d53a feat(search): provenance 출처 필터 — [[workspace.sources]] 멀티소스 + --source/--source-type
혼합 출처 KB(위키+jira 등)에서 색인은 전부 하되 질의 시 출처로 좁히는 provenance
레버. 전역 trust 곱셈가중(weighted-RRF)은 A/B 에서 반증(θ=0.85 만으로 incident MRR
0.918→0.340 절벽, 점수 압축) — 필터가 see-saw 없는 올바른 레버.

- config [[workspace.sources]] (각 id/root/exclude/trust_level/source_type);
  단일 root 는 implicit `default` source 로 정규화. validate: id 유일·비어있지 않음.
- config schema v3→v4 (step_3_to_4, root→[[workspace.sources]] id=default 미러, 멱등)
- V014 documents.source_id 컬럼+인덱스 (additive, DEFAULT 'default', 재색인 0)
- Metadata.source_id + BodyHints trust precedence(frontmatter > source 기본값 > Primary)
- ingest: --root 미지정 시 resolved_sources() 순회 + doc 마다 source_id/trust stamp
- 검색 SearchFilters.source_type/source_id → lexical + vector 두 site (IN, OR)
- CLI kebab search --source <id> / --source-type <type> (repeatable/comma-sep)

도그푸딩(620 doc, jira400+wiki220): --source wiki 로 개념 질의 MRR 0.780→0.810,
--source jira 로 incident 0.918→0.975. trust precedence 실측(jira=secondary 기본값).

version bump 0.28.0 → 0.29.0 (신규 CLI flag + config 키 + V014 migration → minor).
follow-up: MCP search 필터 미노출 · kebab list source_id 미표시 · RAG provenance 라벨.

자세한 내용: tasks/HOTFIXES.md (2026-06-21), docs/release-notes/v0.29.0-draft.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
2026-06-21 08:35:19 +00:00
403e162ac0 Merge pull request 'feat(config): config.toml v2→v3 스키마 재편 — 미디어 [ingest.*] 통합 + 무손실 자동 마이그레이션' (#207) from feat/config-schema-reorg into main
Reviewed-on: #207
v0.28.0
2026-06-04 14:36:41 +00:00
fdf09c369c refactor(config): PR #207 회차 1 반영 — from_file toml::Value 단일 파싱 2026-06-04 13:33:07 +00:00
e7b58017fd docs(config): v3 재편 도그푸딩 evidence + release notes
도그푸딩(release 빌드): 사용자 실제 v2 config 변환(값·주석 보존·멱등) +
재색인 0 실증(v2 자동변환·v3 디스크 양 경로 unchanged). v0.28.0 release notes
draft(변경/trade-off/mitigation/upgrade 4단락).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 13:12:01 +00:00
90812e981f 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>
2026-06-04 13:03:07 +00:00
15e6918cef feat(config): env 이름 보존 RHS 갱신 + pdf paddle 신규 env 6키
apply_env whitelist 의 키 문자열(LHS) 전부 불변, 대입 대상만 self.ingest.*
(불변식 #2). KEBAB_PDF_OCR_{DET_MODEL,REC_MODEL,DICT,SCORE_THRESH,
UNCLIP_RATIO,MAX_BOXES} 신규(image.ocr paddle 패턴 대칭).
게이트: clippy --workspace --all-targets 0, kebab-config/app/eval 테스트 green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:56:25 +00:00
a8ec354188 test(config): v3 무손실 골든 — 사용자 실제 v2 config relocation+멱등
사용자 실제 config(주석·대안 줄·score_gate=0.3000…1192 포함)를 fixture 로.
값·주석 보존 + v3 파싱 일치 + 멱등 검증.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:48:52 +00:00
2686a4f27d feat(config): from_file load 시 v2→v3 메모리 내 자동 변환(디스크 미변경)
schema_version < CURRENT 이면 migrate_document 경유로 메모리에서 변환 후 파싱.
디스크 파일은 불변(갱신은 kebab config migrate). 일회성 warn. 불변식 #3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:47:59 +00:00
25e94feab8 feat(config): step_2_to_3 — 미디어 테이블 [ingest.*] relocation + pdf paddle 값 보존
move_table(decor 포함 통째 이동) + move_indexing_keys(병렬도 키) +
copy_image_paddle_to_pdf(v2 비대칭 보존). CURRENT_SCHEMA_VERSION=3.
section_comment 를 ingest.* 경로로 갱신. 멱등.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:47:14 +00:00
7b7330cdf2 feat(config): per-option 인라인 주석(key_comment) — init/reconcile 부착
annotate_table 의 leaf 분기 추가: 스칼라/배열 키 값 뒤에 한 줄 주석 suffix.
dotted path → 주석 매핑(workspace.root, ocr.model, request_timeout 등).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:45:46 +00:00
3d45994693 refactor(config): signature paddle 경로 미디어화 + 바이트 불변 골든
ocr_engine_version_for_sig 가 det/rec/dict 를 호출자(미디어별)로부터 받도록
인자화 — image 는 [ingest.image.ocr], pdf 는 [ingest.pdf.ocr]. v2 의 pdf↔image
paddle 비대칭 제거. engine_version_for_paths 신설(kebab-parse-image). 출력
문자열은 값 기반이라 v2 와 바이트 동일(불변식 #1). test seam + 골든 추가.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:44:27 +00:00
d5c69f6715 refactor(config): v3 경로 call-site sweep (kebab-app/kebab-eval/kebab-parse-image)
부모 경로에 .ingest 삽입(leaf 구조체 불변). src + 테스트 call-site 전부.
kebab-cli 테스트의 v2 TOML fixture 는 from_file 자동변환(T6) 경로 검증용으로 유지.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:40:06 +00:00
148c8b7040 refactor(config): v3 레이아웃 — 미디어 ingest 통합 + pdf paddle 대칭 + float 직렬화
Config 의 indexing/chunking/image/pdf top-level 필드를 ingest: IngestCfg
하나로 통합. leaf 구조체는 불변, 부모 경로만 [ingest.*] 하위로 이동.
PdfOcrCfg 에 paddle 대칭 6키(det/rec/dict/score_thresh/unclip_ratio/
max_boxes) 추가. ser_f32_clean 으로 f32 직렬화 정리(0.3000000119→0.3).
apply_env RHS 를 self.ingest.* 로 갱신(env 키 문자열 LHS 불변).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:37:09 +00:00
898cdaa043 docs(config): v3 스키마 재편 설계 + 구현 계획 2026-06-04 12:25:49 +00:00
01a17acd3f Merge pull request 'feat(ocr): PP-OCRv5 ONNX Rust 네이티브 OCR 엔진' (#206) from feat/rust-native-ocr into main
Reviewed-on: #206
2026-06-04 09:24:37 +00:00
f3a7222ec5 fix(ocr): PR #206 round-1 리뷰 반영 — 골든 CI 테스트 + PDF 튜닝 문서 + threshold const + mutex 복구
- [MEDIUM] 골든 CI 단위테스트 2건 추가: ctc_greedy_decode_golden (argmax_idx
  one-hot → decoded 문자열 검증), det_box_score_golden (box_score/unclip_rect
  golden corner 검증). 모델/ONNX 불요, CI 상주.
  ctc_greedy_decode를 자유 함수(ctc_greedy_decode_with_dict)로 추출하여 테스트
  가능하게 함.
- [MEDIUM] PDF paddle 튜닝 비대칭 문서화: build_pdf_ocr_engine에 paddle-onnx가
  image.ocr.* 사용(pdf.ocr.* 아님) 이유 명시 + PdfOcrCfg.engine 필드 doc 갱신.
- [MEDIUM] DBNet 이진화 매직넘버 0.3 → DET_BIN_THRESH const 추출 + score_thresh
  기본값 느슨한 이유 1줄 주석.
- [LOW] Mutex poison 복구: det/rec .expect("poisoned") →
  .unwrap_or_else(PoisonError::into_inner). 자산 panic이 ingest abort 안 되도록.
- [LOW] DetBox.score dead field 제거 (box_score 결과는 필터에만 사용, 저장 불요).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 09:13:27 +00:00
3d5bb599e3 feat(ocr): bundle PP-OCRv5 ONNX models (det 4.7MB + rec 13MB)
paddle-onnx engine assets — committed as plain binary blobs (git-lfs not
installed on this host; see .gitattributes for the LFS migration recipe).
NOTICE (Apache-2.0) + korean_dict.txt already tracked. Loaded by default from
this dir or KEBAB_IMAGE_OCR_MODEL_DIR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 08:36:34 +00:00
375a0693e4 chore(ocr): T11/T12 — clippy clean + docs + v0.27.0 bump
T11: fix 12 clippy lints in paddle_onnx.rs/paddle_e2e.rs (doc overindent,
finish_non_exhaustive, map_or_else, RangeInclusive::contains, cast_lossless,
is_some_and, usize::from). Full-workspace clippy -D warnings = 0.

Smoke (paddle-onnx, real binary): clean_paragraph OCR verbatim-correct, real
per-region confidence (0.99/0.96/0.95), FTS5 lexical hit on Korean(검색)+
English(embedding), parser_version folds |ocr:1:paddle-onnx:<ver>. Big page
<4s inference (5.6s ingest incl. one-time session load).

T12: README [image.ocr].engine + ARCHITECTURE OCR row + SMOKE paddle-onnx config
+ HANDOFF + HOTFIXES dated entry. Workspace version 0.26.2 → 0.27.0 (minor:
new engine value + config keys). .gitattributes: onnx as plain blobs (no git-lfs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 08:36:19 +00:00
8cc4e6d563 fix(ocr): T10/T11 — unclip edge-offset (CER 0.26→0.005) + e2e gate + error tests
Root cause found at T11 e2e: unclip_rect pushed corners radially from the
centroid. For a wide/short text box the diagonal is near-horizontal, so the box
barely grew in height and clipped character tops (ㄷ→ㄴ, 다→나). Rewrote unclip
as a proper per-edge polygon offset along the rect's own (u,v) axes — height and
width each grow by 2*distance, matching PaddleOCR pyclipper.

Result (synthetic-ocr-bench, real inference): mean gate CER 0.2585 → 0.0049
(clean_paragraph/korean_heavy/numbers_table/tech_terms = 0.0), beating the
0.976 PoC baseline. Big page 3.9s < 5s.

T10: dict-length-mismatch construction error + undecodable-bytes recognize error.
T11 e2e: tests/paddle_e2e.rs CER<=0.05 gate (skips cleanly when assets absent).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 08:22:47 +00:00
901416d8e9 feat(ocr): T7-T9 — config overrides + engine factory + signature cascade
T7: OcrCfg gains det_model/rec_model/dict overrides + score_thresh/
unclip_ratio/max_boxes (serde default, KEBAB_IMAGE_OCR_* env). OnnxPaddleOcr::new
threads them via ModelPaths::from_config.
T8: build_image_ocr_engine / build_pdf_ocr_engine factories return
Box<dyn OcrEngine>; match on engine string (ollama-vision|paddle-onnx|err).
ImagePipeline.ocr_engine + pdf_ocr_engine signatures switched to &dyn OcrEngine.
OcrEngine gains model() for the progress label.
T9: ingest_config_signature image/pdf branches emit |ocr:1:{engine}:{engine_version}
(memoized blake3 per asset-triple, m3-safe). Unit tests (a)(b)(c) added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 08:15:30 +00:00
b706e3e88c feat(ocr): T2-T6 OnnxPaddleOcr core engine — det/rec ONNX + DBNet postproc + CTC
PP-OCRv5 ONNX OCR engine on the pinned ort rc.9 (no Python, no oar-ocr dep).
Implements the recognize() pipeline end-to-end (compiles + unit-tested):

- T2: OnnxPaddleOcr skeleton, OcrEngine impl, det/rec Session loaded once
  (Mutex-wrapped → Send+Sync), engine_version = blake3(det+rec+dict) cached
  once at construction, dict bounds-check (11945 lines vs 11947 rec classes).
- T2 preproc: det ImageNet mean/std NCHW + limit_side_len 960 → ×32 round
  (golden 192x900→896x192 pinned); rec height-48 keep-aspect, (x-0.5)/0.5.
- T3 det postproc: threshold 0.3 → imageproc contours → min-area rect via
  pure-Rust rotating calipers + convex hull → mean-prob box-score filter →
  pure-Rust unclip(ratio 1.5). No clipper2/OpenCV.
- T4 crop+rectify: corner ordering + bilinear perspective warp to horizontal.
- T5 rec+CTC: greedy decode with the T0a-confirmed mapping
  (idx0=blank, 1..=11945=dict[idx-1], 11946=space), rec-class bounds-check.
- T6 assembly: reading-order OcrText with per-region bbox + real confidence.

Unit tests (4 pass): det_target_dims golden, convex hull, min-area rect,
unclip expansion. Large *.onnx assets stay untracked pending T12 LFS decision.

Remaining: T7 config overrides, T8 factory (4 sites), T9 signature cascade,
T10 error matrix, T11 gates (clippy/e2e CER), T12 docs+bump+PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 07:52:39 +00:00
8f8d3a4100 feat(ocr): T0a/T0/T1 — golden harness(CTC blank=0 도출) + deps(ort rc.9) + dict/NOTICE
T0a: onnxruntime 직접 골든 하네스 → CTC blank/dict 매핑 경험 확정(gt CER 0.000).
T0: 모델 번들 dict+NOTICE(.onnx 는 T12 LFS 결정까지 워크트리 보관).
T1: ort(download-binaries)+imageproc 추가, cargo tree ort rc.9 단일 확인.
2026-06-04 07:43:53 +00:00
75a543ff69 docs(ocr): PP-OCRv5 ONNX Rust 네이티브 OCR spec + plan 2026-06-04 07:31:03 +00:00
a283e56c5c Merge pull request 'fix(ingest): ingest 설정 변경 시 영향 자산 자동 재색인' (#205) from fix/ingest-config-invalidation into main
Reviewed-on: #205
v0.26.2
2026-06-03 14:33:18 +00:00
47ef6532f7 chore(release): v0.26.2 — ingest 설정 변경 자동 재색인 + 문서
- Cargo.toml workspace version 0.26.1 → 0.26.2 (+Cargo.lock cascade).
  결과 포맷·CLI·wire 불변(내부 skip 판정 정정) → patch (CLAUDE.md §Versioning).
- tasks/HOTFIXES.md dated entry: 일반화 + 업그레이드 1회 재색인 안내 + 도그푸딩 evidence.
- HANDOFF.md 1줄.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:14:23 +00:00
03b0745e9d test(ingest): config invalidation e2e + parser_version assert 갱신
- config_invalidation.rs(신규): 동일config=전skip / 청킹변경=md+code재색인 /
  [ingest.code]변경=코드만 / search변경=재색인0 (회귀가드) end-to-end.
- code_ingest_smoke / pdf_pipeline: 저장 parser_version 이 이제
  "{base}|{sig}" composite 라, exact assert 를 base 접두사(split('|').next()) 비교로 갱신.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:14:15 +00:00
e7cb20990a feat(ingest): ingest 설정 변경 시 영향 자산 자동 재색인 (signature 폴딩)
ingest 산출에 영향 주는 설정(청킹/이미지 OCR·caption/pdf.ocr/[ingest.code])의
결정적 서명을 effective parser_version 에 폴딩 → 변경 시 --force-reingest 없이
영향 자산만 자동 재색인.

- ingest_config_signature(config, media_type): per-type 산출-영향 설정만 직렬화.
  비산출 설정(search/rag/ui/log + max_pixels/languages/timeout)은 제외.
- effective_parser_version(config, asset, base) = "{base}|{signature}".
- md/image/pdf/code 경로: composite 를 (a) try_skip_unchanged 비교값,
  (b) persist 전 canonical.parser_version override 에 사용.
- doc_id 는 base parser_version 으로 계속 파생 → 설정 변경에도 안정(orphan churn 회피).
- code Tier-3 fallback 은 bare "none-v1" sentinel 유지(skip bypass 의존).
- 단위테스트 8: 결정성/청킹=전타입/이미지·pdf·code 토글/무관설정 회귀가드.

spec: docs/superpowers/specs/2026-06-03-ocr-toggle-invalidation-spec.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:14:06 +00:00
bebf6e4ac7 docs(spec): ingest 설정 변경 자동 재색인 spec + plan
증분 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>
2026-06-03 13:40:02 +00:00
736d791056 Merge pull request 'feat(ingest): 진행 로그 개선 — 파일명/phase/heartbeat/slowest 요약' (#204) from feat/ingest-log-improve into main
Reviewed-on: #204
v0.26.1
2026-06-03 11:04:19 +00:00
6c9c8df43e chore(version): 0.27.0 → 0.26.1 — 새 bump 규칙상 patch
진행 로그 개선은 검색·색인 결과 불변 + 새 명령/플래그/config 없음 + additive-only
wire(asset_phase)라 CLAUDE.md 신규 규칙(기능/인터페이스 변경=minor, 없으면 patch)상
patch 가 맞음. version·라벨·HOTFIXES 헤더를 0.26.1 로 정정.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 11:02:16 +00:00
0263667684 docs(claude): pre-1.0 버전 bump 규칙 명확화 — 기능/인터페이스 변경=minor, 없으면 patch
minor 과다 사용 교정. 기능(behavior)·인터페이스(CLI/config/breaking wire/결과·동작/
migration) 변경 시 minor, 그 외(bug fix·refactor·관측성/로깅·additive-only wire·문서)
는 patch. 경계 예시 포함(진행로그+additive wire=patch, arctic provider/config=minor).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 11:00:49 +00:00
4918983d9c chore(ingest): PR #204 회차1 리뷰 반영 — 버전 라벨 v0.26.0 → v0.27.0
신규 진행로깅 표면(asset_phase / ocr_ms / caption_ms + progress.rs heartbeat·
slowest 주석)이 v0.26.0 으로 잘못 표기돼 있던 것을 v0.27.0(실제 추가 버전)으로
정정. wire schema 의 "추가 버전" 정확성(외부 통합 참조). 로직 변경 없음(주석/doc).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 10:57:17 +00:00
aeaa18a564 feat(ingest): 진행 로그 개선 — 파일명/phase/heartbeat/slowest 요약
OCR/caption 켜진 볼트 ingest 가 중간부터 느릴 때 TTY 진행바가 파일명·phase·
모델·경과시간을 안 보여 "멈춤"처럼 보이던 문제 해결.
- 신규 wire AssetPhase{idx,total,phase,model} + AssetTimings.ocr_ms/caption_ms
  (additive, ingest_progress.v1 유지)
- app: apply_ocr/apply_caption/embed 진입 시 AssetPhase emit + ocr/caption 시간 측정
- cli: TTY 진행바에 현재 파일명 + phase(model) + asset 경과초(heartbeat),
  종료 시 최장 소요 파일 top-5 요약(quiet 여도 출력, --json 미출력)
- wire schema / README / HANDOFF / HOTFIXES 동기화, version 0.26.0 → 0.27.0

검증(리더): clippy 0, kebab-app/cli 61그룹·parse-image/tui 14그룹 0실패(-j8).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 10:52:26 +00:00
c91ff909ce docs(spec): ingest 로그 개선 spec + plan
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 09:14:15 +00:00
8dee610a97 docs(hotfixes): arctic 종단 도그푸딩 evidence (recall@10 130/132)
kebab v0.26.0 실제 파이프라인(ollama arctic)으로 namu 재색인 → 확장 골든 eval
recall@10 130/132·recall@50 132/132·fully_consistent 22/24 종단 재현. 측정→구현
→실파이프라인 삼중 확인. 릴리스 전 도그푸딩 trigger(embedder 모델 변경) 충족.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v0.26.0
2026-06-03 07:19:19 +00:00
d71ed2516b Merge pull request 'feat(embed): arctic-embed-l-v2.0 임베더(candle+ollama)' (#203) from feat/arctic-embedder into main
Reviewed-on: #203
2026-06-03 06:27:55 +00:00
095c9f37a2 docs(smoke): embedding config 블록 v0.26.0 동기화
SMOKE.md 의 [models.embedding] 예시 주석이 stale: provider 목록에 ollama 누락 +
"candle 은 e5-large 만 지원"(arctic 추가로 더 이상 사실 아님) + endpoint/arctic
미기재. CLAUDE.md §"README Configuration + SMOKE config 블록 동시 갱신" 규칙대로
보완 — provider 4종, arctic 모델(candle/ollama 태그), endpoint(ollama 전용, llm
endpoint fallback), e5↔arctic cascade 주석 추가.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 05:11:03 +00:00
16ddb1dfc3 docs: arctic 임베더 문서 동기화 (README/ARCHITECTURE/HANDOFF/HOTFIXES)
README Configuration: provider candle/ollama + arctic 모델(candle CLS / ollama 태그)
+ endpoint + e5→arctic cascade 경고. ARCHITECTURE: 백엔드 그래프 노드(embedollama)
+ 임베딩 백엔드 결정표(채택 근거 측정 recall@10 130) + 디렉토리 트리. HANDOFF 1줄.
HOTFIXES 2026-06-03 arctic dated entry(레지스트리/pooling/prefix/cascade + 수동
cosine 0.999984 실측 결과).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 04:59:23 +00:00
72c99c452c feat(config,app): embedding provider=ollama 배선 + endpoint, version 0.26.0
kebab-config: EmbeddingModelCfg.endpoint: Option<String>(serde default, ollama용,
None→models.llm.endpoint 폴백) + provider 문서에 ollama + env
KEBAB_MODELS_EMBEDDING_ENDPOINT. kebab-app embedder(): provider match 에 ollama
분기(facade 경유). workspace member += kebab-embed-ollama, app dep 추가.
version 0.25.0 → 0.26.0(minor, +Cargo.lock) — 신규 임베딩 백엔드/모델은 CLAUDE.md
§Release 의 surface 변경 트리거.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 04:59:23 +00:00
cbcae69abf feat(embed): candle 모델 레지스트리 + arctic-embed-l-v2.0 (CLS pooling)
e5 하드코딩(HF_MODEL/SUPPORTED_MODEL/mean/query:+passage:) → 모델 레지스트리
EmbedModelSpec{name,hf_repo,pooling,query_prefix,doc_prefix,dim,version_tag}.
e5(mean, query:/passage:) + arctic(CLS, query:/무접두어). pooling 모델별 분기
(mean=attention-mask-weighted / CLS=hidden[:,0,:]), tokenize/forward/L2 공유.
arctic pooling=CLS 는 HF 1_Pooling/config.json(pooling_mode_cls_token:true) 확인.
model_version 은 arctic 일 때 +arctic-cls 태그(embedding_version cascade 트리거);
e5 는 fastembed-e5 호환(NUMA 드롭인) 위해 plain config.version 유지.

correctness 게이트: tests/arctic_ollama_parity.rs (#[ignore], live Ollama) —
candle arctic vs Ollama snowflake-arctic-embed2 per-sentence 코사인>0.99.
수동 실측 cosine_min=0.999984 (recall@10 130 재현 보장).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 04:59:11 +00:00