Skip to content

fix(watch): implement the documented stop signals (--sentinel-file and .squad/ralph-stop) - #1719

Merged
bradygaster merged 2 commits into
github:devfrom
swigerb:swigerb/1711-watch-stop-signal
Aug 25, 2026
Merged

bradygaster merged 2 commits into
github:devfrom
swigerb:swigerb/1711-watch-stop-signal

Conversation

@swigerb

@swigerb swigerb commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Closes #1711.

Problem

squad watch documents two ways to stop a running loop, and neither was implemented. The only way to stop a run was to signal the process.

  • --sentinel-file <path> was parsed at cli-entry.ts:641-643, passed through at cli-entry.ts:704, and merged in watch/config.ts:103,144,155 - with a doc comment reading "Path to a sentinel file - watch shuts down gracefully when removed." Nothing ever read the value.
  • .squad/ralph-stop appeared in README.md:277 and four places in docs/features/watch-next-gen.md, but the string had zero references anywhere in packages/.

Change

Adds detectStopSignal() and calls it at the top of every round, before any other work:

  • --sentinel-file <path> stops the run once that file is removed, matching the existing doc comment. The file is created at startup if it does not already exist, otherwise the very first round would stop immediately.
  • <squadDir>/ralph-stop stops the run once that file exists, matching the documented touch .squad/ralph-stop.

Both paths log the reason and then reuse the existing shutdown(), so PID cleanup, circuit-breaker persistence, event emission and log-stream teardown all still happen. Both stop targets are printed in the startup banner so they are discoverable.

The shutdown closure was hoisted out of the interval promise so that a stop signal detected on the first round can shut down cleanly - previously it was defined inside the promise executor and could not be reached before the interval was established. The promise resolves immediately in that case.

detectStopSignal() takes an injected exists probe, which keeps it pure and directly unit-testable.

Tests

New test/watch-stop-signal.test.ts, 8 cases:

  • no signal when neither trigger is present
  • no signal while the sentinel still exists
  • stops when the sentinel has been removed
  • stops when ralph-stop exists
  • ralph-stop still fires while the sentinel is present
  • sentinel reason wins when both fire
  • relative sentinel paths resolve against cwd
  • an unset sentinel is not treated as removed

Validation

  • npm run lint (tsc --noEmit, both packages) - clean
  • npx vitest run test/watch-stop-signal.test.ts test/watch-rate-limit.test.ts test/watch-cleanup.test.ts - 28 passed

I was unable to complete a full npm test run locally due to a package-proxy restriction on my machine, not a failure in the suite. Worth a CI confirmation on the full suite.

Notes

Behaviour is backwards compatible - with no --sentinel-file and no ralph-stop present, detectStopSignal() returns null and the loop behaves exactly as before.

Changeset included (patch, @bradygaster/squad-cli).

`--sentinel-file` was parsed and threaded into watch config but never read,
and `.squad/ralph-stop` appeared only in documentation with no code
references. A running watch loop could therefore only be stopped by sending
a signal to the process.

Add `detectStopSignal()` and check it at the top of every round:

- `--sentinel-file <path>` stops the run once that file is removed, matching
  the existing doc comment in watch/config.ts. The file is created at startup
  if absent so the first round does not stop immediately.
- `<squadDir>/ralph-stop` stops the run once it exists, matching the
  documented `touch .squad/ralph-stop` in the README.

Both reuse the existing graceful shutdown path, so PID cleanup, circuit
breaker persistence and session teardown all still run. The shutdown closure
was hoisted out of the interval promise so a stop detected on the very first
round can shut down cleanly.

Closes github#1711.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

🟡 Impact Analysis — PR #1719

Risk tier: 🟡 MEDIUM

📊 Summary

Metric Count
Files changed 3
Files added 2
Files modified 1
Files deleted 0
Modules touched 3
Critical files 1

🎯 Risk Factors

  • 3 files changed (≤5 → LOW)
  • 3 modules touched (2-4 → MEDIUM)
  • Critical files touched: packages/squad-cli/src/cli/commands/watch/index.ts

📦 Modules Affected

root (1 file)
  • .changeset/watch-stop-signal.md
squad-cli (1 file)
  • packages/squad-cli/src/cli/commands/watch/index.ts
tests (1 file)
  • test/watch-stop-signal.test.ts

⚠️ Critical Files

  • packages/squad-cli/src/cli/commands/watch/index.ts

This report is generated automatically for every PR. See #733 for details.

@github-actions

github-actions Bot commented Aug 14, 2026 •

Copy link
Copy Markdown
Contributor

🛫 PR Readiness Check

ℹ️ This comment updates on each push. Last checked: commit 50238bd

PR Scope: 📦🔧 Mixed (product + infrastructure)

⚠️ 4 item(s) to address before review

Status Check Details
❌ Single commit 2 commits — consider squashing before review
✅ Not in draft Ready for review
❌ Branch up to date dev is 84 commit(s) ahead — rebase recommended
❌ Copilot review No Copilot review yet — it may still be processing
✅ Changeset present Changeset file found
✅ Scope clean No .squad/ or docs/proposals/ files
✅ No merge conflicts No merge conflicts
✅ Copilot threads resolved 0 active Copilot thread(s) resolved (1 outdated skipped)
❌ CI passing 6 check(s) still running

Files Changed (3 files, +207 −20)

File +/−
.changeset/watch-stop-signal.md +5 −0
packages/squad-cli/src/cli/commands/watch/index.ts +109 −20
test/watch-stop-signal.test.ts +93 −0

Total: +207 −20


This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.

@bradygaster

Copy link
Copy Markdown
Collaborator

@swigerb heads up — this one looks finished from the outside and is currently the only thing standing between us and an empty PR queue.

Status as of now:

  • Rebased onto current dev and all checks are green (the earlier test failure was a stale-base artifact, not your code — gh-aw-quality.test.ts needed squad-implement-worker, which has since landed).
  • Changeset present, 8 new cases in test/watch-stop-signal.test.ts, scope is tight at 3 files.

If it's actually done, could you flip it out of draft? Happy to merge it the moment you do. If something's still outstanding on your end, drop a note here and we'll work it — just don't want it sitting idle on a technicality.

Context: we cleared 16 of 19 open PRs today; #1750 is the only other one still in flight.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The implementation aligns with the documented semantics and includes targeted unit tests; only a small misleading doc comment needs cleanup.

Pull request overview

Implements the two documented external “stop signals” for squad watch so a running watch loop can be stopped gracefully without sending a process signal (Closes #1711).

Changes:

  • Add detectStopSignal() and check it at the start of each watch round, supporting both --sentinel-file <path> (stop when deleted) and <squadDir>/ralph-stop (stop when created).
  • Hoist shutdown() so a stop detected on the first round can still exit cleanly and resolve the run promise.
  • Add a focused unit test suite for stop-signal behavior and a patch changeset for @bradygaster/squad-cli.
File summaries
File Description
packages/squad-cli/src/cli/commands/watch/index.ts Implements stop-signal detection, startup banner hints, and integrates graceful stop into the watch loop.
test/watch-stop-signal.test.ts Adds unit tests for detectStopSignal() covering precedence and path-resolution behaviors.
.changeset/watch-stop-signal.md Declares a patch release for the CLI with a user-facing description of the new behavior.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

The constant names the ralph-stop *stop file*, not the user-configured
sentinel file. Calling it a sentinel conflated the two mechanisms, which
is confusing now that both exist with opposite trigger semantics.

Refs github#1711
@bradygaster
bradygaster merged commit 6be7d06 into github:dev Aug 25, 2026
16 checks passed
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.

watch: documented kill switches (--sentinel-file and .squad/ralph-stop) are never read - a running loop cannot be stopped except by signal

3 participants