Skip to content

feat(agent-adapter): per-turn provider checkpoints and the forkAfterTurn capability - #511

Open
Zerlight wants to merge 13 commits into
ruocheng/code-631from
ruocheng/code-632
Open

feat(agent-adapter): per-turn provider checkpoints and the forkAfterTurn capability#511
Zerlight wants to merge 13 commits into
ruocheng/code-631from
ruocheng/code-632

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-632/featagent-adapter-per-turn-provider-checkpoints-and-the-forkafterturn

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

Adapters mint a durable provider checkpoint per turn and branch becomes forkAfterTurn. The engine forks at persisted checkpoints (the parent's own binding first, its cut on the current history as fallback), refuses invalid checkpoints typed, commits a turn only when its dispatch resolves, aligns pre-graph lineages to the provider corpus tail only when the alignment verifies uniquely, and never resumes a checkpoint-less tip blind.

Commits

  • feat(agent-adapter): forkAfterTurn capability and the live checkpoint contract
  • feat(agent-adapter): mint live fork checkpoints and refuse invalid ones typed
  • feat(engine): fork turns at persisted provider checkpoints
  • fix(agent-adapter): gate opencode turn forks and mint one checkpoint per turn
  • fix(engine): first live binding wins and legacy branch gates on its own capability
  • fix(engine): commit turns on the adapter's running status and never resume a checkpoint-less tip blind
  • fix(engine): align pre-graph lineages to the provider corpus tail
  • fix(engine): fork pre-graph roots after their hidden history instead of starting fresh
  • docs(agent-adapter): record the per-turn opencode checkpoint and the claude stop-hook cut divergence
  • fix(engine): commit a turn only when its dispatch resolves, never off the adapter's running status
  • fix(engine): accept a corpus-tail alignment only when it is the unique verifying one
  • fix(engine): fork from the parent's own binding first and fall back to its cut on the current history
  • fix(agent-adapter): opencode mints a preceding checkpoint only for a user message first seen inside the turn

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. Not verified here: the live fork surfaces of the installed opencode build and the claude stop-hook cut divergence recorded in the agent-adapter docs (CODE-633's spike tracks them).

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: capability rename under the still-unshipped wire 80
  • New code and assets are my own work
  • Docs and comments are updated where behavior changed (AGENTS.md and module docs in this branch)

@linear-code

linear-code Bot commented Sep 7, 2026

Copy link
Copy Markdown

CODE-632

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

@greptile-apps

greptile-apps Bot commented Sep 7, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces provider-specific per-turn checkpoints and uses them to fork conversation lineages safely.

  • Adds live and replay checkpoint capture with first-live-binding precedence.
  • Adds checkpoint validation and typed refusal for stale or unsupported provider cuts.
  • Adds verified corpus-tail attribution for pre-graph histories.
  • Revises turn dispatch tracking so graph commits follow successful dispatch resolution.
  • Adds root, inactive-tip, and interior-turn fork launch behavior across the engine.
  • One non-blocking provenance inconsistency remains in preceding checkpoint persistence.

Confidence Score: 4/5

The PR appears safe to merge, with a non-blocking durable provenance inconsistency in parent checkpoint bindings.

The checkpoint, attribution, launch, and dispatch-state paths are conservatively guarded; the only accepted issue records the successor run on a checkpoint attached to its parent, which does not currently affect fork selection but leaves inconsistent persisted metadata.

Files Needing Attention: packages/host/engine/src/conversation/turn-service.ts

Important Files Changed

Filename Overview
packages/host/engine/src/conversation/turn-service.ts Adds dispatch-aware turn tracking and live checkpoint persistence; preceding bindings record the successor run instead of the owning parent run.
packages/host/engine/src/conversation/checkpoint-service.ts Resolves live or verified replay cuts and prioritizes a parent's original provider history with a current-history fallback.
packages/host/engine/src/conversation/lineage-attribution.ts Implements conservative positional attribution, hidden-prefix tail alignment, and ambiguity rejection.
packages/host/engine/src/session/lifecycle-service.ts Launches fresh, resumed, or checkpoint-forked runs according to graph position and provider capability.
packages/host/engine/src/session/history-service.ts Maps invalid provider checkpoints to typed failures and retries an explicitly supplied fallback cut.
packages/host/agent-adapter/src/native/claude-code.ts Captures successful-turn assistant-row checkpoints and validates transcript membership before forking.
packages/host/agent-adapter/src/native/codex/adapter.ts Captures completed turn IDs, rejects unforkable paginated histories, and types JSON-RPC fork refusals.
packages/host/agent-adapter/src/native/opencode/adapter.ts Captures one preceding-message checkpoint per turn while keeping the unverified turn-level capability disabled.
packages/host/agent-adapter/src/native/pi/adapter.ts Captures settled session leaves and validates entries before creating branched sessions.
apps/daemon/src/conversation-store.ts Makes the first live binding immutable while allowing replay bindings to be refreshed or replaced by live captures.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Turn dispatch] --> B[Provider adapter]
  B -->|ending checkpoint| C[Current turn binding]
  B -->|preceding checkpoint| D[Parent turn binding]
  C --> E[Conversation checkpoint service]
  D --> E
  F[Verified provider-history read] -->|replay binding| E
  E --> G{Fork requested}
  G -->|valid persisted cut| H[Provider branchHistory]
  H --> I[New run and sibling turn]
  G -->|stale cut| J[Typed unsupported response]
  H -->|primary cut invalid| K{Current-history fallback}
  K -->|valid| I
  K -->|invalid or absent| J
Loading

Reviews (1): Last reviewed commit: "fix(agent-adapter): opencode mints a pre..." | Re-trigger Greptile

Comment thread packages/host/engine/src/conversation/turn-service.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.

@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 turn lifecycle/fork semantics across engine + multiple adapters, so despite strong tests it warrants final human review for correctness and edge-case safety.

Pull request overview

This PR implements Phase 1 of the turn-graph/checkpoint work by introducing per-turn provider checkpoints (persisted as turn bindings) and using them to enable safe “fork after turn” behavior across adapters and the engine session lifecycle, including legacy rewrite (history.branch) flows.

Changes:

  • Added a checkpoint pipeline from adapters (onCheckpoint) → engine (bindLiveCheckpoint) → store bindings, with “first live capture wins” semantics.
  • Reworked forking/resume/replace logic in the engine to fork from persisted per-turn cuts (with verified cold-read backfill), and to refuse typed when a cut is missing or invalid.
  • Updated adapters (claude-code/codex/pi/opencode) to mint/validate checkpoints and to surface invalid cuts via HistoryCheckpointInvalidError, with engine-side mapping to fixed unsupported messages.
File summaries
File Description
packages/host/engine/src/session/session-input-dispatcher.ts Associates live prompt echoes with persisted turn IDs for checkpoint-based rewrite/fork.
packages/host/engine/src/session/session-event-processor.ts Promotes dispatching turns on running so turn stamping works for whole-turn send() adapters.
packages/host/engine/src/session/orchestrator.ts Wires adapter checkpoint stream into turn binding persistence and exposes static history capabilities.
packages/host/engine/src/session/live-session.ts Encodes live branch cursors by (historyId, turnId) and adds checkpoint subscription plumbing.
packages/host/engine/src/session/lifecycle-service.ts Implements fork-after-turn admission/launch paths, rewrite behavior, and typed refusal rules.
packages/host/engine/src/session/history-service.ts Adds branch cut fallback handling and maps invalid checkpoints to fixed typed unsupported.
packages/host/engine/src/engine.ts Introduces and wires ConversationCheckpointService into lifecycle + projection.
packages/host/engine/src/conversation/turn-service.ts Adds dispatching/running tracking split and persists live/replay turn bindings.
packages/host/engine/src/conversation/projection-service.ts Routes attribution/backfill through checkpoint service; reuses host-user content helpers.
packages/host/engine/src/conversation/lineage-attribution.ts Extracts lineage attribution + hidden-prefix alignment logic into a shared module.
packages/host/engine/src/conversation/conversation-store.ts Enforces “live binding is immutable” on (turnId, historyId) in the in-memory store.
packages/host/engine/src/conversation/checkpoint-service.ts New service for resolving fork cuts and replay-backfilling bindings from verified corpora.
packages/host/engine/src/tests/* Adds/updates tests for checkpoint capture, fork semantics, invalid-cut mapping, and attribution/backfill.
packages/host/agent-adapter/src/native/pi/adapter.ts Enables forkAfterTurn and emits/validates checkpoints with typed invalid-cut errors.
packages/host/agent-adapter/src/native/opencode/adapter.ts Keeps forkAfterTurn dark; emits preceding checkpoints safely; validates fork targets before forking.
packages/host/agent-adapter/src/native/codex/history.ts Propagates history_mode and disables cursors for paginated rollouts.
packages/host/agent-adapter/src/native/codex/app-server.ts Preserves JSON-RPC error codes on rejections to distinguish refusal vs disconnect.
packages/host/agent-adapter/src/native/codex/adapter.ts Pre-checks forkability, maps JSON-RPC refusals to typed invalid-cut errors, and emits checkpoints.
packages/host/agent-adapter/src/native/claude-code.ts Enables forkAfterTurn, emits checkpoints, and verifies checkpoint existence in raw transcript.
packages/host/agent-adapter/src/history-branch.ts Introduces HistoryCheckpoint and HistoryCheckpointInvalidError contracts.
packages/host/agent-adapter/src/base.ts Adds checkpoint listener support and a shared emitCheckpoint() implementation.
packages/host/agent-adapter/src/adapter.ts Extends adapter interface with optional onCheckpoint and documents invalid-cut behavior.
packages/host/agent-adapter/AGENTS.md Documents the checkpoint contract and per-provider cut semantics.
packages/foundation/schema/src/model/history.ts Adds forkAfterTurn capability and clarifies the legacy branch capability.
apps/daemon/src/conversation-store.ts Applies “first live capture wins” semantics at the DB upsert layer.
apps/daemon/src/tests/conversation-store.test.ts Updates store tests to assert replay/live overwrite ordering and immutability of live cuts.
Review details
  • Files reviewed: 43/43 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): fixed on the top of the stack in 4b692dd — a preceding checkpoint's binding is now filed under the parent turn's own run, not the successor's; reproduced first with a stopped-and-resumed session (parent and successor on different runs). Gates at the new tip 4952b99: check:ci 0 errors, 3415 tests. Detail 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