speccoder_runtime/attest/
policy.rs1use serde_json::{Value, json};
4
5const JUDGE: &[&str] = &[
7 "checks",
8 "acceptance",
9 "tests",
10 "mutation",
11 "monitor",
12 "sandbox",
13 "executor",
14];
15
16pub fn judge_config_sha256(config: &str) -> Result<String, String> {
20 let table: toml::Table = toml::from_str(config).map_err(|e| e.to_string())?;
21 let section = |k: &str| {
22 table
23 .get(k)
24 .and_then(|v| serde_json::to_value(v).ok())
25 .unwrap_or(Value::Null)
26 };
27 let mut judge = serde_json::Map::new();
28 for k in JUDGE {
29 judge.insert((*k).to_owned(), section(k));
30 }
31 let model = table.get("model");
32 let field = |t: Option<&toml::Value>, k: &str| {
33 t.and_then(|m| m.get(k))
34 .and_then(toml::Value::as_str)
35 .map(str::to_owned)
36 };
37 judge.insert(
38 "model".to_owned(),
39 json!({ "provider": field(model, "provider"), "model": field(model, "model") }),
40 );
41 judge.insert(
42 "attest".to_owned(),
43 json!({ "signer": field(table.get("attest"), "signer").unwrap_or_else(|| "git".to_owned()) }),
44 );
45 judge.insert(
46 "speccoder_version".to_owned(),
47 json!(env!("CARGO_PKG_VERSION")),
48 );
49 Ok(crate::integrity::sha256_hex(&super::statement::canonical(
50 &Value::Object(judge),
51 )))
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 const CONFIG: &str = "[model]\nprovider = \"openai\"\nmodel = \"m\"\nprice_input_per_mtok = 1.0\n\n[checks.test]\nargv = [\"cargo\", \"test\"]\n";
59
60 #[test]
61 fn comments_and_prices_keep_the_hash_and_a_check_changes_it() {
62 let base = judge_config_sha256(CONFIG).unwrap();
63 let commented = format!("# a note\n{}", CONFIG.replace("1.0", "2.5"));
64 assert_eq!(judge_config_sha256(&commented).unwrap(), base);
65 let keyed = CONFIG.replace(
66 "model = \"m\"\n",
67 "model = \"m\"\napi_key_env = \"MY_KEY\"\n",
68 );
69 assert_eq!(judge_config_sha256(&keyed).unwrap(), base);
70 let other = CONFIG.replace("\"test\"]", "\"test\", \"--all\"]");
71 assert_ne!(judge_config_sha256(&other).unwrap(), base);
72 let model = CONFIG.replace("model = \"m\"", "model = \"n\"");
73 assert_ne!(judge_config_sha256(&model).unwrap(), base);
74 }
75}