Skip to content

feat(client-core): merge conversations by daemon sequence and turn identity - #512

Open
Zerlight wants to merge 11 commits into
ruocheng/code-632from
ruocheng/code-634
Open

feat(client-core): merge conversations by daemon sequence and turn identity#512
Zerlight wants to merge 11 commits into
ruocheng/code-632from
ruocheng/code-634

Conversation

@Zerlight

@Zerlight Zerlight commented Sep 7, 2026

Copy link
Copy Markdown
Member

Summary

Phase 2 of CODE-627 — Conversation turn graph & immutable attachment store. Linear: https://linear.app/arcbox/issue/CODE-634/featclient-core-merge-conversations-by-daemon-sequence-and-turn

Stack: #511this PR (ruocheng/code-634, base ruocheng/code-632) ← #513. Merge bottom-up; this PR's diff is only its own commits.

Phase 2 client cutover: when the host serves the graph, client-core seeds from conversation.read pages and merges by the (epoch, seq) watermark and the daemon-minted user-row identity, asking for one re-read on a position skip; the CODE-621 content-matching machinery survives only as the old-daemon path. UI renders the history-unavailable placeholder on web and mobile, workbench and mobile seed from the projection, and the dev mock stamps and journals its frames so reads reproduce the live stream.

Commits

  • feat(engine): mint one user-row identity for live echoes and conversation reads
  • feat(client-core): keep daemon stamps on buffered events and expose the turn graph requests
  • feat(client-core): walk conversation.read pages into one projection seed
  • feat(client-core): add the history-unavailable item and share the user-row identity via schema
  • feat(client-core): merge projection seeds by daemon position and ask to re-read on drift
  • feat(ui): render the history-unavailable placeholder on web and mobile
  • feat(workbench,mobile): seed conversations from the turn-graph projection when the host serves one
  • feat(workbench): stamp and journal dev-mock frames so reads reproduce the live stream
  • docs(client-core): record the two conversation merge paths and their rules
  • test(workbench): seed a conversation through the dev mock's turn graph end to end
  • fix(engine,client-core): unify automation echo identity and re-read when a leaf appears

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. A dev-mock end-to-end test seeds a conversation through the turn graph; the compiled renderer was probed headlessly against dev:mock.

Checklist

  • pnpm check:ci and pnpm test both pass (no Rust changes)
  • I ran the affected surface and observed the change working — the compiled renderer against dev:mock in headless Chrome
  • Wire: no wire change
  • New code and assets are my own work
  • Docs and comments are updated where behavior changed (AGENTS.md and module docs in this branch)

@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𝕏

@linear-code

linear-code Bot commented Sep 7, 2026

Copy link
Copy Markdown

CODE-634

@greptile-apps

greptile-apps Bot commented Sep 7, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces graph-backed conversation seeding and merges projection snapshots with live events using daemon (epoch, seq) positions and stable per-turn user-row identities.

  • Adds paginated conversation.read support, graph-change tracking, projection persistence, and resynchronization.
  • Unifies engine-generated live and projected user-row identities.
  • Seeds workbench and mobile conversations from graph projections with legacy transcript fallback.
  • Adds history-unavailable rendering and dev-mock graph/journal parity.
  • The graph-change optimization does not distinguish a linear continuation from an interior fork, leaving the previous branch visible after switching lineages.

Confidence Score: 4/5

The PR is not safe to merge until interior forks replace the displayed lineage instead of retaining messages from the abandoned branch.

The new graph-change suppression correctly avoids re-reading for ordinary continuations, but it applies the same rule to forks whose new echo is already buffered; because forks emit no rewind, the client combines two sibling lineages in one conversation.

Files Needing Attention: packages/client/core/src/conversation-store.ts

Important Files Changed

Filename Overview
packages/client/core/src/conversation-store.ts Implements watermark-based projection merging and graph resynchronization, but misclassifies interior forks as linear continuations.
packages/client/core/src/conversation-read.ts Walks consistent graph snapshots across cursor pages and falls back to legacy transcript seeding when no turn graph exists.
packages/host/engine/src/session/session-input-dispatcher.ts Gives live prompt echoes the durable turn-derived row identity before committing graph movement.
packages/host/engine/src/conversation/projection-service.ts Projects durable user rows using the same identity and branch cursor as live echoes.
packages/client/workbench/src/surface/use-seeded-conversation.ts Integrates projection reads, persisted fallbacks, and store-requested revalidation into the workbench.
packages/client/workbench/src/mock/dev-mock-host.ts Adds stamped event journaling and graph-backed conversation reads to the development host.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Read conversation projection] --> B[Fold active-lineage seed]
  B --> C[Fold stamped live events above watermark]
  C --> D{Graph revision advances}
  D -->|New linear child echo is present| E[Continue without re-read]
  D -->|Unknown leaf| F[Request fresh projection]
  D -->|Interior fork echo is present| G[Currently treated as continuation]
  G --> H[Old branch descendants remain visible]
Loading

Reviews (1): Last reviewed commit: "fix(engine,client-core): unify automatio..." | Re-trigger Greptile

Comment thread packages/client/core/src/conversation-store.ts
Copilot AI lite review requested due to automatic review settings September 7, 2026 06:54

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

@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.

🔵 Needs a closer look

It changes core conversation identity/merge semantics across schema, engine, client-core, workbench, dev-mock, and mobile, so it warrants final human review despite strong automated coverage.

Pull request overview

Phase 2 of the CODE-627 conversation turn graph cutover: the client now seeds and merges conversation history using the daemon’s (epoch, seq) positions plus a daemon-minted “one user-row identity per turn”, and falls back to the legacy transcript (history.read) path when the turn graph is unavailable.

Changes:

  • Introduces a projection-based seed (conversation.read paging) and a projection merge path that drops already-covered live frames by daemon watermark and requests a single re-read on drift (epoch jump / seq gap / graph leaf mismatch).
  • Unifies “live echo” and “conversation.read” user-row identity via userRowMessageId(turnId) so the client no longer reconciles two ids for the same turn.
  • Adds a UI “history unavailable” placeholder item and updates dev-mock + tests to stamp/journal frames so reads reproduce the live stream.
File summaries
File Description
packages/presentation/ui/src/chat/types.ts Adds history-unavailable conversation item kind to the UI chat model.
packages/presentation/ui/src/chat/turn-segment-view.tsx Renders the new history-unavailable marker in the turn segment switch.
packages/presentation/ui/src/chat/history-unavailable-marker.tsx Introduces the web UI marker component for unavailable projected history.
packages/presentation/ui/src/tests/activity-groups.test.ts Updates test helpers to include the new boundary kind.
packages/presentation/i18n/src/locales/zh-cn.ts Adds historyUnavailable translation key for workbench/mobile namespaces.
packages/presentation/i18n/src/locales/en.ts Adds historyUnavailable translation key for workbench/mobile namespaces.
packages/host/engine/src/session/session-input-dispatcher.ts Switches prompt/command echoes to durable per-turn row identity (userRowMessageId).
packages/host/engine/src/session/orchestrator.ts Aligns orchestrator-side prompt echo identity with durable turn identity.
packages/host/engine/src/session/live-session.ts Exports encodeLiveBranchCursor for reuse in read projections.
packages/host/engine/src/conversation/projection-service.ts Uses userRowMessageId for read user rows and attaches legacy edit cursor when available.
packages/host/engine/src/tests/engine-turn-submit.test.ts Tightens assertions to ensure echoed user-message ids match the durable row identity.
packages/host/engine/src/tests/engine-schedule.test.ts Ensures schedule wiring emits user rows with the durable row identity.
packages/host/engine/src/tests/engine-conversation-read.test.ts Verifies conversation.read rows share identity + edit cursor with the live echo.
packages/foundation/schema/src/wire/index.ts Re-exports the new graph feature-gate wire version constant.
packages/foundation/schema/src/wire/conversation.ts Introduces CONVERSATION_GRAPH_WIRE_VERSION and documents the graph wire introduction.
packages/foundation/schema/src/model/conversation.ts Adds userRowMessageId(turnId) helper (daemon-minted “one user row per turn”).
packages/client/workbench/tests/integration/dev-mock-transport.test.ts Updates subscription callback shape to the new event-buffer envelope signature.
packages/client/workbench/tests/integration/dev-mock-projection.test.ts Adds E2E coverage for projection seeding + resync behavior against dev-mock.
packages/client/workbench/src/surface/use-seeded-conversation.ts Switches seeding to readConversationSeed, persists projection seeds, and wires store resync to SWR mutate.
packages/client/workbench/src/surface/seed-cache.ts Adds projection seed persistence keyed by session, sharing the same LRU cap with transcript seeds.
packages/client/workbench/src/surface/tests/seed-cache.test.ts Tests projection persistence behavior and shared LRU entry cap.
packages/client/workbench/src/mock/dev-mock-host.ts Stamps/journals frames with (epoch, seq) and serves conversation.read from the journal for parity.
packages/client/workbench/src/mock/tests/dev-mock-conversation.test.ts Expands parity tests for stamping, turn recording, read replay, and epoch bump on resume.
packages/client/workbench/AGENTS.md Documents updated workbench seeding/resync/persistence responsibilities.
packages/client/core/tests/integration/conversation-store-projection.test.ts Adds comprehensive projection-merge behavior tests (watermark drop, gaps/epoch, graph drift, placeholders, attachment overlay).
packages/client/core/tests/integration/conversation-read.test.ts Adds paging-walk tests for conversation.read projection reads (watermark, drift restarts, conflict, give-up).
packages/client/core/tests/integration/conversation-client.test.ts Tests graph feature detection by wire version and buffering of daemon position/envelope fields.
packages/client/core/tests/integration/control-client.test.ts Updates event subscription callback shape to match new SequencedAgentEvent delivery.
packages/client/core/src/react.tsx Extends useConversation to accept projection seeds and surface a resync callback.
packages/client/core/src/index.ts Exports the new conversation-read surface.
packages/client/core/src/conversation.ts Adds a history-unavailable item kind and builder support to insert it into the timeline.
packages/client/core/src/conversation-store.ts Splits store into projection vs history merge paths; adds resync signaling and watermark-based merge for projection.
packages/client/core/src/conversation-read.ts Implements conversation.read paging walk + unified seed selection (projection → transcript → none).
packages/client/core/src/client/pending-registry.ts Adds pending registries for conversation graph and read results.
packages/client/core/src/client/event-buffer.ts Preserves daemon (epoch, seq) positions and attribution on buffered events; updates subscriber callback signature.
packages/client/core/src/client/conversation-graph-changes.ts Adds per-session latest graph-change registry + subscription.
packages/client/core/src/client/control-channel.ts Adds conversation.graph.get and conversation.read correlated control-plane APIs.
packages/client/core/src/client.ts Wires new conversation graph/read APIs, event envelope ingestion, and graph change tracking; adds supportsConversationGraph gate.
packages/client/core/src/tests/event-buffer.test.ts Ensures stamped repeats are retained to keep daemon sequence contiguous.
packages/client/core/CLAUDE.md Adds per-directory agent instructions link.
packages/client/core/AGENTS.md Documents the two seeding paths, merge rules, resync reasons, and key invariants.
apps/mobile/src/runtime/use-seeded-conversation.ts Switches mobile seeding to readConversationSeed and handles projection resync by re-running the read.
apps/mobile/src/runtime/tests/use-seeded-conversation.test.ts Adds coverage for projection seeding and resync-triggered re-reads; updates empty-graph behavior.
apps/mobile/src/components/conversation/timeline-item.tsx Renders the history-unavailable item in the mobile timeline.
Review details
  • Files reviewed: 44/44 changed files
  • Comments generated: 0
  • Review effort level: Lite

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

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𝕏

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.

@Zerlight

Zerlight commented Sep 7, 2026

Copy link
Copy Markdown
Member Author

Review round on this PR (Greptile, one inline thread): the fork-retains-abandoned-messages finding is not reachable — every submit that is not a continuation of the active leaf relaunches under a new event epoch, and reads carry no user echoes, so the graph-move shortcut only ever sees a child of the displayed leaf. The constraint is now stated on checkGraph and pinned by a store test (4952b99, top of the stack). Full argument on the thread; the PR body is unchanged.

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