Skip to content

feat(schema,daemon): persist the conversation turn tree and prompt records - #507

Open
Zerlight wants to merge 12 commits into
masterfrom
ruocheng/code-628
Open

feat(schema,daemon): persist the conversation turn tree and prompt records#507
Zerlight wants to merge 12 commits into
masterfrom
ruocheng/code-628

Conversation

@Zerlight

@Zerlight Zerlight commented Sep 7, 2026

Copy link
Copy Markdown
Member

Summary

Phase 1 of CODE-627 — Conversation turn graph & immutable attachment store. Linear: https://linear.app/arcbox/issue/CODE-628/featschemadaemon-persist-the-conversation-turn-tree-and-prompt-records

Stack: masterthis PR (ruocheng/code-628, base master) ← #508. Merge bottom-up; this PR's diff is only its own commits.

Phase 1 foundation: host-authoritative turn identity and durable user prompts. zod schemas for ConversationTurn, PromptRecord, and ProviderTurnBinding; SessionRecord gains activeLeafTurnId/graphRevision and runs gain an explicit runId/baseTurnId; additive wire frames (turn.submit, conversation.graph.get/.changed, conversation.read) under wire 80; drizzle tables behind one ConversationStore connection; the legacy null-prompt migration renders migrated sessions exactly as before.

Commits

  • feat(schema,daemon): add turn-tree model and run/graph session fields
  • feat(schema): declare conversation wire frames, bump wire version to 80
  • feat(engine): add ConversationStore seam and loud conversation stubs
  • feat(daemon): persist the conversation turn tree behind one connection
  • fix(schema): keep runId optional on the wire until the floor bump
  • test(engine): avoid timeout fixture startup race
  • fix(conversation): preserve graph compatibility and cleanup
  • fix(conversation): enforce turn admission in the durable store
  • fix(daemon): fail loud on a runId-less run and assert graph.changed delivery scope
  • fix(schema): collapse unshipped wire bump to 80
  • fix(conversation): report the resolve transition and make the in-memory persist atomic
  • fix(engine): drop the issue references from the conversation stub's refusal message

Verification

Every commit passed pnpm check:ci and pnpm test at its own tip; the stack tip (1d942a62; the same tree as the originally gated 73fc5ff2 plus the two review fixes below on 628/629) is at pnpm check:ci 0 errors, pnpm test 3408 passed / 1 skipped. Adversarial reviewers (one per axis, isolated read-only worktrees) reviewed the branch; each P1/P2 was reproduced with a failing test or a probe step before its fix — the round-by-round record is in the Linear issue's comments.

Checklist

  • pnpm check:ci and pnpm test both pass (no Rust changes)
  • I ran the affected surface and observed the change working — engine exercised through its harness tests and the dev-mock parity suite; the live-daemon run against real providers is recorded per issue in Linear (the claude edit path still needs a paid turn, opencode its binary)
  • Wire: bumped to 80 (this PR introduces the frames)
  • New code and assets are my own work
  • Docs and comments are updated where behavior changed (AGENTS.md and module docs in this branch)

Copilot AI lite review requested due to automatic review settings September 7, 2026 06:30
@linear-code

linear-code Bot commented Sep 7, 2026

Copy link
Copy Markdown

CODE-628

@pullfrog

pullfrog Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Your Claude subscription has hit its usage limit. It resets at 11:30am (UTC). Re-trigger Pullfrog after the reset, or add an ANTHROPIC_API_KEY repo secret — Pullfrog routes around an exhausted subscription automatically when one is present.

Add repo secret → · Model settings → · Setup docs → · Ask in Discord →

Pullfrog  | Rerun failed job ➔View workflow run | via Pullfrog | Using Claude Opus𝕏

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are correctness/concurrency issues in the daemon persistence layer and a schema-load edge case that can cause session record parsing failures.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR lays the Phase 1 foundation for durable conversation history by introducing a host-authoritative conversation turn tree (turn identity, prompt records, provider bindings, operation journal), adding corresponding wire frames, and persisting the data in the daemon via a single-connection SQLite-backed ConversationStore.

Changes:

  • Added new schema models (ConversationTurn, PromptRecord, ProviderTurnBinding, ConversationOperation) plus new primitives (TurnId, PromptId, RunId, etc.) and session-record fields (graphRevision, activeLeafTurnId, fork provenance).
  • Introduced additive wire protocol frames for turn submission and reading/observing conversation graphs; bumped WIRE_PROTOCOL_VERSION to 80 and added delivery scoping for conversation.graph.changed.
  • Implemented a ConversationStore seam (in-memory default + daemon SQLite implementation) and wired it into engine runtime lifecycle, including deletion cleanup.
File summaries
File Description
packages/host/engine/tests/integration/run-command.test.ts Increased timeout to reduce flake in timeout/kill integration test.
packages/host/engine/src/wire/request-router.ts Routed new conversation request kinds to a dedicated handler.
packages/host/engine/src/session/session-record-registry.ts Added runId minting and ensured runs always get a durable id.
packages/host/engine/src/session/orchestrator.ts Ensured session deletion also deletes conversation-store session data.
packages/host/engine/src/session/lifecycle-service.ts Minted runId at session creation/import/start and initialized graphRevision.
packages/host/engine/src/index.ts Re-exported ConversationStore surface and related types/errors.
packages/host/engine/src/engine.ts Added ConversationStore dependency with in-memory default; registered handler.
packages/host/engine/src/deps.ts Added optional conversationStore dependency for engine embedders/daemon.
packages/host/engine/src/conversation/request-handler.ts Added stub handler that fails loudly for new wire kinds.
packages/host/engine/src/conversation/conversation-store.ts Defined ConversationStore interface + in-memory implementation.
packages/host/engine/src/tests/engine-session-records.test.ts Updated fixtures for runId + graphRevision.
packages/host/engine/src/tests/engine-session-lifecycle.test.ts Added coverage ensuring session delete cleans conversation rows and GC’s prompts.
packages/host/engine/src/tests/engine-conversation-stubs.test.ts Added test ensuring conversation requests are refused (not silently dropped).
packages/host/engine/src/tests/conversation-store.test.ts Added in-memory store tests for atomic intent persistence, gating, GC, and resolve semantics.
packages/foundation/schema/tests/contract/wire/session.test.ts Added compatibility tests for v79 session frames and legacy runs without runId.
packages/foundation/schema/tests/contract/wire/conversation.test.ts Added contract tests for new conversation wire frames and delivery scoping.
packages/foundation/schema/src/wire/payload.ts Added payload refinement for turn.submit parent/revision guard coupling.
packages/foundation/schema/src/wire/message.ts Bumped wire protocol version to 80.
packages/foundation/schema/src/wire/index.ts Exported conversation wire types/schemas.
packages/foundation/schema/src/wire/delivery.ts Added session-scoped delivery for conversation.graph.changed.
packages/foundation/schema/src/wire/conversation.ts Added conversation-related wire variants and schemas.
packages/foundation/schema/src/wire/agent.ts Extended agent.event with optional run/turn attribution + watermark coords.
packages/foundation/schema/src/model/session/record.ts Added fork provenance, run identity/base, and graph fields to session record model.
packages/foundation/schema/src/model/primitives.ts Added new branded primitives for turn/prompt/run/attachment/operation ids.
packages/foundation/schema/src/model/index.ts Exported new conversation model.
packages/foundation/schema/src/model/conversation.ts Added conversation model schemas (turns, prompts, operations, watermark).
packages/foundation/schema/src/model/tests/conversation.test.ts Added schema tests for conversation model invariants.
packages/client/workbench/src/mock/dev-mock-host.ts Updated mock session import to include graphRevision.
apps/daemon/tests/integration/session-store.test.ts Updated session-store integration tests for runId persistence.
apps/daemon/src/session-store.ts Persisted runId, fork provenance, and graph fields; made missing runId a hard error on save.
apps/daemon/src/index.ts Wired daemon to inject a SQLite-backed ConversationStore.
apps/daemon/src/db/schema.ts Added new conversation tables + indexes and extended session/session_runs schema for new fields.
apps/daemon/src/conversation-store.ts Implemented single-connection SQLite ConversationStore with transactional intent persistence and GC.
apps/daemon/src/tests/session-store.test.ts Added tests for fork provenance round-trip and save-time refusal of runId-less runs.
apps/daemon/src/tests/conversation-store.test.ts Added comprehensive SQLite conversation-store round-trip and concurrency/GC tests.
apps/daemon/drizzle/meta/0012_snapshot.json Drizzle snapshot update for latest migration state.
apps/daemon/drizzle/meta/0011_snapshot.json Drizzle snapshot update for prior migration state.
apps/daemon/drizzle/meta/0010_snapshot.json Drizzle snapshot introducing new session/session_runs columns.
apps/daemon/drizzle/meta/_journal.json Drizzle journal updated with new migrations.
apps/daemon/drizzle/0012_overjoyed_risque.sql Added unique/partial indexes for open-op and sibling ordinal constraints.
apps/daemon/drizzle/0011_flippant_bastion.sql Added conversation turn-tree tables.
apps/daemon/drizzle/0010_chilly_swordsman.sql Added runId/baseTurnId + fork provenance + graph fields; backfilled existing runs with runId.
Review details
  • Files reviewed: 42/42 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread apps/daemon/src/conversation-store.ts
Comment thread apps/daemon/src/session-store.ts
Comment thread packages/host/engine/src/conversation/request-handler.ts
@greptile-apps

greptile-apps Bot commented Sep 7, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces the schema and durable-storage foundation for conversation turn graphs, prompt records, provider bindings, operation journaling, and explicit session run identity.

  • Adds wire-v80 conversation request/result/event frames while retaining older run parsing compatibility.
  • Adds SQLite migrations and a dedicated transactional conversation store.
  • Extends session persistence with graph revision, active-leaf, fork provenance, run IDs, and base-turn IDs.
  • Wires the store into the engine while conversation requests remain explicit unsupported stubs.
  • The main concerns are unenforced graph ancestry and immutable-record invariants at the new persistence boundary.

Confidence Score: 4/5

The PR appears safe to merge with non-blocking persistence-hardening follow-ups for graph ancestry, immutable prompts, and turn-state upserts.

No currently reachable production request failure was established because conversation requests remain unsupported, but the new public persistence seam permits cross-session ancestry and inconsistent immutable-record behavior that should be corrected before building the submit saga on it.

Files Needing Attention: apps/daemon/src/conversation-store.ts

Important Files Changed

Filename Overview
apps/daemon/src/conversation-store.ts Adds transactional SQLite persistence for turns, prompts, bindings, operations, and prompt GC; relational and immutable-record invariants need strengthening.
apps/daemon/src/db/schema.ts Adds conversation tables, graph indexes, session graph metadata, and run identity columns.
packages/host/engine/src/conversation/conversation-store.ts Defines the conversation persistence seam and in-memory implementation, including atomic intent and terminal-operation transitions.
packages/foundation/schema/src/model/conversation.ts Defines turn, prompt, provider-binding, operation, and watermark contracts.
packages/foundation/schema/src/wire/conversation.ts Adds additive wire-v80 conversation submit, graph, read, and change frames.
apps/daemon/src/session-store.ts Persists graph metadata, fork provenance, run IDs, and base-turn IDs with legacy migration support.
packages/host/engine/src/conversation/request-handler.ts Routes the declared conversation surface to explicit typed unsupported responses until later implementation phases.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    C[Client conversation frame] --> R[Conversation request handler]
    R -->|Phase 1| U[Typed unsupported reply]
    R -. future submit saga .-> S[ConversationStore.persistTurnIntent]
    S --> T[(conversation_turns)]
    S --> P[(prompts)]
    S --> O[(conversation_operations)]
    T --> B[(provider_turn_bindings)]
    T -->|prompt_id| P
    T -->|parent_turn_id| T
    D[Session deletion] --> G[Conversation graph and prompt GC]
    G --> X[Session record deletion]
Loading

Reviews (1): Last reviewed commit: "fix(conversation): report the resolve tr..." | Re-trigger Greptile

Comment thread apps/daemon/src/conversation-store.ts
Comment thread apps/daemon/src/conversation-store.ts
Comment thread apps/daemon/src/conversation-store.ts
Copilot AI review requested due to automatic review settings September 7, 2026 07:25
@pullfrog

pullfrog Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Your Claude subscription has hit its usage limit. It resets at 11:30am (UTC). Re-trigger Pullfrog after the reset, or add an ANTHROPIC_API_KEY repo secret — Pullfrog routes around an exhausted subscription automatically when one is present.

Add repo secret → · Model settings → · Setup docs → · Ask in Discord →

Pullfrog  | Rerun failed job ➔View workflow run | via Pullfrog | Using Claude Opus𝕏

@Zerlight

Zerlight commented Sep 7, 2026

Copy link
Copy Markdown
Member Author

Review round (2026-09-07). Verified every Copilot and Greptile finding against the branch.

Fixed — 284953a0: the conversation stub's refusal message no longer carries issue references.

Declined, with the reasoning on each thread: the turn/operation insert order (admission is gated by an explicit open-operation check inside one synchronous transaction; the unique indexes are backstops), toForkOrigin returning partial provenance (a corrupt row failing the schema parse loudly on load is the daemon-store contract, and no forked rows can exist before phase 5), cross-session parents in the store (validated once at admitSubmit, the boundary), prompt-id content conflicts (ids are minted per intent and prompts are immutable, so only an identical replay can hit the conflict path), and saveTurn rewriting identity columns (the documented upsert contract shared with the in-memory store; every caller changes state only).

Stack tip after this round: 1d942a62; all gates green at the tip, and the intermediate PR tips typecheck.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants