The model has been asking questions in Officer all along. None of them ever rendered. MessageBubble dispatched the interactive question UI on `toolName === 'question'` — OpenCode's name for it. Claude Code's tool is `AskUserQuestion`, so every question from a Claude session fell through to the generic ToolActivity and printed its input as JSON in a bubble. Nothing threw, nothing logged, and the payload was arriving correctly the whole time, so it read as "the model never asks" rather than as a bug. Both harnesses send the same shape under different names; only the name was ever wrong. ── What this deliberately does NOT do ── It does not make the agent wait. Checked against the installed SDK rather than assumed: AskUserQuestion is implemented inside the bundled cli.js (with its own "No answer provided" fallback), appears nowhere in sdk.d.ts, and there is no control method or hook that can return a tool RESULT. canUseTool and PreToolUse can only allow or deny, and canUseTool is shadowed entirely under bypassPermissions, which is how Officer runs. So the tool self-resolves as unanswered the moment it is called, whatever the client does. The two ways to pretend otherwise — forging a tool result, or smuggling the choice through a denial reason — both lie to the model about what happened. Neither is here. The question is rendered, and the answer goes back as the next message, exactly what a person would have typed. The tool contract is untouched. The cost is real and stated in the file: the agent will usually have carried on and reads the answer a beat late. The day the SDK grows an answer channel, this component is the piece that already knows how to ask. ── Also fixed while in there ── State was single-valued and shared across questions, but the schema allows up to four. With more than one, the first click sent a partial answer and locked the rest. Now keyed by question index: nothing is sent until every question has an answer, and a multi-question reply is labelled by header so it cannot be ambiguous. Multi-select joins with commas, which is what the tool's own output schema documents. `multiSelect` (Claude Code) and `multiple` (OpenCode) are normalised in one place rather than at each read site. 6 tests pinning both harness shapes, the multi-question rule, the client-side "Other" escape hatch (both schemas tell the model not to offer one, because providing it is the client's job), and that an already-resolved question is read-only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The documentation, triaged
2026-08-13. A map of what is in here, what it is for, and what should happen to it. Made because there are 42 documents and 13,000 lines, and no way to tell from the filenames which describe the system as it is and which are a record of an afternoon in July.
How much I verified: the classifications below are from filenames, status lines, and greps for things that changed on 2026-08-13. Where I actually read the document or checked the code, it says so. The rest is a starting point for a conversation, not a verdict.
Living — these describe the system and must stay true
| doc | state |
|---|---|
working-on-officer.md |
updated 2026-08-13. Operational guide. |
secret-store.md |
updated 2026-08-13. Built; rotation still open. |
install-variants.md |
new. The branch tree, for discussion. |
http-secure-context-audit.md |
new. What breaks over plain http. |
install-container-testing.md |
new. First container pass and its findings. |
per-user-linux-accounts.md |
partly updated. OFFICER_OS_USERS is gone; check the rest. |
navigation-audit.md |
authoritative on routing. Unverified against tonight's route removals. |
workspace-panels.md + workspace-panel-todo.md |
the panel framework. 1,300 lines combined — likely the biggest cleanup here. |
agent-coordination.md |
the north star for panel work. |
deprovision-os-account.md |
implemented; the 'disabled' stage it may mention was deleted tonight. |
Stale — describe things that changed on 2026-08-13
Each of these references something that no longer exists. Not yet corrected.
sidecar-topology.md— "ecosystem.config.cjs is the source of truth". It is generated now, and holds six processes.sidecar-app-store.md— derives the catalogue fromfull − light. Those files are gone, andcatalogue.test.tswas rewritten.sidecar-bootstrapping.md— "20 PM2 entries, 18 sidecar dirs". Six entries now.mobile-api-keys.md— partly corrected; recheck the origin-checking claims.wallet-key-custody.md—VAULT_STORE_KEYis now the per-purposewalletkey.push-notifications.md— "agreed design, 2026-07-31". Notify is a plugin and unmounted.chat-session-lifetime.md,chat-ui-walkthrough.md— referenceofficer-agent, renamed.
Historical — a record of a moment, and should stay one
Do not rewrite these to match today's code. They document how a decision was reached, and editing them destroys the reasoning. If they mislead, add a dated header pointing forward.
sidecar-audit-2026-07.md(1,377 lines)claude-sidecar-isolation.md— records theofficer-claude→officer-agentrename that preceded tonight'sofficer-agent→officer-claude-codeopen-threads-after-per-user-claude.mdtwo-agent-field-report-2026-08-12.mdapi-method-changes-2026-08-06.md
The opencode cluster — nine documents for one migration
opencode-fork-decision · -parity · -api-2-assessment · -phase0-review · -phase1-report ·
-phase1-review · -serve-migration-plan · -serve-path · -testing-checklist
The migration landed — opencode serve is in the sidecar, verified. So
opencode-serve-migration-plan.md saying "Nothing here is implemented" is false.
This is the clearest consolidation candidate in the whole directory: one document recording what was decided and what shipped, replacing nine that describe stages of getting there. I did not do it because it needs reading all nine, and deleting documents unread is not a thing to do at 4am.
The mobile-dav thread — three documents, one conversation
mobile-dav-provisioning · -feedback · -reply. A correspondence. Almost certainly one document.
Unclassified — I have not looked
design-language-interface · file-sync · jobs-unification · mobile-photo-sync-api ·
nextcloud-replacement · agent-git-identity
The plugin split, which affects most of the above
A core install is six processes. Everything else is a plugin, switched off tonight but present on disk. Most documents here were written when the estate was twenty processes and every one of them was simply "there", so they describe availability that no longer holds.
The useful rewrite is usually one line, not a rewrite: say whether the thing described is core or a plugin, and if a plugin, that it is not mounted on a fresh install.