44 files of prose — CLAUDE.md, AGENTS.md, TODO.md, 20 docs, both plugin design
documents, and the comment surface the earlier steps could not reach.
Applied against an explicit keep-list, not swept, because the word turned out to
have SIX meanings in this repository rather than the three the offscale doc
recorded:
permissions renamed (steps 1–2)
$OFFICER_ROOT/capabilities/ KEPT — the item store, and now the only thing
the word means that is ours
sidecar routing keys renamed to `handles` (step 3)
Lightning wallet KEPT — a domain term, and on the wire to the mobile apps
terminfo queries KEPT — XTGETTCAP, in the pty sidecar
InvoiceShelf KEPT — per-resource { write, bulkDelete } flags
The sweep still falsified two things, both caught by checking rather than by
review, and both in prose that discusses more than one meaning at once:
CLAUDE.md began claiming the item store lives at `$OFFICER_ROOT/permissions`.
It does not; that directory is on disk and full of skills and tools.
And the offscale doc's own note about the collision became
"Named `permissions`, NOT `permissions`" — a sentence that had eaten the thing
it existed to warn about.
Both restored, and the note rewritten to say what is now true: capability means
one thing of ours, and three that belong to somebody else's vocabulary.
Verified live after restart: self and admin permission endpoints 200, gated
route 200, agent-status 200, 9 grants intact with 6 permissions offered.
tsgo clean, 797 tests, 787 pass, same 7.
The rename is done. Four steps, no data lost, no client break that survived
the step it was introduced in.
CLAUDE.md asserted "single-user is a hard invariant, not a stage" while
users held six rows and role_capabilities held grants. Every doc that
repeated it is corrected here, in prose and in the code comments that
carried the same claim.
The accurate statement is narrower: one owner who bypasses every check,
other accounts holding only what their role is granted, and a set of
capabilities — terminal, chat, files, tasks, items, desktop, browser — that
are structurally ungrantable because they execute as the owner's OS user.
TODO.md gains a Multi-user section for what the read turned up: no way to
create a second account, dashboards.id colliding across users, authorize.ts
untested, pty/vault/opencode taking no identity, Radicale still owner_only.
claude-sidecar-isolation.md's open question is answered rather than left
open — the per-email spawn model is dead weight, because chat is an
execution capability and no second account can ever reach it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The chain was: a Stop hook in Claude's settings curls POST /api/hooks/claude-done,
the platform POSTs /_officer/panel-refresh to the pty sidecar, the sidecar sends a
`panel-refresh` frame to every attached terminal, and the Claude Code panel bumps
`preview:refresh` and `files:refresh-signal`.
It has never fired. generateClaudeSettings writes settings.json into the MANAGED
home under DATA_PATH, but HOME_DIR points terminals at the owner's real login home
— which is where Claude reads its settings from. Verified on this machine: no
claude-done hook exists in ~/.claude/settings.json, and DATA_PATH/*/home/.claude
does not exist at all.
Deleting rather than repairing it, because the Chat panel already does exactly this
job from onTurnComplete — in-process, conditioned on the turn having made tool
calls, with no hook, no HTTP round trip, and no endpoint. The chat UI is where agent
work happens; the terminal TUI is not the destination.
Also removes /api/hooks/claude-done, which was mounted above protectedRouter and so
was the one unauthenticated write-ish endpoint on the API surface.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The root held four Markdown files that were not in any git repo and were being read as
current. CLAUDE_SIDECAR_ISOLATION.md is the one that prompted this: it describes, in the
present tense, an agent that dies whenever officer restarts. That was true when it was
written and has not been true for two days.
Rather than delete analysis that version control was not holding, the two substantial ones
moved into platform/docs/ with headers that say what has since happened:
- claude-sidecar-isolation.md — stages 0-2 are done and running (R1, R2, R4, R5 all
satisfied); stages 3-5 are the only live part.
- sidecar-audit-2026-07.md — a snapshot audit, largely executed. Email, pty, music and vnc
have been done since; claude, opencode and the cross-cutting notes are still open. The
vault stays off-limits.
MUSIC_IMAGES_SPEC.md is deleted outright: the sidecar serves /image and /poster, so the
spec is the feature. The workspace root now holds one Markdown file, CLAUDE.md, which is
where cross-cutting operational reality belongs.
Also corrected, in the same pass:
- root CLAUDE.md listed email as "the big one, and untouched" and pty stage 4 as
outstanding; both are done. It now names what actually remains (claude stages 3-5, the
terminal orphan leak) and what landed.
- docs/sidecar-topology.md said "nothing built yet". Two sidecars now serve their own
transport; what has NOT happened is the part the document is about — fixed ports, the
shared table, .env toggles — so it says exactly that rather than implying the design is
underway. Migration order updated: pty and email went first, not music.
- docs/navigation-audit.md is cited as authoritative but still planned work on Projects,
a feature since deleted. A status header marks H3 void, H1/H2 done and H4 the one open
item, so nobody follows it into a directory that no longer exists.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>