Files
boxmaker/docs/plans/M3b/README.md
T
kyleandClaude Opus 5.5 5e55fe4c66 M3b plan: every task's git add includes Cargo.lock
Task 04 added dependencies to toolkit and its git add line left out the lock
file, so the driver stopped on an unclean tree. The lock change is folded into
task 04's commit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 00:00:12 -07:00

71 lines
4.5 KiB
Markdown

# M3b implementation plan: the runner and the tools
> **For the implementing model:** do not work from this file. The owner gives you one task file at
> a time (`01-…` to `13-…`). This file is the index for the owner and the reviewer.
**Goal:** an allowed tool call runs in a fresh rootless Podman container, with no network unless
it is `http_fetch`, which reaches the grant's hosts only through an egress proxy of our own. The
four tools and the proxy are programs in `toolkit`; `brokerd` builds the `podman` argument lists
and runs them.
**Architecture:** shared definitions move to `proto` first (01), the fetch target is sealed (02),
and grant paths that cannot be mounted are refused (03). `toolkit` gets its four tools (04 to 06),
the public-address check (07) and the proxy (08). `brokerd` gets `[runner]` (09), the argument
lists (10), the runtime (11), the proxy's lifecycle (12) and the wiring in `serve` (13).
**Spec:** `docs/specs/2026-09-22-m3b-runner.md`. Brief: `docs/design.md`. Every fail-closed
message ends with a pointer into `docs/runbook.md`; the `runner-unavailable` entry is already
there.
**No task needs Podman, Nix or a network.** Everything runs against fakes: fake `curl` and
`podman` scripts, a fake resolver, `UnixStream::pair()`. The image and the checks on straylight
are done by the design model afterwards.
## Global constraints
- Everything in `AGENTS.md`, including "Lessons from earlier reviews".
- No new dependency. `toolkit` gains `serde` and `serde_json`, both already vetted (task 04).
- Branch `m3b`. One task, one fresh OpenCode session, one commit. Run `cargo fmt --all` before the
gate. Review happens once, after task 13.
- Tests that write a script and run it take a lock (`serial()`): otherwise another test's fork
can make running it fail with "text file busy". Keep that pattern in anything you add.
## Tasks
The last column is how the given tests were checked before hand-over (decision of 2026-09-18, tip
T17). For M3b every task had a reference implementation: the tests of each task were run against
it at that task's end state, then the reference was deleted so it cannot be read (tip T18).
| # | File | Delivers | Tests | Check |
|---|---|---|---|---|
| 01 | `01-proto-tools-hosts.md` | `proto::tools`, `proto::hosts`; `brokerd::args` uses them | `proto/tests/tools.rs`, `hosts.rs` | reference; the given test pinned an accepted serde weakness, removed |
| 02 | `02-brokerd-fetch-url.md` | `ToolArgs::HttpFetch(FetchUrl)`, sealed (M3a finding 14) | `brokerd/tests/args.rs` (changed), a `compile_fail` doctest | reference |
| 03 | `03-brokerd-grant-mount-rule.md` | grant paths with `:` or `,` are invalid | `brokerd/tests/grants_mount.rs` | reference |
| 04 | `04-toolkit-files.md` | the `toolkit` program, `read_file`, `write_file` | `toolkit/tests/files.rs`, `support/mod.rs` | reference |
| 05 | `05-toolkit-shell.md` | `shell` | `toolkit/tests/shell.rs` | reference |
| 06 | `06-toolkit-fetch.md` | `http_fetch` through `curl` | `toolkit/tests/fetch.rs` | reference; found the ETXTBSY race (1 in 7 runs), fixed with `serial()` |
| 07 | `07-toolkit-addr.md` | `is_public` | `toolkit/tests/addr.rs` | reference; found `::/96` missing from the spec, added |
| 08 | `08-toolkit-egress-proxy.md` | the SOCKS5 egress proxy | `toolkit/tests/egress.rs` | reference; 20 runs clean |
| 09 | `09-brokerd-runner-config.md` | `[runner]` | `config_runner.rs`, 5 fixtures, `support/rig.rs` (changed) | reference |
| 10 | `10-brokerd-podman-args.md` | the `podman` argument lists; `RunSpec` gains session and call | `podman_args.rs`, 6 golden files | reference |
| 11 | `11-brokerd-container.md` | the Podman runtime | `container.rs`, `support/fake_podman.rs` | reference; 20 runs clean |
| 12 | `12-brokerd-egress.md` | the proxy's lifecycle for `http_fetch` | `container_egress.rs` | reference; 20 runs clean |
| 13 | `13-brokerd-serve-runner.md` | `brokerd serve` uses the runtime | `serve_runner.rs` | reference |
At the end: `make gate` prints `gate: ok` with about 638 tests.
## Changes during the run
- 2026-09-23, after task 04: task 04 adds dependencies to `toolkit`, which changes `Cargo.lock`, but
its `git add` line left the lock out, so the driver stopped on an unclean tree. The design model
folded the lock into task 04's commit and added `Cargo.lock` to every task's `git add` (a no-op
when it has not changed). The owner resumes from task 05.
## Running it
```sh
BOXMAKER_MODEL=straylight/ornith-1.5-35b-a3b tools/run-plan.sh docs/plans/M3b
```
Keep the OpenCode TUI closed while it runs.