Skip to content

feat(cli): --notify-level to control watch round noise (#803) - #805

Closed
tamirdresher wants to merge 6 commits into
devfrom
squad/803-quiet-notifications
Closed

tamirdresher wants to merge 6 commits into
devfrom
squad/803-quiet-notifications

Conversation

@tamirdresher

Copy link
Copy Markdown
Collaborator

What

Adds --notify-level flag and config option to control how verbose watch round reporting is. Default changed from 'all' to 'important' — empty rounds are now silenced.

Why

Watch console output forwarded to Teams/webhook produces spam: 'Round 160, Round 161...' with no machine or repo attribution. Users can't tell what's happening or where. Closes #803

How

  • New notifyLevel field in WatchConfig: all | important (default) | none
  • reportBoard() accepts ReportOptions with notifyLevel, machineName, repoName
  • In important mode: empty rounds produce zero output
  • Round headers now include (hostname · repo) for attribution
  • CLI flag: --notify-level important
  • Config: .squad/config.json → { watch: { notifyLevel: 'important' } }

Testing

  • npm run build passes
  • npm test passes (5 new tests)

Docs

  • Changeset entry

Exports

N/A

Breaking Changes

Behavioral: Default changed from printing every round to only printing rounds with work. Use --notify-level all to restore old behavior.

Waivers

None

- Default 'important': only print rounds with actual work items
- 'all': old behavior (every round including empty)
- 'none': suppress all round output
- Add machine name + repo name to round headers for attribution
- Configurable via .squad/config.json watch.notifyLevel
- 5 new tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tamirdresher
tamirdresher requested review from Copilot and diberry April 4, 2026 07:02
@github-actions

github-actions Bot commented Apr 4, 2026 •

Copy link
Copy Markdown
Contributor

🛫 PR Readiness Check

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

⚠️ 4 item(s) to address before review

Status Check Details
❌ Single commit 6 commits — consider squashing before review
✅ Not in draft Ready for review
✅ Branch up to date Up to date with dev
❌ 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 3 unresolved Copilot thread(s) — fix and resolve before merging
❌ CI passing 15 check(s) still running

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

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.

Pull request overview

Adds a new watch/triage verbosity control to reduce “empty round” spam in squad watch, and improves round output attribution for forwarded logs.

Changes:

  • Introduces notifyLevel (all | important | none) in WatchConfig, with default important.
  • Extends reportBoard() to accept ReportOptions (notifyLevel + optional machine/repo attribution) and wires it into runWatch().
  • Adds --notify-level CLI parsing plus a changeset entry and new tests.

Reviewed changes

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

Show a summary per file
File Description
test/watch-notify-level.test.ts Adds unit tests for reportBoard() behavior under different notifyLevel modes and attribution fields.
packages/squad-cli/src/cli/commands/watch/index.ts Adds ReportOptions + notify-level gating to reportBoard() and includes (hostname · repo) in round headers.
packages/squad-cli/src/cli/commands/watch/config.ts Adds notifyLevel to config schema/defaults and normalizes it from .squad/config.json.
packages/squad-cli/src/cli-entry.ts Parses --notify-level and passes it into loadWatchConfig() overrides.
.changeset/quiet-notifications.md Documents the new flag/config and the default behavior change.

Comment on lines 174 to 184
export function reportBoard(state: BoardState, round: number, options?: ReportOptions): void {
const level = options?.notifyLevel ?? 'important';
if (level === 'none') return;

const total = Object.values(state).reduce((a, b) => a + b, 0);

// In 'important' mode, suppress empty rounds entirely
if (total === 0 && level === 'important') return;

if (total === 0) {
console.log(`${DIM}📋 Board is clear — Ralph is idling${RESET}`);

Copilot AI Apr 4, 2026

Copy link

Choose a reason for hiding this comment

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

reportBoard() now defaults to notifyLevel: 'important', which means calling it without options suppresses all output for empty board states. Because reportBoard is exported and used directly in the test suite, this default is a breaking behavioral change and will cause callers that expect the "Board is clear" message to stop seeing any output. Consider keeping reportBoard's default at 'all' (preserve the function's historical behavior) and have runWatch() pass the desired config.notifyLevel explicitly (defaulting to 'important' at the watch-command/config layer).

Copilot uses AI. Check for mistakes.
Comment on lines 183 to +192
if (total === 0) {
console.log(`${DIM}📋 Board is clear — Ralph is idling${RESET}`);
return;
}
console.log(`\n${BOLD}🔄 Ralph — Round ${round}${RESET}`);

// Build context tag for attribution
const ctx = [options?.machineName, options?.repoName].filter(Boolean).join(' · ');
const ctxSuffix = ctx ? ` ${DIM}(${ctx})${RESET}` : '';

console.log(`\n${BOLD}🔄 Ralph — Round ${round}${RESET}${ctxSuffix}`);

Copilot AI Apr 4, 2026

Copy link

Choose a reason for hiding this comment

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

In notifyLevel: 'all' mode, the empty-board path logs only "Board is clear — Ralph is idling" and does not include the round header or the (machine · repo) attribution. That undermines the stated goal of making forwarded output attributable (and makes it hard to correlate to a specific round). Consider including the same context tag (and optionally the round number) in the clear-board message when machineName/repoName are provided, or printing a round header even for empty rounds in all mode.

Copilot uses AI. Check for mistakes.
Comment on lines +23 to 32
/**
* 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;
}

Copilot AI Apr 4, 2026

Copy link

Choose a reason for hiding this comment

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

WatchConfig introduces a verbose option and documents it as coming from a --verbose flag, but there is no CLI parsing for --verbose and the value is not referenced anywhere in the watch implementation. This is misleading documentation and effectively a dead config knob. Either remove verbose from WatchConfig/normalize logic for now, or implement the flag parsing and wire config.verbose into actual diagnostics output.

Copilot uses AI. Check for mistakes.
Comment thread test/watch-notify-level.test.ts Outdated
Comment on lines +10 to +19
it('suppresses empty rounds in important mode (default)', () => {
const logs: string[] = [];
const origLog = console.log;
console.log = (...args: unknown[]) => logs.push(args.join(' '));
try {
reportBoard(emptyState(), 42, { notifyLevel: 'important' });
expect(logs).toHaveLength(0);
} finally {
console.log = origLog;
}

Copilot AI Apr 4, 2026

Copy link

Choose a reason for hiding this comment

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

These tests reassign console.log directly. Vitest commonly runs test files in parallel workers, so mutating the global console.log function can create cross-test interference/flakiness (and it’s inconsistent with existing tests in this repo that use vi.spyOn(console, 'log')). Prefer vi.spyOn(console, 'log').mockImplementation(...) plus vi.restoreAllMocks() in afterEach to keep the capture isolated and automatically restored.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +12
- `--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
- Configurable via `.squad/config.json` watch section: `"notifyLevel": "important"`
- Empty rounds silenced by default — no more "Round 160, Round 161" spam

Copilot AI Apr 4, 2026

Copy link

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.

Copilot uses AI. Check for mistakes.
Copilot and others added 3 commits April 4, 2026 10:07
- notification-level.md: full feature doc for --notify-level
- ralph.md: add --notify-level + --cleanup to capabilities table
- 030-v092-whats-coming.md: blog post covering all 10 features in next release

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
captureBoardOutput now defaults to 'all' mode to preserve existing
test behavior. The production default is 'important' but tests need
to verify clear-board message formatting.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CSpell docs-quality CI was failing because 'WCBED' is an unknown
word. Replaced CPC-tamir-WCBED with DEVBOX-01 in the blog post and
notification-level feature doc.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
tamirdresher and others added 2 commits April 4, 2026 18:29
- Add JSDoc to reportBoard() documenting default notifyLevel='important'
- Include machine/repo context in 'Board is clear' message for all modes
- Add comment noting verbose flag parsing is in PR #782
- Replace console.log direct mutation with vi.spyOn in tests
- Update changeset to clarify attribution is in round headers and clear message

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

Copy link
Copy Markdown
Collaborator Author

Superseded by #830 — combined into a single watch next-gen PR for easier review.

@tamirdresher

Copy link
Copy Markdown
Collaborator Author

Closing — superseded by #830 (watch-next-gen combined PR)

@bradygaster
bradygaster deleted the squad/803-quiet-notifications branch August 8, 2026 08:41
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.

Visual issues while running squad

2 participants