feat(engine): conversation graph reads and turn-scoped projection - #510
feat(engine): conversation graph reads and turn-scoped projection#510Zerlight wants to merge 9 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 implements turn-scoped conversation graph reads and paginated conversation projections across the schema, engine, and development mock.
Confidence Score: 2/5The PR is not safe to merge until failed-turn output is preserved and paginated reads reliably disclose truncation and remain pinned to one provider-history snapshot. Failed turns can silently lose streamed output, oversized tails can be finalized after silently dropping state, and provider-history changes between pages can shift item offsets without invalidating the cursor. Files Needing Attention: packages/host/engine/src/conversation/projection-service.ts
|
| Filename | Overview |
|---|---|
| packages/host/engine/src/conversation/projection-service.ts | Introduces the core graph/read projection, attribution, live-tail merge, and pagination; failed-turn output, tail truncation, and snapshot drift need correction. |
| packages/host/engine/src/conversation/live-journal.ts | Adds bounded tracking for chunk streams whose earlier deltas were evicted. |
| packages/host/engine/src/conversation/request-handler.ts | Replaces unsupported read stubs with projection-backed graph and conversation responses. |
| packages/host/engine/src/session/history-service.ts | Adds settlement-aware cache freshness metadata used by projection history reads. |
| packages/foundation/schema/src/wire/conversation.ts | Extends graph/read frames with summaries, placeholders, item unions, paging cursors, and final-page-only watermarks. |
| packages/client/workbench/src/mock/dev-mock-host.ts | Adds minimal turn graph, submission, graph-read, and prompt-only projection behavior to the development host. |
Sequence Diagram
sequenceDiagram
participant Client
participant Handler as ConversationRequestHandler
participant Projection as ConversationProjectionService
participant Store as ConversationStore
participant History as Provider History
participant Journal as Live Journal
Client->>Handler: conversation.read(session, leaf, cursor)
Handler->>Projection: read(request)
Projection->>Store: load graph and root-to-leaf turns
Projection->>History: read provider corpus
Projection->>Projection: fingerprint-gated turn attribution
Projection->>Journal: snapshot events above durable cut
Projection->>Projection: compose host rows + history + live tail
Projection->>Projection: page by offset and byte budget
Projection-->>Handler: events, cursor or final watermark
Handler-->>Client: conversation.read.result
Comments Outside Diff (1)
-
packages/host/engine/src/conversation/projection-service.ts, line 2053-2065 (link)The cursor pins the graph shape and terminal-turn count, but not the provider-history snapshot, even though each page recomposes the durable projection. Provider history can gain final rows after a turn settles, and the history cache can expire between pages without changing any cursor field. The cursor is then accepted against a differently shaped projection, so its item offset can skip or duplicate conversation items instead of returning a conflict.
Reviews (1): Last reviewed commit: "fix(engine): structured conversation cur..." | Re-trigger Greptile
cf47f8e to
de8b30b
Compare
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
The CODE-35 backstop/deduping logic can drop still-open interactive asks (and their responding state) from conversation.read under tail trimming/truncation conditions.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Implements Phase 1 of the engine-side “conversation turn graph & turn-scoped projection” read surfaces by adding host-composed conversation.graph.get and cursor-paged conversation.read, including provider-history attribution gating, a bounded live tail merge, and dev-mock parity.
Changes:
- Add
ConversationProjectionServiceto compose a root→leaf projection from durable turns/prompts, provider history (fingerprint-gated), and the bounded live journal tail (watermark-merged). - Add wire handlers for
conversation.graph.getandconversation.read, plus schema updates for paged read items (events + placeholders) and graph turninputSummary. - Extend journal/history plumbing (in-flight chunk eviction tracking; history cache freshness bound) and add/refresh tests + dev mock responses.
File summaries
| File | Description |
|---|---|
| packages/host/engine/src/session/orchestrator.ts | Exposes live-session interactive requests for projection/backstop reads. |
| packages/host/engine/src/session/history-service.ts | Adds freshAfter cache-bypass bound and tracks builtAt for event-cache entries. |
| packages/host/engine/src/engine.ts | Wires ConversationProjectionService into the engine runtime and request handler. |
| packages/host/engine/src/conversation/turn-service.ts | Exposes prompts and tracks last terminal-settle timestamps for cache freshness. |
| packages/host/engine/src/conversation/request-handler.ts | Handles conversation.graph.get and conversation.read requests via projection service. |
| packages/host/engine/src/conversation/projection-service.ts | New: composes graph/read projections, paging, cursor integrity, and live-tail merging. |
| packages/host/engine/src/conversation/live-journal.ts | Tracks evicted in-flight stream keys so headless chunk tails can be cleared. |
| packages/host/engine/src/tests/engine-conversation-stubs.test.ts | Removes obsolete “unimplemented stubs fail loudly” test now that reads are implemented. |
| packages/host/engine/src/tests/engine-conversation-read.test.ts | New: end-to-end tests for graph/read, paging, watermark behavior, freshness, and tail merge. |
| packages/host/engine/src/tests/conversation-projection.test.ts | New: focused unit tests for projection attribution, tail semantics, paging, and cursor integrity. |
| packages/host/engine/src/tests/conversation-live-journal.test.ts | Extends tests for in-flight stream eviction/clearing and keying logic. |
| packages/foundation/schema/tests/contract/wire/conversation.test.ts | Updates wire contract tests for new read/graph result shapes and validation. |
| packages/foundation/schema/src/wire/index.ts | Re-exports new conversation wire types/schemas (read items, placeholders, graph turns). |
| packages/foundation/schema/src/wire/conversation.ts | Adds placeholder/read-item schemas, graph turn inputSummary, and optional watermark for non-final pages. |
| packages/client/workbench/src/mock/dev-mock-host.ts | Adds dev-mock responses for turn submit + graph/read parity and minimal graph storage. |
| packages/client/workbench/src/mock/tests/dev-mock-conversation.test.ts | New: dev-mock conversation parity tests for submit/graph/read and loud parameter failures. |
Review details
- Files reviewed: 16/16 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.
de8b30b to
8f1d551
Compare
|
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 →
|
|
Review round (2026-09-07). Verified every Copilot and Greptile finding against the branch. Fixes landed on the top of the stack (#517) because the code moved: the attribution gate lives in Fixed — Fixed — Declined, with the reasoning on the threads: tail trimming is unreachable in production (journal cap 10 MiB, page budget ~16 MiB — now stated in a constraint comment on the budget), which also covers the trim half of the backstop concern. |

Summary
Phase 1 of CODE-627 — Conversation turn graph & immutable attachment store. Linear: https://linear.app/arcbox/issue/CODE-631/featengine-conversation-graph-reads-and-turn-scoped-projection
Stack: #509 ← this PR (
ruocheng/code-631, baseruocheng/code-630) ← #511. Merge bottom-up; this PR's diff is only its own commits.conversation.graph.getand cursor-pagedconversation.readcompose the turn-scoped projection: host user rows from the ConversationStore, provider output through the attribution gate (fingerprint-verified positional alignment, active-lineage only), the live tail from the journal with an honest truncation marker, and the prompt-onlyhistory-unavailablefallback. Structured read cursors reject drift as a typed conflict. The dev mock answers the same frames.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. The dev mock host serves the frames the renderer consumes, so the projection was exercised end to end indev:mock.Checklist
pnpm check:ciandpnpm testboth pass (no Rust changes)AGENTS.mdand module docs in this branch)