Skip to content

fix(session): reconcile worktree path on resume after repo relocation - #1896

Open
gtrrz-victor wants to merge 4 commits into
mainfrom
fix/resume-worktree-path-reconciliation
Open

fix(session): reconcile worktree path on resume after repo relocation#1896
gtrrz-victor wants to merge 4 commits into
mainfrom
fix/resume-worktree-path-reconciliation

Conversation

@gtrrz-victor

@gtrrz-victor gtrrz-victor commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

https://entire.io/gh/entireio/cli/trails/969

Fixes #1890.

Problem

A session records SessionState.WorktreePath once at start, and the commit-time
matcher (findSessionsForWorktree) uses exact string equality. When the repo
directory is renamed/moved while the session is stopped and the agent resumes it
(Claude Code's own /resume) from the new location, the recorded path no longer
resolves — every commit silently loses its Entire-Checkpoint trailer. #1440's
sibling-worktree fallback doesn't cover a whole-repo move: the old path is gone,
so nothing matches.

Fix

Reconcile on the resumed turn, scoped to main-worktree relocations. When a
turn starts and the recorded path no longer resolves to this repo's git common
dir, repoint WorktreePath at the current worktree — but only when the session
and the current worktree are both the main worktree (WorktreeID == "" on
both sides). The session store lives in the common dir, so a loaded state is the
same repo by construction; a recorded path that still resolves to this repo (a
live sibling worktree) is left untouched.

Why main-worktree only

WorktreeID keys the session's shadow branch (entire/<base>-hash(WorktreeID)).
Repointing a linked-worktree session onto the main worktree while keeping its
WorktreeID would leave WorktreeID describing a different worktree than
WorktreePath. An audit of every WorktreeID/WorktreePath consumer showed that
disalignment is reachable by code that re-derives the worktree id from the current
directory instead of reading state.WorktreeID:

  • entire clean (and its --dry-run preview) compute the wrong shadow-branch
    name → the session's real branch is orphaned.
  • entire explain / rewind-point listing filter shadow branches by the current
    worktree hash → the session's checkpoints are hidden.
  • post-commit base/attribution updates follow the wrong worktree's HEAD → skewed
    agent-vs-human attribution.

The reported case (#1890) is a whole main-checkout relocation, where WorktreeID
is "" throughout — so restricting to that fixes the bug without ever creating
the disaligned state
, and WorktreePath/WorktreeID stay aligned by
construction. Linked-worktree relocation is a rare, documented non-goal; the
zero-match path still warns so the trailer loss is not silent.

Test plan

  • RED→GREEN integration test (resume_repo_rename_test.go): start session →
    rename repo folder → resumed turn → commit still gets its trailer. Confirmed
    failing before the fix, passing after.
  • Guard unit tests: main relocation reconciles; linked-worktree session left
    untouched
    ; resuming into a linked worktree left untouched; live sibling
    untouched; same-path no-op.
  • Full integration suite green; strategy pkg green; lint clean.

Scope

Reconciliation only. Follow-ups from the issue (WARN on zero-match, adopt
without a live --from, doctor flagging stale paths) are deliberately left to
separate PRs.

🤖 Generated with Claude Code

A session records SessionState.WorktreePath once at start, and the
commit-time matcher (findSessionsForWorktree) uses exact string equality.
When the repo directory is renamed/moved while the session is stopped and
the agent resumes it (Claude Code's own /resume) from the new location,
the recorded path no longer resolves and every commit silently loses its
Entire-Checkpoint trailer (#1890). #1440's sibling-worktree fallback does
not cover a whole-repo move: the old path is gone, so nothing matches.

Reconcile on the resumed turn: when a turn starts from a worktree that
differs from the recorded one AND the recorded path no longer resolves to
this repo's git common dir, repoint WorktreePath/WorktreeID at the current
worktree. The session store lives in the common dir, so a loaded state is
the same repo by construction. A recorded path that still resolves to this
repo (a live sibling worktree) is left untouched, so a session is never
stolen from a concurrent sibling.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 01KZ6BP5N2Y8NHC85KT283C7SW
Copilot AI lite review requested due to automatic review settings August 4, 2026 12:27
@gtrrz-victor
gtrrz-victor requested a review from a team as a code owner August 4, 2026 12:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Ready to approve

The reconciliation logic is narrowly scoped with a clear safety guard (common-dir validation) and is backed by both unit and integration coverage for the reported regression.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

Fixes a session-to-worktree mismatch that occurs when a repository is relocated (renamed/moved) while a session is stopped, then resumed from the new path. The change reconciles SessionState.WorktreePath (and WorktreeID) at turn start so commit-time session matching continues to find the session and append the Entire-Checkpoint trailer.

Changes:

  • Reconcile a session’s recorded worktree path on turn start when the stored path no longer resolves to the current repo’s git common dir (repo-relocation case).
  • Add unit coverage for relocation reconciliation, sibling-worktree non-stealing guard, and same-path no-op.
  • Add an integration test reproducing repo rename + resume + commit trailer linkage.
File summaries
File Description
cmd/entire/cli/strategy/manual_commit_session.go Adds worktree-path reconciliation logic gated by git-common-dir resolution to handle repo relocation without stealing sibling sessions.
cmd/entire/cli/strategy/manual_commit_hooks.go Invokes reconciliation during InitializeSession turn-start mutations for existing sessions.
cmd/entire/cli/strategy/manual_commit_reconcile_worktree_test.go Adds unit tests covering relocation repointing, sibling guard, and no-op behavior.
cmd/entire/cli/integration_test/resume_repo_rename_test.go Adds an integration RED→GREEN repro for #1890 verifying commits after rename/resume still get Entire-Checkpoint trailers.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Reconcile repointed WorktreePath *and* WorktreeID at the current worktree
on a resumed turn. The shadow branch is keyed on WorktreeID
(entire/<base>-hash(worktreeID)), so when a linked-worktree session was
resumed from the main worktree after a repo move, WorktreeID flipped
"<name>" -> "" and every prior checkpoint became unreachable to migrate,
GetRewindPoints, ListCheckpoints, and condensation — silently losing the
session's rewind history.

Repoint WorktreePath only; retain the original WorktreeID so the existing
shadow branch stays reachable. Reconcile fires only once the recorded path
is gone, so keeping the ID cannot collide with a live sibling worktree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 01KZ8SY50KYND90FY5ARZWPFTR
Restrict reconcileWorktreePathForResumedTurn to sessions where WorktreeID
is "" on both the recorded state and the current worktree (i.e. a main
checkout moved to another main checkout). This keeps WorktreePath and
WorktreeID aligned by construction — both stay "".

Previously the fix repointed WorktreePath while preserving a non-empty
WorktreeID, which produced a state where the two describe different
worktrees. That disalignment is reachable by several consumers that
re-derive the worktree id from the current directory rather than reading
state.WorktreeID: `entire clean` (and its --dry-run preview) compute the
wrong shadow-branch name and orphan the session's real branch; `entire
explain`/rewind-point listing filter shadow branches by the current
worktree hash and hide the session's checkpoints; and the post-commit
base/attribution updates follow the wrong worktree's HEAD. The reported
case (#1890) is a whole main-checkout relocation, where WorktreeID is ""
throughout, so this scoping fixes it without ever creating the disaligned
state. Linked-worktree relocation stays a documented non-goal; the
zero-match path still warns so trailer loss is not silent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gtrrz-victor

Copy link
Copy Markdown
Contributor Author

Scoped reconciliation to main-worktree relocations only (WorktreeID == "" on both the recorded session and the current worktree).

On the earlier review note about the WorktreeID/WorktreePath invariant: rather than document the divergence and chase the affected consumers, this scoping prevents the divergence entirely — a reconciled session keeps WorktreeID == "", so the two never disagree. That moots both follow-ups:

  1. No state.go doc change neededWorktreeID == GetWorktreeID(WorktreePath) still holds.
  2. No entire clean orphan — the shadow-branch name derived from the current worktree matches the session's.

A full audit of every WorktreeID/WorktreePath consumer had surfaced a wider blast radius from the disaligned state (also entire explain hiding the session's checkpoints, and post-commit base/attribution drift). Restricting to main-worktree relocations collapses all of it. Linked-worktree relocation stays a documented non-goal; the zero-match path still warns so trailer loss is not silent.

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

Labels

None yet

2 participants