block checkpoint data writers on policy · Entire

block checkpoint data writers on policy

c553b61→main·

pfleidi·2w ago·7 files·+112 added/-38 removed

Reject explicit checkpoint-data writes when the local checkpoint policy requires unsupported checkpoint versions.

Sessions

89792fc1526aView transcript

[?
Enforce Checkpoint Policies in CLICodex·GPT-5.5·2 steps](/content/gh/entireio/cli/session/019f05ad-eea0-7202-a508-ec34d069a2d2#timeline-89792fc1526a/index.html)

Changes

7

254 unmodified lines

255
256
257
258
258
259
260
261
262
263
264

254 unmodified lines

return nil
    }

checkpointVersion := committedCheckpointVersion(ctx, repo)
checkpointVersion, err := checkpointVersionForNewCheckpoint(ctx, repo)
if err != nil {
    return err
}

// Resolve agent and transcript path.
ag, transcriptPath, err := resolveAgentAndTranscript(logCtx, w, sessionID, agentName, existingState)

Mcmd/entire/cli/attach.go+4/-1

68 unmodified lines

69
70
71
72
72
73
74
75
1 unmodified line

77
78
79
80
81
82
83
84
85
80
81
82
83
84
2 unmodified lines

87
88
89
94
95
90
91
92
93
94
3 unmodified lines

98
99
100
105
106
107
108
109
110
111
112
113
101
102
103
104
105
1389 unmodified lines

1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511

68 unmodified lines

}
}

func TestAttachUsesDefaultWhenPolicyWriteUnsupported(t *testing.T) {
func TestAttachBlocksWhenPolicyWriteUnsupported(t *testing.T) {
setupAttachTestRepo(t)

repoRoot := mustGetwd(t)
1 unmodified line

if err != nil {
    t.Fatal(err)
}
if _, err := checkpointpolicy.WriteLocal(context.Background(), repo, plumbing.ZeroHash, checkpointpolicy.Policy{
    CheckpointVersion: "refs-v1",
    CheckpointMinVersion: "branch-v1",
}); err != nil {
    t.Fatal(err)
}
t.Cleanup(func() { _ = repo.Close() })
writeUnsupportedCheckpointPolicyForCLITest(t, repo)

sessionID := "test-attach-policy-unsupported"
setupClaudeTranscript(t, sessionID, `{"type":"user","message":{"role":"user","content":"create a file"},"uuid":"uuid-1"}`

2 unmodified lines

var out bytes.Buffer
    err = runAttach(context.Background(), &out, sessionID, agent.AgentNameClaudeCode, attachOptions{Force: true})
if err != nil {
    t.Fatalf("runAttach failed: %v", err)
if err == nil || !strings.Contains(err.Error(), "checkpoint policy cannot be satisfied by this Entire CLI") {
    t.Fatalf("runAttach error = %v, want unsupported checkpoint policy", err)
}
stateStore, err := session.NewStateStore(context.Background())
if err != nil {
3 unmodified lines

if err != nil {
    t.Fatal(err)
}
if state == nil || state.LastCheckpointID.IsEmpty() {
    t.Fatalf("expected attach to record checkpoint state, got %+v", state)
}
summary, err := cpkg.NewGitStore(repo, cpkg.DefaultV1Refs()).Read(context.Background(), state.LastCheckpointID)
if err != nil {
    t.Fatalf("Read: %v", err)
}
if summary.CheckpointVersion != checkpointpolicy.DefaultCheckpointVersion() {
    t.Fatalf("CheckpointVersion = %q, want %q", summary.CheckpointVersion, checkpointpolicy.DefaultCheckpointVersion())
if state != nil {
    t.Fatalf("expected attach not to record checkpoint state, got %+v", state)
}
}

1389 unmodified lines

}
}

func writeUnsupportedCheckpointPolicyForCLITest(t *testing.T, repo *git.Repository) {
t.Helper()
_, err := checkpointpolicy.WriteLocal(t.Context(), repo, plumbing.ZeroHash, checkpointpolicy.Policy{
    CheckpointVersion: "refs-v1",
    CheckpointMinVersion: "branch-v1",
})
if err != nil {
    t.Fatal(err)
}
}

// setupAttachTestRepo creates a temp git repo with one commit and enables Entire.
// Returns the repo directory. Caller must not use t.Parallel() (uses t.Chdir).
func setupAttachTestRepo(t *testing.T) {

Mcmd/entire/cli/attach_test.go+18/-18

1 unmodified line

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
12
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
15
16
38
39
18
40
20
41
42
22
43
44
45
46
47
48
49
50
51
52

1 unmodified line

import (
    "context"
    "errors"
    "fmt"
    "log/slog"
    "strings"

"github.com/entireio/cli/cmd/entire/cli/checkpointpolicy"
    "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 committedCheckpointVersion(ctx context.Context, repo *git.Repository) string {
var errUnsupportedCheckpointPolicy = errors.New("checkpoint policy cannot be satisfied by this Entire CLI")

func checkpointVersionForNewCheckpoint(ctx context.Context, repo *git.Repository) (string, error) {
    policy := localCheckpointPolicyForNewCheckpoint(ctx, repo)
    if !checkpointpolicy.CanSatisfyPolicy(policy) {
        return "", unsupportedCheckpointPolicyError(policy)
    }
    return checkpointpolicy.CheckpointVersion(policy), nil
}

func ensureCheckpointPolicyAllowsCheckpointData(ctx context.Context, repo *git.Repository) error {
    policy := localCheckpointPolicyForNewCheckpoint(ctx, repo)
    if checkpointpolicy.CanSatisfyPolicy(policy) {
        return nil
    }
    return unsupportedCheckpointPolicyError(policy)
}

func localCheckpointPolicyForNewCheckpoint(ctx context.Context, repo *git.Repository) checkpointpolicy.Policy {
    state, err := checkpointpolicy.ReadLocal(ctx, repo)
    if err != nil {
        version := checkpointpolicy.DefaultCheckpointVersion()
        logging.Warn(ctx, "checkpoint policy read failed; using default checkpoint version", logging.Warn(ctx, "checkpoint policy read failed; using default checkpoint policy",
s					slog.String("error", err.Error()),
                    slog.String("using_checkpoint_version", version),
            )
        return version
        return checkpointpolicy.DefaultPolicy()
    }
    return checkpointpolicy.CheckpointVersion(state.Policy)
    return state.Policy
}

func unsupportedCheckpointPolicyError(policy checkpointpolicy.Policy) error {
    message := strings.TrimSpace(checkpointpolicy.UnsupportedPolicyMessage(
        policy,
        versioncheck.UpdateCommandForCurrentBinary(versioninfo.Version),
    ))
    return fmt.Errorf("%w:\n%s", errUnsupportedCheckpointPolicy, message)
}