Skip to content

Add actionable version mismatch alerts and watcher management#47

Open
Joxx0r wants to merge 6 commits intomainfrom
feature/actionable-version-mismatch-alert
Open

Add actionable version mismatch alerts and watcher management#47
Joxx0r wants to merge 6 commits intomainfrom
feature/actionable-version-mismatch-alert

Conversation

@Joxx0r
Copy link
Collaborator

@Joxx0r Joxx0r commented Feb 23, 2026

Summary

  • Compare git commit timestamps (not just hashes) between service and watcher to show "Service is outdated" or "Watcher is outdated" with the correct action button (rebuild & restart service vs restart watcher)
  • Bake BUILD_GIT_TIMESTAMP into Docker images alongside the existing git hash, with fallback to .git-timestamp file
  • Add ability to stop individual watchers when multiple are connected (shows each watcher with ID, start time, file count)
  • Add "Restart Watcher" button in the dashboard Watcher Status card
  • Stop watchers before workspace deletion to prevent zombie watchers that keep reconnecting
  • Auto-start watcher after container start in the setup wizard
  • Show per-project scan progress (phase, discovered/processed files) in setup workspace cards

Files changed

  • Dockerfile — bake .git-timestamp alongside .git-hash
  • src/service/api.js — add SERVICE_GIT_TIMESTAMP, include in /health and /watcher-status responses
  • src/watcher/watcher-client.js — add watcherGitTimestamp and scanProgress tracking, include in heartbeat
  • src/setup-gui.js — pass BUILD_GIT_TIMESTAMP build-arg, forward watcherId in stop requests
  • public/index.html — timestamp comparison UI, overviewRebuildAndRestart(), stopSpecificWatcher(), restart button
  • public/setup.html — stop watcher on workspace delete, auto-start watcher after container start, watcher progress polling

Test plan

  • Rebuild Docker image + restart container → no mismatch alert
  • Make a new commit, restart watcher only → alert says "Service is outdated" with "Rebuild & Restart Service" button
  • Rebuild Docker + restart container (watcher still old) → alert says "Watcher is outdated" with "Restart Watcher" button
  • Start two watchers → dashboard shows both with individual Stop buttons
  • Delete a workspace → verify watcher stops before deletion (no zombie)
  • Run setup wizard → watcher auto-starts after container start

🤖 Generated with Claude Code

Joxx0r and others added 6 commits February 23, 2026 18:47
Compare git commit timestamps between service and watcher to show
"Service is outdated" or "Watcher is outdated" with the correct action
button (rebuild service vs restart watcher). Bake timestamps into Docker
images alongside the existing git hash.

Also adds:
- Stop individual watchers when multiple are connected
- Restart Watcher button in dashboard
- Stop watchers before workspace deletion to prevent zombies
- Auto-start watcher after container start in setup wizard
- Per-project scan progress display in setup workspace cards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…hutdown

The watcher stop mechanism relied entirely on heartbeat-based signaling
(up to 15s delay), which was fragile — especially when the service was
down (ECONNREFUSED). Now setup-gui tracks spawned watcher PIDs and kills
them directly via SIGTERM, with heartbeat fallback for watchers started
outside setup-gui.

Also fixes:
- stopAndRestartWatcher() finding the wrong status element when called
  from the watcher card vs version alert banner
- Service-unreachable errors during status polling after stop

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The /api/docker/stop and DELETE /api/workspaces/:name routes now kill the
watcher process (via tracked PID) before stopping/removing the container.
This prevents zombie watchers that keep reconnecting with ECONNREFUSED
loops after the service goes down.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The tracked PID map only works for watchers spawned by the current
setup-gui session. Watchers that survived a setup-gui restart are
orphans with no tracked PID. Now all stop paths (watcher/stop,
docker/stop, workspace delete) also scan the OS process list via wmic
for node processes matching "watcher-client.js --workspace <name>" and
kill them directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…er termination

process.kill(pid, 'SIGTERM') is unreliable for detached Node.js processes
on Windows. Replace with taskkill /F /PID which forcefully terminates the
process via the Windows API. All kill paths now go through a killPid()
helper that uses taskkill on Windows and SIGTERM on Linux/macOS.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Kills any existing setup-gui and watcher-client Node processes via wmic,
then opens the browser and starts a fresh setup-gui. Useful for picking
up code changes without manually finding and killing processes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good feature overall. The directional alerts and per-watcher stop buttons are solid UX improvements. A few issues to address before merging:

1. wmic deprecated on Windows 11 24H2+

start.bat uses wmic to terminate old processes, and killWatcherProcesses() in setup-gui.js uses it for the Windows orphan process scan. wmic was deprecated in Windows 10 and removed in Windows 11 24H2 (released Oct 2024).

The try/catch in killWatcherProcesses degrades gracefully (falls through to PID tracking + heartbeat), but start.bat is silent on failure — old processes simply are not killed. Replace with PowerShell equivalents.

For start.bat, replace the two wmic lines with:

powershell -NoProfile -Command "Get-CimInstance Win32_Process | Where-Object { $.CommandLine -like 'setup-gui.js' -or $.CommandLine -like 'watcher-client.js' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }"

For the Windows branch in killWatcherProcesses, replace the wmic CSV command with a PowerShell equivalent that outputs CommandLine + PID pairs.

2. No tests for new complex logic

The multi-strategy watcher kill (tracked PID -> OS scan -> heartbeat), stopSpecificWatcher() polling, and the timestamp comparison direction logic are all untested. A unit test covering the kill-strategy selection in setup-gui.js would catch regressions without requiring integration setup.

3. loadWorkspaces monkey-patch is fragile (setup.html)

const _origLoadWorkspaces = loadWorkspaces;
loadWorkspaces = async function() {
await _origLoadWorkspaces();
stopWatcherPolling();
startWatcherPolling();
};

This breaks silently if anything else re-assigns loadWorkspaces. Better to call stopWatcherPolling()/startWatcherPolling() at the explicit callsites of loadWorkspaces instead of wrapping it.

4. Dual-context escaping in onclick (minor / low risk)

onclick="stopSpecificWatcher('${esc(id)}', this)"

esc() applies HTML entity escaping but the value is also a JS string literal. A watcherId containing a single quote or backslash produces broken JS — esc() does not apply JS-string escaping. Since watcherId is server-generated this is unlikely to trigger in practice, but using a data attribute + addEventListener avoids the dual-escaping problem entirely.

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.

1 participant