Remove Unused Checkpoint Version Gates

Claude Code·Fable 5·pfleidi·2mo ago·18min·2 Checkpoints·26 file changes·+36/-661·57.9K tokens

For added context:

Session summary: Remove checkpoint_version from checkpoint metadata

Goal: Remove the CheckpointVersion property from checkpoint metadata.json and the gates that check it. Existing checkpoints will be handled by an external migration script, so no backward-compat handling for the stored attribute is needed.

Scope decision (after investigation): The version machinery has three parts: (A) the checkpoint_version stamp in metadata.json — a pure label nothing dispatches on (backend selection comes from settings topology, not the stamp), (B) the EnsureCanReadVersion read gates at resume/explain/rewind/export — forward-compat tripwires that can only fire for hypothetical future formats, and (C) the checkpointpolicy subsystem (policy ref at refs/entire/policies/checkpoint, hidden entire checkpoint policy command, write gates, telemetry). Agreed scope: remove A + B only; C stays untouched. Existing policy refs are left alone.

Approved plan: step-by-step execution, two commits:

  1. remove checkpoint_version read gates
  2. drop checkpoint_version from checkpoint metadata

(Gates must go first — they read the field being removed in commit 2.)

Step 1 — done, awaiting review/commit approval:

  • Deleted cmd/entire/cli/checkpointpolicy/version.go and the gate-only test file checkpoint_policy_read_test.go.
  • Removed the 7 gate call sites and unreachable IsUnsupportedVersion error branches in resume.go, explain.go, explain_export.go, rewind.go, strategy/manual_commit_rewind.go.
  • Dropped two redundant ReadCheckpoint calls (resume session-log restore, rewind transcript restore) that existed only to feed the gate — ReadRawSessionLogForCheckpoint already reads the checkpoint internally.
  • Removed 5 gate-behavior tests and the rewriteExportCheckpointVersionToRefsV2 test helper.
  • Docs: removed the sentence in docs/architecture/sessions-and-checkpoints.md about explain/resume failing on unsupported checkpoint_version.
  • Verified: go build ./... clean, focused tests pass (cli, checkpointpolicy, strategy packages), mise run fmt && mise run lint → 0 issues.

Next steps: Commit Step 1 on approval, then Step 2: remove WriteOptions.CheckpointVersion and CheckpointSummary.CheckpointVersion (api/checkpoint/metadata.go), the stamping plumbing in checkpoint/persistent.go / refs_store.go / manual_commit_condensation.go, normalizeCheckpointSummary, checkpointVersionForNewCheckpoint (attach.go switches to ensureCheckpointPolicyAllowsCheckpointData to keep the write gate), the now-unused CheckpointVersionRefsV1 constant, associated tests, and the metadata.json doc example. Plan file: ~/.claude/plans/abstract-splashing-lemon.md.

2mo ago·17.4s

Step 1 is complete and ready for your review — here's the checkpoint summary.

Step 1: remove checkpoint_version read gates

  • Deleted cmd/entire/cli/checkpointpolicy/version.go (the EnsureCanReadVersion gate) and its gate-only test file checkpoint_policy_read_test.go.
  • Removed the 7 gate call sites and the now-unreachable IsUnsupportedVersion error branches across resume.go, explain.go, explain_export.go, rewind.go, and strategy/manual_commit_rewind.go.
  • Dropped two ReadCheckpoint calls (in the resume session-log restore and the rewind transcript restore) that existed only to feed the gate — ReadRawSessionLogForCheckpoint already reads the checkpoint itself.
  • Removed 5 gate-behavior tests plus the rewriteExportCheckpointVersionToRefsV2 test helper, and dropped the sentence in docs/architecture/sessions-and-checkpoints.md about explain/resume failing on unsupported versions.

Verification (all from this step, nothing changed since): go build ./... clean, focused tests in the cli, checkpointpolicy, and strategy packages pass, and mise run fmt && mise run lint reported 0 issues.

One thing worth calling out from the diff: the dropped redundant ReadCheckpoint calls are a small behavior simplification, not just gate removal — the reads were gate-feeders only, so nothing downstream loses data.

On your approval I'll commit this as remove checkpoint_version read gates, then start Step 2 (dropping the checkpoint_version stamp from metadata writes: WriteOptions.CheckpointVersion, CheckpointSummary.CheckpointVersion, the stamping plumbing, normalizeCheckpointSummary, checkpointVersionForNewCheckpoint, the unused constant, tests, and the doc example).

commit the changes

2mo ago·1m

Committed as bc49bc896b — remove checkpoint_version read gates (11 files, 4 insertions, 359 deletions).

Ready to start Step 2 when you say go: dropping the checkpoint_version stamp itself from metadata writes (WriteOptions.CheckpointVersion, CheckpointSummary.CheckpointVersion, the stamping plumbing in checkpoint/persistent.go / refs_store.go / manual_commit_condensation.go, normalizeCheckpointSummary, checkpointVersionForNewCheckpoint, the unused constant, associated tests, and the metadata.json doc example).

Is there anything else that's missing here?

2mo ago·1m

Step 1 missed nothing, and Step 2's plan is nearly complete — the sweep found two small additions to fold into Step 2, plus confirmation that everything else remaining is the policy subsystem we agreed to leave alone.

Additions for Step 2:

  1. cmd/entire/cli/checkpoint/aliases.go:57-58 — the re-exported CheckpointVersionRefsV1 alias has to go along with the underlying constant in api/checkpoint/errors.go. (The CheckpointVersionBranchV1 constant and its alias must stay — the policy subsystem uses it heavily as the default policy version in checkpointpolicy/policy.go.)
  2. Doc paragraph, not just the example — in sessions-and-checkpoints.md, the checkpoint metadata example (line 254) is planned, but the Checkpoint Policy section's prose (lines 323-329) says checkpoint_version "selects the checkpoint format for new writes" and that "the CLI writes its default checkpoint version." After Step 2 nothing is written — the policy field becomes purely a write gate (writers fail if the configured version is unsupported, which ensureCheckpointPolicyAllowsCheckpointData keeps enforcing). That paragraph needs a light rewording, not removal. The policy file example at line 311 stays as-is — that's the policy's own field, not the metadata stamp.

Confirmed in-plan: normalizeCheckpointSummary lives in checkpoint/persistent_reader.go (its only job is backfilling the version default, so file and both call sites in persistent.go go away), and the version-asserting tests are in persistent_write_test.go, persistent_update_test.go, refs_store_test.go, refs_store_seam_test.go, and persistent_tripwire_test.go.

Confirmed out of scope (policy subsystem, stays): hook_registry.go, hooks_git_cmd.go, import_cmd.go's help text, and everything under checkpointpolicy/ — those all reference the policy's CheckpointVersion field, not the metadata stamp. attach.go switching from checkpointVersionForNewCheckpoint to ensureCheckpointPolicyAllowsCheckpointData matches the existing pattern in explain.go:701 and import_cmd.go:59, after which checkpointVersionForNewCheckpoint is deleted.

Say the word and I'll execute Step 2 with these two additions folded in.

Do it

2mo ago·10m

Step 2: drop checkpoint_version from checkpoint metadata — ready for review, 32 insertions / 302 deletions across 15 files.

What changed:

  • api/checkpoint/metadata.go: removed WriteOptions.CheckpointVersion and CheckpointSummary.CheckpointVersion; api/checkpoint/errors.go and checkpoint/aliases.go: removed the now-unused CheckpointVersionRefsV1 constant and its alias. CheckpointVersionBranchV1 stays — the policy subsystem uses it as its default version.
  • checkpoint/persistent.go + refs_store.go: the checkpointVersion parameter is gone from the whole write chain (applySessionWrite → writeStandardCheckpointEntries → writeCheckpointSummary), along with the preserve-existing-version read in writeCheckpointSummary. normalizeCheckpointSummary deleted from persistent_reader.go (the file keeps AuthorReader); its two call sites now return the parsed summary directly.
  • strategy/manual_commit_condensation.go and attach.go: no longer resolve a version from the policy. The policy write gate stays in both — condensation keeps its CanSatisfyPolicy check, and attach switched from checkpointVersionForNewCheckpoint (deleted from checkpoint_policy_write.go) to the existing ensureCheckpointPolicyAllowsCheckpointData, matching explain and import.
  • Tests: deleted the six version stamp/backfill/preserve tests and the now-orphaned rewriteRootSummary helper; dropped the version assertion from the refs-store and seam tests (their other assertions stay).
  • Docs: removed checkpoint_version from the metadata.json example, and reworded the policy section — its checkpoint_version field is now described as a pure write guard rather than "selects the format for new writes", since nothing is stamped anymore.

One diff note: attach.go shows more changed lines than the logical edit because removing CheckpointVersion (the longest field name) made gofmt realign the whole writeOpts struct literal.

Verification: go build ./... clean; go test -count=1 passes on api/checkpoint, all cmd/entire/cli/checkpoint/... packages, strategy, and cli; mise run fmt && mise run lint → 0 issues on the formatted tree. A leftover-reference sweep finds only the policy subsystem's own CheckpointVersion fields, which are intentionally untouched.

On approval I'll commit as drop checkpoint_version from checkpoint metadata, which completes the planned work.

I'm assuming CheckpointVersionBranchV1 is still used for the other policy decisions, like setting checkpoint version via a policy, correct?

2mo ago·26.5s

Correct. CheckpointVersionBranchV1 remains the anchor value for the whole policy subsystem — that's exactly why it stayed while CheckpointVersionRefsV1 was deleted. In checkpointpolicy/policy.go it feeds:

  • DefaultPolicy() / DefaultCheckpointVersion() — the values used when no policy ref is configured (lines 15-24).
  • Normalize() — backfills both checkpoint_version and checkpoint_min_version when a policy omits them or sets them to empty (lines 26-34).
  • The gate evaluations built on those — ValidatePolicy (rejects a --checkpoint-version this CLI can't write, and min > version downgrades), CanSatisfyPolicy (the write gate that condensation, attach, explain, and import still call), RequiresUpgrade, and the upgrade-nudge error messages.

So entire checkpoint policy --checkpoint-version ... still works end to end: the value is parsed via ParseFormat, validated against CanWrite, stored in the policy ref, and enforced as a write gate on every checkpoint-data writer. The only thing that changed is what happens after the gate passes — the resolved version used to also be stamped into each checkpoint's metadata.json, and now it isn't consumed for anything beyond the gate decision. That matches the doc rewording in Step 2: the policy field went from "selects the format for new writes" to a pure write guard.

Still holding at the Step 2 checkpoint — ready to commit on your approval.

commit and continue, then

2mo ago·30.2s

Committed as e4ca4508d0 — drop checkpoint_version from checkpoint metadata (15 files, 32 insertions, 302 deletions).

That was the last planned step, so the branch is complete: two commits, gates removed first, then the stamp itself.

  1. bc49bc896b — remove checkpoint_version read gates
  2. e4ca4508d0 — drop checkpoint_version from checkpoint metadata

The policy subsystem (refs/entire/policies/checkpoint, the hidden entire checkpoint policy command, write gates, telemetry) is untouched as agreed, and existing checkpoints' stored checkpoint_version values are left for the external migration script.

One note before this ships: I ran unit tests on the changed packages but not mise run test:integration or the e2e canary — if any integration test asserts on checkpoint_version in metadata.json, it would only surface there. Want me to run the integration suite, or go straight to /pr?