Session Adopt Validation and PR Review

Pi·GPT-5.6-sol·Soph·1mo ago·1290hr 43min·1 Checkpoint·81 file changes·+20103/-1494·236.4K tokens
ed552a0session adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+340/-13

can you review this branch

1mo ago·6.4s

Findings

  • [P2] Cross-repository adoption rejects legitimate Gemini/OpenCode callers — cmd/entire/cli/session_adopt.go:164
    ResolveCallerSession only loads states from the target repository’s session store. During adoption from a different repository, the source state—and its recorded process owner—is therefore unavailable to ancestry resolution. Agents without caller-session environment variables (Gemini CLI and OpenCode) cannot be recognized as owning their own source session, so non-interactive adoption is refused unless they incorrectly use --allow-foreign-session. Include the selected source state in caller resolution, or directly compare its owner against current process ancestry before falling back to the foreign-session path.

Tests run:

Passed. No other findings.

5f5853esession adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+525/-13

can you check again

1mo ago·7.0s

Finding

  • [P2] Ambiguous callers can bypass the ownership guard through any ancestor — cmd/entire/cli/session_adopt.go:197
    The fallback runs for every non-IsCaller resolution, including ResolutionCallerAmbiguous. If the source belongs to an outer agent, HasAncestor succeeds even though an unresolved environment claim may identify a nearer inner agent. Adoption then proceeds silently despite the resolver explicitly declaring ownership ambiguous. The existing ambiguous test misses this because its source has no Owner. Refuse ambiguous resolutions, or only accept the source owner when it is provably nearest (for example, depth zero with the resolver’s claim-ordering rules).

The previous cross-repository Gemini/OpenCode issue is fixed.

Tests run:

Passed.

05f3282session adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+634/-13

can you check again

1mo ago·8.7s

Finding

  • [P2] A nested no-env agent can still adopt an outer agent’s session silently — cmd/entire/cli/session_adopt.go:212
    With no environment claims, HasAncestor treats any matching ancestor as uncontested. Consider OpenCode launching Gemini, with both sessions stored in the source repository while the command runs from a separate target repository. The target-only resolver sees neither state; explicitly selecting OpenCode’s outer session passes HasAncestor, although Gemini’s owner is nearer. Resolve against all relevant source and target states together—or otherwise require the selected source owner to be the nearest recorded owner—instead of checking only whether it appears anywhere in the ancestry.

The previous ambiguous-caller finding is fixed.

Tests passed:

99d7dd9session adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+839/-13
166750dsession adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+860/-13

can you review the branch for 2332 again, can you also read the description again and see if it can be improved

1mo ago·11.7s

Code review

  • [P2] Equal-depth sessions are all treated as the caller — cmd/entire/cli/session_adopt.go:272
    sourceOwnerIsNearest only rejects a strictly nearer owner. A long-lived agent process can own multiple session states at the same ancestry depth, such as an older stale session and a newer resumed session. Selecting the older session therefore passes silently. The existing resolver handles this case by choosing the most recently interacting session (isNearerOwner); this fallback should use equivalent tie-breaking rather than treating every equal-depth state as the caller.

The previous nearer-owner finding is otherwise fixed.

Tests passed:

PR description feedback

The description is now stale:

  • “Five new” tests should be ten new tests.
  • The Cursor summary still references commit ed552a050 and the original five-test implementation; remove or regenerate it.
  • Add the important cross-repository behavior:
    • Gemini/OpenCode have no session-ID environment variable.
    • Source-store owners are therefore ranked by ancestry.
    • Nested source sessions require nearest-owner selection.
    • Ambiguous resolutions never fall back to ancestry.
  • In the behavior table, replace “refused; terminal gets a confirmation” with “requires terminal confirmation; refused without a terminal”, since confirmation can permit the operation.
  • Mention that non-interactive refusal now prints the --allow-foreign-session remedy and prompt cancellation uses the shared cancellation handler.

The “Why” and separate-flag rationale are strong and should remain.

95d3725session adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+1004/-13
6a5c19csession adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+1151/-154
8512495session adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+1192/-154
d4a8876session adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+1374/-154

can you review again

1mo ago·9.0s

Code review

  • [P2] Ownership is authorized from an unlocked snapshot — cmd/entire/cli/session_adopt.go:110
    The ownership check runs before WithSessionStateLocks / MutateSessionState. Both mutation paths subsequently reload the source state under lock but do not repeat ownership validation. A turn-start or resume can update the source owner—or introduce a nearer source session—between authorization and mutation, allowing adoption based on stale ownership evidence. Revalidate the locked source state and ownership ranking immediately before buildAdoptedSessionState; if interactive confirmation happened, ensure the locked state still matches what was confirmed.

The equal-depth recency issue is fixed.

Tests passed:

PR description

It remains substantially stale:

  • “Five new” should now account for 13 unit tests and 3 integration tests.
  • It omits source-store ancestry ranking, nested no-env agents, equal-depth recency, flag-parser coverage, and real-binary non-interactive coverage.
  • The behavior rows should say “requires confirmation in a terminal; refused without one”, not simply “refused.”
  • The Cursor summary still describes commit ed552a050 and the original five-test implementation; remove or regenerate it.
  • Mention that detailed rationale moved to docs/architecture/session-caller-resolution.md.

The “Why,” existing-check explanation, and separate-flag rationale remain strong.

can you update the description of the PR and also the trail body

1mo ago·6.2s

Updated both:

Removed the stale Cursor summary and documented current behavior, ancestry ranking, tests, integration coverage, and validation.

mise run lint passed before the remote updates.

Note: uncommitted changes appeared in cmd/entire/cli/session_adopt.go during the update. I left them untouched.

10cfacfsession adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+1575/-158
15294c4session adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+1591/-158
a0ca110session adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+1657/-158
73e727esession adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+1825/-158
3343a32session adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+1936/-158
98b4346session adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+1942/-158
863fca4session adopt: refuse a session that is not this command's caller `session adopt` moves the named session into the current worktree and resets its checkpoint bookkeeping — `StepCount`, `CheckpointTranscriptStart`, `LastCheckpointID`, `FilesTouched`. Naming the wrong session therefore mutates a third party's *running* session and silently detaches their next commit from it. That is the damage in the report this series came from: an agent read a session ID out of `entire session current`, which used to answer with a foreign worktree's live session, and adopted it. `strategy.ResolveCallerSession` (#2322) made the caller knowable and `SessionResolution.IsCaller()` was added as the gate for commands that act on a session rather than display one. This wires the one command that acts. None of the pre-existing checks could have caught it, which is worth stating because they look like they should. `sessionBelongsToSourceWorktree` asks only whether the ID and the worktree agree with *each other* — and any pair read out of `session current` satisfies that by construction, since they were reported together. `isAdoptableSourceSession` asks only whether the session is still live, and being live is precisely what makes hijacking it harmful. Neither asks "is this mine". Three outcomes. A session the resolver confirms is the caller's own is adopted silently, because that is what the command is for: an agent whose work moved to another worktree adopting its own session. A session positively identified as someone else's, or one whose ownership cannot be confirmed at all, is refused — a terminal gets a confirmation naming both sessions, and without one the command fails. Auto-selection goes through the same gate, which matters because `adopt --from <path>` needs no session ID at all and was reachable without `session current` ever being run. No terminal means refuse rather than proceed. The incident was an agent running this non-interactively, so a prompt nobody can see must not count as consent. Unconfirmable also covers every cross-machine `--from`, where process ancestry cannot speak to another host. `--allow-foreign-session` is the escape hatch and is deliberately not folded into `--force`/`--yes`. Those already carry two meanings (replace local state; confirm same-store adoption), and `--force` is the flag an agent reaches for on any refusal — so waiving a safety check on someone else's live session gets a name that says that, pinned by a test. The existing adoption tests now declare ownership through `adoptAsCaller` rather than the override, so they exercise the supported path; `go test` inherits the developer's real agent variable, which names a session that is emphatically not the fixture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2389BRDDWK3DT2913TCPBNR+1658/-6