fix: add tree hash and patch ID checkpoint linkage fallback#840
fix: add tree hash and patch ID checkpoint linkage fallback#840peyton-alt wants to merge 21 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Stores a commit’s git tree hash in checkpoint metadata during condensation so the web side can re-link checkpoints after history rewrites that drop the Entire-Checkpoint trailer, and tweaks prepare-commit-msg behavior so agent-driven revert/cherry-pick operations still get checkpoint trailers when a session is active.
Changes:
- Add
tree_hashto committed checkpoint metadata (CommittedMetadata,WriteCommittedOptions) and populate it during PostCommit condensation from the HEAD commit’s tree hash. - Allow prepare-commit-msg to proceed during git sequence operations (revert/cherry-pick/rebase) when an ACTIVE session exists in the current worktree; keep skipping when no active session.
- Add unit tests for
tree_hashpersistence and the new revert trailer behavior split by agent-active vs user/manual.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| cmd/entire/cli/strategy/manual_commit_test.go | Adds tests ensuring agent revert gets a trailer when session is ACTIVE and user revert is skipped when not active. |
| cmd/entire/cli/strategy/manual_commit_session.go | Adds helper to detect whether any session in the current worktree is ACTIVE. |
| cmd/entire/cli/strategy/manual_commit_hooks.go | Adjusts PrepareCommitMsg sequence-operation skip logic; passes commit tree hash into condensation options. |
| cmd/entire/cli/strategy/manual_commit_condensation.go | Threads treeHash through condensation options into committed checkpoint write options. |
| cmd/entire/cli/checkpoint/committed.go | Writes TreeHash into per-session committed metadata.json. |
| cmd/entire/cli/checkpoint/checkpoint.go | Extends checkpoint option/metadata structs to include TreeHash serialized as tree_hash. |
| cmd/entire/cli/checkpoint/checkpoint_test.go | Adds coverage that tree_hash is written and read back from committed metadata. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7b9d87d to
8302847
Compare
0dfcba0 to
703bd67
Compare
703bd67 to
fa8cbb5
Compare
When an agent runs git revert or cherry-pick as part of its work, the commit should be checkpointed. Previously prepare-commit-msg unconditionally skipped during sequence operations, making the agent's work invisible to Entire. Now checks for active sessions: if an agent session is ACTIVE, the operation is agent-initiated and gets a trailer. If no active session, it's user-initiated and is skipped as before. Part of fix for #834. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 85df9ac94bc7
Add tree_hash field to committed checkpoint metadata. Records the git tree hash of the commit being condensed, enabling fallback checkpoint lookup by tree hash when the Entire-Checkpoint trailer is stripped by git history rewrites (rebase, filter-branch, amend). Part of fix for #834. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 77773a25069e
- Add debug logging to hasActiveSessionInWorktree error paths - Remove unrelated files (greetings.md, agent configs) from PR Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: bacd9b68b1c0
Define content-based linkage signals (tree_hash, patch_id, files_changed_hash, session_files_hash) for re-linking checkpoints after git history rewrites. Stored at checkpoint level, not per-session. Part of fix for #834. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 4661e8c50610
Needed by linkage signal tests that verify patch ID stability across rebase. Part of fix for #834. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 4129c08c80b0
ComputePatchID: git patch-id of the commit diff, survives rebase. ComputeFilesChangedHash: SHA256 of sorted file:blob pairs, survives rebase even with conflicts in non-agent files. Uses single git ls-tree call for all files (O(1) subprocess). Part of fix for #834. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: c100b592e3a0
Replace per-session TreeHash with checkpoint-level LinkageMetadata containing tree_hash, patch_id, files_changed_hash, and session_files_hash. Computed in PostCommit handlers, passed through condenseOpts to CondenseSession, written to CheckpointSummary on entire/checkpoints/v1. Part of fix for #834. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 75ce05cfe11b
Verify LinkageMetadata is stored in CheckpointSummary and readable. Also verify nil linkage is omitted (backward compat with old checkpoints). Part of fix for #834. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 10dae87903d7
gofmt stripped nolint directives from capabilities.go. Restore from main. Add encoding/hex import for ComputeFilesChangedHash. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: c26d3dce1d32
- Restore nolint:ireturn on capabilities.go (gofmt stripped them) - Set user.name/email in gitops initTestRepo for CI compatibility (git rebase fails without repo-level config on CI runners) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: f1ca53b63c79
- Add omitempty to all LinkageMetadata JSON tags for consistency - Return error for malformed git ls-tree lines instead of silent skip - Compute commit-level linkage once (not per-session) via baseLinkage cache; only SessionFilesHash varies per session - Add code comment explaining deferred condensation for agent reverts - Add integration test verifying full linkage pipeline (PostCommit → condensation → ReadCommitted with all four signals populated) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: e3539c5bfa31
- Replace unreachable Fields/len guard with strings.Cut in ComputePatchID - Use logCtx variable in linkageForSession for logging consistency - Use strings.TrimSpace in revParse test helper instead of raw byte slice Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 91b06c4aabdb
fa8cbb5 to
7cfa919
Compare
Entire-Checkpoint: 5f47003ea308
Entire-Checkpoint: 391cbda054cc
Narrow checkpoint linkage to tree hash and patch ID
7487fcf to
da5cea2
Compare
There was a problem hiding this comment.
Claude Review Suggestions:
- Misleading field name
IsRebaseInProgressatcmd/entire/cli/strategy/manual_commit_hooks.go:1098— now also covers cherry-pick and revert. Rename toIsSequenceOperationInProgressor add a comment. - PatchID unstable under conflict-resolved rebases —
git patch-id --stablechanges if the user resolves conflicts during rebase. Add a one-line doc note onPatchIDinLinkageMetadata. - Missing cherry-pick test for
PrepareCommitMsgwith an active session (revert is covered thoroughly, cherry-pick is symmetric). - Duplicate helpers in
cmd/entire/cli/gitops/diff_test.go(revParse,gitCheckout,gitRebase) shadow the newtestutilhelpers added in this branch — maybe replace them? - Document "same ID from two sessions = ambiguous" in
singleReusableCheckpointID— the behavior is correct, just non-obvious.

Summary
Store a narrow, Git-native linkage block in checkpoint metadata so the web has limited fallback signals when history rewrites strip the
Entire-Checkpointtrailer.linkagemetadata withtree_hashandpatch_idLastCheckpointIDScope
This PR is intentionally narrow.
It does not implement full automatic relinking across all rewrite scenarios, and it does not add custom derived signals like
files_changed_hashorsession_files_hash.The supported fallback signals are:
tree_hashfor message-only rewrites where the tree is unchangedpatch_idfor clean rebases or cherry-picks where the diff content is unchangedWhy this shape
The broader multi-signal design implied server-side indexing and reconciliation work that this PR does not provide.
This branch keeps only the cheap, Git-native fallback signals that are defensible today, while the real active-session rewrite tracking work moves to the CLI (
post-rewrite) in a separate follow-up.Sequence-operation behavior
prepare-commit-msgcan still add a trailer during agent-driven revert/cherry-pick when a session is ACTIVE, but it now only reuses an existingLastCheckpointID.That matters because
post-commitstill skips condensation while the sequence operation is in progress. Reusing an existing checkpoint ID preserves a valid link; minting a fresh one would leave a danglingEntire-Checkpointtrailer on the commit.Testing
Passed:
GOCACHE=/tmp/go-build go test ./cmd/entire/cli/strategy -run 'TestShadowStrategy_(PrepareCommitMsg_(AgentRevertReusesLastCheckpointID|AgentRevertWithoutCheckpointIDSkipped|UserRevertSkipped)|PostCommit_LinkagePopulated)' -count=1GOCACHE=/tmp/go-build go test ./cmd/entire/cli/checkpoint ./cmd/entire/cli/gitops -count=1GOCACHE=/tmp/go-build GOLANGCI_LINT_CACHE=/tmp/golangci-lint golangci-lint run ./cmd/entire/cli/strategy ./cmd/entire/cli/checkpoint ./cmd/entire/cli/gitopsIncluded coverage verifies:
linkageis written at the checkpoint summary leveltree_hashandpatch_idare populated in the condensation pathpatch_id