Route checkpoint reads by ID kind across git backends#1630
Conversation
Follow-up #1 from the ULID-emission review: reads resolved only against the configured primary store, so after a git-branch⇄git-refs flip (or during coexistence) a checkpoint stored in the other backend was reported missing. Now `checkpoint.Open` returns a kind-routing store that resolves id-keyed reads by the checkpoint's format: - A ULID is read from the git-refs store only — never the branch (any backend). - A hex ID is read from the active primary first; under a git-refs primary it also falls back to the git-branch store (a hex checkpoint may still sit on the pre-migration v1 branch, or have been migrated into refs). - List unions both backends; GetCheckpointAuthor routes the same way (AuthorReader preserved). Writes are NOT kind-routed — they stay on the configured primary (+ mirrors); the minted ID already matches the primary's format. The router is built once in Open, reusing the primary for its kind and building the sibling read store. All the general read paths (resume, explain, attribution, blame, tokens, attach) inherit routing for free. Tests: routing_store_test.go covers ULID→refs (incl. "a ULID never reads from the branch"), hex→branch, hex-fallback and migrated-hex-in-refs under a refs primary, List union, session-read routing, and author routing. Updated open_config_test to the new "Persistent is always the routing store" invariant. Verified: unit + integration (390) + canary both backends (git-branch, git-refs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: fe977d819cd6
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 4a1f5fa. Configure here.
There was a problem hiding this comment.
Pull request overview
This PR adds checkpoint read routing by ID kind so checkpoint reads work correctly across git-branch and git-refs backends during migrations or when both backends coexist. The routing is implemented by always returning a kindRoutingStore from checkpoint.Open, allowing the CLI to resolve checkpoints by format (ULID vs legacy hex) without requiring config changes.
Changes:
- Introduces
kindRoutingStoreto route id-keyed reads across git-branch and git-refs according to the checkpoint ID kind. - Updates
checkpoint.Opento always wrap the write fanout with the routing store and to build both read backends for routing. - Adds/updates tests and documentation describing the new routing behavior.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/architecture/sessions-and-checkpoints.md | Documents the new id-kind read routing and list/write behavior. |
| cmd/entire/cli/checkpoint/routing_store.go | Implements the routing store, including read-order rules and list merging. |
| cmd/entire/cli/checkpoint/routing_store_test.go | Adds unit tests covering routing behavior for ULID vs hex IDs and backend combinations. |
| cmd/entire/cli/checkpoint/open.go | Updates Open to return the routing store and builds both read stores for routing. |
| cmd/entire/cli/checkpoint/open_config_test.go | Updates expectations to match the new “Persistent is always the routing store” invariant. |
Addresses the two Cursor Bugbot findings on the read-routing PR plus one reuse cleanup from /simplify (the rest of /simplify was judged already-clean — notably firstResolved/readOrder stay centralized, which the fallback fix relies on): - Fetch errors no longer block the branch fallback (Cursor, medium): firstResolved now falls through to the next store on a non-final store's error as well as on "absent". Under a git-refs primary, a hex read whose refs lookup fails on an on-demand ref fetch (network) still resolves from the git-branch store. The final store's result (hit/absent/error) is still returned verbatim, and single-store orders (ULID→refs, hex under a branch primary) are unchanged. - List dedups by checkpoint ID (Cursor, low): a checkpoint present in both backends (a mirrored ULID, or a hex on the branch also migrated into refs) now appears once — keeping the most-recent after the sort. - Extracted sortCheckpointInfosByRecency, shared by the git-branch, git-refs, and routing List implementations (drops the duplicated CreatedAt comparator; refs no longer imports sort). Tests: added refs-fetch-error-falls-back-to-branch and List-dedup cases. Skipped (noted): inlining firstResolved/readOrder (centralizing is worth more, especially now the fallback logic lives in one place); the metaAndPrompts wrapper and build-both-stores (negligible per efficiency review); routing-as-a-backend / backends-declare-their-id-kind (real generalization but over-engineering for two backends). List querying both backends is intentional (union completeness) — a bounded per-command cost, not a hot path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 022f449bbfda

What
Follow-up #1 from the ULID-emission review (#1629). Reads previously resolved only against the configured primary store, so after a git-branch⇄git-refs flip — or while running the two side by side — a checkpoint stored in the other backend was reported missing (and attach could even suggest an unfixable fetch). This adds id-kind read routing so the CLI resolves a checkpoint by its format, wherever it lives.
Routing rules (per Stefan's spec)
Listunions both backends;GetCheckpointAuthorroutes the same way (AuthorReaderpreserved).How
checkpoint.Opennow returns akindRoutingStorewrapping the write fanout plus the two git read stores (reusing the primary for its kind, building the sibling). Every general read path — resume, explain, attribution, blame, tokens, attach — inherits routing for free. No config knob; it's automatic.Tests
routing_store_test.gocovers: ULID→refs (incl. "a ULID never reads from the branch"), hex→branch, hex-fallback and migrated-hex-in-refs under a refs primary,Listunion, session-read routing, author routing.open_config_test.goupdated to the new "Persistentis always the routing store" invariant.Verified: unit + integration (390) + E2E canary on both backends (git-branch 4/4, git-refs 58/59 +1 known skip).
Not in scope (separate follow-up #2)
The write-boundary guard for the ULID⇒refs invariant (warn/reject when a ULID would be condensed onto a git-branch primary). Left deliberately — it needs topology-role awareness (a git-branch mirror of a git-refs primary legitimately receives ULIDs).
Review updates (commit
80c7a4e)Addressed the Cursor Bugbot findings and a
/simplifyreuse cleanup:firstResolvednow falls through to the next store on a non-final store's error as well as on "absent". Under a git-refs primary, a hex read whose refs lookup fails on an on-demand ref fetch (network) still resolves from the git-branch store. The final store's result (hit/absent/error) is returned verbatim, and single-store orders are unchanged.Listdedups by checkpoint ID (Cursor, low): a checkpoint present in both backends (a mirrored ULID, or a hex on the branch also migrated into refs) now appears once (most-recent kept)./simplifyreuse): extractedsortCheckpointInfosByRecency, now used by the git-branch, git-refs, and routingListimplementations.The rest of
/simplifywas judged already-clean:firstResolved/readOrderstay centralized (the fallback fix depends on it), and "routing-as-a-backend / backends-declare-their-id-kind" was deemed over-engineering for two backends.Listquerying both backends is intentional (union completeness) — a bounded per-command cost, not a hot path.🤖 Generated with Claude Code
Note
Medium Risk
Touches the central
checkpoint.Openread path for resume, explain, attach, and list; behavior changes are intentional for migration but any routing bug could surface as missing checkpoints.Overview
Checkpoint reads no longer depend on whichever git backend is configured as primary.
checkpoint.Openalways exposes akindRoutingStorearound the write fanout (primary + mirrors) plus dedicated git-branch and git-refs read stores, built viabuildKindReadStores(reusing the primary where it matches).Routing behavior: ULID IDs are read only from git-refs; legacy hex IDs follow the active primary (branch-only under git-branch, refs then branch fallback under git-refs).
Listmerges both backends; session reads andGetCheckpointAuthoruse the same order; writes still go only through the configured primary and mirrors.Adds
routing_store.gowithfirstResolvedfallback semantics, unit tests for migration/coexistence cases, updatesopen_config_test.goexpectations, and documents read routing insessions-and-checkpoints.md.Reviewed by Cursor Bugbot for commit 4a1f5fa. Configure here.