Route checkpoint reads by ID kind across git backends · Entire

Route checkpoint reads by ID kind across git backends

4a1f5fa→main·Soph·1w ago·5 files·+414 added/-11 removed

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:

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

Sessions

fe977d819cd6View transcript

Changes

5

if err != nil {
    return nil, err
}
writer := newFanoutStore(primary, mirrors)

// Read routing: resolve id-keyed reads by the checkpoint's format across both
// git backends (a ULID lives in refs, a hex ID on the branch or a migrated
// ref), so a coexisting / mid-migration repo reads either format without
// reconfiguring. Writes still go through writer (configured primary + mirrors).
branchStore, refsStore, err := buildKindReadStores(ctx, env, primaryType, primary)
if err != nil {
    return nil, err
}

return &Stores{
    Persistent: newFanoutStore(primary, mirrors),
    Persistent: newKindRoutingStore(writer, branchStore, refsStore, primaryType),
    ephemeral:  newEphemeralStore(repo, refs),
    refs:       refs,
}, nil
}
// buildKindReadStores returns the git-branch and git-refs read stores used for
// id-kind read routing, reusing the already-built primary for whichever kind it
// is and constructing the sibling. A non-branch/refs git-backed primary (not a
// real configuration today, since buildPrimary only accepts git-backed backends)
// yields both freshly built git stores.
func buildKindReadStores(ctx context.Context, env OpenEnv, primaryType string, primary PersistentStore) (branch, refs PersistentStore, err error) {
    switch primaryType {
    case BackendTypeGitBranch:
        branch = primary
        refs, err = build(ctx, env, BackendTypeGitRefs, nil)
    case BackendTypeGitRefs:
        refs = primary
        branch, err = build(ctx, env, BackendTypeGitBranch, nil)
    default:
        if branch, err = build(ctx, env, BackendTypeGitBranch, nil); err == nil {
            refs, err = build(ctx, env, BackendTypeGitRefs, nil)
        }
    }
    return branch, refs, err
}
// resolvePrimaryType returns the configured primary backend type, defaulting to
// the git-branch backend when none is configured.
func resolvePrimaryType(cfg *settings.CheckpointsConfig) string {

}
// kindRoutingStore resolves id-keyed reads across the two git backends so a repo
// running git-refs and git-branch side by side (or mid-migration between them)
// can read checkpoints of BOTH formats without reconfiguring:
//
// - A ULID checkpoint only ever lives in the git-refs store, so a ULID ID is
//   read from refs and NEVER from the branch (regardless of the active backend).
// - A legacy-hex ID is read from the active (configured) primary first. When the
//   active primary is git-refs, it also falls back to the git-branch store,
//   because a hex checkpoint may still sit on the pre-migration v1 branch. Under
//   a git-branch primary the branch is authoritative for hex, so refs is not
//   consulted.
// - List unions both backends (disjoint ID spaces). Writes are NOT kind-routed:
//  they go to the configured primary (+ mirrors) via writer, since a new
// checkpoint's ID is already minted to match the primary's format
// (see checkpoint.GenerateCheckpointID).
type kindRoutingStore struct {
    writer      PersistentStore // configured primary + mirrors (fanout); handles Write
    branch      PersistentStore // git-branch store; serves hex reads
    refs        PersistentStore // git-refs store; serves ULID reads (+ hex under refs primary)
    primaryType string
}

// newKindRoutingStore wraps the write fanout plus the two git read stores. It
// preserves the optional AuthorReader capability (explain relies on it) when both
// read stores provide it — the built-in git backends always do.
func newKindRoutingStore(writer, branch, refs PersistentStore, primaryType string) PersistentStore {
    s := &kindRoutingStore{writer: writer, branch: branch, refs: refs, primaryType: primaryType}
    if _, ok := branch.(AuthorReader); ok {
        if _, ok := refs.(AuthorReader); ok {
            return &kindRoutingStoreWithAuthor{kindRoutingStore: s}
        }
    }
    return s
}