Skip to content

fix: don't point the image watcher at a remote session's directory - #727

Merged
Ark0N merged 1 commit into
Ark0N:masterfrom
timkjr:fix/image-watcher-remote
Oct 28, 2026
Merged

Ark0N merged 1 commit into
Ark0N:masterfrom
timkjr:fix/image-watcher-remote

Conversation

@timkjr

@timkjr timkjr commented Oct 27, 2026

Copy link
Copy Markdown

What

Follow-up to #722, same root cause. imageWatcher.watchSession(session.id, session.workingDir) hands a remote-SSH session's workingDir to chokidar, which watches it on the local disk. Usually that path doesn't exist locally and the watch sits on a missing folder. Where a folder with the same path does exist locally (an sshfs mount of the remote tree, or the same home layout on both hosts), local images pop up as the remote session's.

Three places start a watch:

  • setupSessionListeners() (server.ts): create, respawn, restore and recovery all go through it;
  • POST /api/sessions/:id/image-watcher (session-routes.ts), the per-session switch;
  • PUT /api/settings (system-routes.ts): turning the app-wide watcher on re-watches every session that has it enabled.

Fix

  • setupSessionListeners() and the app-wide toggle skip a remote session (&& !session.remote).
  • The per-session route refuses to turn it on for a remote session with INVALID_INPUT, as POST .../git-fetch-settings does, and persists nothing. Turning it off still works.
  • The Session Options row is hidden for a remote session, as the Git tab is. _apiPost() doesn't throw on {success:false}, so the switch would otherwise report "Image watcher enabled".

Docker-case sessions are unchanged, since their workspace is local.

Tests

test/image-watcher-remote.test.ts spies on the real imageWatcher singleton, so all three modules see the spy:

  • setupSessionListeners(): a remote session is never watched, and a local one still is;
  • the per-session route: enabling on a remote session is refused, with nothing watched or persisted; disabling still unwatches and persists; a local session is still watched;
  • PUT /api/settings: turning the watcher on re-watches the local session only, not the remote one.

On master the three remote cases fail and the three local ones pass.

🤖 Generated with Claude Code

A remote-SSH session's workingDir is a path on the remote host, and the
image watcher watched it on the local disk: a folder that is not there,
or a same-named local folder whose images then popped up as the remote
session's. Same root cause as the Ralph @fix_plan.md watcher fix.

Three places start a watch, and all three now skip a remote session:
setupSessionListeners(), the app-wide toggle in PUT /api/settings (which
re-watches every enabled session), and POST /api/sessions/:id/image-watcher,
which refuses to turn it on (INVALID_INPUT, like the Git fetch settings
route) and still lets it be turned off. The Session Options row is hidden
for a remote session, the way the Git tab is, since the client's
_apiPost() does not throw on a refused request.
@Ark0N

Ark0N commented Oct 27, 2026

Copy link
Copy Markdown
Owner

Thank you @timkjr, this is a clean follow-up to #722: the image watcher no longer watches a remote session's working directory on the local disk at any of the three places that start a watch, and the Session Options row is hidden for remote sessions so the switch cannot report a success the server refused. The new test fails the three remote cases on master and passes here, and CI is green.

Nothing here needs a change from you. Two small things I would touch at merge time:

  • src/web/public/session-ui.js:3163: toggleSessionImageWatcher shows the success toast without checking the response (_apiPost does not throw on a 400). Your hidden row covers the remote case; a res.ok check would cover any other refusal.
  • docs/architecture-invariants.md (Remote sessions over SSH): one sentence saying the image watcher never watches a remote session's workingDir, so a future call site keeps the guard.

This is ready to merge as it stands.

@Ark0N
Ark0N merged commit 2c2f69a into Ark0N:master Oct 28, 2026
2 checks passed
Ark0N pushed a commit that referenced this pull request Oct 28, 2026
toggleSessionImageWatcher checks the response, so any refusal puts the
switch back and toasts an error instead of a success; architecture-
invariants says the image watcher never watches a remote workingDir.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ark0N pushed a commit that referenced this pull request Oct 28, 2026
@Ark0N

Ark0N commented Oct 28, 2026

Copy link
Copy Markdown
Owner

Merged in 1.47.0 (https://github.com/Ark0N/Codeman/releases/tag/codeman%401.47.0). Thank you for following #722 through to all three call sites. At merge I added the res.ok check to toggleSessionImageWatcher, so any refusal puts the switch back and toasts an error instead of a success, and the sentence in the remote-sessions part of docs/architecture-invariants.md (dd56674).

@timkjr
timkjr deleted the fix/image-watcher-remote branch October 28, 2026 15:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants