use std::fmt;
const NO_INTENT_SOURCE: &str = "no intent source";
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NoIntentKind {
Unresolved(String),
Gap,
NonTicketed,
AutonomyInputEmpty,
}
impl fmt::Display for NoIntentKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NoIntentKind::Unresolved(reason) => write!(f, "unresolved: {reason}"),
NoIntentKind::Gap => f.write_str("no prescribed method; gap"),
NoIntentKind::NonTicketed => f.write_str("non-ticketed work"),
NoIntentKind::AutonomyInputEmpty => f.write_str("autonomy input empty"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DispositionReason {
T1StyleOnly,
T1ReviewReject,
T1CiRed,
T2AllClear,
T2GuardrailFailed(&'static str),
T3Approved,
T3RequiresApprove(&'static str),
T4Escalate,
PriorRejection(u32),
NoIntentSource(NoIntentKind),
ConformanceMatch,
ConformanceNoDivergentPlan,
ConformanceDivergence {
prescribed: String,
planned: String,
},
}
impl fmt::Display for DispositionReason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DispositionReason::T1StyleOnly => {
f.write_str("T1: style-only change with no objecting guardrail")
}
DispositionReason::T1ReviewReject => {
f.write_str("T1: trusty-review returned REJECT on a style-only change")
}
DispositionReason::T1CiRed => f.write_str("T1: CI is red on a style-only change"),
DispositionReason::T2AllClear => f.write_str(
"T2: all structured guardrails green (review APPROVE, CI green, search+memory consistent, in-scope)",
),
DispositionReason::T2GuardrailFailed(signal) => {
write!(f, "T2: guardrail not satisfied: {signal}")
}
DispositionReason::T3Approved => f.write_str(
"T3: architecture-touching change with explicit trusty-review APPROVE and in-scope validation",
),
DispositionReason::T3RequiresApprove(signal) => {
write!(f, "T3: requires explicit APPROVE + in-scope + non-red CI; got {signal}")
}
DispositionReason::T4Escalate => f.write_str(
"T4: irreversible or security-sensitive operation; human confirmation required",
),
DispositionReason::PriorRejection(count) => write!(
f,
"decision previously rejected {count} time(s); re-escalating to human"
),
DispositionReason::NoIntentSource(kind) => write!(f, "{NO_INTENT_SOURCE} ({kind})"),
DispositionReason::ConformanceMatch => {
f.write_str("conformance: planned method matches the ticket/spec method")
}
DispositionReason::ConformanceNoDivergentPlan => {
f.write_str("conformance: no divergent plan; ticket/spec method honoured")
}
DispositionReason::ConformanceDivergence {
prescribed,
planned,
} => write!(
f,
"conformance divergence: ticket/spec specifies \"{prescribed}\"; plan uses \"{planned}\""
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_intent_prefix_matches_gate_const() {
assert_eq!(
NO_INTENT_SOURCE,
crate::daemon::managed_routes::front_gate::NO_INTENT_SOURCE
);
}
#[test]
fn renders_tier_reasons_byte_identical() {
assert_eq!(
DispositionReason::T1StyleOnly.to_string(),
"T1: style-only change with no objecting guardrail"
);
assert_eq!(
DispositionReason::T1ReviewReject.to_string(),
"T1: trusty-review returned REJECT on a style-only change"
);
assert_eq!(
DispositionReason::T1CiRed.to_string(),
"T1: CI is red on a style-only change"
);
assert_eq!(
DispositionReason::T2AllClear.to_string(),
"T2: all structured guardrails green (review APPROVE, CI green, search+memory consistent, in-scope)"
);
assert_eq!(
DispositionReason::T2GuardrailFailed("trusty-review did not APPROVE").to_string(),
"T2: guardrail not satisfied: trusty-review did not APPROVE"
);
assert_eq!(
DispositionReason::T3Approved.to_string(),
"T3: architecture-touching change with explicit trusty-review APPROVE and in-scope validation"
);
assert_eq!(
DispositionReason::T3RequiresApprove("CI not green").to_string(),
"T3: requires explicit APPROVE + in-scope + non-red CI; got CI not green"
);
assert_eq!(
DispositionReason::T4Escalate.to_string(),
"T4: irreversible or security-sensitive operation; human confirmation required"
);
assert_eq!(
DispositionReason::PriorRejection(2).to_string(),
"decision previously rejected 2 time(s); re-escalating to human"
);
}
#[test]
fn renders_conformance_reasons_byte_identical() {
assert_eq!(
DispositionReason::ConformanceMatch.to_string(),
"conformance: planned method matches the ticket/spec method"
);
assert_eq!(
DispositionReason::ConformanceNoDivergentPlan.to_string(),
"conformance: no divergent plan; ticket/spec method honoured"
);
assert_eq!(
DispositionReason::ConformanceDivergence {
prescribed: "use cursor-based pagination".to_string(),
planned: "use offset pagination".to_string(),
}
.to_string(),
"conformance divergence: ticket/spec specifies \"use cursor-based pagination\"; plan uses \"use offset pagination\""
);
}
#[test]
fn renders_no_intent_reasons_byte_identical() {
assert_eq!(
DispositionReason::NoIntentSource(NoIntentKind::Unresolved(
"ticket fetch failed: 404".to_string()
))
.to_string(),
"no intent source (unresolved: ticket fetch failed: 404)"
);
assert_eq!(
DispositionReason::NoIntentSource(NoIntentKind::Gap).to_string(),
"no intent source (no prescribed method; gap)"
);
assert_eq!(
DispositionReason::NoIntentSource(NoIntentKind::NonTicketed).to_string(),
"no intent source (non-ticketed work)"
);
assert_eq!(
DispositionReason::NoIntentSource(NoIntentKind::AutonomyInputEmpty).to_string(),
"no intent source (autonomy input empty)"
);
}
}