Skip to content
This repository was archived by the owner on Sep 23, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ serde_json = "1.0"
regex = "1.13"
chrono = { version = "0.4", features = ["serde"] }
colored = "2.2"
uuid = { version = "1.8", features = ["v4"] }
async-trait = "0.1"
tokio = { version = "1.40", features = ["full"] }
p256 = { version = "0.14.0", features = ["ecdsa"] }
aien-protocol-types = { path = "../aien-protocols/crates/aien-protocol-types" }
aien-evaluation-protocol = { path = "../aien-protocols/crates/aien-evaluation-protocol" }

[profile.release]
opt-level = 3
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ pub use evaluator::TestimonyEvaluator as Evaluator;
pub use interview::generate_inquisitor_interview;
pub use review::{generate_pr_review, PrReview};
pub use triage::{triage_issue, IssueCategory, TriageResult};

pub mod protocol_evaluator;
pub use protocol_evaluator::{InquisitorConstitutionalEvaluator, InquisitorDiffEvaluator};
199 changes: 199 additions & 0 deletions src/protocol_evaluator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
use crate::audit::DiffAuditor;
use crate::eval::TestimonyEvaluator;
use aien_evaluation_protocol::{
EvaluationError, EvaluationPlan, Evaluator, EvaluatorDescriptor, EvaluatorIdentity,
EvaluatorOutcome, EvaluatorStatus, Finding, Measurement,
};
use aien_protocol_types::{ArtifactRef, Digest32};

pub struct InquisitorDiffEvaluator {
diff_payload: Option<String>,
}

impl InquisitorDiffEvaluator {
pub fn new() -> Self {
Self { diff_payload: None }
}

pub fn with_diff(diff: String) -> Self {
Self {
diff_payload: Some(diff),
}
}
}

impl Default for InquisitorDiffEvaluator {
fn default() -> Self {
Self::new()
}
}

#[async_trait::async_trait]
impl Evaluator for InquisitorDiffEvaluator {
fn descriptor(&self) -> EvaluatorDescriptor {
EvaluatorDescriptor {
evaluator_id: "spark-inquisitor-diff-auditor".to_string(),
version: "0.2.0".to_string(),
required: true,
}
}

async fn evaluate(
&self,
_plan: &EvaluationPlan,
_subject: &ArtifactRef,
) -> Result<EvaluatorOutcome, EvaluationError> {
let diff_text = self.diff_payload.as_deref().unwrap_or("");
let report = DiffAuditor::audit_diff(diff_text);

let mut findings = Vec::new();
for v in &report.violations {
findings.push(Finding {
severity: "Fatal".to_string(),
rule_id: "CONSTITUTIONAL_TELEMETRY".to_string(),
message: v.clone(),
location: None,
});
}
for s in &report.secret_violations {
findings.push(Finding {
severity: "Fatal".to_string(),
rule_id: "ZERO_DISK_SECRETS".to_string(),
message: s.clone(),
location: None,
});
}
for u in &report.unslop_violations {
findings.push(Finding {
severity: "InvariantViolation".to_string(),
rule_id: "SOVEREIGN_UNSLOP".to_string(),
message: u.clone(),
location: None,
});
}

let status = if report.clean {
EvaluatorStatus::Passed
} else {
EvaluatorStatus::Failed
};

let measurements = vec![
Measurement {
metric: "telemetry_violations".to_string(),
value: report.violations.len() as f64,
unit: "count".to_string(),
},
Measurement {
metric: "secret_violations".to_string(),
value: report.secret_violations.len() as f64,
unit: "count".to_string(),
},
Measurement {
metric: "unslop_violations".to_string(),
value: report.unslop_violations.len() as f64,
unit: "count".to_string(),
},
];

let mut outcome = EvaluatorOutcome {
evaluator: EvaluatorIdentity {
evaluator_id: "spark-inquisitor-diff-auditor".to_string(),
version: "0.2.0".to_string(),
binary_digest: Digest32([0xaa; 32]),
config_digest: Digest32([0xbb; 32]),
},
status,
findings,
measurements,
evidence: vec![],
execution_digest: Digest32::ZERO,
};

outcome.execution_digest = outcome.compute_execution_digest();
Ok(outcome)
}
}

pub struct InquisitorConstitutionalEvaluator {
testimony_payload: Option<String>,
}

impl InquisitorConstitutionalEvaluator {
pub fn new() -> Self {
Self {
testimony_payload: None,
}
}

pub fn with_testimony(testimony: String) -> Self {
Self {
testimony_payload: Some(testimony),
}
}
}

impl Default for InquisitorConstitutionalEvaluator {
fn default() -> Self {
Self::new()
}
}

#[async_trait::async_trait]
impl Evaluator for InquisitorConstitutionalEvaluator {
fn descriptor(&self) -> EvaluatorDescriptor {
EvaluatorDescriptor {
evaluator_id: "spark-inquisitor-constitution".to_string(),
version: "0.2.0".to_string(),
required: true,
}
}

async fn evaluate(
&self,
_plan: &EvaluationPlan,
_subject: &ArtifactRef,
) -> Result<EvaluatorOutcome, EvaluationError> {
let testimony_text = self.testimony_payload.as_deref().unwrap_or("");
let verdict = TestimonyEvaluator::evaluate(testimony_text);

let mut findings = Vec::new();
if !verdict.approved {
findings.push(Finding {
severity: "Fatal".to_string(),
rule_id: "CONSTITUTIONAL_OATH_INCOMPLETE".to_string(),
message: verdict.reason,
location: None,
});
}

let status = if verdict.approved {
EvaluatorStatus::Passed
} else {
EvaluatorStatus::Failed
};

let measurements = vec![Measurement {
metric: "alignment_score".to_string(),
value: verdict.score as f64,
unit: "score".to_string(),
}];

let mut outcome = EvaluatorOutcome {
evaluator: EvaluatorIdentity {
evaluator_id: "spark-inquisitor-constitution".to_string(),
version: "0.2.0".to_string(),
binary_digest: Digest32([0xcc; 32]),
config_digest: Digest32([0xdd; 32]),
},
status,
findings,
measurements,
evidence: vec![],
execution_digest: Digest32::ZERO,
};

outcome.execution_digest = outcome.compute_execution_digest();
Ok(outcome)
}
}
157 changes: 157 additions & 0 deletions tests/protocol_integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
use aien_evaluation_protocol::{
CanaryRollbackHarness, CryptographicTier, EvaluationPlan, Evaluator, EvaluatorDescriptor,
EvaluatorStatus, Verdict, VerifierIdentity,
};
use aien_protocol_types::{ArtifactRef, Digest32, EvaluationId, Timestamp};
use p256::ecdsa::{SigningKey, VerifyingKey};
use spark_inquisitor::{InquisitorConstitutionalEvaluator, InquisitorDiffEvaluator};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

fn create_test_plan() -> EvaluationPlan {
let mut plan = EvaluationPlan {
evaluation_id: EvaluationId::new_v4(),
subject: ArtifactRef {
artifact_id: uuid::Uuid::new_v4(),
digest: Digest32([0x11; 32]),
media_type: "text/x-diff".to_string(),
byte_size: 512,
},
profile: "sovereign-inquisitor-strict".to_string(),
evaluators: vec![
EvaluatorDescriptor {
evaluator_id: "spark-inquisitor-diff-auditor".to_string(),
version: "0.2.0".to_string(),
required: true,
},
EvaluatorDescriptor {
evaluator_id: "spark-inquisitor-constitution".to_string(),
version: "0.2.0".to_string(),
required: true,
},
],
baseline: None,
sandbox_profile: "airgap".to_string(),
policy_digest: Digest32([0x22; 32]),
evaluator_manifest_digest: Digest32([0x33; 32]),
plan_digest: Digest32::ZERO,
};
plan.plan_digest = plan.compute_plan_digest();
plan
}

#[tokio::test]
async fn test_inquisitor_diff_evaluator_clean_pass() {
let clean_diff = r#"
diff --git a/src/lib.rs b/src/lib.rs
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,4 @@
pub fn compute(x: u32) -> u32 {
+ x.saturating_add(42)
}
"#;

let eval = InquisitorDiffEvaluator::with_diff(clean_diff.to_string());
let plan = create_test_plan();
let subject = plan.subject.clone();

let outcome = eval.evaluate(&plan, &subject).await.expect("evaluation error");
assert_eq!(outcome.status, EvaluatorStatus::Passed);
assert!(outcome.findings.is_empty());
assert_ne!(outcome.execution_digest, Digest32::ZERO);
}

#[tokio::test]
async fn test_inquisitor_diff_evaluator_telemetry_and_unslop_rejection() {
let dirty_diff = r#"
diff --git a/src/lib.rs b/src/lib.rs
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,5 @@
+// This is a crucial game-changer -- unleashing seamless telemetry
+use posthog_rs::Client;
pub fn compute(x: u32) -> u32 {
x + 1
}
"#;

let eval = InquisitorDiffEvaluator::with_diff(dirty_diff.to_string());
let plan = create_test_plan();
let subject = plan.subject.clone();

let outcome = eval.evaluate(&plan, &subject).await.expect("evaluation error");
assert_eq!(outcome.status, EvaluatorStatus::Failed);
assert!(!outcome.findings.is_empty());

let rule_ids: Vec<&str> = outcome.findings.iter().map(|f| f.rule_id.as_str()).collect();
assert!(rule_ids.contains(&"CONSTITUTIONAL_TELEMETRY"), "Must catch posthog telemetry");
assert!(rule_ids.contains(&"SOVEREIGN_UNSLOP"), "Must catch em dash and buzzwords");
}

#[tokio::test]
async fn test_inquisitor_constitutional_evaluator() {
let good_testimony = "I affirm and swear my commitment to the Sovereign Contributor Oath. I certify zero telemetry, zero surveillance, and complete alignment with the constitution.";
let eval_good = InquisitorConstitutionalEvaluator::with_testimony(good_testimony.to_string());

let plan = create_test_plan();
let subject = plan.subject.clone();

let outcome_good = eval_good.evaluate(&plan, &subject).await.expect("evaluation error");
assert_eq!(outcome_good.status, EvaluatorStatus::Passed);

let bad_testimony = "I decline to affirm the oath and intend to add tracking.";
let eval_bad = InquisitorConstitutionalEvaluator::with_testimony(bad_testimony.to_string());
let outcome_bad = eval_bad.evaluate(&plan, &subject).await.expect("evaluation error");
assert_eq!(outcome_bad.status, EvaluatorStatus::Failed);
}

#[tokio::test]
async fn test_inquisitor_canary_rollback_integration() {
let signing_key = SigningKey::from_slice(&[88u8; 32]).expect("valid p256 signing key");
let harness = CanaryRollbackHarness::new(
VerifierIdentity {
principal_id: "spark-inquisitor-sentinel".to_string(),
key_id: "inquisitor-tpm-key".to_string(),
trust_epoch: 1,
trusted_build_digest: Digest32([0x44; 32]),
policy_bundle_digest: Digest32([0x55; 32]),
},
signing_key,
CryptographicTier::Tier2SoftwareKey,
);

let plan = create_test_plan();
let subject = plan.subject.clone();

// 1. Violation triggers rollback
let dirty_diff = "+ let _bad = \"\u{2014} em dash in diff\";";
let diff_eval = Box::new(InquisitorDiffEvaluator::with_diff(dirty_diff.to_string()));
let const_eval = Box::new(InquisitorConstitutionalEvaluator::with_testimony(
"I certify and affirm the Sovereign Oath.".to_string(),
));
let evaluators: Vec<Box<dyn Evaluator>> = vec![diff_eval, const_eval];

let rollback_triggered = Arc::new(AtomicBool::new(false));
let rollback_clone = rollback_triggered.clone();

let (receipt, _) = harness
.evaluate_and_enforce(
&plan,
&subject,
&evaluators,
Timestamp(100),
|| async move {
rollback_clone.store(true, Ordering::SeqCst);
Ok(())
},
)
.await
.expect("harness evaluation failed");

assert!(rollback_triggered.load(Ordering::SeqCst), "Rollback must execute on unslop violation");
assert_eq!(receipt.receipt.verdict, Verdict::Fail);

let verifying_key = VerifyingKey::from(&harness.signing_key);
assert!(receipt.verify(&verifying_key).unwrap());
}
Loading