# Add checkpoint backend registry and factory

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

Introduce a backend registry in the checkpoint package: a Factory type, an OpenEnv construction context, Register/build, and a built-in "git" backend. Open now builds its primary store through the registry (defaulting to the git backend) rather than calling NewGitStore directly.

This is the seam Phase 2 needs: a new persistent backend becomes a factory registered under a type name. Production registers only the git backend; test-only backends will register through their own RegisterForTesting helpers so a production binary can never select them. The registry default of git matches today's Open exactly, so there is no behavior change at default config.

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

## Sessions

56c7fecd2a55View transcript

## Changes

3

- cmd/entire/cli/checkpoint

- Mopen.go+8/-7

- Aregistry.go+91

- Aregistry_test.go+40

```
30 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.
// store(s). It keeps ref resolution, backend selection, and blob-fetcher wiring
// in one place. The primary is built through the backend registry; today it
// always resolves to the git backend, so default behavior is unchanged.
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)
	}
	env := OpenEnv{Repo: repo, BlobFetcher: opts.BlobFetcher, Refs: refs}
	primary, err := build(ctx, env, BackendTypeGit, nil)
	if err != nil {
		return nil, err
	}
	return &Stores{
		Persistent: store,
		Secondary:  primary,
		ephemeral:  newEphemeralStore(repo, refs),
		refs:       refs,
	}, nil
}
```

```go
package checkpoint

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

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

const BackendTypeGit = "git"

type OpenEnv struct {
	Repo        *git.Repository
	BlobFetcher BlobFetchFunc
	Refs        PersistentRefs
}

type Factory func(ctx context.Context, env OpenEnv, cfg json.RawMessage) (PersistentStore, error)

var (
	registryMu sync.RWMutex
	registry = map[string]Factory{BackendTypeGit: gitBackendFactory}
)

func Register(typ string, f Factory) {
	registryMu.Lock()
	defer registryMu.Unlock()
	if _, exists := registry[typ]; exists {
		panic(fmt.Sprintf("checkpoint: backend type %q already registered", typ))
	}
	registry[typ] = f
}

func build(ctx context.Context, env OpenEnv, typ string, cfg json.RawMessage) (PersistentStore, error) {
	registryMu.RLock()
	f, ok := registry[typ]
	registryMu.RUnlock()
	if !ok {
		return nil, fmt.Errorf("unknown checkpoint backend type %q (registered: %s)", typ, registeredTypes())
	}
	store, err := f(ctx, env, cfg)
	if err != nil {
		return nil, fmt.Errorf("construct %q checkpoint backend: %w", typ, err)
	}
	return store, nil
}

func registeredTypes() string {
	registryMu.RLock()
	defer registryMu.RUnlock()
	types := make([]string, 0, len(registry))
	for t := range registry {
		types = append(types, t)
	}
	sort.Strings(types)
	return strings.Join(types, ", ")
}

func gitBackendFactory(_ context.Context, env OpenEnv, _ json.RawMessage) (PersistentStore, error) {
	if env.Repo == nil {
		return nil, errors.New("git checkpoint backend requires a repository")
	}
	store := NewGitStore(env.Repo, env.Refs)
	if env.BlobFetcher != nil {
		store.SetBlobFetcher(env.BlobFetcher)
	}
	return store, nil
}
```

```go
package checkpoint

import (
	"context"
	"encoding/json"
	"testing"

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

func TestRegistry_GitBackendRegistered(t *testing.T) {
	t.Parallel()

_, err := build(context.Background(), OpenEnv{}, BackendTypeGit, nil)
	require.Error(t, err)
	assert.Contains(t, err.Error(), "git checkpoint backend requires a repository")
}

func TestRegistry_UnknownType(t *testing.T) {
	t.Parallel()

_, err := build(context.Background(), OpenEnv{}, "definitely-not-a-backend", nil)
	require.Error(t, err)
	assert.Contains(t, err.Error(), `unknown checkpoint backend type "definitely-not-a-backend"`)
	assert.Contains(t, err.Error(), BackendTypeGit)
}

func TestRegistry_GitFactoryIgnoresConfig(t *testing.T) {
	t.Parallel()

_, err := build(context.Background(), OpenEnv{}, BackendTypeGit, json.RawMessage(`{"anything":true}`))
	require.Error(t, err)
	assert.Contains(t, err.Error(), "git checkpoint backend requires a repository")
}
```
