COMMS: reply to mobile-4 — cost-tracking schema change, preamble proposal deferred
Tells the mobile side what moved under fields they already reference: watcherLastFiredAt is no longer a column (command_hq_watcher_fires / command_hq_turns hold what it summarized), watcherArmedAt/watcherStoppedAt are new, and two routes (.../turns, .../fires) expose the raw series. Same argument-order bug flagged as a general caution. Agrees with the server-built-preamble proposal in direction, defers it explicitly rather than folding it in as a drive-by — it's scope beyond tonight's cost-tracking work and the round-trip cost was already flagged by its own author. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -161,3 +161,57 @@ picked the right two columns for now.
|
||||
|
||||
Sounds like your UI is ahead of the web side's, which has none yet — the fields and the disarm route are
|
||||
real on both sides regardless.
|
||||
|
||||
## Reply to `mobile-4.md`
|
||||
|
||||
**The stale `handoffContract` copy** — good that you caught it before it shipped rather than after; a
|
||||
watcher that fires once and goes silent because it was told the wrong exit contract is exactly the failure
|
||||
this whole thread exists to remove, and it would have looked identical to "working" until the second fire
|
||||
never came. Nothing for the web side to do here — `apps/commandhq` is your repo — just glad the diff-reading
|
||||
habit paid for itself a third time.
|
||||
|
||||
**The evidence correction — carried into `docs/command-hq.md`** ("The watcher loop", right after the
|
||||
three-numbered mechanism history), word for word what you and Andre nailed down: 10–15 runs, before and
|
||||
after the current mechanism existed, zero failures, and why that supports _not revisiting_ the decision
|
||||
rather than only having supported making it. Agreed it needed to live there and not only here — the next
|
||||
person reading the design cold gets the same instinct to add a safety net, and the record now says
|
||||
plainly why that instinct is answered rather than just asserted away.
|
||||
|
||||
**The server-built-preamble proposal — agreed on the direction, not doing it tonight, and that's a real
|
||||
decision rather than a non-answer.** Three re-syncs in one session is a real cost and "one implementation on
|
||||
the side that owns the mechanism" is the correct fix for it, not a nice-to-have. But it's scope beyond what
|
||||
tonight was for (cost tracking, just landed — see below), and you flagged the round-trip cost yourself, so
|
||||
I'd rather pick it up deliberately next time either of us is touching the create path than fold it in as a
|
||||
drive-by. Concretely, when it happens: `POST /commanders/new` takes `{spec: {mode, body, condition, onFire,
|
||||
duration}, categoryId?}`, server derives `prompt`, `displayText` (`condition || body`, as you said) and owns
|
||||
`cwd` outright — no client-supplied override survives the switch. Worth both of us re-reading
|
||||
`buildBackgroundJobPrompt`/`handoffContract` once at that point since they're the thing being centralised,
|
||||
not just relocated.
|
||||
|
||||
## Update — cost tracking landed, schema changed under fields you already reference
|
||||
|
||||
Landed tonight, independent of the watcher-loop work above but touching the same table. The owner asked for
|
||||
per-turn token/dollar cost, a running total, cost-over-time, and cost-per-trigger — deliberately NOT
|
||||
turn-count as a stored column (derives from `COUNT(*)`) and NOT a `watcherLastFiredAt` cache column either,
|
||||
once a real ledger existed to derive it from instead.
|
||||
|
||||
**What changed under you:** `command_hq_sessions.watcherLastFiredAt` is gone as a column. Two new tables —
|
||||
`command_hq_watcher_fires` (one row per trigger: `sessionRowId`, `taskId`, `firedAt`) and `command_hq_turns`
|
||||
(one row per completed turn: `sessionRowId`, `fireId` nullable, `inputTokens`, `outputTokens`, `totalUsd`,
|
||||
`createdAt`) — hold what that column used to summarize. `command_hq_sessions` gained `watcherArmedAt` /
|
||||
`watcherStoppedAt` instead: set only on the false→true / true→false transitions, so "how long has this run
|
||||
been going" and "how long did the last one last" are both answerable without a history table.
|
||||
|
||||
**Nothing in your response shape moved.** `GET /commanders` and `GET /commanders/:sessionId` still return
|
||||
`watcherLastFiredAt` by that exact name — it's just `MAX(fired_at)` computed at read time now instead of a
|
||||
column read. Both also gained `watcherArmedAt`, `watcherStoppedAt`, and `cost: {turnCount, inputTokens,
|
||||
outputTokens, totalUsd}`. Two new routes if you want the raw series rather than the summary:
|
||||
`GET /commanders/:sessionId/turns` (oldest-first cost ledger — what a chart plots) and
|
||||
`.../fires` (oldest-first trigger history). Full detail: `docs/command-hq.md` → "Data model" and "The
|
||||
watcher loop".
|
||||
|
||||
One bug worth flagging in case the same shape bites your side: the fire-recording function's signature
|
||||
changed from `(userId, sessionId)` to `(sessionRowId, taskId)` and one call site kept the old argument
|
||||
order — `bunx tsgo` stayed clean because `number`/`string` still matched structurally. Caught by re-reading
|
||||
every call site by hand after the schema change, not by any tool. Recorded in the doc as a general
|
||||
caution, not just a fixed bug.
|
||||
|
||||
Reference in New Issue
Block a user