Delivery, Focus and Muting
How Daintree decides whether something interrupts you: toast routing and burst collapsing, the attention gates including Do Not Disturb and Quiet Hours, OS-level focus, per-project muting and silencing, OS notifications, and the banner surfaces.
Toasts
Toasts appear in the top-right of the app window, glass-styled with a colored left border for their type. At most three are visible at once.
Auto-dismiss timing sits at the middle of the published range rather than the top of it: errors and warnings 8 seconds, info 6, success 5. A toast carrying an action button is sticky and waits for you. Hovering pauses the timer. Error toasts announce assertively to screen readers; the rest announce politely.
The reason those durations can be that short is the inbox. A toast is time-limited content, and the persistent inbox is its conforming alternative under WCAG 2.2.1: anything you miss is recoverable. That constraint is enforced in the type system: a notification whose toast message is rich content rather than plain text must supply a plain-text inbox message, or it will not compile. There is no way to write a notification that only exists for eight seconds.
Toasts fired into a window you are not looking at
A high-priority toast only fires when the Daintree window is focused. Blurred, it goes to the inbox instead, marked unseen.
That is a deliberate correction of an earlier behavior. A toast rendered into a background window would run its dismiss timer, disappear unwatched, and mark its inbox entry as seen, quietly removing it from the unread badge and from the re-entry summary. The event you most needed to know about was the one most likely to be swallowed. Now a blurred window routes everything to the inbox unseen, so it is waiting when you come back. Toasts evicted by the three-toast cap are marked seen and surface as a +N more overflow pill that opens the inbox, except while the inbox is already open: signalling an arrival you can watch land would be a contradiction.
The same rule governs the grid bar: while notifications are disabled or a quiet gate is active, a grid-bar entry is written to history unseen, so turning notifications back on does not present you with a pile of already-read events you never saw.
Collapsing bursts
Four mechanisms keep a noisy producer from taking over the screen, and they solve different problems:
- Coalescing collapses a burst into a single updating toast over a short window, roughly two seconds, with a count in the title. Used where several events arriving together really are one event.
- Rate limiting catches slow drips that sit outside the coalesce window. Each source gets a bucket of three toasts refilling at one per ten seconds; overflow drops the toast entirely and aggregates into a single in-place summary row in the inbox. It only engages when a toast would actually have fired, so a blurred or muted window does not accumulate summary rows alongside the normal entries they are meant to replace.
- Superseding retires a stale entry when its resolution arrives. "Disconnected" is archived automatically when "reconnected" lands, so the inbox does not accumulate rows describing states that are no longer true.
- Transient notifications are toast-only, with no inbox entry at all. Reserved for one-shot confirmations whose result is already visible elsewhere, like a clipboard write.
Attention Gates
Three things can hold a notification back, and they stack. Because working out the combined effect in your head is exactly the kind of thing software should do for you, the inbox states the answer directly.
The "what will fire" summary
Above the muted pill, the inbox leads with a hero line naming only what breaks through right now: "Will interrupt you: Waiting, Git, System", or "Nothing will interrupt you right now". A second line, "Off: Completed, Working pulse", names the kinds you have switched off, so a kind that is missing from the first line is never ambiguous between "suppressed for now" and "turned off entirely".
The summary is computed from the gate state directly, with no round trip to the main process, so it is correct the instant the drawer opens. Every kind resolves to one of six states: will interrupt, quiet-gated (would interrupt, but a mute is holding it), kind off, sound off, inbox only for the passive kinds, or disabled when the master toggle is off.
Do Not Disturb
Pauses delivery for the current session. Open the inbox and click Pause notifications (the moon icon). The menu offers For 1 hour, Until {next morning} labelled with the actual time (the next 8:00) and Custom…, which opens notification settings rather than setting a timer, plus a Notification settings… link.
While DND is on, the toolbar bell switches to a struck-through icon and the inbox shows a Muted until {time} pill with a Resume button. The session mute is not persisted, so quitting clears it.
Quiet Hours
The scheduled version. Under Settings > Notifications: enable it, set a start and end, and pick the days. The default window is 22:00 to 08:00. It can cross midnight with no special handling. Two details worth knowing:
- Setting start and end to the same time shows an inline note ("Start and end match, so the schedule is effectively disabled until the times differ.") rather than silently doing nothing.
- The weekday selector is framed as "leave all boxes checked to apply every day", and an all-checked selection normalizes back to the empty set internally, so the two ways of saying "every day" cannot disagree.
During Quiet Hours the bell shows the struck-through icon and the inbox shows a Quiet hours pill. When a session mute and a scheduled window overlap, the resume time shown is whichever one actually unblocks you last.
Both gates pause delivery, nothing else. The inbox keeps recording. Urgent items break through, and agents waiting for input page through the OS layer regardless, because a blocked agent is blocking your work whether or not it is a convenient time.
OS-level Do Not Disturb
Daintree also reads your operating system's own Do Not Disturb or Focus state and surfaces it, so a silent app is never a mystery. When it is on, the inbox summary adds "OS Do Not Disturb is active".
It is a display signal and a gate on the working-pulse sound, and deliberately nothing more. In-app toasts are not suppressed by it: the OS already silences its own banners, and double-gating would hide signals inside the app window that the OS was never hiding.
Detection is macOS-only. The initial state is probed from the system's Focus assertions, and live transitions come from the system's own DND notifications. On Windows and Linux the state stays unknown (Windows Focus Assist exposes no subscription to Electron and cross-desktop Linux detection has no reliable standard), and unknown means nothing extra is shown rather than a guess.
Muting & Silencing
Where the gates pause everything for a while, muting and silencing are narrow and persistent. Both sit on a toast's kebab and on an inbox entry's kebab:
- Silence {event kind} from this project turns off one event kind for one project and leaves the rest alone. The kind names itself in the menu item, so the item reads "Silence completed notifications from this project" rather than something generic.
- Mute project notifications switches off that project's completion and waiting alerts together.
Once both are off, a struck-through bell appears next to the project in the sidebar worktree header and in the project switcher, so a muted project is visible without opening its settings. These live in the project's settings, survive a restart, and stay in effect after a DND or Quiet Hours window ends.
Project muting and Do Not Disturb are independent. Muting is persisted configuration for one project; DND is a temporary global pause. Ending DND does not un-mute a project, and muting a project pauses nothing else.
OS Notifications
OS-native alerts go through Electron's notification API, appear as system popups, and reach you when Daintree is minimized or behind another window. Clicking one brings Daintree forward and jumps to the relevant agent or terminal.
Waiting reasons
"An agent is waiting" is not a useful signal when four of them are. Daintree classifies why an agent stopped and carries that into the notification body, so you can triage from the banner without switching apps:
| Reason | Notification body | What it means |
|---|---|---|
approval | "… is waiting for approval" | A one-keystroke unblock. Highest triage priority. |
error | "… is blocked by an error" | Hard-blocked and burning wall-clock silently. |
question | "… asked a question" | Needs a considered answer. |
prompt | "… is waiting for input" | The classifier's fallback — no positive evidence either way. |
The ordering above is the triage order the app uses everywhere the reason appears, ranked by leverage per second of your attention. prompt never earns a specific label, because it is the absence of evidence rather than a finding; it and an unclassified state both present as the generic "waiting for input". When several agents are grouped into one notification, the specific wording is used only if every agent in the group shares the same reason: a mixed group falls back to the generic phrasing rather than overclaiming for any member. The same vocabulary drives the terminal header chip, the waiting popover, Fleet badges and screen-reader announcements, so one agent reads as one signal everywhere. See Agent States.
OS popups fire only for watched terminals: those with the Watch toggle on in the panel header. An agent running unwatched generates no OS alerts even when the event type is on. Completion popups are also suppressed while you are already looking at the worktree that finished.
Dock badges and window title indicators are separate from OS popups. They are always on and count waiting agents across all terminals, watched or not:
The dock icon shows a badge count of agents currently waiting for input. The badge clears when the Daintree window takes focus. System notification popups appear in the top-right of the screen.
The window title becomes (N) Daintree, where N is the number of waiting agents, and that count appears in the taskbar label. The title reverts when the window regains focus. Notification popups go through the system tray.
The window title becomes (N) Daintree, reverting when the window regains focus. Popups go through the desktop environment's notification daemon.
Banners
Banners carry state rather than events, and Daintree splits them by whether you can act on them.
Global banners are advisory
A single slot at the top of the window holds app-wide conditions. Exactly one renders at a time, chosen by a fixed priority order, highest first:
- Host crash: the backend is unusable right now.
- Watchdog disabled: the deadlock detector is gone, so the protection layer is down.
- Safe mode: panels were not restored after a crash loop.
- Restore confirmation: the informational "session recovered" banner.
- Missing prerequisite: a required tool such as Git or Node is not installed.
- Forge token: a forge provider's credentials expired.
- Cloud sync: the project sits in a synced folder.
- Rosetta: an x64 build being translated on Apple Silicon.
The ordering is by urgency and blast radius rather than by category. A live host failure outranks a downed monitor; the watchdog outranks safe mode because it protects against the next crash while safe mode is a consequence of the last one; a missing Git outranks an expired token because it breaks every git operation rather than one panel's data; and Rosetta sits last because nothing in the app can change it. Restore confirmation stays high despite being purely informational, because its auto-dismiss timer only runs while it is mounted.
Suppressed banners are unmounted rather than hidden, so a banner's timers only run while you can actually see it. On Windows, the native caption strip is told which banner color sits beneath it, and the swap is coalesced so a change from warning to error does not flash back through the canvas in between.
Pane-local banners are actionable
An error that belongs to one panel renders inside that panel, next to the thing that failed and next to the control that retries it. A terminal that could not reattach, a spawn that failed, a worktree move that did not complete, a file browser, file viewer, diff viewer or dev preview that hit an error: each shows its own banner in its own pane.
The split is the point. A global banner tells you something about the application; a pane-local banner tells you something about the thing you are looking at, and gives you the button that fixes it. Neither one is a toast, because neither describes an event that happened at a moment: they describe a state that is still true.
The grid bar
The grid bar is a single inline slot above the panel grid for notifications that should stay put rather than float. Contention for the slot resolves by severity, with ties going to the oldest, and a newcomer cannot preempt the current occupant within its first 5 seconds: otherwise a burst of producers could strobe the bar and make it unreadable. Grid-bar notifications carry either their own action buttons or, when they have none, a Dismiss control, so a stale one is never stuck on screen.