Files
boxmaker/AGENTS.md
T
kyleandClaude Opus 5.5 d2e6cfa51f flake: Rust 1.98.1 from rust-overlay; rust-version 1.98
The owner's hosts (titan, talos) run 1.98.1; nixos-26.05's Rust is
1.95.0, so builds and lints differed by host. The flake gains a second
input, oxalica's rust-overlay (following our nixpkgs, so host configs
can still follow theirs), and both the package build and the dev shell
use its stable 1.98.1 with clippy and rustfmt. The workspace's
rust-version rises to 1.98 to match; AGENTS.md, docs/dependencies.md
and the M5c-1 plan follow.

Checked on straylight: nix build gives all seven binaries; the dev
shell has rustc, cargo and clippy 1.98.1, rustfmt 1.9.0, cargo-deny
0.19.6; make gate in it passes with 1122 tests, as on titan.

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

132 lines
7.8 KiB
Markdown

# AGENTS.md
Boxmaker is a personal agent harness in Rust: a Cargo workspace of small daemons that talk over
Unix sockets. You are implementing it one task at a time.
## How you work
1. The owner gives you one task file, `docs/plans/M<n>/NN-name.md`. Read it fully. Do that task and
nothing else. Do not start the next task.
2. Do the steps in order. Where a step shows a command and its expected output, run it and compare.
3. Do not read `docs/design.md` or other plans unless the task tells you to. The task file quotes
what you need.
4. If something in the task is impossible, contradictory, or fails twice in the same way, **stop**.
Do not improvise, do not change a test, do not weaken a check. Add your row to
`docs/implementer-log.md` with status `stopped`, say what you tried and what happened, commit
only that file, and tell the owner.
A later session that finishes the task adds a new `done` row; it never edits the `stopped`
row, because the record of the stop is part of the log.
5. Work only from files inside this repository. Never read another checkout or worktree (such as
`~/src/boxmaker-ref`), and never search the file system for code (`find /`, `locate`). If you
are stuck, stop as in point 4; copying code from elsewhere is not finishing the task.
To try something out (a small program, a script, a copy of a file), work under
`.state/scratch/`, which git ignores, and delete it before you commit. Never use `/tmp` or any
other directory outside the repository.
6. If you hand a task to another agent, tell it to read `AGENTS.md` and the task file itself. Do
not pass on your own summary of either. Log what the agent did, as it did it.
## Files you must never edit
- `docs/design.md`, `docs/decisions.md`, `docs/inference-contract.md`, `docs/specs/`, `docs/plans/`
- Anything a task told you to copy from `docs/plans/**/files/`: tests, fixtures, `Makefile`,
`deny.toml`, `scripts/test-gate-scripts.sh`. If a copied test fails, your code is wrong.
- `AGENTS.md`, `CLAUDE.md`
## Code rules
- Rust stable, edition 2024, `rust-version = "1.98"`. No `unsafe`. No async runtime.
- No dependency that the task file does not name. All external dependencies are declared in the
root `Cargo.toml` under `[workspace.dependencies]`, used with `name.workspace = true`, and have a
row in `docs/dependencies.md`.
- `bxcore` (formerly `proto`) depends on no workspace crate. Every other crate depends on `bxcore`
only, never on another role crate.
- No source file over 500 lines.
- Library code never panics on input: no `unwrap`, `expect`, `panic!`, indexing that can go out of
bounds, or `as` casts that can lose data on untrusted values. Tests may use them.
- Errors are plain enums that implement `std::fmt::Display` and `std::error::Error`. No `anyhow`,
no `thiserror`.
- Do not silence a lint with `#[allow(...)]` unless the task says so. Fix the code.
- Keep struct fields and enum variants in the order the task gives. The order is the wire format.
- Formats we define (config, grants, IPC messages, log records) reject unknown fields. Formats
another program defines (the inference server's JSON, later Mattermost's) ignore them: those
programs send fields we do not use and add more over time. Each task says which kind it handles.
- Everything read from a socket is bounded, and a number that came from a peer is never used to
index, allocate or cast without a check.
- Comments say why, not what. Match the amount of commenting you see in the task's examples.
## Lessons from earlier reviews
These come from defects found in review. The evidence is in `docs/implementer-lessons.md`.
- When a rule says "every" or "everywhere", finish by listing each place it could apply (every
struct and enum in the file, every script, every branch) and check them one by one. An example
in a task shows one place; the rule covers all of them.
- A check must fail when it cannot do its job: missing input, unreadable file, a tool that errors.
Never throw errors away with `2>/dev/null`, `|| true` or an ignored `Result`.
- Report every problem you find, not only the first.
- Log every attempt, including one you abandon.
- Never end a turn by describing what you are about to do. Do it, then report.
- A refused or failed tool call is not a reason to end your turn. Read the error, then continue
with another tool call.
- Fill `todo!()` bodies one at a time, and run `cargo check` after each.
- If a tool you were told to use does not exist, stop and say so. Do not invent a command in its
place.
- A `read` returns as soon as it has any data to give and blocks only when it has none.
- A rule about one path applies to every path that does the same thing, including early returns
and error paths the task did not walk through.
- A file that exists but cannot be read is an error. Only a missing file may count as absent.
- A file your own program can create before it writes anything must load like an empty one. Test
the state a crash leaves behind, not only the states you write on purpose.
- When two programs must agree about a set of things (which files are a log, which names are ids),
one of them deciding alone is a bug. Write the case that walks both.
- Never write the model's chat-template tags out whole (the think and tool-call tags, the
angle-bar turn markers), in code, comments, commands or your log. Build them from pieces
(`concat!("<", "think")`) and name them in words: text holding them becomes your own control
tokens when you read it.
- When a test outside your task fails, check once whether it fails without your change, then stop
and report both results. Do not keep experimenting, and never add a file the task does not list,
outside `.state/scratch/`.
- Write your log row from `git diff` after staging, naming only what the diff shows.
## The gate
`make gate` must print `gate: ok` before a task is done. Run it with a shell timeout of 600000 ms:
it takes longer than the default 2 minutes, and a gate cut off by the timeout is not a failure of
your code. It runs offline: rustfmt, clippy with
warnings denied, all tests, cargo-deny, and the scripts in `scripts/`. Run `cargo fmt --all` before
the gate; rustfmt decides the order of `mod` and `use` lines, not you. After you add a dependency,
run `cargo build` once so that `Cargo.lock` is updated, then run the gate.
Useful while working: `cargo test -p <crate> --test <file>` runs one test file, and
`cargo test -p <crate> <name>` runs tests whose name contains `<name>`.
## Git
- Work on the branch the task names. One task is one commit.
- Stage only the paths the task lists, plus `Cargo.lock` if it changed: `git add <path> ...`.
Never `git add -A` or `git add .`.
- Never push, amend, rebase, reset, or switch to another branch.
- Commit message: the subject line the task gives, a blank line, then the `Co-Authored-By` line
your session prompt gives, exactly, for example
`Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>`. If the prompt gives none, stop and
report.
- Write the message to `.state/commit-msg.txt` and commit with `git commit -F
.state/commit-msg.txt`, never with `-m`: quotes and apostrophes in a message break `-m`.
## The implementer log
Before you commit, add one row to the table in `docs/implementer-log.md` and include the file in
the commit. Be honest: the log is how the owner judges the process, and a wrong row is worse than a
bad one.
| Column | What to write |
|---|---|
| Task | The task file name, for example `M1/03-proto-wire` |
| Date | Today's date, `YYYY-MM-DD` |
| Status | `done` or `stopped` |
| Gate runs | How many times you ran `make gate` |
| First gate | `pass` or `fail` for the first run |
| Deviations | Anything you did that the task did not say, or `none` |
| Notes | Problems you hit and how you solved them, in one or two sentences |
| Model | The model name your session prompt gives (as in the `Co-Authored-By` line), or `?` if it gives none. |