use std::collections::{HashMap, HashSet};
use serde_json::Value;
use time::{OffsetDateTime, format_description::well_known::Rfc3339};
use crate::{
CONSTRAINT_PROVENANCE_FIELDS, CandidateCluster, DISPOSITIONS,
EVOLUTION_ADJUDICATING_DISPOSITIONS, EVOLUTION_INSTRUMENT_LIMITED_DISPOSITIONS,
EXTERNAL_OWNER_KINDS, GateStatus, SYMPTOM_KEYS,
};
const USE_PAYLOAD_KEYS: &[&str] = &[
"qualifying_use",
"retrospective",
"task_label",
"task_fingerprint",
"outcome",
"symptom_key",
"expected",
"observed",
"consequence",
"workaround_taken",
"evidence_refs",
"same_run_group",
];
const USE_PAYLOAD_OPTIONAL_KEYS: &[&str] = &["run_condition"];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum EventType {
UseRecorded,
ReviewStarted,
ReviewDisposition,
ValidationCompleted,
ChangeLanded,
DecontaminationStarted,
DecontaminationCompleted,
}
impl EventType {
fn parse(value: &str) -> Option<Self> {
match value {
"use_recorded" => Some(Self::UseRecorded),
"review_started" => Some(Self::ReviewStarted),
"review_disposition" => Some(Self::ReviewDisposition),
"validation_completed" => Some(Self::ValidationCompleted),
"change_landed" => Some(Self::ChangeLanded),
"decontamination_started" => Some(Self::DecontaminationStarted),
"decontamination_completed" => Some(Self::DecontaminationCompleted),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Outcome {
Clean,
Friction,
MaterialFailure,
SevereIncident,
}
impl Outcome {
pub(crate) const ALLOWED: &'static str = "clean|friction|material_failure|severe_incident";
pub(crate) fn parse(value: &str) -> Option<Self> {
match value {
"clean" => Some(Self::Clean),
"friction" => Some(Self::Friction),
"material_failure" => Some(Self::MaterialFailure),
"severe_incident" => Some(Self::SevereIncident),
_ => None,
}
}
pub(crate) fn severity(self) -> u8 {
match self {
Self::Clean => 0,
Self::Friction => 1,
Self::MaterialFailure => 2,
Self::SevereIncident => 3,
}
}
pub(crate) fn as_str(self) -> &'static str {
match self {
Self::Clean => "clean",
Self::Friction => "friction",
Self::MaterialFailure => "material_failure",
Self::SevereIncident => "severe_incident",
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct UseRecordedEvent {
pub(crate) retrospective: bool,
pub(crate) task_fingerprint: String,
pub(crate) same_run_group: String,
pub(crate) outcome: Outcome,
pub(crate) symptom_key: Option<String>,
}
#[derive(Debug, Clone)]
pub(crate) enum EventKind {
UseRecorded(UseRecordedEvent),
ReviewStarted {
review_id: String,
},
ReviewDisposition {
review_id: String,
disposition: String,
adjudicated_event_ids: Vec<String>,
instrument_limited_event_ids: Vec<String>,
},
ValidationCompleted {
review_id: String,
},
ChangeLanded {
review_id: String,
},
DecontaminationStarted {
review_id: String,
},
DecontaminationCompleted {
review_id: String,
},
}
#[derive(Debug, Clone)]
pub(crate) struct EvidenceEvent {
pub(crate) event_id: String,
pub(crate) recorded_at: String,
pub(crate) target_content_hash: String,
pub(crate) top_level_session_id: String,
pub(crate) kind: EventKind,
pub(crate) raw: Value,
}
impl EvidenceEvent {
fn from_validated(event: &Value) -> Self {
let payload = event
.get("payload")
.and_then(Value::as_object)
.expect("validated payload");
let event_type = event
.get("event_type")
.and_then(Value::as_str)
.and_then(EventType::parse)
.expect("validated event type");
let review_id = || {
payload
.get("review_id")
.and_then(Value::as_str)
.expect("validated review id")
.to_owned()
};
let kind = match event_type {
EventType::UseRecorded => EventKind::UseRecorded(UseRecordedEvent {
retrospective: payload
.get("retrospective")
.and_then(Value::as_bool)
.expect("validated retrospective flag"),
task_fingerprint: payload
.get("task_fingerprint")
.and_then(Value::as_str)
.expect("validated task fingerprint")
.to_owned(),
same_run_group: payload
.get("same_run_group")
.and_then(Value::as_str)
.expect("validated run group")
.to_owned(),
outcome: payload
.get("outcome")
.and_then(Value::as_str)
.and_then(Outcome::parse)
.expect("validated outcome"),
symptom_key: payload
.get("symptom_key")
.and_then(Value::as_str)
.map(str::to_owned),
}),
EventType::ReviewStarted => EventKind::ReviewStarted {
review_id: review_id(),
},
EventType::ReviewDisposition => EventKind::ReviewDisposition {
review_id: review_id(),
disposition: payload
.get("disposition")
.and_then(Value::as_str)
.expect("validated review disposition")
.to_owned(),
adjudicated_event_ids: payload
.get("adjudicated_event_ids")
.and_then(Value::as_array)
.expect("validated adjudicated event identities")
.iter()
.map(|identity| {
identity
.as_str()
.expect("validated adjudicated event identity")
.to_owned()
})
.collect(),
instrument_limited_event_ids: payload
.get("instrument_limited_event_ids")
.and_then(Value::as_array)
.map(|identities| {
identities
.iter()
.map(|identity| {
identity
.as_str()
.expect("validated instrument-limited event identity")
.to_owned()
})
.collect()
})
.unwrap_or_default(),
},
EventType::ValidationCompleted => EventKind::ValidationCompleted {
review_id: review_id(),
},
EventType::ChangeLanded => EventKind::ChangeLanded {
review_id: review_id(),
},
EventType::DecontaminationStarted => EventKind::DecontaminationStarted {
review_id: review_id(),
},
EventType::DecontaminationCompleted => EventKind::DecontaminationCompleted {
review_id: review_id(),
},
};
Self {
event_id: event
.get("event_id")
.and_then(Value::as_str)
.expect("validated event id")
.to_owned(),
recorded_at: event
.get("recorded_at")
.and_then(Value::as_str)
.expect("validated event timestamp")
.to_owned(),
target_content_hash: event
.pointer("/target/content_hash")
.and_then(Value::as_str)
.expect("validated target content hash")
.to_owned(),
top_level_session_id: event
.get("top_level_session_id")
.and_then(Value::as_str)
.expect("validated top-level session identity")
.to_owned(),
kind,
raw: event.clone(),
}
}
pub(crate) fn review_id(&self) -> Option<&str> {
match &self.kind {
EventKind::UseRecorded(_) => None,
EventKind::ReviewStarted { review_id }
| EventKind::ReviewDisposition { review_id, .. }
| EventKind::ValidationCompleted { review_id }
| EventKind::ChangeLanded { review_id }
| EventKind::DecontaminationStarted { review_id }
| EventKind::DecontaminationCompleted { review_id } => Some(review_id),
}
}
pub(crate) fn use_recorded(&self) -> Option<&UseRecordedEvent> {
match &self.kind {
EventKind::UseRecorded(recorded) => Some(recorded),
_ => None,
}
}
pub(crate) fn is_review_start(&self) -> bool {
matches!(self.kind, EventKind::ReviewStarted { .. })
}
pub(crate) fn starts_ownership(&self) -> bool {
matches!(
self.kind,
EventKind::ReviewStarted { .. } | EventKind::DecontaminationStarted { .. }
)
}
pub(crate) fn terminates_ownership(&self) -> bool {
matches!(
self.kind,
EventKind::ReviewDisposition { .. }
| EventKind::ChangeLanded { .. }
| EventKind::DecontaminationCompleted { .. }
)
}
}
#[derive(Debug)]
pub(crate) enum ThresholdReason {
Severe,
MaterialRecurrence(String),
FrictionRecurrence(String),
TenUseUnresolved,
}
impl ThresholdReason {
pub(crate) fn parse(value: &str) -> Option<Self> {
match value {
"severe" => Some(Self::Severe),
"ten_use_unresolved" => Some(Self::TenUseUnresolved),
_ => value
.strip_prefix("material_recurrence:")
.filter(|symptom| !symptom.is_empty())
.map(|symptom| Self::MaterialRecurrence(symptom.to_owned()))
.or_else(|| {
value
.strip_prefix("friction_recurrence:")
.filter(|symptom| !symptom.is_empty())
.map(|symptom| Self::FrictionRecurrence(symptom.to_owned()))
}),
}
}
pub(crate) fn as_string(&self) -> String {
match self {
Self::Severe => "severe".to_owned(),
Self::MaterialRecurrence(symptom) => {
format!("material_recurrence:{symptom}")
}
Self::FrictionRecurrence(symptom) => {
format!("friction_recurrence:{symptom}")
}
Self::TenUseUnresolved => "ten_use_unresolved".to_owned(),
}
}
pub(crate) fn is_severe(&self) -> bool {
matches!(self, Self::Severe)
}
}
pub(crate) fn authorization_reason_names_incident(
reason: Option<&ThresholdReason>,
anchor_symptoms: &HashSet<&str>,
recorded: &UseRecordedEvent,
) -> bool {
match reason {
Some(ThresholdReason::Severe) => false,
Some(ThresholdReason::TenUseUnresolved) => {
!recorded.retrospective
&& recorded
.symptom_key
.as_deref()
.is_some_and(|symptom| anchor_symptoms.contains(symptom))
}
Some(ThresholdReason::MaterialRecurrence(symptom)) => {
recorded.symptom_key.as_deref() == Some(symptom.as_str())
&& recorded.outcome.severity() >= Outcome::MaterialFailure.severity()
}
Some(ThresholdReason::FrictionRecurrence(symptom)) => {
recorded.symptom_key.as_deref() == Some(symptom.as_str())
}
None => recorded
.symptom_key
.as_deref()
.is_some_and(|symptom| anchor_symptoms.contains(symptom)),
}
}
#[derive(Debug)]
struct ThresholdTrigger {
reason: ThresholdReason,
trigger_event_ids: Vec<String>,
threshold_session_id: Option<String>,
fired_at: String,
event_index: usize,
}
#[derive(Debug, Clone)]
pub(crate) struct ValidatedStream {
events: Vec<EvidenceEvent>,
integrity_errors: Vec<String>,
}
impl ValidatedStream {
pub(crate) fn parse(bytes: &[u8], source: &str) -> Self {
let Ok(text) = std::str::from_utf8(bytes) else {
return Self {
events: Vec::new(),
integrity_errors: vec![format!("event stream is not valid UTF-8: {source}")],
};
};
let mut events = Vec::new();
let mut integrity_errors = Vec::new();
let mut seen_ids = HashSet::new();
for (line_index, line) in text.lines().enumerate() {
if line.trim().is_empty() {
continue;
}
match serde_json::from_str::<Value>(line) {
Ok(event) => {
let event_errors = validate_event(&event, &seen_ids);
if event_errors.is_empty() {
if let Some(event_id) = non_empty_string(event.get("event_id")) {
seen_ids.insert(event_id.to_owned());
}
events.push(EvidenceEvent::from_validated(&event));
} else {
integrity_errors.extend(
event_errors
.into_iter()
.map(|message| format!("line {}: {message}", line_index + 1)),
);
}
}
Err(_) => {
integrity_errors.push(format!("line {}: not valid JSON", line_index + 1));
}
}
}
Self {
events,
integrity_errors,
}
}
pub(crate) fn empty() -> Self {
Self {
events: Vec::new(),
integrity_errors: Vec::new(),
}
}
pub(crate) fn events(&self) -> &[EvidenceEvent] {
&self.events
}
pub(crate) fn integrity_errors(&self) -> &[String] {
&self.integrity_errors
}
pub(crate) fn is_intact(&self) -> bool {
self.integrity_errors.is_empty()
}
pub(crate) fn with_integrity_error(mut self, message: String) -> Self {
self.integrity_errors.push(message);
self
}
pub(crate) fn without_event(&self, event_id: &str) -> Self {
Self {
events: self
.events
.iter()
.filter(|event| event.event_id != event_id)
.cloned()
.collect(),
integrity_errors: self.integrity_errors.clone(),
}
}
pub(crate) fn accept_candidate(&self, event: &Value) -> Result<Self, Vec<String>> {
let seen_ids = self
.events
.iter()
.map(|existing| existing.event_id.clone())
.collect::<HashSet<_>>();
let errors = validate_event(event, &seen_ids);
if !errors.is_empty() {
return Err(errors);
}
let mut events = self.events.clone();
events.push(EvidenceEvent::from_validated(event));
Ok(Self {
events,
integrity_errors: self.integrity_errors.clone(),
})
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct GateTarget<'a> {
pub(crate) name: &'a str,
pub(crate) repo_relative_path: &'a str,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct GateClock<'a> {
pub(crate) generated_at: &'a str,
pub(crate) now_epoch_milliseconds: i64,
pub(crate) session_id: &'a str,
}
fn is_valid_external_owner_entry(owner: &Value) -> bool {
owner.as_object().is_some_and(|entry| {
entry.len() == 3
&& ["event_id", "kind", "reference"]
.iter()
.all(|field| entry.contains_key(*field))
}) && non_empty_string(owner.get("event_id")).is_some()
&& non_empty_string(owner.get("kind"))
.is_some_and(|kind| EXTERNAL_OWNER_KINDS.contains(&kind))
&& non_empty_string(owner.get("reference")).is_some()
}
fn is_valid_constraint_provenance_entry(citation: &Value) -> bool {
citation.as_object().is_some_and(|entry| {
["constraint_label", "event_id", "field", "field_value"]
.iter()
.all(|field| entry.contains_key(*field))
}) && non_empty_string(citation.get("constraint_label")).is_some()
&& non_empty_string(citation.get("event_id")).is_some()
&& non_empty_string(citation.get("field"))
.is_some_and(|field| CONSTRAINT_PROVENANCE_FIELDS.contains(&field))
&& non_empty_string(citation.get("field_value")).is_some()
}
fn validate_event(event: &Value, seen_ids: &HashSet<String>) -> Vec<String> {
let mut errors = Vec::new();
let Some(event) = event.as_object() else {
return vec!["event is not an object".to_owned()];
};
if event.get("schema_version").and_then(Value::as_u64) != Some(1) {
errors.push("schema_version must be 1".to_owned());
}
match non_empty_string(event.get("event_id")) {
None => errors.push("event_id missing".to_owned()),
Some(event_id) if seen_ids.contains(event_id) => {
errors.push(format!("duplicate event_id {event_id}"));
}
Some(_) => {}
}
let event_type_name = non_empty_string(event.get("event_type"));
let event_type = event_type_name.and_then(EventType::parse);
if event_type.is_none() {
errors.push(format!(
"unknown event_type {}",
event
.get("event_type")
.map_or_else(|| "null".to_owned(), Value::to_string)
));
}
let timestamp_valid = non_empty_string(event.get("recorded_at"))
.is_some_and(|timestamp| OffsetDateTime::parse(timestamp, &Rfc3339).is_ok());
if !timestamp_valid {
errors.push("recorded_at is not a parseable timestamp".to_owned());
}
if non_empty_string(event.get("operator_workflow")).is_none() {
errors.push("operator_workflow missing".to_owned());
}
match event.get("target").and_then(Value::as_object) {
None => errors.push("target missing".to_owned()),
Some(target) => {
for key in ["name", "repo_relative_path", "content_hash", "repo_head"] {
if non_empty_string(target.get(key)).is_none() {
errors.push(format!("target.{key} missing"));
}
}
}
}
if non_empty_string(event.get("top_level_session_id")).is_none() {
errors.push("top_level_session_id missing".to_owned());
}
let Some(payload) = event.get("payload").and_then(Value::as_object) else {
errors.push("payload missing".to_owned());
return errors;
};
if matches!(
event_type,
Some(
EventType::ReviewStarted
| EventType::ValidationCompleted
| EventType::ChangeLanded
| EventType::ReviewDisposition
)
) && payload
.get("operating_skill_hash")
.is_some_and(|value| non_empty_string(Some(value)).is_none())
{
errors.push("operating_skill_hash must be a non-empty string when present".to_owned());
}
if matches!(
event_type,
Some(
EventType::ReviewStarted
| EventType::ValidationCompleted
| EventType::ChangeLanded
| EventType::ReviewDisposition
)
) && payload
.get("operating_package_matches_shipped")
.is_some_and(|value| !value.is_boolean())
{
errors.push("operating_package_matches_shipped must be boolean when present".to_owned());
}
if event_type == Some(EventType::UseRecorded) {
let missing = USE_PAYLOAD_KEYS
.iter()
.copied()
.filter(|key| !payload.contains_key(*key))
.collect::<Vec<_>>();
let unknown = payload
.keys()
.filter(|key| {
!USE_PAYLOAD_KEYS.contains(&key.as_str())
&& !USE_PAYLOAD_OPTIONAL_KEYS.contains(&key.as_str())
})
.cloned()
.collect::<Vec<_>>();
if !missing.is_empty() || !unknown.is_empty() {
let mut message = format!(
"use_recorded payload keys must be [{}] plus any of [{}]",
USE_PAYLOAD_KEYS.join(", "),
USE_PAYLOAD_OPTIONAL_KEYS.join(", ")
);
if !missing.is_empty() {
message.push_str(&format!("; missing: {}", missing.join(", ")));
}
if !unknown.is_empty() {
message.push_str(&format!("; unknown: {}", unknown.join(", ")));
}
errors.push(message);
return errors;
}
if payload.get("qualifying_use").and_then(Value::as_bool) != Some(true) {
errors.push("qualifying_use must be true".to_owned());
}
if payload
.get("retrospective")
.and_then(Value::as_bool)
.is_none()
{
errors.push("retrospective must be boolean".to_owned());
}
for key in ["task_label", "task_fingerprint", "same_run_group"] {
if non_empty_string(payload.get(key)).is_none() {
errors.push(format!("{key} missing"));
}
}
let outcome = non_empty_string(payload.get("outcome")).and_then(Outcome::parse);
if outcome.is_none() {
errors.push(format!("outcome must be one of {}", Outcome::ALLOWED));
}
let evidence_refs = payload.get("evidence_refs").and_then(Value::as_array);
if evidence_refs.is_none_or(|references| {
references
.iter()
.any(|reference| non_empty_string(Some(reference)).is_none())
}) {
errors.push("evidence_refs must be an array of non-empty strings".to_owned());
} else if payload.get("retrospective").and_then(Value::as_bool) == Some(true)
&& evidence_refs.is_some_and(Vec::is_empty)
{
errors.push("retrospective events require at least one evidence_ref".to_owned());
}
if outcome == Some(Outcome::Clean) {
for key in [
"symptom_key",
"expected",
"observed",
"consequence",
"workaround_taken",
] {
if payload.get(key).is_some_and(|value| !value.is_null()) {
errors.push(format!("{key} must be null for a clean outcome"));
}
}
if payload
.get("run_condition")
.is_some_and(|value| !value.is_null())
{
errors.push("run_condition must be null for a clean outcome".to_owned());
}
} else {
if non_empty_string(payload.get("symptom_key"))
.is_none_or(|key| !SYMPTOM_KEYS.contains(&key))
{
errors.push(format!(
"symptom_key must be one of {}",
SYMPTOM_KEYS.join("|")
));
}
for key in ["expected", "observed", "consequence"] {
if non_empty_string(payload.get(key)).is_none() {
errors.push(format!("{key} required for a non-clean outcome"));
}
}
for key in ["workaround_taken", "run_condition"] {
if payload.get(key).is_some_and(|value| {
!value.is_null() && non_empty_string(Some(value)).is_none()
}) {
errors.push(format!("{key} must be null or a non-empty string"));
}
}
}
} else if event_type == Some(EventType::ReviewStarted) {
if non_empty_string(payload.get("review_id")).is_none() {
errors.push("review_id missing".to_owned());
}
} else if event_type == Some(EventType::ReviewDisposition) {
if non_empty_string(payload.get("review_id")).is_none() {
errors.push("review_id missing".to_owned());
}
if non_empty_string(payload.get("disposition"))
.is_none_or(|disposition| !DISPOSITIONS.contains(&disposition))
{
errors.push(format!(
"disposition must be one of {}",
DISPOSITIONS.join("|")
));
}
if payload
.get("adjudicated_event_ids")
.and_then(Value::as_array)
.is_none_or(|identities| {
identities.is_empty()
|| identities
.iter()
.any(|identity| non_empty_string(Some(identity)).is_none())
})
{
errors.push("adjudicated_event_ids must be a non-empty array of event ids".to_owned());
}
if payload
.get("instrument_limited_event_ids")
.is_some_and(|value| {
value.as_array().is_none_or(|identities| {
identities.is_empty()
|| identities
.iter()
.any(|identity| non_empty_string(Some(identity)).is_none())
})
})
{
errors.push(
"instrument_limited_event_ids must be a non-empty array of event ids when present"
.to_owned(),
);
}
if payload.get("constraint_provenance").is_some_and(|value| {
value.as_array().is_none_or(|citations| {
citations.is_empty()
|| citations
.iter()
.any(|citation| !is_valid_constraint_provenance_entry(citation))
})
}) {
errors.push(format!(
"constraint_provenance must be a non-empty array of citations containing non-empty constraint_label, event_id, field ({}), and field_value when present",
CONSTRAINT_PROVENANCE_FIELDS.join("|")
));
}
if payload.get("external_owners").is_some_and(|value| {
value.as_array().is_none_or(|owners| {
owners.is_empty()
|| owners
.iter()
.any(|owner| !is_valid_external_owner_entry(owner))
})
}) {
errors.push(format!(
"external_owners must be a non-empty array of objects containing exactly event_id, kind ({}), and reference when present",
EXTERNAL_OWNER_KINDS.join("|")
));
}
if let Some(owners) = payload.get("external_owners").and_then(Value::as_array)
&& !owners.is_empty()
&& owners.iter().all(is_valid_external_owner_entry)
{
let coverage = payload
.get("adjudicated_event_ids")
.and_then(Value::as_array)
.into_iter()
.flatten()
.filter_map(Value::as_str)
.collect::<Vec<_>>();
let undecidable = payload
.get("instrument_limited_event_ids")
.and_then(Value::as_array)
.into_iter()
.flatten()
.filter_map(Value::as_str)
.collect::<HashSet<_>>();
let concluded = coverage
.iter()
.copied()
.filter(|identity| !undecidable.contains(identity))
.collect::<HashSet<_>>();
let owner_ids = owners
.iter()
.filter_map(|owner| owner["event_id"].as_str())
.collect::<Vec<_>>();
let owner_id_set = owner_ids.iter().copied().collect::<HashSet<_>>();
if payload.get("disposition").and_then(Value::as_str) != Some("outside_target")
|| owner_ids.len() != owner_id_set.len()
|| owner_id_set != concluded
{
errors.push(
"external_owners, when present, must name each concluded coverage event exactly once on an outside_target disposition"
.to_owned(),
);
}
}
if payload
.get("trial_count")
.is_some_and(|value| value.as_u64().is_none_or(|count| count == 0))
{
errors.push("trial_count must be a positive integer when present".to_owned());
}
if payload
.get("artifacts_path")
.is_some_and(|value| non_empty_string(Some(value)).is_none())
{
errors.push("artifacts_path must be a non-empty string when present".to_owned());
}
} else if non_empty_string(payload.get("review_id")).is_none() {
errors.push(format!(
"{} payload requires review_id",
event_type_name.unwrap_or("unknown")
));
}
errors
}
fn non_empty_string(value: Option<&Value>) -> Option<&str> {
value
.and_then(Value::as_str)
.filter(|value| !value.is_empty())
}
pub(crate) fn derive(
stream: &ValidatedStream,
target: GateTarget<'_>,
current_hash: &str,
clock: GateClock<'_>,
) -> GateStatus {
let events = stream.events();
let mut status = GateStatus {
schema_version: 1,
generated_at: clock.generated_at.to_owned(),
target_content_hash: current_hash.to_owned(),
qualifying_uses_on_current_hash: 0,
open_incident_ids: Vec::new(),
candidate_clusters: Vec::new(),
state: "closed".to_owned(),
authorized_workflow: None,
authorization_reason: None,
trigger_event_ids: Vec::new(),
threshold_session_id: None,
not_before: None,
active_review_id: None,
last_completed_review_id: None,
review_reentry_basis: None,
target_name: target.name.to_owned(),
target_repo_relative_path: target.repo_relative_path.to_owned(),
derivation_session_id: (clock.session_id != crate::SESSION_UNAVAILABLE)
.then(|| clock.session_id.to_owned()),
instrument_limited_incident_ids: Vec::new(),
integrity_errors: stream.integrity_errors().to_vec(),
};
if !status.integrity_errors.is_empty() {
status.state = "blocked".to_owned();
return status;
}
let mut adjudicated = HashSet::new();
for event in events {
let EventKind::ReviewDisposition {
disposition,
adjudicated_event_ids,
instrument_limited_event_ids,
..
} = &event.kind
else {
continue;
};
if !EVOLUTION_ADJUDICATING_DISPOSITIONS.contains(&disposition.as_str()) {
continue;
}
for identity in adjudicated_event_ids {
if !instrument_limited_event_ids.contains(identity) {
adjudicated.insert(identity.as_str());
}
}
}
let recorded_symptoms = events
.iter()
.filter_map(|event| {
let symptom = event.use_recorded()?.symptom_key.as_deref()?;
Some((event.event_id.as_str(), symptom))
})
.collect::<HashMap<_, _>>();
let review_starts = events
.iter()
.filter(|event| event.is_review_start())
.map(|event| (event.review_id().expect("review start identity"), event))
.collect::<Vec<_>>();
let ran_on_current_hash = |review_id: &str| {
review_starts.iter().any(|(started_id, start)| {
*started_id == review_id && start.target_content_hash == current_hash
})
};
let mut last_same_hash_disposition_index = None;
let mut last_same_hash_close_was_instrument_limited = false;
for (index, event) in events.iter().enumerate() {
let EventKind::ReviewDisposition { disposition, .. } = &event.kind else {
continue;
};
let review_id = event.review_id().expect("review disposition identity");
if ran_on_current_hash(review_id) {
last_same_hash_disposition_index = Some(index);
last_same_hash_close_was_instrument_limited =
EVOLUTION_INSTRUMENT_LIMITED_DISPOSITIONS.contains(&disposition.as_str());
}
}
let instrument_limited_closes = events
.iter()
.enumerate()
.filter_map(|(index, event)| {
let EventKind::ReviewDisposition {
disposition,
adjudicated_event_ids,
..
} = &event.kind
else {
return None;
};
if !EVOLUTION_INSTRUMENT_LIMITED_DISPOSITIONS.contains(&disposition.as_str()) {
return None;
}
if !ran_on_current_hash(event.review_id().expect("review disposition identity")) {
return None;
}
let covered = adjudicated_event_ids
.iter()
.map(String::as_str)
.collect::<HashSet<_>>();
let symptoms = covered
.iter()
.filter_map(|identity| recorded_symptoms.get(identity).copied())
.collect::<HashSet<_>>();
let authorizing_reason = review_starts
.iter()
.find(|(started_id, _)| {
*started_id == event.review_id().expect("review disposition identity")
})
.and_then(|(_, start)| {
start
.raw
.pointer("/payload/authorizing_rule")
.and_then(Value::as_str)
})
.and_then(ThresholdReason::parse);
Some((index, covered, symptoms, authorizing_reason))
})
.collect::<Vec<_>>();
let named_untestable_coverage = events
.iter()
.filter_map(|event| {
let EventKind::ReviewDisposition {
disposition,
adjudicated_event_ids,
instrument_limited_event_ids,
..
} = &event.kind
else {
return None;
};
if !EVOLUTION_ADJUDICATING_DISPOSITIONS.contains(&disposition.as_str()) {
return None;
}
if !ran_on_current_hash(event.review_id().expect("review disposition identity")) {
return None;
}
Some(
instrument_limited_event_ids
.iter()
.filter(|identity| adjudicated_event_ids.contains(identity))
.map(String::as_str),
)
})
.flatten()
.collect::<HashSet<_>>();
let started = events
.iter()
.filter(|event| event.starts_ownership())
.collect::<Vec<_>>();
let terminated = events
.iter()
.filter(|event| event.terminates_ownership())
.filter_map(|event| event.review_id())
.collect::<HashSet<_>>();
let active_starts = started
.iter()
.filter(|event| {
event
.review_id()
.is_some_and(|review_id| !terminated.contains(review_id))
})
.copied()
.collect::<Vec<_>>();
let completed_starts = started
.iter()
.filter(|event| {
event
.review_id()
.is_some_and(|review_id| terminated.contains(review_id))
})
.copied()
.collect::<Vec<_>>();
status.active_review_id = active_starts
.last()
.and_then(|event| event.review_id())
.map(str::to_owned);
status.last_completed_review_id = completed_starts
.last()
.and_then(|event| event.review_id())
.map(str::to_owned);
let event_positions = events
.iter()
.enumerate()
.map(|(index, event)| (event.event_id.as_str(), index))
.collect::<HashMap<_, _>>();
let mut cluster_events = Vec::<(String, Vec<&EvidenceEvent>)>::new();
let mut fired = None;
let mut queued_pre_close_evidence = false;
let mut counted_run_groups = HashSet::new();
for (event_index, event) in events.iter().enumerate() {
let Some(recorded) = event.use_recorded() else {
continue;
};
if event.target_content_hash != current_hash {
continue;
}
if counted_run_groups.insert(recorded.same_run_group.as_str()) {
status.qualifying_uses_on_current_hash += 1;
}
let open_incident =
recorded.outcome != Outcome::Clean && !adjudicated.contains(event.event_id.as_str());
let authorizes_on_its_own =
recorded.outcome == Outcome::SevereIncident && !recorded.retrospective;
let instrument_limited_incident = open_incident
&& !authorizes_on_its_own
&& (named_untestable_coverage.contains(event.event_id.as_str())
|| instrument_limited_closes.iter().any(
|(close, covered, symptoms, authorizing_reason)| {
covered.contains(event.event_id.as_str())
|| (event_index < *close
&& authorization_reason_names_incident(
authorizing_reason.as_ref(),
symptoms,
recorded,
))
},
));
if open_incident {
status.open_incident_ids.push(event.event_id.clone());
}
if instrument_limited_incident {
status
.instrument_limited_incident_ids
.push(event.event_id.clone());
}
let symptom = if open_incident && !instrument_limited_incident {
let symptom = recorded
.symptom_key
.as_deref()
.expect("validated symptom key")
.to_owned();
if !cluster_events.iter().any(|(key, _)| key == &symptom) {
cluster_events.push((symptom.clone(), Vec::new()));
status.candidate_clusters.push(CandidateCluster {
symptom_key: symptom.clone(),
open_event_ids: Vec::new(),
independent_incidents: 0,
max_severity: "friction".to_owned(),
});
}
let events_for_cluster = &mut cluster_events
.iter_mut()
.find(|(key, _)| key == &symptom)
.expect("cluster event list was inserted")
.1;
events_for_cluster.push(event);
let cluster = status
.candidate_clusters
.iter_mut()
.find(|cluster| cluster.symptom_key == symptom)
.expect("cluster was inserted");
cluster.open_event_ids.push(event.event_id.clone());
cluster.independent_incidents = independent_count(events_for_cluster);
if recorded.outcome.severity()
> Outcome::parse(&cluster.max_severity)
.expect("candidate severity")
.severity()
{
cluster.max_severity = recorded.outcome.as_str().to_owned();
}
Some(symptom)
} else {
None
};
if fired.is_some() || recorded.retrospective {
continue;
}
if open_incident && recorded.outcome == Outcome::SevereIncident {
fired = Some(threshold_trigger(
ThresholdReason::Severe,
&[event],
event,
event_index,
));
continue;
}
let mut candidate = None;
if let Some(symptom) = symptom.as_deref() {
let events_for_cluster = &cluster_events
.iter()
.find(|(key, _)| key == symptom)
.expect("open incident cluster exists")
.1;
let material = events_for_cluster
.iter()
.copied()
.filter(|event| {
event
.use_recorded()
.expect("cluster contains use records")
.outcome
.severity()
>= Outcome::MaterialFailure.severity()
})
.collect::<Vec<_>>();
if recorded.outcome.severity() >= Outcome::MaterialFailure.severity()
&& independent_count(&material) >= 2
{
candidate = Some(threshold_trigger(
ThresholdReason::MaterialRecurrence(symptom.to_owned()),
&material,
event,
event_index,
));
} else if independent_count(events_for_cluster) >= 3 {
candidate = Some(threshold_trigger(
ThresholdReason::FrictionRecurrence(symptom.to_owned()),
events_for_cluster,
event,
event_index,
));
}
}
let open_contemporaneous = cluster_events
.iter()
.flat_map(|(_, cluster)| cluster.iter().copied())
.filter(|incident| {
incident
.use_recorded()
.is_some_and(|recorded| !recorded.retrospective)
})
.collect::<Vec<_>>();
if candidate.is_none()
&& status.qualifying_uses_on_current_hash >= 10
&& !open_contemporaneous.is_empty()
{
let anchor = last_same_hash_disposition_index
.and_then(|watermark| {
open_contemporaneous.iter().copied().find(|incident| {
event_positions
.get(incident.event_id.as_str())
.is_some_and(|index| *index > watermark)
})
})
.unwrap_or(open_contemporaneous[0]);
let symptom = anchor
.use_recorded()
.and_then(|recorded| recorded.symptom_key.as_deref())
.expect("validated symptom key");
let triggers = cluster_events
.iter()
.find(|(key, _)| key == symptom)
.expect("anchor cluster exists")
.1
.iter()
.filter(|event| {
event
.use_recorded()
.is_some_and(|recorded| !recorded.retrospective)
})
.copied()
.collect::<Vec<_>>();
candidate = Some(threshold_trigger(
ThresholdReason::TenUseUnresolved,
&triggers,
event,
event_index,
));
}
if let Some(candidate) = candidate {
if let Some(watermark) = last_same_hash_disposition_index {
let has_post_review_incident = open_contemporaneous.iter().any(|incident| {
event_positions
.get(incident.event_id.as_str())
.is_some_and(|index| *index > watermark)
});
if event_index > watermark && has_post_review_incident {
fired = Some(candidate);
} else {
queued_pre_close_evidence = true;
}
} else {
fired = Some(candidate);
}
}
}
if status.active_review_id.is_some() {
status.state = "review_in_progress".to_owned();
} else if let Some(trigger) = fired {
let severe = trigger.reason.is_severe();
status.authorized_workflow = Some("skill-evolution".to_owned());
status.authorization_reason = Some(trigger.reason.as_string());
status.trigger_event_ids = trigger.trigger_event_ids;
status.review_reentry_basis = Some(
if last_same_hash_disposition_index.is_none() {
"first_eligibility"
} else if severe
&& last_same_hash_disposition_index.is_some_and(|index| trigger.event_index < index)
{
"unadjudicated_severe"
} else {
"post_review_incident"
}
.to_owned(),
);
status.threshold_session_id = trigger.threshold_session_id.clone();
let cooldown_passed = if let Some(threshold_session_id) = trigger.threshold_session_id {
clock.session_id != crate::SESSION_UNAVAILABLE
&& clock.session_id != threshold_session_id
} else {
let fired_at = OffsetDateTime::parse(&trigger.fired_at, &Rfc3339)
.expect("validated event timestamp parses");
let not_before_milliseconds =
i64::try_from(fired_at.unix_timestamp_nanos() / 1_000_000)
.expect("event timestamp milliseconds fit in i64")
+ 12 * 60 * 60 * 1_000;
status.not_before = Some(format_timestamp_milliseconds(not_before_milliseconds));
clock.now_epoch_milliseconds >= not_before_milliseconds
};
status.state = cooldown_state(severe, cooldown_passed).to_owned();
} else if !status.open_incident_ids.is_empty() {
status.state = "collecting".to_owned();
}
if queued_pre_close_evidence && status.state == "collecting" {
status.review_reentry_basis = Some(
if last_same_hash_close_was_instrument_limited {
"queued_behind_instrument_limited_review"
} else {
"queued_pre_close_evidence"
}
.to_owned(),
);
}
reanchor_authorized_coverage(&mut status, events);
status
}
fn reanchor_authorized_coverage(status: &mut GateStatus, events: &[EvidenceEvent]) {
let Some(reason) = status
.authorization_reason
.as_deref()
.and_then(ThresholdReason::parse)
else {
return;
};
let symptom = match &reason {
ThresholdReason::MaterialRecurrence(symptom)
| ThresholdReason::FrictionRecurrence(symptom) => symptom.clone(),
ThresholdReason::TenUseUnresolved => {
let Some(symptom) = status.trigger_event_ids.iter().find_map(|identity| {
events
.iter()
.find(|event| event.event_id == *identity)
.and_then(EvidenceEvent::use_recorded)
.and_then(|recorded| recorded.symptom_key.clone())
}) else {
return;
};
symptom
}
ThresholdReason::Severe => return,
};
let Some(cluster) = status
.candidate_clusters
.iter()
.find(|cluster| cluster.symptom_key == symptom)
else {
return;
};
let anchor_symptoms = [symptom.as_str()].into_iter().collect::<HashSet<_>>();
status.trigger_event_ids = cluster
.open_event_ids
.iter()
.filter(|identity| {
events
.iter()
.find(|event| event.event_id == **identity)
.and_then(EvidenceEvent::use_recorded)
.is_some_and(|recorded| {
authorization_reason_names_incident(Some(&reason), &anchor_symptoms, recorded)
})
})
.cloned()
.collect();
}
fn threshold_trigger(
reason: ThresholdReason,
trigger_events: &[&EvidenceEvent],
threshold_event: &EvidenceEvent,
event_index: usize,
) -> ThresholdTrigger {
ThresholdTrigger {
reason,
trigger_event_ids: trigger_events
.iter()
.map(|event| event.event_id.clone())
.collect(),
threshold_session_id: (threshold_event.top_level_session_id != crate::SESSION_UNAVAILABLE)
.then(|| threshold_event.top_level_session_id.clone()),
fired_at: threshold_event.recorded_at.clone(),
event_index,
}
}
fn cooldown_state(severe: bool, cooldown_passed: bool) -> &'static str {
match (severe, cooldown_passed) {
(true, true) => "quarantined_eligible",
(true, false) => "quarantined_pending_cooldown",
(false, true) => "eligible",
(false, false) => "eligible_pending_cooldown",
}
}
pub(crate) fn format_timestamp_milliseconds(epoch_milliseconds: i64) -> String {
let timestamp =
OffsetDateTime::from_unix_timestamp_nanos(i128::from(epoch_milliseconds) * 1_000_000)
.expect("supported evidence timestamps fit OffsetDateTime");
let format = time::format_description::parse_borrowed::<3>(
"[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:3]Z",
)
.expect("static timestamp format is valid");
timestamp
.format(&format)
.expect("UTC timestamp always formats")
}
fn independent_count(events: &[&EvidenceEvent]) -> usize {
events
.iter()
.map(|event| {
(
event.top_level_session_id.as_str(),
event
.use_recorded()
.expect("incident cluster contains use records")
.task_fingerprint
.as_str(),
)
})
.collect::<HashSet<_>>()
.len()
}
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use serde_json::json;
use super::*;
struct Fixture {
target_relative: &'static str,
target_hash: String,
recorded: RefCell<Vec<Value>>,
}
impl Fixture {
fn new() -> Self {
Self {
target_relative: ".claude/skills/demo-skill",
target_hash: "fixture-target-content-hash".to_owned(),
recorded: RefCell::new(Vec::new()),
}
}
fn write_events(&self, events: &[Value]) {
*self.recorded.borrow_mut() = events.to_vec();
}
fn recorded_events(&self) -> Vec<Value> {
self.recorded.borrow().clone()
}
fn derive(&self, session_id: &str, now_millis: i64) -> GateStatus {
self.derive_against(&self.target_hash, session_id, now_millis)
}
fn derive_against(
&self,
current_hash: &str,
session_id: &str,
now_millis: i64,
) -> GateStatus {
let mut text = self
.recorded
.borrow()
.iter()
.map(Value::to_string)
.collect::<Vec<_>>()
.join("\n");
text.push('\n');
let stream = ValidatedStream::parse(text.as_bytes(), "fixture events.jsonl");
super::derive(
&stream,
GateTarget {
name: "demo-skill",
repo_relative_path: self.target_relative,
},
current_hash,
GateClock {
generated_at: "2026-01-03T00:00:00Z",
now_epoch_milliseconds: now_millis,
session_id,
},
)
}
fn target(&self) -> Value {
json!({
"name": "demo-skill",
"repo_relative_path": self.target_relative,
"content_hash": self.target_hash,
"repo_head": "fixture-head"
})
}
fn review(&self, review_id: &str, disposition: &str, covered: &[&str]) -> [Value; 2] {
[
json!({
"schema_version": 1,
"event_id": format!("evt_review_started_{review_id}"),
"event_type": "review_started",
"recorded_at": "2026-01-02T20:00:00Z",
"operator_workflow": "skill-evolution",
"target": self.target(),
"top_level_session_id": format!("review-session-{review_id}"),
"payload": {"review_id": review_id}
}),
json!({
"schema_version": 1,
"event_id": format!("evt_review_disposition_{review_id}"),
"event_type": "review_disposition",
"recorded_at": "2026-01-02T21:00:00Z",
"operator_workflow": "skill-evolution",
"target": self.target(),
"top_level_session_id": format!("review-session-{review_id}"),
"payload": {
"review_id": review_id,
"disposition": disposition,
"adjudicated_event_ids": covered
}
}),
]
}
fn review_with_authorizing_rule(
&self,
review_id: &str,
disposition: &str,
covered: &[&str],
authorizing_rule: &str,
) -> [Value; 2] {
let mut review = self.review(review_id, disposition, covered);
review[0]["payload"]["authorizing_rule"] = json!(authorizing_rule);
review
}
fn review_naming_untestable_coverage(
&self,
review_id: &str,
disposition: &str,
covered: &[&str],
untestable: &[&str],
) -> [Value; 2] {
let mut review = self.review(review_id, disposition, covered);
review[1]["payload"]["instrument_limited_event_ids"] = json!(untestable);
review
}
fn use_event(
&self,
serial: usize,
outcome: &str,
symptom_key: Option<&str>,
session_id: &str,
) -> Value {
json!({
"schema_version": 1,
"event_id": format!("evt_{serial}"),
"event_type": "use_recorded",
"recorded_at": format!("2026-01-02T{serial:02}:00:00Z"),
"operator_workflow": "skill-evidence-capture",
"target": {
"name": "demo-skill",
"repo_relative_path": self.target_relative,
"content_hash": self.target_hash,
"repo_head": "fixture-head"
},
"top_level_session_id": session_id,
"payload": {
"qualifying_use": true,
"retrospective": false,
"task_label": format!("task {serial}"),
"task_fingerprint": format!("fingerprint-{serial}"),
"outcome": outcome,
"symptom_key": symptom_key,
"expected": symptom_key.map(|_| "expected"),
"observed": symptom_key.map(|_| "observed"),
"consequence": symptom_key.map(|_| "consequence"),
"workaround_taken": null,
"run_condition": symptom_key.map(|_| "condition"),
"evidence_refs": [],
"same_run_group": format!("run-{serial}")
}
})
}
fn further_incident_event(
&self,
serial: usize,
of_run: usize,
outcome: &str,
symptom_key: Option<&str>,
session_id: &str,
) -> Value {
let mut event = self.use_event(serial, outcome, symptom_key, session_id);
event["payload"]["task_label"] = json!(format!("task {of_run}"));
event["payload"]["task_fingerprint"] = json!(format!("fingerprint-{of_run}"));
event["payload"]["same_run_group"] = json!(format!("run-{of_run}"));
event
}
}
#[test]
fn qualifying_uses_count_run_groups_rather_than_records() {
let fixture = Fixture::new();
fixture.write_events(&[
fixture.use_event(1, "friction", Some("execution"), "session-a"),
fixture.further_incident_event(2, 1, "friction", Some("output"), "session-a"),
fixture.further_incident_event(3, 1, "friction", Some("state"), "session-a"),
fixture.use_event(4, "clean", None, "session-b"),
]);
let status = fixture.derive("session-c", 1_767_398_400_000);
assert_eq!(
status.qualifying_uses_on_current_hash, 2,
"three siblings record one run, so two runs are two uses"
);
assert_eq!(
status.open_incident_ids,
["evt_1", "evt_2", "evt_3"],
"every sibling stays separately addressable in the ledger"
);
}
#[test]
fn friction_recurrence_requires_a_fresh_top_level_session() {
let fixture = Fixture::new();
fixture.write_events(&[
fixture.use_event(1, "friction", Some("execution"), "session-a"),
fixture.use_event(2, "friction", Some("execution"), "session-b"),
fixture.use_event(3, "friction", Some("execution"), "session-c"),
]);
let same_session = fixture.derive("session-c", 1_767_398_400_000);
assert_eq!(same_session.state, "eligible_pending_cooldown");
assert_eq!(
same_session.authorization_reason.as_deref(),
Some("friction_recurrence:execution")
);
assert_eq!(
same_session.threshold_session_id.as_deref(),
Some("session-c")
);
assert_eq!(same_session.trigger_event_ids, ["evt_1", "evt_2", "evt_3"]);
let fresh_session = fixture.derive("session-d", 1_767_398_400_000);
assert_eq!(fresh_session.state, "eligible");
assert_eq!(
fresh_session.derivation_session_id.as_deref(),
Some("session-d")
);
}
#[test]
fn naming_one_sibling_untestable_leaves_its_siblings_clustering() {
let fixture = Fixture::new();
let mut events = vec![
fixture.use_event(1, "friction", Some("execution"), "session-a"),
fixture.further_incident_event(2, 1, "friction", Some("execution"), "session-a"),
fixture.use_event(3, "friction", Some("execution"), "session-b"),
];
events.extend(fixture.review_naming_untestable_coverage(
"rev_1",
"closed_no_skill_defect",
&["evt_2"],
&["evt_2"],
));
fixture.write_events(&events);
let status = fixture.derive("session-c", 1_767_398_400_000);
assert_eq!(
status.open_incident_ids,
["evt_1", "evt_2", "evt_3"],
"a named event was never adjudicated, so it stays open in the ledger"
);
assert_eq!(
status.instrument_limited_incident_ids,
["evt_2"],
"retirement reaches the name, not the run group it belongs to"
);
let cluster = status
.candidate_clusters
.iter()
.find(|cluster| cluster.symptom_key == "execution")
.expect("the unnamed incidents still cluster");
assert_eq!(cluster.open_event_ids, ["evt_1", "evt_3"]);
assert_eq!(cluster.independent_incidents, 2);
assert_eq!(status.qualifying_uses_on_current_hash, 2);
}
#[test]
fn siblings_of_one_run_cannot_reach_material_recurrence() {
let fixture = Fixture::new();
fixture.write_events(&[
fixture.use_event(1, "material_failure", Some("output"), "session-a"),
fixture.further_incident_event(2, 1, "material_failure", Some("output"), "session-a"),
]);
let status = fixture.derive("session-b", 1_767_398_400_000);
assert_eq!(status.state, "collecting");
assert_eq!(status.authorization_reason, None);
assert_eq!(status.trigger_event_ids, [] as [String; 0]);
let cluster = status
.candidate_clusters
.iter()
.find(|cluster| cluster.symptom_key == "output")
.expect("the siblings cluster on their shared symptom");
assert_eq!(cluster.open_event_ids, ["evt_1", "evt_2"]);
assert_eq!(
cluster.independent_incidents, 1,
"one run contributes one independent incident however many ways it deviated"
);
}
#[test]
fn two_independent_material_failures_fire_the_material_recurrence_gate() {
let fixture = Fixture::new();
fixture.write_events(&[
fixture.use_event(1, "material_failure", Some("output"), "session-a"),
fixture.use_event(2, "material_failure", Some("output"), "session-b"),
]);
let status = fixture.derive("session-b", 1_767_398_400_000);
assert_eq!(status.state, "eligible_pending_cooldown");
assert_eq!(
status.authorization_reason.as_deref(),
Some("material_recurrence:output")
);
assert_eq!(status.trigger_event_ids, ["evt_1", "evt_2"]);
}
#[test]
fn one_contemporaneous_severe_incident_quarantines_the_target() {
let fixture = Fixture::new();
fixture.write_events(&[fixture.use_event(
1,
"severe_incident",
Some("state"),
"threshold-session",
)]);
let same_session = fixture.derive("threshold-session", 1_767_398_400_000);
assert_eq!(same_session.state, "quarantined_pending_cooldown");
assert_eq!(same_session.authorization_reason.as_deref(), Some("severe"));
assert_eq!(same_session.trigger_event_ids, ["evt_1"]);
let fresh_session = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(fresh_session.state, "quarantined_eligible");
}
#[test]
fn unavailable_threshold_session_uses_the_twelve_hour_clock() {
let fixture = Fixture::new();
fixture.write_events(&[
fixture.use_event(1, "friction", Some("cost"), "unavailable"),
fixture.use_event(2, "friction", Some("cost"), "unavailable"),
fixture.use_event(3, "friction", Some("cost"), "unavailable"),
]);
let before_deadline = fixture.derive("fresh-host-session", 1_767_365_999_999);
assert_eq!(before_deadline.state, "eligible_pending_cooldown");
assert_eq!(before_deadline.threshold_session_id, None);
assert_eq!(
before_deadline.not_before.as_deref(),
Some("2026-01-02T15:00:00.000Z")
);
let at_deadline = fixture.derive("fresh-host-session", 1_767_366_000_000);
assert_eq!(at_deadline.state, "eligible");
}
#[test]
fn every_v1_event_type_is_accepted_and_review_ownership_is_derived() {
let fixture = Fixture::new();
let target = fixture.target();
let lifecycle = |serial: usize, event_type: &str, payload: Value| {
json!({
"schema_version": 1,
"event_id": format!("evt_{serial}"),
"event_type": event_type,
"recorded_at": format!("2026-01-02T0{serial}:00:00Z"),
"operator_workflow": "skill-evolution",
"target": target,
"top_level_session_id": "review-session",
"payload": payload
})
};
fixture.write_events(&[
fixture.use_event(1, "friction", Some("execution"), "session-a"),
lifecycle(2, "review_started", json!({"review_id": "review-1"})),
lifecycle(3, "validation_completed", json!({"review_id": "review-1"})),
lifecycle(4, "change_landed", json!({"review_id": "review-1"})),
lifecycle(
5,
"decontamination_started",
json!({"review_id": "decontamination-1"}),
),
lifecycle(
6,
"decontamination_completed",
json!({"review_id": "decontamination-1"}),
),
lifecycle(
7,
"review_disposition",
json!({
"review_id": "review-1",
"disposition": "monitor_for_recurrence",
"adjudicated_event_ids": ["evt_1"]
}),
),
]);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.state, "closed");
assert_eq!(status.open_incident_ids, Vec::<String>::new());
assert_eq!(status.active_review_id, None);
assert_eq!(
status.last_completed_review_id.as_deref(),
Some("decontamination-1")
);
}
#[test]
fn ten_uses_with_one_open_contemporaneous_incident_fire_the_ten_use_gate() {
let fixture = Fixture::new();
let mut events = vec![fixture.use_event(1, "friction", Some("cost"), "incident-session")];
for serial in 2..=10 {
events.push(fixture.use_event(serial, "clean", None, &format!("session-{serial}")));
}
fixture.write_events(&events);
let status = fixture.derive("session-10", 1_767_398_400_000);
assert_eq!(status.qualifying_uses_on_current_hash, 10);
assert_eq!(status.state, "eligible_pending_cooldown");
assert_eq!(
status.authorization_reason.as_deref(),
Some("ten_use_unresolved")
);
assert_eq!(status.trigger_event_ids, ["evt_1"]);
}
#[test]
fn retrospective_tenth_use_does_not_complete_the_ten_use_gate() {
let fixture = Fixture::new();
let mut events = vec![fixture.use_event(1, "friction", Some("cost"), "incident-session")];
for serial in 2..=9 {
events.push(fixture.use_event(serial, "clean", None, &format!("session-{serial}")));
}
let mut retrospective = fixture.use_event(10, "clean", None, "retrospective-session");
retrospective["payload"]["retrospective"] = Value::Bool(true);
retrospective["payload"]["evidence_refs"] = json!(["logs/retrospective-use.txt"]);
events.push(retrospective);
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.qualifying_uses_on_current_hash, 10);
assert_eq!(status.state, "collecting");
assert_eq!(status.authorization_reason, None);
assert_eq!(status.threshold_session_id, None);
}
#[test]
fn first_ten_use_threshold_wins_over_a_later_severe_incident() {
let fixture = Fixture::new();
let mut events = vec![fixture.use_event(1, "friction", Some("cost"), "incident-session")];
for serial in 2..=10 {
events.push(fixture.use_event(serial, "clean", None, &format!("session-{serial}")));
}
events.push(fixture.use_event(11, "severe_incident", Some("state"), "later-session"));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.qualifying_uses_on_current_hash, 11);
assert_eq!(status.state, "eligible");
assert_eq!(
status.authorization_reason.as_deref(),
Some("ten_use_unresolved")
);
assert_eq!(status.trigger_event_ids, ["evt_1"]);
assert_eq!(status.threshold_session_id.as_deref(), Some("session-10"));
}
#[test]
fn completed_same_hash_review_does_not_reopen_from_queued_pre_close_evidence() {
let fixture = Fixture::new();
let mut events = vec![
fixture.use_event(1, "friction", Some("execution"), "session-1"),
fixture.use_event(2, "material_failure", Some("output"), "session-2"),
fixture.use_event(3, "material_failure", Some("output"), "session-3"),
];
for serial in 4..=10 {
events.push(fixture.use_event(serial, "clean", None, &format!("session-{serial}")));
}
events.extend(fixture.review(
"review-queued",
"candidate_rejected_validation",
&["evt_2", "evt_3"],
));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.state, "collecting");
assert_eq!(status.authorization_reason, None);
assert_eq!(
status.review_reentry_basis.as_deref(),
Some("queued_pre_close_evidence")
);
assert_eq!(status.open_incident_ids, ["evt_1"]);
}
#[test]
fn blocked_no_valid_test_retires_covered_incidents_from_the_gate() {
let fixture = Fixture::new();
let mut events = (1..=3)
.map(|serial| {
fixture.use_event(
serial,
"friction",
Some("execution"),
&format!("session-{serial}"),
)
})
.collect::<Vec<_>>();
events.extend(fixture.review(
"review-blocked",
"blocked_no_valid_test",
&["evt_1", "evt_2", "evt_3"],
));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.state, "collecting");
assert_eq!(status.authorization_reason, None);
assert_eq!(
status.open_incident_ids,
["evt_1", "evt_2", "evt_3"],
"a blocked close adjudicates nothing, so the incidents remain open in the ledger"
);
assert!(
status.candidate_clusters.is_empty(),
"evidence a review proved untestable must stop clustering, or one new incident \
re-fires the same threshold the instrument already failed: {:?}",
status.candidate_clusters
);
assert_eq!(
status.review_reentry_basis, None,
"nothing is queued behind the close: the covered evidence left the gate"
);
}
#[test]
fn a_blocked_close_retires_the_stragglers_in_the_clusters_it_covered() {
let fixture = Fixture::new();
let mut events = (1..=4)
.map(|serial| {
fixture.use_event(
serial,
"friction",
Some("execution"),
&format!("session-{serial}"),
)
})
.collect::<Vec<_>>();
events.extend(fixture.review(
"review-blocked",
"blocked_no_valid_test",
&["evt_1", "evt_2", "evt_3"],
));
events.extend((5..=6).map(|serial| {
fixture.use_event(
serial,
"friction",
Some("execution"),
&format!("session-{serial}"),
)
}));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(
status.instrument_limited_incident_ids,
["evt_1", "evt_2", "evt_3", "evt_4"],
"the straggler shares the symptom the instrument could not test"
);
assert_eq!(
status.authorization_reason, None,
"two new incidents must not re-authorize a threshold that takes three: {:?}",
status.candidate_clusters
);
assert_eq!(status.state, "collecting");
}
#[test]
fn a_blocked_close_retires_frictional_siblings_a_material_trigger_list_cannot_name() {
let fixture = Fixture::new();
let mut events = vec![
fixture.use_event(1, "friction", Some("output"), "session-1"),
fixture.use_event(2, "material_failure", Some("output"), "session-2"),
fixture.use_event(3, "material_failure", Some("output"), "session-3"),
];
events.extend(fixture.review(
"review-blocked",
"blocked_no_valid_test",
&["evt_2", "evt_3"],
));
events.extend((4..=5).map(|serial| {
fixture.use_event(
serial,
"friction",
Some("output"),
&format!("session-{serial}"),
)
}));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(
status.instrument_limited_incident_ids,
["evt_1", "evt_2", "evt_3"],
"the frictional sibling shares the retired symptom"
);
assert_eq!(status.authorization_reason, None);
assert_eq!(status.state, "collecting");
}
#[test]
fn a_ten_use_blocked_close_leaves_retrospective_incidents_in_the_anchor_cluster() {
let fixture = Fixture::new();
let mut events = vec![fixture.use_event(1, "friction", Some("cost"), "session-1")];
let mut retrospective = fixture.use_event(2, "friction", Some("cost"), "session-2");
retrospective["payload"]["retrospective"] = json!(true);
retrospective["payload"]["evidence_refs"] = json!(["logs/retrospective-cost.txt"]);
events.push(retrospective);
events.extend(
(3..=10).map(|serial| {
fixture.use_event(serial, "clean", None, &format!("session-{serial}"))
}),
);
events.extend(fixture.review_with_authorizing_rule(
"review-blocked",
"blocked_no_valid_test",
&["evt_1"],
"ten_use_unresolved",
));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.instrument_limited_incident_ids, ["evt_1"]);
assert_eq!(
status.candidate_clusters[0].open_event_ids,
["evt_2"],
"ten-use retirement names only contemporaneous incidents in the anchor cluster"
);
}
#[test]
fn a_blocked_close_does_not_retire_evidence_recorded_after_it() {
let fixture = Fixture::new();
let mut events = (1..=3)
.map(|serial| {
fixture.use_event(
serial,
"friction",
Some("execution"),
&format!("session-{serial}"),
)
})
.collect::<Vec<_>>();
events.extend(fixture.review(
"review-blocked",
"blocked_no_valid_test",
&["evt_1", "evt_2", "evt_3"],
));
events.extend((4..=6).map(|serial| {
fixture.use_event(
serial,
"friction",
Some("execution"),
&format!("session-{serial}"),
)
}));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(
status.instrument_limited_incident_ids,
["evt_1", "evt_2", "evt_3"],
"only evidence that existed when the review closed is retired"
);
assert_eq!(
status.authorization_reason.as_deref(),
Some("friction_recurrence:execution"),
"three genuinely new incidents reach the threshold on their own"
);
assert_eq!(status.trigger_event_ids, ["evt_4", "evt_5", "evt_6"]);
}
#[test]
fn retirement_and_the_watermark_can_come_from_different_closes() {
let fixture = Fixture::new();
let target = fixture.target();
let review = |serial: usize, review_id: &str, event_type: &str, payload: Value| {
json!({
"schema_version": 1,
"event_id": format!("evt_{review_id}_{event_type}"),
"event_type": event_type,
"recorded_at": format!("2026-01-02T{serial:02}:30:00Z"),
"operator_workflow": "skill-evolution",
"target": target,
"top_level_session_id": format!("review-session-{review_id}"),
"payload": payload
})
};
let mut events = (1..=3)
.map(|serial| {
fixture.use_event(
serial,
"friction",
Some("execution"),
&format!("session-{serial}"),
)
})
.collect::<Vec<_>>();
events.push(review(
3,
"blocked",
"review_started",
json!({"review_id": "rev-blocked"}),
));
events.push(review(
4,
"blocked",
"review_disposition",
json!({
"review_id": "rev-blocked",
"disposition": "blocked_no_valid_test",
"adjudicated_event_ids": ["evt_1", "evt_2", "evt_3"]
}),
));
events.extend((5..=7).map(|serial| {
fixture.use_event(
serial,
"friction",
Some("output"),
&format!("session-{serial}"),
)
}));
events.extend((8..=9).map(|serial| {
fixture.use_event(
serial,
"material_failure",
Some("state"),
&format!("session-{serial}"),
)
}));
events.push(review(
10,
"closed",
"review_started",
json!({"review_id": "rev-closed"}),
));
events.push(review(
11,
"closed",
"review_disposition",
json!({
"review_id": "rev-closed",
"disposition": "monitor_for_recurrence",
"adjudicated_event_ids": ["evt_8", "evt_9"]
}),
));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(
status.instrument_limited_incident_ids,
["evt_1", "evt_2", "evt_3"],
"the adjudicating close retired nothing; the earlier blocked close did"
);
assert_eq!(
status.review_reentry_basis.as_deref(),
Some("queued_pre_close_evidence"),
"the watermark close reached a conclusion, so its own label is the honest one"
);
}
#[test]
fn a_blocked_close_retires_a_retrospective_severe_incident_it_covered() {
let fixture = Fixture::new();
let mut retrospective =
fixture.use_event(1, "severe_incident", Some("execution"), "session-1");
retrospective["payload"]["retrospective"] = json!(true);
retrospective["payload"]["evidence_refs"] = json!(["logs/retrospective-severe.txt"]);
let mut events = vec![retrospective];
events.extend((2..=3).map(|serial| {
fixture.use_event(
serial,
"friction",
Some("execution"),
&format!("session-{serial}"),
)
}));
events.extend(fixture.review(
"review-blocked",
"blocked_no_valid_test",
&["evt_1", "evt_2", "evt_3"],
));
events.extend((4..=5).map(|serial| {
fixture.use_event(
serial,
"friction",
Some("execution"),
&format!("session-{serial}"),
)
}));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(
status.instrument_limited_incident_ids,
["evt_1", "evt_2", "evt_3"],
"a retrospective severe incident authorizes nothing, so nothing is protected by \
leaving it clustered"
);
assert_eq!(
status.authorization_reason, None,
"two new incidents must not re-authorize a threshold that takes three: {:?}",
status.candidate_clusters
);
}
#[test]
fn the_projection_names_the_evidence_a_blocked_close_retired() {
let fixture = Fixture::new();
let mut events = (1..=3)
.map(|serial| {
fixture.use_event(
serial,
"friction",
Some("execution"),
&format!("session-{serial}"),
)
})
.collect::<Vec<_>>();
events.extend(fixture.review(
"review-blocked",
"blocked_no_valid_test",
&["evt_1", "evt_2", "evt_3"],
));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(
status.instrument_limited_incident_ids,
["evt_1", "evt_2", "evt_3"]
);
}
#[test]
fn a_blocked_close_does_not_quiet_a_severe_incident() {
let fixture = Fixture::new();
let mut events =
vec![fixture.use_event(1, "severe_incident", Some("execution"), "session-1")];
events.extend(fixture.review("review-blocked-severe", "blocked_no_valid_test", &["evt_1"]));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.state, "quarantined_eligible");
assert_eq!(status.authorization_reason.as_deref(), Some("severe"));
assert_eq!(
status.review_reentry_basis.as_deref(),
Some("unadjudicated_severe")
);
assert!(
status.instrument_limited_incident_ids.is_empty(),
"a severe incident is not retired by an instrument-limited close: {:?}",
status.instrument_limited_incident_ids
);
assert_eq!(status.candidate_clusters.len(), 1);
}
#[test]
fn evidence_behind_an_instrument_limited_close_is_not_reported_as_accounted_for() {
let fixture = Fixture::new();
let mut events = (1..=3)
.map(|serial| {
fixture.use_event(
serial,
"friction",
Some("execution"),
&format!("session-{serial}"),
)
})
.collect::<Vec<_>>();
events.extend((4..=6).map(|serial| {
fixture.use_event(
serial,
"friction",
Some("output"),
&format!("session-{serial}"),
)
}));
events.extend(fixture.review(
"review-blocked",
"blocked_no_valid_test",
&["evt_1", "evt_2", "evt_3"],
));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.state, "collecting");
assert_eq!(status.authorization_reason, None);
assert_eq!(
status.review_reentry_basis.as_deref(),
Some("queued_behind_instrument_limited_review")
);
assert_eq!(
status.instrument_limited_incident_ids,
["evt_1", "evt_2", "evt_3"],
"only the covered cluster left the gate"
);
}
#[test]
fn superseded_by_target_version_does_not_retire_covered_incidents() {
struct Case {
name: &'static str,
disposition: &'static str,
outcomes: &'static [&'static str],
expected_state: &'static str,
expected_reason: Option<&'static str>,
expected_reentry_basis: &'static str,
}
for case in [
Case {
name: "superseded review queues retained pre-close evidence",
disposition: "superseded_by_target_version",
outcomes: &["friction", "friction", "friction"],
expected_state: "collecting",
expected_reason: None,
expected_reentry_basis: "queued_pre_close_evidence",
},
Case {
name: "superseded severe review remains quarantined",
disposition: "superseded_by_target_version",
outcomes: &["severe_incident"],
expected_state: "quarantined_eligible",
expected_reason: Some("severe"),
expected_reentry_basis: "unadjudicated_severe",
},
] {
let fixture = Fixture::new();
let mut events = case
.outcomes
.iter()
.enumerate()
.map(|(index, outcome)| {
fixture.use_event(
index + 1,
outcome,
Some("execution"),
&format!("session-{}", index + 1),
)
})
.collect::<Vec<_>>();
let covered_ids = (1..=case.outcomes.len())
.map(|serial| format!("evt_{serial}"))
.collect::<Vec<_>>();
events.extend(fixture.review(
"review-non-adjudicating",
case.disposition,
&covered_ids.iter().map(String::as_str).collect::<Vec<_>>(),
));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.state, case.expected_state, "{}", case.name);
assert_eq!(
status.authorization_reason.as_deref(),
case.expected_reason,
"{}",
case.name
);
assert_eq!(
status.review_reentry_basis.as_deref(),
Some(case.expected_reentry_basis),
"{}",
case.name
);
assert_eq!(status.open_incident_ids, covered_ids, "{}", case.name);
assert_eq!(status.candidate_clusters.len(), 1, "{}", case.name);
assert_eq!(
status.candidate_clusters[0].open_event_ids, covered_ids,
"{}",
case.name
);
}
}
#[test]
fn every_adjudicating_evolution_disposition_retires_covered_incidents() {
for disposition in [
"resolved_by_change",
"closed_no_skill_defect",
"outside_target",
"insufficient_independence",
"monitor_for_recurrence",
"candidate_rejected_validation",
] {
let fixture = Fixture::new();
let target = json!({
"name": "demo-skill",
"repo_relative_path": fixture.target_relative,
"content_hash": fixture.target_hash,
"repo_head": "fixture-head"
});
fixture.write_events(&[
fixture.use_event(1, "friction", Some("execution"), "session-1"),
json!({
"schema_version": 1,
"event_id": "evt_review_started",
"event_type": "review_started",
"recorded_at": "2026-01-02T02:00:00Z",
"operator_workflow": "skill-evolution",
"target": target,
"top_level_session_id": "review-session",
"payload": {"review_id": "review-adjudicating"}
}),
json!({
"schema_version": 1,
"event_id": "evt_review_disposition",
"event_type": "review_disposition",
"recorded_at": "2026-01-02T03:00:00Z",
"operator_workflow": "skill-evolution",
"target": target,
"top_level_session_id": "review-session",
"payload": {
"review_id": "review-adjudicating",
"disposition": disposition,
"adjudicated_event_ids": ["evt_1"]
}
}),
]);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.state, "closed", "disposition {disposition}");
assert!(
status.open_incident_ids.is_empty(),
"disposition {disposition}"
);
assert!(
status.candidate_clusters.is_empty(),
"disposition {disposition}"
);
}
}
#[test]
fn an_adjudicating_close_leaves_coverage_it_named_untestable_open_and_unclustered() {
let fixture = Fixture::new();
let mut events = vec![
fixture.use_event(1, "friction", Some("execution"), "session-1"),
fixture.use_event(2, "friction", Some("execution"), "session-2"),
];
events.extend(fixture.review_naming_untestable_coverage(
"review-mixed",
"candidate_rejected_validation",
&["evt_1", "evt_2"],
&["evt_1"],
));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(
status.open_incident_ids,
vec!["evt_1".to_owned()],
"the review reached no conclusion about evt_1, so it stays open"
);
assert_eq!(
status.instrument_limited_incident_ids,
vec!["evt_1".to_owned()],
"evt_1 retires from the gate as untestable rather than as adjudicated"
);
assert!(
status.candidate_clusters.is_empty(),
"an incident retired as untestable can never reach a threshold again"
);
}
#[test]
fn post_review_incident_reopens_ten_use_gate_with_its_bounded_cluster() {
let fixture = Fixture::new();
let mut events = vec![
fixture.use_event(1, "friction", Some("output"), "session-1"),
fixture.use_event(2, "friction", Some("tool-compatibility"), "session-2"),
fixture.use_event(3, "friction", Some("tool-compatibility"), "session-3"),
fixture.use_event(4, "friction", Some("tool-compatibility"), "session-4"),
];
for serial in 5..=9 {
events.push(fixture.use_event(serial, "clean", None, &format!("session-{serial}")));
}
events.extend(fixture.review(
"review-post",
"candidate_rejected_validation",
&["evt_2", "evt_3", "evt_4"],
));
events.push(fixture.use_event(10, "friction", Some("execution"), "session-10"));
fixture.write_events(&events);
let status = fixture.derive("session-10", 1_767_398_400_000);
assert_eq!(status.state, "eligible_pending_cooldown");
assert_eq!(
status.authorization_reason.as_deref(),
Some("ten_use_unresolved")
);
assert_eq!(
status.review_reentry_basis.as_deref(),
Some("post_review_incident")
);
assert_eq!(status.trigger_event_ids, ["evt_10"]);
}
#[test]
fn queued_pre_close_threshold_does_not_mask_a_later_post_review_incident() {
let fixture = Fixture::new();
let mut events = vec![
fixture.use_event(1, "material_failure", Some("output"), "session-1"),
fixture.use_event(2, "material_failure", Some("output"), "session-2"),
fixture.use_event(3, "friction", Some("tool-compatibility"), "session-3"),
fixture.use_event(4, "friction", Some("tool-compatibility"), "session-4"),
fixture.use_event(5, "friction", Some("tool-compatibility"), "session-5"),
];
for serial in 6..=10 {
events.push(fixture.use_event(serial, "clean", None, &format!("session-{serial}")));
}
events.extend(fixture.review(
"review-masked",
"candidate_rejected_validation",
&["evt_3", "evt_4", "evt_5"],
));
events.push(fixture.use_event(11, "friction", Some("execution"), "session-11"));
fixture.write_events(&events);
let status = fixture.derive("session-11", 1_767_398_400_000);
assert_eq!(
status.authorization_reason.as_deref(),
Some("ten_use_unresolved")
);
assert_eq!(status.trigger_event_ids, ["evt_11"]);
assert_eq!(
status.review_reentry_basis.as_deref(),
Some("post_review_incident")
);
}
#[test]
fn repeated_same_session_and_task_incidents_are_not_independent() {
let fixture = Fixture::new();
let mut events = (1..=3)
.map(|serial| fixture.use_event(serial, "friction", Some("execution"), "same-session"))
.collect::<Vec<_>>();
for event in &mut events {
event["payload"]["task_fingerprint"] = json!("same-task");
}
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.state, "collecting");
assert_eq!(status.candidate_clusters[0].independent_incidents, 1);
}
#[test]
fn retrospective_incident_can_support_but_not_complete_a_threshold() {
let fixture = Fixture::new();
let mut retrospective =
fixture.use_event(3, "friction", Some("cost"), "retrospective-session");
retrospective["payload"]["retrospective"] = json!(true);
retrospective["payload"]["evidence_refs"] = json!(["reports/evidence.txt"]);
fixture.write_events(&[
fixture.use_event(1, "friction", Some("cost"), "session-1"),
fixture.use_event(2, "friction", Some("cost"), "session-2"),
retrospective,
]);
assert_eq!(
fixture.derive("fresh-session", 1_767_398_400_000).state,
"collecting"
);
let mut events = fixture.recorded_events();
events.push(fixture.use_event(4, "friction", Some("cost"), "session-4"));
fixture.write_events(&events);
assert_eq!(
fixture.derive("session-4", 1_767_398_400_000).state,
"eligible_pending_cooldown"
);
}
#[test]
fn ten_clean_uses_authorize_nothing() {
let fixture = Fixture::new();
let events = (1..=10)
.map(|serial| fixture.use_event(serial, "clean", None, &format!("session-{serial}")))
.collect::<Vec<_>>();
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.qualifying_uses_on_current_hash, 10);
assert_eq!(status.state, "closed");
assert_eq!(status.authorized_workflow, None);
}
#[test]
fn evidence_recorded_against_another_hash_does_not_count_toward_this_one() {
let fixture = Fixture::new();
fixture.write_events(&[
fixture.use_event(1, "friction", Some("execution"), "session-1"),
fixture.use_event(2, "friction", Some("execution"), "session-2"),
]);
let status =
fixture.derive_against("a-hash-the-events-do-not-carry", "fresh", 1_767_398_400_000);
assert_eq!(status.qualifying_uses_on_current_hash, 0);
assert_eq!(status.open_incident_ids, Vec::<String>::new());
assert_eq!(status.state, "closed");
assert_eq!(status.authorization_reason, None);
}
#[test]
fn active_review_owns_the_target() {
let fixture = Fixture::new();
let target = fixture.target();
fixture.write_events(&[
fixture.use_event(1, "friction", Some("execution"), "session-1"),
json!({
"schema_version": 1,
"event_id": "evt_review_started",
"event_type": "review_started",
"recorded_at": "2026-01-02T02:00:00Z",
"operator_workflow": "skill-evolution",
"target": target,
"top_level_session_id": "review-session",
"payload": {"review_id": "active-review"}
}),
]);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.state, "review_in_progress");
assert_eq!(status.active_review_id.as_deref(), Some("active-review"));
}
#[test]
fn unadjudicated_severe_incident_remains_quarantined_across_later_disposition() {
let fixture = Fixture::new();
let target = fixture.target();
fixture.write_events(&[
fixture.use_event(1, "material_failure", Some("output"), "session-1"),
fixture.use_event(2, "material_failure", Some("output"), "session-2"),
fixture.use_event(3, "severe_incident", Some("state"), "session-3"),
json!({
"schema_version": 1,
"event_id": "evt_review_started",
"event_type": "review_started",
"recorded_at": "2026-01-02T04:00:00Z",
"operator_workflow": "skill-evolution",
"target": target,
"top_level_session_id": "review-session",
"payload": {"review_id": "review-severe"}
}),
json!({
"schema_version": 1,
"event_id": "evt_review_disposition",
"event_type": "review_disposition",
"recorded_at": "2026-01-02T05:00:00Z",
"operator_workflow": "skill-evolution",
"target": target,
"top_level_session_id": "review-session",
"payload": {
"review_id": "review-severe",
"disposition": "candidate_rejected_validation",
"adjudicated_event_ids": ["evt_1", "evt_2"]
}
}),
]);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.state, "quarantined_eligible");
assert_eq!(status.authorization_reason.as_deref(), Some("severe"));
assert_eq!(
status.review_reentry_basis.as_deref(),
Some("unadjudicated_severe")
);
assert_eq!(status.trigger_event_ids, ["evt_3"]);
}
#[test]
fn naming_a_contemporaneous_severe_incident_stops_adjudication_without_retiring_it() {
let fixture = Fixture::new();
let mut events =
vec![fixture.use_event(1, "severe_incident", Some("execution"), "session-1")];
events.extend(fixture.review_naming_untestable_coverage(
"review-severe",
"monitor_for_recurrence",
&["evt_1"],
&["evt_1"],
));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.open_incident_ids, vec!["evt_1".to_owned()]);
assert_eq!(
status.instrument_limited_incident_ids,
Vec::<String>::new(),
"it never left the gate, so the projection must not say it did"
);
assert_eq!(
status.authorized_workflow.as_deref(),
Some("skill-evolution"),
"and it still authorizes on its own"
);
}
#[test]
fn untestable_coverage_outside_the_coverage_list_retires_nothing() {
let fixture = Fixture::new();
let mut events = vec![
fixture.use_event(1, "friction", Some("execution"), "session-1"),
fixture.use_event(2, "friction", Some("execution"), "session-2"),
];
events.extend(fixture.review_naming_untestable_coverage(
"review-overreaching",
"monitor_for_recurrence",
&["evt_1"],
&["evt_2"],
));
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(
status.instrument_limited_incident_ids,
Vec::<String>::new(),
"evt_2 is outside the coverage list, so this close established nothing about it"
);
assert_eq!(
status.open_incident_ids,
vec!["evt_2".to_owned()],
"and it stays open, driving the gate as before"
);
assert_eq!(
status.candidate_clusters.len(),
1,
"and stays in its cluster"
);
}
#[test]
fn untestable_coverage_refuses_a_malformed_shape_on_read() {
for (case, invalid) in [
("not an array", json!("evt_1")),
("non-string member", json!([42])),
("empty array", json!([])),
("empty member", json!([""])),
] {
let fixture = Fixture::new();
let mut events = vec![fixture.use_event(1, "friction", Some("execution"), "session-1")];
let mut review =
fixture.review("review-malformed", "monitor_for_recurrence", &["evt_1"]);
review[1]["payload"]["instrument_limited_event_ids"] = invalid;
events.extend(review);
fixture.write_events(&events);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.state, "blocked", "case {case}");
assert!(
status
.integrity_errors
.iter()
.any(|error| error.contains("instrument_limited_event_ids")),
"case {case}: a shape the reader cannot trust must not be silently read as an empty narrowing: {:?}",
status.integrity_errors
);
}
}
#[test]
fn optional_incident_text_fields_refuse_non_string_or_empty_values_on_read() {
for (field, invalid) in [
("workaround_taken", json!(false)),
("workaround_taken", json!("")),
("run_condition", json!(false)),
("run_condition", json!("")),
] {
let fixture = Fixture::new();
let mut event = fixture.use_event(1, "friction", Some("execution"), "session-1");
event["payload"][field] = invalid;
fixture.write_events(&[event]);
let status = fixture.derive("fresh-session", 1_767_398_400_000);
assert_eq!(status.state, "blocked", "field {field}");
assert!(
status
.integrity_errors
.iter()
.any(|error| error.contains(field)),
"missing {field} integrity error: {:?}",
status.integrity_errors
);
}
}
}