# checkpoint: centralize store construction behind Open (Phase 0)

`0747ca9`→[main](/content/gh/entireio/cli/commits/main/index.html)·

Soph·1mo ago·19 files·+277 added/-73 removed

Replace the scattered NewGitStore(repo, ResolveCommittedRefs(ctx)) construction across cli, strategy, dispatch, and the in-package LookupSessionLog with a single seam: checkpoint.Open(ctx, repo, OpenOptions) (*Stores, error).

This lands the final facade signature now (issue #1433 Phase 0) so call sites migrate only once: Stores.Primary holds the concrete *GitStore today and the same instance backs Temporary(); later phases narrow Primary to a pluggable committed-store interface and add independent-backend mirrors without further call-site churn. The facade exposes Temporary()/Refs()/Repository() so callers no longer reach for the concrete type, and OpenOptions carries the CLI-level BlobFetcher plus explicit Settings/Refs overrides (attach keeps its injected-settings / PrimaryAsRead topology).

Pure mechanical, no behavior change.

Notes:
- getCheckpointStore now returns (*GitStore, error) (propagated through its callers); the old withBlobFetcher folds into OpenOptions.BlobFetcher.
- generateCheckpointSummary takes the facade since it needs both the committed writer and Repository(); its mirror still resolves refs from settings (ResolveCommittedRefs) to preserve exact behavior.
- Type is checkpoint.Stores (not CheckpointStores) to avoid the revive stutter; Open's always-nil error is the forward-looking facade contract.
- benchutil and test files keep using NewGitStore, which Open wraps.

Refs #1433

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

## Sessions

c11527631d6bView transcript

## Changes

19

- cmd/entire/cli
  
  - Mattach.go+24/-3
  
  - Mattribution.go+5/-3
  
  - checkpoint
  
    - Mcommitted.go+5/-2
  
    - Aopen.go+90

- dispatch
  
    - Mmode_local.go+5/-1

- Mexplain.go+30/-12
  
  - Mexplain_test.go+5/-2
  
  - Mhead_checkpoint_flags.go+6/-2
  
  - Mresume.go+18/-11
  
  - Mreview_context.go+6/-1
  
  - Mrewind.go+10/-4
  
  - strategy
  
    - Mcleanup.go+5/-2
  
    - Mcommon.go+5/-2
  
    - Mmanual_commit.go+9/-12
  
    - Mmanual_commit_condensation.go+12/-3
  
    - Mmanual_commit_git.go+8/-2
  
    - Mmanual_commit_hooks.go+24/-6
  
    - Mmanual_commit_rewind.go+8/-4
  
    - Mmanual_commit_test.go+2/-1

```  
65 unmodified lines

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
200 unmodified lines

283
284
285
275
286
287
288
289
290
291
292
84 unmodified lines

377
378
379
366
380
381
382
383
384
385
386
93 unmodified lines

480
481
482
466
483
484
485
486
487
488
489
490

65 unmodified lines

return cpkg.ResolveCommittedRefs(ctx)
}

// openAttachStore opens the committed store for the resolved topology. refs is
// passed explicitly (not re-resolved from live settings) so attach preserves
// any injected EntireSettings / PrimaryAsRead() pinning.
func openAttachStore(ctx context.Context, repo *git.Repository, refs cpkg.CommittedRefs) (*cpkg.GitStore, error) {

stores, err := cpkg.Open(ctx, repo, cpkg.OpenOptions{Refs: &refs})
 if err != nil {
 return nil, fmt.Errorf("open checkpoint store: %w", err)
 }
 return stores.Primary, nil
}

func newAttachCmd() *cobra.Command {
 var (
 force      bool
 200 unmodified lines

return err
 }

store := cpkg.NewGitStore(repo, refs)
 store, err := openAttachStore(ctx, repo, refs)
 if err != nil {
 return err
 }

// Defense-in-depth guard: the earlier existingState.LastCheckpointID
 // check only fires when the session's state file records its
 84 unmodified lines

// at Primary. Reads target Primary directly, not refs.Read, because this guard
// must reflect what the next write would target.
func checkpointHasSessionMetadata(ctx context.Context, repo *git.Repository, refs cpkg.CommittedRefs, checkpointID id.CheckpointID, sessionID string) (bool, error) {
 store := cpkg.NewGitStore(repo, refs.PrimaryAsRead())
 store, err := openAttachStore(ctx, repo, refs.PrimaryAsRead())
 if err != nil {
 return false, err
 }
 summary, err := store.ReadCommitted(ctx, checkpointID)
 if err != nil {
 return false, fmt.Errorf("read checkpoint summary: %w", err)
 }

if _, err := repo.Reference(refs.Primary, true); err != nil {
 return false, nil //nolint:nilerr // Missing ref is the "absent" signal, not an error.
 }
 summary, err := cpkg.NewGitStore(repo, refs.PrimaryAsRead()).ReadCommitted(ctx, checkpointID)
 store, err := openAttachStore(ctx, repo, refs.PrimaryAsRead())
 if err != nil {
 return false, err
 }
 summary, err := store.ReadCommitted(ctx, checkpointID)
 if err != nil {
 return false, err //nolint:wrapcheck // Caller wraps with checkpoint ID context
 }
}

Mcmd/entire/cli/attach.go+24/-3

``
311 unmodified lines

312
313
314
315
316
315
316
317
318
319
320
321
322
321
323
324
325
326

311 unmodified lines

return nil, fmt.Errorf("not a git repository: %w", err)
 }

store := checkpoint.NewGitStore(repo, checkpoint.ResolveCommittedRefs(ctx))
 store.SetBlobFetcher(FetchBlobsByHash)
 stores, err := checkpoint.Open(ctx, repo, checkpoint.OpenOptions{BlobFetcher: FetchBlobsByHash})
 if err != nil {
 return nil, fmt.Errorf("open checkpoint store: %w", err)
 }

return &attributionResolver{
 ctx:             ctx,
 repo:            repo,
 store:           store,
 store:           stores.Primary,
 fetchOnMiss:     fetchOnMiss,
 commitCache:     make(map[string]*object.Commit),
 checkpointCache: make(map[string]attributionCheckpointContext),
}
}

Mcmd/entire/cli/checkpoint/committed.go+5/-2

``
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90

package checkpoint

import (
	"context"

"github.com/go-git/go-git/v6"

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

// OpenOptions configures Open. The zero value resolves the committed-ref
// topology from on-disk settings and attaches no blob fetcher.
type OpenOptions struct {

// BlobFetcher is the CLI-level on-demand blob fetcher. The checkpoint
	// package cannot resolve it itself, so the CLI layer injects it here and
	// Open attaches it to the constructed store(s). nil leaves on-demand
	// fetching off.
	BlobFetcher BlobFetchFunc

// Settings overrides on-disk settings when resolving the committed-ref
	// topology. nil resolves from disk via ResolveCommittedRefs. Ignored when
	// Refs is non-nil.
	Settings *settings.EntireSettings

// Refs overrides the resolved committed-ref topology outright. nil resolves
	// from Settings (or disk). A non-nil value wins — e.g. attach pins reads to
	// Primary via PrimaryAsRead().
	Refs *CommittedRefs
}

// Stores is the facade returned by Open: the committed store plus the git-only
// temporary capability and the resolved topology accessors callers need during
// the transition to pluggable backends.
//
// Phase 0 (centralized construction) holds the concrete *GitStore in Primary,
// and the same instance backs Temporary(). Later phases narrow Primary to a
// pluggable committed-store interface and add independent-backend mirrors
// without changing this call-site-facing API.
type Stores struct {

// Primary is the committed store — the source of truth that serves all
	// committed reads and writes.
	Primary *GitStore

temporary *GitStore
	refs      CommittedRefs
}

// Open resolves the checkpoint storage topology and constructs the backing
// store(s). It is the single construction seam that replaces scattered
// NewGitStore(repo, ResolveCommittedRefs(ctx)) calls, so ref resolution and
// blob-fetcher wiring live in one place.
//
//nolint:unparam // The error result is part of the forward-looking facade contract: pluggable backends (Phase 2+) can fail to open. The git backend never returns one today.
func Open(ctx context.Context, repo *git.Repository, opts OpenOptions) (*Stores, error) {
	refs := resolveOpenRefs(ctx, opts)
	store := NewGitStore(repo, refs)
	if opts.BlobFetcher != nil {
		store.SetBlobFetcher(opts.BlobFetcher)
	}
	return &Stores{
		Primary:   store,
		temporary: store,
		refs:      refs,
	}, nil
}

func resolveOpenRefs(ctx context.Context, opts OpenOptions) CommittedRefs {
	switch {
	case opts.Refs != nil:
		return *opts.Refs
	case opts.Settings != nil:
		return ResolveCommittedRefsFromSettings(opts.Settings)
	default:
		return ResolveCommittedRefs(ctx)
	}
}

// Temporary returns the git-backed temporary (shadow-branch) store. Temporary
// capture is inherently git-only; a future non-git Primary would leave this
// pointing at a dedicated git store.
func (s *Stores) Temporary() *GitStore { return s.temporary }

// Refs returns the resolved committed-ref topology. Transition accessor: this
// ref logic moves behind sync/admin capabilities in a later phase.
func (s *Stores) Refs() CommittedRefs { return s.refs }

// Repository returns the underlying git repository. Transition accessor for
// git-topology operations (e.g. mirror repair) that have not yet moved behind a
// capability.
func (s *Stores) Repository() *git.Repository { return s.Primary.Repository() }
```
