Add M1 plan, given tests and fixtures, AGENTS.md and implementer log
Seven task files for the implementing model under docs/plans/M1/, with the test files, byte-exact fixtures, Makefile, deny.toml and gate-script self-test they copy into place. All of it was verified against a private reference implementation: the gate passes after every task in order. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
# M1 task 05: `proto` grant files
|
||||
|
||||
**Branch:** `m1` (run `git switch m1`; `git status --short` must be empty, otherwise stop)
|
||||
**Commit subject:** `Add Grant, Mode and Constraints to proto`
|
||||
|
||||
## Goal
|
||||
|
||||
Add the data type for a grant file. A grant is a TOML file the owner writes to allow one kind of
|
||||
tool call. This task only parses grants. Deciding whether a grant matches a request is `brokerd`'s
|
||||
job in a later milestone; write no matching logic.
|
||||
|
||||
## Context
|
||||
|
||||
From the design brief: "A grant is a file the owner writes: tool, argument constraints (paths,
|
||||
hosts, patterns), allowed data classes, expiry, mode (`auto`, `ask`, `deny`)."
|
||||
|
||||
From the spec: `max_taint` is "the highest session taint under which the grant applies".
|
||||
`result_class` is the data class of the tool's results and defaults to `private`. `untrusted`
|
||||
says whether results may have been written by someone other than the owner and defaults to `true`.
|
||||
A typing mistake in a grant file must be an error, never silently ignored: a misspelt
|
||||
`max_tiant` that fell back to a default would widen what the grant allows.
|
||||
|
||||
A full grant file:
|
||||
|
||||
```toml
|
||||
tool = "http_fetch"
|
||||
mode = "ask"
|
||||
max_taint = "secret"
|
||||
result_class = "public"
|
||||
untrusted = false
|
||||
expires = "2026-12-31T00:00:00.000Z"
|
||||
secret = "example-api-token"
|
||||
|
||||
[constraints]
|
||||
paths = ["/home/kyle/notes/**"]
|
||||
hosts = ["example.com", "api.example.com"]
|
||||
patterns = ["^GET "]
|
||||
```
|
||||
|
||||
## Files
|
||||
|
||||
- Copy: `crates/proto/tests/grant.rs`, `crates/proto/tests/fixtures/grant/` (6 files)
|
||||
- Create: `crates/proto/src/grant.rs`
|
||||
- Modify: `Cargo.toml`, `crates/proto/Cargo.toml`, `crates/proto/src/lib.rs`,
|
||||
`docs/dependencies.md`, `docs/implementer-log.md`, `Cargo.lock` (generated)
|
||||
|
||||
## Interfaces
|
||||
|
||||
Consumes from task 02: `proto::DataClass`, `proto::Timestamp`.
|
||||
|
||||
Produces, in `crates/proto/src/grant.rs`, re-exported from the crate root:
|
||||
|
||||
```rust
|
||||
// TOML and JSON: "auto", "ask", "deny"
|
||||
pub enum Mode { Auto, Ask, Deny }
|
||||
|
||||
pub struct Grant {
|
||||
pub tool: String, // required
|
||||
pub mode: Mode, // required
|
||||
pub max_taint: DataClass, // required
|
||||
pub result_class: DataClass, // default: DataClass::Private
|
||||
pub untrusted: bool, // default: true
|
||||
pub expires: Option<Timestamp>, // default: None, meaning no expiry
|
||||
pub secret: Option<String>, // default: None
|
||||
pub constraints: Constraints, // default: all three lists empty
|
||||
}
|
||||
|
||||
pub struct Constraints { // also derives Default
|
||||
pub paths: Vec<String>, // default: empty
|
||||
pub hosts: Vec<String>, // default: empty
|
||||
pub patterns: Vec<String>, // default: empty
|
||||
}
|
||||
```
|
||||
|
||||
Derives: all three get `Debug, Clone, PartialEq, Eq, Serialize, Deserialize`. `Mode` also gets
|
||||
`Copy`. `Constraints` also gets `Default`.
|
||||
|
||||
Rules the tests check: `tool`, `mode` and `max_taint` are required. Unknown keys are errors in the
|
||||
grant and in `[constraints]`. An unknown mode is an error. `expires` is written as a quoted string
|
||||
in the `Timestamp` spelling from task 02, not as a bare TOML date.
|
||||
|
||||
## API notes (verified on docs.rs, 2026-09-17)
|
||||
|
||||
- `toml::from_str::<T>(&str) -> Result<T, toml::de::Error>`. Only the test uses the `toml` crate,
|
||||
so it is a dev-dependency. `grant.rs` itself uses only serde.
|
||||
- `#[serde(default)]` on a field uses `Default::default()` when the key is missing.
|
||||
`#[serde(default = "function_name")]` calls a function in the same module instead; use it for
|
||||
`result_class` and `untrusted`, whose defaults are not the types' `Default`.
|
||||
- `#[serde(deny_unknown_fields)]` and `#[serde(rename_all = "lowercase")]` as in earlier tasks.
|
||||
|
||||
## Steps
|
||||
|
||||
- [ ] **1. Copy the test and fixtures, add the dev-dependency.**
|
||||
|
||||
```sh
|
||||
git switch m1
|
||||
cp docs/plans/M1/files/crates/proto/tests/grant.rs crates/proto/tests/
|
||||
cp -r docs/plans/M1/files/crates/proto/tests/fixtures/grant crates/proto/tests/fixtures/
|
||||
```
|
||||
|
||||
Add to `[workspace.dependencies]` in the root `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
toml = "1.1.6"
|
||||
```
|
||||
|
||||
Add a new section at the end of `crates/proto/Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dev-dependencies]
|
||||
toml.workspace = true
|
||||
```
|
||||
|
||||
Add this row to `docs/dependencies.md`:
|
||||
|
||||
```markdown
|
||||
| `toml` | 1.1.6 | `proto` tests; `brokerd` and `bxctl` from M3 | Parses grant files. MIT OR Apache-2.0. |
|
||||
```
|
||||
|
||||
- [ ] **2. See the test fail.** `cargo test -p proto --test grant`. Expected: it does not compile.
|
||||
|
||||
- [ ] **3. Write `grant.rs`,** and add to `lib.rs` (alphabetical order):
|
||||
|
||||
```rust
|
||||
pub mod grant;
|
||||
|
||||
pub use grant::{Constraints, Grant, Mode};
|
||||
```
|
||||
|
||||
- [ ] **4. See the test pass.** `cargo test -p proto --test grant`. Expected: `4 passed; 0 failed`.
|
||||
|
||||
- [ ] **5. Run the gate.** `make gate`. Expected last line: `gate: ok`. If `cargo deny` reports a
|
||||
licence that is not allowed or a duplicate crate version, stop and report; do not edit
|
||||
`deny.toml`.
|
||||
|
||||
- [ ] **6. Log and commit.**
|
||||
|
||||
```sh
|
||||
git add Cargo.toml Cargo.lock crates/proto docs/dependencies.md docs/implementer-log.md
|
||||
git commit
|
||||
```
|
||||
|
||||
## Done when
|
||||
|
||||
- `cargo test -p proto --test grant` reports 4 passed.
|
||||
- `make gate` prints `gate: ok`.
|
||||
- `diff -r crates/proto/tests/fixtures/grant docs/plans/M1/files/crates/proto/tests/fixtures/grant`
|
||||
prints nothing.
|
||||
|
||||
## Stop and report if
|
||||
|
||||
- `cargo deny` rejects `toml` or one of its dependencies.
|
||||
Reference in New Issue
Block a user