Skip to content

Fix watch status on Windows (#72)#73

Merged
JordanCoin merged 3 commits into
mainfrom
fix/windows-watch-status
Jul 2, 2026
Merged

Fix watch status on Windows (#72)#73
JordanCoin merged 3 commits into
mainfrom
fix/windows-watch-status

Conversation

@JordanCoin

Copy link
Copy Markdown
Owner

Fixes #72.

Problem

On Windows, codemap watch start leaves a live daemon with a matching .codemap/watch.pid, but codemap watch status immediately reports Watch daemon not running.

Root cause

IsRunning probed liveness with os.Process.Signal(syscall.Signal(0)). On Windows, os.Process.Signal returns "not supported by windows" for any signal except Kill, so signal 0 always errored → IsRunning always returned false. It's a Unix idiom that never worked on Windows. Stop (SIGTERM) had the same latent problem.

Fix

  • Move liveness behind a platform-specific processAlive(pid):
    • Unix keeps the Signal(0) probe.
    • Windows opens the process and checks GetExitCodeProcess for STILL_ACTIVE (stdlib syscall only, no new dependency).
  • Stop falls back to Process.Kill() when SIGTERM is unsupported, so watch stop also works on Windows.
  • Adds a host-runnable processAlive test (live / exited / invalid PIDs).

Verification

  • GOOS=windows go build and go vet clean — the actual target platform.
  • Host build green; watch tests pass, including the existing status test and the new liveness test.

I couldn't run this on real Windows from my environment — @ScottRosenberg2, if you're able to try a build from this branch (Win11/Scoop), confirmation would be appreciated.

🤖 Generated with Claude Code

IsRunning probed process liveness with os.Process.Signal(0), which is
unsupported on Windows: Signal returns an error for any signal other than
Kill, so IsRunning always reported false and `watch status` said
"Watch daemon not running" even with a live PID in .codemap/watch.pid.

Move liveness detection behind a platform-specific processAlive:
- Unix keeps the Signal(0) probe.
- Windows opens the process and checks GetExitCodeProcess for STILL_ACTIVE.

Also make Stop fall back to Process.Kill when SIGTERM is unsupported
(Windows), so `watch stop` actually terminates the daemon there.

Adds a host-runnable processAlive test covering live, dead, and invalid
PIDs.

Fixes #72

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 2, 2026 01:19
@codecov-commenter

Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment

Thanks for integrating Codecov - We've got you covered ☂️

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes codemap watch status on Windows by replacing the Unix-only liveness probe (Signal(0)) with a platform-specific processAlive(pid) implementation, and improves watch stop behavior on Windows where SIGTERM is unsupported.

Changes:

  • Replace IsRunning’s liveness check with a new OS-specific processAlive(pid) helper (Unix: Signal(0), Windows: OpenProcess + GetExitCodeProcess).
  • Add a new unit test covering processAlive for live, exited, and invalid PIDs.
  • Update Stop to fall back to a hard kill when SIGTERM is unsupported (intended for Windows).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
watch/state.go Switches IsRunning to platform-specific liveness and adjusts Stop behavior for Windows.
watch/state_test.go Adds a new processAlive test to validate live/dead PID detection.
watch/process_windows.go Implements Windows liveness detection using Win32 process APIs via syscall.
watch/process_unix.go Implements non-Windows liveness detection via Signal(0).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread watch/state_test.go
Comment on lines +156 to +162
// Spawn a short-lived process, wait for it to exit, then confirm it is dead.
cmd := exec.Command("go", "version")
if err := cmd.Start(); err != nil {
t.Skipf("cannot spawn helper process: %v", err)
}
pid := cmd.Process.Pid
_ = cmd.Wait()
Comment thread watch/state.go Outdated
Comment on lines +111 to +115
if err := proc.Signal(syscall.SIGTERM); err != nil {
return err
// Windows does not support SIGTERM; fall back to a hard kill so
// `watch stop` actually terminates the daemon there.
if killErr := proc.Kill(); killErr != nil {
return killErr
- Stop: use a platform-specific terminateProcess (SIGTERM on Unix,
  Kill on Windows) instead of a blanket Kill-on-any-SIGTERM-error
  fallback, so Unix no longer masks ESRCH/EPERM.
- Test: re-exec the test binary with `-test.run=^$` for the exited-PID
  case instead of depending on `go` being on PATH.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JordanCoin

Copy link
Copy Markdown
Owner Author

Thanks @copilot — both addressed in the latest commit:

  1. Stop now delegates to a platform-specific terminateProcess (SIGTERM on Unix, Kill on Windows), so the Unix path returns its real error (ESRCH/EPERM) instead of masking it with a Kill fallback.
  2. The exited-PID test now re-execs the test binary with -test.run=^$ instead of relying on go being on PATH — fully self-contained.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: abc5b9de17

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread watch/process_windows.go Outdated

// terminateProcess stops the daemon. Windows has no SIGTERM, so Kill (which
// maps to TerminateProcess) is the correct mechanism.
func terminateProcess(proc *os.Process) error {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Verify ownership before killing Windows PIDs

When .codemap/watch.pid is stale and its PID has been reused on Windows, the new liveness check lets runWatchSubcommand("stop") proceed for any active process, and this Kill terminates it even though processCommandLine still cannot verify that the PID belongs to a codemap daemon for this root. This is a new Windows failure mode from making IsRunning succeed there, so please verify daemon ownership before killing or refuse to stop when ownership cannot be established.

Useful? React with 👍 / 👎.

Revert the Windows Kill fallback in Stop. It let `watch stop` terminate
any live process matching a stale .codemap/watch.pid — on Windows the PID
could have been reused by an unrelated process, and processCommandLine
cannot yet verify daemon ownership there (P2 flagged by Codex).

Stop returns to its prior behavior (SIGTERM, error on Windows), so this
PR is scoped to the reported issue: fixing `watch status` via the new
platform-specific processAlive. Safe Windows `stop` (with PID-ownership
verification) is left as tracked follow-up.

This also resolves the earlier Copilot note that the blanket Kill
fallback masked ESRCH/EPERM on Unix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JordanCoin

Copy link
Copy Markdown
Owner Author

Thanks @chatgpt-codex-connector — good catch on the PID-reuse risk. I've reverted the Windows Kill fallback in Stop. It could have terminated an unrelated process that reused a stale watch.pid, and ownership can't be verified on Windows yet (processCommandLine is a stub there).

This PR is now scoped to the reported issue — fixing watch status via the platform-specific processAlive. Windows stop returns to its prior (non-destructive) behavior; making it work safely needs a Windows command-line/ownership lookup, which I'll track as a follow-up. This also resolves the earlier Copilot note about the blanket Kill masking ESRCH/EPERM on Unix.

@chatgpt-codex-connector

Copy link
Copy Markdown

Summary

  • No code changes were needed. The current branch already reflects the requested scope: Stop now only sends SIGTERM and returns the original error, leaving Windows watch stop non-destructive until ownership verification can be implemented safely. watch/state.goL101-L118
  • watch status remains fixed through the platform-specific processAlive path used by IsRunning. watch/state.goL90-L98
  • Windows liveness uses OpenProcess + GetExitCodeProcess without adding a Windows kill fallback. watch/process_windows.goL15-L35
  • Unix liveness keeps the existing signal-0 probe. watch/process_unix.goL21-L33
  • The liveness test is self-contained by re-executing the current test binary instead of depending on go being on PATH. watch/state_test.goL156-L167

Testing

  • go test ./...
  • ⚠️ GOOS=windows go test ./watch — compiled the Windows test binary but could not execute it on this Linux host: exec format error.
  • GOOS=windows go test -c ./watch -o /tmp/watch.test.exe

No commit or new PR was created because there were no code changes to commit.

View task →

@JordanCoin JordanCoin merged commit fa45aa7 into main Jul 2, 2026
23 of 24 checks passed
@JordanCoin JordanCoin deleted the fix/windows-watch-status branch July 2, 2026 02:14

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 45968bc1ac

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread watch/process_windows.go
if err := syscall.GetExitCodeProcess(h, &code); err != nil {
return false
}
return code == stillActive

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require daemon ownership before accepting Windows PIDs

When .codemap/watch.pid is stale and Windows has reused that PID for an unrelated live process, this returns true even though processCommandLine is still unsupported on Windows and no repo/daemon ownership is verified. runWatchSubcommand("start") checks watchIsRunning(absRoot) before spawning, so that stale file makes codemap watch start refuse to start a real daemon, while status can report running/stale state until the user deletes the PID file. Fresh evidence: Stop is now non-destructive, but start/status still rely only on this liveness result.

Useful? React with 👍 / 👎.

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.

Codemap watch status doesn't work on windows

3 participants