도그푸딩 item 13/14 (multi-turn 영속화) — TUI Ask 의 "이전 대화
이어가기" + 향후 CLI `--session foo` (p9-fb-18) backing store. session
header + per-turn 두 테이블, ON DELETE CASCADE 로 reset --data-only 가
한꺼번에 wipe.
## 핵심 변경
- **SQLite V005 migration** `chat_sessions` (session_id PK + created_at
+ updated_at + title + config_snapshot_json) + `chat_turns` (turn_id
PK + session_id FK ON DELETE CASCADE + turn_index + question +
answer + citations_json + created_at + UNIQUE(session_id, turn_index))
+ `idx_chat_turns_session(session_id, turn_index)`. 모두 `STRICT`.
- **`kebab_core::ChatSessionRepo`** trait (6 method): create_session /
get_session / list_sessions(limit, ORDER BY updated_at DESC) /
delete_session / append_turn / list_turns(ORDER BY turn_index ASC)
- **`kebab_core::{ChatSessionRow, ChatTurnRow}`** structs — Serialize
+ Deserialize 둘 다 (CLI / wire 출력 호환)
- **`kebab-store-sqlite::SqliteStore`** impl 신규 모듈 `chat_sessions.rs`.
`append_turn` 이 insert + parent updated_at bump 같은 connection
에서 처리.
- **frozen design §5** 에 §5.7a chat_sessions / chat_turns 절 신설
(full schema + trait 메서드 6 개 명시).
## HOTFIXES (V004 → V005)
spec p9-fb-17 의 `V004__chat_sessions.sql` 가 p9-fb-19 의
`V004__kv.sql` (이미 머지) 와 refinery migration number 충돌. 무중단
정정: `V005__chat_sessions.sql` 로 시프트. schema / 동작 동일, 파일명
만 이동. HOTFIXES entry 추가.
## 테스트
- 9 신규 integration unit (create/get roundtrip, missing→None, PK
collision error, append+list ordered, dup turn_index error,
append bumps updated_at, delete CASCADE turns, list_sessions
ORDER BY updated_at DESC, list_sessions LIMIT)
- workspace 전체 `cargo test --workspace --no-fail-fast -j 1` exit 0
- `cargo clippy --workspace --all-targets -- -D warnings` clean
## 문서
- frozen design §5.7a 신설
- HANDOFF: 2026-05-03 entry
- HOTFIXES: V004 → V005 rename rationale
- spec status planned → in_progress
## Out of scope
- session 검색 / 필터 UI (p9-fb-18 의 `kebab ask --session list`
같은 admin command 가 후속)
- 다른 store backend (postgres 등) — trait 만 정의, impl 은 SQLite
unblocks p9-fb-18 (CLI session/repl).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
55 lines
2.2 KiB
SQL
55 lines
2.2 KiB
SQL
-- V005__chat_sessions.sql — multi-turn conversation persistence.
|
|
--
|
|
-- p9-fb-17 introduces session-level storage for the multi-turn `Ask`
|
|
-- conversation primitive (p9-fb-15 facade, p9-fb-16 TUI). Each session
|
|
-- groups N consecutive Q/A turns under one `session_id`; the TUI
|
|
-- "이전 대화 이어가기" + the future `kebab ask --session foo` flag
|
|
-- (p9-fb-18) read+append against these tables.
|
|
--
|
|
-- Schema notes:
|
|
--
|
|
-- * `session_id` is user-supplied (`--session foo`) or auto-derived
|
|
-- from `blake3(first_question || first_ts)` as a 32-hex string. No
|
|
-- foreign-key into another table — sessions are sovereign.
|
|
--
|
|
-- * `chat_turns.turn_index` is monotonic per session (0-based). The
|
|
-- `UNIQUE(session_id, turn_index)` pair enforces the invariant on
|
|
-- the storage side so a buggy caller cannot double-append turn 3.
|
|
--
|
|
-- * `ON DELETE CASCADE` so `kebab reset --data-only` (p9-fb-06)
|
|
-- wipes both tables together — orphan turns can never outlive
|
|
-- their session.
|
|
--
|
|
-- * `config_snapshot_json` mirrors `eval_runs.config_snapshot_json`
|
|
-- (P5-1) — captures the prompt_template_version, llm.model, and
|
|
-- max_context_tokens that produced the session so a retroactive
|
|
-- answer-quality regression can be re-traced.
|
|
--
|
|
-- * `citations_json` carries `Vec<Citation>` so the answer can be
|
|
-- redisplayed with the same citation markers a future session
|
|
-- sees on resume.
|
|
--
|
|
-- * `INTEGER` timestamps (unix epoch seconds) — same convention the
|
|
-- rest of the schema uses (P1-7 baselines this).
|
|
|
|
CREATE TABLE chat_sessions (
|
|
session_id TEXT PRIMARY KEY NOT NULL,
|
|
created_at INTEGER NOT NULL,
|
|
updated_at INTEGER NOT NULL,
|
|
title TEXT,
|
|
config_snapshot_json TEXT NOT NULL
|
|
) STRICT;
|
|
|
|
CREATE TABLE chat_turns (
|
|
turn_id TEXT PRIMARY KEY NOT NULL,
|
|
session_id TEXT NOT NULL REFERENCES chat_sessions(session_id) ON DELETE CASCADE,
|
|
turn_index INTEGER NOT NULL,
|
|
question TEXT NOT NULL,
|
|
answer TEXT NOT NULL,
|
|
citations_json TEXT NOT NULL,
|
|
created_at INTEGER NOT NULL,
|
|
UNIQUE(session_id, turn_index)
|
|
) STRICT;
|
|
|
|
CREATE INDEX idx_chat_turns_session ON chat_turns(session_id, turn_index);
|