Add grid agent milestone acceptance gate (#135)
This commit is contained in:
130
crates/metacrate-grid-agent/tests/acceptance_gate.rs
Normal file
130
crates/metacrate-grid-agent/tests/acceptance_gate.rs
Normal file
@@ -0,0 +1,130 @@
|
||||
use metacrate_grid_agent::{
|
||||
ACCEPTANCE_SCHEMA_VERSION, ACCEPTANCE_SECRET_CANARY, AcceptanceBudgets,
|
||||
AcceptanceEvidenceWriter, AcceptanceRecord, AcceptanceStatus, LiveGridOptIns,
|
||||
run_deterministic_acceptance,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
fn temporary(name: &str) -> PathBuf {
|
||||
std::env::temp_dir().join(format!(
|
||||
"metacrate-acceptance-{}-{name}",
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos()
|
||||
))
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn deterministic_gate_writes_ordered_redacted_evidence_and_drains() {
|
||||
let path = temporary("gate.jsonl");
|
||||
run_deterministic_acceptance(&path, AcceptanceBudgets::default())
|
||||
.await
|
||||
.expect("acceptance gate");
|
||||
let text = fs::read_to_string(&path).expect("evidence");
|
||||
assert!(!text.contains(ACCEPTANCE_SECRET_CANARY));
|
||||
let records = text
|
||||
.lines()
|
||||
.map(|line| serde_json::from_str::<AcceptanceRecord>(line).expect("record"))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(records.len(), 7);
|
||||
assert!(
|
||||
records
|
||||
.iter()
|
||||
.all(|record| record.status == AcceptanceStatus::Passed)
|
||||
);
|
||||
assert_eq!(
|
||||
records
|
||||
.iter()
|
||||
.map(|record| record.sequence)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![1, 2, 3, 4, 5, 6, 7]
|
||||
);
|
||||
for required in [
|
||||
"headless_startup",
|
||||
"control_conformance",
|
||||
"chat_scheduling",
|
||||
"clean_shutdown",
|
||||
"maintenance_reconnect",
|
||||
"policy_redaction_and_protocol_audit",
|
||||
] {
|
||||
assert!(records.iter().any(|record| record.stage == required));
|
||||
}
|
||||
assert!(
|
||||
run_deterministic_acceptance(&path, AcceptanceBudgets::default())
|
||||
.await
|
||||
.is_err(),
|
||||
"evidence is never overwritten"
|
||||
);
|
||||
fs::remove_file(path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn live_actions_require_exact_independent_confirmations() {
|
||||
let mut values = BTreeMap::new();
|
||||
values.insert("METACRATE_AGENT_LIVE_CHAT_IM", "CHAT-IM");
|
||||
let action_without_login =
|
||||
LiveGridOptIns::from_environment(|name| values.get(name).map(ToString::to_string));
|
||||
assert!(action_without_login.validate().is_err());
|
||||
values.insert("METACRATE_AGENT_LIVE_LOGIN", "LOGIN");
|
||||
values.insert("METACRATE_AGENT_LIVE_BUILD", "wrong");
|
||||
let confirmed =
|
||||
LiveGridOptIns::from_environment(|name| values.get(name).map(ToString::to_string))
|
||||
.validate()
|
||||
.unwrap();
|
||||
assert!(confirmed.login && confirmed.chat_and_im);
|
||||
assert!(!confirmed.reversible_build);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn evidence_writer_rejects_secret_markers_and_schema_is_committed() {
|
||||
let path = temporary("unsafe.jsonl");
|
||||
let mut writer = AcceptanceEvidenceWriter::create(&path).unwrap();
|
||||
let record = AcceptanceRecord {
|
||||
schema_version: ACCEPTANCE_SCHEMA_VERSION,
|
||||
sequence: 1,
|
||||
unix_millis: 0,
|
||||
package_version: "test".into(),
|
||||
source_revision: "test".into(),
|
||||
rust_toolchain: "test".into(),
|
||||
command: "test".into(),
|
||||
profile: "test".into(),
|
||||
grid_type: "test".into(),
|
||||
endpoint_capabilities: Vec::new(),
|
||||
stage: "canary".into(),
|
||||
status: AcceptanceStatus::Failed,
|
||||
duration_millis: 0,
|
||||
budget_millis: None,
|
||||
outcome_code: ACCEPTANCE_SECRET_CANARY.into(),
|
||||
evidence_sha256: "0".repeat(64),
|
||||
metrics: serde_json::Map::new(),
|
||||
limitations: Vec::new(),
|
||||
};
|
||||
assert!(writer.write(&record).is_err());
|
||||
drop(writer);
|
||||
fs::remove_file(path).unwrap();
|
||||
|
||||
let workspace = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..");
|
||||
let schema =
|
||||
fs::read_to_string(workspace.join("config/grid-agent-acceptance-evidence.schema.json"))
|
||||
.unwrap();
|
||||
let schema: serde_json::Value = serde_json::from_str(&schema).unwrap();
|
||||
assert_eq!(
|
||||
schema["properties"]["schema_version"]["const"],
|
||||
ACCEPTANCE_SCHEMA_VERSION
|
||||
);
|
||||
let report = fs::read_to_string(workspace.join("docs/grid-agent-acceptance.md")).unwrap();
|
||||
for required in [
|
||||
"Deterministic gate",
|
||||
"Live-grid matrix",
|
||||
"Resource budgets",
|
||||
"Remaining limitations",
|
||||
"#128",
|
||||
"#134",
|
||||
] {
|
||||
assert!(report.contains(required), "report missing {required}");
|
||||
}
|
||||
}
|
||||
@@ -52,10 +52,10 @@ fn package_has_only_reviewed_rust_dependencies_and_no_build_script() {
|
||||
#[test]
|
||||
fn runtime_source_has_no_subprocess_or_native_abi_escape_hatch() {
|
||||
let source = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src");
|
||||
let mut files = Vec::with_capacity(37);
|
||||
let mut files = Vec::with_capacity(38);
|
||||
collect_rust_files(&source, &mut files);
|
||||
assert!(
|
||||
files.len() <= 37,
|
||||
files.len() <= 38,
|
||||
"source-file count needs a reviewed bound update"
|
||||
);
|
||||
for path in files {
|
||||
|
||||
Reference in New Issue
Block a user