refactor: 척추 재작성 — 표면·동작·내부 단순화 (cuts + config 재편 + ingest 스파인) #214
Reference in New Issue
Block a user
Delete Branch "refactor/spine-cuts"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
요약
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서브커맨드ask --session,chat_sessions/chat_turns, V015 drop migrationsearch_cache·cache_capacityconfigkebab-embed-candlecrate (macOS 실사용도 ollama 확인 → 삭제 확정)Config 재편 (Phase 2)
[ingest.ocr]엔진 블록(image 13 + pdf 4 고유) + config v4→v5 무손실 자동 마이그레이션(round-trip 테스트로 잠금, effective 값 byte-동일 → 강제 재색인 없음)apply_env75→22 arm, 노출 키 109→~30&Config통째 대신 타입 슬라이스 수령 (god-struct 결합 해소)Ingest 스파인 (Phase 3)
ingest+ingest_with_config{IngestOpts}) + file/stdinstore_document_records·fingerprint_and_skip·chunk_asset·MarkdownExtractor(registry 통일)src/ingest.rs로 — lib.rs 4331→493줄, API 무변⚠️ Breaking / 마이그레이션
chat_sessions/chat_turns드롭. 첫 실행 시 자동.kebab tui,ask --session제거.kebab-tui/kebab-embed-candlecrate 제거.→ 본 PR 머지 후 별도 "정리" 커밋에서 minor 버전 bump + release + 마이그레이션 도그푸딩(V015 drop + config v5 경로) 수행 예정.
검증
cargo clippy --workspace --all-targets -- -D warnings0 warning. 전체 테스트 green — 단kebab-cli5개(cli_error_wire/cli_readonly_quiet/cli_schema/ingest_progress_cli/reset_cli)는 바이너리를target/debug/kebab하드코딩 경로로 찾아 로컬CARGO_TARGET_DIRoverride 환경에서만 실패(기본 target dir·CI 에선 통과). 본 브랜치가 건드리지 않은 pre-existing 테스트-하네스 버그 — 별도 fix(test) PR 예정.비범위
시험 항목 (Test Plan)
Assisted-by: Claude Code
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_012Mc6W1fgsrbFKTsqA6P8Lamarkdown 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회차 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.
🔴 머지 차단
[config v4→v5 마이그레이션 버그]
crates/kebab-config/src/migrate.rsstep_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 로 승계. 재현 확인: imageendpoint="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.[round-trip 테스트 tautology]
migrate.rsmigrate_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-endpointfixture 추가.[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로 갱신.[live doc stale]
docs/mcp-usage.md— 제거된 multi-turn 세션을 live 로 문서화(L179session_id입력 스키마, L403–468 "Session 관리" 섹션, L423chat_sessions/chat_turns영속화). ARCHITECTURE·SKILL 에서 링크됨. 세션 섹션 +session_id언급 삭제.[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)
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 와 동일 처리.)CLAUDE.md:7"24 crates" → 실제 22.🟢 후속/정리 (post-merge "정리" 단계, 본 PR 비차단)
missing field engine로 못 읽음) + env 통합으로KEBAB_PDF_OCR_ENGINE단일-미디어 타깃 불가 — 릴리스 노트/HOTFIXES 업그레이드 노트에 명시.UiCfg [ui] themedead surface(TUI 가 유일 소비자였음),docs/components/*stale 참조,main.rs:126/1196tui 주석, markdown warnings 포맷 결합 주석, 잉여pdf_chunker_from_config(ingest.rs:2328),resolve_ocr(None)방어 가드.🟢 긍정
DROP TABLE IF EXISTS), 런타임 참조 0, corpus_revision 무변경(default-off 기능 drop 으로 적절).회차 2 — 회차 1 머지-차단 + Minor 전부 반영 확인. APPROVE.
해소 (회차 1 지적 → 커밋)
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)--session/session_id·chat_sessions안내 제거, answer wire-schemaconversation_id/turn_index제거,capabilities.rag_multi_turntrue→false(키 유지·값만 flip, search_cache 와 동일), CLAUDE.md 24→22 crates. (cebfc89)clippy --workspace --all-targets0 warning, kebab-app capabilities/schema + kebab-cli wire 단위테스트 green.후속 "정리" (머지 후, 본 PR 비차단)
missing field engine) + env 통합으로KEBAB_PDF_OCR_ENGINE단일-미디어 불가 → 릴리스 노트/HOTFIXES 업그레이드 노트.UiCfg [ui] themedead surface,docs/components/*stale 참조,main.rstui 주석,resolve_ocr(None)방어 가드, 잉여pdf_chunker_from_config.머지 차단 항목 0건. APPROVE.