use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Primitive {
Syscall,
Sched,
Mm,
}
impl Primitive {
pub fn label(self) -> &'static str {
match self {
Self::Syscall => "syscall",
Self::Sched => "sched",
Self::Mm => "mm",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum KernelEventCategory {
Syscall,
Sched,
Mm,
Proc,
Ipc,
}
impl KernelEventCategory {
pub fn primitive(self) -> Primitive {
match self {
Self::Syscall => Primitive::Syscall,
Self::Sched | Self::Proc | Self::Ipc => Primitive::Sched,
Self::Mm => Primitive::Mm,
}
}
}
pub fn primitive_for_kind(kind: &str) -> Primitive {
category_for_kind(kind).primitive()
}
pub fn category_for_kind(kind: &str) -> KernelEventCategory {
match kind {
"tool_gated" | "capability_changed" => KernelEventCategory::Syscall,
"suspended"
| "resumed"
| "budget_exceeded"
| "checkpoint_taken"
| "rollbacked"
| "milestone_advanced"
| "milestone_blocked"
| "milestone_evidence" => KernelEventCategory::Sched,
"compressed"
| "page_out"
| "page_in"
| "page_in_requested"
| "renewed"
| "context_renewed"
| "large_result_spooled" => KernelEventCategory::Mm,
"agent_process_changed" | "agent_spawned" | "workflow_batch_spawned"
| "workflow_completed" | "agent_preempted" => KernelEventCategory::Proc,
"signal_disposed" => KernelEventCategory::Ipc,
"memory_written" | "memory_queried" | "memory_validation_failed" => KernelEventCategory::Mm,
_ => KernelEventCategory::Sched,
}
}
pub const KERNEL_OBSERVATION_KINDS: &[&str] = &[
"compressed",
"page_out",
"page_in_requested",
"renewed",
"rollbacked",
"capability_changed",
"milestone_advanced",
"milestone_blocked",
"milestone_evidence",
"checkpoint_taken",
"agent_process_changed",
"workflow_batch_spawned",
"workflow_completed",
"agent_preempted",
"tool_gated",
"signal_disposed",
"budget_exceeded",
"suspended",
"resumed",
"memory_written",
"memory_queried",
"memory_validation_failed",
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn maps_observation_kinds_to_categories() {
assert_eq!(category_for_kind("tool_gated"), KernelEventCategory::Syscall);
assert_eq!(category_for_kind("page_out"), KernelEventCategory::Mm);
assert_eq!(category_for_kind("agent_process_changed"), KernelEventCategory::Proc);
assert_eq!(category_for_kind("signal_disposed"), KernelEventCategory::Ipc);
assert_eq!(category_for_kind("suspended"), KernelEventCategory::Sched);
}
#[test]
fn kernel_observation_kinds_cover_abi_surface() {
assert!(KERNEL_OBSERVATION_KINDS.contains(&"page_out"));
assert!(KERNEL_OBSERVATION_KINDS.contains(&"resumed"));
}
#[test]
fn categories_roll_up_to_three_primitives() {
assert_eq!(KernelEventCategory::Syscall.primitive(), Primitive::Syscall);
assert_eq!(KernelEventCategory::Sched.primitive(), Primitive::Sched);
assert_eq!(KernelEventCategory::Proc.primitive(), Primitive::Sched);
assert_eq!(KernelEventCategory::Ipc.primitive(), Primitive::Sched);
assert_eq!(KernelEventCategory::Mm.primitive(), Primitive::Mm);
}
#[test]
fn every_kernel_observation_kind_maps_to_a_primitive() {
for kind in KERNEL_OBSERVATION_KINDS {
let p = primitive_for_kind(kind);
assert!(matches!(p, Primitive::Syscall | Primitive::Sched | Primitive::Mm));
}
assert_eq!(primitive_for_kind("agent_process_changed"), Primitive::Sched);
assert_eq!(primitive_for_kind("signal_disposed"), Primitive::Sched);
assert_eq!(primitive_for_kind("tool_gated"), Primitive::Syscall);
assert_eq!(primitive_for_kind("page_out"), Primitive::Mm);
}
}