Skip to content

fix: detect reset-driven session divergence in status#948

Draft
peyton-alt wants to merge 1 commit intomainfrom
peyton/reset-linkage-support
Draft

fix: detect reset-driven session divergence in status#948
peyton-alt wants to merge 1 commit intomainfrom
peyton/reset-linkage-support

Conversation

@peyton-alt
Copy link
Copy Markdown
Contributor

@peyton-alt peyton-alt commented Apr 14, 2026

Summary

Adds lightweight support for git reset in the CLI status path.

When an active session's tracked BaseCommit no longer matches the current HEAD, entire status now:

  • silently refreshes local linkage when HEAD lands on a commit with the same Entire-Checkpoint trailer as the session's LastCheckpointID
  • otherwise shows a clear divergence warning instead of guessing and mutating session state

This keeps reset support intentionally narrow: safe auto-reconciliation for the obvious same-checkpoint case, warning-only behavior for ambiguous history movement.

Fix

  • resolve current HEAD and parse its Entire-Checkpoint trailer in status
  • compare that against active same-worktree sessions before rendering session cards
  • update BaseCommit and AttributionBaseCommit only when the session's LastCheckpointID matches the checkpoint trailer on HEAD
  • leave session state unchanged when HEAD moved without a matching checkpoint trailer, and surface a warning in status
  • normalize worktree paths before comparing them so temp repos and symlinked paths reconcile correctly

Tests

Passed:

  • GOCACHE=/tmp/go-build go test ./cmd/entire/cli -run 'Test(ResolveWorktreeBranch_|RunStatus_|WriteActiveSessions_)' -count=1
  • GOCACHE=/tmp/go-build go test ./cmd/entire/cli ./cmd/entire/cli/session ./cmd/entire/cli/trailers -count=1
  • GOCACHE=/tmp/go-build go test -tags=integration ./cmd/entire/cli/integration_test -run 'TestShadow_(AmendPreservesTrailer|PostRewriteAmendRemapsSessionState|PostRewriteRebaseRemapsSessionState)' -count=1
  • GOCACHE=/tmp/go-build GOLANGCI_LINT_CACHE=/tmp/golangci-lint golangci-lint run ./cmd/entire/cli

Manual user-simulated reset repro:

  • safe reset back to a commit with the same Entire-Checkpoint trailer updated persisted session BaseCommit to the reset target and showed no warning
  • reset to a commit without a matching checkpoint trailer left session state unchanged and showed tracking diverged from current HEAD after git history movement

New Behavior

Before this change, git reset left active session linkage stale until the user repaired it elsewhere.

After this change:

  • git reset back to a known checkpoint-linked commit is recovered automatically on the next entire status
  • git reset to an ambiguous or unrelated commit is surfaced clearly in entire status without rewriting linkage state

Note

Medium Risk
Touches session-linkage persistence during entire status rendering by conditionally mutating BaseCommit/AttributionBaseCommit based on HEAD trailers; incorrect matching or path normalization issues could cause misleading status or unintended state updates.

Overview
entire status now detects when an active session’s stored BaseCommit no longer matches the repo’s current HEAD (e.g., after git reset). If HEAD’s commit message contains an Entire-Checkpoint trailer matching the session’s LastCheckpointID, status silently re-links the session by updating and persisting BaseCommit/AttributionBaseCommit to the current HEAD; otherwise it leaves state unchanged and prints a per-session divergence warning.

This adds HEAD inspection via go-git, parses checkpoint trailers, and normalizes worktree paths (including symlink resolution) before comparing sessions to the current repo. New tests cover both the safe auto-reconciliation path and the warning-only divergent reset path.

Reviewed by Cursor Bugbot for commit f692674. Configure here.

Copilot AI review requested due to automatic review settings April 14, 2026 04:11
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds narrow, status-only handling for git reset-driven session divergence by comparing active session linkage against the current HEAD’s Entire-Checkpoint trailer, auto-reconciling only when the checkpoint match is unambiguous and otherwise emitting a warning.

Changes:

  • Resolve HEAD and parse Entire-Checkpoint trailer(s) during entire status rendering.
  • Reconcile active session BaseCommit/AttributionBaseCommit only when HEAD checkpoint matches LastCheckpointID; otherwise warn without mutating session state.
  • Add unit tests covering safe reset reconciliation and divergence warning behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
cmd/entire/cli/status.go Adds HEAD linkage detection, worktree-path normalization, and session divergence reconciliation + warning rendering in status.
cmd/entire/cli/status_test.go Adds tests exercising reset reconciliation vs divergence warning behavior.

Comment on lines +468 to +471
head := headLinkage{commitHash: headRef.Hash().String()}
if checkpointIDs := trailers.ParseAllCheckpoints(commit.Message); len(checkpointIDs) > 0 {
head.checkpointID = checkpointIDs[0].String()
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currentHeadLinkage only records the first Entire-Checkpoint trailer (checkpointIDs[0]). trailers.ParseAllCheckpoints explicitly supports multiple checkpoint trailers (e.g., squash merges) while preserving order, so HEAD may legitimately link to several checkpoints. With the current implementation, a session whose LastCheckpointID matches a later trailer will incorrectly fail to reconcile and will show a divergence warning. Consider storing all parsed checkpoint IDs (e.g., as a []string or a map[string]struct{} in headLinkage) and treating HEAD as matching if LastCheckpointID equals any of them; update the warning text accordingly (e.g., mention the matched/available checkpoint IDs).

Copilot uses AI. Check for mistakes.
Comment on lines +468 to +471
head := headLinkage{commitHash: headRef.Hash().String()}
if checkpointIDs := trailers.ParseAllCheckpoints(commit.Message); len(checkpointIDs) > 0 {
head.checkpointID = checkpointIDs[0].String()
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new reset-reconciliation logic is covered for the single-trailer case, but there's no test covering HEAD commits that contain multiple Entire-Checkpoint trailers (which ParseAllCheckpoints supports for squash merges). Adding a status test where HEAD has multiple checkpoint trailers would prevent regressions (especially if reconciliation should succeed when LastCheckpointID matches any trailer).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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

Reviewed by Cursor Bugbot for commit f692674. Configure here.

head := headLinkage{commitHash: headRef.Hash().String()}
if checkpointIDs := trailers.ParseAllCheckpoints(commit.Message); len(checkpointIDs) > 0 {
head.checkpointID = checkpointIDs[0].String()
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only first checkpoint ID checked, missing valid matches

Low Severity

currentHeadLinkage calls trailers.ParseAllCheckpoints but only stores the first checkpoint ID in headLinkage.checkpointID. In reconcileActiveSessionHeadDivergence, each session's LastCheckpointID is compared against only this first ID. If HEAD has multiple Entire-Checkpoint trailers (e.g. from a squash merge used as a reset target), sessions whose LastCheckpointID matches a non-first trailer will receive a false divergence warning instead of being auto-reconciled.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f692674. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants