From 3e5211e18b8def32a149f6be9577f95b24694206 Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Fri, 6 Nov 2026 08:23:43 +0100 Subject: [PATCH] fix: note the silent skip of codeman-named tmux sessions and two adopted details (release review) A session named codeman-* or claudeman-* on any socket is dropped with no note, which contradicted the docs. paneCwd is absent for an adopted session, and only a claude wrapper reports statusLineExporter. Co-Authored-By: Claude Opus 5.5 --- docs/api-reference.md | 5 +++-- docs/architecture-invariants.md | 2 +- docs/wiki/The-Dashboard.md | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/api-reference.md b/docs/api-reference.md index 8c8635e7..84c048e7 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -986,7 +986,7 @@ the directory the session's local tmux pane is in now (`#{pane_current_path}`), the web UI uses as the base for a relative path printed in the terminal. It is never restored after a restart (it is written to `state.json` with the rest of the session state but not read back), absent for remote, -Docker, direct-PTY and rediscovered sessions and for a session split into several panes, +Docker, adopted, direct-PTY and rediscovered sessions and for a session split into several panes, and never a confinement root: file routes stay confined to `workingDir`. tmux reports the resolved directory, so for a workspace reached through a symlink a directory at or under the resolved workspace is reported in `workingDir`'s spelling. @@ -1166,7 +1166,8 @@ gave nothing, each opening with where it came from (`local`, `docker `, pass to a shell, or whose name tmux reads as a target rather than a name (one starting with `=`, `@` or `%`, or a bare `~`), is skipped and counted in a note. A tmux server that does not answer within 2 seconds costs only its own rows and gets a note, and a scan cut -short keeps the rows it read and says so in one. Sessions on a Codeman socket never appear. +short keeps the rows it read and says so in one. Sessions on a Codeman socket never appear, +and neither does a session named `codeman-*` or `claudeman-*` on any other socket, with no note. `POST /api/v1/sessions/adopt` wraps one, with `{ "id": "...", "docker": true, "remote": true }` (the two flags must match the listing the id came from; `name` and diff --git a/docs/architecture-invariants.md b/docs/architecture-invariants.md index fff7a789..4673753b 100644 --- a/docs/architecture-invariants.md +++ b/docs/architecture-invariants.md @@ -109,7 +109,7 @@ Input arriving during a wake is buffered: a chunk over 4 KB is dropped whole, ne **One wrapper per target.** `foreignTargetKey()` is the ONE key, read by the adopt route and `adoptedBy`, built from the location, the host identity, the socket and the name: a socket and a name alone collide across containers and hosts. Both read live `Session`s, since the mux record only exists after the first attach. -**No workspace.** ⚠️ An adopted session keeps its connection on `adopt.remote`/`adopt.docker`, so `session.remote` and `session.docker` are undefined, and its `workingDir` is the neutral `/tmp`. Code that branches on those two reads an in-container or over-ssh adoption as a local session working in `/tmp`. So anything that treats `workingDir` as the session's workspace must also test `session.isAdopted` (`adopt` on a pure view): uploads, the upload GC, git status, git fetch and its scheduler, the image watcher, Read My Mind's git signals, the Discord folder, `last-response`, guarded input and Ralph's `@fix_plan.md` and `@ralph_prompt.md` routes all do. The file surfaces (Files panel, File Viewer and its edit mode) still treat `/tmp` as the workspace, which is harmless: it is world-writable anyway, and adoption is admin-only in multi-user mode. A wrapper reports `statusLineExporter: 'remote'`, which is also what keeps keep-warm off it. +**No workspace.** ⚠️ An adopted session keeps its connection on `adopt.remote`/`adopt.docker`, so `session.remote` and `session.docker` are undefined, and its `workingDir` is the neutral `/tmp`. Code that branches on those two reads an in-container or over-ssh adoption as a local session working in `/tmp`. So anything that treats `workingDir` as the session's workspace must also test `session.isAdopted` (`adopt` on a pure view): uploads, the upload GC, git status, git fetch and its scheduler, the image watcher, Read My Mind's git signals, the Discord folder, `last-response`, guarded input and Ralph's `@fix_plan.md` and `@ralph_prompt.md` routes all do. The file surfaces (Files panel, File Viewer and its edit mode) still treat `/tmp` as the workspace, which is harmless: it is world-writable anyway, and adoption is admin-only in multi-user mode. A claude wrapper reports `statusLineExporter: 'remote'`, which is also what keeps keep-warm off it. **Lifecycle.** `paneExitApplies` is false for a wrapper, so an attach that ended is never `paneExit` and the sweep never closes the tab. ⚠️ `remain-on-exit` still keeps Codeman's own tmux client on the dead pane, where a pid and `busy` hid it and both attach routes refused it, so `detachEndedAdoptedAttaches()` detaches that client on the reconnect watcher's tick: the tab closes nothing, its pid goes null, and selecting it (or "Go to tab" once a session of that name is back) runs the guard again. Boot recovery re-attaches a dead wrapper, which prints that the session is gone and exits. Reboot restore refuses one, since all Codeman ever had was an attach command. ⚠️ A wrapper that cannot start fails the start, and selecting the tab tries it again (`_failAdoptedStart()`). It never takes the direct-PTY fallback, which would run a new local `claude --dangerously-skip-permissions` or a server-account shell in `/tmp` under the foreign session's name, and `runPrompt()` refuses it for the same reason. diff --git a/docs/wiki/The-Dashboard.md b/docs/wiki/The-Dashboard.md index 0e44f86b..77f0164d 100644 --- a/docs/wiki/The-Dashboard.md +++ b/docs/wiki/The-Dashboard.md @@ -313,7 +313,8 @@ button, and **Rescan** runs it again. - **A short line in the block says why a session is missing.** One whose name holds unusual characters is skipped, and so is one named so that tmux reads the name as something else (it starts with `=`, `@` or `%`, or is a bare `~`). Rename it with `tmux rename-session`. A tmux - server that stopped answering costs only its own sessions. + server that stopped answering costs only its own sessions. The one silent case: a session + named `codeman-*` or `claudeman-*` counts as Codeman's own and is left out with no line. Only sockets in tmux's own folder are scanned, so `tmux -L ` is found and `tmux -S /custom/path` is not. In multi-user mode the block is for admins, since it shows