diff --git a/src/servers/api/command-hq/directory.ts b/src/servers/api/command-hq/directory.ts index db4bd40a..06f72cd4 100644 --- a/src/servers/api/command-hq/directory.ts +++ b/src/servers/api/command-hq/directory.ts @@ -228,9 +228,14 @@ export function commanderClaudeMd(params: CommanderClaudeMdParams): string { ' subdirectory of `data/` named for that category. It changes where the item IS, never what it is.', ' Optional, and most commanders do not need it: it earns its place only when there are enough items', ' that finding one matters. It is a STEP, not a place — the place is always `data/`.', - '- **Dispatch** — doing something with a result beyond keeping it. Telling the owner, handing it to', - ' another agent, sending it somewhere. Optional, off until the owner arms it, and the only one of the', - ' four that acts outside this directory.', + '- **Dispatch** — anything that LEAVES this commander. Telling the owner, waking another conversation,', + ' handing work to an agent, sending something somewhere. Optional, off until the owner arms it, and the', + ' only one of the four that acts outside this directory.', + '', + ' Note what that includes: a notification saying classification could not place an item is DISPATCH,', + ' not classification. Classification decided where the item goes and it went to `unclassified/`.', + ' Telling somebody about it is a different step and its rule lives in `DISPATCHING.md`. The test is', + ' never what the message is about — it is whether anything left.', '', 'Storing the result is NOT dispatch. Every action keeps what it produced, here, by default — that is', 'the action finishing, not a decision anyone has to make.', diff --git a/src/servers/notifications/catalogue.ts b/src/servers/notifications/catalogue.ts index 905e3bc8..ed2faabc 100644 --- a/src/servers/notifications/catalogue.ts +++ b/src/servers/notifications/catalogue.ts @@ -127,8 +127,18 @@ const CORE_ENTRIES: Record = { A commander with something to say. ONE entry rather than one per commander, and the commander travels as `{id}` — so arming a new - commander needs no catalogue change and no app resync. `{id}` is the commander's row id, which is what - `officer://command-hq/{id}` opens. + commander needs no catalogue change and no app resync. `{id}` is the commander's DIRECTORY name, which + is what threads its notifications together. + + The route is the LIST, deliberately, and not `officer://command-hq/{id}`. That is what it was, and it + was wrong: `/command-hq/:sessionId` addresses a commander by its TRANSCRIPT id, so a deeplink built + from the directory name lands nowhere. Threading wants the directory name (stable from creation, before + any transcript exists); the route wants the transcript id (which does not exist yet when the commander + is created). Rather than make a commander fetch its own transcript id to send a notification, the tap + opens Command HQ and the owner picks — one tap worse, and correct. + + Worth revisiting when the app handles deep links properly: the honest fix is a route that accepts a + directory name, not a commander doing a lookup before it can speak. This entry is what a commander sees when it sends nothing but a code and an id: "Voice Memos" would be a lie without knowing which one, so the generic is honest and the deeplink does the rest. In practice a @@ -163,7 +173,7 @@ const CORE_ENTRIES: Record = { title: 'Commander: {title}', body: '{body}', target: 'mobile', - route: 'officer://command-hq/{id}', + route: 'officer://command-hq', thread: 'commander:{id}', }, };