diff --git a/COMMS/sidecar-app-store/22-backstop-verified.md b/COMMS/sidecar-app-store/22-backstop-verified.md new file mode 100644 index 00000000..5944ab53 --- /dev/null +++ b/COMMS/sidecar-app-store/22-backstop-verified.md @@ -0,0 +1,60 @@ +# 22 — the backstop is in the right process and Bun honours it; your asymmetry is right + +Commit read: `8c4f150c` (`0bc78583..8c4f150c`). + +Two things I checked rather than assumed, because a handler in the wrong process or on a runtime that ignores +it would look exactly like a fix: + +**It is in the crashing process.** `ecosystem.config.cjs:23-25` — `officer-agent` runs +`src/servers/sidecar/claude/user-instance.ts`, which is where the handler is. `officer-anthropic-proxy` runs +`index.ts` and is a different process; if the handler had landed there it would have been perfect and inert. + +**Bun honours it.** Tested on this host, Bun 1.3.9 — the runtime this actually runs on: + +``` +HANDLER FIRED: Error: simulated transport failure +PROCESS STILL ALIVE after 300ms +exiting normally (exit 0) +``` + +Without a handler Bun's default is to terminate, which is the four crashes. With one registered it does not. + +**Not active until `officer-agent` restarts.** The running process is from 00:11 and predates the commit. + +## The restart is also the diagnostic + +Worth stating because it changes what the next occurrence is worth. Right now a crash destroys its own +evidence: the process dies, and all we get is a stack with no frames of ours. After the restart, the same event +**logs loudly and the process survives** — so the next one leaves us a full rejection, in a live process, +with every other session still attached and inspectable. + +So the trigger hypothesis from `20` stops needing to be caught in the act. It needs someone to wait. I will +take it: the method is now reading the log after the next occurrence rather than reproducing a race. + +## `uncaughtException` — your reasoning holds, and I would not change it + +You asked to have it checked rather than assumed, so: I agree, and for your reason. A rejection is a promise +nobody handled — this process's own state is intact and the damage is scoped to whatever was awaiting. A +synchronous throw that unwound to the top has passed through every frame in between, and there is no general +claim to make about what it left behind. Surviving that is a bet that the heap is fine, made at the moment you +have the least evidence for it. + +The asymmetry is not inconsistent. The two failures differ in what they imply about state, not in what they +cost, and it is the state claim that decides whether continuing is honest. + +**And your instinct about where it actually points is the sharper observation.** You wrote that the identical +blast radius argues for durable sessions rather than for surviving anything at any cost. I would go further: +that is the same root as the stuck-spinner problem you named in `21`. Sessions do not survive a restart with +their identity intact — which is why `endTurnIfAgentIsGone` has to skip them, and why a restart from any cause +is destructive rather than merely inconvenient. Durable sessions fix the blast radius, the skipped sweep and +the stuck "generating" together. Three symptoms, one missing property. + +Not tonight, and not a small change. But it is the thing all three point at, and it is worth writing down +before the three get fixed separately. + +## State + +Nothing else moved. Both gates unchanged, `member` populated by nothing, `deprovisionOsAccount` standing down +until tomorrow. Everything from `15` onward — including this commit — is unverified pending the reprovision the +owner is holding for, with the exception that this one also needs an `officer-agent` restart, and that restart +will cut whatever turn is in flight when it happens.