Add M2b plan: ten tasks, tests, recordings and the first system prompt

The tasks build the agent loop on M2a's client: channel messages and
the usage record in proto, four config tables, the tool port and
registry with find_tool and call_tool, the baseline and replay, the
session store, the turn loop with its limits and the append-only
property test, the channel server, loopd serve, bxctl chat, and the
device checks including a four-turn conversation with a restart.

Checked against a private reference implementation: the gate passes
after every task in order, the new suites pass under CPU load, and the
reference passes make verify-device on straylight with no cache loss.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 17:19:02 -07:00
co-authored by Claude Fable 5.1
parent f238e6a260
commit e156975649
41 changed files with 4883 additions and 3 deletions
+7 -2
View File
@@ -142,11 +142,13 @@ Events for one turn arrive in order on one connection. Nothing else is promised.
`bxctl chat [--socket <path>] [--session <id>] [--no-thinking] [--say <text>] [--json]`
- A `--session` id that does not exist yet is created (the first turn is retried with
`resume: false` when `loopd` answers `no_such_session`), so scripts can choose their ids.
- Prints the session id, then reads one line at a time from stdin and runs a turn per line.
Content is printed as it streams; reasoning is printed dimmed; each tool call is one line;
`waiting`, `retrying` and `thinking_capped` are one status line each. EOF or `/quit` ends it.
- `--say` runs one turn and exits with the answer on stdout and status 0, or the error on stderr
and status 1.
and status 1. Its events go to stderr, so stdout carries only the answer.
- `--json` prints every frame body as one JSON line, for scripts.
- No readline, no history. The socket path defaults to `<BOXMAKER_HOME>/run/loop/loop.sock`.
@@ -155,7 +157,10 @@ Events for one turn arrive in order on one connection. Nothing else is promised.
`loopd serve --config <path>`: runs the self-test and exits 1 if it fails; binds `loop.sock`
(from config, `[channel] socket`) with mode 0600; accepts connections, one thread each. The M2a
`Client` is shared in an `Arc`; its slot gate serialises requests per slot. A registry of
per-session locks makes a concurrent `turn` on a busy session answer `session_busy` at once.
per-session locks makes a concurrent `turn` on a busy session answer `session_busy` at once; the
lock is released before the final frame is sent, so a channel that sends its next turn on reading
that frame is never refused. A stale socket file is removed before the self-test runs, so clients
see "not running" rather than "connection refused" during startup.
Nothing is held in memory that is not also on disk, except the locks and the `call` counters.
Config additions: `[channel] socket`, `[paths] home` (default `BOXMAKER_HOME` or