refactor: 척추 재작성 — 표면·동작·내부 단순화 (cuts + config 재편 + ingest 스파인) #214

Merged
altair823 merged 29 commits from refactor/spine-cuts into main 2026-06-24 15:59:08 +00:00
Owner

요약

kebab 은 기능을 하나씩 더하며 자랐고 consolidation pass 없이 누적돼 표면·동작·내부 세 층위가 모두 산만해졌다. 단일 사용자·pre-1.0 이점을 살려 능력은 (거의) 유지하되 조작 표면과 코드 구조를 강하게 단순화한 척추 재작성이다. 출력 계약(wire)·ingest 출력은 불변(재색인 불필요)으로 유지했고, 각 수정 단위마다 출력-동등성 패리티 게이트(SEARCH/ASK/CHUNKS byte-IDENTICAL)를 HARD GATE 로 통과시켰다.

설계: docs/superpowers/specs/2026-06-24-spine-rewrite-simplification-design.md
계획: docs/superpowers/plans/2026-06-24-spine-phase2-config.md · docs/superpowers/plans/2026-06-24-spine-phase3-ingest.md
증거: tasks/HOTFIXES.md (Phase 0/1/2/3 dated entries — 단위별 게이트 결과)

Cuts (삭제) — crate 24→22

  • kebab-tui 전체 crate + kebab tui 서브커맨드
  • multi-turn 세션: ask --session, chat_sessions/chat_turns, V015 drop migration
  • legacy RAG 템플릿 rag-v1/v2 (rag-v3 기본·rag-v4 provenance 만 유지)
  • search LRU 캐시(p9-fb-19): search_cache·cache_capacity config
  • candle 임베더 provider + kebab-embed-candle crate (macOS 실사용도 ollama 확인 → 삭제 확정)

Config 재편 (Phase 2)

  • OCR 중복 제거: 공유 [ingest.ocr] 엔진 블록(image 13 + pdf 4 고유) + config v4→v5 무손실 자동 마이그레이션(round-trip 테스트로 잠금, effective 값 byte-동일 → 강제 재색인 없음)
  • 표면 정리: apply_env 75→22 arm, 노출 키 109→~30
  • slice refactor: consumer 6종이 &Config 통째 대신 타입 슬라이스 수령 (god-struct 결합 해소)

Ingest 스파인 (Phase 3)

  • ingest API 6변종 → 2 (ingest + ingest_with_config{IngestOpts}) + file/stdin
  • stage 헬퍼 추출: store_document_records · fingerprint_and_skip · chunk_asset · MarkdownExtractor(registry 통일)
  • 모듈 분리: ingest 코드를 src/ingest.rs 로 — lib.rs 4331→493줄, API 무변

⚠️ Breaking / 마이그레이션

  • V015 drop migration: 기존 KB 의 chat_sessions/chat_turns 드롭. 첫 실행 시 자동.
  • config v4→v5: 자동·무손실. 사용자 개입 불필요.
  • CLI surface: kebab tui, ask --session 제거. kebab-tui/kebab-embed-candle crate 제거.

→ 본 PR 머지 후 별도 "정리" 커밋에서 minor 버전 bump + release + 마이그레이션 도그푸딩(V015 drop + config v5 경로) 수행 예정.

검증

  • 각 수정 단위 출력-동등성 패리티 게이트: SEARCH·ASK·CHUNKS byte-IDENTICAL (GPU ollama baseline, Phase 0 동결). Phase 3 은 재인덱싱 게이트(gate-ingest.sh: fresh dir 재인덱싱 후 byte-IDENTICAL).
  • 전체 브랜치 최종: cargo clippy --workspace --all-targets -- -D warnings 0 warning. 전체 테스트 green — 단 kebab-cli 5개(cli_error_wire/cli_readonly_quiet/cli_schema/ingest_progress_cli/reset_cli)는 바이너리를 target/debug/kebab 하드코딩 경로로 찾아 로컬 CARGO_TARGET_DIR override 환경에서만 실패(기본 target dir·CI 에선 통과). 본 브랜치가 건드리지 않은 pre-existing 테스트-하네스 버그 — 별도 fix(test) PR 예정.
  • config v4→v5 round-trip 테스트(비-default 보존 + 오염 X + 멱등).

비범위

  • 새 기능 추가 없음 (순수 단순화).
  • crate 토폴로지 병합 없음 (삭제되는 2 crate 제외).
  • 검색/RAG 결과의 의도적 변경 없음 (동작 보존; 표면·기본값만 정리).

시험 항목 (Test Plan)

  • clippy --workspace --all-targets 0 warning
  • cargo test --workspace green (기본 target dir / CI)
  • 패리티 게이트 SEARCH/ASK/CHUNKS IDENTICAL (단위별, HOTFIXES 기록)
  • config v4→v5 round-trip 테스트 pass

Assisted-by: Claude Code

## 요약 kebab 은 기능을 하나씩 더하며 자랐고 consolidation pass 없이 누적돼 표면·동작·내부 세 층위가 모두 산만해졌다. 단일 사용자·pre-1.0 이점을 살려 **능력은 (거의) 유지하되 조작 표면과 코드 구조를 강하게 단순화**한 척추 재작성이다. 출력 계약(wire)·ingest 출력은 **불변**(재색인 불필요)으로 유지했고, 각 수정 단위마다 **출력-동등성 패리티 게이트**(SEARCH/ASK/CHUNKS byte-IDENTICAL)를 HARD GATE 로 통과시켰다. 설계: docs/superpowers/specs/2026-06-24-spine-rewrite-simplification-design.md 계획: docs/superpowers/plans/2026-06-24-spine-phase2-config.md · docs/superpowers/plans/2026-06-24-spine-phase3-ingest.md 증거: tasks/HOTFIXES.md (Phase 0/1/2/3 dated entries — 단위별 게이트 결과) ## Cuts (삭제) — crate 24→22 - `kebab-tui` 전체 crate + `kebab tui` 서브커맨드 - multi-turn 세션: `ask --session`, `chat_sessions`/`chat_turns`, **V015 drop migration** - legacy RAG 템플릿 rag-v1/v2 (rag-v3 기본·rag-v4 provenance 만 유지) - search LRU 캐시(p9-fb-19): `search_cache`·`cache_capacity` config - candle 임베더 provider + `kebab-embed-candle` crate (macOS 실사용도 ollama 확인 → 삭제 확정) ## Config 재편 (Phase 2) - OCR 중복 제거: 공유 `[ingest.ocr]` 엔진 블록(image 13 + pdf 4 고유) + **config v4→v5 무손실 자동 마이그레이션**(round-trip 테스트로 잠금, effective 값 byte-동일 → 강제 재색인 없음) - 표면 정리: `apply_env` 75→22 arm, 노출 키 109→~30 - slice refactor: consumer 6종이 `&Config` 통째 대신 타입 슬라이스 수령 (god-struct 결합 해소) ## Ingest 스파인 (Phase 3) - ingest API 6변종 → 2 (`ingest` + `ingest_with_config{IngestOpts}`) + file/stdin - stage 헬퍼 추출: `store_document_records` · `fingerprint_and_skip` · `chunk_asset` · `MarkdownExtractor`(registry 통일) - 모듈 분리: ingest 코드를 `src/ingest.rs` 로 — **lib.rs 4331→493줄**, API 무변 ## ⚠️ Breaking / 마이그레이션 - **V015 drop migration**: 기존 KB 의 `chat_sessions`/`chat_turns` 드롭. 첫 실행 시 자동. - **config v4→v5**: 자동·무손실. 사용자 개입 불필요. - **CLI surface**: `kebab tui`, `ask --session` 제거. `kebab-tui`/`kebab-embed-candle` crate 제거. → 본 PR 머지 후 별도 "정리" 커밋에서 **minor 버전 bump + release + 마이그레이션 도그푸딩**(V015 drop + config v5 경로) 수행 예정. ## 검증 - 각 수정 단위 **출력-동등성 패리티 게이트**: SEARCH·ASK·CHUNKS byte-IDENTICAL (GPU ollama baseline, Phase 0 동결). Phase 3 은 재인덱싱 게이트(gate-ingest.sh: fresh dir 재인덱싱 후 byte-IDENTICAL). - 전체 브랜치 최종: `cargo clippy --workspace --all-targets -- -D warnings` 0 warning. 전체 테스트 green — 단 `kebab-cli` 5개(`cli_error_wire`/`cli_readonly_quiet`/`cli_schema`/`ingest_progress_cli`/`reset_cli`)는 바이너리를 `target/debug/kebab` 하드코딩 경로로 찾아 **로컬 `CARGO_TARGET_DIR` override 환경에서만** 실패(기본 target dir·CI 에선 통과). 본 브랜치가 건드리지 않은 pre-existing 테스트-하네스 버그 — 별도 fix(test) PR 예정. - config v4→v5 round-trip 테스트(비-default 보존 + 오염 X + 멱등). ## 비범위 - 새 기능 추가 없음 (순수 단순화). - crate 토폴로지 병합 없음 (삭제되는 2 crate 제외). - 검색/RAG **결과**의 의도적 변경 없음 (동작 보존; 표면·기본값만 정리). ## 시험 항목 (Test Plan) - [ ] clippy --workspace --all-targets 0 warning - [ ] cargo test --workspace green (기본 target dir / CI) - [ ] 패리티 게이트 SEARCH/ASK/CHUNKS IDENTICAL (단위별, HOTFIXES 기록) - [ ] config v4→v5 round-trip 테스트 pass Assisted-by: Claude Code
altair823 added 27 commits 2026-06-24 15:23:57 +00:00
기능 추가로 산만해진 표면·동작·내부를 강하게 단순화하는 설계. 단일 사용자·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
사용자 필수 제약 반영: 각 수정 단위(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
Phase 0: 코어 품질 baseline 동결(kebab eval run + chunk dump). Phase 1: 5건 순수
삭제(search 캐시 → RAG v1/v2 → candle → 세션+V015 → TUI), 각 PR 마다 Parity Gate
(eval compare 델타≈0 + chunk byte-diff=0) HARD GATE. 정확한 제거점(파일:심볼) 명시.
ingest API 5→1 통합은 Phase 3(ingest 스파인)로 이동. Phase 2~5는 도달 시 상세 작성.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
eval golden set이 ground-truth 라벨 없음 + 도그푸딩 KB 임베더 down → 라벨 기반
metric 패리티 불가. 삭제 단계엔 출력 동등성(search/ask --json byte-diff + chunk diff)이
더 정확하고 라벨·외부엔드포인트 비의존. Task 0는 kebab 자체 docs + fastembed 로컬 +
lemonade RAG로 작은 재현 KB 구축 후 baseline 출력 동결.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
- App::search() → search_uncached() 직통 (캐시 로직 전체 삭제)
- SearchCacheKey struct + impl 제거
- App.search_cache 필드 + 초기화 블록 제거
- build_cache_key() / clear_search_cache() 제거
- config SearchCfg.cache_capacity + default_cache_capacity() 제거
- CLI clear_search_cache() 호출 제거; --no-cache 플래그는 유지(no-op)
- wire/schema search_cache 값 true → false (필드 유지)
- lru workspace dep + kebab-app direct dep 제거
- unicode-normalization dep 유지 (first_question_title() 에서 사용 중)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
Phase 2 Unit 1. OcrCfg(image) 13필드가 PdfOcrCfg(pdf) 와 전부 중복(image 고유
0, pdf 고유 4) + apply_env 에 KEBAB_IMAGE_OCR_*/KEBAB_PDF_OCR_* 27 arm 복제를
제거한다.

- 신규 SharedOcrEngineCfg(13 공유 필드, 전부 Option, default None) = [ingest.ocr].
  엔진 설정 단일 출처. image/pdf 블록은 on/off 토글 + override.
- load-time resolution(Config::resolve_ocr, from_file 호출): 공유 필드가 Some 이고
  미디어 블록이 그 키 미명시면 concrete OcrCfg/PdfOcrCfg 로 overlay(presence 는
  toml::Value 로 판정; 미디어 > 공유 > 내장 default). struct 필드는 그대로 두고
  엔진 필드에 #[serde(default)] 만 추가(slim 블록 파싱) → image(gemma4:e4b/1600)
  vs pdf(qwen2.5vl:3b/2048) 미디어별 기본값 보존.
- resolver Config::image_ocr()/pdf_ocr() 추가. consumer(kebab-parse-image,
  kebab-app build_*_ocr_engine·ingest gate·pdf_ocr_apply·ingest_config_signature)가
  전부 경유 → god-struct 직접 read 제거.
- apply_env: 27 arm → 공유 KEBAB_OCR_* 12 arm(image+pdf 동시) + pdf 고유 4 arm +
  미디어별 KEBAB_IMAGE_OCR_ENABLED/KEBAB_PDF_OCR_ENABLED.
- step_4_to_5: [ingest.image.ocr] 12 엔진 키를 [ingest.ocr] 로 move_table(enabled
  제외). pdf 블록 무손상(reconcile 이 채워 공유 overlay 오염 X). annotated_default
  도 동일 통합으로 v5 canonical 형상. CURRENT_SCHEMA_VERSION=5.
- v4→v5 round-trip 테스트(비-default image engine 보존 + pdf 오염 X + 멱등). effective
  OCR 바이트 동일 → ingest_config_signature 불변 → 강제 재색인 없음.

검증: clippy --workspace --all-targets 0 / kebab-config·kebab-parse-image·
kebab-parse-pdf·kebab-app 테스트 pass. surface: README [ingest.ocr] 절 + SMOKE
config 블록 + DOGFOOD env + HOTFIXES dated entry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
apply_env 의 KEBAB_* 매치 암을 103개→22개로 정리. 삭제된 암은 런타임에서
바꿀 일이 거의 없는 per-field 튜닝 노브(score_thresh, rrf_k, temperature,
multi_hop_*, nli_threshold, chunker_version, context_tokens 등) — struct
field 와 serde default 는 그대로 유지하므로 TOML 로는 여전히 설정 가능.

유지한 22개: endpoint×3, 모델명/프로바이더×5, 경로×2, 병렬도×2,
청킹 target/overlap×2, OCR 엔진/모델/언어+per-medium enabled×5,
caption enabled×1, search default_k×1, rag prompt_template_version×1.

영향 범위: struct field 삭제 없음(defaults 불변) → search/ask/chunk 출력
byte-identical 확인 (parity gate u2-surface IDENTICAL ✓).

docs: README KEBAB_* 항목을 실제 노출 키 목록으로 교체.
      SMOKE.md config 예시를 ~30개 공통 키로 슬림화 + env 설명 갱신.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
summary_only를 IngestOpts에 흡수, progress/cancellable/opts 변종 제거.
재인덱싱 게이트 CHUNKS/SEARCH/ASK byte-IDENTICAL, clippy --all-targets 0.
markdown ingest arm 이 그동안 유일하게 `App::extract_for` extractor
registry 를 우회하고 `kebab_parse_md::{parse_frontmatter, parse_blocks,
build_canonical_document}` free function 을 직접 호출했다 ("the single
biggest asymmetry"). 이를 image/pdf/code 와 동일하게 registry 경유로
통일.

- `kebab-parse-md` 에 `MarkdownExtractor` 신설 — 기존 free function 3종을
  동일 순서·동일 인자로 감싸 `bytes → CanonicalDocument` 생산만 담당.
  fm_span_end / count_lines_in / build_body_hints 헬퍼도 함께 이식.
- `App.extractors` registry 에 등록 (11 → 12 entry), markdown 이 `supports`
  로 발견되도록 첫 entry 로 배치.
- `ExtractContext` 에 `source_id` / `source_trust` 필드 추가 — markdown
  frontmatter 가 per-source trust 기본값을 override 하고 그 precedence 가
  `parse_frontmatter` *내부*에서 결정되므로 ctx 가 carry 해야 함. 다른
  extractor 는 None (post-extract 에서 source_id stamp 유지).
- 핸들러는 추출 stage 만 registry 로 이전 — version stamping / chunking /
  embedding / store 는 그대로. IngestItem.warnings 는 pdf/code 처럼
  `canonical.provenance` 의 Warning 이벤트에서 도출.

byte-identical 검증: parity gate-ingest (all-markdown 183 doc / 7676 chunk)
CHUNKS / SEARCH / ASK 모두 IDENTICAL. clippy 0, kebab-app + kebab-parse-md
test 전체 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
claude-reviewer-01 requested changes 2026-06-24 15:39:11 +00:00
Dismissed
claude-reviewer-01 left a comment
Member

회차 1 — 4개 focused 리뷰어(마이그레이션 / config 해석·슬라이스 / ingest 추출 / cut·문서) 통합. clippy --workspace --all-targets 0 + per-unit 패리티 게이트 IDENTICAL 로 기계적 정확성은 강하나, 게이트 미커버 영역(마이그레이션 엣지·diff 밖 contract surface)에서 아래를 발견. 다수 findings 가 PR diff 밖 파일이라 inline 대신 본문에 file:line 명기. REQUEST_CHANGES.

🔴 머지 차단

  1. [config v4→v5 마이그레이션 버그] crates/kebab-config/src/migrate.rs step_4_to_5 — image-only Option 키(endpoint/det_model/rec_model/dict)가 PDF OCR 로 누출. image 가 설정하고 pdf 가 명시 안 한 Option 키는 None 이라 annotated-default 참조문서에서 누락 → reconcile 가 pdf 에 미충전 → medium_has(pdf,key)=false → 공유 [ingest.ocr] 오버레이가 pdf 로 승계. 재현 확인: image endpoint="http://image-host" + pdf 미설정 → 마이그레이션 후 pdf_ocr().endpoint == Some("http://image-host") (구 바이너리는 None→llm fallback). det/rec/dictingest_config_signature 에 들어가 paddle PDF 강제 재색인 유발 → "강제 재색인 없음" 불변식 위반. (기본/대칭 config 는 무영향 — blast radius 는 비대칭 image-only Option 키 config.) 수정: step_4_to_5 에서 Option 키는 pdf 도 명시한 경우에만 hoist.

  2. [round-trip 테스트 tautology] migrate.rs migrate_v4_to_v5_preserves_effective_ocrbefore = Config::from_file(v4) 가 이미 in-memory 마이그레이션+resolve 를 거친 오염값 이라 after == before 가 무의미하게 통과. 진짜 v4 시맨틱(pdf Option 키 = None)을 검증 못 함 → #1 을 영원히 못 잡음. baseline 을 구-바이너리 시맨틱(None)으로 재작성 + 비대칭 image-only-endpoint fixture 추가.

  3. [shipped integration stale] integrations/claude-code/kebab/SKILL.md — 제거된 --session/session_id(L83/88/116/156), rag-v1/v2(L89), kebab tui(L201)를 아직 안내. PR diff 밖(미수정). kebab CLAUDE.md "shipped integration 은 같은 PR 에서 갱신" 규칙 위반 — 에이전트가 없는 플래그/입력을 쓰게 됨. 세션·tui 안내 제거 + rag 기본 rag-v4 로 갱신.

  4. [live doc stale] docs/mcp-usage.md — 제거된 multi-turn 세션을 live 로 문서화(L179 session_id 입력 스키마, L403–468 "Session 관리" 섹션, L423 chat_sessions/chat_turns 영속화). ARCHITECTURE·SKILL 에서 링크됨. 세션 섹션 + session_id 언급 삭제.

  5. [wire-schema 괴리] docs/wire-schema/v1/answer.schema.json(L48–58) + answer_event.schema.json(L14) — Answer 구조체에서 제거된 conversation_id/turn_index 가 doc 에 잔존(producer 가 더는 방출 ��� 함). skip_serializing_if=None 이라 v1 backward-compat 는 유지되나 doc/producer 괴리 — 속성 제거(v2 bump 불필요).

🟡 함께 수정 권장 (Minor)

  1. capabilities.rag_multi_turn 이 아직 true (search_cachefalse 로 바꿨는데 누락). crates/kebab-app/src/schema.rs:158 + crates/kebab-cli/src/wire.rs:339false. (frozen v1 맵이라 키 자체는 유지, 값만 flip — search_cache 와 동일 처리.)
  2. CLAUDE.md:7 "24 crates" → 실제 22.

🟢 후속/정리 (post-merge "정리" 단계, 본 PR 비차단)

  • v5 config downgrade cliff(구 v0.30.1 바이너리가 v5 config 를 missing field engine 로 못 읽음) + env 통합으로 KEBAB_PDF_OCR_ENGINE 단일-미디어 타깃 불가 — 릴리스 노트/HOTFIXES 업그레이드 노트에 명시.
  • UiCfg [ui] theme dead surface(TUI 가 유일 소비자였음), docs/components/* stale 참조, main.rs:126/1196 tui 주석, markdown warnings 포맷 결합 주석, 잉여 pdf_chunker_from_config(ingest.rs:2328), resolve_ocr(None) 방어 가드.

🟢 긍정

  • ingest stage 추출: 4미디어(markdown/image/pdf/code/tier-3) byte-identical 동등을 라인별 + 단위테스트로 검증. tier-3 sentinel(in-place→return value) 양쪽 sub-case 수렴, doc_id/chunker_version 불변, store 순서 동일, API 6→2 progress/cancel/summary 보존.
  • config 슬라이스: default drift 없음(VectorRetriever snippet_chars·RagPipeline·Ollama endpoint·Fastembed cache_dir 보존), apply_env 축소 테스트 커버, resolve_ocr 의 presence-from-toml::Value 접근은 정확(단 #1 의 Option-키 케이스 제외).
  • V015 drop: idempotent(DROP TABLE IF EXISTS), 런타임 참조 0, corpus_revision 무변경(default-off 기능 drop 으로 적절).
  • 3-doc 동기화(README/HANDOFF/ARCHITECTURE) + SMOKE 철저, frozen-history(task spec/design/dated HOTFIXES) 경계 정확히 준수.
회차 1 — 4개 focused 리뷰어(마이그레이션 / config 해석·슬라이스 / ingest 추출 / cut·문서) 통합. clippy --workspace --all-targets 0 + per-unit 패리티 게이트 IDENTICAL 로 기계적 정확성은 강하나, 게이트 미커버 영역(마이그레이션 엣지·diff 밖 contract surface)에서 아래를 발견. 다수 findings 가 PR diff 밖 파일이라 inline 대신 본문에 file:line 명기. **REQUEST_CHANGES.** ## 🔴 머지 차단 1. **[config v4→v5 마이그레이션 버그]** `crates/kebab-config/src/migrate.rs` `step_4_to_5` — image-only Option 키(`endpoint`/`det_model`/`rec_model`/`dict`)가 PDF OCR 로 누출. image 가 설정하고 pdf 가 명시 안 한 Option 키는 `None` 이라 annotated-default 참조문서에서 누락 → reconcile 가 pdf 에 미충전 → `medium_has(pdf,key)=false` → 공유 `[ingest.ocr]` 오버레이가 pdf 로 승계. **재현 확인**: image `endpoint="http://image-host"` + pdf 미설정 → 마이그레이션 후 `pdf_ocr().endpoint == Some("http://image-host")` (구 바이너리는 `None`→llm fallback). `det/rec/dict` 는 `ingest_config_signature` 에 들어가 paddle PDF **강제 재색인** 유발 → "강제 재색인 없음" 불변식 위반. (기본/대칭 config 는 무영향 — blast radius 는 비대칭 image-only Option 키 config.) 수정: `step_4_to_5` 에서 Option 키는 pdf 도 명시한 경우에만 hoist. 2. **[round-trip 테스트 tautology]** `migrate.rs` `migrate_v4_to_v5_preserves_effective_ocr` — `before = Config::from_file(v4)` 가 이미 in-memory 마이그레이션+resolve 를 거친 *오염값* 이라 `after == before` 가 무의미하게 통과. 진짜 v4 시맨틱(pdf Option 키 = `None`)을 검증 못 함 → #1 을 영원히 못 잡음. baseline 을 구-바이너리 시맨틱(`None`)으로 재작성 + 비대칭 image-only-`endpoint` fixture 추가. 3. **[shipped integration stale]** `integrations/claude-code/kebab/SKILL.md` — 제거된 `--session`/`session_id`(L83/88/116/156), `rag-v1/v2`(L89), `kebab tui`(L201)를 아직 안내. PR diff 밖(미수정). kebab CLAUDE.md "shipped integration 은 같은 PR 에서 갱신" 규칙 위반 — 에이전트가 없는 플래그/입력을 쓰게 됨. 세션·tui 안내 제거 + rag 기본 `rag-v4` 로 갱신. 4. **[live doc stale]** `docs/mcp-usage.md` — 제거된 multi-turn 세션을 live 로 문서화(L179 `session_id` 입력 스키마, L403–468 "Session 관리" 섹션, L423 `chat_sessions`/`chat_turns` 영속화). ARCHITECTURE·SKILL 에서 링크됨. 세션 섹션 + `session_id` 언급 삭제. 5. **[wire-schema 괴리]** `docs/wire-schema/v1/answer.schema.json`(L48–58) + `answer_event.schema.json`(L14) — `Answer` 구조체에서 제거된 `conversation_id`/`turn_index` 가 doc 에 잔존(producer 가 더는 방출 ��� 함). `skip_serializing_if=None` 이라 v1 backward-compat 는 유지되나 doc/producer 괴리 — 속성 제거(v2 bump 불필요). ## 🟡 함께 수정 권장 (Minor) 6. `capabilities.rag_multi_turn` 이 아직 `true` (`search_cache` 는 `false` 로 바꿨는데 누락). `crates/kebab-app/src/schema.rs:158` + `crates/kebab-cli/src/wire.rs:339` → `false`. (frozen v1 맵이라 키 자체는 유지, 값만 flip — search_cache 와 동일 처리.) 7. `CLAUDE.md:7` "24 crates" → 실제 22. ## 🟢 후속/정리 (post-merge "정리" 단계, 본 PR 비차단) - v5 config downgrade cliff(구 v0.30.1 바이너리가 v5 config 를 `missing field engine` 로 못 읽음) + env 통합으로 `KEBAB_PDF_OCR_ENGINE` 단일-미디어 타깃 불가 — 릴리스 노트/HOTFIXES 업그레이드 노트에 명시. - `UiCfg [ui] theme` dead surface(TUI 가 유일 소비자였음), `docs/components/*` stale 참조, `main.rs:126/1196` tui 주석, markdown warnings 포맷 결합 주석, 잉여 `pdf_chunker_from_config`(ingest.rs:2328), `resolve_ocr(None)` 방어 가드. ## 🟢 긍정 - **ingest stage 추출**: 4미디어(markdown/image/pdf/code/tier-3) byte-identical 동등을 라인별 + 단위테스트로 검증. tier-3 sentinel(in-place→return value) 양쪽 sub-case 수렴, doc_id/chunker_version 불변, store 순서 동일, API 6→2 progress/cancel/summary 보존. - **config 슬라이스**: default drift 없음(VectorRetriever snippet_chars·RagPipeline·Ollama endpoint·Fastembed cache_dir 보존), apply_env 축소 테스트 커버, resolve_ocr 의 presence-from-toml::Value 접근은 정확(단 #1 의 Option-키 케이스 제외). - **V015 drop**: idempotent(`DROP TABLE IF EXISTS`), 런타임 참조 0, corpus_revision 무변경(default-off 기능 drop 으로 적절). - 3-doc 동기화(README/HANDOFF/ARCHITECTURE) + SMOKE 철저, frozen-history(task spec/design/dated HOTFIXES) 경계 정확히 준수.
altair823 added 2 commits 2026-06-24 15:54:40 +00:00
endpoint/det_model/rec_model/dict 는 default None 이라 reconcile 가 pdf 블록에
채우지 않는다(None 은 annotated-default 에서 누락). image 만 설정한 이 키를 공유
[ingest.ocr] 로 끌어올리면 resolve_ocr overlay 가 (medium_has(pdf,key)=false 라)
pdf 로 누출돼, pdf OCR endpoint/asset 경로가 v4 와 달라지고 det/rec/dict 는
ingest_config_signature 에 들어가 강제 재색인까지 유발했다. step_4_to_5 에서
Option 키는 pdf 도 명시한 경우에만 hoist 하도록 OPTION_OCR_KEYS 가드 추가.

기존 round-trip 테스트는 before=Config::from_file(v4) 가 이미 마이그레이션+resolve
를 거친 오염값이라 after==before tautology 였음 — pdf.det_model==None 명시 검증
추가 + 비대칭 image-only-endpoint 회귀 테스트 신설.
cut 으로 제거된 기능이 diff 밖 contract surface 에 남긴 잔재 정리:
- integrations/claude-code/kebab/SKILL.md: 제거된 --session/session_id 안내,
  answer.v1 의 conversation_id/turn_index 필드 목록, rag-v2 기본 pin, kebab tui
  언급 제거. 기본 템플릿 rag-v4(대체 rag-v3)로 갱신.
- docs/mcp-usage.md: ask 입력 스키마의 session_id, 'Session 관리(multi-turn)'
  섹션 전체(chat_sessions/chat_turns 포함), 예시의 conversation_id/turn_index 제거.
- docs/wire-schema/v1/answer.schema.json + answer_event.schema.json: Answer 가
  더는 방출 않는 conversation_id/turn_index 속성 제거(producer/doc 괴리 해소).
- schema.rs + wire.rs: capabilities.rag_multi_turn true→false(search_cache 와
  동일 처리; frozen v1 맵이라 키는 유지, 값만 flip).
- CLAUDE.md: 24→22 crates(tui+candle crate 제거 반영).
claude-reviewer-01 approved these changes 2026-06-24 15:55:16 +00:00
claude-reviewer-01 left a comment
Member

회차 2 — 회차 1 머지-차단 + Minor 전부 반영 확인. APPROVE.

해소 (회차 1 지적 → 커밋)

  • [버그] config v4→v5 Option OCR 키 pdf 누출step_4_to_5OPTION_OCR_KEYS(endpoint/det_model/rec_model/dict) 가드: pdf 가 명시한 경우에만 hoist, 아니면 image-local 유지. tautology round-trip 테스트에 명시적 pdf.det_model==None 단언 추가 + 비대칭 image-only-endpoint 회귀 테스트 신설. migrate_v4_to_v5_* 2건 green, kebab-config 전체 green. (0ae739e)
  • [contract surface] SKILL.md·mcp-usage.md 의 --session/session_id·chat_sessions 안내 제거, answer wire-schema conversation_id/turn_index 제거, capabilities.rag_multi_turn true→false(키 유지·값만 flip, search_cache 와 동일), CLAUDE.md 24→22 crates. (cebfc89)
  • 게이트: clippy --workspace --all-targets 0 warning, kebab-app capabilities/schema + kebab-cli wire 단위테스트 green.

후속 "정리" (머지 후, 본 PR 비차단)

  • v5 config downgrade cliff(구 바이너리 missing field engine) + env 통합으로 KEBAB_PDF_OCR_ENGINE 단일-미디어 불가 → 릴리스 노트/HOTFIXES 업그레이드 노트.
  • UiCfg [ui] theme dead surface, docs/components/* stale 참조, main.rs tui 주석, resolve_ocr(None) 방어 가드, 잉여 pdf_chunker_from_config.
  • ingest pdf/image/code byte-identical 은 현재 inspected-equivalent(라인+단위테스트) → paddle 결정적 게이트 도그푸딩으로 observed-equivalent 격상(후속 계획됨).

머지 차단 항목 0건. APPROVE.

회차 2 — 회차 1 머지-차단 + Minor 전부 반영 확인. **APPROVE.** ## 해소 (회차 1 지적 → 커밋) - **[버그] config v4→v5 Option OCR 키 pdf 누출** — `step_4_to_5` 에 `OPTION_OCR_KEYS`(endpoint/det_model/rec_model/dict) 가드: pdf 가 명시한 경우에만 hoist, 아니면 image-local 유지. tautology round-trip 테스트에 명시적 `pdf.det_model==None` 단언 추가 + 비대칭 image-only-endpoint 회귀 테스트 신설. `migrate_v4_to_v5_*` 2건 green, kebab-config 전체 green. (`0ae739e`) - **[contract surface]** SKILL.md·mcp-usage.md 의 `--session`/`session_id`·`chat_sessions` 안내 제거, answer wire-schema `conversation_id`/`turn_index` 제거, `capabilities.rag_multi_turn` true→false(키 유지·값만 flip, search_cache 와 동일), CLAUDE.md 24→22 crates. (`cebfc89`) - **게이트**: `clippy --workspace --all-targets` 0 warning, kebab-app capabilities/schema + kebab-cli wire 단위테스트 green. ## 후속 "정리" (머지 후, 본 PR 비차단) - v5 config downgrade cliff(구 바이너리 `missing field engine`) + env 통합으로 `KEBAB_PDF_OCR_ENGINE` 단일-미디어 불가 → 릴리스 노트/HOTFIXES 업그레이드 노트. - `UiCfg [ui] theme` dead surface, `docs/components/*` stale 참조, `main.rs` tui 주석, `resolve_ocr(None)` 방어 가드, 잉여 `pdf_chunker_from_config`. - ingest pdf/image/code byte-identical 은 현재 inspected-equivalent(라인+단위테스트) → paddle 결정적 게이트 도그푸딩으로 observed-equivalent 격상(후속 계획됨). 머지 차단 항목 0건. APPROVE.
altair823 merged commit 4ae305b396 into main 2026-06-24 15:59:08 +00:00
altair823 deleted branch refactor/spine-cuts 2026-06-24 15:59:12 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: altair823-org/kebab#214