refactor(import): store imported checkpoints on v1, not a separate ref · Entire

refactor(import): store imported checkpoints on v1, not a separate ref

241ac12→main ·

computermode·3w ago·15 files·+65 added/-227 removed

Imported checkpoints now live on entire/checkpoints/v1 (tagged Kind:imported) instead of a local-only entire/imports/v1 ref. They are redacted and pushed like normal checkpoints, which is what lights up server-side search/recap/UI.

This collapses the read paths back to a single store and fixes two bugs that came from the two-ref design:

Removes ImportsRefs/OpenImports/ImportsBranchName and the read-path unions; keeps Kind:imported, provenance, the [imported] label, rewind refusal, and the deterministic-ID idempotency.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com

Sessions

fb2f7d9f9b04 View transcript

?\ Generalize Agent Import Across Platforms Claude Code·Opus 4.8·4 steps

Changes

15

31 unmodified lines

32
33
34
35
36
37
38
39
7 unmodified lines

47
48
49
48
49
50
51
52
53
54
55
50
51
52

31 unmodified lines

// Open resolves the checkpoint storage topology and constructs the backing
// store. It keeps ref resolution and blob-fetcher wiring in one place.
//
//nolint:unparam // Callers treat store construction as fallible at this boundary; the git backend has no fallible setup today.
func Open(ctx context.Context, repo *git.Repository, opts OpenOptions) (*Stores, error) {
    refs := resolveOpenRefs(ctx, opts)
    store := NewGitStore(repo, refs)
    7 unmodified lines

}, nil
}

// OpenImports opens the local-only imports store (entire/imports/v1) used for
// read-only imported checkpoints. It is a thin convenience over Open with the
// imports ref topology, shared by every reader/writer of imported history.
func OpenImports(ctx context.Context, repo *git.Repository) (*Stores, error) {
    refs := ImportsRefs()
    return Open(ctx, repo, OpenOptions{Refs: &refs})
}

func resolveOpenRefs(ctx context.Context, opts OpenOptions) PersistentRefs {
    if opts.Refs != nil {
        return *opts.Refs
}

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

35 unmodified lines

36
37
38
39
39
40
41
42

35 unmodified lines

t.Fatal(err)
}

store := NewGitStore(repo, ImportsRefs())
store := NewGitStore(repo, DefaultV1Refs())
red, err := redact.JSONLBytes([]byte(`{"type":"user","uuid":"u1","message":{"role":"user","content":"hi"}}` + "\n"))
if err != nil {
    t.Fatal(err)
}

Mcmd/entire/cli/checkpoint/persistent_imported_test.go +1/-1

25 unmodified lines

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
29
30
31

25 unmodified lines

}
}

// ImportsRefs returns the local-only topology for imported checkpoints.
// Primary and Read target entire/imports/v1; Push is empty so imports are
// never sent to a remote in this pass. A future opt-in adds the ref to Push.
func ImportsRefs() PersistentRefs {
    importsBranch := plumbing.NewBranchReferenceName(paths.ImportsBranchName)
    return PersistentRefs{
        Primary: importsBranch,
        Read:    importsBranch,
        Push:    nil,
    }
}

// PrimaryFetchableFromOrigin reports whether Primary has an origin-tracking shadow.
func (r PersistentRefs) PrimaryFetchableFromOrigin() bool {
    return r.Primary.IsBranch() && slices.Contains(r.Push, r.Primary)
}

Mcmd/entire/cli/checkpoint/persistent_refs.go -12

5 unmodified lines

6
7
8
9
10
9
10
13
14
15
16
17
18
19
20
21
11
12
13

5 unmodified lines

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

"github.com/entireio/cli/cmd/entire/cli/paths"
)

func TestImportsRefs_NeverPushed(t *testing.T) {
    t.Parallel()
    refs := ImportsRefs()
    want := plumbing.NewBranchReferenceName(paths.ImportsBranchName)
    assert.Equal(t, want, refs.Primary, "Primary should target the imports branch")
    assert.Equal(t, want, refs.Read, "Read should target the imports branch")
    assert.Empty(t, refs.Push, "imports are local-only and must never be pushed")
}

// Not parallel: uses t.Chdir() to exercise on-disk settings being ignored.
func TestResolveCommittedRefs(t *testing.T) {
    v1 := v1BranchRef()

Mcmd/entire/cli/checkpoint/persistent_refs_test.go -11

681 unmodified lines

682
683
684
685
686
687
688
689
690
691
1478 unmodified lines

2170
2171
2172
2169
2170
2171
2173
2174
2175
2176
2177
2181
2178
2179
2180
2181
1 unmodified line

2183
2184
2185
2181
2186
2187
2188
2190
2191
2192
2193
2194
2195
2196

681 unmodified lines

}
// Handle summary generation — uses raw transcript.
if generate {
    if summary != nil && summary.Imported {
        stopLoad(false)
        return fmt.Errorf("cannot generate a summary for imported checkpoint %s: imported history is read-only", fullCheckpointID)
    }
    stopLoad(false) // generation prints its own progress to w/errW
    writeStores, openErr := checkpoint.Open(ctx, lookup.repo, checkpoint.OpenOptions{})
    if openErr != nil {
        return nil
}
}

// getImportedRewindPoints returns read-only imported checkpoints from
// entire/imports/v1 as RewindPoint entries (flagged Imported, not rewindable).
// Best-effort: returns nil when the imports ref is absent or unreadable.
getImportedRewindPoints(ctx context.Context, repo *git.Repository) []strategy.RewindPoint {
    stores, err := checkpoint.OpenImports(ctx, repo)
    stores, err := checkpoint.Open(ctx, repo, checkpoint.OpenOptions{})
    if err != nil {
        return nil
    }
1 unmodified line

if err != nil {
    return nil
}
points := make([]strategy.RewindPoint, 0, len(infos))
points := make([]strategy.RewindPoint, 0)
for _, info := range infos {
    // Imported checkpoints live on v1 alongside normal ones but have no
    // commit trailer, so the commit-driven walk above never surfaces them.
    // Add only the imported ones here.
    if !info.Imported {
        continue
    }
    point := strategy.RewindPoint{
        ID:           info.CheckpointID.String(),
        Message:      readLatestCommittedSessionPrompt(ctx, stores.Persistent, info.CheckpointID, info.SessionCount),

Mcmd/entire/cli/explain.go +16/-5

44 unmodified lines

45
46
47
48
48
49
50
50
51
52
53

44 unmodified lines

return res, err
}

stores, err := cp.OpenImports(ctx, repo)
stores, err := cp.Open(ctx, repo, cp.OpenOptions{})
if err != nil {
    return res, fmt.Errorf("open imports store: %w", err)
    return res, fmt.Errorf("open checkpoint store: %w", err)
}
existing := make(map[string]bool)
if infos, listErr := stores.Persistent.List(ctx); listErr == nil {

Mcmd/entire/cli/importclaude/importer.go +2/-2

74 unmodified lines

75
76
77
78
78
79
80
81
1 unmodified line

83
84
85
86
87
86
87
88
89
90
91
92
93
94
95
96
97
98
99
89
90
91
92
93
94
95
14 unmodified lines

110
111
112
120
113
114
115
116

74 unmodified lines

t.Fatalf("re-run not idempotent: %+v", res2)
}

stores, err := cp.OpenImports(context.Background(), repo)
stores, err := cp.Open(context.Background(), repo, cp.OpenOptions{})
if err != nil {
    t.Fatal(err)
}
1 unmodified line

if err != nil {
    t.Fatal(err)
}
if len(infos) != 2 || !infos[0].Imported {
    t.Fatalf("expected 2 imported on imports ref, got %+v", infos)
if len(infos) != 2 {
    t.Fatalf("expected 2 imported checkpoints on v1, got %+v", infos)
}

v1, err := cp.Open(context.Background(), repo, cp.OpenOptions{})
if err != nil {
    t.Fatal(err)
}
v1infos, err := v1.Persistent.List(context.Background())
if err != nil {
    t.Fatal(err)
}
if len(v1infos) != 0 {
    t.Fatalf("imports leaked onto v1: %+v", v1infos)
for _, in := range infos {
    if !in.Imported {
        t.Fatalf("checkpoint %s missing Imported flag: %+v", in.CheckpointID, in)
    }
}
}

14 unmodified lines

t.Fatalf("dry-run should count 2 turns, got %+v", res)
}

stores, err := cp.OpenImports(context.Background(), repo)
stores, err := cp.Open(context.Background(), repo, cp.OpenOptions{})
if err != nil {
    t.Fatal(err)
}

Mcmd/entire/cli/importclaude/importer_test.go +8/-15

7 unmodified lines

8
9
10
11
12
11
12
13
16
14
15
16
9 unmodified lines

26
27
28
32
33
29
30
31
37
38
39
40
41
32
33
43
34
35
36
46
47
48
37
50
38
39
40
41
42
43
44
54
55
56
57
58
45
46
47
48
49
50
51
52
53
54
55
56
57
58

7 unmodified lines

"strings"
    "testing"

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

"github.com/entireio/cli/cmd/entire/cli/importclaude"
    "github.com/entireio/cli/cmd/entire/cli/paths"
)

func TestImportClaudeCode_EndToEnd(t *testing.T) {
9 unmodified lines

}, "\n") + "\n"
    require.NoError(t, os.WriteFile(filepath.Join(env.ClaudeProjectDir, sessionID+".jsonl"), []byte(content), 0o644))

importsRef := plumbing.NewBranchReferenceName(paths.ImportsBranchName);

// 1. Dry-run reports counts and writes nothing.
    out := env.RunCLI("import", "claude-code", "--dry-run")
    require.Contains(t, out, "Would import 2", "dry-run should count 2 turns; got: %s", out)
    repo, err := git.PlainOpen(env.RepoDir)
    require.NoError(t, err)
    if _, err := repo.Reference(importsRef, false); err == nil {
        t.Fatal("dry-run must not create the imports ref")
    }
    require.NotContains(t, env.RunCLI("checkpoint", "list"), "[imported]", "dry-run must not write checkpoints")

// 2. Real import creates the imports ref.
    // 2. Real import writes the imported checkpoints (onto the v1 metadata branch).
    out = env.RunCLI("import", "claude-code")
    require.Contains(t, out, "Imported 2", "got: %s", out)
    ref, err := repo.Reference(importsRef, false)
    require.NoError(t, err, "imports ref should exist after import")
    require.False(t, ref.Hash().IsZero())

// 3. checkpoint list surfaces imported entries labeled [imported].
    // 3. checkpoint list surfaces imported entries labeled [imported], and does
    //    NOT duplicate them as [temporary] (regression: the imports were once
    //    mis-read by the shadow-branch scanner).
    listOut := env.RunCLI("checkpoint", "list")
    require.Contains(t, listOut, "[imported]", "checkpoint list should label imported checkpoints; got: %s", listOut)
    require.NotContains(t, listOut, "[temporary]", "imported checkpoints must not appear as temporary; got: %s", listOut)

// 4. Re-running import is idempotent.
    out = env.RunCLI("import", "claude-code")
    require.Contains(t, out, "(2 already imported)", "re-run should skip already-imported turns; got: %s", out)

// 5. Rewinding to an imported checkpoint is refused with a clear message.
    // 4. explain resolves an imported checkpoint by ID (regression: explain once
    //    only consulted the committed/shadow paths and missed imports).
    importedID := importclaude.DeriveCheckpointID(sessionID, "u1").String()
    explainOut := env.RunCLI("checkpoint", "explain", importedID)
    require.Contains(t, explainOut, "first", "explain should show the imported turn's prompt; got: %s", explainOut)

// 5. Re-running import is idempotent.
    out = env.RunCLI("import", "claude-code")
    require.Contains(t, out, "(2 already imported)", "re-run should skip already-imported turns; got: %s", out)

// 6. Rewinding to an imported checkpoint is refused with a clear message.
    rewindOut, rewindErr := env.RunCLIWithError("checkpoint", "rewind", "--to", importedID)
    require.Error(t, rewindErr, "rewind to imported checkpoint should fail")
    require.Contains(t, rewindOut, "read-only and not rewindable", "got: %s", rewindOut);

Mcmd/entire/cli/integration_test/import_claude_test.go +16/-20

40 unmodified lines

41
42
43
44
45
46
47
44
45
46

40 unmodified lines

// MetadataBranchName is the orphan branch used by manual-commit strategy to store metadata
const MetadataBranchName = "entire/checkpoints/v1";

// ImportsBranchName is the local-only orphan branch holding imported
// (commit-less, read-only) checkpoints. It is intentionally never pushed.
const ImportsBranchName = "entire/imports/v1";

// TrailsBranchName is the orphan branch used to store trail metadata.
// Trails are branch-centric work tracking abstractions that link to checkpoints by branch name.
const TrailsBranchName = "entire/trails/v1";

Mcmd/entire/cli/paths/paths.go -4

412 unmodified lines

413
414
415
416
417
418
416
417
418
419
420
421
422
1 unmodified line

424
425
426
426
427
428
429
430
2 unmodified lines

433
434
435
436
437
438
439
440
441

412 unmodified lines

}

// refuseIfImportedCheckpoint blocks rewinding to imported (read-only,
// commit-less) checkpoints on entire/imports/v1. It matches commitID against
// imported checkpoint IDs (full or >=7-char prefix). Best-effort: when the
// imports ref is absent or unreadable it returns nil so normal rewind proceeds.
func refuseIfImportedCheckpoint(ctx context.Context, errW io.Writer, commitID string) error {
    repo, err := strategy.OpenRepository(ctx)
    if err != nil {
1 unmodified line
    }
    defer repo.Close();

stores, err := checkpoint.OpenImports(ctx, repo);
    stores, err := checkpoint.Open(ctx, repo, checkpoint.OpenOptions{});
    if err != nil {
        return nil;
    }
2 unmodified lines

return nil;
    }
    for _, in := range infos {
        if !in.Imported {
            continue;
        }
        idStr := in.CheckpointID.String();
        if idStr == commitID || (len(commitID) >= 7 && strings.HasPrefix(idStr, commitID)) {
            fmt.Fprintln(errW, "This checkpoint was imported from existing agent history. Imported history is read-only and not rewindable.");

Mcmd/entire/cli/rewind.go +8/-4

38 unmodified lines

39
40
41
42
42
43
44
45

38 unmodified lines

t.Chdir(repoDir);

cid := id.MustCheckpointID("aabbccddeeff");
    store := checkpoint.NewGitStore(repo, checkpoint.ImportsRefs());
    store := checkpoint.NewGitStore(repo, checkpoint.DefaultV1Refs());
    red, err := redact.JSONLBytes([]byte(`{"type":"user","uuid":"u1","message":{"role":"user","content":"hi"}}` + "\n"));
    if err != nil {
        t.Fatal(err);
    }

Mcmd/entire/cli/rewind_imports_test.go +1/-1

55 unmodified lines

56
57
58
59
59
60
61
62

55 unmodified lines

t.Fatal(err);
    }
    defer repo.Close();
    stores, err := checkpoint.OpenImports(context.Background(), repo);
    stores, err := checkpoint.Open(context.Background(), repo, checkpoint.OpenOptions{});
    if err != nil {
        t.Fatal(err);
    }

Mcmd/entire/cli/setup_import_prompt_test.go +1/-1

338 unmodified lines

339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
342
343
344

338 unmodified lines

return result;
}

// ListCheckpointsWithImports returns committed v1 checkpoints plus imported
// (local-only) checkpoints from entire/imports/v1, the latter flagged
// Imported=true. Use this in read/inspect commands that should surface imported
// history; lifecycle/cleanup paths keep using ListCheckpoints (v1 only).
func ListCheckpointsWithImports(ctx context.Context) ([]CheckpointInfo, error) {
    base, err := ListCheckpoints(ctx);
    if err != nil {
        return nil, err;
    }
    repo, err := OpenRepository(ctx);
    if err != nil {
        return nil, fmt.Errorf("failed to open git repository: %w", err);
    }
    defer repo.Close();

imports, err := checkpoint.OpenImports(ctx, repo);
    if err != nil {
        return base, nil;
    }
    committed, err := imports.Persistent.List(ctx);
    if err != nil {
        return base, nil;
    }
    imp := checkpointInfosFromCommitted(committed);
    for i := range imp {
        imp[i].Imported = true;
    }
    all := make([]CheckpointInfo, 0, len(base)+len(imp));
    all = append(all, base...);
    all = append(all, imp...);
    sort.Slice(all, func(i, j int) bool { return all[i].CreatedAt.After(all[j].CreatedAt) });
    return all, nil;
}

const (
    entireGitignore = ".entire/.gitignore";
    entireDir = ".entire";

Mcmd/entire/cli/strategy/common.go -34

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

package strategy

import (
    "context";
    "testing";

"github.com/go-git/go-git/v6";
    "github.com/go-git/go-git/v6/plumbing/object";

"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";
)

func TestListCheckpointsWithImports_UnionsAndFlags(t *testing.T) {
    // Not parallel: uses t.Chdir for CWD-based repo resolution.
    repoDir := t.TempDir();
    testutil.InitRepo(t, repoDir);
    repo, err := git.PlainOpen(repoDir);
    if err != nil {
        t.Fatal(err);
    }
    wt, err := repo.Worktree();
    if err != nil {
        t.Fatal(err);
    }
    testutil.WriteFile(t, repoDir, "f.txt", "x");
    if _, err := wt.Add("f.txt"); err != nil {
        t.Fatal(err);
    }
    if _, err := wt.Commit("init", &git.CommitOptions{
        Author: &object.Signature{Name: "Test", Email: "test@test.com"},
    }); err != nil {
        t.Fatal(err);
    }
    t.Chdir(repoDir);

// Write one imported checkpoint directly to the imports ref.
    store := checkpoint.NewGitStore(repo, checkpoint.ImportsRefs());
    red, err := redact.JSONLBytes([]byte(`{"type":"user","uuid":"u1","message":{"role":"user","content":"hi"}}` + "\n"));
    if err != nil {
        t.Fatal(err);
    }
    if err := store.Write(context.Background(), checkpoint.Session(checkpoint.WriteOptions{
        CheckpointID: id.MustCheckpointID("aabbccddeeff"), SessionID: "s",
        Strategy: "import", Kind: "imported", Transcript: red, Prompts: []string{"hi"}, CheckpointsCount: 1,
    })); err != nil {
        t.Fatal(err);
    }

infos, err := ListCheckpointsWithImports(context.Background());
    if err != nil {
        t.Fatal(err);
    }
    var importedCount int;
    for _, in := range infos {
        if in.Imported {
            importedCount++;
        }
    }
    if importedCount != 1 {
        t.Fatalf("want 1 imported in union, got %d (total %d)", importedCount, len(infos));
    }

// Default ListCheckpoints (v1 only) must NOT include imports.
    v1, err := ListCheckpoints(context.Background());
    if err != nil {
        t.Fatal(err);
    }
    for _, in := range v1 {
        if in.Imported {
            t.Fatal("imports leaked into v1-only ListCheckpoints");
        }
    }
}

Dcmd/entire/cli/strategy/common_imports_test.go -76

119 unmodified lines

120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
123
124
125
126
5 unmodified lines

132
133
134
149
150
151
152
153
154
155
156
157
158
135
136
137
138
139
140
141
163
142
143
144
145
146
168
169
170
147
148
149
150
34 unmodified lines

185
186
187
211
212
213
188
189
190
191
215
192
193
194
195

119 unmodified lines

return fmt.Errorf("failed to list checkpoints: %w", err);
}

refs := make([]tokensCheckpointRef, 0, len(infos));
for _, info := range infos {
    refs = append(refs, tokensCheckpointRef{store: store, info: info});
}
// Include read-only imported checkpoints from entire/imports/v1, read from
// their own store. Best-effort: skipped when the ref is absent.
importsStore := checkpoint.NewGitStore(repo, checkpoint.ImportsRefs());
importsStore.SetBlobFetcher(FetchBlobsByHash);
if importInfos, listErr := importsStore.List(ctx); listErr == nil {
    for _, info := range importInfos {
        refs = append(refs, tokensCheckpointRef{store: importsStore, info: info});
    }
}

report, err := buildTokensProfileReport(ctx, refs, limit);
report, err := buildTokensProfileReport(ctx, store, infos, limit);
if err != nil {
    return err;
}
5 unmodified lines

return nil;
}

// tokensCheckpointRef binds a checkpoint info to the store that owns it, so the
// profile can read both committed (v1) and imported (entire/imports/v1) sources.
type tokensCheckpointRef struct {
    store *checkpoint.GitStore;
    info  checkpoint.CheckpointInfo;
}

func buildTokensProfileReport(ctx context.Context, refs []tokensCheckpointRef, limit int) (tokensProfileReport, error) {
    checkpointsAvailable := len(refs);
    refs = limitTokensProfileCheckpoints(refs, limit);
func buildTokensProfileReport(ctx context.Context, store *checkpoint.GitStore, infos []checkpoint.CheckpointInfo, limit int) (tokensProfileReport, error) {
    checkpointsAvailable := len(infos);
    infos = limitTokensProfileCheckpoints(infos, limit);
    report := tokensProfileReport{
        Source:               "committed_checkpoints",
        UsageScope:           tokensProfileUsageScopeCheckpointObserved,
        CheckpointsAvailable: checkpointsAvailable,
        CheckpointsAnalyzed:  len(refs),
        CheckpointsAnalyzed:  len(infos),
    };
    signals := make(map[string]*tokensProfileSignal, len(tokensProfileSignalDefinitions));
    var aggregate *agent.TokenUsage;

for _, ref := range refs {
        store := ref.store;
        info := ref.info;
for _, info := range infos {
    if err := ctx.Err(); err != nil {
        return tokensProfileReport{}, err //nolint:wrapcheck // Propagating context cancellation.
    }
34 unmodified lines

return report, nil;
}

func limitTokensProfileCheckpoints(refs []tokensCheckpointRef, limit int) []tokensCheckpointRef {
if limit <= 0 || len(refs) <= limit {
    return refs;
}
func limitTokensProfileCheckpoints(infos []checkpoint.CheckpointInfo, limit int) []checkpoint.CheckpointInfo {
if limit <= 0 || len(infos) <= limit {
    return infos;
}
return refs[:limit];
return infos[:limit];
}

func tokensProfileCheckpointUsage(ctx context.Context, store *checkpoint.GitStore, checkpointID id.CheckpointID, summary *checkpoint.CheckpointSummary) (*agent.TokenUsage, bool, error) {