Repository navigation
feat(cli): --notify-level to control watch round noise (#803) #805
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6571324
8cb4a34
7a62317
db03b33
c72c908
a6436f2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| --- | ||
| "@bradygaster/squad-cli": minor | ||
| --- | ||
|
|
||
| Add `--notify-level` to control watch round reporting noise (#803) | ||
|
|
||
| - `--notify-level important` (default): only print rounds with actual work items | ||
| - `--notify-level all`: print every round including empty (old behavior) | ||
| - `--notify-level none`: suppress all round output | ||
| - Add machine name (`os.hostname()`) and repo name to round headers for attribution | ||
| (shown in round headers when the board has items, and in "Board is clear" message) | ||
| - Configurable via `.squad/config.json` watch section: `"notifyLevel": "important"` | ||
| - Empty rounds silenced by default — no more "Round 160, Round 161" spam | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| --- | ||
| title: "What's Coming in v0.9.2 — 10 Features, Zero Breaking Changes" | ||
| date: 2026-04-04 | ||
| author: "Tamir" | ||
| tags: [squad, release, features, watch, cleanup, fact-checker, external-state, self-upgrade, fleet, verbose, skills, scratch-dir, triage, notifications] | ||
| status: draft | ||
| hero: "The next Squad release ships 10 new features, 4 bug fixes, and 60+ tests — all backward-compatible. Here's what to expect." | ||
| --- | ||
|
|
||
| # What's Coming in v0.9.2 — 10 Features, Zero Breaking Changes | ||
|
|
||
| > ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. | ||
|
|
||
| > _The next Squad release ships 10 new features, 4 bug fixes, and 60+ tests — all backward-compatible. Here's what to expect._ | ||
|
|
||
| ## The Noise Problem | ||
|
|
||
| If you've been running `squad watch` in production with output forwarded to Teams or Slack, you've probably noticed this: | ||
|
|
||
| ``` | ||
| Squad Monitor Round 158 | ||
| Squad Monitor Round 159 | ||
| Squad Monitor Round 160 | ||
| Squad Monitor Round 161 | ||
| ``` | ||
|
|
||
| Hundreds of messages. No useful information. No way to tell which machine or repo they came from. | ||
|
|
||
| This release fixes that — and ships 9 other features while we're at it. | ||
|
|
||
| ## New Features | ||
|
|
||
| ### 1. Quiet Notifications (`--notify-level`) | ||
|
|
||
| The biggest quality-of-life improvement: watch rounds are now **silent by default when the board is empty**. Only rounds with actual work produce output. | ||
|
|
||
| ```bash | ||
| squad watch --notify-level important # default — only meaningful rounds | ||
| squad watch --notify-level all # old behavior (every round) | ||
| squad watch --notify-level none # fully silent | ||
| ``` | ||
|
|
||
| Round headers now include **machine name and repo** for attribution: | ||
|
|
||
| ``` | ||
| 🔄 Ralph — Round 5 (DEVBOX-01 · my-project) | ||
| ``` | ||
|
|
||
| Persistent config: `{ "watch": { "notifyLevel": "important" } }` in `.squad/config.json`. | ||
|
|
||
| ### 2. Fleet Hybrid Dispatch (`--dispatch-mode`) | ||
|
|
||
| Parallel issue processing via Copilot CLI `/fleet`. Benchmarked at **2.9x faster** for read-heavy workloads. | ||
|
|
||
| ```bash | ||
| squad watch --execute --dispatch-mode hybrid | ||
| ``` | ||
|
|
||
| Issues are auto-classified as read (research, reviews → fleet) or write (implementations, fixes → local). Fleet batches reads in parallel while writes execute sequentially. | ||
|
|
||
| ### 3. Verbose Debugging (`--verbose`) | ||
|
|
||
| When watch seems stuck on "Board is clear" but you know there's work, `--verbose` shows you exactly what's happening: | ||
|
|
||
| ```bash | ||
| squad watch --verbose | ||
| ``` | ||
|
|
||
| Prints: issue counts, label matches, auth status, PR review states, round timing, capability execution paths. | ||
|
|
||
| ### 4. Fact-Checker Agent Role (🔍) | ||
|
|
||
| New built-in role for output verification. Validates claims, detects hallucinations, runs counter-hypotheses. | ||
|
|
||
| ``` | ||
| squad new agent fact-checker | ||
| ``` | ||
|
|
||
| Confidence ratings: ✅ Verified, ⚠️ Unverified, ❌ Contradicted. Routes automatically on "fact-check", "verify", "audit" keywords. | ||
|
|
||
| ### 5. 8 Built-in Skills | ||
|
|
||
| `squad init` and `squad upgrade` now ship 8 curated skills: | ||
|
|
||
| | Skill | What it teaches | | ||
| |-------|-----------------| | ||
| | squad-conventions | Core patterns and file layout | | ||
| | error-recovery | Graceful failure handling | | ||
| | secret-handling | Credential safety | | ||
| | git-workflow | Branch and commit conventions | | ||
| | session-recovery | Checkpoint and resume | | ||
| | reviewer-protocol | Code review gates | | ||
| | test-discipline | Test-first discipline | | ||
| | agent-collaboration | Multi-agent handoffs | | ||
|
|
||
| ### 6. Scratch Directory (`.squad/.scratch/`) | ||
|
|
||
| Agents no longer dump temp files in the repo root. The new `scratchDir()` and `scratchFile()` SDK APIs route all ephemeral files to `.squad/.scratch/` — gitignored and auto-cleaned. | ||
|
|
||
| ### 7. Cleanup Watch Capability | ||
|
|
||
| Automated housekeeping during `squad watch`: | ||
| - Clears `.squad/.scratch/` every 10 rounds | ||
| - Archives orchestration-log and session-log entries older than 30 days | ||
| - Warns about stale decision inbox files (>7 days) | ||
|
|
||
| ### 8. External State Storage | ||
|
|
||
| Move `.squad/` state outside the working tree so it survives branch switches: | ||
|
|
||
| ```bash | ||
| squad externalize # state moves to ~/.squad/projects/{repo}/ | ||
| squad internalize # move it back | ||
| ``` | ||
|
|
||
| ### 9. Self-Upgrade (`squad upgrade --self`) | ||
|
|
||
| Update the CLI itself from within squad: | ||
|
|
||
| ```bash | ||
| squad upgrade --self # latest stable | ||
| squad upgrade --self --insider # latest prerelease | ||
| ``` | ||
|
|
||
| Auto-detects npm/pnpm/yarn. After CLI upgrade, automatically runs repo upgrade to apply new templates. | ||
|
|
||
| ### 10. Triage Label Slug Fix | ||
|
|
||
| Multi-word agent names (like "Steve Rogers") now correctly generate `squad:steve-rogers` labels instead of `squad:steve rogers` (which GitHub rejects). Labels are pre-created at watch startup. | ||
|
|
||
| ## Bug Fixes | ||
|
|
||
| - **PR contamination** — Scribe now stages only `.squad/` files, not `git add .` (#783) | ||
| - **Outdated review threads** — PR readiness check ignores threads where code changed (#780) | ||
| - **Filename uniqueness** — `scratchFile()` uses monotonic counter, not just `Date.now()` | ||
| - **Cross-platform paths** — `deriveProjectKey()` handles Windows paths on Linux CI | ||
|
|
||
| ## By the Numbers | ||
|
|
||
| - **10 features** across SDK, CLI, and watch capabilities | ||
| - **4 bug fixes** (1 critical — PR contamination) | ||
| - **60+ new tests** (scratch: 8, cleanup: 12, fact-checker: 8, skills: 5, external: 12, self-upgrade: 4, notify: 5, triage: 12) | ||
| - **4 new feature docs** + 2 updated + README command table (15→17) | ||
| - **0 breaking changes** — all opt-in, all backward-compatible | ||
| - **1 behavioral change** — notify default flipped from `all` to `important` (use `--notify-level all` to restore) | ||
|
|
||
| ## Upgrade | ||
|
|
||
| ```bash | ||
| squad upgrade --self # get the latest CLI | ||
| squad upgrade # apply new templates to your repo | ||
| ``` | ||
|
|
||
| Or if you're on insider: | ||
|
|
||
| ```bash | ||
| squad upgrade --self --insider | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| # Notification Level | ||
|
|
||
| > ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. | ||
|
|
||
| **Try this to silence empty rounds:** | ||
| ``` | ||
| squad watch --notify-level important | ||
| ``` | ||
|
|
||
| **Try this to see everything (debugging):** | ||
| ``` | ||
| squad watch --notify-level all | ||
| ``` | ||
|
|
||
| **Try this in config for persistent setting:** | ||
| ```json | ||
| { "watch": { "notifyLevel": "important" } } | ||
| ``` | ||
|
|
||
| When `squad watch` runs continuously, it prints a board report after every round. In production setups where output is forwarded to Teams, Slack, or email, this creates noise — hundreds of "Round N" messages with no useful content when the board is clear. | ||
|
|
||
| --- | ||
|
|
||
| ## Notify Levels | ||
|
|
||
| | Level | Behavior | When to use | | ||
| |-------|----------|-------------| | ||
| | `important` | Only print rounds with actual work items | **Default.** Best for production/Teams channels | | ||
| | `all` | Print every round, including empty ones | Debugging. Old behavior before this feature | | ||
| | `none` | Suppress all round output | Headless/CI — only errors matter | | ||
|
|
||
| ## Machine and Repo Attribution | ||
|
|
||
| Every round header now includes the machine hostname and repo name: | ||
|
|
||
| ``` | ||
| 🔄 Ralph — Round 5 (DEVBOX-01 · my-project) | ||
| ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
| 🔴 Untriaged: 2 | ||
| 🟢 Ready to merge: 1 | ||
| ``` | ||
|
|
||
| This tells you **where** the message came from — which machine and which repo — so when multiple watch instances report to the same Teams channel, you can distinguish them. | ||
|
|
||
| ## Configuration | ||
|
|
||
| ### CLI flag (per-run) | ||
|
|
||
| ```bash | ||
| squad watch --notify-level important # default | ||
| squad watch --notify-level all # old behavior | ||
| squad watch --notify-level none # silent | ||
| ``` | ||
|
|
||
| ### Config file (persistent) | ||
|
|
||
| In `.squad/config.json`: | ||
|
|
||
| ```json | ||
| { | ||
| "watch": { | ||
| "notifyLevel": "important", | ||
| "interval": 10, | ||
| "execute": true | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Config file settings are overridden by CLI flags when both are present. | ||
|
|
||
| ## What Counts as "Important" | ||
|
|
||
| A round is considered important (and reported) when **any** board counter is non-zero: | ||
|
|
||
| - Untriaged issues | ||
| - Assigned but unstarted work | ||
| - Draft PRs | ||
| - Changes requested on PRs | ||
| - CI failures | ||
| - PRs needing review | ||
| - PRs ready to merge | ||
| - Issues executed this round | ||
|
|
||
| If all counters are zero, the round is silent in `important` mode. | ||
|
|
||
| ## See Also | ||
|
|
||
| - [Ralph — Work Monitor](/docs/features/ralph) — full Ralph documentation | ||
| - [Watch capabilities](/docs/features/ralph#watch-mode) — how squad watch works |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,13 +20,23 @@ export interface WatchConfig { | |
| agentCmd?: string; | ||
| /** Per-capability config: `true` / `false` / object with sub-options. */ | ||
| capabilities: Record<string, boolean | Record<string, unknown>>; | ||
| /** | ||
| * Controls how verbose watch round reporting is. | ||
| * - 'all' — print every round (current behavior) | ||
| * - 'important' — only print rounds with actual work (default) | ||
| * - 'none' — suppress all round reporting | ||
| */ | ||
| notifyLevel?: 'all' | 'important' | 'none'; | ||
| /** Verbose diagnostics (--verbose flag). */ | ||
| verbose?: boolean; | ||
| } | ||
|
Comment on lines
+23
to
32
|
||
|
|
||
| const DEFAULTS: WatchConfig = { | ||
| interval: 10, | ||
| execute: false, | ||
| maxConcurrent: 1, | ||
| timeout: 30, | ||
| notifyLevel: 'important', | ||
| capabilities: {}, | ||
| }; | ||
|
|
||
|
|
@@ -63,6 +73,8 @@ export function loadWatchConfig( | |
| timeout: cliOverrides.timeout ?? fileConfig.timeout ?? DEFAULTS.timeout, | ||
| copilotFlags: cliOverrides.copilotFlags ?? fileConfig.copilotFlags ?? DEFAULTS.copilotFlags, | ||
| agentCmd: cliOverrides.agentCmd ?? fileConfig.agentCmd ?? DEFAULTS.agentCmd, | ||
| notifyLevel: cliOverrides.notifyLevel ?? fileConfig.notifyLevel ?? DEFAULTS.notifyLevel, | ||
| verbose: cliOverrides.verbose ?? fileConfig.verbose ?? false, | ||
| capabilities: { | ||
| ...DEFAULTS.capabilities, | ||
| ...(fileConfig.capabilities ?? {}), | ||
|
|
@@ -83,10 +95,14 @@ function normalizeFileConfig(raw: Record<string, unknown>): Partial<WatchConfig> | |
| if (typeof raw['timeout'] === 'number') result.timeout = raw['timeout']; | ||
| if (typeof raw['copilotFlags'] === 'string') result.copilotFlags = raw['copilotFlags']; | ||
| if (typeof raw['agentCmd'] === 'string') result.agentCmd = raw['agentCmd']; | ||
| if (raw['notifyLevel'] === 'all' || raw['notifyLevel'] === 'important' || raw['notifyLevel'] === 'none') { | ||
| result.notifyLevel = raw['notifyLevel']; | ||
| } | ||
| if (typeof raw['verbose'] === 'boolean') result.verbose = raw['verbose']; | ||
|
|
||
| // Everything else is a capability key | ||
| const caps: Record<string, boolean | Record<string, unknown>> = {}; | ||
| const reserved = new Set(['interval', 'execute', 'maxConcurrent', 'timeout', 'copilotFlags', 'agentCmd']); | ||
| const reserved = new Set(['interval', 'execute', 'maxConcurrent', 'timeout', 'copilotFlags', 'agentCmd', 'notifyLevel', 'verbose']); | ||
| for (const [key, value] of Object.entries(raw)) { | ||
| if (reserved.has(key)) continue; | ||
| if (typeof value === 'boolean' || (typeof value === 'object' && value !== null && !Array.isArray(value))) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changeset states machine/repo attribution is added to "round headers", but in
notifyLevel: 'all'mode empty rounds currently print only the clear-board message without any(machine · repo)context. Either update the implementation so empty-round output also includes attribution, or tweak the changeset wording so it doesn’t overstate the attribution coverage.