enforce checkpoint policy compatibility · Entire
enforce checkpoint policy compatibility
a5fac5d·
pfleidi·3w ago·17 files·+365 added/-3 removed
Warn interactive users when the repo policy requires newer checkpoint support.
Reject user-driven checkpoint reads for unsupported formats, and skip checkpoint writes or pushes from hooks when the configured policy cannot be written by this CLI.
Sessions
b2c7f027b944View transcript
Changes
17
cmd/entire
cli
Acheckpoint_policy_read_test.go+40
Acheckpoint_policy_warning.go+42
Acheckpoint_policy_warning_test.go+46
checkpointpolicy
Mpolicy.go+16
Awarning_test.go+30
Mexplain.go+4
Mresume.go+13/-1
strategy
Acheckpoint_policy.go+80
Acheckpoint_policy_test.go+62
Mmanual_commit.go+2/-2
Mmanual_commit_condensation.go+3
Mmanual_commit_hooks.go+4
Mmanual_commit_push.go+3
Mmanual_commit_rewind.go+4
versioncheck
Mversioncheck.go+4
Mversioncheck_test.go+9
Mmain.go+3
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
package cli
import (
"context"
"testing"
"github.com/entireio/cli/cmd/entire/cli/checkpoint"
"github.com/entireio/cli/cmd/entire/cli/checkpoint/id"
"github.com/stretchr/testify/require"
)
func TestReadCheckpointInfoFromStoreRejectsUnsupportedCheckpointVersion(t *testing.T) {
t.Parallel()
cpID := id.MustCheckpointID("111111111111")
_, err := readCheckpointInfoFromStore(context.Background(), checkpointInfoPolicyStub{
summary: &checkpoint.CheckpointSummary{
CheckpointID: cpID,
CheckpointVersion: "refs-v1",
},
}, cpID)
require.ErrorContains(t, err, `checkpoint 111111111111 uses unsupported checkpoint_version "refs-v1"`)
}
type checkpointInfoPolicyStub struct {
summary *checkpoint.CheckpointSummary
}
func (s checkpointInfoPolicyStub) ReadCommitted(context.Context, id.CheckpointID) (*checkpoint.CheckpointSummary, error) {
return s.summary, nil
}
func (s checkpointInfoPolicyStub) ReadSessionContent(context.Context, id.CheckpointID, int) (*checkpoint.SessionContent, error) {
return nil, checkpoint.ErrCheckpointNotFound
}
func (s checkpointInfoPolicyStub) ReadSessionMetadata(context.Context, id.CheckpointID, int) (*checkpoint.CommittedMetadata, error) {
return nil, checkpoint.ErrCheckpointNotFound
}
Acmd/entire/cli/checkpoint_policy_read_test.go+40
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
package cli
import (
"context"
"fmt"
"io"
"github.com/entireio/cli/cmd/entire/cli/checkpointpolicy"
"github.com/entireio/cli/cmd/entire/cli/gitrepo"
"github.com/entireio/cli/cmd/entire/cli/versioncheck"
"github.com/spf13/cobra"
)
func ShouldCheckCheckpointPolicyWarning(cmd *cobra.Command) bool {
if cmd == nil {
return false
}
for c := cmd; c != nil; c = c.Parent() {
if c.Hidden || c.Name() == "hooks" {
return false
}
}
return true
}
func WarnCheckpointPolicyIfNeeded(ctx context.Context, w io.Writer, currentVersion string) {
repo, err := gitrepo.OpenCurrent(ctx)
if err != nil {
return
}
defer repo.Close()
state, err := checkpointpolicy.ReadLocal(ctx, repo)
if err != nil {
return
}
if !checkpointpolicy.RequiresUpgrade(state.Policy) && !checkpointpolicy.UnsupportedWrite(state.Policy) {
return
}
fmt.Fprint(w, checkpointpolicy.UpgradeWarning(versioncheck.UpdateCommandForCurrentBinary(currentVersion)))
}
Acmd/entire/cli/checkpoint_policy_warning.go+42
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
package cli
import (
"bytes"
"context"
"testing"
"github.com/entireio/cli/cmd/entire/cli/checkpointpolicy"
"github.com/go-git/go-git/v6"
"github.com/go-git/go-git/v6/plumbing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/require"
)
func TestWarnCheckpointPolicyIfNeeded(t *testing.T) {
_, _ = setupPolicyCheckpointRepo(t)
repo, err := git.PlainOpen(".")
require.NoError(t, err)
t.Cleanup(func() {
_ = repo.Close()
})
_, err = checkpointpolicy.WriteLocal(t.Context(), repo, plumbing.ZeroHash, checkpointpolicy.Policy{
CheckpointVersion: "refs-v1",
CheckpointMinVersion: "refs-v1",
})
require.NoError(t, err)
var buf bytes.Buffer
WarnCheckpointPolicyIfNeeded(context.Background(), &buf, "1.0.0")
require.Contains(t, buf.String(), "requires checkpoint support newer than this Entire CLI")
}
func TestShouldCheckCheckpointPolicyWarning(t *testing.T) {
root := &cobra.Command{Use: "entire"}
visible := &cobra.Command{Use: "status"}
root.AddCommand(visible)
hooks := &cobra.Command{Use: "hooks", Hidden: true}
gitHook := &cobra.Command{Use: "git"}
hooks.AddCommand(gitHook)
root.AddCommand(hooks)
require.True(t, ShouldCheckCheckpointPolicyWarning(visible))
require.False(t, ShouldCheckCheckpointPolicyWarning(gitHook))
}
Acmd/entire/cli/checkpoint_policy_warning_test.go+46
69 unmodified lines
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
69 unmodified lines
}
return !CanWrite(version)
}
func UpgradeWarning(updateCommand string) string {
return fmt.Sprintf("[entire] This repository requires checkpoint support newer than this Entire CLI.\n[entire] Upgrade Entire, then rerun the command:\n[entire] %s\n", updateCommand)
}
func EnsureCanReadVersion(checkpointID, version string) error {
policy := Normalize(Policy{CheckpointMinVersion: version})
format, err := ParseFormat(policy.CheckpointMinVersion)
if err != nil {
return fmt.Errorf("checkpoint %s uses unsupported checkpoint_version %q: %w", checkpointID, policy.CheckpointMinVersion, err)
}
if !CanRead(format) {
return fmt.Errorf("checkpoint %s uses unsupported checkpoint_version %q: not read-supported by this Entire CLI", checkpointID, policy.CheckpointMinVersion)
}
return nil
}
Mcmd/entire/cli/checkpointpolicy/policy.go+16
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
package checkpointpolicy_test
import (
"testing"
"github.com/entireio/cli/cmd/entire/cli/checkpoint"
"github.com/entireio/cli/cmd/entire/cli/checkpointpolicy"
"github.com/stretchr/testify/require"
)
func TestUpgradeWarning(t *testing.T) {
t.Parallel()
got := checkpointpolicy.UpgradeWarning("brew upgrade entire")
require.Contains(t, got, "[entire] This repository requires checkpoint support newer than this Entire CLI.")
require.Contains(t, got, "[entire] Upgrade Entire, then rerun the command:")
require.Contains(t, got, "[entire] brew upgrade entire")
}
func TestEnsureCanReadVersion(t *testing.T) {
t.Parallel()
require.NoError(t, checkpointpolicy.EnsureCanReadVersion("abc123", checkpoint.CheckpointVersionBranchV1))
require.NoError(t, checkpointpolicy.EnsureCanReadVersion("abc123", ""))
err := checkpointpolicy.EnsureCanReadVersion("abc123", "refs-v1")
require.ErrorContains(t, err, `checkpoint abc123 uses unsupported checkpoint_version "refs-v1"`)
require.ErrorContains(t, err, "not read-supported")
}
Acmd/entire/cli/checkpointpolicy/warning_test.go+30
22 unmodified lines
23
24
25
26
27
28
29
725 unmodified lines
755
756
757
758
759
760
761
762
763
22 unmodified lines
"github.com/entireio/cli/cmd/entire/cli/agent/types"
"github.com/entireio/cli/cmd/entire/cli/checkpoint"
"github.com/entireio/cli/cmd/entire/cli/checkpoint/id"
"github.com/entireio/cli/cmd/entire/cli/checkpointpolicy"
"github.com/entireio/cli/cmd/entire/cli/interactive"
"github.com/entireio/cli/cmd/entire/cli/logging"
"github.com/entireio/cli/cmd/entire/cli/paths"
725 unmodified lines
if err != nil {
return nil, nil, fmt.Errorf("failed to read checkpoint: %w", err)
}
if err := checkpointpolicy.EnsureCanReadVersion(cpID.String(), summary.CheckpointVersion); err != nil {
return nil, nil, fmt.Errorf("check checkpoint version: %w", err)
}
content, contentErr := checkpoint.ReadLatestSessionContent(ctx, store, cpID, summary)
if contentErr != nil {
return fmt.Errorf("failed to get session log: %w", err)
}
}
Mcmd/entire/cli/explain.go+4
14 unmodified lines
15
16
17
18
19
20
21
338 unmodified lines
360
361
362
363
364
365
366
367
368
577 unmodified lines
946
947
948
945
949
950
951
952
953
954
955
956
957
958
959
12 unmodified lines
972
973
974
975
976
977
978
14 unmodified lines
"github.com/entireio/cli/cmd/entire/cli/checkpoint"
"github.com/entireio/cli/cmd/entire/cli/checkpoint/id"
"github.com/entireio/cli/cmd/entire/cli/checkpoint/remote"
"github.com/entireio/cli/cmd/entire/cli/checkpointpolicy"
"github.com/entireio/cli/cmd/entire/cli/logging"
"github.com/entireio/cli/cmd/entire/cli/paths"
"github.com/entireio/cli/cmd/entire/cli/strategy"
338 unmodified lines
if err != nil {
return nil, fmt.Errorf("read checkpoint: %w", err)
}
if err := checkpointpolicy.EnsureCanReadVersion(checkpointID.String(), summary.CheckpointVersion); err != nil {
return nil, fmt.Errorf("check checkpoint version: %w", err)
}
info := &strategy.CheckpointInfo{
CheckpointID: checkpointID,
CheckpointsCount: summary.CheckpointsCount,
}
return info, nil
}
Mcmd/entire/cli/resume.go+13/-1
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
package strategy
import (
"context"
"fmt"
"log/slog"
"github.com/entireio/cli/cmd/entire/cli/checkpointpolicy"
"github.com/entireio/cli/cmd/entire/cli/interactive"
"github.com/entireio/cli/cmd/entire/cli/logging"
"github.com/entireio/cli/cmd/entire/cli/versioncheck"
"github.com/entireio/cli/cmd/entire/cli/versioninfo"
"github.com/go-git/go-git/v6"
)
func committedCheckpointWriteAllowed(ctx context.Context, repo *git.Repository) bool {
state, err := checkpointpolicy.ReadLocal(ctx, repo)
if err != nil {
logging.Warn(ctx, "checkpoint policy read failed; allowing checkpoint write",
slog.String("error", err.Error()),
)
return true
}
if !checkpointpolicy.UnsupportedWrite(state.Policy) {
return true
}
warnOrLogUnsupportedCheckpointWrite(ctx, state.Policy)
return false
}
func syncCheckpointPolicyForPrePush(ctx context.Context, remoteName string) bool {
repo, err := OpenRepository(ctx)
if err != nil {
logging.Warn(ctx, "checkpoint policy pre-push: failed to open repository; allowing checkpoint push",
slog.String("error", err.Error()),
)
return true
}
defer repo.Close()
target, err := checkpointpolicy.ResolveTarget(ctx, remoteName)
if err != nil {
logging.Warn(ctx, "checkpoint policy pre-push: failed to resolve policy remote; allowing checkpoint push",
slog.String("error", err.Error()),
)
return true
}
state, err := checkpointpolicy.Sync(ctx, repo, target)
if err != nil {
warnOrLogCheckpointPolicySyncFailure(ctx, err)
return true
}
if !checkpointpolicy.UnsupportedWrite(state.Policy) {
return true
}
warnOrLogUnsupportedCheckpointWrite(ctx, state.Policy)
return false
}
func warnOrLogCheckpointPolicySyncFailure(ctx context.Context, err error) {
if interactive.CanPromptInteractively() {
fmt.Fprintf(stderrWriter, "[entire] Could not refresh checkpoint policy: %v\n", err)
return
}
logging.Warn(ctx, "checkpoint policy sync failed",
slog.String("error", err.Error()),
)
}
func warnOrLogUnsupportedCheckpointWrite(ctx context.Context, policy checkpointpolicy.Policy) {
warning := checkpointpolicy.UpgradeWarning(versioncheck.UpdateCommandForCurrentBinary(versioninfo.Version))
if interactive.CanPromptInteractively() {
fmt.Fprint(stderrWriter, warning)
return
}
logging.Warn(ctx, "checkpoint write skipped by policy",
slog.String("checkpoint_version", policy.CheckpointVersion),
slog.String("checkpoint_min_version", policy.CheckpointMinVersion),
)
}
Mcmd/entire/cli/strategy/checkpoint_policy.go+80
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
package strategy
import (
"bytes"
"context"
"os/exec"
"path/filepath"
"strings"
"testing"
"github.com/entireio/cli/cmd/entire/cli/checkpointpolicy"
"github.com/entireio/cli/cmd/entire/cli/interactive"
"github.com/entireio/cli/cmd/entire/cli/paths"
"github.com/entireio/cli/cmd/entire/cli/testutil"
"github.com/go-git/go-git/v6"
"github.com/go-git/go-git/v6/plumbing"
"github.com/stretchr/testify/require"
)
func TestPrePushSkipsCheckpointPushWhenPolicyWriteUnsupported(t *testing.T) {
workDir := setupRepoWithCheckpointBranch(t)
bareDir := filepath.Join(t.TempDir(), "remote.git")
_, err := git.PlainInit(bareDir, true)
require.NoError(t, err)
runCheckpointPolicyGit(t, workDir, "remote", "add", "origin", bareDir)
repo, err := git.PlainOpen(workDir)
require.NoError(t, err)
t.Cleanup(func() {
_ = repo.Close()
})
_, err = checkpointpolicy.WriteLocal(t.Context(), repo, plumbing.ZeroHash, checkpointpolicy.Policy{
CheckpointVersion: "refs-v1",
CheckpointMinVersion: "branch-v1",
})
require.NoError(t, err)
t.Chdir(workDir)
paths.ClearWorktreeRootCache()
t.Setenv(interactive.EnvTestTTY, "1")
oldWriter := stderrWriter
var stderr bytes.Buffer
stderrWriter = &stderr
t.Cleanup(func() { stderrWriter = oldWriter })
err = NewManualCommitStrategy().PrePush(context.Background(), "origin")
require.NoError(t, err)
require.Contains(t, stderr.String(), "requires checkpoint support newer than this Entire CLI")
out := runCheckpointPolicyGit(t, workDir, "ls-remote", bareDir, "refs/heads/"+paths.MetadataBranchName)
require.Empty(t, strings.TrimSpace(out))
}
func runCheckpointPolicyGit(t *testing.T, dir string, args ...string) string {
t.Helper()
cmd := exec.CommandContext(context.Background(), "git", args...)
cmd.Dir = dir
cmd.Env = testutil.GitIsolatedEnv()
output, err := cmd.CombinedOutput()
require.NoError(t, err, string(output))
return string(output)
}
Acmd/entire/cli/strategy/checkpoint_policy_test.go+62
51 unmodified lines
52
53
54
55
55
56
57
58
3 unmodified lines
62
63
64
65
65
66
67
68
51 unmodified lines
// topology. Writes target refs.Primary; reads target refs.Read. The strategy's
// blob fetcher is wired in so reads can fetch blobs on demand after a treeless
// fetch.
func (s *ManualCommitStrategy) getCheckpointStore(ctx context.Context, repo *git.Repository) (checkpoint.CommittedStore, error) {
func (s *ManualCommitStrategy) getCheckpointStore(ctx context.Context, repo *git.Repository) (checkpoint.CommittedStore, error) { //nolint:ireturn // committed store capability is the abstraction boundary
stores, err := s.getCheckpointStores(ctx, repo)
if err != nil {
return nil, err
}
}
// getTemporaryStore returns the git-backed shadow-branch store with the
// strategy's blob fetcher wired in.
func (s *ManualCommitStrategy) getTemporaryStore(ctx context.Context, repo *git.Repository) (checkpoint.TemporaryStore, error) {
func (s *ManualCommitStrategy) getTemporaryStore(ctx context.Context, repo *git.Repository) (checkpoint.TemporaryStore, error) { //nolint:ireturn // temporary store capability is the abstraction boundary
stores, err := s.getCheckpointStores(ctx, repo)
if err != nil {
return nil, err
}
}
Mcmd/entire/cli/strategy/manual_commit.go+2/-2
146 unmodified lines
147
148
149
150
151
152
153
154
155
146 unmodified lines
}
logCtx := logging.WithComponent(ctx, "checkpoint")
condenseStart := time.Now()
if !committedCheckpointWriteAllowed(ctx, repo) {
return newSkippedResult(checkpointID, state.SessionID), nil
}
shadowBranchName := getShadowBranchNameForCommit(state.BaseCommit, state.WorktreeID)
ref, hasShadowBranch := resolveShadowRef(repo, shadowBranchName, o.shadowRef)
}
Mcmd/entire/cli/strategy/manual_commit_condensation.go+3
2761 unmodified lines
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2761 unmodified lines
return 1 // Count as error - all checkpoints will be skipped
}
defer repo.Close()
if !committedCheckpointWriteAllowed(ctx, repo) {
state.TurnCheckpointIDs = nil
return 0
}
prompts := readPromptsFromShadowBranch(ctx, repo, state)
if len(prompts) == 0 {
}
Mcmd/entire/cli/strategy/manual_commit_hooks.go+4
43 unmodified lines
44
45
46
47
48
49
50
51
52
43 unmodified lines
}
refs := checkpoint.ResolveCommittedRefs(ctx)
if !syncCheckpointPolicyForPrePush(ctx, remote) {
return nil
}
// OPF pre-push rewrite: if OPF is configured, resolve the user's
// decision (env > settings > prompt > non-TTY auto-run), then
}
Mcmd/entire/cli/strategy/manual_commit_push.go+3
15 unmodified lines
16
17
18
19
20
21
22
629 unmodified lines
652
653
654
655
656
657
658
659
660
15 unmodified lines
"github.com/entireio/cli/cmd/entire/cli/agent/types"
cpkg "github.com/entireio/cli/cmd/entire/cli/checkpoint"
"github.com/entireio/cli/cmd/entire/cli/checkpoint/id"
"github.com/entireio/cli/cmd/entire/cli/checkpointpolicy"
"github.com/entireio/cli/cmd/entire/cli/interactive"
"github.com/entireio/cli/cmd/entire/cli/osroot"
"github.com/entireio/cli/cmd/entire/cli/paths"
629 unmodified lines
if err != nil {
return nil, fmt.Errorf("failed to read checkpoint: %w", err)
}
if err := checkpointpolicy.EnsureCanReadVersion(point.CheckpointID.String(), summary.CheckpointVersion); err != nil {
return nil, fmt.Errorf("check checkpoint version: %w", err)
}
// Get worktree root for agent session directory lookup
repoRoot, err := paths.WorktreeRoot(ctx)
}
Mcmd/entire/cli/strategy/manual_commit_rewind.go+4
409 unmodified lines
410
411
412
413
414
415
416
417
418
419
409 unmodified lines
return "curl -fsSL https://entire.io/install.sh | bash"
}
func UpdateCommandForCurrentBinary(currentVersion string) string {
return updateCommand(currentVersion)
}
// printNotification prints the version update notification to the user.
func printNotification(w io.Writer, current, latest string) {
fmt.Fprintf(w, "\nUpdate available! %s -> %s\nRelease notes: %s\n",
current, latest, "");
}
Mcmd/entire/cli/versioncheck/versioncheck.go+4
422 unmodified lines
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
422 unmodified lines
}
}
func TestUpdateCommandForCurrentBinary(t *testing.T) {
t.Parallel()
got := UpdateCommandForCurrentBinary("1.2.3")
if got == "" {
t.Fatal("UpdateCommandForCurrentBinary returned empty command")
}
}
// setupCheckAndNotifyTest points the global config dir at a per-test temp
// dir and overrides githubAPIURL. Returns a cobra.Command with captured
// stdout and a cleanup function.
Mcmd/entire/cli/versioncheck/versioncheck_test.go+9
86 unmodified lines
87
88
89
90
91
92
93
94
95
86 unmodified lines
cancel()
os.Exit(1)
}
if cli.ShouldCheckCheckpointPolicyWarning(executed) {
cli.WarnCheckpointPolicyIfNeeded(ctx, rootCmd.ErrOrStderr(), versioninfo.Version)
}
cancel() // Cleanup on successful exit
}
Mcmd/entire/main.go+3