From 028bd874944e8db955b586bfb5cc1d7aaca5e528 Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Sun, 8 Nov 2026 04:08:43 +0100 Subject: [PATCH] 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 --- src/live-worktree.ts | 9 ++++++--- src/web/server.ts | 10 ++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/live-worktree.ts b/src/live-worktree.ts index 86b85a1c..6f3738be 100644 --- a/src/live-worktree.ts +++ b/src/live-worktree.ts @@ -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". diff --git a/src/web/server.ts b/src/web/server.ts index a50b314f..1dbcbb00 100644 --- a/src/web/server.ts +++ b/src/web/server.ts @@ -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 => {