FE-535: SQLite foundation and project persistence#11
Conversation
FE-535 SQLite foundation + project persistence
Replace Dolt with better-sqlite3. Schema: project, interview_exchange, spec_output. Auto-create DB on startup. Session CRUD. Resume via Claude Agent SDK resume option. |
|
@CiaranMn this is still draft but FWIW the |
🤖 Augment PR SummarySummary: Adds SQLite-backed persistence for projects and chat messages so conversations survive refresh/close and can be replayed for multi-turn context. Changes:
Technical Notes: Multi-turn context is implemented by formatting stored history into the next prompt (stateless SDK calls), and the SSE translator is now request-scoped to avoid cross-request corruption. 🤖 Was this summary useful? React with 👍 or 👎 |
|
|
||
| export function createDb(path: string = ':memory:'): DB { | ||
| const db = new Database(path); | ||
| db.pragma('journal_mode = WAL'); |
There was a problem hiding this comment.
createDb() enables WAL but doesn’t enable SQLite foreign-key enforcement; without PRAGMA foreign_keys = ON, the message.project_id REFERENCES project(id) constraint won’t actually be enforced, which can allow orphaned rows if future code paths insert messages before projects.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
|
|
||
| const project = getOrCreateProject(db); | ||
| const history = getMessages(db, project.id); | ||
| saveMessage(db, project.id, 'user', prompt); |
There was a problem hiding this comment.
saveMessage(..., prompt) runs even when extractPrompt() returns an empty string (e.g., malformed client request or empty messages array), which will persist blank messages and replay them into formatHistory() on subsequent turns.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| res.end(); | ||
| }); | ||
|
|
||
| if (assistantText) { |
There was a problem hiding this comment.
If the Claude SDK stream throws mid-response, assistantText may contain a partial answer (accumulated before the exception) and will still be persisted; consider whether you want to persist incomplete assistant output (or otherwise record that it errored) to avoid confusing “truncated” history on reload.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| } | ||
|
|
||
| export function getOrCreateProject(db: DB, name = 'default'): Project { | ||
| const existing = db.prepare('SELECT * FROM project ORDER BY created_at DESC LIMIT 1').get() as Project | undefined; |
There was a problem hiding this comment.
getOrCreateProject(db, name) ignores the name parameter once any project exists (it always returns the most recently created project), which could produce surprising behavior if/when multiple projects are introduced.
Severity: low
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
Replace in-memory chat with better-sqlite3 persistence. Messages survive browser close/reopen. Server replays conversation history into the prompt for multi-turn context. App factory pattern enables testable DB injection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…r factory Eliminates shared mutable state (thinkingBlocks/textBlocks Sets) that would corrupt output under concurrent requests. Each request now creates its own translator instance via createTranslator(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
extractPrompt, formatHistory, and collectText are now named functions. The handler reads as a clear pipeline: extract → persist → format → stream → persist. Exports AIEvent type from sse-adapter for reuse. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Documents cmux + cdp-cli pattern for outer-loop UI verification. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Grill session produced a fundamentally different domain model: turn tree as version history, decision/assumption dependency graphs, observer agent extraction, phase-as-skill architecture, soft invalidation. SPEC.md evolved with new domain decisions D1-D6, lexicon, and requirements. PLAN.md re-sliced against the new model (14 items, 2 done). Schema captured in docs/design/schema.dbml. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ments SPEC.md: D14 (AI Elements), A16/A17, I7/I8, assistant-ui constraint, tool call feedback loops PLAN.md: slice 3b (rich chat UI) with dependency graph update Skills: patch mode for ln-spec/ln-plan, cross-reference integrity checklist for ln-spec, post-edit checklist for ln-plan, parallelism awareness for ln-scope/ln-build AGENTS.md: PR title and branch naming conventions Amp-Thread-ID: https://ampcode.com/threads/T-019d4866-d870-730e-bd14-3bebb55a2b63 Co-authored-by: Amp <amp@ampcode.com>
e090dff to
9dffd2b
Compare

feat: SQLite foundation — project persistence and conversation resume
Replace in-memory chat with better-sqlite3 persistence. Messages survive
browser close/reopen. Server replays conversation history into the prompt
for multi-turn context. App factory pattern enables testable DB injection.
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
refactor: introduce Role union type for message role field
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
refactor: replace SSE adapter module state with per-request translator factory
Eliminates shared mutable state (thinkingBlocks/textBlocks Sets) that would
corrupt output under concurrent requests. Each request now creates its own
translator instance via createTranslator().
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
refactor: extract pure helpers from chat handler
extractPrompt, formatHistory, and collectText are now named functions.
The handler reads as a clear pipeline: extract → persist → format →
stream → persist. Exports AIEvent type from sse-adapter for reuse.
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
chore: add manual testing workflow to CLAUDE.md
Documents cmux + cdp-cli pattern for outer-loop UI verification.
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
docs: evolve spec with turn-tree domain model, re-plan slices
Grill session produced a fundamentally different domain model: turn tree
as version history, decision/assumption dependency graphs, observer agent
extraction, phase-as-skill architecture, soft invalidation. SPEC.md
evolved with new domain decisions D1-D6, lexicon, and requirements.
PLAN.md re-sliced against the new model (14 items, 2 done).
Schema captured in docs/design/schema.dbml.
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com