use std::fs;
use std::path::PathBuf;
use std::process::Command;
use serde_json::json;
fn cortex_bin() -> PathBuf {
PathBuf::from(env!("CARGO_BIN_EXE_cortex"))
}
fn run(args: &[&str]) -> std::process::Output {
Command::new(cortex_bin())
.env_remove("RUST_LOG")
.args(args)
.output()
.expect("spawn cortex")
}
fn assert_exit(out: &std::process::Output, expected: i32) {
let code = out.status.code().expect("process exited via signal");
assert_eq!(
code,
expected,
"expected exit {expected}, got {code}\nstdout: {}\nstderr: {}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr),
);
}
fn authority_grade_input(kind: &str) -> String {
json!({
"evidence_kind": kind,
"runtime_mode": "authority_grade",
"authority_class": "operator",
"proof_state": "full_chain_verified",
"claim_ceiling": "authority_grade",
"policy_outcome": "allow",
"source_refs": ["signed://fixture/evidence"]
})
.to_string()
}
fn weak_input(kind: &str) -> String {
json!({
"evidence_kind": kind,
"runtime_mode": "local_unsigned",
"authority_class": "observed",
"proof_state": "partial",
"claim_ceiling": "local_unsigned",
"policy_outcome": "allow",
"source_refs": ["local://fixture/evidence"]
})
.to_string()
}
fn blake3_hex(input: &str) -> String {
blake3::hash(input.as_bytes()).to_hex().to_string()
}
fn malformed_source_ref_input(kind: &str) -> String {
json!({
"evidence_kind": kind,
"runtime_mode": "authority_grade",
"authority_class": "operator",
"proof_state": "full_chain_verified",
"claim_ceiling": "authority_grade",
"policy_outcome": "allow",
"source_refs": ["fixture-without-authority-scheme"]
})
.to_string()
}
fn missing_source_ref_input(kind: &str) -> String {
json!({
"evidence_kind": kind,
"runtime_mode": "authority_grade",
"authority_class": "operator",
"proof_state": "full_chain_verified",
"claim_ceiling": "authority_grade",
"policy_outcome": "allow",
"source_refs": []
})
.to_string()
}
fn advisory_input(kind: &str, runtime_mode: &str) -> String {
json!({
"evidence_kind": kind,
"runtime_mode": runtime_mode,
"authority_class": "observed",
"proof_state": "partial",
"claim_ceiling": "local_unsigned",
"policy_outcome": "allow",
"source_refs": ["local://fixture/evidence"]
})
.to_string()
}
fn assert_contains_str(array: &serde_json::Value, expected: &str) {
assert!(
array
.as_array()
.expect("field is array")
.iter()
.any(|value| value.as_str() == Some(expected)),
"expected array to contain {expected}, got {array:?}"
);
}
fn assert_missing_evidence(report: &serde_json::Value, expected: &str) {
assert_contains_str(&report["evidence_input"]["missing_evidence"], expected);
}
fn contribution_outcome(report: &serde_json::Value, rule_id: &str) -> Option<String> {
report["policy_contributing"]
.as_array()
.expect("policy_contributing is array")
.iter()
.find(|contribution| contribution["rule_id"] == rule_id)
.and_then(|contribution| contribution["outcome"].as_str())
.map(str::to_string)
}
fn discarded_outcome(report: &serde_json::Value, rule_id: &str) -> Option<String> {
report["policy_discarded"]
.as_array()
.expect("policy_discarded is array")
.iter()
.find(|contribution| contribution["rule_id"] == rule_id)
.and_then(|contribution| contribution["outcome"].as_str())
.map(str::to_string)
}
#[test]
fn release_readiness_fails_closed_without_trusted_stdout_artifact() {
let out = run(&["release", "readiness"]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "release.readiness");
assert_eq!(report["artifact_emitted"], false);
assert_eq!(report["trusted_artifact_emitted"], false);
assert_eq!(report["trusted_stdout_artifact"], false);
assert_eq!(report["independent_verification"], false);
assert_eq!(report["claim_allowed"], false);
assert_eq!(report["policy_outcome"], "reject");
assert_contains_str(&report["forbidden_uses"], "release_readiness_artifact");
assert_eq!(
report["policy_contributing"][0]["rule_id"],
"release.remote_ci_evidence"
);
assert_eq!(
report["evidence_input"]["verification_state"],
"missing_input"
);
assert_eq!(
report["evidence_input"]["failed_rule"],
"release.evidence_input.missing"
);
assert_eq!(
report["evidence_input"]["trusted_evidence_input_present"],
false
);
assert_missing_evidence(&report, "release.evidence_input");
}
#[test]
fn release_readiness_rejects_weak_evidence_input() {
let input = weak_input("remote_ci");
let out = run(&["release", "readiness", "--evidence-json", &input]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "release.readiness");
assert_eq!(report["artifact_emitted"], false);
assert_eq!(report["claim_allowed"], false);
assert_eq!(report["policy_outcome"], "reject");
assert_eq!(report["evidence_input"]["present"], true);
assert_eq!(report["evidence_input"]["accepted"], false);
assert_eq!(
report["evidence_input"]["verification_state"],
"advisory_only"
);
assert_eq!(
report["evidence_input"]["failed_rule"],
"release.evidence_input.advisory_only"
);
assert_eq!(report["evidence_input"]["accepted_as"], "none");
assert_eq!(report["evidence_input"]["advisory_only"], true);
assert_eq!(
contribution_outcome(&report, "release.remote_ci_evidence").as_deref(),
Some("reject")
);
}
#[test]
fn release_readiness_rejects_pre_v2_backup_evidence_as_advisory_only() {
let input = authority_grade_input("cortex_pre_v2_backup");
let digest = blake3_hex(&input);
let out = run(&[
"release",
"readiness",
"--evidence-json",
&input,
"--evidence-blake3",
&digest,
]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "release.readiness");
assert_eq!(report["trusted_artifact_emitted"], false);
assert_eq!(report["trusted_stdout_artifact"], false);
assert_eq!(report["claim_allowed"], false);
assert_eq!(report["policy_outcome"], "reject");
assert_eq!(report["evidence_input"]["accepted"], false);
assert_eq!(report["evidence_input"]["accepted_as"], "none");
assert_eq!(
report["evidence_input"]["verification_state"],
"advisory_only"
);
assert_eq!(
report["evidence_input"]["failed_rule"],
"release.evidence_input.advisory_only"
);
assert_eq!(
report["evidence_input"]["evidence_kind"],
"cortex_pre_v2_backup"
);
assert_eq!(report["evidence_input"]["advisory_only"], true);
assert_contains_str(
&report["evidence_input"]["forbidden_uses"],
"compliance_evidence",
);
}
#[test]
fn release_readiness_rejects_dev_drill_evidence_as_advisory_only() {
let input = advisory_input("run_ledger_drill", "dev");
let out = run(&["release", "readiness", "--evidence-json", &input]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "release.readiness");
assert_eq!(report["trusted_artifact_emitted"], false);
assert_eq!(report["claim_allowed"], false);
assert_eq!(report["evidence_input"]["runtime_mode"], "dev");
assert_eq!(
report["evidence_input"]["evidence_kind"],
"run_ledger_drill"
);
assert_eq!(
report["evidence_input"]["failed_rule"],
"release.evidence_input.advisory_only"
);
assert_eq!(report["evidence_input"]["advisory_only"], true);
}
#[test]
fn release_readiness_parses_authority_grade_input_but_policy_still_fails_closed() {
let input = authority_grade_input("remote_ci");
let digest = blake3_hex(&input);
let out = run(&[
"release",
"readiness",
"--evidence-json",
&input,
"--evidence-blake3",
&digest,
]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "release.readiness");
assert_eq!(report["artifact_emitted"], false);
assert_eq!(report["trusted_artifact_emitted"], false);
assert_eq!(report["trusted_stdout_artifact"], false);
assert_eq!(report["independent_verification"], false);
assert_eq!(report["claim_allowed"], false);
assert_eq!(report["claim_ceiling"], "dev_only");
assert_eq!(report["required_ceiling"], "authority_grade");
assert_eq!(report["policy_outcome"], "reject");
assert_eq!(report["evidence_input"]["accepted"], false);
assert_eq!(report["evidence_input"]["accepted_as"], "none");
assert_eq!(
report["evidence_input"]["trusted_evidence_input_present"],
false
);
assert_eq!(report["evidence_input"]["verification_state"], "partial");
assert_eq!(
report["evidence_input"]["failed_rule"],
"release.evidence_input.independent_verification_partial"
);
assert_eq!(report["evidence_input"]["independent_verification"], false);
assert_eq!(report["evidence_input"]["trusted_artifact_emitted"], false);
assert_eq!(report["evidence_input"]["source_refs_valid"], true);
assert_eq!(report["evidence_input"]["digest_verified"], true);
assert_missing_evidence(&report, "release.remote_ci_independent_verification");
assert_missing_evidence(&report, "release.trusted_artifact_emission");
assert_contains_str(
&report["evidence_input"]["forbidden_uses"],
"external_reporting",
);
assert_eq!(
discarded_outcome(&report, "release.remote_ci_evidence"),
None
);
assert_eq!(
contribution_outcome(&report, "release.evidence_input_verification").as_deref(),
Some("reject")
);
assert_eq!(report["verifier"]["invoked"], true);
assert_eq!(report["verifier"]["state"], "partial");
assert!(report["verifier"]["reasons"]
.as_array()
.expect("verifier.reasons is an array")
.iter()
.any(|reason| reason
.as_str()
.is_some_and(|text| text.contains("verifier.witness.missing"))));
}
#[test]
fn release_readiness_rejects_digest_mismatch_before_accepting_input() {
let input = authority_grade_input("remote_ci");
let out = run(&[
"release",
"readiness",
"--evidence-json",
&input,
"--evidence-blake3",
"0000000000000000000000000000000000000000000000000000000000000000",
]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "release.readiness");
assert_eq!(report["trusted_stdout_artifact"], false);
assert_eq!(report["policy_outcome"], "reject");
assert_eq!(report["evidence_input"]["accepted"], false);
assert_eq!(report["evidence_input"]["digest_verified"], false);
assert_eq!(report["evidence_input"]["verification_state"], "rejected");
assert_eq!(
report["evidence_input"]["failed_rule"],
"release.evidence_input.digest_mismatch"
);
assert_missing_evidence(&report, "release.evidence_input_matching_blake3");
assert_eq!(
contribution_outcome(&report, "release.remote_ci_evidence").as_deref(),
Some("reject")
);
}
#[test]
fn release_readiness_rejects_missing_digest_with_stable_fields() {
let input = authority_grade_input("remote_ci");
let out = run(&["release", "readiness", "--evidence-json", &input]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "release.readiness");
assert_eq!(report["trusted_artifact_emitted"], false);
assert_eq!(report["independent_verification"], false);
assert_eq!(report["evidence_input"]["accepted"], false);
assert_eq!(report["evidence_input"]["digest_verified"], false);
assert_eq!(
report["evidence_input"]["failed_rule"],
"release.evidence_input.missing_digest"
);
assert_missing_evidence(&report, "release.evidence_input_blake3");
}
#[test]
fn release_readiness_rejects_malformed_source_ref_before_accepting_input() {
let input = malformed_source_ref_input("remote_ci");
let digest = blake3_hex(&input);
let out = run(&[
"release",
"readiness",
"--evidence-json",
&input,
"--evidence-blake3",
&digest,
]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "release.readiness");
assert_eq!(report["trusted_stdout_artifact"], false);
assert_eq!(report["policy_outcome"], "reject");
assert_eq!(report["evidence_input"]["accepted"], false);
assert_eq!(report["evidence_input"]["source_refs_valid"], false);
assert_eq!(report["evidence_input"]["digest_verified"], true);
assert_eq!(
report["evidence_input"]["failed_rule"],
"release.evidence_input.malformed_source_refs"
);
assert_missing_evidence(&report, "release.authority_source_refs");
}
#[test]
fn release_readiness_rejects_missing_source_refs_with_stable_fields() {
let input = missing_source_ref_input("remote_ci");
let digest = blake3_hex(&input);
let out = run(&[
"release",
"readiness",
"--evidence-json",
&input,
"--evidence-blake3",
&digest,
]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "release.readiness");
assert_eq!(report["trusted_artifact_emitted"], false);
assert_eq!(report["evidence_input"]["accepted"], false);
assert_eq!(report["evidence_input"]["source_refs_present"], false);
assert_eq!(report["evidence_input"]["source_refs_valid"], true);
assert_eq!(report["evidence_input"]["digest_verified"], true);
assert_eq!(
report["evidence_input"]["failed_rule"],
"release.evidence_input.missing_source_refs"
);
assert_missing_evidence(&report, "release.evidence_input_source_refs");
}
#[test]
fn compliance_evidence_fails_closed_without_trusted_stdout_artifact() {
let out = run(&["compliance", "evidence"]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "compliance.evidence");
assert_eq!(report["artifact_emitted"], false);
assert_eq!(report["trusted_artifact_emitted"], false);
assert_eq!(report["trusted_stdout_artifact"], false);
assert_eq!(report["independent_verification"], false);
assert_eq!(report["claim_allowed"], false);
assert_eq!(report["policy_outcome"], "reject");
assert_contains_str(&report["forbidden_uses"], "compliance_evidence_artifact");
assert_eq!(
report["policy_contributing"][0]["rule_id"],
"compliance.external_authority"
);
assert_eq!(
report["evidence_input"]["verification_state"],
"missing_input"
);
assert_eq!(
report["evidence_input"]["failed_rule"],
"compliance.evidence_input.missing"
);
assert_eq!(
report["evidence_input"]["trusted_evidence_input_present"],
false
);
assert_missing_evidence(&report, "compliance.evidence_input");
}
#[test]
fn compliance_evidence_rejects_weak_evidence_input() {
let input = weak_input("external_authority");
let out = run(&["compliance", "evidence", "--evidence-json", &input]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "compliance.evidence");
assert_eq!(report["artifact_emitted"], false);
assert_eq!(report["claim_allowed"], false);
assert_eq!(report["policy_outcome"], "reject");
assert_eq!(report["evidence_input"]["present"], true);
assert_eq!(report["evidence_input"]["accepted"], false);
assert_eq!(
report["evidence_input"]["verification_state"],
"advisory_only"
);
assert_eq!(
report["evidence_input"]["failed_rule"],
"compliance.evidence_input.advisory_only"
);
assert_eq!(report["evidence_input"]["accepted_as"], "none");
assert_eq!(report["evidence_input"]["advisory_only"], true);
assert_eq!(
contribution_outcome(&report, "compliance.external_authority").as_deref(),
Some("reject")
);
}
#[test]
fn compliance_evidence_rejects_pre_v2_backup_evidence_as_advisory_only() {
let input = authority_grade_input("cortex_pre_v2_backup");
let digest = blake3_hex(&input);
let out = run(&[
"compliance",
"evidence",
"--evidence-json",
&input,
"--evidence-blake3",
&digest,
]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "compliance.evidence");
assert_eq!(report["trusted_artifact_emitted"], false);
assert_eq!(report["trusted_stdout_artifact"], false);
assert_eq!(report["claim_allowed"], false);
assert_eq!(report["policy_outcome"], "reject");
assert_eq!(report["evidence_input"]["accepted"], false);
assert_eq!(report["evidence_input"]["accepted_as"], "none");
assert_eq!(
report["evidence_input"]["verification_state"],
"advisory_only"
);
assert_eq!(
report["evidence_input"]["failed_rule"],
"compliance.evidence_input.advisory_only"
);
assert_eq!(
report["evidence_input"]["evidence_kind"],
"cortex_pre_v2_backup"
);
assert_eq!(report["evidence_input"]["advisory_only"], true);
assert_contains_str(
&report["evidence_input"]["forbidden_uses"],
"release_readiness_artifact",
);
}
#[test]
fn compliance_evidence_rejects_local_drill_evidence_as_advisory_only() {
let input = advisory_input("run_ledger_drill", "local_unsigned");
let out = run(&["compliance", "evidence", "--evidence-json", &input]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "compliance.evidence");
assert_eq!(report["trusted_artifact_emitted"], false);
assert_eq!(report["claim_allowed"], false);
assert_eq!(report["evidence_input"]["runtime_mode"], "local_unsigned");
assert_eq!(
report["evidence_input"]["evidence_kind"],
"run_ledger_drill"
);
assert_eq!(
report["evidence_input"]["failed_rule"],
"compliance.evidence_input.advisory_only"
);
assert_eq!(report["evidence_input"]["advisory_only"], true);
}
#[test]
fn compliance_evidence_parses_authority_grade_input_but_policy_still_fails_closed() {
let input = authority_grade_input("external_authority");
let digest = blake3_hex(&input);
let out = run(&[
"compliance",
"evidence",
"--evidence-json",
&input,
"--evidence-blake3",
&digest,
]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "compliance.evidence");
assert_eq!(report["artifact_emitted"], false);
assert_eq!(report["trusted_artifact_emitted"], false);
assert_eq!(report["trusted_stdout_artifact"], false);
assert_eq!(report["independent_verification"], false);
assert_eq!(report["claim_allowed"], false);
assert_eq!(report["claim_ceiling"], "dev_only");
assert_eq!(report["required_ceiling"], "authority_grade");
assert_eq!(report["policy_outcome"], "reject");
assert_eq!(report["evidence_input"]["accepted"], false);
assert_eq!(report["evidence_input"]["accepted_as"], "none");
assert_eq!(
report["evidence_input"]["trusted_evidence_input_present"],
false
);
assert_eq!(report["evidence_input"]["verification_state"], "partial");
assert_eq!(
report["evidence_input"]["failed_rule"],
"compliance.evidence_input.independent_verification_partial"
);
assert_eq!(report["evidence_input"]["independent_verification"], false);
assert_eq!(report["evidence_input"]["trusted_artifact_emitted"], false);
assert_eq!(report["evidence_input"]["source_refs_valid"], true);
assert_eq!(report["evidence_input"]["digest_verified"], true);
assert_missing_evidence(
&report,
"compliance.external_authority_independent_verification",
);
assert_missing_evidence(&report, "compliance.trusted_artifact_emission");
assert_contains_str(
&report["evidence_input"]["forbidden_uses"],
"external_reporting",
);
assert_eq!(
discarded_outcome(&report, "compliance.external_authority"),
None
);
assert_eq!(
contribution_outcome(&report, "compliance.evidence_input_verification").as_deref(),
Some("reject")
);
assert_eq!(report["verifier"]["invoked"], true);
assert_eq!(report["verifier"]["state"], "partial");
}
#[test]
fn compliance_evidence_reads_file_and_verifies_declared_digest_before_policy_reject() {
let input = authority_grade_input("external_authority");
let digest = blake3_hex(&input);
let tmp = tempfile::tempdir().unwrap();
let path = tmp.path().join("compliance-evidence.json");
fs::write(&path, &input).unwrap();
let out = run(&[
"compliance",
"evidence",
"--evidence-file",
path.to_str().unwrap(),
"--evidence-blake3",
&digest,
]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "compliance.evidence");
assert_eq!(report["artifact_emitted"], false);
assert_eq!(report["trusted_stdout_artifact"], false);
assert_eq!(report["policy_outcome"], "reject");
assert_eq!(report["evidence_input"]["accepted"], false);
assert_eq!(report["evidence_input"]["verification_state"], "partial");
assert_eq!(report["evidence_input"]["source_refs_valid"], true);
assert_eq!(report["evidence_input"]["digest_verified"], true);
assert_missing_evidence(
&report,
"compliance.external_authority_independent_verification",
);
assert_eq!(
contribution_outcome(&report, "compliance.evidence_input_verification").as_deref(),
Some("reject")
);
}
#[test]
fn compliance_evidence_rejects_malformed_source_ref_before_accepting_input() {
let input = malformed_source_ref_input("external_authority");
let digest = blake3_hex(&input);
let out = run(&[
"compliance",
"evidence",
"--evidence-json",
&input,
"--evidence-blake3",
&digest,
]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "compliance.evidence");
assert_eq!(report["trusted_stdout_artifact"], false);
assert_eq!(report["policy_outcome"], "reject");
assert_eq!(report["evidence_input"]["accepted"], false);
assert_eq!(report["evidence_input"]["source_refs_valid"], false);
assert_eq!(report["evidence_input"]["digest_verified"], true);
assert_eq!(
report["evidence_input"]["failed_rule"],
"compliance.evidence_input.malformed_source_refs"
);
assert_missing_evidence(&report, "compliance.authority_source_refs");
}
#[test]
fn compliance_evidence_rejects_missing_digest_with_stable_fields() {
let input = authority_grade_input("external_authority");
let out = run(&["compliance", "evidence", "--evidence-json", &input]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "compliance.evidence");
assert_eq!(report["trusted_artifact_emitted"], false);
assert_eq!(report["independent_verification"], false);
assert_eq!(report["evidence_input"]["accepted"], false);
assert_eq!(report["evidence_input"]["digest_verified"], false);
assert_eq!(
report["evidence_input"]["failed_rule"],
"compliance.evidence_input.missing_digest"
);
assert_missing_evidence(&report, "compliance.evidence_input_blake3");
}
#[test]
fn compliance_evidence_rejects_missing_source_refs_with_stable_fields() {
let input = missing_source_ref_input("external_authority");
let digest = blake3_hex(&input);
let out = run(&[
"compliance",
"evidence",
"--evidence-json",
&input,
"--evidence-blake3",
&digest,
]);
assert_exit(&out, 7);
assert!(out.stdout.is_empty());
let report: serde_json::Value =
serde_json::from_slice(&out.stderr).expect("stderr preflight report is JSON");
assert_eq!(report["command"], "compliance.evidence");
assert_eq!(report["trusted_artifact_emitted"], false);
assert_eq!(report["evidence_input"]["accepted"], false);
assert_eq!(report["evidence_input"]["source_refs_present"], false);
assert_eq!(report["evidence_input"]["source_refs_valid"], true);
assert_eq!(report["evidence_input"]["digest_verified"], true);
assert_eq!(
report["evidence_input"]["failed_rule"],
"compliance.evidence_input.missing_source_refs"
);
assert_missing_evidence(&report, "compliance.evidence_input_source_refs");
}