checkpoint: rename temporary types to ephemeral vocabulary · Entire

checkpoint: rename temporary types to ephemeral vocabulary

5710439→main·

Soph·3w ago·14 files·+96 added/-96 removed

Type-level rename of the shadow-branch surface (method names and the generic Read/Write collapse follow): TemporaryStore -> EphemeralStore WriteTemporaryOptions -> WriteEphemeralOptions WriteTemporaryResult -> WriteEphemeralResult WriteTemporaryTaskOptions -> WriteEphemeralTaskOptions ReadTemporaryResult -> ReadEphemeralResult TemporaryInfo -> EphemeralInfo TemporaryCheckpointInfo -> EphemeralCheckpointInfo

Sessions

7c586656fb39View transcript

[?
Review Checkpoint Commit f16b7101Codex·GPT-5.5·1 step](/content/gh/entireio/cli/session/019eefbd-bb6a-7f51-a909-feb4cd95588d#timeline-7c586656fb39/index.html)

Changes

14

62 unmodified lines

63
64
65
66
66
67
68
69
54 unmodified lines

124
125
126
127
127
128
129
130
42 unmodified lines

173
174
175
176
176
177
178
179
29 unmodified lines

209
210
211
212
212
213
214
215
40 unmodified lines

256
257
258
259
259
260
261
262

62 unmodified lines

// measure the first-checkpoint path (which runs collectChangedFiles).
            // We use a unique session ID per iteration to get a fresh shadow branch.
            sid := fmt.Sprintf("bench-first-%d", i)
            _, writeErr := repo.Ephemeral.WriteTemporary(ctx, checkpoint.WriteTemporaryOptions{
            _, writeErr := repo.Ephemeral.WriteTemporary(ctx, checkpoint.WriteEphemeralOptions{
                SessionID:         sid,
                BaseCommit:        repo.HeadHash,
                WorktreeID:        repo.WorktreeID,
54 unmodified lines

ctx := context.Background()
        b.ResetTimer()
        for range b.N {
            _, writeErr := repo.Ephemeral.WriteTemporary(ctx, checkpoint.WriteTemporaryOptions{
            _, writeErr := repo.Ephemeral.WriteTemporary(ctx, checkpoint.WriteEphemeralOptions{
                SessionID:         sessionID,
                BaseCommit:        repo.HeadHash,
                WorktreeID:        repo.WorktreeID,
42 unmodified lines

ctx := context.Background()
        b.ResetTimer()
        for range b.N {
            _, writeErr := repo.Ephemeral.WriteTemporary(ctx, checkpoint.WriteTemporaryOptions{
            _, writeErr := repo.Ephemeral.WriteTemporary(ctx, checkpoint.WriteEphemeralOptions{
                SessionID:         sessionID,
                BaseCommit:        repo.HeadHash,
                WorktreeID:        repo.WorktreeID,
29 unmodified lines

ctx := context.Background()
        b.ResetTimer()
        for range b.N {
            result, writeErr := repo.Ephemeral.WriteTemporary(ctx, checkpoint.WriteTemporaryOptions{
            result, writeErr := repo.Ephemeral.WriteTemporary(ctx, checkpoint.WriteEphemeralOptions{
                SessionID:         sessionID,
                BaseCommit:        repo.HeadHash,
                WorktreeID:        repo.WorktreeID,
40 unmodified lines

ctx := context.Background()
        b.ResetTimer()
        for range b.N {
            _, writeErr := repo.Ephemeral.WriteTemporary(ctx, checkpoint.WriteTemporaryOptions{
            _, writeErr := repo.Ephemeral.WriteTemporary(ctx, checkpoint.WriteEphemeralOptions{
                SessionID:         sessionID,
                BaseCommit:        repo.HeadHash,
                WorktreeID:        repo.WorktreeID,

Mcmd/entire/cli/benchutil/bench_test.go+5/-5

41 unmodified lines

42
43
44
45
45
46
47
48
312 unmodified lines

361
362
363
364
364
365
366
367

41 unmodified lines

Store *checkpoint.GitStore

// Ephemeral is the shadow-branch (temporary) checkpoint store for this repo.
    Ephemeral checkpoint.TemporaryStore
    Ephemeral checkpoint.EphemeralStore

// HeadHash is the current HEAD commit hash string.
    HeadHash string
312 unmodified lines

b.Fatalf("write transcript: %v", err)
    }

_, err := br.Ephemeral.WriteTemporary(context.Background(), checkpoint.WriteTemporaryOptions{
    _, err := br.Ephemeral.WriteTemporary(context.Background(), checkpoint.WriteEphemeralOptions{
        SessionID:         sessionID,
        BaseCommit:        br.HeadHash,
        WorktreeID:        br.WorktreeID,

Mcmd/entire/cli/benchutil/benchutil.go+2/-2

58 unmodified lines

59
60
61
62
63
64
65
66
67
68
69
62
63
64
65
66
67
68
69
70
71
72
73
74
75
74
75
76
77
78
2 unmodified lines

81
82
83
84
85
84
85
86
87
88
33 unmodified lines

122
123
124
125
126
125
126
127
128
129
10 unmodified lines

140
141
142
143
144
143
144
145
146
147
503 unmodified lines

651
652
653
654
654
655
656
657
657
658
659
660
50 unmodified lines

711
712
713
714
714
715
716
716
717
718
719

58 unmodified lines

Committed
)

// TemporaryStore provides the production shadow-branch checkpoint surface.
type TemporaryStore interface {
    WriteTemporary(ctx context.Context, opts WriteTemporaryOptions) (WriteTemporaryResult, error)
    WriteTemporaryTask(ctx context.Context, opts WriteTemporaryTaskOptions) (plumbing.Hash, error)
    ListTemporary(ctx context.Context) ([]TemporaryInfo, error)
    ListTemporaryCheckpoints(ctx context.Context, baseCommit, worktreeID, sessionID string, limit int) ([]TemporaryCheckpointInfo, error)
    ListCheckpointsForBranch(ctx context.Context, branchName, sessionID string, limit int) ([]TemporaryCheckpointInfo, error)
    ListAllTemporaryCheckpoints(ctx context.Context, sessionID string, limit int) ([]TemporaryCheckpointInfo, error)
// EphemeralStore provides the production shadow-branch checkpoint surface.
type EphemeralStore interface {
    WriteTemporary(ctx context.Context, opts WriteEphemeralOptions) (WriteEphemeralResult, error)
    WriteTemporaryTask(ctx context.Context, opts WriteEphemeralTaskOptions) (plumbing.Hash, error)
    ListTemporary(ctx context.Context) ([]EphemeralInfo, error)
    ListTemporaryCheckpoints(ctx context.Context, baseCommit, worktreeID, sessionID string, limit int) ([]EphemeralCheckpointInfo, error)
    ListCheckpointsForBranch(ctx context.Context, branchName, sessionID string, limit int) ([]EphemeralCheckpointInfo, error)
    ListAllTemporaryCheckpoints(ctx context.Context, sessionID, limit int) ([]EphemeralCheckpointInfo, error)
    GetTranscriptFromCommit(ctx context.Context, commitHash plumbing.Hash, metadataDir string, agentType types.AgentType) ([]byte, error)
    ShadowBranchExists(baseCommit, worktreeID string) bool
}

// WriteTemporaryResult contains the result of writing a temporary checkpoint.
type WriteTemporaryResult struct {
// WriteEphemeralResult contains the result of writing a temporary checkpoint.
type WriteEphemeralResult struct {
    // CommitHash is the hash of the created or existing checkpoint commit
    CommitHash plumbing.Hash

2 unmodified lines

Skipped bool
}

// WriteTemporaryOptions contains options for writing a temporary checkpoint.
type WriteTemporaryOptions struct {
// WriteEphemeralOptions contains options for writing a temporary checkpoint.
type WriteEphemeralOptions struct {
    // SessionID is the session identifier
    SessionID string

33 unmodified lines

IsFirstCheckpoint bool
}

// ReadTemporaryResult contains the result of reading a temporary checkpoint.
type ReadTemporaryResult struct {
// ReadEphemeralResult contains the result of reading a temporary checkpoint.
type ReadEphemeralResult struct {
    // CommitHash is the hash of the checkpoint commit
    CommitHash plumbing.Hash

10 unmodified lines

Timestamp time.Time
}

// TemporaryInfo contains summary information about a shadow branch.
type TemporaryInfo struct {
// EphemeralInfo contains summary information about a shadow branch.
type EphemeralInfo struct {
    // BranchName is the full branch name (e.g., "entire/abc1234")
    BranchName string

503 unmodified lines

Message string
}

// WriteTemporaryTaskOptions contains options for writing a task checkpoint.
// WriteEphemeralTaskOptions contains options for writing a task checkpoint.
// Task checkpoints are created when a subagent completes and contain both
// code changes and task-specific metadata.
type WriteTemporaryTaskOptions struct {
type WriteEphemeralTaskOptions struct {
    // SessionID is the session identifier
    SessionID string

50 unmodified lines

IncrementalData []byte
}

// TemporaryCheckpointInfo contains information about a single commit on a shadow branch.
// EphemeralCheckpointInfo contains information about a single commit on a shadow branch.
// Used by ListTemporaryCheckpoints to provide rewind point data.
type TemporaryCheckpointInfo struct {
type EphemeralCheckpointInfo struct {
    // CommitHash is the hash of the checkpoint commit
    CommitHash plumbing.Hash

Mcmd/entire/cli/checkpoint/checkpoint.go+20/-20

349 unmodified lines

350
351
352
353
353
354
355
356
15 unmodified lines

372
373
374
375
375
376
377
378
20 unmodified lines

399
400
401
402
402
403
404
405
1397 unmodified lines

1803
1804
1805
1806
1806
1807
1808
1809
121 unmodified lines

1931
1932
1933
1934
1934
1935
1936
1937
95 unmodified lines

2033
2034
2035
2036
2036
2037
2038
2039
109 unmodified lines

2149
2150
2151
2152
2152
2153
2154
2155
110 unmodified lines

2266
2267
2268
2269
2269
2270
2271
2272
94 unmodified lines

2367
2368
2369
2370
2370
2371
2372
2373
12 unmodified lines

2386
2387
2388
2389
2389
2390
2391
2392
95 unmodified lines

2488
2489
2490
2491
2491
2492
2493
2494
9 unmodified lines

2504
2505
2506
2507
2507
2508
2509
2510
88 unmodified lines

2599
2600
2601
2602
2602
2603
2604
2605
9 unmodified lines

2615
2616
2617
2618
2618
2619
2620
2621
100 unmodified lines

2722
2723
2724
2725
2725
2726
2727
2728
108 unmodified lines

2837
2838
2839
2840
2840
2841
2842
2843
92 unmodified lines

2936
2937
2938
2939
2939
2940
2941
2942
90 unmodified lines

3033
3034
3035
3036
3036
3037
3038
3039
1100 unmodified lines

4140
4141
4142
4143
4143
4144
4145
4146
281 unmodified lines

4428
4429
4430
4431
4431
4432
4433
4434
100 unmodified lines

4535
4536
4537
4538
4538
4539
4540
4541

349 unmodified lines

// First checkpoint should be created
    baseCommit := initialCommit.String()
    result1, err := store.WriteTemporary(context.Background(), WriteTemporaryOptions{
    result1, err := store.WriteTemporary(context.Background(), WriteEphemeralOptions{
        SessionID:         "test-session",
        BaseCommit:        baseCommit,
        ModifiedFiles:     []string{"test.go"},
15 unmodified lines

}

// Second checkpoint with identical content should be skipped
    result2, err := store.WriteTemporary(context.Background(), WriteTemporaryOptions{
    result2, err := store.WriteTemporary(context.Background(), WriteEphemeralOptions{
        SessionID:         "test-session",
        BaseCommit:        baseCommit,
        ModifiedFiles:     []string{"test.go"},
20 unmodified lines

t.Fatalf("failed to modify test file: %v", err)
    }

result3, err := store.WriteTemporary(context.Background(), WriteTemporaryOptions{
    result3, err := store.WriteTemporary(context.Background(), WriteEphemeralOptions{
        SessionID:         "test-session",
        BaseCommit:        baseCommit,
        ModifiedFiles:     []string{"test.go"},
1397 unmodified lines

store := newEphemeralStore(repo, DefaultV1Refs())
    baseCommit := initialCommit.String()

result, err := store.WriteTemporary(context.Background(), WriteTemporaryOptions{
    result, err := store.WriteTemporary(context.Background(), WriteEphemeralOptions{
        SessionID:         "test-session",
        BaseCommit:        baseCommit,
        ModifiedFiles:     []string{}, // Agent hasn't modified anything
121 unmodified lines

}

store := newEphemeralStore(repo, DefaultV1Refs())
    result, err := store.WriteTemporary(context.Background(), WriteTemporaryOptions{
    result, err := store.WriteTemporary(context.Background(), WriteEphemeralOptions{
        SessionID:         "test-session",
        BaseCommit:        initialCommit.String(),
        ModifiedFiles:     tt.modifiedFiles(tempDir, mainFile),
95 unmodified lines

store := newEphemeralStore(repo, DefaultV1Refs())
    baseCommit := initialCommit.String()

result, err := store.WriteTemporary(context.Background(), WriteTemporaryOptions{
    result, err := store.WriteTemporary(context.Background(), WriteEphemeralOptions{
        SessionID:         "test-session",
        BaseCommit:        baseCommit,
        ModifiedFiles:     []string{},
109 unmodified lines

store := newEphemeralStore(repo, DefaultV1Refs())
    baseCommit := initialCommit.String()

result, err := store.WriteTemporary(context.Background(), WriteTemporaryOptions{
    result, err := store.WriteTemporary(context.Background(), WriteEphemeralOptions{
        SessionID:         "test-session",
        BaseCommit:        baseCommit,
        ModifiedFiles:     []string{},
94 unmodified lines

store := newEphemeralStore(repo, DefaultV1Refs())
    baseCommit := initialCommit.String()

result, err := store.WriteTemporary(context.Background(), WriteTemporaryOptions{
    result, err := store.WriteTemporary(context.Background(), WriteEphemeralOptions{
        SessionID:         "test-session",
        BaseCommit:        baseCommit,
        ModifiedFiles:     []string{},
1100 unmodified lines

store := newEphemeralStore(repo, DefaultV1Refs())
    baseCommit := initialCommit.String()

_, err = store.WriteTemporaryTask(context.Background(), WriteTemporaryTaskOptions{
    _, err = store.WriteTemporaryTask(context.Background(), WriteEphemeralTaskOptions{
        SessionID:              "test-session",
        BaseCommit:             baseCommit,
        ToolUseID:              "toolu_test456",
281 unmodified lines

t.Chdir(tempDir)

store := newEphemeralStore(repo, DefaultV1Refs())
    commitHash, err := store.WriteTemporaryTask(context.Background(), WriteTemporaryTaskOptions{
    commitHash, err := store.WriteTemporaryTask(context.Background(), WriteEphemeralTaskOptions{
        SessionID:      "test-session",
        BaseCommit:     initialCommit.String(),
        ToolUseID:      "toolu_symlink123",
100 unmodified lines

baseCommit := initialCommit.String()

// Write task checkpoint where subagent reports .env as modified
    commitHash, err := store.WriteTemporaryTask(context.Background(), WriteTemporaryTaskOptions{
    commitHash, err := store.WriteTemporaryTask(context.Background(), WriteEphemeralTaskOptions{
        SessionID:              "test-session",
        BaseCommit:             baseCommit,
        ToolUseID:              "toolu_test789",

Mcmd/entire/cli/checkpoint/checkpoint_test.go+21/-21

25 unmodified lines

26
27
28
29
29
30
31
32
22 unmodified lines

55
56
57
58
58
59
60
61

25 unmodified lines

// Primary is the committed store that serves committed reads and writes.
    Primary PersistentStore

temporary TemporaryStore
    temporary EphemeralStore
    refs      CommittedRefs
}

22 unmodified lines

// Temporary returns the git-backed temporary shadow-branch store.
func (s *Stores) Temporary() TemporaryStore { return s.temporary } //nolint:ireturn // temporary store capability is the abstraction boundary
func (s *Stores) Temporary() EphemeralStore { return s.temporary } //nolint:ireturn // temporary store capability is the abstraction boundary

// Refs returns the resolved committed-ref topology.
func (s *Stores) Refs() CommittedRefs { return s.refs }

Mcmd/entire/cli/checkpoint/open.go+2/-2

10 unmodified lines

11
12
13
14
14
15
16
17
23 unmodified lines

41
42
43
44
44
45
46
47

10 unmodified lines

_ PersistentStore = (*GitStore)(nil)
    _ AuthorReader    = (*GitStore)(nil)
    _ Writer          = (*GitStore)(nil)
    _ TemporaryStore  = (*ephemeralStore)(nil)
    _ EphemeralStore  = (*ephemeralStore)(nil)
// GitStore is the committed (persistent) checkpoint store. Writes target
23 unmodified lines

// store. Most callers reach it via Open(...).Temporary(); this direct
// constructor exists for benchmarks and tests that exercise the shadow-branch
// surface without the full facade.
func NewEphemeralStore(repo *git.Repository, refs CommittedRefs) TemporaryStore { //nolint:ireturn // temporary store capability is the abstraction boundary
func NewEphemeralStore(repo *git.Repository, refs CommittedRefs) EphemeralStore { //nolint:ireturn // temporary store capability is the abstraction boundary
    return newEphemeralStore(repo, refs)
}

Mcmd/entire/cli/checkpoint/store.go+2/-2

53 unmodified lines

54
55
56
57
57
58
59
60
60
61
62
63
64
65
65
66
67
68
11 unmodified lines

80
81
82
83
83
84
85
86
15 unmodified lines

102
103
104
105
105
106
107
108
108
109
110
111
22 unmodified lines

134
135
136
137
137
138
139
140
7 unmodified lines

148
149
150
151
151
152
153
154
20 unmodified lines

175
176
177
178
178
179
180
181
1 unmodified line

183
184
185
186
186
187
188
189
15 unmodified lines

205
206
207
208
208
209
210
211
3 unmodified lines

215
216
217
218
218
219
220
221
3 unmodified lines

225
226
227
228
228
229
230
231
20 unmodified lines

252
253
254
255
255
256
257
258
14 unmodified lines

273
274
275
276
276
277
278
279
81 unmodified lines

361
362
363
364
364
365
366
367
121 unmodified lines

489
490
491
492
492
493
494
495
1 unmodified line

497
498
499
500
500
501
502
503
504
505
506
506
507
508
509
10 unmodified lines

520
521
522
523
523
524
525
526
20 unmodified lines

547
548
549
550
550
551
552
553
31 unmodified lines

585
586
587
588
588
589
590
591
4 unmodified lines

596
597
598
599
599
600
601
602

53 unmodified lines

// Returns the result containing commit hash and whether it was skipped.
// If the new tree hash matches the last checkpoint's tree hash, the checkpoint
// is skipped to avoid duplicate commits (deduplication).
func (s *ephemeralStore) WriteTemporary(ctx context.Context, opts WriteTemporaryOptions) (WriteTemporaryResult, error) {
func (s *ephemeralStore) WriteTemporary(ctx context.Context, opts WriteEphemeralOptions) (WriteEphemeralResult, error) {
    // Validate base commit - required for shadow branch naming
    if opts.BaseCommit == "" {
        return WriteTemporaryResult{}, errors.New("BaseCommit is required for temporary checkpoint")
        return WriteEphemeralResult{}, errors.New("BaseCommit is required for temporary checkpoint")
    }

// Validate session ID to prevent path traversal
    if err := validation.ValidateSessionID(opts.SessionID); err != nil {
        return WriteTemporaryResult{}, fmt.Errorf("invalid temporary checkpoint options: %w", err)
        return WriteEphemeralResult{}, fmt.Errorf("invalid temporary checkpoint options: %w", err)
    }

// Get shadow branch name
11 unmodified lines

// all unchanged tracked files. We also capture user's pre-existing deletions.
        result, err := collectChangedFiles(ctx, s.repo)
        if err != nil {
            return WriteTemporaryResult{}, fmt.Errorf("failed to collect changed files: %w", err)
            return WriteEphemeralResult{}, fmt.Errorf("failed to collect changed files: %w", err)
        }
        allFiles = result.Changed
        // Merge user's pre-existing deletions with agent's deletions
15 unmodified lines

repoRoot, commonDir, err := s.repoDirs(ctx)
    if err != nil {
        return WriteTemporaryResult{}, fmt.Errorf("failed to resolve repo dirs: %w", err)
        return WriteEphemeralResult{}, fmt.Errorf("failed to resolve repo dirs: %w", err)
    }

var result WriteTemporaryResult
    var result WriteEphemeralResult
    // withShadowBranchFlock serializes all writers targeting this shadow
    // branch — across goroutines and across processes — so the inner CAS
    // only sees contention from external `git update-ref` callers (rare).
22 unmodified lines

// Deduplication: skip if tree hash matches the current shadow tip.
        if lastTreeHash != plumbing.ZeroHash && treeHash == lastTreeHash {
            result = WriteTemporaryResult{
            result = WriteEphemeralResult{
                CommitHash: parentHash,
                Skipped:    true,
            }
        }

releaseFlock(ctx)
        return result, nil
    }
1 unmodified line

// ReadTemporary reads the latest checkpoint from a shadow branch.
// Returns nil if the shadow branch doesn't exist.
// worktreeID should be empty for main worktree or the internal git worktree name for linked worktrees.
func (s *ephemeralStore) ReadTemporary(ctx context.Context, baseCommit, worktreeID string) (*ReadTemporaryResult, error) {
func (s *ephemeralStore) ReadTemporary(ctx context.Context, baseCommit, worktreeID string) (*ReadEphemeralResult, error) {
    if err := ctx.Err(); err != nil {
        return nil, err //nolint:wrapcheck // Propagating context cancellation
    }
15 unmodified lines

sessionID, _ := trailers.ParseSession(commit.Message)
    metadataDir, _ := trailers.ParseMetadata(commit.Message)

return &ReadTemporaryResult{
    return &ReadEphemeralResult{
        CommitHash:  ref.Hash(),
        TreeHash:    commit.TreeHash,
        SessionID:   sessionID,
3 unmodified lines

// ListTemporary lists all shadow branches with their checkpoint info.
func (s *ephemeralStore) ListTemporary(ctx context.Context) ([]TemporaryInfo, error) {
func (s *ephemeralStore) ListTemporary(ctx context.Context) ([]EphemeralInfo, error) {
    if err := ctx.Err(); err != nil {
        return nil, err //nolint:wrapcheck // Propagating context cancellation
    }
3 unmodified lines

return nil, fmt.Errorf("failed to list branches: %w", err)
    
    var results []TemporaryInfo
    var results []EphemeralInfo
    err = iter.ForEach(func(ref *plumbing.Reference) error {
        if err := ctx.Err(); err != nil {
            return err //nolint:wrapcheck // Propagating context cancellation
        }
20 unmodified lines

// Extract base commit from branch name (handles new "entire/<commit>-<worktreeHash>" format)
    baseCommit, _, _ := ParseShadowBranchName(branchName)

results = append(results, TemporaryInfo{
    results = append(results, EphemeralInfo{
        BranchName:   branchName,
        BaseCommit:   baseCommit,
        LatestCommit: ref.Hash(),
14 unmodified lines

// WriteTemporaryTask writes a task checkpoint to a shadow branch.
// Task checkpoints include both code changes and task-specific metadata.
// Returns the commit hash of the created checkpoint.
func (s *ephemeralStore) WriteTemporaryTask(ctx context.Context, opts WriteTemporaryTaskOptions) (plumbing.Hash, error) {
func (s *ephemeralStore) WriteTemporaryTask(ctx context.Context, opts WriteEphemeralTaskOptions) (plumbing.Hash, error) {
    // Validate base commit - required for shadow branch naming
    if opts.BaseCommit == "" {
        return plumbing.ZeroHash, errors.New("BaseCommit is required for task checkpoint")
81 unmodified lines

// Uses ApplyTreeChanges (tree surgery) instead of FlattenTree+BuildTreeFromEntries,
// so only affected subtrees are read/rebuilt.
func (s *ephemeralStore) addTaskMetadataToTree(ctx context.Context, baseTreeHash plumbing.Hash, opts WriteTemporaryTaskOptions) (plumbing.Hash, error) {
func (s *ephemeralStore) addTaskMetadataToTree(ctx context.Context, baseTreeHash plumbing.Hash, opts WriteEphemeralTaskOptions) (plumbing.Hash, error) {
    // Compute metadata paths
    sessionMetadataDir := paths.EntireMetadataDir + "/" + opts.SessionID
    taskMetadataDir := sessionMetadataDir + "/tasks/" + opts.ToolUseID
121 unmodified lines

// This returns individual commits (rewind points), not just branch info.
// The sessionID filter, if provided, limits results to commits from that session.
// worktreeID should be empty for main worktree or the internal git worktree name for linked worktrees.
func (s *ephemeralStore) ListTemporaryCheckpoints(ctx context.Context, baseCommit, worktreeID, sessionID string, limit int) ([]TemporaryCheckpointInfo, error) {
func (s *ephemeralStore) ListTemporaryCheckpoints(ctx context.Context, baseCommit, worktreeID, sessionID string, limit int) ([]EphemeralCheckpointInfo, error) {
    shadowBranchName := ShadowBranchNameForCommit(baseCommit, worktreeID)
    return s.listCheckpointsForBranch(ctx, shadowBranchName, sessionID, limit)
}

// ListCheckpointsForBranch lists checkpoint commits for a shadow branch by name.
// Use this when you already have the full branch name (e.g., from ListTemporary).
// The sessionID filter, if provided, limits results to commits from that session.
func (s *ephemeralStore) ListCheckpointsForBranch(ctx context.Context, branchName, sessionID string, limit int) ([]TemporaryCheckpointInfo, error) {
func (s *ephemeralStore) ListCheckpointsForBranch(ctx context.Context, branchName, sessionID string, limit int) ([]EphemeralCheckpointInfo, error) {
    return s.listCheckpointsForBranch(ctx, branchName, sessionID, limit)
}

// listCheckpointsForBranch lists checkpoint commits for a specific shadow branch name.
// This is an internal helper used by ListTemporaryCheckpoints, ListCheckpointsForBranch, and ListAllTemporaryCheckpoints.
func (s *ephemeralStore) listCheckpointsForBranch(ctx context.Context, shadowBranchName, sessionID string, limit int) ([]TemporaryCheckpointInfo, error) {
func (s *ephemeralStore) listCheckpointsForBranch(ctx context.Context, shadowBranchName, sessionID string, limit int) ([]EphemeralCheckpointInfo, error) {
    if err := ctx.Err(); err != nil {
        return nil, err //nolint:wrapcheck // Propagating context cancellation
    }
10 unmodified lines

return nil, fmt.Errorf("failed to get commit log: %w", err)
    
    var results []TemporaryCheckpointInfo
    var results []EphemeralCheckpointInfo
    count := 0

err = iter.ForEach(func(c *object.Commit) error {
20 unmodified lines

message = message[:idx]
        }

info := TemporaryCheckpointInfo{
        info := EphemeralCheckpointInfo{
            CommitHash: c.Hash,
            Message:    message,
            SessionID:  commitSessionID,
31 unmodified lines

// ListAllTemporaryCheckpoints lists checkpoint commits from ALL shadow branches.
// This is used for checkpoint lookup when the base commit is unknown (e.g., HEAD advanced since session start).
// The sessionID filter, if provided, limits results to commits from that session.
func (s *ephemeralStore) ListAllTemporaryCheckpoints(ctx context.Context, sessionID string, limit int) ([]TemporaryCheckpointInfo, error) {
func (s *ephemeralStore) ListAllTemporaryCheckpoints(ctx context.Context, sessionID string, limit int) ([]EphemeralCheckpointInfo, error) {
    if err := ctx.Err(); err != nil {
        return nil, err //nolint:wrapcheck // Propagating context cancellation
    }
4 unmodified lines

return nil, fmt.Errorf("failed to list shadow branches: %w", err)

var results []TemporaryCheckpointInfo
    var results []EphemeralCheckpointInfo

// Iterate through each shadow branch and collect checkpoints
    for _, branch := range branches {

Mcmd/entire/cli/checkpoint/temporary.go+23/-23

90 unmodified lines

91 92 93 94 94 95 96 97 33 unmodified lines

131 132 133 134 134 135 136 137 148 unmodified lines

286 287 288 289 289 290 291 292

90 unmodified lines

// Test without transcripts — the tree structure equivalence is what matters. // Transcript processing (chunking, redaction) is covered by integration tests. opts := WriteTemporaryTaskOptions{ opts := WriteEphemeralTaskOptions{ SessionID: "sess-001", ToolUseID: "tool-001", AgentID: "agent-001", 33 unmodified lines

t.Fatalf("commit: %v", err) }

opts := WriteTemporaryTaskOptions{ opts := WriteEphemeralTaskOptions{ SessionID: "sess-002", ToolUseID: "tool-002", IsIncremental: true, 148 unmodified lines

func flattenRebuildTaskMetadata( t *testing.T, repo *gogit.Repository, baseTreeHash plumbing.Hash, opts WriteTemporaryTaskOptions, opts WriteEphemeralTaskOptions, ) plumbing.Hash {

t.Helper()


Mcmd/entire/cli/checkpoint/tree_surgery_equiv_test.go+3/-3

1199 unmodified lines

1200 1201 1202 1203 1203 1204 1205 1206 2 unmodified lines

1209 1210 1211 1212 1212 1213 1214 1215 963 unmodified lines

2179 2180 2181 2182 2182 2183 2184 2185 49 unmodified lines

2235 2236 2237 2238 2238 2239 2240 2241 2242 2243 2244 2245 2245 2246 2247 2248

1199 unmodified lines

// Searches ALL shadow branches, not just the one for current HEAD, to find checkpoints // created from different base commits (e.g., if HEAD advanced since the session start). // The writer w is used for raw transcript output to bypass the pager. func explainTemporaryCheckpoint(ctx context.Context, w, errW io.Writer, repo *git.Repository, store checkpoint.TemporaryStore, shaPrefix string, verbose, full, rawTranscript bool) (string, bool, error) { func explainTemporaryCheckpoint(ctx context.Context, w, errW io.Writer, repo *git.Repository, store checkpoint.EphemeralStore, shaPrefix string, verbose, full, rawTranscript bool) (string, bool, error) { // List temporary checkpoints from ALL shadow branches // This ensures we find checkpoints even if HEAD has advanced since the session started tempCheckpoints, err := store.ListAllTemporaryCheckpoints(ctx, "", branchCheckpointsLimit) 2 unmodified lines

}

// Find checkpoints matching the SHA prefix - check for ambiguity var matches []checkpoint.TemporaryCheckpointInfo var matches []checkpoint.EphemeralCheckpointInfo for _, tc := range tempCheckpoints { if strings.HasPrefix(tc.CommitHash.String(), shaPrefix) { matches = append(matches, tc) 963 unmodified lines

// whose base commit is reachable from the given HEAD hash and that belong to this worktree. // For default branches, all shadow branches for this worktree are included. // For feature branches, only shadow branches whose base commit is in HEAD's history are included. func getReachableTemporaryCheckpoints(ctx context.Context, repo *git.Repository, store checkpoint.TemporaryStore, headHash plumbing.Hash, isOnDefault bool, limit int) []strategy.RewindPoint { func getReachableTemporaryCheckpoints(ctx context.Context, repo *git.Repository, store checkpoint.EphemeralStore, headHash plumbing.Hash, isOnDefault bool, limit int) []strategy.RewindPoint { var points []strategy.RewindPoint

// Compute current worktree's hash for filtering shadow branches 49 unmodified lines

return found }

// convertTemporaryCheckpoint converts a TemporaryCheckpointInfo to a RewindPoint. // convertTemporaryCheckpoint converts a EphemeralCheckpointInfo to a RewindPoint. // Returns nil if the checkpoint should be skipped (no tree changes or can't be read). // // Filtering uses hasAnyChanges (O(1) tree hash comparison) rather than hasCodeChanges // (O(files) full diff). This means metadata-only checkpoints (.entire/ changes without // code changes) are kept — only true no-ops (identical tree as parent) are dropped. // This trade-off is intentional for list-view performance. func convertTemporaryCheckpoint(repo *git.Repository, tc checkpoint.TemporaryCheckpointInfo) *strategy.RewindPoint { func convertTemporaryCheckpoint(repo *git.Repository, tc checkpoint.EphemeralCheckpointInfo) *strategy.RewindPoint { shadowCommit, commitErr := repo.CommitObject(tc.CommitHash) if commitErr != nil { return nil `