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:
2026-09-17 01:22:43 -07:00
co-authored by Claude Fable 5.1
parent 19104a9629
commit 3e26c2e3c0
36 changed files with 2129 additions and 2 deletions
@@ -0,0 +1,67 @@
//! Tests for grant files. Do not edit these or the fixtures.
use proto::{Constraints, DataClass, Grant, Mode, Timestamp};
fn parse(name: &str) -> Result<Grant, toml::de::Error> {
let path = format!("{}/tests/fixtures/grant/{name}", env!("CARGO_MANIFEST_DIR"));
let text = std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("{path}: {e}"));
toml::from_str(&text)
}
#[test]
fn minimal_grant_gets_safe_defaults() {
let want = Grant {
tool: "read_file".to_string(),
mode: Mode::Auto,
max_taint: DataClass::Private,
result_class: DataClass::Private,
untrusted: true,
expires: None,
secret: None,
constraints: Constraints::default(),
};
assert_eq!(parse("minimal.toml").unwrap(), want);
assert_eq!(Constraints::default().paths, Vec::<String>::new());
}
#[test]
fn full_grant() {
let want = Grant {
tool: "http_fetch".to_string(),
mode: Mode::Ask,
max_taint: DataClass::Secret,
result_class: DataClass::Public,
untrusted: false,
expires: Some(Timestamp::parse("2026-12-31T00:00:00.000Z").unwrap()),
secret: Some("example-api-token".to_string()),
constraints: Constraints {
paths: vec!["/home/kyle/notes/**".to_string()],
hosts: vec!["example.com".to_string(), "api.example.com".to_string()],
patterns: vec!["^GET ".to_string()],
},
};
assert_eq!(parse("full.toml").unwrap(), want);
}
#[test]
fn mistakes_in_grant_files_are_errors() {
for name in [
"unknown_field.toml",
"unknown_constraint.toml",
"bad_mode.toml",
"missing_max_taint.toml",
] {
assert!(parse(name).is_err(), "{name} was accepted");
}
}
#[test]
fn modes_are_lowercase() {
for (mode, text) in [
(Mode::Auto, "auto"),
(Mode::Ask, "ask"),
(Mode::Deny, "deny"),
] {
assert_eq!(serde_json::to_string(&mode).unwrap(), format!("\"{text}\""));
}
}