Skip to content
55 changes: 21 additions & 34 deletions cmd/entire/cli/attach_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,13 +352,16 @@ func TestAttach_V2DualWriteEnabled(t *testing.T) {
}
}

func TestAttach_CheckpointsVersion2(t *testing.T) {
// TestAttach_CheckpointsVersion2_FallsBackToV1 verifies that
// strategy_options.checkpoints_version: 2 is now ignored — attach writes
// v1 metadata only, and never creates v2 refs solely because of the setting.
func TestAttach_CheckpointsVersion2_FallsBackToV1(t *testing.T) {
setupAttachTestRepo(t)

repoDir := mustGetwd(t)
setAttachCheckpointsV2Only(t, repoDir)

sessionID := "test-attach-v2-only"
sessionID := "test-attach-v2-disallowed"
setupClaudeTranscript(t, sessionID, `{"type":"user","message":{"role":"user","content":"create hello.txt"},"uuid":"uuid-1"}
{"type":"assistant","message":{"role":"assistant","content":[{"type":"tool_use","id":"tu_1","name":"Write","input":{"file_path":"hello.txt","content":"hello"}}]},"uuid":"uuid-2"}
{"type":"user","message":{"content":[{"type":"tool_result","tool_use_id":"tu_1","content":"wrote file"}]},"uuid":"uuid-3"}
Expand Down Expand Up @@ -388,24 +391,12 @@ func TestAttach_CheckpointsVersion2(t *testing.T) {
}

cpPath := state.LastCheckpointID.Path()
if _, found := readFileFromRef(t, repo, paths.MetadataBranchName, cpPath+"/"+paths.MetadataFileName); found {
t.Fatalf("did not expect %s metadata for %s when checkpoints_version is 2", paths.MetadataBranchName, cpPath)
v1Ref := string(plumbing.NewBranchReferenceName(paths.MetadataBranchName))
if _, found := readFileFromRef(t, repo, v1Ref, cpPath+"/"+paths.MetadataFileName); !found {
t.Fatalf("expected v1 metadata at %s for %s — checkpoints_version: 2 should fall back to v1", paths.MetadataBranchName, cpPath)
}

mainCompact, found := readFileFromRef(t, repo, paths.V2MainRefName, cpPath+"/0/"+paths.CompactTranscriptFileName)
if !found {
t.Fatalf("expected %s on %s", paths.CompactTranscriptFileName, paths.V2MainRefName)
}
if !strings.Contains(mainCompact, "create hello.txt") {
t.Errorf("compact transcript missing prompt, got:\n%s", mainCompact)
}

fullTranscript, found := readFileFromRef(t, repo, paths.V2FullCurrentRefName, cpPath+"/0/"+paths.V2RawTranscriptFileName)
if !found {
t.Fatalf("expected %s on %s", paths.V2RawTranscriptFileName, paths.V2FullCurrentRefName)
}
if !strings.Contains(fullTranscript, "hello.txt") {
t.Errorf("raw transcript missing file content, got:\n%s", fullTranscript)
if _, err := repo.Reference(plumbing.ReferenceName(paths.V2MainRefName), true); err == nil {
t.Fatalf("did not expect %s when checkpoints_version: 2 is configured but disallowed", paths.V2MainRefName)
}
}

Expand Down Expand Up @@ -622,36 +613,32 @@ func TestAttach_RefusesWhenCheckpointOnlyInRemoteTrackingRef(t *testing.T) {
}
}

// In v2-only mode, the refuse hint must reference the v2 /main ref and
// its fully-qualified refspec (refs/entire/checkpoints/v2/main lives under
// refs/entire/, not refs/heads/, so a short refspec won't resolve).
func TestAttach_RefuseHint_V2Only(t *testing.T) {
// TestAttach_RefuseHint_CheckpointsVersion2IgnoredFallsBackToV1 verifies that
// strategy_options.checkpoints_version: 2 no longer flips attach into v2-only
// mode — the refuse hint references the v1 metadata branch, not the v2 /main
// ref, because v2 is disallowed and the system falls back to v1.
func TestAttach_RefuseHint_CheckpointsVersion2IgnoredFallsBackToV1(t *testing.T) {
setupAttachTestRepo(t)

repoRoot := mustGetwd(t)
setAttachCheckpointsV2Only(t, repoRoot)

runGitInDir(t, repoRoot, "commit", "--amend", "-m", "init\n\nEntire-Checkpoint: ffffffffeeee")

sessionID := "v2-orphaned-attach"
sessionID := "v2-disallowed-attach"
setupClaudeTranscript(t, sessionID, `{"type":"user","message":{"role":"user","content":"hi"},"uuid":"u1"}
`)

var out bytes.Buffer
err := runAttach(context.Background(), &out, sessionID, agent.AgentNameClaudeCode, attachOptions{Force: true})
if err == nil {
t.Fatal("expected v2-only attach to refuse when checkpoint is missing")
t.Fatal("expected attach to refuse when checkpoint is missing")
}
if !strings.Contains(err.Error(), "missing from the local v2 /main ref") {
t.Errorf("error should describe the v2 /main ref; got: %v", err)
}
v2Refspec := paths.V2MainRefName + ":" + paths.V2MainRefName
if !strings.Contains(err.Error(), v2Refspec) {
t.Errorf("error should include v2 refspec %q; got: %v", v2Refspec, err)
if !strings.Contains(err.Error(), "missing from the local entire/checkpoints/v1 branch") {
t.Errorf("error should describe the v1 branch (v2 is disallowed); got: %v", err)
}
// And must NOT suggest the v1 refspec.
if strings.Contains(err.Error(), "entire/checkpoints/v1:entire/checkpoints/v1") {
t.Errorf("v2-only hint should not reference the v1 branch; got: %v", err)
if !strings.Contains(err.Error(), "entire/checkpoints/v1:entire/checkpoints/v1") {
t.Errorf("error should include v1 refspec; got: %v", err)
}
}

Expand Down
56 changes: 23 additions & 33 deletions cmd/entire/cli/integration_test/v2_dual_write_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,11 +245,10 @@ func TestV2DualWrite_StopTimeFinalization(t *testing.T) {
assert.Contains(t, compactTranscript, `"v":1`)
}

// TestCheckpointsVersion2_SkipsV1Write verifies the specific deltas of running
// with checkpoints_version: 2 — v1 metadata is not written and v2 refs still
// exist. The full v2 payload shape is already covered by
// TestV2DualWrite_FullWorkflow.
func TestCheckpointsVersion2_SkipsV1Write(t *testing.T) {
// TestCheckpointsVersion2Disallowed_WritesV1 verifies that setting
// strategy_options.checkpoints_version: 2 is now ignored — v1 metadata is
// written and v2 refs are not created by the setting alone.
func TestCheckpointsVersion2Disallowed_WritesV1(t *testing.T) {
t.Parallel()
env := NewTestEnv(t)
defer env.Cleanup()
Expand All @@ -260,7 +259,7 @@ func TestCheckpointsVersion2_SkipsV1Write(t *testing.T) {
env.GitAdd("README.md")
env.GitAdd(".gitignore")
env.GitCommit("Initial commit")
env.GitCheckoutNewBranch("feature/checkpoints-v2-test")
env.GitCheckoutNewBranch("feature/checkpoints-v2-disallowed")

env.InitEntireWithOptions(map[string]any{
"checkpoints_version": 2,
Expand All @@ -285,21 +284,21 @@ func TestCheckpointsVersion2_SkipsV1Write(t *testing.T) {
require.NoError(t, err)
cpPath := cpID.Path()

// v1: should NOT be written.
_, found := env.ReadFileFromBranch(paths.MetadataBranchName, cpPath+"/"+paths.MetadataFileName)
assert.False(t, found,
"v1 committed checkpoint metadata should NOT exist when checkpoints_version is 2")
assert.True(t, found,
"v1 committed checkpoint metadata should be written even with checkpoints_version: 2 (the setting is disallowed)")

// v2: smoke check that the checkpoint still landed.
assert.True(t, env.RefExists(paths.V2MainRefName), "v2 /main ref should exist")
assert.True(t, env.RefExists(paths.V2FullCurrentRefName), "v2 /full/current ref should exist")
assert.False(t, env.RefExists(paths.V2MainRefName),
"v2 /main ref should NOT exist solely because checkpoints_version: 2 is configured")
assert.False(t, env.RefExists(paths.V2FullCurrentRefName),
"v2 /full/current ref should NOT exist solely because checkpoints_version: 2 is configured")
}

// TestCheckpointsVersion2_HookDrivenCommitOnMain verifies the same hook-driven
// prompt -> stop -> commit flow used by the attach E2E precondition setup. In
// checkpoints_version: 2 mode, the normal git hook path should still write only
// v2 refs even when committing on the default branch.
func TestCheckpointsVersion2_HookDrivenCommitOnMain(t *testing.T) {
// TestCheckpointsVersion2Disallowed_HookDrivenCommitOnMain verifies the same
// hook-driven prompt -> stop -> commit flow used by the attach E2E
// precondition setup. checkpoints_version: 2 is now disallowed, so the normal
// git hook path writes v1 metadata even when the setting is configured.
func TestCheckpointsVersion2Disallowed_HookDrivenCommitOnMain(t *testing.T) {
t.Parallel()
env := NewTestEnv(t)
defer env.Cleanup()
Expand All @@ -315,8 +314,6 @@ func TestCheckpointsVersion2_HookDrivenCommitOnMain(t *testing.T) {
"checkpoints_version": 2,
})

// Mirror the E2E flow: enable is committed first, then a normal prompt/stop
// cycle produces content that the subsequent user commit condenses.
env.GitAdd(".entire/settings.json")
env.GitCommit("Enable entire")

Expand All @@ -339,19 +336,12 @@ func TestCheckpointsVersion2_HookDrivenCommitOnMain(t *testing.T) {
require.NoError(t, err)
cpPath := cpID.Path()

// v1 should remain absent in strict v2-only mode.
_, found := env.ReadFileFromBranch(paths.MetadataBranchName, cpPath+"/"+paths.MetadataFileName)
assert.False(t, found, "v1 metadata branch should not be used when checkpoints_version is 2")

// v2 refs should contain the checkpoint created by the normal hook path.
assert.True(t, env.RefExists(paths.V2MainRefName), "v2 /main ref should exist after hook-driven commit")
assert.True(t, env.RefExists(paths.V2FullCurrentRefName), "v2 /full/current ref should exist after hook-driven commit")

mainSummary, found := env.ReadFileFromRef(paths.V2MainRefName, cpPath+"/"+paths.MetadataFileName)
require.True(t, found, "v2 /main metadata.json should exist")
assert.Contains(t, mainSummary, cpIDStr)
v1Summary, found := env.ReadFileFromBranch(paths.MetadataBranchName, cpPath+"/0/"+paths.MetadataFileName)
require.True(t, found, "v1 metadata branch should be written even with checkpoints_version: 2 (the setting is disallowed)")
assert.Contains(t, v1Summary, cpIDStr)

fullTranscript, found := env.ReadFileFromRef(paths.V2FullCurrentRefName, cpPath+"/0/"+paths.V2RawTranscriptFileName)
require.True(t, found, "v2 /full/current raw transcript should exist")
assert.Contains(t, fullTranscript, "existing checkpoints")
assert.False(t, env.RefExists(paths.V2MainRefName),
"v2 /main ref should NOT exist solely because checkpoints_version: 2 is configured")
assert.False(t, env.RefExists(paths.V2FullCurrentRefName),
"v2 /full/current ref should NOT exist solely because checkpoints_version: 2 is configured")
}
20 changes: 10 additions & 10 deletions cmd/entire/cli/integration_test/v2_push_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,11 @@ func TestV2Push_Disabled_NoV2Refs(t *testing.T) {
"v1 metadata branch should still exist on remote")
}

// TestV2Push_Version2SkipsV1Branch verifies that the v1 metadata branch is not
// pushed when checkpoints_version is set to 2; v2 ref pushing itself is covered
// by TestV2Push_FullCycle.
func TestV2Push_Version2SkipsV1Branch(t *testing.T) {
// TestPush_CheckpointsVersion2DisallowedFallsBackToV1 verifies that setting
// strategy_options.checkpoints_version: 2 is now ignored — the v1 metadata
// branch is pushed (as if checkpoints_version were unset), and no v2 refs are
// created by the setting alone.
func TestPush_CheckpointsVersion2DisallowedFallsBackToV1(t *testing.T) {
t.Parallel()
env := NewTestEnv(t)
defer env.Cleanup()
Expand All @@ -138,7 +139,7 @@ func TestV2Push_Version2SkipsV1Branch(t *testing.T) {
env.GitAdd("README.md")
env.GitAdd(".gitignore")
env.GitCommit("Initial commit")
env.GitCheckoutNewBranch("feature/checkpoints-v2-push-test")
env.GitCheckoutNewBranch("feature/checkpoints-v2-disallowed-push")

env.InitEntireWithOptions(map[string]any{
"checkpoints_version": 2,
Expand All @@ -161,9 +162,8 @@ func TestV2Push_Version2SkipsV1Branch(t *testing.T) {

env.RunPrePush("origin")

assert.False(t, bareRefExists(t, bareDir, "refs/heads/"+paths.MetadataBranchName),
"v1 metadata branch should NOT exist on remote when checkpoints_version is 2")
// Smoke: v2 refs still land; full payload asserted in TestV2Push_FullCycle.
assert.True(t, bareRefExists(t, bareDir, paths.V2MainRefName),
"v2 /main ref should exist on remote after push")
assert.True(t, bareRefExists(t, bareDir, "refs/heads/"+paths.MetadataBranchName),
"v1 metadata branch should be pushed even with checkpoints_version: 2 (the setting is disallowed)")
assert.False(t, bareRefExists(t, bareDir, paths.V2MainRefName),
"v2 /main ref should NOT be pushed solely because checkpoints_version: 2 is configured")
}
26 changes: 13 additions & 13 deletions cmd/entire/cli/integration_test/v2_resume_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,10 @@ func TestV2Resume_FallsBackToV1(t *testing.T) {
assert.Contains(t, output, "claude -r")
}

// TestCheckpointsVersion2Resume_SwitchBranchWithSession verifies that resume
// works in strict v2-only mode. The session should be restorable from v2 refs
// even though no v1 committed metadata was written.
func TestCheckpointsVersion2Resume_SwitchBranchWithSession(t *testing.T) {
// TestCheckpointsVersion2DisallowedResume_SwitchBranchWithSession verifies
// that resume works when checkpoints_version: 2 is configured but disallowed.
// The session is restored from v1 (the fallback target) rather than v2.
func TestCheckpointsVersion2DisallowedResume_SwitchBranchWithSession(t *testing.T) {
t.Parallel()
env := NewTestEnv(t)
defer env.Cleanup()
Expand All @@ -198,35 +198,35 @@ func TestCheckpointsVersion2Resume_SwitchBranchWithSession(t *testing.T) {
env.GitAdd("README.md")
env.GitAdd(".gitignore")
env.GitCommit("Initial commit")
env.GitCheckoutNewBranch("feature/v2-only-resume-test")
env.GitCheckoutNewBranch("feature/v2-disallowed-resume-test")

env.InitEntireWithOptions(map[string]any{
"checkpoints_version": 2,
})

session := env.NewSession()
err := env.SimulateUserPromptSubmitWithPrompt(session.ID, "Create hello script in v2 only mode")
err := env.SimulateUserPromptSubmitWithPrompt(session.ID, "Create hello script with v2 disallowed")
require.NoError(t, err)

content := "puts 'Hello from v2-only session'"
env.WriteFile("hello_v2_only.rb", content)
content := "puts 'Hello from v2-disallowed session'"
env.WriteFile("hello_v2_disallowed.rb", content)

session.CreateTranscript(
"Create hello script in v2 only mode",
[]FileChange{{Path: "hello_v2_only.rb", Content: content}},
"Create hello script with v2 disallowed",
[]FileChange{{Path: "hello_v2_disallowed.rb", Content: content}},
)
err = env.SimulateStop(session.ID, session.TranscriptPath)
require.NoError(t, err)

env.GitCommitWithShadowHooks("Create hello script in v2 only mode", "hello_v2_only.rb")
env.GitCommitWithShadowHooks("Create hello script with v2 disallowed", "hello_v2_disallowed.rb")

cpIDStr := env.GetLatestCheckpointIDFromHistory()
require.NotEmpty(t, cpIDStr, "checkpoint ID should be in commit trailer")

cpID, err := id.NewCheckpointID(cpIDStr)
require.NoError(t, err)
_, found := env.ReadFileFromBranch(paths.MetadataBranchName, cpID.Path()+"/"+paths.MetadataFileName)
assert.False(t, found, "v1 committed checkpoint metadata should not exist when checkpoints_version is 2")
_, found := env.ReadFileFromBranch(paths.MetadataBranchName, cpID.Path()+"/0/"+paths.MetadataFileName)
assert.True(t, found, "v1 committed metadata should be written when checkpoints_version: 2 is disallowed")

featureBranch := env.GetCurrentBranch()
env.GitCheckoutBranch("master")
Expand Down
20 changes: 15 additions & 5 deletions cmd/entire/cli/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -1072,8 +1072,9 @@ func (s *EntireSettings) IsCheckpointsV2Enabled() bool {
}

// CheckpointsVersion returns the configured checkpoints format version from
// strategy_options.checkpoints_version. Returns 1 when unset, invalid, or
// unsupported. The currently supported versions are 1 and 2.
// strategy_options.checkpoints_version. Returns 1 when unset or invalid.
// checkpoints_version: 2 is no longer supported — when configured, a one-time
// warning is emitted and 1 is returned instead.
func (s *EntireSettings) CheckpointsVersion() int {
if s.StrategyOptions == nil {
return 1
Expand All @@ -1083,10 +1084,19 @@ func (s *EntireSettings) CheckpointsVersion() int {
return 1
}
version, ok := parseCheckpointsVersion(val)
if ok {
return version
if !ok {
return 1
}
return 1
if version == 2 {
checkpointsVersionWarningOnce.Do(func() {
fmt.Fprintf(os.Stderr,
"[entire] strategy_options.checkpoints_version %v is no longer supported. Falling back to version 1.\n",
val,
)
})
return 1
}
return version
Comment thread
computermode marked this conversation as resolved.
Outdated
}

func parseCheckpointsVersion(val any) (int, bool) {
Expand Down
Loading
Loading