pub mod bundle;
pub mod effect;
pub mod exception;
pub mod facts;
pub mod join;
pub mod kleene;
pub mod tier1;
pub mod tier2;
pub mod tier3;
pub mod types;
use crate::generated::types::PolicyMode;
pub use bundle::{load, ArtifactBody, Bundle, BundleError, LoadedArtifact};
pub use kleene::{Inconclusive, Kleene};
pub use types::{
AgentContext, Anomaly, Classification, Contribution, Decision, Effect, EvalContext, Event,
EventEnv, HoldRequest, Rewrite, RunState, SessionFacts, SessionState, SkippedItem, SpendDelta,
StateLayout, UnknownCommand,
};
pub const ENGINE_VERSION: &str = "1.0.0";
pub const PROTOCOL_VERSION: u32 = 1;
pub fn evaluate(
bundle: &Bundle,
event: &Event,
state: Option<&SessionState>,
now_ms: i64,
) -> (Decision, SessionState) {
let facts = facts::resolve(bundle, now_ms);
let classification = effect::classify(event, &facts, bundle.effect_classes.as_ref());
let mut ctx = EvalContext::new(event, &classification, &facts, now_ms);
for fact_id in facts.declared_ids() {
if !facts::is_known_fact_id(&fact_id) {
ctx.warn(format!(
"fact_id '{fact_id}' is not a known FactId \
(schemas/enums.schema.json $defs/FactId x-known-values)"
));
}
}
let scan = bundle.scan.scan(&ctx);
let evicted = state.is_none();
let mut working = match state {
Some(state) => state.clone(),
None => SessionState::blank(&bundle.state_layout),
};
let mut contributions: Vec<Contribution> = Vec::new();
for artifact in &bundle.artifacts {
let monitored;
let artifact = if bundle.enforcement_enabled {
artifact
} else {
monitored = as_monitor(artifact);
&monitored
};
let contribution = match &artifact.body {
ArtifactBody::T1(body) => {
tier1::contribution_with(artifact, body, &mut ctx, &bundle.scan, &scan)
}
ArtifactBody::T2(body) => {
if evicted {
tier2::evicted_contribution(
artifact,
body,
tier2::declared_mode(artifact),
&bundle.state_layout,
)
.or_else(|| {
tier2::contribution(
artifact,
body,
&mut ctx,
&bundle.scan,
&mut working,
&bundle.state_layout,
)
})
} else {
tier2::contribution(
artifact,
body,
&mut ctx,
&bundle.scan,
&mut working,
&bundle.state_layout,
)
}
}
ArtifactBody::T3(body) => tier3::contribution(artifact, body, &mut ctx, &bundle.scan),
ArtifactBody::Exception(_) => None,
};
if let Some(contribution) = contribution {
contributions.push(contribution);
}
}
exception::apply(
&mut contributions,
&bundle.exceptions,
&ctx,
bundle
.meta
.as_ref()
.and_then(|meta| meta.effective_zone.as_ref()),
);
let decision = join::assemble(&contributions, &ctx);
(decision, working)
}
fn as_monitor(artifact: &LoadedArtifact) -> LoadedArtifact {
let mut artifact = artifact.clone();
artifact.envelope.mode = Some(PolicyMode(types::MODE_MONITOR.to_string()));
artifact
}
#[cfg(test)]
mod tests {
use super::*;
use crate::generated::types::Verdict;
fn empty_bundle() -> Bundle {
load(serde_json::json!({
"schema_version": 2,
"organization_id": "org",
"revision": 1,
"built_at": "2026-09-01T00:00:00Z",
"enforcement_enabled": true,
"signature": null,
"artifacts": [],
}))
.expect("the envelope parses")
}
#[test]
fn an_empty_bundle_allows_and_reports_undecided() {
let bundle = empty_bundle();
let event = Event::default();
let state = SessionState::blank(&bundle.state_layout);
let (decision, state_out) = evaluate(&bundle, &event, Some(&state), 1_756_742_400_000);
assert_eq!(decision.verdict, Verdict::Allow);
assert!(decision.undecided, "nothing decided it");
assert_eq!(state_out, state, "a stateless bundle advances no state");
}
#[test]
fn evaluation_is_deterministic_for_the_same_inputs() {
let bundle = empty_bundle();
let event = Event::default();
let state = SessionState::blank(&bundle.state_layout);
let first = evaluate(&bundle, &event, Some(&state), 1_756_742_400_000);
let second = evaluate(&bundle, &event, Some(&state), 1_756_742_400_000);
assert_eq!(first, second);
}
#[test]
fn an_unknown_fact_id_warns_and_still_evaluates() {
let bundle = load(serde_json::json!({
"schema_version": 2,
"organization_id": "org",
"revision": 1,
"built_at": "2026-09-01T00:00:00Z",
"enforcement_enabled": true,
"signature": null,
"artifacts": [],
"facts": [{"fact_id": "change_ticket", "kind": "set", "value": []}],
}))
.expect("the envelope parses");
let event = Event::default();
let state = SessionState::blank(&bundle.state_layout);
let (decision, _) = evaluate(&bundle, &event, Some(&state), 1_756_742_400_000);
assert_eq!(
decision.warnings.len(),
1,
"the typo is named, not swallowed"
);
assert_eq!(decision.verdict, Verdict::Allow, "and it still evaluates");
}
#[test]
fn an_evicted_session_is_not_a_blank_one_at_the_boundary() {
let bundle = empty_bundle();
let event = Event::default();
let blank = SessionState::blank(&bundle.state_layout);
let (_, from_none) = evaluate(&bundle, &event, None, 1_756_742_400_000);
let (_, from_blank) = evaluate(&bundle, &event, Some(&blank), 1_756_742_400_000);
assert_eq!(from_none, from_blank);
assert_eq!(from_none, blank);
}
fn bundle_of(enforcement_enabled: bool, artifact: serde_json::Value) -> Bundle {
load(serde_json::json!({
"schema_version": 2,
"organization_id": "org",
"revision": 1,
"built_at": "2026-09-01T00:00:00Z",
"enforcement_enabled": enforcement_enabled,
"signature": null,
"artifacts": [artifact],
}))
.expect("the envelope parses")
}
fn inconclusive_artifact(on_inconclusive: &str) -> serde_json::Value {
serde_json::json!({
"artifact_id": "probe",
"atom_id": "atom-probe",
"mode": "enforce",
"tier": 1,
"kind": "t1_predicate_tree",
"on_inconclusive": on_inconclusive,
"body": {
"node": {
"op": "leaf",
"leaf": {"pred": "fact", "fact": {
"fact_id": "change_ticket", "op": "equals", "value": true
}},
},
"verdict": "block",
"reason": "probe fired",
},
})
}
#[test]
fn switching_enforcement_off_changes_what_every_artifact_contributes() {
let artifact = inconclusive_artifact("block");
let event = Event::default();
let now = 1_756_742_400_000;
let on = bundle_of(true, artifact.clone());
let (decision, _) = evaluate(&on, &event, None, now);
assert_eq!(decision.verdict, Verdict::Block, "enforce blocks");
assert_eq!(decision.mode.map(|m| m.0), Some("enforce".to_string()));
let off = bundle_of(false, artifact);
let (decision, _) = evaluate(&off, &event, None, now);
assert_eq!(decision.verdict, Verdict::Allow, "monitor never joins");
assert_eq!(decision.artifact_id, None);
assert!(
!decision.undecided,
"it contributed, it just did not decide"
);
assert_eq!(
decision.would_have_verdict,
Some(Verdict::Allow),
"the kill switch reaches on_inconclusive, not only the mode field"
);
}
#[test]
fn an_evicted_fail_static_artifact_blocks_rather_than_failing_open() {
let bundle = bundle_of(
true,
serde_json::json!({
"artifact_id": "prog",
"atom_id": "atom-prog",
"mode": "enforce",
"tier": 2,
"kind": "t2_register_program",
"body": {
"state_layout": {"c": 1, "f": 0, "t": 0, "a": 0, "run": false},
"pre": [],
"post": [],
"on_evict": "fail_static",
"verdict": "block",
"reason": "the registers are gone and this rule keeps enforcing",
},
}),
);
let event = Event::default();
let (decision, _) = evaluate(&bundle, &event, None, 1_756_742_400_000);
assert_eq!(decision.verdict, Verdict::Block);
assert_eq!(decision.artifact_id.as_deref(), Some("prog"));
let state = SessionState::blank(&bundle.state_layout);
let (decision, _) = evaluate(&bundle, &event, Some(&state), 1_756_742_400_000);
assert!(decision.undecided, "a present state runs the program");
}
#[test]
fn the_scan_is_computed_once_per_event_and_changes_no_answer() {
let keyword = |id: &str| {
serde_json::json!({
"artifact_id": id,
"atom_id": format!("atom-{id}"),
"mode": "enforce",
"tier": 1,
"kind": "t1_predicate_tree",
"body": {
"node": {"op": "leaf", "leaf": {
"pred": "keyword", "field": "input.strings", "value": ["curl"]
}},
"verdict": "block",
"reason": format!("{id} fired"),
},
})
};
let bundle = load(serde_json::json!({
"schema_version": 2,
"organization_id": "org",
"revision": 1,
"built_at": "2026-09-01T00:00:00Z",
"enforcement_enabled": true,
"signature": null,
"artifacts": [keyword("a"), keyword("b")],
}))
.expect("the envelope parses");
let event = Event {
tool_name: "Bash".to_string(),
tool_input: serde_json::json!({"command": "curl https://example.com"}),
..Default::default()
};
let (decision, _) = evaluate(&bundle, &event, None, 1_756_742_400_000);
assert_eq!(decision.verdict, Verdict::Block);
assert_eq!(
decision.artifact_id.as_deref(),
Some("a"),
"both fired; the tie breaks on artifact_id"
);
}
#[test]
fn the_engine_version_is_not_the_crate_version() {
assert_ne!(
ENGINE_VERSION,
env!("CARGO_PKG_VERSION"),
"engine semantics and the client release have independent version lifecycles"
);
}
}