Specify and plan M3b: the runner and the tools

A draft spec for the owner's review and 13 offline tasks with their given
tests: shared tool arguments and host rules in proto, the sealed fetch
target (M3a finding 14), the toolkit tools and SOCKS5 egress proxy, and
brokerd's [runner], podman argument lists, runtime and proxy lifecycle. Each
task's tests were run against a reference at that task's end state (560 to
638 tests, clippy clean); the reference is not in the repository. Adds the
runner-unavailable runbook entry and tip T23 (ETXTBSY in script tests).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-22 22:29:27 -07:00
co-authored by Claude Opus 5.5
parent d988edac4a
commit b426ca1958
47 changed files with 4491 additions and 2 deletions
+93
View File
@@ -0,0 +1,93 @@
# M3b task 01: the tools' arguments and the host rules move to `proto`
**Branch:** `m3b` (create it: `git switch master && git switch -c m3b`; `git status --short` must be
empty first, otherwise stop)
**Commit subject:** `Move the tools' arguments and the host rules to proto`
## Goal
Two programs will now read the same things: `brokerd` parses the model's tool arguments and checks
hosts, and `toolkit` (inside the container) reads the same arguments and checks the same hosts.
Each must have **one** definition. Move them into `proto`; `brokerd` uses them from there. Nothing
changes in behaviour: every existing test passes unchanged.
## Files
- Copy: `crates/proto/tests/tools.rs`, `crates/proto/tests/hosts.rs`
- Create: `crates/proto/src/tools.rs`, `crates/proto/src/hosts.rs`
- Modify: `crates/proto/src/lib.rs`, `crates/brokerd/src/args.rs`, `docs/implementer-log.md`
## Interfaces
`crates/proto/src/tools.rs` (module doc: the four tools' arguments, shared by `brokerd` and
`toolkit`):
```rust
use serde::{Deserialize, Serialize};
// Each: #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(deny_unknown_fields)]
pub struct ReadFileArgs { pub path: String }
pub struct WriteFileArgs { pub path: String, pub content: String }
pub struct ShellArgs {
pub command: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cwd: Option<String>,
}
pub struct HttpFetchArgs { pub url: String }
```
These are the four private structs at the bottom of `brokerd/src/args.rs` today (`ReadFileArgs`,
`WriteFileArgs`, `ShellArgs`, `HttpFetchArgs`), made public, with `Debug, Clone, PartialEq, Eq`
added. **Move** them: delete them from `args.rs` and `use proto::tools::{…}` there instead.
`crates/proto/src/hosts.rs` (module doc: host names and patterns, shared by `brokerd` and the
egress proxy):
```rust
pub fn valid_host(host: &str) -> bool;
pub fn valid_host_pattern(pattern: &str) -> bool;
pub fn host_matches(pattern: &str, host: &str) -> bool;
fn valid_label(label: &str) -> bool; // private, as it is now
```
**Move** these four functions from `brokerd/src/args.rs`, bodies and doc comments unchanged. In
`args.rs`, where they were, put:
```rust
/// Moved to `proto::hosts` in M3b, so `toolkit` checks hosts with the same rules.
pub use proto::hosts::{host_matches, valid_host, valid_host_pattern};
```
so that `brokerd::args::valid_host` and the others still exist for `brokerd`'s code and tests.
`url_host`, `valid_path`, `inside` and `MAX_URL` stay in `args.rs`.
In `crates/proto/src/lib.rs`: add `pub mod hosts;` and `pub mod tools;` in alphabetical order with
the other `pub mod` lines. No `pub use` for them: callers write `proto::tools::ShellArgs` and
`proto::hosts::host_matches`.
`args.rs` no longer needs `use serde::{Deserialize, Serialize};` once the structs are gone; remove
it if the compiler says it is unused.
## Steps
- [ ] **1. Branch and copy.** `git switch master && git switch -c m3b`, then
`cp docs/plans/M3b/files/crates/proto/tests/tools.rs docs/plans/M3b/files/crates/proto/tests/hosts.rs crates/proto/tests/`
- [ ] **2. See them fail.** `cargo test -p proto --test tools --test hosts`. Expected: they do not
compile (`proto::tools` and `proto::hosts` do not exist).
- [ ] **3. Move the code** as above. Run `cargo fmt --all`.
- [ ] **4. See them pass.** `cargo test -p proto --test tools --test hosts`. Expected: 3 and 3
passed. Then `cargo test -p brokerd --test args --test grants --test policy`: all pass, unchanged.
- [ ] **5. Check nothing is defined twice.** `grep -rn "fn valid_host\|struct ShellArgs" crates/`
must show only `crates/proto/src/`.
- [ ] **6. Run the gate.** `make gate`. Expected last line: `gate: ok`.
- [ ] **7. Log and commit.** Add your row to `docs/implementer-log.md`, then
`git add crates/proto crates/brokerd docs/implementer-log.md && git commit`
## Done when
- Both new suites pass, every existing suite passes, `make gate` prints `gate: ok`.
## Stop and report if
- A given test needs a change to pass.
- An existing test fails after the move.