Add fsstore: test-only reference checkpoint backend · Entire

Add fsstore: test-only reference checkpoint backend

8b512ab→main·

Soph·3w ago·4 files·+686 added/-0 removed

Add a filesystem/JSON reference backend that implements the full api/checkpoint contract, plus a seam integration test that drives a git primary with the fsstore as a configured mirror through checkpoint.Open and asserts all four WriteRequest variants land in both backends while reads resolve from the git primary.

fsstore is registered only via RegisterForTesting (idempotent), never by production code, so a production binary cannot select the "fs" type. It intentionally ignores the contract's git-specific blob-hash fields and stores transcript bytes directly — keeping the example small and making the contract's remaining git leakage concrete. It serves as both the seam's test exercise and a worked example for implementing a new backend.

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

Sessions

5b6ab3e1bdb2View transcript

Changes

4

// Package fsstore is a reference, test-only persistent checkpoint backend that
// stores checkpoints as JSON files on disk. It exists to exercise the pluggable
// backend seam (registry + mirror fan-out) with a real, non-git implementation
// of the api/checkpoint contract, and to serve as a worked example for new
// backends.
//
// It is deliberately NOT registered by production code: only RegisterForTesting
// wires it into the checkpoint registry, so a production binary can never select
// it. As a mirror it receives best-effort write fan-out; it intentionally ignores
// the git-specific blob-hash fields of the contract and stores transcript bytes
// directly, which keeps the example small and makes the contract's remaining
// git leakage concrete.
package fsstore

import (
    "context"
    "encoding/json"
    "fmt"
    "os"
    "path/filepath"
    "sort"
    "strings"
    "sync"

cp "github.com/entireio/cli/api/checkpoint"
    "github.com/entireio/cli/cmd/entire/cli/checkpoint"
    "github.com/entireio/cli/cmd/entire/cli/checkpoint/id"
    "github.com/entireio/cli/redact"
)

// BackendType is the registry type name for the filesystem reference backend.
const BackendType = "fs"

// Store is a JSON-file-backed persistent checkpoint store. One file per
// checkpoint (<root>/<checkpoint-id>.json) holds the root summary plus all
// session content.
type Store struct {
    root string
    mu   sync.Mutex
}

// New constructs a filesystem store rooted at dir.
func New(dir string) *Store {
    return &Store{root: dir}
}

type storedSession struct {
    SessionID  string      `json:"session_id"`
    Metadata   cp.Metadata `json:"metadata"`
    Transcript []byte      `json:"transcript,omitempty"`
    Prompts    string      `json:"prompts,omitempty"`
}

type storedCheckpoint struct {
    Summary  cp.CheckpointSummary `json:"summary"`
    Sessions []storedSession      `json:"sessions"`
}

var (
    _ cp.PersistentStore = (*Store)(nil)
    _ cp.Writer          = (*Store)(nil)
)

func (s *Store) path(checkpointID id.CheckpointID) string {
    return filepath.Join(s.root, string(checkpointID)+".json")
}

// load reads the stored checkpoint, returning (nil, nil) when it does not exist.
func (s *Store) load(checkpointID id.CheckpointID) (*storedCheckpoint, error) {
    data, err := os.ReadFile(s.path(checkpointID))
    if err != nil {
        if os.IsNotExist(err) {
            return nil, nil //nolint:nilnil // absent checkpoint, not an error
        }
        return nil, fmt.Errorf("fsstore: read %s: %w", checkpointID, err)
    }
    var sc storedCheckpoint
    if err := json.Unmarshal(data, &sc); err != nil {
        return nil, fmt.Errorf("fsstore: parse %s: %w", checkpointID, err)
    }
    return &sc, nil
}

func (s *Store) save(sc *storedCheckpoint) error {
    if err := os.MkdirAll(s.root, 0o750); err != nil {
        return fmt.Errorf("fsstore: create root %s: %w", s.root, err)
    }
    data, err := json.MarshalIndent(sc, "", "  ")
    if err != nil {
        return fmt.Errorf("fsstore: encode %s: %w", sc.Summary.CheckpointID, err)
    }
    if err := os.WriteFile(s.path(sc.Summary.CheckpointID), data, 0o644); err != nil { //nolint:gosec // reference test backend
        return fmt.Errorf("fsstore: write %s: %w", sc.Summary.CheckpointID, err)
    }
    return nil
}

// Write dispatches on the request type, mirroring the git store's Write.
func (s *Store) Write(_ context.Context, req cp.WriteRequest) error {
    s.mu.Lock()
    defer s.mu.Unlock()

switch r := req.(type) {
    case cp.Session:
        return s.writeSession(cp.WriteOptions(r))
    case cp.SessionTranscript:
        return s.backfillTranscript(cp.UpdateOptions(r))
    case cp.SessionSummary:
        return s.writeSessionSummary(r)
    case cp.CheckpointAttribution:
        return s.writeAttribution(r)
    default:
        return fmt.Errorf("fsstore: unsupported write request %T", req)
    }
}

func (s *Store) writeSession(opts cp.WriteOptions) error {
    sc, err := s.load(opts.CheckpointID)
    if err != nil {
        return err
    }
    if sc == nil {
        sc = &storedCheckpoint{Summary: cp.CheckpointSummary{CheckpointID: opts.CheckpointID}}
    }

session := storedSession{
        SessionID:  opts.SessionID,
        Metadata:   metadataFromWriteOptions(opts),
        Transcript: opts.Transcript.Bytes(),
        Prompts:    redact.String(strings.Join(opts.Prompts, checkpoint.PromptSeparator)),
    }
    sc.Sessions = upsertSession(sc.Sessions, session)
    recomputeSummary(sc)
    return s.save(sc)
}

func (s *Store) backfillTranscript(opts cp.UpdateOptions) error {
    sc, err := s.load(opts.CheckpointID)
    if err != nil {
        return err
    }
    if sc == nil {
        return fmt.Errorf("fsstore: cannot backfill transcript for unknown checkpoint %s", opts.CheckpointID)
    }
    idx := sessionIndexByID(sc.Sessions, opts.SessionID)
    if idx < 0 {
        return fmt.Errorf("fsstore: cannot backfill transcript for unknown session %q in %s", opts.SessionID, opts.CheckpointID)
    }
    // Replace semantics, but do not clobber sibling fields (matches the git
    // store's stop-time transcript backfill).
    sc.Sessions[idx].Transcript = opts.Transcript.Bytes()
    sc.Sessions[idx].Prompts = redact.String(strings.Join(opts.Prompts, checkpoint.PromptSeparator))
    if len(opts.SkillEvents) > 0 {
        sc.Sessions[idx].Metadata.SkillEvents = opts.SkillEvents
    }
    return s.save(sc)
}

func (s *Store) writeSessionSummary(r cp.SessionSummary) error {
    sc, err := s.load(r.CheckpointID)
    if err != nil {
        return err
    }
    if sc == nil || len(sc.Sessions) == 0 {
        return fmt.Errorf("fsstore: cannot set summary for unknown checkpoint %s", r.CheckpointID)
    }
    sc.Sessions[len(sc.Sessions)-1].Metadata.Summary = r.Summary
    return s.save(sc)
}

func (s *Store) writeAttribution(r cp.CheckpointAttribution) error {
    sc, err := s.load(r.CheckpointID)
    if err != nil {
        return err
    }
    if sc == nil {
        return fmt.Errorf("fsstore: cannot set attribution for unknown checkpoint %s", r.CheckpointID)
    }
    sc.Summary.CombinedAttribution = r.Attribution
    return s.save(sc)
}

// Read returns the checkpoint summary, or (nil, nil) when absent so the
// contract helper normalizes it to ErrCheckpointNotFound.
func (s *Store) Read(_ context.Context, checkpointID id.CheckpointID) (*cp.CheckpointSummary, error) {
    s.mu.Lock()
    defer s.mu.Unlock()
    sc, err := s.load(checkpointID)
    if err != nil || sc == nil {
        return nil, err
    }
    summary := sc.Summary
    return &summary, nil
}

func (s *Store) List(_ context.Context) ([]cp.CheckpointInfo, error) {
    s.mu.Lock()
    defer s.mu.Unlock()

entries, err := os.ReadDir(s.root)
    if err != nil {
        if os.IsNotExist(err) {
            return nil, nil
        }
        return nil, fmt.Errorf("fsstore: list %s: %w", s.root, err)
    }

var infos []cp.CheckpointInfo
    for _, e := range entries {
        if e.IsDir() || !strings.HasSuffix(e.Name(), ".json") {
            continue
        }
        checkpointID := id.CheckpointID(strings.TrimSuffix(e.Name(), ".json"))
        sc, err := s.load(checkpointID)
        if err != nil {
            return nil, err
        }
        if sc == nil {
            continue
        }
        infos = append(infos, infoFromStored(sc))
    }
    sort.Slice(infos, func(i, j int) bool { return infos[i].CreatedAt.After(infos[j].CreatedAt) })
    return infos, nil
}

func (s *Store) ReadSessionContent(_ context.Context, checkpointID id.CheckpointID, sessionIndex int) (*cp.SessionContent, error) {
    s.mu.Lock()
    defer s.mu.Unlock()
    session, err := s.sessionAt(checkpointID, sessionIndex)
    if err != nil {
        return nil, err
    }
    return &cp.SessionContent{
        Metadata:   session.Metadata,
        Transcript: session.Transcript,
        Prompts:    session.Prompts,
    }, nil
}

func (s *Store) ReadSessionMetadata(_ context.Context, checkpointID id.CheckpointID, sessionIndex int) (*cp.Metadata, error) {
    s.mu.Lock()
    defer s.mu.Unlock()
    session, err := s.sessionAt(checkpointID, sessionIndex)
    if err != nil {
        return nil, err
    }
    meta := session.Metadata
    return &meta, nil
}

func (s *Store) ReadSessionPrompts(_ context.Context, checkpointID id.CheckpointID, sessionIndex int) (string, error) {
    s.mu.Lock()
    defer s.mu.Unlock()
    session, err := s.sessionAt(checkpointID, sessionIndex)
    if err != nil {
        return "", err
    }
    return session.Prompts, nil
}

func (s *Store) ReadSessionMetadataAndPrompts(_ context.Context, checkpointID id.CheckpointID, sessionIndex int) (*cp.Metadata, string, error) {
    s.mu.Lock()
    defer s.mu.Unlock()
    session, err := s.sessionAt(checkpointID, sessionIndex)
    if err != nil {
        return nil, "", err
    }
    meta := session.Metadata
    return &meta, session.Prompts, nil
}

func (s *Store) sessionAt(checkpointID id.CheckpointID, sessionIndex int) (*storedSession, error) {
    sc, err := s.load(checkpointID)
    if err != nil {
        return nil, err
    }
    if sc == nil {
        return nil, cp.ErrCheckpointNotFound
    }
    if sessionIndex < 0 || sessionIndex >= len(sc.Sessions) {
        return nil, fmt.Errorf("fsstore: session index %d out of range for %s (%d sessions)", sessionIndex, checkpointID, len(sc.Sessions))
    }
    return &sc.Sessions[sessionIndex], nil
}

func metadataFromWriteOptions(opts cp.WriteOptions) cp.Metadata {
    return cp.Metadata{
        CheckpointID:     opts.CheckpointID,
        SessionID:        opts.SessionID,
        Strategy:         opts.Strategy,
        CreatedAt:        opts.CreatedAt,
        Branch:           opts.Branch,
        CheckpointsCount: opts.CheckpointsCount,
        SaveStepCount:    opts.SaveStepCount,
        FilesTouched:     opts.FilesTouched,
        Agent:            opts.Agent,
        Model:            opts.Model,
        TurnID:           opts.TurnID,
        IsTask:           opts.IsTask,
        ToolUseID:        opts.ToolUseID,
        TokenUsage:       opts.TokenUsage,
        SkillEvents:      opts.SkillEvents,
        SessionMetrics:   opts.SessionMetrics,
        Summary:          opts.Summary,
        Attribution:      opts.Attribution,
        Kind:             opts.Kind,
        ReviewSkills:     opts.ReviewSkills,
        ReviewPrompt:     opts.ReviewPrompt,
    }
}

func upsertSession(sessions []storedSession, session storedSession) []storedSession {
    if idx := sessionIndexByID(sessions, session.SessionID); idx >= 0 {
        sessions[idx] = session
        return sessions
    }
    return append(sessions, session)
}

func sessionIndexByID(sessions []storedSession, sessionID string) int {
    for i := range sessions {
        if sessions[i].SessionID == sessionID {
            return i
        }
    }
    return -1
}

// recomputeSummary rebuilds the aggregated root summary from the sessions, so a
// reader sees one Sessions entry per stored session and aggregate counts.
func recomputeSummary(sc *storedCheckpoint) {
    summary := &sc.Summary
    summary.Sessions = make([]cp.SessionFilePaths, len(sc.Sessions))
    summary.CheckpointsCount = 0
    files := map[string]struct{}{}
    var orderedFiles []string

for i := range sc.Sessions {
        session := &sc.Sessions[i]
        summary.Sessions[i] = cp.SessionFilePaths{
            Metadata:   fmt.Sprintf("%d/metadata.json", i+1),
            Transcript: fmt.Sprintf("%d/full.jsonl", i+1),
            Prompt:     fmt.Sprintf("%d/prompt.txt", i+1),
        }
        summary.CheckpointsCount += session.Metadata.CheckpointsCount
        if session.Metadata.Strategy != "" {
            summary.Strategy = session.Metadata.Strategy
        }
        if session.Metadata.Branch != "" {
            summary.Branch = session.Metadata.Branch
        }
        if session.Metadata.TokenUsage != nil {
            summary.TokenUsage = session.Metadata.TokenUsage
        }
        for _, f := range session.Metadata.FilesTouched {
            if _, seen := files[f]; !seen {
                files[f] = struct{}{}
                orderedFiles = append(orderedFiles, f)
            }
        }
    }
    summary.FilesTouched = orderedFiles
}

func infoFromStored(sc *storedCheckpoint) cp.CheckpointInfo {
    info := cp.CheckpointInfo{
        CheckpointID:     sc.Summary.CheckpointID,
        CheckpointsCount: sc.Summary.CheckpointsCount,
        FilesTouched:     sc.Summary.FilesTouched,
        SessionCount:     len(sc.Sessions),
    }
    if n := len(sc.Sessions); n > 0 {
        last := sc.Sessions[n-1]
        info.SessionID = last.SessionID
        info.CreatedAt = last.Metadata.CreatedAt
        info.Agent = last.Metadata.Agent
        info.IsTask = last.Metadata.IsTask
        info.ToolUseID = last.Metadata.ToolUseID
    }
    info.SessionIDs = make([]string, 0, len(sc.Sessions))
    for i := range sc.Sessions {
        info.SessionIDs = append(info.SessionIDs, sc.Sessions[i].SessionID)
    }
    return info
}
package fsstore

import (
    "context"
    "testing"
    "time"

"github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"

func TestStore_WriteSessionRoundTrips(t *testing.T) {
    t.Parallel()
    ctx := context.Background()
    store := New(t.TempDir())
    cid := id.MustCheckpointID("a1b2c3d4e5f6")

require.NoError(t, store.Write(ctx, cp.Session{
        CheckpointID:     cid,
        SessionID:        "sess-1",
        Strategy:         "manual-commit",
        Transcript:       redact.AlreadyRedacted([]byte("transcript-1")),
        Prompts:          []string{"hello"},
        FilesTouched:     []string{"a.go"},
        CheckpointsCount: 2,
    }))

summary, err := store.Read(ctx, cid)
    require.NoError(t, err)
    require.NotNil(t, summary)
    assert.Equal(t, cid, summary.CheckpointID)
    require.Len(t, summary.Sessions, 1)
    assert.Equal(t, 2, summary.CheckpointsCount)
    assert.Equal(t, []string{"a.go"}, summary.FilesTouched)

content, err := store.ReadSessionContent(ctx, cid, 0)
    require.NoError(t, err)
    assert.Equal(t, []byte("transcript-1"), content.Transcript)
    assert.Contains(t, content.Prompts, "hello")
}

func TestStore_ReadUnknownCheckpoint(t *testing.T) {
    t.Parallel()
    ctx := context.Background()
    store := New(t.TempDir())

summary, err := store.Read(ctx, id.MustCheckpointID("ffffffffffff"))
    require.NoError(t, err)
    assert.Nil(t, summary, "absent checkpoint should read as nil summary")

_, err = store.ReadSessionContent(ctx, id.MustCheckpointID("ffffffffffff"), 0)
    require.ErrorIs(t, err, cp.ErrCheckpointNotFound)
}

func TestStore_BackfillTranscriptReplacesWithoutClobbering(t *testing.T) {
    t.Parallel()
    ctx := context.Background()
    store := New(t.TempDir())
    cid := id.MustCheckpointID("a1b2c3d4e5f6")

require.NoError(t, store.Write(ctx, cp.Session{
        CheckpointID: cid, SessionID: "sess-1", Strategy: "manual-commit",
        Transcript: redact.AlreadyRedacted([]byte("old")), FilesTouched: []string{"a.go"},
    }))
    require.NoError(t, store.Write(ctx, cp.SessionTranscript{
        CheckpointID: cid, SessionID: "sess-1",
        Transcript: redact.AlreadyRedacted([]byte("new")), Prompts: []string{"p"},
    }))

content, err := store.ReadSessionContent(ctx, cid, 0)
    require.NoError(t, err)
    assert.Equal(t, []byte("new"), content.Transcript)
    // Sibling field (files touched, surfaced via the summary) must survive.
    summary, err := store.Read(ctx, cid)
    require.NoError(t, err)
    assert.Equal(t, []string{"a.go"}, summary.FilesTouched)
}

func TestStore_SessionSummaryAndAttribution(t *testing.T) {
    t.Parallel()
    ctx := context.Background()
    store := New(t.TempDir())
    cid := id.MustCheckpointID("a1b2c3d4e5f6")

require.NoError(t, store.Write(ctx, cp.Session{
        CheckpointID: cid, SessionID: "sess-1", Strategy: "manual-commit",
        Transcript: redact.AlreadyRedacted([]byte("t")),
    }))
    require.NoError(t, store.Write(ctx, cp.SessionSummary{
        CheckpointID: cid, Summary: &cp.Summary{Intent: "do a thing", Outcome: "did it"},
    }))
    require.NoError(t, store.Write(ctx, cp.CheckpointAttribution{
        CheckpointID: cid, Attribution: &cp.Attribution{AgentLines: 10, AgentPercentage: 80},
    }))

meta, err := store.ReadSessionMetadata(ctx, cid, 0)
    require.NoError(t, err)
    require.NotNil(t, meta.Summary)
    assert.Equal(t, "do a thing", meta.Summary.Intent)

summary, err := store.Read(ctx, cid)
    require.NoError(t, err)
    require.NotNil(t, summary.CombinedAttribution)
    assert.Equal(t, 10, summary.CombinedAttribution.AgentLines)
}

func TestStore_ListReturnsCheckpoints(t *testing.T) {
    t.Parallel()
    ctx := context.Background()
    store := New(t.TempDir())

require.NoError(t, store.Write(ctx, cp.Session{
        CheckpointID: id.MustCheckpointID("a1b2c3d4e5f6"), SessionID: "s1",
        CreatedAt: time.Unix(100, 0), Transcript: redact.AlreadyRedacted([]byte("t")),
    }))
    require.NoError(t, store.Write(ctx, cp.Session{
        CheckpointID: id.MustCheckpointID("b1b2c3d4e5f6"), SessionID: "s2",
        CreatedAt: time.Unix(200, 0), Transcript: redact.AlreadyRedacted([]byte("t")),
    }))

infos, err := store.List(ctx)
    require.NoError(t, err)
    require.Len(t, infos, 2)
    // Sorted newest-first by CreatedAt.
    assert.Equal(t, id.MustCheckpointID("b1b2c3d4e5f6"), infos[0].CheckpointID)
}

func TestStore_FactoryRequiresPath(t *testing.T) {
    t.Parallel()
    _, err := factory(context.Background(), checkpoint.OpenEnv{}, nil)
    require.Error(t, err)
    assert.Contains(t, err.Error(), "config.path is required")
}
package fsstore

import (
    "context"
    "encoding/json"
    "errors"
    "fmt"
    "sync"

cp "github.com/entireio/cli/api/checkpoint"
    "github.com/entireio/cli/cmd/entire/cli/checkpoint"
)

// Config is the fsstore backend's settings "config" block.
type Config struct {
    Path string `json:"path"`
}

var registerOnce sync.Once

// RegisterForTesting registers the fsstore backend under its type so tests can
// select it as a checkpoint mirror (or primary in fsstore's own tests). It is
// the only path that adds fsstore to the registry: production code never calls
// it, so a production binary cannot resolve the "fs" backend. Registration is
// process-wide and idempotent (checkpoint.Register panics on duplicates).
func RegisterForTesting() {
    registerOnce.Do(func() {
        checkpoint.Register(BackendType, factory)
    })
}

//nolint:ireturn // must return the contract interface to satisfy checkpoint.Factory
func factory(_ context.Context, _ checkpoint.OpenEnv, cfg json.RawMessage) (cp.PersistentStore, error) {
    var c Config
    if len(cfg) > 0 {
        if err := json.Unmarshal(cfg, &c); err != nil {
            return nil, fmt.Errorf("fsstore: invalid config: %w", err)
        }
    }
    if c.Path == "" {
        return nil, errors.New("fsstore: config.path is required")
    }
    return New(c.Path), nil
}
package fsstore

import (
    "context"
    "encoding/json"
    "os"
    "path/filepath"
    "testing"

git "github.com/go-git/go-git/v6"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"

cp "github.com/entireio/cli/api/checkpoint"
    "github.com/entireio/cli/cmd/entire/cli/checkpoint"
    "github.com/entireio/cli/cmd/entire/cli/checkpoint/id"
    "github.com/entireio/cli/cmd/entire/cli/testutil"
    "github.com/entireio/cli/redact"
)

// TestSeam_GitPrimaryWithFsMirror exercises the full pluggable seam: a git
// primary with the fsstore as a configured mirror, driven through
// checkpoint.Open. It writes all four WriteRequest variants and asserts each
// lands in BOTH backends, while reads resolve from the git primary.
//
// Not parallel: uses t.Chdir so settings + ref resolution target the test repo.
func TestSeam_GitPrimaryWithFsMirror(t *testing.T) {
    RegisterForTesting()

dir := t.TempDir()
    testutil.InitRepo(t, dir)
    testutil.WriteFile(t, dir, "README.md", "# test")
    testutil.GitAdd(t, dir, "README.md")
    testutil.GitCommit(t, dir, "init")

mirrorDir := filepath.Join(t.TempDir(), "fs-mirror")
    writeMirrorSettings(t, dir, mirrorDir)
    t.Chdir(dir)

repo, err := git.PlainOpen(dir)
    require.NoError(t, err)
    stores, err := checkpoint.Open(context.Background(), repo, checkpoint.OpenOptions{})
    require.NoError(t, err)

ctx := context.Background()
    cid := id.MustCheckpointID("a1b2c3d4e5f6")
    const sessionID = "sess-1"

// 1. Session: create the checkpoint.
    require.NoError(t, stores.Persistent.Write(ctx, cp.Session{
        CheckpointID: cid, SessionID: sessionID, Strategy: "manual-commit",
        Transcript: redact.AlreadyRedacted([]byte("initial transcript")),
        Prompts:    []string{"do the thing"}, FilesTouched: []string{"a.go"},
        AuthorName: "Test", AuthorEmail: "test@example.com",
    }))
    // 2. SessionTranscript: replace transcript at stop time.
    require.NoError(t, stores.Persistent.Write(ctx, cp.SessionTranscript{
        CheckpointID: cid, SessionID: sessionID,
        Transcript: redact.AlreadyRedacted([]byte("final transcript")),
        Prompts:    []string{"do the thing"},
    }))
    // 3. SessionSummary: set the latest session's summary.
    require.NoError(t, stores.Persistent.Write(ctx, cp.SessionSummary{
        CheckpointID: cid, Summary: &cp.Summary{Intent: "intent-x", Outcome: "outcome-y"},
    }))
    // 4. CheckpointAttribution: set combined attribution.
    require.NoError(t, stores.Persistent.Write(ctx, cp.CheckpointAttribution{
        CheckpointID: cid, Attribution: &cp.Attribution{AgentLines: 7, AgentPercentage: 70},
    }))

// Reads resolve from the git primary.
    t.Run("git primary", func(t *testing.T) {
        assertAllVariants(t, stores.Persistent, cid)
    })

// The fsstore mirror independently received every write.
    t.Run("fs mirror", func(t *testing.T) {
        mirror := New(mirrorDir)
        assertAllVariants(t, mirror, cid)
    })
}

// assertAllVariants verifies that all four writes are visible in a backend.
func assertAllVariants(t *testing.T, store cp.PersistentStore, cid id.CheckpointID) {
    t.Helper()
    ctx := context.Background()

summary, err := store.Read(ctx, cid)
    require.NoError(t, err)
    require.NotNil(t, summary, "checkpoint should exist")
    require.Len(t, summary.Sessions, 1)

// SessionTranscript landed.
    content, err := store.ReadSessionContent(ctx, cid, 0)
    require.NoError(t, err)
    assert.Equal(t, []byte("final transcript"), content.Transcript)

// SessionSummary landed.
    meta, err := store.ReadSessionMetadata(ctx, cid, 0)
    require.NoError(t, err)
    require.NotNil(t, meta.Summary)
    assert.Equal(t, "intent-x", meta.Summary.Intent)

// CheckpointAttribution landed.
    require.NotNil(t, summary.CombinedAttribution)
    assert.Equal(t, 7, summary.CombinedAttribution.AgentLines)
}

func writeMirrorSettings(t *testing.T, repoDir, mirrorDir string) {
    t.Helper()
    // json-encode the path so separators / spaces are escaped correctly.
    encodedPath, err := json.Marshal(mirrorDir)
    require.NoError(t, err)
    body := `{"enabled": true, "checkpoints": {"primary": {"type": "git"}, "mirrors": [{"type": "fs", "config": {"path": ` +
        string(encodedPath) + `}}]}}`
    require.NoError(t, os.MkdirAll(filepath.Join(repoDir, ".entire"), 0o755))
    require.NoError(t, os.WriteFile(filepath.Join(repoDir, ".entire", "settings.json"), []byte(body), 0o644))
}