Files
kyleandClaude Fable 5.1 2be8581a0c Review M2b: accept with one follow-up task; record lessons
All ten tasks pass the checklist, the gate, the audit and the device
checks, including a four-turn conversation with a loopd restart and no
cache loss. Reading and probing found four low defects: the busy guard
is released before the final frame on the main path but not on the
three error paths, its Drop skips a poisoned lock, an unreadable
core.md is treated as missing, and bxctl's interactive loop exits on a
failed turn. Task 11 carries the fixes with two new tests, checked
against a fixed copy of the branch.

The Model column is filled in (all Ornith) and one malformed row is
repaired. Two rules are promoted to AGENTS.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 20:47:03 -07:00

4.3 KiB

M2b implementation plan: the agent loop

For the implementing model: do not work from this file. The owner gives you one task file at a time (01-… to 11-…). This file is the index for the owner and the reviewer.

Goal: loopd serve holds conversations: each turn's request extends the one before, sessions live on disk and survive a restart, tool calls go through a port with limits on every kind of runaway, and bxctl chat drives it all over loop.sock.

Architecture: On top of M2a's inference client. A session is a directory with a baseline snapshot and an append-only log; the request for every turn is rebuilt from them. The turn loop runs the model, dispatches tool calls through a ToolPort trait (a fake in process for now, brokerd in M3), and enforces the limits. The channel server runs one turn per connection with the M1 frame protocol. bxctl chat is a client of that protocol.

Tech stack: as M2a. No new dependencies. serde_json is added to bxctl.

Spec: docs/specs/2026-09-18-m2b-agent-loop.md. Brief: docs/design.md.

Global constraints

  • Everything in AGENTS.md, including "Lessons from earlier reviews".
  • No new dependency. Formats we define reject unknown fields (the baseline file, the config tables, the wire messages); the server's JSON does not.
  • Nothing is written to a session log until a completion is final. Nothing volatile is ever put in a message or the baseline.
  • Branch m2b. One task, one fresh OpenCode session, one commit. Run cargo fmt --all before the gate. Review happened once, after task 10; task 11 is its follow-up.

Tasks

# File Delivers Tests that define it
01 01-proto-channel-types.md Usage, LogRecord::Usage, Turn, TurnEvent, TurnDone, six error codes proto/tests/turn_wire.rs, updated strict.rs, fixtures
02 02-loopd-config.md [paths], [channel], [loop], [baseline], channel_socket() updated loopd/tests/config.rs, m2b.toml
03 03-loopd-tools.md ToolPort, Registry, dispatch, cap_result, FakeTools loopd/tests/tools.rs
04 04-loopd-baseline.md Baseline and messages() loopd/tests/baseline.rs, updated support/mod.rs
05 05-loopd-session.md Session: create, open, append, resume loopd/tests/session.rs
06 06-loopd-turn.md run_turn with every limit loopd/tests/turn.rs, limits.rs, support/turn.rs, three recordings
07 07-loopd-channel.md The channel server on loop.sock loopd/tests/channel.rs
08 08-loopd-serve.md loopd serve loopd/tests/serve.rs
09 09-bxctl-chat.md bxctl chat bxctl/tests/chat.rs
10 10-verify-device.md make verify-device extended; the first config/system.md loopd/tests/device.rs
11 11-review-fixes.md Review follow-up: busy release before every final frame, poison recovery, unreadable core.md, chat loop continues updated loopd/tests/baseline.rs, bxctl/tests/chat.rs

files/ holds everything the tasks copy in. As in M2a, all of it was 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, including a four-turn conversation with a loopd restart in the middle and no cache loss.

For the owner: running a task

tools/run-plan.sh docs/plans/M2b, or one fresh OpenCode session per task with "Read docs/plans/M2b/01-proto-channel-types.md and do exactly that task." Task 10 talks to straylight and takes about a minute.

For the reviewer: after task 10

  1. git log --oneline master..m2b: ten commits with the trailer.
  2. Copied files unchanged: for f in $(cd docs/plans/M2b/files && find . -type f); do cmp "docs/plans/M2b/files/$f" "$f"; done
  3. git diff master..m2b --stat -- docs/design.md docs/specs docs/plans AGENTS.md CLAUDE.md deny.toml is empty.
  4. make gate, make audit, make verify-device.
  5. Read every source file against its task and the spec. Probe from outside: a log edited by hand, two channels on one session, a tool result at the cap, a call_tool with odd arguments.
  6. Run channel, serve, limits and bxctl's chat repeatedly under CPU load.
  7. Findings to docs/implementer-log.md; lessons to docs/implementer-lessons.md.