fix(files): no outside worktree for an adopted session, and say what the routes re-check (#780)

outsideWorktreeOptions() now returns undefined for an adopted session as it does for a remote or Docker one: session.remote and session.docker are undefined on an adopted session, so it needs its own test. An adopted pane gets no paneCwd today (paneExitApplies), so nothing changes in practice; the rule stays true if that ever changes.

The live-worktree.ts @fileoverview said the routes re-check only the realpath of an outside worktree. They also judge the attachment guard rules again on every request (isOutsideWorktreeRootRefused() through withGuardedOutsideWorktree()), which is what makes a confinement or blocked-tree change apply at once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-11-08 04:08:43 +01:00
1 parent 2e61ac9534
commit 028bd87494
2 files changed
+12 -7

No files matched your search

+6 -3
View File
@@ -31,9 +31,12 @@
* `workingDir`.
*
* For an outside worktree {@link findOutsideWorktree} IS the confinement decision for the second
* root. Its vouching, home, Docker and `allowRoot` rules run only when the pane moves. The routes
* re-check on each request only that the folder still realpaths to the recorded path
* (`outsideWorktreeRootFor()`, `canWalkOutsideWorktree()`).
* root. Its vouching, home and Docker rules, and the multi-user half of `allowRoot`, run only when
* the pane moves. The routes re-check two things on each request: that the folder still realpaths
* to the recorded path (`outsideWorktreeRootFor()`, `canWalkOutsideWorktree()`), and the attachment
* guard half of `allowRoot` (workspace confinement, blocked and sensitive trees:
* `isOutsideWorktreeRootRefused()` through `withGuardedOutsideWorktree()` in file-routes.ts), so a
* guard setting changed later applies at once.
*
* Probes are bounded (#516). The folder can sit on a mount that stopped answering, and an
* `unknown` answer stops the walk rather than being read as "no `.git` here".
+6 -4
View File
@@ -3054,12 +3054,14 @@ export class WebServer extends EventEmitter {
*
* Codeman judges these rules when the pane moves. The file routes judge the two guard rules
* again on every request (`isOutsideWorktreeRootRefused()`), so a setting changed later applies
* at once. The Docker and multi-user rules are judged only when the pane moves. Undefined for a remote or Docker session,
* whose pane directory is not a path the agent works in on this host (it has no `paneCwd`
* either, so this is belt and braces).
* at once. The Docker and multi-user rules are judged only when the pane moves. Undefined for a remote, Docker or
* adopted session, whose pane directory is not a path the agent works in on this host (an
* adopted session's pane runs our tmux attach client). None of the three gets a `paneCwd`
* either (`paneExitApplies`), so this is belt and braces; an adopted session needs its own
* test because `session.remote` and `session.docker` are undefined on one.
*/
private outsideWorktreeOptions(session: Session): OutsideWorktreeOptions | undefined {
if (session.remote || session.docker) return undefined;
if (session.remote || session.docker || session.isAdopted) return undefined;
return {
dockerWorkspaces: defaultDockerWorkspaces,
allowRoot: async (root: string): Promise<boolean> => {