feat(engine): outbox-ordered turn.submit saga with an operation journal - #508
feat(engine): outbox-ordered turn.submit saga with an operation journal#508Zerlight wants to merge 14 commits into
Conversation
|
Your Claude subscription has hit its usage limit. It resets at 11:30am (UTC). Re-trigger Pullfrog after the reset, or add an Add repo secret → · Model settings → · Setup docs → · Ask in Discord →
|
Greptile SummaryThis PR introduces the durable, operation-journaled
Confidence Score: 1/5The PR is not safe to merge because the daemon conversation store violates required interfaces and saga semantics, session deletion references a nonexistent field, and legacy run IDs are not stable across saves. Normal daemon turn submission cannot obtain a store-assigned turn or a successful transition result, late operation resolvers can overwrite committed outcomes, session deletion does not compile against the refactored orchestrator, and missing run IDs are regenerated on every save. Files Needing Attention: apps/daemon/src/conversation-store.ts, packages/host/engine/src/session/orchestrator.ts, apps/daemon/src/session-store.ts
|
| Filename | Overview |
|---|---|
| apps/daemon/src/conversation-store.ts | Refactors conversation persistence onto the shared database, but breaks intent assignment and terminal-operation compare-and-set contracts. |
| packages/host/engine/src/conversation/turn-service.ts | Implements durable intent, commit, failure, settlement, and boot-recovery orchestration while depending on return semantics absent from the daemon store. |
| packages/host/engine/src/session/lifecycle-service.ts | Adds replay-first admission, phase-specific timeouts, cold resume, fresh-root launch, and operation resolution for turn submission. |
| packages/host/engine/src/session/orchestrator.ts | Adds run-addressed dispatch and lifecycle tracking but leaves session deletion referencing the removed conversation-store field. |
| apps/daemon/src/session-store.ts | Moves the session store to the shared client but remints legacy missing run IDs without stabilizing the in-memory record. |
| apps/daemon/src/db/database.ts | Centralizes SQLite setup, migration-ledger reconciliation, migration execution, and connection ownership. |
Comments Outside Diff (1)
-
packages/host/engine/src/session/orchestrator.ts, line 138-146 (link)The constructor field was renamed from
conversationstoturns, butdelete()still reads and callsconversations. That property no longer exists, so the engine fails type-checking. If emitted without type-checking, everysession.deletefails before removing the conversation graph and session record.
Reviews (1): Last reviewed commit: "fix(engine): gate resolve side effects o..." | Re-trigger Greptile
There was a problem hiding this comment.
Greptile has paused reviews on this repository — it used its 100 free open-source review credits for this billing period. Reviews resume automatically on September 23. To continue before then, an organization admin can keep reviews running past the free credits — those bill as normal usage.
|
Your Claude subscription has hit its usage limit. It resets at 11:30am (UTC). Re-trigger Pullfrog after the reset, or add an Add repo secret → · Model settings → · Setup docs → · Ask in Discord →
|
There was a problem hiding this comment.
🟡 Changes recommended
turn.submit replay is not currently scoped to sessionId, allowing cross-session operationId replay (and potential information leakage) unless guarded.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Implements Phase 1 of the conversation turn graph execution path in the engine by introducing an outbox-ordered turn.submit saga backed by a durable operation journal, while also ensuring legacy agent.input paths persist turn intents so the graph never misses turn-starting inputs.
Changes:
- Add
ConversationTurnServiceto persist turn intents + operation journal entries, commit running turns, resolve failures, and recover open operations at boot. - Route
turn.submitthrough a submit saga with admission gating, durable commit points, hard timeouts, and idempotent replay byoperationId. - Rework run bookkeeping to address runs by
runId(history binding, sealing, event gating) and share a single daemon DB connection for session + conversation stores.
File summaries
| File | Description |
|---|---|
| packages/host/engine/src/session/session-record-registry.ts | Address run mutations by runId, add graph revision/leaf commit helper, and make beginRun return a stable runId. |
| packages/host/engine/src/session/session-input-dispatcher.ts | Persist legacy turn intents, coordinate with open-operation gating, and ensure failures resolve stored intents. |
| packages/host/engine/src/session/session-event-processor.ts | Gate session-scoped straggler events by current run; settle/record turn/run outcomes via ConversationTurnService. |
| packages/host/engine/src/session/orchestrator.ts | Thread runId into live sessions, integrate ConversationTurnService, and ensure teardown settles turns and seals runs by id. |
| packages/host/engine/src/session/live-session.ts | Attach runId to each live adapter instance for run-addressed bookkeeping. |
| packages/host/engine/src/session/lifecycle-service.ts | Implement the turn.submit saga (admit/persist/launch/dispatch/resolve) and wire it into lifecycle paths (including rewrites). |
| packages/host/engine/src/failure.ts | Add busy request code and introduce causeToRequestFailure for interrupt/defect exit mapping. |
| packages/host/engine/src/engine.ts | Instantiate and wire ConversationTurnService, switch conversation request handling to lifecycle-backed saga, and run boot recovery. |
| packages/host/engine/src/conversation/turn-service.ts | New durable turn/operation journal service: intent persistence, commit/resolve, running-turn tracking, and boot recovery. |
| packages/host/engine/src/conversation/request-handler.ts | Handle turn.submit by invoking the lifecycle saga and replying with turn.submitted / stored request.failed. |
| packages/host/engine/src/automation/turn-watcher.ts | Add onDispatchAccepted hook and ensure it runs once after dispatch acceptance or terminal outcome. |
| packages/host/engine/src/tests/session-record-registry.test.ts | New tests for run addressing semantics (sealRun, bindHistoryId, isCurrentRun, caller-minted runId). |
| packages/host/engine/src/tests/engine-turn-tracking.test.ts | New tests for legacy input turn tracking, recovery behavior, and commit/resolve idempotence. |
| packages/host/engine/src/tests/engine-turn-submit.test.ts | New tests for the turn.submit saga: replay, admission, timeouts, relaunch/resume behavior, and failure handling. |
| packages/host/engine/src/tests/engine-session-lifecycle.test.ts | Update test harness construction to pass runId into LiveSession. |
| packages/host/engine/src/tests/engine-schedule.test.ts | Ensure scheduled prompts are recorded as completed turns; extend harness to use conversation/resource stores. |
| packages/host/engine/src/tests/engine-conversation-stubs.test.ts | Update stubs to only assert loud failures for still-unimplemented read/projection requests. |
| apps/daemon/tests/integration/session-store.test.ts | Update integration tests to use the shared openDaemonDatabase() connection lifecycle. |
| apps/daemon/src/session-store.ts | Refactor session store to accept a shared Drizzle client rather than owning its own SQLite connection/migrations. |
| apps/daemon/src/index.ts | Open a shared daemon database connection and pass its client to session + conversation stores. |
| apps/daemon/src/db/schema.ts | Update comments to reflect shared connection ownership for conversation/session tables. |
| apps/daemon/src/db/database.ts | New shared DB opener that runs migration ledger reconciliation + migrations once per owned connection. |
| apps/daemon/src/database-migrations.ts | New single source of truth for daemon migrations folder resolution. |
| apps/daemon/src/conversation-store.ts | Refactor conversation store to accept the shared Drizzle client instead of owning its own connection. |
| apps/daemon/src/tests/session-store.test.ts | Update unit tests to manage DB lifetime via openDaemonDatabase(). |
| apps/daemon/src/tests/resource-store.test.ts | Adjust resource store tests to coexist with the new shared DB setup for session persistence. |
| apps/daemon/src/tests/conversation-store.test.ts | Update conversation store tests to use the shared DB client and explicitly reopen/close databases. |
| apps/daemon/AGENTS.md | Document the new shared graph/session DB connection ownership and migration workflow. |
Review details
- Files reviewed: 28/28 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…n operations at boot
…versation-reported flag on a dispatch failure
7992992 to
bc95654
Compare
|
Review round (2026-09-07). Verified every Copilot and Greptile finding against the branch. Fixed — Already fixed before this round — the three Greptile P1s (store contract, terminal-result overwrite, legacy run-id drift) and the "deletion uses removed field" item outside the diff all described the tree at Stack tip after this round: |
|
Your Claude subscription has hit its usage limit. It resets at 11:30am (UTC). Re-trigger Pullfrog after the reset, or add an Add repo secret → · Model settings → · Setup docs → · Ask in Discord →
|

Summary
Phase 1 of CODE-627 — Conversation turn graph & immutable attachment store. Linear: https://linear.app/arcbox/issue/CODE-629/featengine-outbox-ordered-turnsubmit-saga-with-an-operation-journal
Stack: #507 ← this PR (
ruocheng/code-629, baseruocheng/code-628) ← #509. Merge bottom-up; this PR's diff is only its own commits.One mutation primitive for every turn-starting affordance: the outbox-ordered
turn.submitsaga (replay → admit → persist intent → provider work under hard timeouts → dispatch → cutover) with an operation journal that makes every outcome committed-or-failed and idempotent byoperationId. Legacyhistory.branchwrites the tree non-destructively from day one; boot recovery resolves open operations; run sealing and history binding address runs by id.Commits
Verification
Every commit passed
pnpm check:ciandpnpm testat its own tip; the stack tip (1d942a62; the same tree as the originally gated73fc5ff2plus the two review fixes below on 628/629) is atpnpm check:ci0 errors,pnpm test3408 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:ciandpnpm testboth pass (no Rust changes)AGENTS.mdand module docs in this branch)