Close the remaining M3a spec review findings

brokerd internals: the ledger lock over the audit writer and session
state, the pending table's take-it-and-answer rule, sticky
audit_unavailable after a failed write, pure policy functions with an
Ask type that only redecide turns into a Decision, how a waiting
thread detects a lost connection without peek, fixed RunError text.

Approvals: bxctl chat fetches the block from brokerd by id, shows the
parsed arguments with invisible and bidi characters escaped, asks for
the approval id instead of y, and escapes model text. A tool_denied
event carries the reason to the owner.

Also: BrokerPort timeouts, tests for runbook pointers and concurrency,
threat-model notes, P14 for broker/sessions in the brief's State list.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 22:18:13 -07:00
co-authored by Claude Fable 5.1
parent 3492734434
commit 477e28a759
3 changed files with 230 additions and 52 deletions
+13 -8
View File
@@ -69,9 +69,12 @@ file system.
**Fix.** Free space or correct ownership (`chown` to the `brokerd` user; files are mode 0600, the
directory 0700). Do not edit, move or delete audit files to make space: that breaks the chain.
Then stop and start `brokerd`. It does not try again by itself: a failed write may have left part
of a line, and only the startup check deals with that. Expect
[audit-recovered](#audit-recovered) at that start.
**Check.** Ask for any tool call. It is decided normally, and `bxctl audit verify` prints
`audit: ok`. If `brokerd` keeps failing after the cause is gone, stop and start it.
`audit: ok`.
## audit-chain-broken
@@ -205,12 +208,14 @@ again.
## broker-unavailable
**What you see.** `loopd` prints that it cannot reach `broker.sock`, then this entry. Tool calls
fail with "the tool broker is unavailable", the model says so, and the conversation goes on.
**What you see.** `loopd` prints that it cannot reach `broker.sock`, or that `brokerd` did not
answer in time, then this entry. Tool calls fail with "the tool broker is unavailable", the model
says so, and the conversation goes on. Or, once at startup, `loopd` prints "no tool broker is
configured" and this entry, and tool calls fail with those words.
**Why.** `brokerd` is not running, was restarted during a call, or `loopd`'s `[broker] socket`
points somewhere else. Pending approvals are lost when `brokerd` restarts. `bxctl audit verify`
lists them as abandoned.
**Why.** `brokerd` is not running, was restarted during a call, did not answer within `[broker]
timeout_ms`, or `loopd`'s `[broker] socket` points somewhere else or is not set. Pending approvals
are lost when `brokerd` restarts. `bxctl audit verify` lists them as pending or abandoned.
**Confirm.**
@@ -222,8 +227,8 @@ ls -l "$BOXMAKER_HOME/run/loop-broker/"
Compare the socket path with `[broker] socket` in `loopd`'s config and `[sockets] broker` in
`brokerd.toml`.
**Fix.** Start `brokerd`, or make the two paths agree. `loopd` needs no restart: it connects per
call.
**Fix.** Start `brokerd`, or make the two paths agree. `loopd` needs no restart for that: it
connects per call. A changed or newly set `[broker] socket` does need a `loopd` restart.
**Check.** Ask the model to use a tool. The call is decided (allowed, asked or denied) instead of
failing.