feat(kebab-app + kebab-cli): p9-fb-18 CLI ask --session multi-turn #82

Merged
altair823 merged 2 commits from feat/p9-fb-18-cli-session into main 2026-05-03 06:25:37 +00:00
Owner

요약

도그푸딩 item 14 — CLI 에서도 multi-turn 가능하도록 kebab ask --session <id> 추가. p9-fb-17 의 ChatSessionRepo 위에 build, 첫 호출 세션 자동 생성, 이후 호출이 prior turns 를 history 로 받아 follow-up.

변경

  • App::ask_with_session(session_id, query, opts) -> Answer — load → list_turns → retriever stack → RagPipeline::ask_with_history → 첫 호출이면 session row 자동 생성 → turn append
  • helpers: first_question_title (NFC + trim + 40 cap, fallback "untitled") + blake3_truncate (32-hex turn_id)
  • ask_with_session_with_config facade
  • CLI --session <id> flag in Cmd::Ask
  • 에러 정책: persistence 실패 시 warn 로그 남기고 answer 그대로 반환 (사용자 컴퓨트 보존)

Out of scope

  • --repl (stdin loop) — spec 명시이나 stdin fixture 부담으로 deferral
  • session list / show / delete 관리 명령 (spec 의 Out of scope)

테스트

  • 4 신규 unit (first_question_title 3 + blake3_truncate 1)
  • cargo test --workspace --no-fail-fast -j 1 exit 0
  • cargo clippy --workspace --all-targets -- -D warnings clean

문서

  • README kebab ask 행 + 외부 AI 통합 절
  • HANDOFF entry
  • spec status planned → in_progress
## 요약 도그푸딩 item 14 — CLI 에서도 multi-turn 가능하도록 `kebab ask --session <id>` 추가. p9-fb-17 의 ChatSessionRepo 위에 build, 첫 호출 세션 자동 생성, 이후 호출이 prior turns 를 history 로 받아 follow-up. ## 변경 - **`App::ask_with_session(session_id, query, opts) -> Answer`** — load → list_turns → retriever stack → `RagPipeline::ask_with_history` → 첫 호출이면 session row 자동 생성 → turn append - **helpers**: `first_question_title` (NFC + trim + 40 cap, fallback `"untitled"`) + `blake3_truncate` (32-hex turn_id) - **`ask_with_session_with_config`** facade - **CLI `--session <id>` flag** in `Cmd::Ask` - **에러 정책**: persistence 실패 시 warn 로그 남기고 answer 그대로 반환 (사용자 컴퓨트 보존) ## Out of scope - `--repl` (stdin loop) — spec 명시이나 stdin fixture 부담으로 deferral - session list / show / delete 관리 명령 (spec 의 Out of scope) ## 테스트 - 4 신규 unit (first_question_title 3 + blake3_truncate 1) - `cargo test --workspace --no-fail-fast -j 1` exit 0 - `cargo clippy --workspace --all-targets -- -D warnings` clean ## 문서 - README `kebab ask` 행 + 외부 AI 통합 절 - HANDOFF entry - spec status planned → in_progress
altair823 added 1 commit 2026-05-03 06:20:46 +00:00
도그푸딩 item 14 — CLI 에서도 multi-turn 가능하도록 `kebab ask
--session <id>` 추가. p9-fb-17 의 ChatSessionRepo 위에 build, 첫 호출
세션 자동 생성, 이후 호출이 prior turns 를 history 로 받아 follow-up.
external AI integration (Claude Code skill / MCP) 도 같은 facade 로
stateful 대화 가능.

## 핵심 변경

- **`App::ask_with_session(session_id, query, opts) -> Answer`** —
  load session header → list_turns 로 prior history → 빌드 retriever
  stack (lexical / vector / hybrid 같은 분기) → `RagPipeline::ask_
  with_history` 호출 → 첫 호출이면 `chat_sessions` row 자동 생성
  (title = first_question_title) → `chat_turns` 새 row append.
- **`App::first_question_title(question)`** helper — `trim() + nfc()
  + 40 chars cap`, fallback `"untitled"`. unicode-normalization
  workspace dep 재사용.
- **`App::blake3_truncate(input)`** helper — `blake3(session_id ||
  ":" || turn_index)` 의 첫 16 byte 를 u128 으로, format!{:032x} 로
  32-hex `turn_id`.
- **`ask_with_session_with_config`** facade — CLI 진입점.
- **CLI `--session <id>` flag** — `Cmd::Ask` 의 `session: Option<
  String>` field, handler 가 None 이면 `ask_with_config` (기존
  단발), Some(id) 면 `ask_with_session_with_config` 호출.
- **에러 정책**: session create / turn append 실패 시 warn 로그
  남기고 answer 는 그대로 반환 — 사용자가 답변 받은 컴퓨트를 잃지
  않음. 영속성 실패가 답변 응답을 가로막지 않는 conservative shape.

## 테스트

- `App::first_question_title` 3 unit (trim + cap, empty → untitled,
  korean NFD → NFC)
- `App::blake3_truncate` 1 unit (deterministic + distinct across
  varying session/index)
- 워크스페이스 전체 `cargo test --workspace --no-fail-fast -j 1` exit 0
- `cargo clippy --workspace --all-targets -- -D warnings` clean

## 문서

- README `kebab ask` 행: `--session` 안내 + chat_sessions 자동 생성
  + `kebab reset --data-only` wipe 안내
- README **외부 AI 통합** 절: Claude Code skill 이 `--session` 으로
  multi-turn 가능하다는 한 문장 추가
- HANDOFF entry
- spec status planned → in_progress

## Out of scope (spec deviation)

- `--repl` (stdin loop) — spec 명시되어 있으나 stdin fixture 부담
  으로 deferral. 별도 후속 task 또는 `--session` 사용자 경험 회신
  후 결정.
- session list / show / delete 관리 명령 (spec 의 Out of scope).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-reviewer-01 requested changes 2026-05-03 06:22:31 +00:00
Dismissed
claude-reviewer-01 left a comment
Member

회차 1 — multi-turn CLI 진입점 디자인 정확. App::ask_with_session 의 load → ask → create_session(if first) → append_turn 순서 합리적. helpers (first_question_title NFC + 40 cap + untitled fallback, blake3_truncate 32-hex) 도 도큐멘트 잘 됨. 신규 4 unit 테스트 깔끔.

actionable nit 2 건 — (a) retriever-stack 빌드 35+ 줄이 ask 와 중복 (refactor 의도 명시 또는 helper 추출), (b) V005 migration doc 의 'Vec' 표현이 실제 stored type (Vec) 과 어긋남.

회차 1 — multi-turn CLI 진입점 디자인 정확. App::ask_with_session 의 load → ask → create_session(if first) → append_turn 순서 합리적. helpers (first_question_title NFC + 40 cap + untitled fallback, blake3_truncate 32-hex) 도 도큐멘트 잘 됨. 신규 4 unit 테스트 깔끔. actionable nit 2 건 — (a) retriever-stack 빌드 35+ 줄이 ask 와 중복 (refactor 의도 명시 또는 helper 추출), (b) V005 migration doc 의 'Vec<Citation>' 표현이 실제 stored type (Vec<AnswerCitation>) 과 어긋남.

ask_with_session 의 retriever-stack 빌드 (Lexical/Vector/Hybrid 분기, 35+ 줄) 가 ask (line ~256-295) 와 거의 1:1 복사. 수정이 두 군데 따라가야 하고, 한 곳 빠지면 미묘한 분기 차이 (e.g. snippet_chars 변경) 가 생길 수 있습니다.

제안: fn build_retriever(&self, mode: SearchMode) -> Result<Arc<dyn Retriever>> 로 추출. 두 호출 사이트에 전부 단순화 + 미래 retriever 변경이 한 곳만.

현 PR 안에서 추출하지 않고 별 PR 로 미루는 것도 OK — refactor PR 단독으로 깔끔. 다만 본 PR commit message 에 "중복 의도, refactor 후속" 명시는 추가하면 좋겠습니다.

`ask_with_session` 의 retriever-stack 빌드 (Lexical/Vector/Hybrid 분기, 35+ 줄) 가 `ask` (line ~256-295) 와 거의 1:1 복사. 수정이 두 군데 따라가야 하고, 한 곳 빠지면 미묘한 분기 차이 (e.g. snippet_chars 변경) 가 생길 수 있습니다. 제안: `fn build_retriever(&self, mode: SearchMode) -> Result<Arc<dyn Retriever>>` 로 추출. 두 호출 사이트에 전부 단순화 + 미래 retriever 변경이 한 곳만. 현 PR 안에서 추출하지 않고 별 PR 로 미루는 것도 OK — refactor PR 단독으로 깔끔. 다만 본 PR commit message 에 "중복 의도, refactor 후속" 명시는 추가하면 좋겠습니다.
@@ -303,0 +412,4 @@
session_id: session_id.to_string(),
created_at: now_unix,
updated_at: now_unix,
title: Some(title),

코드는 Vec<AnswerCitation> 을 serialize 해서 chat_turns.citations_json 에 저장합니다 (실제 타입은 Answer.citations: Vec<AnswerCitation> + Turn.citations: Vec<AnswerCitation> 으로 일치 — 정확). 다만 V005 migration 의 doc comment 가 "Vec JSON-encoded" 라고 되어 있어 misleading.

-- migrations/V005__chat_sessions.sql:32
-- * `citations_json` carries `Vec<Citation>` so the answer can be
--   redisplayed with the same citation markers a future session
--   sees on resume.

실제로는 Vec<AnswerCitation> (각 AnswerCitation 가 marker + Citation 등 포함). 후속 reader 가 Vec<Citation> 으로 파싱하려고 하면 실패. fix:

-- * `citations_json` carries `Vec<AnswerCitation>` so the answer can be
코드는 `Vec<AnswerCitation>` 을 serialize 해서 chat_turns.citations_json 에 저장합니다 (실제 타입은 `Answer.citations: Vec<AnswerCitation>` + `Turn.citations: Vec<AnswerCitation>` 으로 일치 — 정확). 다만 V005 migration 의 doc comment 가 "Vec<Citation> JSON-encoded" 라고 되어 있어 misleading. ``` -- migrations/V005__chat_sessions.sql:32 -- * `citations_json` carries `Vec<Citation>` so the answer can be -- redisplayed with the same citation markers a future session -- sees on resume. ``` 실제로는 `Vec<AnswerCitation>` (각 AnswerCitation 가 marker + Citation 등 포함). 후속 reader 가 `Vec<Citation>` 으로 파싱하려고 하면 실패. fix: ``` -- * `citations_json` carries `Vec<AnswerCitation>` so the answer can be ```
altair823 added 1 commit 2026-05-03 06:25:18 +00:00
- `App::build_retriever(mode) -> Result<Arc<dyn Retriever>>` 추출.
  `ask` 와 `ask_with_session` 모두 사용. 35+ 줄 retriever stack 중복
  제거 — 미래 retriever 변경이 한 곳만.
- V005 migration `chat_sessions.sql` 의 `citations_json` doc 수정:
  `Vec<Citation>` → `Vec<AnswerCitation>` (실제 stored type 과 일치).
  AnswerCitation 가 marker + Citation 등 포함하므로 deserialize 시
  type mismatch 회피.

15 app lib + 9 store chat_sessions + clippy 통과.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-reviewer-01 approved these changes 2026-05-03 06:25:32 +00:00
claude-reviewer-01 left a comment
Member

회차 2 — nit 2 건 깔끔히 반영.

  • App::build_retriever helper 추출, ask + ask_with_session 둘 다 사용 (중복 35+ 줄 → 1 호출). 미래 retriever 변경 단일 지점.
  • V005 migration doc citations_json: Vec → Vec (실제 stored type 과 일치).

추가 지적 없음. 머지 OK.

회차 2 — nit 2 건 깔끔히 반영. - App::build_retriever helper 추출, ask + ask_with_session 둘 다 사용 (중복 35+ 줄 → 1 호출). 미래 retriever 변경 단일 지점. - V005 migration doc citations_json: Vec<Citation> → Vec<AnswerCitation> (실제 stored type 과 일치). 추가 지적 없음. 머지 OK.
altair823 merged commit cce2fbce86 into main 2026-05-03 06:25:37 +00:00
altair823 deleted branch feat/p9-fb-18-cli-session 2026-05-03 06:25:38 +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#82