0034. Events carry their resource and actions; clients name an action and notify runs it as the person
Status: accepted, 2026-10-21
Context
A notification is a title, a body and one link. To act on it from a phone, a person opens a panel and finds
the object. The audit asked for buttons (roll back, restart, resolve, run again) in Telegram, push and the
hub, and the native apps need the same. If services sent signed URLs, each would invent its own signing,
expiry and replay checks, and an old button could undo a newer deploy. If clients called the operations
themselves, every client would need every service's routes, and a notification would carry something a
service could get people to call.
Decision
- Events carry a resource (service, kind, id, name) and up to four actions. An action is a link or an
operation of the emitting service, built from its contract: the method, a path under that service's own
/api/v1/, a small body, and its risk for this target, never critical. Operations are offered for 24 hours
unless the service says otherwise. An action that replaces what runs carries the state it was made for
(expect) and fails with stale_action once the target has moved on.
- Clients name the action; notify runs it with the caller's own token, forwarded as it came, so the
service's checks, approvals (ADR 0025) and journal apply exactly as if the person had called it. No client
calls an event's operation itself, and no service can make anyone call another service.
- Confirmation follows the risk on every surface: none and low run with one tap; medium and high name the
action and its target first, then approvals ask what the policy asks; critical is never offered. Only
notify's own low-risk actions (acknowledge, snooze) run from a notification without opening the app, through
one-time links bound to the channel and its person.
- Attention items, alerts and the inbox share these shapes. Today offers actions by reference and home
runs them the same way; the inbox is notify's per-person record of the events a person hears, with read
state, folded repeats and a live stream.
Consequences
- kit/events gains resources and actions with their checks, and kit/api builds actions from operations. notify
refuses unknown fields, so it must accept the new ones before any service sends them, and upgrades first.
- Each service declares the actions of its problem events and adds
expect to the operations that replace
what runs; notify records every run and updates every message that showed the event.
- Telegram, Web Push, the hub, home's desktop panel and the native apps render the same actions; e-mail keeps
links only.
- Design:
superpowers/specs/2026-10-21-phone-hub-design.md in this repository.