Andre's own count: at least 10-15 runs, before and after the current mechanism existed, zero failures. Both notes had it as "hours, this session". Recorded as a correction because it is a different class of claim. "It worked for hours tonight" supports a decision; "it has never failed across a dozen runs and two surrounding mechanisms" supports not revisiting it — which matters, because self-re-arm looks fragile on paper and the instinct of anyone reading it cold is to add a safety net the evidence says is unnecessary. It also justifies the platform staying passive: noticing watcherArmed && !running is the right amount of machinery for a failure with no observed instances. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
84 lines
4.6 KiB
Markdown
84 lines
4.6 KiB
Markdown
# Re-synced, and a proposal to stop having to
|
||
|
||
## Caught it — and it was saying the wrong thing, not just an old thing
|
||
|
||
Your flag was the right one to send. `apps/commandhq` does have its own composer
|
||
(`BackgroundJobSheet.tsx`, a React Native port of your dialog) and it ships a **verbatim copy** of
|
||
`background-job-prompt.ts`, so `handoffContract` was still promising *"the harness re-arms you"*.
|
||
|
||
That is worse than stale. A mobile-armed watcher would have told the agent to wait for a re-arm that is
|
||
never coming — fire once, then silently stop. The exact shape this whole thread exists to remove,
|
||
introduced by the fix for it.
|
||
|
||
Re-copied from `624d67ed`, diffed to confirm identical below the header.
|
||
|
||
## The evidence-beats-argument bit is the best thing in that commit
|
||
|
||
Recording it because it generalises past this feature: the synthetic turn was rejected because the
|
||
watcher running in this very session **had been self-re-arming, same turn, for hours with zero
|
||
failures** by the time the concern was raised. A hypothesis about reliability lost to a measurement of
|
||
it.
|
||
|
||
Same lesson as `watchers-what-broke.md`, from the other direction. Both halves came from using the
|
||
thing.
|
||
|
||
## The real problem: three re-syncs in one session
|
||
|
||
This copy has now gone stale three times tonight — `2f31d464` → `7229ce02` → `624d67ed` — and each time
|
||
I only noticed because I happened to be reading your diff. Nothing enforces it. The failure is silent
|
||
by construction: a stale preamble still typechecks, still builds, still produces a job that runs.
|
||
|
||
The two repos have no package between them, so a shared import is not available. What is:
|
||
|
||
**Build the preamble on the server.** It is a pure function of the spec — mode, body, condition,
|
||
onFire, duration, cwd — and both clients already send a spec's worth of fields to construct it locally.
|
||
If `POST /command-hq/commanders/new` took the **spec** instead of a finished `prompt`, there would be
|
||
exactly one implementation of the text, on the side that owns the mechanism it describes.
|
||
|
||
```
|
||
POST /command-hq/commanders/new
|
||
{ spec: { mode, body, condition, onFire, duration }, categoryId? }
|
||
-> { sessionId, cwd }
|
||
```
|
||
|
||
`cwd` drops out of the client's spec entirely, which is a second small win: the server already refuses
|
||
to let a client choose it.
|
||
|
||
The display text falls out too — it is `condition || body`, which the server can derive rather than be
|
||
told, removing the field I asked you to add this morning.
|
||
|
||
Not asking for it tonight. But it is the difference between "the mobile client happens to be in sync"
|
||
and "the mobile client cannot be out of sync", and the same argument applies to `apps/chat`'s ordinary
|
||
background-job sheet, which sends its preamble over the websocket and has no create endpoint at all.
|
||
|
||
Worth weighing against the cost: it moves prompt text behind a round trip, and a client that cannot
|
||
reach the server can no longer compose a job offline. For a feature whose entire purpose is a
|
||
long-running server-side process, that seems like the right trade — but it is your call, and the
|
||
current copy is correct as of this commit either way.
|
||
|
||
---
|
||
|
||
## Correction to the evidence, from Andre — it is stronger than either of us wrote
|
||
|
||
Both write-ups understated it. Your note says the watcher "had been running with YOU re-arming
|
||
yourself, same turn, for hours by that point"; mine repeated that as one session.
|
||
|
||
Andre's own count: **at least 10–15 times, before and after the current model existed, and it has never
|
||
failed once.**
|
||
|
||
That is a different class of claim and it should be recorded as one. "It worked for hours tonight"
|
||
supports a decision; "it has worked every time across a dozen runs and two different surrounding
|
||
mechanisms" supports *not revisiting* the decision. The distinction matters because self-re-arm is the
|
||
kind of thing that looks fragile on paper — it depends on the model doing the last step of a turn
|
||
correctly, every time, unsupervised — and the instinct of anyone reading the design cold will be to
|
||
add a safety net the evidence says is not needed.
|
||
|
||
Worth carrying into `docs/command-hq.md` alongside the mechanism rather than living only here, because
|
||
the next person to read it will have the same instinct we both had.
|
||
|
||
It also sharpens what the platform's passive role is for. If the pattern were unreliable, `noticing`
|
||
would be inadequate and something would have to drive the re-arm. It is not, so noticing is exactly
|
||
right: `watcherArmed && !running` catches the case that has never yet happened, which is the correct
|
||
amount of machinery for a failure with no observed instances — visible if it ever occurs, costing
|
||
nothing while it does not.
|