todo: backend turn delivery exists — correct three stale entries

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-21 05:25:57 +00:00
co-authored by Claude Fable 5
parent 446f3fa64d
commit 8fa560df1b
+44 -16
View File
@@ -79,6 +79,36 @@ hold a platform checkout, three run it, and it records how to reach each one.
- The updater then does a checkout at that SHA, not a `pull` — same discipline the plugin-pinning - The updater then does a checkout at that SHA, not a `pull` — same discipline the plugin-pinning
item asks for: **verify `rev-parse HEAD` matches, or refuse.** item asks for: **verify `rev-parse HEAD` matches, or refuse.**
## Speaking into a session from the backend
**Correction, 2026-08-21: the door exists, and three entries in this file said it did not.** The claim
that `pushTurn` has exactly one caller (the chat websocket) is **stale**. `api/chat/nudger.ts:102` calls
`startClaudeTurn({ ws: null, … })` — a turn started server-side, with no browser attached, into a live
session. Whoever is watching the chat sees it arrive the same way they would see a turn typed in another
tab. `startNudger()` is wired at `server.tsx:182` and ticks once a minute against a `nudges` table.
So **both** "missing" pieces are built: a recurring trigger, and a way to put a prompt into a running
session. What remains is narrower, and worth doing.
- [ ] **Generalise it: deliver a turn to a named session, from anywhere in the backend.** Wanted
2026-08-21 — "ideal" is the owner's word for it. Today the only server-side caller is the nudger,
and delivery is welded to its schedule: a row in `nudges`, one per (user, session), a fixed
message, a tick. What the dispatcher, the file watchers and Command HQ all want instead is a plain
verb — *here is some text, deliver it to session X* — with the schedule, if any, belonging to the
caller.
Two things already established and worth not re-deriving. The nudger's `checkSafe` holds three
refusals — not live, live but generating, another live session in the same directory — and the
comments above it carry why each one is there. And "not live" is the case that differs by caller:
the nudger refuses (nudging a dead session would CREATE an agent, which is not what nudge means),
while a dispatcher handing over five todos does want it created.
- [ ] **The cost of resuming grows with the transcript.** Raised 2026-08-21 by the owner as a challenge
worth taking rather than a limitation to live with. Every resume re-reads the whole conversation as
input tokens, so a long-lived commander costs more per note than it did in week one, for identical
work. Measurable today rather than theoretical: `command_hq_turns` already records input tokens and
USD per turn, so the curve can be plotted before anyone argues about a fix.
## File watchers ## File watchers
- [ ] **Watch a directory, and attach actions to what lands in it.** Wanted 2026-08-21. The owner sets a - [ ] **Watch a directory, and attach actions to what lands in it.** Wanted 2026-08-21. The owner sets a
@@ -91,10 +121,10 @@ hold a platform checkout, three run it, and it records how to reach each one.
session open and no phone unlocked into the right screen. session open and no phone unlocked into the right screen.
What it needs, in rough order: a watcher registry (path + what to do), a trigger that fires on What it needs, in rough order: a watcher registry (path + what to do), a trigger that fires on
file arrival rather than on a clock, and a way to hand the file to an agent turn. Note the second file arrival rather than on a clock, and a way to hand the file to an agent turn. **The third is
and third are the **same two gaps the Nudger item names** — the platform has no periodic/event no longer missing** — see "Speaking into a session from the backend"; the nudger already starts a
trigger, and `pushTurn` has one caller, so nothing server-side can speak into a session. Build server-side turn with no browser attached. What is missing here is arrival-triggered firing, since
them once. the existing scheduler is a clock tick.
Open, not decided: whether the watcher is a plugin or core; whether "action" is a task, a Open, not decided: whether the watcher is a plugin or core; whether "action" is a task, a
prompt from the prompt library, or a chat message; and how a file is marked consumed so it is not prompt from the prompt library, or a chat message; and how a file is marked consumed so it is not
@@ -186,11 +216,11 @@ hold a platform checkout, three run it, and it records how to reach each one.
- **Inferred.** Nothing is named, and the dispatcher works it out from the content. This is the - **Inferred.** Nothing is named, and the dispatcher works it out from the content. This is the
one that makes the app worth using: the ideal is that most notes need no prefix at all. one that makes the app worth using: the ideal is that most notes need no prefix at all.
What this implies, and it is the same missing piece named twice already: **destinations must be What this implies: **destinations must be addressable and speakable.** "My todo guy" is a name for
addressable and speakable.** "My todo guy" is a name for a standing agent session, so the a standing agent session, so the dispatcher needs a registry of destinations with names a person
dispatcher needs a registry of destinations with names a person would actually say, and a door to would actually say. Command HQ is already most of that registry — `command_hq_sessions` names
deliver into a live session — which is the `pushTurn`-has-one-caller gap in the Nudger and File commanders and keys them on Claude's own transcript uuid. Delivery into a session exists too; see
watchers items. Three features now want it; it is the thing to build. "Speaking into a session from the backend".
Fits with the rest rather than duplicating it: the **File watchers** split applies unchanged — Fits with the rest rather than duplicating it: the **File watchers** split applies unchanged —
a script transcribes on arrival, the dispatcher is the agent stage — and the **iPhone Action a script transcribes on arrival, the dispatcher is the agent stage — and the **iPhone Action
@@ -959,13 +989,11 @@ outside the example's new README:
and speaks over the chat websocket, exactly as a browser does, so the platform needed no changes. and speaks over the chat websocket, exactly as a browser does, so the platform needed no changes.
That is the workaround, not the feature. That is the workaround, not the feature.
**Two things the platform genuinely lacks**, and they are the work: **Both things this said the platform lacked have since been built** (verified 2026-08-21): the
recurring trigger is `startNudger()` at `server.tsx:182`, ticking once a minute over a `nudges`
1. **A recurring trigger.** The queue has retries and delays but nothing periodic, and table; the door into a session is `startClaudeTurn({ ws: null })` at `nudger.ts:102`. What is left
`queue/handlers/index.ts` is empty. `officer-notify` is now a natural home for "fire this is the UI — a control in the chat composer, beside the background-job button — plus generalising
later" — it already runs as core and already fans out on a schedule-free signal. delivery beyond the nudger's own schedule. See "Speaking into a session from the backend".
2. **A way to put a prompt into a live session.** `pushTurn` has exactly ONE caller, the chat
websocket handler. Anything server-side that wants to speak to a session has no door.
Design notes worth keeping: Design notes worth keeping: