Skip to content

planning: confirm round.sh stop leaves no watch.sh running on Windows Git Bash #5723

Closed

Description

@kyle-sexton

Refs #5569

Problem

Item 15 of #5569 (batch A comment) has the acceptance criterion: "After round.sh stop, no watch.sh for that data dir is still running within 10 s (Windows Git Bash included)." The original report was a watch.sh and its curl long-poll left running after round.sh stop on Windows Git Bash, killed by PID.

PR #5722 ends the watcher on POSIX only. end_watcher in plugins/planning/surface/round.py returns early when os.name != "posix", because the pid a Git Bash watch.sh records ($$) is not a native Windows PID and could name any process. On Windows the watcher is meant to end through its refused-poll path: round.sh stop removes the env file, the next poll is refused, and watch.sh exits 3 with "the interview server was stopped (round.sh stop): not re-arming". plugins/planning/surface/README.md says that path has not been exercised on Windows Git Bash.

This needs a Windows host with Git for Windows and an operator run. A Linux or WSL session cannot produce the evidence.

Acceptance criteria

  • On Windows Git Bash, after round.sh stop, no watch.sh for that data dir and no curl long-poll it started is still running within 10 s, and the watch.sh process exited 3 with the "the interview server was stopped" message (the refused-poll path).
  • bash plugins/planning/surface/watch.test.sh run on that host passes, including "round.sh stop ended the data dir's watch.sh" and "watch.sh exits 3 at once on a refused poll after a clean stop".
  • The result (host, Git for Windows version, commands, output) is posted on this issue. If it passes, a PR that closes this issue replaces the README sentence "That path has not been exercised on Windows Git Bash." with the verified behavior. If it fails, the failing output is the repro for a fix: a stop the lease can trust on Windows, or a watch.sh that ends sooner, with the POSIX cases in watch.test.sh still green.

Operator steps

In Git Bash, from plugins/planning/surface, with d set to a scratch data dir:

bash round.sh --dir "$d" ensure-running --port 0
bash watch.sh "$d" >watch.out 2>watch.err &
wpid=$!
ps -ef | grep -E 'watch\.sh|curl'
bash round.sh --dir "$d" stop
sleep 10
ps -ef | grep -E 'watch\.sh|curl'
wait "$wpid"; echo "exit $?"; cat watch.err

Related

Parent: #5569 (item 15, third acceptance criterion). POSIX path: PR #5722.

Activity

  1. added
    priority: lowNice-to-have, cosmetic, or speculative; opportunistic.
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    on Oct 1, 2026
  2. kyle-sexton commented on Oct 1, 2026

    @kyle-sexton
    MemberAuthor

    Windows Git Bash run, 2026-10-01. The first criterion holds. The second does not: one of the two watch.test.sh cases it names fails on Windows, and the cause is the test, not watch.sh. This issue stays open.

    Where and what

    • Host: melo-desk-001, native Windows 11 (build 26200). Git for Windows 2.55.0 (MINGW64_NT-10.0-26200, bash 5.3.15, curl 8.21.0), Python 3.14.7.
    • Code: origin/main at 038c2ae22 (includes feat(planning): interview watcher stop, restatement diff and page polish #5722). I copied plugins/planning out of git into a scratch folder, so the host's own checkout, which is behind main, was not touched.
    • How it was launched: bash.exe from Git for Windows, started from the WSL lane through interop. It is not a Claude Code Bash tool session. The processes are the same Windows ones.
    • Data dir: a throwaway folder under the sandbox, C:/Users/KyleSexton/claude-lane-sandbox/drain-20261001/5723-data.

    1. Operator steps: pass

    I ran the issue's steps as written (ensure-running --port 0, watch.sh in the background, ps -ef | grep -E 'watch\.sh|curl', round.sh stop, sleep 10, the same ps, wait). I also listed the Windows processes by command line, because Git Bash's ps shows only part of them.

    Before round.sh stop (watcher armed, long-poll open). The token is redacted:

    $ ps -ef | grep -E 'watch\.sh|curl'
    AzureAD+  197255  197235 ?  16:44:32 "C:\Program Files\Git\mingw64\bin\curl.exe" -s -w \n%{http_code} --noproxy * --max-time 100 -H "X-Interview-Token: <token>" http://127.0.0.1:60103/api/wait?after=handled&replayed=0&timeout=90&watcher=melo-desk-001-197214&pid=197235
    AzureAD+  197235  197214 ?  16:44:31 bash watch.sh /c/Users/KyleSexton/claude-lane-sandbox/drain-20261001/5723-data
    
    Windows view (command lines naming watch.sh, curl, round.py, server.py or the data dir):
     59212 ppid=8624   python.exe: ...\python.exe ...\plugins\planning\surface\server.py --dir C:\Users\KyleSexton\claude-lane-sandbox\drain-20261001\5723-data --port 0 --nonce 0639a4c4c384773a
      6804 ppid=27964  bash.exe: "C:\Program Files\Git\usr\bin\bash.exe" watch.sh /c/Users/KyleSexton/claude-lane-sandbox/drain-20261001/5723-data
     31312 ppid=6804   bash.exe: "C:\Program Files\Git\usr\bin\bash.exe" watch.sh /c/Users/KyleSexton/claude-lane-sandbox/drain-20261001/5723-data
     44348 ppid=31312  curl.exe: "C:\Program Files\Git\mingw64\bin\curl.exe" -s -w \n%{http_code} --noproxy * --max-time 100 -H "X-Interview-Token: <token>" http://127.0.0.1:60103/api/wait?...
    

    Then bash round.sh --dir "$d" stop printed stopped 59212 and took 2 seconds. The watcher was already gone 0.1 seconds after stop returned. Ten seconds after stop returned:

    $ ps -ef | grep -E 'watch\.sh|curl'
    (no output)
    
    Windows view: none
    

    The watch.sh process exited 3, and watch.err held the interview server was stopped (round.sh stop): not re-arming. So after round.sh stop on Windows, no watch.sh and no curl long-poll was left, and the refused-poll path worked as designed.

    2. bash watch.test.sh: 26 pass, 1 fail

    ok: round.sh stop stops the server
    ok: round.sh stop ended the data dir's watch.sh
    FAIL: watch.sh kept retrying after a clean stop
    PASS=26 FAIL=1
    

    "round.sh stop ended the data dir's watch.sh" passes. "watch.sh exits 3 at once on a refused poll after a clean stop" does not: its case (o) fails with the message above.

    Cause: the test, not watch.sh. Case (o) removes the env file, then stops the server with kill "$spid", where $spid is the pid the server wrote to .interview-session.json. On Windows that is a native Windows pid, and Git Bash's kill only knows its own process numbers. I reproduced the case by hand:

    • kill 42680 (the server's native pid) printed kill: (42680) - No such process. The server kept running and answered HTTP 200, so the watcher's poll kept succeeding and watch.sh never took its refused-poll branch.
    • Bash's built-in kill rejects -f, and /usr/bin/kill -f <native pid> also printed "No such process".
    • taskkill //F //PID 10708 (the server's native pid) with the env file removed, same as the test: watch.sh exited 3 with the interview server was stopped (round.sh stop): not re-arming, inside the 10 second window.

    So the refused-poll behavior holds on Windows Git Bash. The test fails because it cannot kill the server there.

    What this leaves

    • Criterion 1 (no watch.sh or curl left within 10 s, exit 3 with the stop message): met.
    • Criterion 2 (watch.test.sh passes on that host): not met, for the test defect above.
    • Criterion 3 (result posted): this comment. I did not open the README PR, because the suite is not green yet.

    Next step is a code change, not another host run: make case (o) in plugins/planning/surface/watch.test.sh kill the server by native pid on Windows (for example taskkill //F //PID "$spid" when uname -s is MINGW* or MSYS*, kill elsewhere), keeping the POSIX path as is. Then re-run bash watch.test.sh on a Windows host. If it is 27 of 27, a PR can replace the README sentence "That path has not been exercised on Windows Git Bash." with the behavior verified above.

  3. removed
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    on Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority: lowNice-to-have, cosmetic, or speculative; opportunistic.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions