Files
boxmaker/docs/plans/M3b
kyleandClaude Opus 5.5 c3aaecdae2 M3b review fixes by the design model: curl --globoff, podman --pull=never
Review findings 1 and 2, both plan defects. curl gains --globoff and a
leading --disable; both podman runs gain --pull=never. The given fetch.rs and
the six golden files change with them. Checked on straylight with a rebuilt
image: a glob URL is one request, a missing image fails at once.

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

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.

  • 2026-09-23, task 08: the first attempt passed 13 of 15 tests and ended without a commit, after OpenCode refused a command in /tmp. Its two defects: main did not skip the program name, so egress-proxy never matched; and serve joined each handler thread, serving one connection at a time. The attempt is saved in .state/runs/M3b/08-first-attempt.diff; the tree was reset, and the task now says both things explicitly and to debug inside the repository. Resume from task 08.

  • 2026-09-23, task 11: the session ended in deliberation, with nothing written. The task stored egress_dir for task 12 but gave nothing that read it, so clippy's dead_code would fail the gate and #[allow] is forbidden: a task that could not be written as described (tip T16). The reference had a public egress_dir() getter; the task now gives it. Resume from task 11.

  • 2026-09-23, task 11 again: the second session also ended with nothing written, cut off while deliberating over API details the compiler would have settled. The task now hands over a compiling skeleton of container.rs (signatures, constants, the steps as comments, todo!() bodies), checked against the given tests (11 red), and says to fill one function at a time with cargo check between. Resume from task 11.

  • 2026-09-23, task 11, third attempt: it filled five of the six functions from the skeleton, with cargo check between, then planned all of run in one turn and was cut off (two compile errors left). Saved in .state/runs/M3b/11-third-attempt.diff. The skeleton now has run and run_container written as glue, and the rest as small helpers (spawn, Io::start, Io::finish, answer); the design model filled them in a scratch copy to check the split can pass (11 of 11, five runs, clippy clean) and removed that. Task 12 now calls run_container. Resume from task 11.

  • 2026-09-23, after the review: two plan defects found on straylight were fixed by the design model: curl gains --globoff (and a leading --disable), since [1-3] in a URL made three requests; both podman runs gain --pull=never, since a missing image made Podman try to pull. The given fetch.rs and the six golden files changed with them. Follow-up tasks 14 to 16 are for the implementer.

Running it

BOXMAKER_MODEL=straylight/ornith-1.5-35b-a3b tools/run-plan.sh docs/plans/M3b

Keep the OpenCode TUI closed while it runs.