0020 — Tray notifications¶
- Status: Implemented (unreleased), for 0.8.0. The behaviour is in SPEC §13. Item of 0017 "before the runner".
- Size: one pull request, about 2 agent-days. No migration.
Problem¶
Browser notifications (SPEC §2) need an open board tab that was given the permission. The owner runs the desktop program with its window hidden: a task can wait on him for an hour with nothing on the screen.
Decisions¶
- The server decides what is news, the program decides how to show it. A pure
domain/notifications.pyturns an event into a notice (classify).GET /api/notifications(Bearer) reads the events after a cursor.desktop/notifier.pypolls it, filters bynotify_kinds, groups and shows. The filter is on the program's side, so the kinds are a setting of the program and the server needs none. - Polling, one path. Every 5 s over HTTP, loopback without a proxy as
healthz, for the embedded and an external server alike. A thread readingServicedirectly would work only in embedded mode. An idle poll reads oneMAX(id). - The cursor is an event id in
notify_state.jsonin the config folder, machine-written likewindow-state.json. A first start has no cursor and learns the newest id: no replay of history. A cursor belongs to one board (its base URL and data folder): another board starts from its newest event. A cursor past the newest event (another database behind one address) moves back. - Paging.
limitcounts the events read, not the notices, andlast_idalways moves on, so a page of events that are no notice cannot stall the reader. The program reads untillast_idstops moving, which is why a long downtime ends in one summary, not in a toast per page. - Grouping.
icon.notifyreplaces the previous balloon, so a burst would lose all but the last. At most one toast per 20 s window, the first at once: one item with its own title and text, several as "3 need you: 2 reviews, 1 reply". The same task and kind collapse inside the window, keeping the newest wording. A move to review that also setswaiting_onis two events and one thing to do:reviewabsorbswaitingof the same task. - The owner's own writes are not news: actors
board,ownerand theowner_names. - Quiet mode is a setting, not a state: a tray checkbox writes
notify_quiet. Held items live innotify_state.json, so a restart in quiet mode keeps them and the dot. notify = falsestops the polling and forgets the cursor: switching it on later starts from the newest event, not with a summary of the time it was asked to be silent.- A click cannot open anything. pystray's Windows backend handles only clicks on the icon, not on
a balloon. Instead the tray has Open Inbox. The API's
urlis there for a client that can. - A poll that fails changes nothing. The cursor stays, one log line says paused and one resumed.
The watchdog is what tells the owner about a server that is gone:
_watch_externaladds one notice of the kindserveron becoming unreachable. The embedded "keeps failing" notice stays as it was and is not subject to the settings, because the problem page comes with it.
Kinds¶
| Kind | From | Default |
|---|---|---|
waiting |
task.updated with changes.waiting_on naming an owner, matched as the Inbox does |
on |
review |
task.stage_changed to review or test |
on |
reply |
comment.added with author_kind == "agent" |
on |
answer |
intake.answer_received |
on |
permission |
session.status_changed to waiting (the Notification hook) |
off |
brake |
brake.set by an agent or a rule, not a budget's own (budget tells it) |
on |
budget |
budget.warned, budget.exceeded |
on |
server |
the program's watchdog, local | on |
permission is off because the Notification hook also fires for an idle prompt, and a working
day has many of them.
Risks¶
A board tab with browser notifications allowed tells a review twice (documented in SPEC; they are
different channels and the program cannot see the tab) | the balloon's limits (63 and 255 UTF-16
units, pystray raises beyond them: fit cuts by units, not characters) | pystray's X11 backend has
no notifications, and that is the backend of the .deb: there a notice is only a log line |
Focus Assist or Do not disturb hide balloons with no way to tell | the text of a toast (a task title,
the first words of a reply) shows on the desktop, also when others look at it: quiet mode or
notify_kinds | macOS and Linux are CI smoke only.