Commit Graph

232 Commits

Author SHA1 Message Date
76fbb44e83 Merge pull request 'feat(rag): multi-turn ask — Turn + ask_with_history + token budget (p9-fb-15)' (#60) from feat/p9-fb-15-rag-multiturn into main 2026-05-02 23:14:54 +00:00
b19ebfd2bc review(회차1): AskOpts::single_shot helper 제거 (yagni)
회차 1 nit 반영. helper 가 본 PR 안 caller 0 — 모든 사용처가
struct literal 패턴. CLAUDE.md "Don't add abstractions beyond
what the task requires" 룰. 미래 caller 가 필요 시 본인이 추가.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 23:14:12 +00:00
2c058ab175 feat(rag): multi-turn ask — Turn struct + ask_with_history + token budget (p9-fb-15)
Spec PR #59 의 §3.8 multi-turn behaviour 구현. RAG facade 가 prior
turns 받아 prompt 에 prepend, retrieval query expansion 적용,
Answer 에 conversation_id / turn_index 채움.

신규 (kebab-core):
- Answer 에 conversation_id (Option<String>) / turn_index (Option<u32>)
  field 추가. serde skip_serializing_if 로 single-shot 의 wire
  output 변경 0 (기존 외부 wrapper 영향 없음).
- Turn struct (question + answer + citations + created_at).
- RefusalReason::LlmStreamAborted variant.

신규 (kebab-rag):
- AskOpts 에 history (Vec<Turn>) / conversation_id / turn_index 3 field.
- AskOpts::single_shot(mode) helper.
- RagPipeline::ask_with_history(query, history, conversation_id,
  turn_index, opts) — combined opts 로 ask 호출.
- expand_query_with_history: history.last() 의 answer 첫 200 자
  concat 해 SearchQuery.text 확장 (spec §3.8 의 \"cheap concat\";
  LLM-based standalone-question rewriting 은 P+).
- serialize_history + remaining_history_budget_chars: spec 의 priority
  enforcement — system+question 필수, retrieved chunks 가 차지한
  뒤 남은 char budget 안에서 newest 우선, oldest drop.
- ask 본문: history 가 비어있지 않으면 [이전 대화] 블록을 user
  prompt 위에 prepend. Answer 생성 site 3 곳 (정상 / NoChunks /
  ScoreGate refuse) 모두 conversation_id / turn_index 채움.

신규 (kebab-store-sqlite):
- refusal_reason_label 가 LlmStreamAborted → 'llm_stream_aborted'.

기존 caller 변경 (single-shot 동작 동일):
- kebab-cli main.rs Cmd::Ask: AskOpts 에 history=Vec::new(),
  conversation_id=None, turn_index=None 명시 (CLI multi-turn 은
  p9-fb-18 의 --session/--repl 가 채움).
- kebab-tui src/ask.rs spawn site 동일 (multi-turn UI 는 p9-fb-16).
- kebab-eval runner.rs golden eval 동일 (single-shot per query).
- kebab-app tests/ask_smoke.rs / kebab-tui tests/ask.rs / kebab-rag
  tests/pipeline.rs / kebab-eval metrics.rs Answer literal 갱신.

Test:
- 9 신규 lib unit (expand_query 4 / serialize_history 3 / remaining_budget 2).
- 기존 12 PASS 회귀 0.

Plan 갱신:
- p9-fb-15 status planned → in_progress. 머지 후 한 줄 commit
  으로 completed flip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 23:09:46 +00:00
9ddd199727 Merge pull request 'spec(p9-fb-15): RAG multi-turn 정책 + answer.v1 conversation_id/turn_index' (#59) from spec/p9-fb-15-multi-turn-ask into main 2026-05-02 22:12:06 +00:00
6480b463bc review(회차1): RefusalReason::LlmStreamAborted variant 추가 + 빈 줄 정리
회차 1 actionable 2건 반영.

- §3.8 RefusalReason enum 에 LlmStreamAborted variant 추가 + doc
  comment (RAG retrieval 정상, model generation 단계에서만 중단).
  spec PR self-contained 원칙 — impl PR 이 spec 변경 없이 진행
  가능.
- Multi-turn behaviour 절 끝 빈 줄 2 → 1 + RefusalReason 정의
  cross-link 한 줄 추가.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 22:11:52 +00:00
ecb85651ea spec(p9-fb-15): RAG multi-turn 정책 + answer.v1 conversation_id/turn_index
도그푸딩 후 추가된 ask multi-turn (꼬리 물기) surface 를 frozen design
+ wire schema 에 명시. p9-fb-15 (RAG core) + p9-fb-16 (TUI UI) +
p9-fb-17 (V004 chat sessions) + p9-fb-18 (CLI session/repl) 의 spec
PR — impl PR 들이 이어진다.

변경:
- §2.3 Answer wire schema: conversation_id (String?) + turn_index
  (u32?) 두 optional 필드. 기존 single-shot 소비자 (외부 wrapper)
  영향 없음 — 두 필드 모두 optional.
- §3.8 RAG types:
  - Answer struct 에 conversation_id / turn_index field 추가.
  - Turn struct 신설 (history 가 prompt 에 들어갈 때 한 turn).
- §3.8 \"Multi-turn behaviour\" 신설 절:
  - kebab-rag::ask vs ask_with_history 두 entry.
  - prompt 빌드 priority: system+question (필수) → retrieved chunks
    (k 줄여 fit) → history (newest 우선, oldest drop).
  - retrieval query expansion (직전 answer 첫 200자 concat).
  - Aborted vs Completed semantics — ask 는 single-shot 이라 cancel
    시 partial token + grounded=false + LlmStreamAborted refusal
    (variant 추가는 p9-fb-15 impl 가 함께).
- docs/wire-schema/v1/answer.schema.json: 두 필드 추가 +
  created_at 에 format: date-time (sibling ingest_progress.v1 와
  일관).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 22:09:34 +00:00
bffe609f9d Merge pull request 'chore(tasks): mark p9-fb-04 completed' (#58) from chore/p9-fb-04-status-completed into main 2026-05-02 21:40:50 +00:00
b7f95cd694 chore(tasks): mark p9-fb-04 completed (PR #57 merged)
Plan task 6 follow-up flip — separate one-line commit so spec history
reflects reality.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 21:40:22 +00:00
6d5c98bf87 Merge pull request 'feat: ingest cooperative cancellation (p9-fb-04)' (#57) from feat/p9-fb-04-cancel into main 2026-05-02 21:39:54 +00:00
6260df5b30 review(회차1): SIGNAL_COUNT lifetime 명시 + cancel-mid race 코멘트
회차 1 actionable 2건 반영 + 1건 (CLI Ctrl-C integration test)
은 본 PR 에서 별도 task 로 미룸 (signal handler subprocess test 의
flaky 위험 + facade 3 PASS + tui lib 3 PASS 가 안정 surface).

- cancel.rs::install_sigint_cancel: SIGNAL_COUNT 위에 process-lifetime
  invariant 코멘트 — multi-install 차단 (ctrlc::set_handler) 덕분에
  reset 불필요. 미래 다중 caller 가 같은 cancel token 공유하려면
  install 함수 분리 필요.
- ingest_cancel.rs::cancel_mid_loop: redundant `report.new == 1 || 0
  || 2` 제거, race timing 의도 코멘트로 대체 (0=listener 승, 1=first
  only, 2=extra slipped in 모두 valid; 3 = cancel never propagated
  = 유일한 fail).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 21:39:39 +00:00
fa02a7c68d feat: ingest cooperative cancellation (p9-fb-04)
Ctrl-C / Esc 가 ingest 를 즉시 중단. 현재 in-flight asset 마무리 후
이후 asset 미실행, IngestEvent::Aborted { partial_counts } 발신,
Ok(IngestReport) 정상 반환 (Err 아님). 부분 commit 보존, 다음 ingest
가 idempotent 재개.

신규 facade: kebab-app::ingest_with_config_cancellable(.., progress,
cancel: Option<Arc<AtomicBool>>). 기존 _progress 가 cancel=None
forwarding wrapper. asset loop 시작 boundary 마다 atomic load —
true 면 break + Aborted emit + 정상 종료. Lock 없음.

CLI: ctrlc crate 신규 dep. SIGINT handler 가 첫 신호에 cancel.store(true)
+ stderr hint, 두 번째 신호에 std::process::exit(130) (canonical SIGINT
exit code). install_sigint_cancel() helper 가 Arc<AtomicBool> 반환,
Cmd::Ingest 가 facade 에 전달.

TUI: IngestState 에 cancel: Arc<AtomicBool> field 추가 (회차 1 review
결과의 reshape 정확). start_ingest 가 둘 다 만들어 worker 에 clone
move. cancel_running_ingest(&app) helper — Esc / Ctrl-C 가
ingest 진행 중일 때만 cancel 우선, 그 외에는 quit.

Test:
- 3 facade integration (cancel-before / cancel-mid / no-cancel
  default).
- 3 tui lib unit (cancel_running_ingest no-state / in-flight /
  terminated).

Plan 갱신: p9-fb-04 status planned → in_progress. 머지 후 한 줄
commit 으로 completed flip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 21:36:17 +00:00
82ca380583 Merge pull request 'chore(tasks): mark p9-fb-03 completed' (#56) from chore/p9-fb-03-status-completed into main 2026-05-02 20:48:06 +00:00
d2944de754 chore(tasks): mark p9-fb-03 completed (PR #55 merged)
Plan task 6 follow-up flip — separate one-line commit so spec history
reflects reality.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 20:47:11 +00:00
3177ba01a4 Merge pull request 'feat(tui): TUI background ingest worker + status bar (p9-fb-03)' (#55) from feat/p9-fb-03-tui-bg into main 2026-05-02 20:46:25 +00:00
62929efdbd review(회차1): cancel_tx slot 제거 — dead-channel shim 회피
회차 1 의 설계 결함 지적 반영. 원래 IngestState 에 cancel_tx:
Sender<()> 만 두고 receiver 는 start_ingest 안에서 즉시 drop —
실제 send() 호출 시 항상 Err(SendError) 인 dead channel 이 됨.
\"slot 만 정의\" 의도였으나 실용 가치 0 + CLAUDE.md 의 backward-
compat shim 금지 룰 위반.

수정:
- IngestState 에서 cancel_tx field 제거.
- start_ingest 의 cancel channel allocation 제거.
- doc comment 갱신 — p9-fb-04 가 (cancel_tx, cancel_rx) pair 동시
  추가 + receiver 를 worker thread 로 move 하는 형태로 reshape 한다고
  명시.
- test fresh_state helper 도 cancel_tx 인자 제거.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 20:46:10 +00:00
474b776c09 feat(tui): TUI background ingest worker + status bar (p9-fb-03)
Library 의 `r` 키가 `kebab_app::ingest_with_config_progress` 를
spawned thread 에서 호출. run loop 가 매 frame 마다 progress channel
drain → 화면 하단 status bar 1 줄 갱신. blocking 하지 않음.

신규:
- crates/kebab-tui/src/app.rs: `IngestState` struct (rx + counts +
  current_path + started_at + terminal_at + aborted + thread +
  cancel_tx) + `App.ingest_state` slot + `TERMINAL_LINE_HOLD_SECS`.
- crates/kebab-tui/src/ingest_progress.rs: `start_ingest` (worker
  spawn + channel allocation), `drain_progress` (try_recv loop),
  `apply_event` (per-kind counter accumulation + Completed/Aborted
  marking), `status_line` (사람-친화 텍스트), `ready_to_clear`
  (3 초 hold).
- 키 cheatsheet: Library footer 에 `r=ingest` 추가.

Run loop:
- 매 tick `drain_progress` + `ready_to_clear` 체크 → terminal 후
  3 초 경과 시 slot drop + worker 스레드 join + Library refresh
  큐.
- Layout: ingest_state Some 일 때 footer 위에 status line 1 줄
  추가 (있을 때만, 평시 영향 0).
- status line: scanning 중 / 진행 (idx/total %, current path,
  elapsed) / 완료 (✓) / abort (✗) 4 모드.

Cancel wiring (p9-fb-04) 의 `IngestState.cancel_tx: Sender<()>`
slot 은 정의만 — 본 PR 에서 sender 보유, send 호출 X.

Test:
- 10 lib unit (apply_event 분기 5 / status_line 4 / ready_to_clear 2).
- 기존 15 tui test 회귀 0.

Plan 갱신:
- p9-fb-03: status `planned` → `in_progress`. 머지 후 한 줄
  commit 으로 `completed` flip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 20:42:32 +00:00
fdd1ed3c56 Merge pull request 'chore(tasks): mark p9-fb-02 completed' (#54) from chore/p9-fb-02-status-completed into main 2026-05-02 20:28:11 +00:00
b4c178a18c chore(tasks): mark p9-fb-02 completed (PR #53 merged)
Plan task 6 follow-up flip — separate one-line commit so spec history
reflects reality.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 20:27:44 +00:00
f8584a26f3 Merge pull request 'feat(cli): kebab ingest progress display (p9-fb-02) + p9-fb-01 status flip' (#53) from feat/p9-fb-02-cli-progress into main 2026-05-02 20:01:12 +00:00
1a8fd08f6c review(회차1): nit 3건 — 의도 문서화 (best-effort IO 의도 + bar invariant + display join)
회차 1 actionable 모두 동작 변경 없음, 의도 명시.

- progress.rs handle_human: doc-comment 한 단락 — `let _ = writeln!`
  의 IO error swallow 와 `bar.as_ref()` None 분기 silent skip 의
  두 best-effort 의도 + §2.4a ordering invariant (ScanStarted 가
  bar 를 lazy 초기화) 명시.
- main.rs Cmd::Ingest: `let _ = display_handle.join();` 위에 한 줄
  trailing comment — Result<Result<(), anyhow::Error>, Box<dyn Any>>
  를 모두 discard 하는 이유 (display thread 의 에러 / panic 이
  ingest exit code 에 영향 없어야 함).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 20:00:54 +00:00
e613236d60 feat(cli): kebab ingest progress display (p9-fb-02) + p9-fb-01 status flip
`kebab ingest` 가 진행 상황을 사용자에게 보여주는 두 surface 추가:

- **사람 모드 (TTY)**: indicatif `ProgressBar` on stderr — scan 중에는
  spinner, ScanCompleted 후 bar 로 전환, 매 asset 마다 message 갱신.
- **사람 모드 (non-TTY, CI/pipe)**: indicatif draw target 을 hidden
  으로 두고 stderr 에 한 줄씩 (`ingest: scanning`, `ingest: 1/N path`,
  `ingest: complete (...)`).
- **`--json` 모드**: stderr 비우고 stdout 에 line-delimited
  `ingest_progress.v1` JSON 을 emit. 마지막 줄은 기존
  `ingest_report.v1` 그대로 (외부 wrapper backward-compat).

구현:

- 신규 `crates/kebab-cli/src/progress.rs` — `ProgressMode::{Json,
  Human { tty }}`, `ProgressDisplay` (background thread 가 channel
  drain + 모드별 render), `now_rfc3339` helper. mode 가 무엇이든 ts
  는 wire emit 시점에 stamp.
- `crates/kebab-cli/src/wire.rs` 에 `wire_ingest_progress` 추가.
  serde tag (`kind`) 위에 `schema_version` + `ts` 두 필드 더해 spec
  §2.4a wire shape 완성.
- `Cmd::Ingest` 핸들러: mpsc channel 만들고 background thread 가
  display 돌리는 동안 main 이 `ingest_with_config_progress` 호출.
  ingest 반환 시 Sender drop → display thread 정상 종료. join 후
  최종 ingest_report 출력.
- 새 dep: `indicatif` 0.17 (TTY 전용 진행 바, non-TTY/--json 에서는
  hidden draw target).

Test:

- 3 lib unit (mode resolution + RFC 3339 round-trip).
- 3 integration (--json line-delimited / non-TTY stderr text /
  ts+kind 검증). 16 PASS 전체 회귀 0.

Plan 갱신:

- p9-fb-01: status `in_progress` → `completed` (PR #52 머지 후속).
- p9-fb-02: status `planned` → `in_progress`. 머지 후 별도 한 줄
  commit 으로 `completed` flip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 19:57:02 +00:00
7c6009f7e7 Merge pull request 'feat(app): IngestEvent + ingest_with_config_progress (p9-fb-01)' (#52) from feat/p9-fb-01-progress into main 2026-05-02 19:47:48 +00:00
538f028575 review(회차1): emit 의 trace 메시지 단순화
회차 1 nit 반영. 'suppressing further sends would require caller
cooperation' 의 두 번째 절이 의미 불명확 — 단순 'event discarded
(best-effort send per ingest_progress contract)' 로 단축. 사실 +
영향만 기록, caller 가 어떻게 cooperate 하는지 추측은 제거.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 19:47:22 +00:00
eb331f9b29 feat(app): add IngestEvent + ingest_with_config_progress (p9-fb-01)
Streaming progress channel for ingest. Facade emits one IngestEvent per
step boundary into an optional `mpsc::Sender<IngestEvent>` injected by
the caller. CLI (p9-fb-02), TUI (p9-fb-03), and future desktop UI all
consume the same stream.

신규:
- crates/kebab-app/src/ingest_progress.rs: `IngestEvent` enum (`#[serde(tag
  = "kind", rename_all = "snake_case")]` matching wire schema
  ingest_progress.v1) + `AggregateCounts` struct + `media_label` helper
  + best-effort `emit` helper.
- ingest_with_config_progress(cfg, scope, summary_only, progress) —
  존재 시 `mpsc::Sender<IngestEvent>` 로 ScanStarted → ScanCompleted →
  (AssetStarted < AssetFinished)* → Completed 발신. dropped receiver
  는 silent absorb (hot path stall 금지).
- 기존 ingest_with_config 가 `progress=None` forwarding wrapper.

미적용 (계약 상 향후 task 가 채움):
- IngestEvent::Aborted: cancel token wiring 은 p9-fb-04.
- embed_batch_started / embed_batch_finished: spec 의 \"asset 이벤트 사이
  임의 위치\" 에 해당. v1 단순화 — asset 단위 해상도면 CLI / TUI 충분.

Test:
- 6 lib unit (media_label / serde discriminator / emit corner cases).
- 3 integration (이벤트 sequence 가 §2.4a invariant 준수 / forwarding
  wrapper / dropped receiver tolerance).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 19:44:34 +00:00
85c535d220 Merge pull request 'spec(p9-fb-01..03): ingest progress events + cancellation in §2.4a / §10' (#51) from spec/p9-fb-01-progress-events into main 2026-05-02 19:19:07 +00:00
9aa7459e87 review(회차1): nit 3건 반영
- §10 long-running 절 끝 빈 줄 3 → 1 (다른 절 사이 일관)
- wire schema + §2.4a 예제 JSON: kind_result → result (top-level
  kind 와의 모호성 제거; ingest_report.v1.items[].kind 와 짝)
- wire schema 의 ts 필드: format: \"date-time\" 추가 (RFC 3339
  자동 검증, wrapper 가 다른 format emit 시 즉시 잡힘)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 19:18:32 +00:00
5ef8598e5c spec(p9-fb-01..03): ingest progress events + cancellation in §2.4a / §10
도그푸딩 후 추가된 long-running 작업 진행 표시 + cancel 정책을 frozen
design 에 명시. p9-fb-01/02/03 (ingest progress callback / CLI display
/ TUI background) 의 spec PR — impl PR 들이 이어진다.

변경:
- docs/wire-schema/v1/ingest_progress.schema.json (신규):
  line-delimited streaming event schema. discriminated by `kind`
  (scan_started → scan_completed → asset_started → asset_finished* →
  embed_batch_* → completed | aborted). 마지막 줄은 기존
  ingest_report.v1 그대로 (외부 wrapper backward-compat).
- 2026-04-27-kebab-final-form-design.md §2.4a (신규):
  IngestProgressEvent 절. 이벤트 ordering / aborted 의 idempotency /
  CLI 의 stderr vs stdout 분리 / TUI · desktop 의 in-memory 소비.
- 2026-04-27-kebab-final-form-design.md §10:
  long-running 작업 (ingest, future eval run, RAG streaming, embed
  batch) 의 두 invariant — progress 의 단일 source / cooperative
  cancel + step boundary. trait (§7.2) 시그니처는 무영향 — facade
  hidden parameter 로 추가.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 19:14:37 +00:00
8d8544546c Merge pull request 'chore(p9-fb-06): Cargo.lock sync + spec status completed' (#50) from chore/p9-fb-06-followup into main 2026-05-02 18:49:55 +00:00
8879bd4de2 chore(p9-fb-06): sync Cargo.lock for tempfile dev-dep + flip spec to completed
Two follow-ups after PR #49 (kebab reset) merged:
- Cargo.lock: kebab-cli's new dev-dep `tempfile` was committed in the
  feature PR but the lockfile entry was not regenerated, leaving main
  with a stale lock. `cargo metadata` regenerates the one-line addition
  to kebab-cli's dependency list.
- tasks/p9/p9-fb-06-data-reset-command.md: status `in_progress` →
  `completed`, per the plan's task 6 commitment to flip in a separate
  one-line commit only after the implementation PR merges.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 18:47:23 +00:00
b6203514c5 Merge pull request 'feat(cli): kebab reset (p9-fb-06)' (#49) from feat/p9-fb-06-reset into main 2026-05-02 18:45:22 +00:00
565caebec6 review(회차1): 회차 1 critical + nit 반영
- (critical) embeddings.rs: truncate_embedding_records 위치 이동.
  mark_embedding_records_committed 함수 위에 끼워 넣었더니 위쪽
  mark_committed 의 14 줄짜리 doc comment (`WHERE status='pending'`
  의 design rationale 등) 가 truncate 의 doc 으로 흡수되고
  mark_committed 자체는 doc 없이 남는 버그. impl block 끝 (mark_committed
  의 닫는 } 다음) 으로 옮겨 plan 의 원래 의도와도 일치.
- (nit) tests/reset_cli.rs: removed_paths 의 idempotency 검증 보강.
  data dir 은 reported, cache dir 은 omit (생성 안 했으니)
  되어야 함을 strict 하게 assert. state dir 은 logging init 의
  side-effect 로 자동 생성되어 둘 다 가능하므로 허용.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 18:43:25 +00:00
9d96d603b0 chore(tasks): mark p9-fb-06 in_progress
Final flip to completed lands in a separate one-line commit AFTER PR
merges so spec history reflects reality.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 18:39:12 +00:00
1df8731006 docs: kebab reset in README 명령 table + cleanup paragraph + HANDOFF
3-doc sync rule (CLAUDE.md): user-visible CLI surface change → README
and HANDOFF land in the same PR. ARCHITECTURE.md is not touched —
kebab reset doesn't move the crate graph or any locked-in technical
decision.

p9-fb-06 task 5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 18:33:31 +00:00
286ac68c12 feat(cli): add kebab reset command with TTY confirm gate
Mutually-exclusive scope flags (--all / --data-only / --vector-only /
--config-only via clap ArgGroup) plus --yes for non-interactive use.
Aborts with exit 2 when stdin is non-interactive and --yes is missing
— silent destruction is forbidden. Self-contained 20-line confirm
prompt (no new dep; std::io::IsTerminal).

Integration tests exercise the bin in a fresh subprocess against
tempdir-rooted XDG env to keep the assertions independent of the host
config:
- --data-only --yes wipes data + cache + state, preserves config.
- non-TTY without --yes exits 2 with the documented hint.
- --json emits reset_report.v1 schema with snake_case scope.
- conflicting --all + --data-only rejected by clap before any wipe.

Plan deviation (task 4): the data-only test used to write a stub
config.toml containing only `schema_version = 1`, but Config parsing
requires every section. Switched to a marker file in the cfg dir +
the documented Config::load(None)→defaults fallback.

p9-fb-06 task 4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 18:32:14 +00:00
233b708624 feat(cli/wire): add reset_report.v1 schema + wire_reset helper
JSON Schema 7 frozen surface for `kebab reset --json`. Mirrors the
ResetReport struct from kebab-app. Test asserts schema_version tag,
scope serialization (snake_case enum), removed_paths array, and
embedding_rows_truncated u64.

p9-fb-06 task 3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 18:28:24 +00:00
c3a48a88a5 feat(app): add reset module — scope, path enumeration, execute
Provides the wipe core for `kebab reset`. Mutually-exclusive ResetScope
variants (All / DataOnly / VectorOnly / ConfigOnly), pure path
enumeration for the confirm UI preview, byte-size estimator, and an
execute helper that removes paths off-disk + truncates
embedding_records when scope is VectorOnly.

Plan deviation from the original spec (task 2):
- Original `truncate_embeddings` helper opened SqliteStore via path and
  ran a separate COUNT query through `lock_conn` (private). Both APIs
  are unavailable from outside the crate, so the helper now opens the
  store via `SqliteStore::open(&Config)` and lets
  `truncate_embedding_records` (task 1) report the deleted count
  directly.
- Skipped the XDG-env-overriding unit test from the original plan to
  avoid race conditions with sibling tests; the equivalent integration
  coverage moves up to the CLI tests in task 4 where each invocation
  runs in a fresh process.
- Added an FS-touching unit test (`estimate_size_sums_file_lengths`)
  to cover the read-side of `estimate_size_bytes` against a tempdir.

p9-fb-06 task 2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 18:23:42 +00:00
cf65afaef0 feat(store-sqlite): add truncate_embedding_records helper
Wipes every row from embedding_records and returns the deleted row
count. Used by the upcoming `kebab reset --vector-only` to keep SQLite
consistent after the on-disk Lance store is removed.

Plan deviation from the original spec (task 1):
- Original test plan opened SqliteStore with a raw path; the actual
  signature is `SqliteStore::open(&Config)`, so the integration test
  builds a Config with `storage.data_dir` pointed at a tempdir.
- Original return type was Result<()>; bumped to Result<u64> so the
  caller (kebab-app::reset) can surface the truncated count in the
  reset_report.v1 wire payload without a separate COUNT query.

p9-fb-06 task 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 18:08:22 +00:00
8784606028 Merge pull request 'docs(p9): 도그푸딩 피드백을 20개 task spec + reset 구현 plan으로 분해' (#48) from docs/p9-dogfooding-decomposition into main 2026-05-02 18:03:55 +00:00
d445df515d review(회차2): nit 2건 — 권장 실행 순서 표기 명확화
회차 2 의 가독성 nit 반영:
- '5번 debounce' → 'p9-fb-08 debounce' (task ID 명시)
- '12 와 같은 batch 가능, 11 prerequisite' → 'p9-fb-12 와 같은 batch 가능, p9-fb-11 의 prerequisite'
- 13번 항목 'prerequisite for 18' → 'prerequisite for p9-fb-18' (일관 패턴)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 18:02:54 +00:00
a757e2cdb3 review(회차1): 회차 1 지적 5건 반영
- p9-dogfooding-feedback.md item 14: README 오타 (READE → README)
- p9-fb-11.md frontmatter: depends_on=[p9-fb-14] 추가 (14.unblocks 와 양방향 정합)
- p9-fb-01.md Behavior contract: '14 번과 wiring' 모호 cross-ref 정정 — cancel wiring 은 p9-fb-04, TUI 신호는 p9-fb-03
- plan File Structure: 'tasks/HOTFIXES.md — n/a (skip)' 자기모순 제거 → 별도 HOTFIXES 절로 분리
- plan task 4 handler: let _ = data_only; 제거, pattern binding 자체를 data_only: _ 로 변경 (관용적)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 18:01:14 +00:00
5428412688 docs(p9): decompose dogfooding feedback into 20 task specs + reset plan
P9-1~P9-4 머지 후 사용자가 직접 도그푸딩 하며 수집한 16 항목 UX
피드백을 20 개 single-PR 사이즈 task spec 으로 분해. 각 spec 은
frontmatter (depends_on / unblocks / source_feedback), Goal,
Allowed deps, Public surface, Behavior contract, Test plan, DoD,
Out of scope 절 포함.

추가:
- p9-fb-01 ~ 20-*.md: 분해된 task spec 20 개
- p9-dogfooding-feedback.md: master index + 우선순위 + 권장 실행 순서
  + spec PR vs impl PR 절
- INDEX.md: p9-fb-01 ~ 20 link 추가
- docs/superpowers/plans/2026-05-02-p9-fb-06-reset-command.md:
  첫 후속 작업 (kebab reset 명령) 의 6-task 구현 plan
- .gitignore: .worktrees/ 추가 (superpowers worktree skill 용)

피드백 항목 → task spec 매핑은 p9-dogfooding-feedback.md 의 표 참조.
실행 시작 task: p9-fb-06 (reset 명령) — 도그푸딩 막힘 강도 1위.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 17:54:15 +00:00
8691bfe381 Merge pull request 'fix(config + tilde): LLM default → gemma4:e4b + workspace.root ~ expansion' (#47) from fix/config-defaults-and-tilde-expansion into main
Reviewed-on: #47
2026-05-02 16:39:45 +00:00
893287a5a3 fix(config + tilde): LLM default → gemma4:e4b + workspace.root ~ expansion 일관성
도그푸딩 시 사용자 결정 (2026-05-02): 텍스트 LLM 기본을 gemma4 계열로
통일. OCR/caption 어댑터 (P6-2/P6-3) 가 이미 gemma4:e4b 사용 중 —
사용자가 한 family 만 pull 하면 ingest + ask 모두 작동.

같이 발견된 ~ expansion 불일치:
- kebab-source-fs::connector 는 expand_tilde 사용 (walk 정상)
- kebab-app::ingest_one_image_asset / ingest_one_pdf_asset 은 직접
  PathBuf::from → ~ 미확장 → ExtractContext 에 ~/KnowledgeBase
  그대로 전달
- kebab-tui::search::handle_key_search 의 editor jump 도 동일 →
  의미 없는 경로 spawn

Fix:
- Config::defaults().models.llm.model = \"gemma4:e4b\". OCR/caption
  family 통일 코멘트 추가.
- kebab-app 의 image / pdf 분기 두 곳 모두 expand_tilde 호출.
- kebab-tui::search jump 가 kebab_config::expand_path(.., \"\") 사용
  (expand_path 는 ~ / ${XDG_DATA_HOME} / {data_dir} 모두 처리하는
  정식 helper).

Caveat: kebab-app::expand_tilde 와 kebab-config::expand_path 가 별도
정의. 통합은 P+ task.

Docs (sync rule):
- README 사전 요구 절: gemma4:e4b 기본 + 더 큰 variant override 안내.
- docs/ARCHITECTURE 핵심 결정 표: LLM default qwen2.5:7b-instruct →
  gemma4:e4b.
- docs/SMOKE: ollama pull 예시 + KEBAB_MODELS_LLM_MODEL env 예시
  qwen2.5:32b → gemma4:26b.
- HOTFIXES: 새 entry (\"Config defaults: LLM = gemma4:e4b + workspace.root
  tilde expansion\").
- Memory: project_llm_default.md 신설, MEMORY.md 인덱스 추가.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 16:34:24 +00:00
e0b2ae4db6 Merge pull request 'feat(kebab-tui): P9-4 Inspect pane' (#46) from feat/p9-4-tui-inspect into main
Reviewed-on: #46
2026-05-02 16:00:52 +00:00
8c6d29cc2d review(p9-4): 회차 1 지적 반영
blocks / embeddings 섹션의 count 라인이 collapse 검사 *밖* 에서 push
되어 collapsed 상태에서 부분만 사라지던 일관성 깨짐. fix: count 를
section header 에 inline 으로 (`▾ blocks (N)`, `▾ embeddings (N)`),
body 만 collapse 검사 안. 새 helper `push_section_header_with_count`
가 둘 다 통일.

회귀 테스트 보강:
- doc_view_collapse_hides_section_body: collapsed 상태에서 \"blocks (2)\"
  inline count 표시 + \"Heading L1\" body 숨김 검증.
- chunk_view_renders_text_and_block_ids: \"embeddings (2)\" inline
  count 검증.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 15:44:14 +00:00
b6e0ab352f feat(kebab-tui): P9-4 Inspect pane — doc/chunk detail with collapsible sections
Library Enter / Search 'i' 가 Inspect 진입. Doc 또는 Chunk 단일 view 로
metadata / provenance / blocks (doc) 또는 spans / text / embeddings (chunk)
6 section 을 collapsible 로 표시. Esc/q 로 originating pane 으로 복귀.

핵심:
- InspectTarget enum (`Doc(DocumentId) | Chunk(ChunkId)`).
- InspectState 본체 (`app.rs`) — target / doc / chunk / collapsed
  HashSet / scroll / return_to / needs_fetch / loading.
- `src/inspect.rs`:
  - `render_inspect` — target 종류별 render_doc / render_chunk 분기,
    section header 가 collapse marker (▾/▸) 표시. metadata.user JSON
    pretty-printed.
  - `handle_key_inspect`: j/k / Down/Up scroll. PageDown/PageUp 10 row.
    c = toggle all sections (v1 simplification). Esc/q = SwitchPane(return_to).
  - `enter_inspect(state, target, return_to)` helper — Library 와 Search
    공통 entry point.
  - run-loop hook `refresh_inspect` — needs_fetch 면 lazy
    inspect_doc_with_config / inspect_chunk_with_config.
- run.rs: Pane::Inspect arm 이 handle_key_inspect + render_inspect.
  Idle tick 마다 refresh_inspect. SwitchPane(Inspect) lazy init.
- Library: Enter 가 enter_inspect(Doc(selected)) 호출 후 SwitchPane.
- Search: 'i' (plain modifier) 가 enter_inspect(Chunk(selected_hit))
  호출 후 SwitchPane. typing 'i' (\"instance\") 와 충돌 가드.

테스트 12개 (`tests/inspect.rs`, TestBackend) — Esc 가 return_to 사용
/ q 도 동작 / j/k scroll bounds / PgUp PgDn ±10 / c 일괄 toggle / no
target hint / loading / doc view header+metadata+provenance+blocks /
collapse hides body / chunk view text+block_ids / no slot →
SwitchPane(Library) / enter_inspect helper sets fields.

Spec deviation (HOTFIXES `2026-05-02 P9-4`):
- `render_inspect<B: Backend>` generic 제거 (P9-1/2/3 와 동일).
- Search `i` 키 추가 (P9-2 spec 에 없었음, P9-4 retroactive 추가).
- `c` 일괄 collapse — spec 의 \"focus 기반 selective collapse\" 는 P+.

Docs (sync rule):
- README: TUI 행 \"4 패널\" + Quick start 코멘트.
- HANDOFF: 한 줄 요약 + Phase status (P9 3/5 → 4/5) + deviation 한 줄.
- HOTFIXES: P9-4 entry.
- tasks/p9/p9-4 status: completed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 15:41:11 +00:00
8396e22ad3 Merge pull request 'feat(kebab-tui): P9-3 Ask pane' (#45) from feat/p9-3-tui-ask into main
Reviewed-on: #45
2026-05-02 15:31:57 +00:00
ad7bd7d309 review(p9-3): 회차 1 지적 반영
Esc 후 재질문 시 detached prior worker + 새 worker 동시 in-flight 가능
했음. Ollama endpoint 에 두 요청 동시 발사 → 응답 시간 두 배 + stream
혼동. spawn_ask_worker 진입 시 `s.thread.is_some()` 검사 추가, 이전
worker 가 still alive 면 Enter 무시. input bar 의 busy 텍스트 가 세
상태 (streaming / awaiting prior / idle) 분리 표시 — 사용자가 Enter
가 왜 안 먹히는지 즉시 확인.

회귀 테스트 `enter_with_detached_prior_thread_is_blocked` 추가 — never-
ending 더미 thread 를 hand-install 후 Enter no-op 검증, 종료 시 thread
take() 로 leak 명시 (test process 종료 시 OS 가 reap).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 15:27:39 +00:00
f08fefec1d feat(kebab-tui): P9-3 Ask pane — streaming answer + citation panel + explain toggle
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>
2026-05-02 15:24:26 +00:00
0bcc4c5132 Merge pull request 'feat(kebab-tui): P9-2 Search pane' (#44) from feat/p9-2-tui-search into main
Reviewed-on: #44
2026-05-02 15:05:16 +00:00