retire two phantom gaps, and plan the migration

Crash-recovery state is not a gap: state:sync goes to the proxy capability and carries
proxySecret, and syncState/getCachedState have no callers at all. The row compared opencode
against a mechanism officer never consults. The real recovery story now exists and is better
— a sidecar restart stops in-flight turns and writes the reason to chat_session_events.

Identity is deferred, not forgotten: TODO.md already records it, and chat is kind execution,
which the grants API refuses to share at any level, so no member can reach it.

Also adds the serve migration plan, written while the facts are fresh and nothing is on
fire. It leads with the five things that will bite whoever implements it — per-request
location, the data wrapper, delivery defaulting to steer, silent failure on an unconnected
credential, and the session.next event names — because none of them are in the API docs and
each cost time to find today.

Phased so the old path stays one config flip away, and so warm-session lifetime (idle GC,
orphan adoption, the supersede race) is imported deliberately rather than discovered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 18:27:23 +01:00
co-authored by Claude Opus 5
parent 86fd03b35b
commit 38ebe168f0
2 changed files with 99 additions and 1 deletions
+13 -1
View File
@@ -102,6 +102,18 @@ passed that one model.
Until the model question is answered, turns stay on `opencode run --dir`, which is verified working on
1.18.16.
**Crash-recovery state is not a gap either.** `state:sync` is sent to the `proxy` capability and carries
`proxySecret` — it is the Anthropic proxy s state, not a chat recovery record — and `syncState` /
`getCachedState` have **no callers at all** outside `sidecar-registry.ts`. The row compared OpenCode
against a mechanism officer never consults. The real recovery story now exists and is better: a sidecar
restart stops in-flight turns and writes the reason to `chat_session_events`, and `/chat/live`
enumerates what is running.
**Identity is correctly deferred, not forgotten.** `TODO.md:40-47` already records that `pty`, `vault`
and `opencode` receive no identity and are covered today only because those capabilities are owner-only —
"a correct outcome resting on the wrong layer". `chat` is `kind: execution`, which the grants API refuses
to share at any level, so this cannot be reached by a member. It is latent by construction.
**`messageCount` is a non-issue, not a gap.** `SessionList.tsx:197-203` renders an `OpenCode` badge in
place of the count for OpenCode rows, so the hardcoded `0` is never displayed. Computing a real count
would cost one HTTP call per listed session — the session record carries no count field — to populate
@@ -135,7 +147,7 @@ Ordered roughly by user-visible value.
| MCP tools | `--mcp-config` | **Nothing** — no MCP anywhere in the OpenCode path | No |
| `messageCount` on the list | from the transcript | hardcoded `0` | No |
| Idle GC / warm-session lifetime | 30-min heartbeat, task-aware | N/A — nothing warm to collect | **Yes** |
| Crash-recovery state on disk | `claude-state.json` | **None**`state:sync` returns an error | No |
| Crash-recovery state on disk | `claude-state.json` | **not a gap — see below** | No |
| Identity | validates `X-Officer-User` | **None** — flagged in `TODO.md:42-47` | No |
| Tests | 4 test files on the pure pieces | **Zero** | No |