chat notifications reach the phone, on the same rule as the bell
the browser bell and phone push are now two subscribers to one signal. both
ask shouldNotify(); neither owns the rule.
deliberately a second subscriber rather than a branch inside notify-delivery.
the two channels share only the question "does this account want to hear about
this" — the browser needs an open tab and sends the chat's real title over a
loopback socket on the owner's own tailnet, the phone needs no tab and must
send NO title, because that hop goes through apple. one function doing both
means an `if (isPush)` at every step, and the first thing to go wrong is a
title leaking because the branch was missed in one place.
two codes, officer/core:chat.turn-ended and chat.input-needed. the route is
written out with its scheme rather than derived from the target: the hub has
no chat screen — chat is Butler, a sibling app — so these deep-link OUT over
officer-chat://, and two of the suite's twenty-one schemes already break the
officer-<app> pattern (officer-videos, officer-readaloud). deriving that works
until it silently does not.
`thread` interpolates now, so `chat:{id}` groups per conversation. a static
thread forces a choice between "three pings from one chat collapse" and "two
busy chats stay apart", and the wrong answer is that every chat is the same
chat.
dropped officer/core:agent: chat.turn-ended supersedes it and its route
pointed at officer://chat/{id}, a hub screen that does not exist.
the banner cannot name which conversation. a chat's title is content and
content does not travel; it says what happened, and tapping opens the chat,
which is where the name lives.
This commit is contained in:
@@ -34,7 +34,13 @@ export type NotificationEntry = {
|
||||
target?: string;
|
||||
/** The deep link, `{id}` interpolated. */
|
||||
route?: string;
|
||||
/** Grouping key, so like-notifications collapse rather than stack. Defaults to the code's namespace. */
|
||||
/**
|
||||
* Grouping key, so like-notifications collapse rather than stack. Defaults to the code's namespace.
|
||||
*
|
||||
* A TEMPLATE, like the others — `chat:{id}` groups per conversation, so two busy chats do not collapse
|
||||
* into one banner while three pings from the SAME chat still do. A static thread would force a choice
|
||||
* between those, and the wrong one is "every chat is the same chat".
|
||||
*/
|
||||
thread?: string;
|
||||
};
|
||||
|
||||
@@ -58,6 +64,29 @@ export type Catalogue = {
|
||||
* on a machine that has never installed anything, because it is what first light is proved with.
|
||||
*/
|
||||
const CORE_ENTRIES: Record<string, NotificationEntry> = {
|
||||
// ── Chat ──
|
||||
//
|
||||
// The hub has no chat screen; chat is Butler, a sibling app. So these route OUT of the hub over
|
||||
// `officer-chat://`, and the scheme is written here rather than derived from the target: two of the
|
||||
// suite's twenty-one schemes already break the `officer-<app>` pattern (`officer-videos`,
|
||||
// `officer-readaloud`), so deriving it works until it silently does not.
|
||||
//
|
||||
// The banner cannot name WHICH conversation — a chat's title is content, and content does not travel.
|
||||
// It says what happened; tapping opens the chat, which is where the name lives.
|
||||
'officer/core:chat.turn-ended': {
|
||||
title: 'Chat',
|
||||
body: 'Finished its turn',
|
||||
target: 'chat',
|
||||
route: 'officer-chat://session/{id}',
|
||||
thread: 'chat:{id}',
|
||||
},
|
||||
'officer/core:chat.input-needed': {
|
||||
title: 'Chat',
|
||||
body: 'Needs your input',
|
||||
target: 'chat',
|
||||
route: 'officer-chat://session/{id}',
|
||||
thread: 'chat:{id}',
|
||||
},
|
||||
'officer/core:test': {
|
||||
title: 'Officer',
|
||||
body: 'Test notification',
|
||||
@@ -76,12 +105,6 @@ const CORE_ENTRIES: Record<string, NotificationEntry> = {
|
||||
route: 'officer://jobs/{id}',
|
||||
thread: 'officer/core:job',
|
||||
},
|
||||
'officer/core:agent': {
|
||||
title: 'Agent finished',
|
||||
target: 'mobile',
|
||||
route: 'officer://chat/{id}',
|
||||
thread: 'officer/core:agent',
|
||||
},
|
||||
};
|
||||
|
||||
/** FNV-1a, 32-bit. Small, stable across processes, and not a security boundary. */
|
||||
|
||||
Reference in New Issue
Block a user