Skip to content

Defer terminal-notification alerts until animation stops by default - #706

Merged
nedtwigg merged 2 commits into
mainfrom
alert-defer-default-on
Sep 21, 2026
Merged

nedtwigg merged 2 commits into
mainfrom
alert-defer-default-on

Conversation

@nedtwigg

@nedtwigg nedtwigg commented Sep 20, 2026

Copy link
Copy Markdown
Member

First of a stacked series simplifying the alert system.

Coding agents (claude, codex) emit their "done" notification while their TUI is still redrawing, so an undeferred ring summons you to a pane that is still animating. deferAlertsUntilQuiet now defaults on; the switch stays as an escape hatch for the protocols' literal timing. The AlertManager field follows DEFAULT_ALERT_SETTINGS so a manager that never receives a settings blob behaves like one that does.

  • docs/specs/alert.md Alarm settings row and the rationale paragraph updated.
  • Two tests that pinned latch-through-output behavior now set the flag off explicitly; one new test pins the shipped default at the manager and at normalizeAlertSettings.
  • Story DeferralEnabledDeferralDisabled (the non-default state is the one worth a snapshot).

Installs that have never saved an alert-settings blob pick the new default up. Any install that has changed any alert setting has the whole blob persisted, so it keeps deferAlertsUntilQuiet: false until the switch is flipped in Settings; the blob has no version field to key a one-shot migration on, and dropping a persisted false on read would make the preference unpersistable (decision recorded in the rationale).

🤖 Generated with Claude Code

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 20, 2026

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: 8fe508f
Status: ✅  Deploy successful!
Preview URL: https://fa99eaf9.mouseterm.pages.dev
Branch Preview URL: https://alert-defer-default-on.mouseterm.pages.dev

View logs

@dormouse-bot dormouse-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Feedback on work in progress, not a merge verdict — mark the PR ready when you want the full review.

The flag gates a second behavior, and nothing a user reads mentions it. deferAlertsUntilQuiet also gates withdrawResumedWatchingRing, which docs/specs/alert.md states as "With deferAlertsUntilQuiet, withdraw a WATCHING ring when watched work resumes confirmed BUSY." That is why the two tests here had to switch the flag off — ALERT_RINGING latches when user has no attention (view hidden) and leaves a stale WATCHING ring alone once output has resumed. So the default flip ships ring withdrawal out of the box as well, while the switch's helper text in lib/src/components/SettingsDialog.tsx ("When the animation watcher is fully armed, terminal notifications wait for the pane to become quiet") and the Alarm settings row both describe only the deferral half. A latched claude-finished ring that disappears when the agent starts a new turn is the case a user will actually hit, with nothing to read about it. Either widen that copy or put withdrawal on its own condition.

"Users who saved the setting keep their value" reaches further than the body says. updateAlertSettings normalizes and persists the whole blob, so a user who once changed only speakDelayMs has deferAlertsUntilQuiet: false sitting in dormouse:alert-settings and never picks up the new default — despite never having expressed a preference about deferral. For a change motivated by an experience every agent user has, the set that keeps the old behavior is "anyone who has opened Settings and touched anything", not "anyone who set this switch". Worth deciding whether that is intended, or whether the persisted key should be dropped when it still matches the old default.

The rationale's closing clause reads as attributing indefinite deferral to the switch being off, when the on state is what produces it — scheduleDeferredNotification re-arms at the detector's quiet deadline with no cap. Since that unbounded case is now the shipped default, it is the sentence most worth being unambiguous. Inline suggestion below.

One small thing outside the diff: standalone/src/browser-sidecar-adapter.test.ts builds its canonical fixture as { ...DEFAULT_ALERT_SETTINGS, deferAlertsUntilQuiet: true }, now identical to DEFAULT_ALERT_SETTINGS — so the assertion no longer distinguishes "forwarded the blob it was handed" from "emitted the default". Flipping that override to false restores it.

Comment thread docs/specs/alert.rationale.md Outdated
nedtwigg and others added 2 commits September 20, 2026 22:35
Coding agents emit their "done" notification while their TUI is still
redrawing, so the undeferred ring summoned the user to a pane that was
still animating. The switch stays as an escape hatch for the protocols'
literal timing, and the AlertManager field now follows
DEFAULT_ALERT_SETTINGS so a manager that never receives a settings blob
behaves like one that does.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The switch also gates withdrawal of a WATCHING ring when watched work
resumes, so the default flip ships that out of the box too, while the
helper text under the switch and the Alarm settings row described only
the deferral half. Both now name both behaviors, and the settings row
points at the section that owns each rule instead of restating one.

The rationale leads with deferral being the unbounded state rather than
reading as though the off position were, and records why an install that
already saved a settings blob keeps the old value: with no version field
to key a migration on, dropping a persisted false on read would leave
the off position unpersistable.

The browser-sidecar adapter's canonical blob differs from
DEFAULT_ALERT_SETTINGS again, so the assertion still tells a forwarded
blob apart from an emitted default.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nedtwigg
nedtwigg force-pushed the alert-defer-default-on branch from dd35485 to 8fe508f Compare September 21, 2026 05:54
@nedtwigg
nedtwigg marked this pull request as ready for review September 21, 2026 06:00
@nedtwigg
nedtwigg merged commit 77a0851 into main Sep 21, 2026
14 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.

2 participants