Plan Versioning With Manual Checkpoints, Branch Graphs, and Git-Style Diffs
- Persist every manual checkpoint of a proposed plan inside DoltLite so operators have a canonical, branchable history even when threads restart.
- Extend the server snapshot + contracts so web clients can fetch, subscribe to, and diff any two plan versions in-thread.
- Upgrade the PlanSidebar to add a “Save Checkpoint” control, a branch-aware version list, and a Git-style diff viewer that reuses the existing DiffPanel widgets.
- In scope: thread-local version graphs with branching metadata, DoltLite-backed persistence, websocket propagation, PlanSidebar UX, and parity diff tooling.
- Out of scope: automatic checkpoints on every provider delta (only user-triggered snapshots), formula/bead promotion flows, and server-side semantic diffing beyond markdown text.
- New tables (sidecar schema
proj):projection_thread_plan_versions(version_id PK, plan_id FK, thread_id, branch_id, parent_version_id nullable, root_version_id, plan_markdown, turn_id, message_id, created_by_user_id, created_at, auto DoltLitetransaction_id). Migration fileapps/server/src/persistence/Migrations/023_AddPlanVersioning.ts:1.projection_thread_plan_branches(branch_id PK, plan_id, thread_id, label, head_version_id, created_at) for tracking branch names and latest heads.projection_thread_plan_branch_links(version_id, derived_thread_id, derived_turn_id) to preserve the “start new thread from version” relation.- Indices:
(plan_id, branch_id),(thread_id, created_at),(plan_id, parent_version_id).
- Existing tables:
- Add
latest_version_idtoprojection_thread_proposed_plansvia the same migration so current plan rows know which version corresponds to the latest checkpoint (apps/server/src/persistence/Migrations/023_AddPlanVersioning.ts:60). - Add
source_plan_version_idtoprojection_turns(nullable) for provenance when we start an implementation turn from a plan (apps/server/src/persistence/Migrations/023_AddPlanVersioning.ts:110).
- Add
- Backfill script: for every existing plan, insert a synthetic “v0” version derived from the most recent
plan_markdownso history begins populated (Effect pipeline inside the migration file).
- Repositories:
- Create
apps/server/src/persistence/Layers/PlanVersions.ts:1with Effect repository functions:createCheckpoint,listByPlanId,listByThreadId,linkDerivedThread,updateBranchHead. - Add service definition in
apps/server/src/persistence/Services/PlanVersions.ts:1for typing.
- Create
- Domain services:
- Introduce
PlanVersionManagerlayer inapps/server/src/orchestration/Layers/PlanVersionManager.ts:1that coordinates repository writes, branch graph validation (no cycles), and DoltLite transaction boundaries.
- Introduce
- Manual checkpoint command path:
- New Native API method
planVersions.createCheckpointhandled insideapps/server/src/wsServer.ts:320that accepts{ threadId, planId, parentVersionId }. - Handler reads the latest plan projection via
ProjectionThreadProposedPlanRepository(apps/server/src/persistence/Layers/ProjectionThreadProposedPlans.ts:17), validates the requesting session owns that thread, then callsPlanVersionManager.
- New Native API method
- Projection snapshot updates:
- Extend
apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts:605to join the new tables and assemble aplanVersionsarray plusplanBranchesper thread. Include branch adjacency (parent → child list) so the client can build DAGs. - Emit live diff events on the
orchestration.domainEventchannel when checkpoints are created or branch heads move.
- Extend
- Thread linkage:
- When
thread.turn.startis invoked with asourceProposedPlan, capture the selected version id (new optional field) and update bothprojection_turnsandprojection_thread_plan_branch_linksto maintain traceability (apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:835and:874).
- When
- Reliability & permissions:
- Ensure PlanVersionManager enforces manual snapshots only when a plan exists, denies duplicates with identical
plan_markdownunlessforceflag is set, and writes structured events for audit logging.
- Ensure PlanVersionManager enforces manual snapshots only when a plan exists, denies duplicates with identical
- Types: extend
packages/contracts/src/orchestration.ts:168with:OrchestrationPlanVersion(id, planId, threadId, branchId, parentVersionId, childrenIds, planMarkdown, createdAt, createdByUserId, linkedTurnId, derivedThreadId).OrchestrationPlanBranchwith label + headVersionId + rootVersionId.- Optional
selectedPlanVersionIdonOrchestrationLatestTurn.
- Events: update the websocket payload for snapshot (
orchestration.snapshot) and incrementalorchestration.planVersion.checkpointedevents so clients can merge updates without reloading entire threads. - Native API schema: update
apps/web/src/nativeApi/types.ts:1and server implementation so the PlanSidebar button can callplanVersions.createCheckpoint.
- Store slices: extend
apps/web/src/store.ts:131to hydrateplanVersions/planBranchesper thread, andapps/web/src/store.ts:947to merge live version insertions. - Selectors: update
apps/web/src/session-logic.ts:421to computelatestActionablePlanVersion(prefers branch head for running turns) and provide helper selectors for DAG traversal (e.g.,selectPlanVersionNeighbors). - Error handling: capture websocket errors for checkpoint creation and show toasts via
toastManager.
- Checkpoint control: add a button group near
PlanSidebarheader (apps/web/src/components/PlanSidebar.tsx:70) that triggers manual checkpoints. Disable when there is no active plan or an in-flight request. - Version history panel:
- Insert collapsible panel above the markdown view showing each branch as a tree (use vertically stacked list with indent + connectors). Each row displays version id, timestamp (reuse
formatTimestamp), branch label, and derived thread badge if present. - Provide actions per row: “View details”, “Set as compare base/target”, and “Start thread from this version” (existing action now passes version id).
- Insert collapsible panel above the markdown view showing each branch as a tree (use vertically stacked list with indent + connectors). Each row displays version id, timestamp (reuse
- Diff UX:
- When two versions are selected, render a modal that reuses
apps/web/src/components/DiffPanel.tsx:1andDiffWorkerPoolProviderto show a Git-style side-by-side markdown diff. - Precompute markdown diff on the client using the same worker pipeline as Git diffs (buddy hooking).
- When two versions are selected, render a modal that reuses
- Branch creation flow: clicking “Fork branch” in the panel prompts for branch name (optional). If omitted, auto-name
checkpoint-{timestamp}. Persist viaplanVersions.createCheckpointwithbranchLabel. - Visual cues: highlight the branch head that matches
latestTurn.sourcePlanVersionId, show derived-thread links (badge with thread name), and fall back to linear list if there’s only one branch.
- Emit structured log
plan_version.checkpoint_createdwith threadId, planId, versionId. - Add metrics (counter + timer) inside PlanVersionManager for checkpoint latency.
- Update admin tooling or DevTools view to surface branch graph (optional but recommended for debugging DoltLite state).
- Server:
- Unit tests for PlanVersionManager ensuring branching rules, duplicate prevention, and derived thread linkage (Effect-based tests under
apps/server/src/orchestration/Layers/__tests__/PlanVersionManager.test.ts:1). - Integration tests for the websocket method
planVersions.createCheckpointcovering auth and error flows. - Migration tests verifying schema + backfill (DoltLite fixture with
bun run test apps/server/...).
- Unit tests for PlanVersionManager ensuring branching rules, duplicate prevention, and derived thread linkage (Effect-based tests under
- Web:
- Store reducer tests for version DAG updates (
apps/web/src/store.test.ts:1). - Session-logic tests verifying
latestActionablePlanVersion. - Component tests for new PlanSidebar controls and Diff modal (React Testing Library + Vitest).
- Store reducer tests for version DAG updates (
- End-to-end smoke: scripted flow that snapshots a plan, forks a branch, compares versions, and starts a new thread to confirm provenance.
- Automation: finish by running
bun fmt,bun lint, andbun typecheckat repo root.
- DoltLite remains the system of record; auto metadata (timestamp, thread, user) from DoltLite is sufficient—no manual checkpoint note field for v1.
- Version history stays thread-local, but we store explicit derived-thread references for forks.
- Manual checkpoints are opt-in and initiated via the PlanSidebar control; no provider-driven auto snapshots.
- Branch diffs reuse existing Git diff UI/worker code without new diff algorithms.