feat(kebab-core + kebab-store-sqlite): p9-fb-17 chat session storage (V005) #80
Reference in New Issue
Block a user
Delete Branch "feat/p9-fb-17-chat-storage"
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?
요약
multi-turn 영속화 (storage 만 — UI p9-fb-18 후속). chat_sessions header + chat_turns rows, ON DELETE CASCADE.
변경
kebab_core::ChatSessionRepotrait (6 method) +ChatSessionRow/ChatTurnRowstructsSqliteStoreimpl in 신규chat_sessions.rs모듈HOTFIXES (V004 → V005)
spec 의
V004__chat_sessions.sql가 p9-fb-19 의V004__kv.sql(이미 머지) 와 refinery number 충돌 →V005__chat_sessions.sql로 시프트. 동작 동일, 파일명만. HOTFIXES entry.테스트
cargo test --workspace --no-fail-fast -j 1exit 0cargo clippy --workspace --all-targets -- -D warningsclean문서
unblocks p9-fb-18 (CLI session/repl).
도그푸딩 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>회차 1 — schema + trait 디자인 정석. ON DELETE CASCADE, UNIQUE(session_id, turn_index), STRICT 모드 등 안전장치 빠짐없음. 9 신규 integration unit 도 roundtrip / missing / collision / cascade / ORDER BY / LIMIT 골고루 커버.
actionable bug 1 건 — append_turn 의 doc 은 transaction 보장한다고 하지만 실제로는 auto-commit 두 번이라 두번째 실패 시 inconsistent. 단일 nit 라 회차 2 가능.
@@ -0,0 +121,4 @@],).map_err(StoreError::from).context("append_turn: insert")?;Real bug: doc comment 가 "Wrap insert + parent updated_at in one transaction" 라고 명시하지만, 실제 코드는
conn.execute두 번을 그냥 호출합니다 — rusqlite 의 default 는 auto-commit 이라서 두 statement 가 별도 commit. 두 번째 (parent updated_at bump) 가 실패하면 첫 statement (turn insert) 는 이미 commit 됐고, 결과적으로 turn 은 있는데 session 의 updated_at 은 stale.실용 시나리오: SQLite 가 두 번째 statement 처리 중 disk full / lock contention 으로 fail 하면 inconsistent state. spec 의 contract ("Bumps the parent's updated_at") 가 깨짐.
Fix:
주의:
conn.transaction()은&mut Connection을 받아서MutexGuard<Connection>의 deref 가Connection인지 확인 필요.lock_conn()이MutexGuard<Connection>반환하면let mut conn = ...; let tx = conn.transaction()패턴 가능 (MutexGuard 가 DerefMut).회차 2 — append_turn 진짜 transaction 으로 교체됨 (begin → 2 execute → commit). 두 statement 가 atomic, 두번째 실패 시 첫번째도 rollback. 9 테스트 + clippy 통과. 머지 OK.