P9-1 Library 의 ? 키 활성화. App.ask slot 채움 (parallel-safety contract
그대로). Worker thread 가 kebab-app::ask_with_config 호출하면서
AskOpts.stream_sink 로 token 을 mpsc 채널 에 보냄, 메인 스레드 (TUI) 는
매 render frame 마다 drain 으로 문자열 누적 → 답변 영역 이 token-by-token
업데이트.
핵심:
- AskState 본체 (`app.rs`) — input / explain / streaming / partial /
answer / thread JoinHandle / rx Receiver / scroll / last_error.
- `src/ask.rs`:
- `render_ask` — input bar / 답변 영역 (streaming 시 ▍ cursor) /
bottom split (status: grounded/model/prompt/k/refusal · citations
or explain panel).
- `handle_key_ask`: typing → input. Enter → spawn_ask_worker (input
있음 + not streaming). e (input empty 시) → toggle explain.
j/k (input empty 시) → scroll. Esc → SwitchPane(Library) +
streaming/rx/thread 클리어 (best-effort cancel).
- `spawn_ask_worker` — mpsc::channel + thread::spawn(|| ask_with_config).
- run-loop hooks: `drain_stream` (try_iter → partial), `poll_worker`
(handle.is_finished → take + join → answer 채움 또는 ErrorOverlay).
- run.rs: Pane::Ask arm 이 handle_key_ask + render_ask. Idle tick 마다
drain_stream + poll_worker. SwitchPane(Ask) 시 lazy init.
테스트 13개 (`tests/ask.rs`) — Esc/typing/backspace/e toggle (input
empty)/e typed (input nonempty)/Enter empty/Enter while streaming
no-op/render pre-submission hint/streaming partial+cursor/grounded
answer + citation [1]/refusal score_gate 패널 panic 없음/explain panel
title flip/no slot.
Spec deviation (HOTFIXES `2026-05-02 P9-3`):
- `render_ask<B: Backend>` generic 제거 — ratatui 0.28 Frame
backend-agnostic (P9-1/P9-2 와 동일).
- e/j/k 가 input 빈 상태 일 때만 command 키, 입력 있으면 typing —
vim "command vs insert" 변형. spec literal 의 단순 \"e=toggle\" 은
\"explain\" / \"javascript\" 같은 단어 입력 깨뜨림.
Docs (sync rule):
- README: TUI 행 \"Library + Search + Ask 패널\" + Quick start 코멘트.
- HANDOFF: 한 줄 요약 + Phase status (P9 2/5 → 3/5) + deviation 한 줄.
- HOTFIXES: P9-3 entry.
- tasks/p9/p9-3 status: completed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5.8 KiB
HANDOFF — 진척도
새 conversation / 다른 사람이 이어받을 때 "지금 어디까지 됐고 다음에 뭘 할지" 의 단일 출처. 사용자 사용법은 README.md, 아키텍처는 docs/ARCHITECTURE.md, per-component 진행은 tasks/INDEX.md, 머지 후 발견된 버그는 tasks/HOTFIXES.md. 이 파일은 "phase 단위 진척" + "다음 task 후보" 만 담는다.
한 줄 요약
P0–P5 + P6 + P7 + P9-1 (Library) + P9-2 (Search) + P9-3 (Ask) 머지 완료. kebab ingest 가 markdown / image / PDF 모두 처리. kebab search / kebab ask 가 매체 가로질러 결과 + page citation 반환. kebab tui 가 Library + Search + Ask 패널 제공 (inspect/desktop 진행 예정). 다음 후보 = P9-4 (TUI inspect) / P9-5 (desktop tauri), 또는 보류 중인 P8 (audio) 의 시스템 dep brainstorm.
Phase 로드맵
| Phase | 내용 | 핵심 산출 crate | 선행 | 상태 |
|---|---|---|---|---|
| P0 | Workspace 뼈대 + 도메인 계약 + ID recipe | kebab-core, kebab-parse-types, kebab-config, kebab-app, kebab-cli |
– | ✅ 완료 |
| P1 | Markdown ingestion (walk → parse → chunk → SQLite) | kebab-source-fs, kebab-parse-md, kebab-normalize, kebab-chunk, kebab-store-sqlite |
P0 | ✅ 완료 |
| P2 | SQLite FTS5 lexical 검색 + citation | kebab-search (lexical) |
P1 | ✅ 완료 |
| P3 | Local embedding + LanceDB + hybrid (RRF) + kebab-app wiring | kebab-embed, kebab-embed-local, kebab-store-vector, kebab-search |
P2 | ✅ 완료 |
| P4 | Local LLM + RAG + grounded answer | kebab-llm, kebab-llm-local, kebab-rag |
P3 | ✅ 완료 |
| P5 | Golden query / regression eval | kebab-eval |
P4 | ✅ 완료 |
| P6 | 이미지 ingestion (OCR + caption) | kebab-parse-image |
P5 | ✅ 완료 (4/4 component, OCR/caption Ollama-vision) |
| P7 | PDF text + page citation | kebab-parse-pdf |
P5 | ✅ 완료 (3/3 component, page-level chunker + ingest wiring) |
| P8 | 음성 transcription + timestamp citation | kebab-parse-audio |
P5 | ⏸ 보류 (whisper-rs 시스템 dep brainstorm 필요) |
| P9 | TUI + desktop app | kebab-tui, kebab-desktop |
P5 | 🟡 진행 (3/5 component — P9-1 Library + P9-2 Search + P9-3 Ask 완료, P9-4/5 예정) |
P0P5 직렬. P6P9 P5 이후 병렬 가능.
Component 카운트
총 33 component task — spec 시점 31 개 + 후속 wiring task 3 (P3-5 / P6-4 / P7-3) 가 머지 시점에 추가됨. per-component 진행 + status 는 tasks/INDEX.md.
머지 후 발견된 버그 / 결정 (요약)
머지 후 발견된 모든 deviation / hotfix 의 dated 로그는 tasks/HOTFIXES.md. 본 요약은 "누군가가 인수받을 때 알아두면 시간을 많이 절약하는" 항목만:
- P3-5 / P4-3
--config누락 —kebab-cli가--config <path>를 honor 하려면kebab_app::*_with_configcompanion 을 호출해야 함. 두 번 같은 모양으로 회귀했음. - P6-2 OCR 기본 엔진 — spec literal 의 Tesseract 가 시스템 dep 부담으로 거부됨, Ollama vision LM 으로 대체.
OcrEnginetrait 그대로라 future swap 가능. - P6-3 caption —
GenerateRequest.images필드를kebab-core::LanguageModeltrait 에 신설. 기존 caller 모두images: Vec::new()로 마이그레이션. - P7-2
chunk_id충돌 — pdf-page-v1 가 한 페이지 여러 chunk 분할 → 같은block_ids충돌. per-chunkpolicy_hash#c{char_start}변형 으로 회피. - P7-3 storage UNIQUE bug —
assets.workspace_pathUNIQUE +upsert_asset_row의ON CONFLICT(asset_id)gap 으로 byte 변경 re-ingest 실패.purge_orphan_at_workspace_pathhelper 추가, follow-up PR 으로 vector store orphan cleanup 까지 닫음 (VectorStore::delete_by_chunk_ids). - P9-1 ratatui 0.28 — spec literal 의
render_library<B: Backend>generic 이 ratatui 0.28 의 backend-agnostic Frame 과 어긋나 있어 제거. 테스트 seamApp::populate_library_for_testing(#[doc(hidden)]) 추가. - P9-2 jump_to_citation workspace_root — spec literal 의
jump_to_citation(citation, editor_env)가 workspace_root 인자 누락. citation.path 가 workspace 상대라 editor 호출 시 절대 경로 필요 →workspace_root: &Path인자 추가. 동일하게render_search<B: Backend>generic 도 P9-1 과 같은 사유로 제거. - P9-3 e/j/k 키 의 "input empty" 분기 — spec 의
e=toggle explain/j=k=scroll이 typing 과 충돌 ("explain" / "javascript" 같은 단어 입력 깨짐). input 이 비어 있을 때만 command 키로 동작 — vim "command vs insert" 컨벤션 변형. 사용자가 텍스트 입력 시 모든 알파벳 정상 통과.
다음 task 후보
- P9-2 TUI search —
App.searchslot 채움. Library 의/가 enable 됨. - P9-3 TUI ask —
App.askslot 채움.?enable. - P9-4 TUI inspect —
App.inspectslot 채움.Enterenable. - P9-5 desktop tauri — 별도 분기. PDF citation rendering UI 가치 큼.
- P8 audio brainstorm — whisper-rs 시스템 dep 받을지 / 외부 transcription endpoint 사용할지 사용자 결정 필요. 사용자 패턴 (책+PDF 위주, audio 의향 없음) 상 후순위.
P9-2/3/4 는 P9-1 의 parallel-safety contract (sub-state slot 패턴) 덕에 병렬 진행 가능 — 같은 App 손대지 않음.
검증된 운영 동작 (release binary, fastembed enabled)
P7-3 머지 직후 25 시나리오 smoke 통과 — markdown + image + PDF 5 자산 워크스페이스에서 doctor / ingest / list / inspect / search (lex/vec/hybrid) / re-ingest / byte-edit re-ingest / corrupt PDF / RAG ask + page citation 모두. 자세한 시나리오 표는 conversation 기록 참조; 워크스페이스에 직접 돌려보는 절차는 docs/SMOKE.md.