Skip to main content

deepstrike_core/runtime/
event_log.rs

1//! Unified kernel OS event log — category taxonomy for observations and session events.
2//!
3//! Phase 5: every kernel decision is classifiable as `syscall` / `sched` / `mm` / `proc` / `ipc`
4//! so SDK session logs can be audited and replayed as a single OS event stream.
5//!
6//! Three-primitives lens (M4): every kernel event rolls up to exactly one of the three kernel
7//! primitives — **P1 syscall** (the adjudication trap), **P2 sched** (the TCB/task table + the
8//! scheduler), **P3 mm** (the handle table + paging). The five wire categories above are retained
9//! as finer-grained audit labels (a stable, shipped field), but `proc` and `ipc` are facets of the
10//! P2 scheduler — the process table *is* the task table, and signal disposition *feeds* the
11//! scheduler — so they project onto [`Primitive::Sched`]. See [`KernelEventCategory::primitive`].
12
13use serde::{Deserialize, Serialize};
14
15/// One of the three kernel primitives every OS event belongs to (the canonical decision planes).
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
17#[serde(rename_all = "snake_case")]
18pub enum Primitive {
19    /// P1 — the single syscall trap (governance/capability/spawn/memory adjudication).
20    Syscall,
21    /// P2 — the TCB/task table + scheduler (budgets, lifecycle, process table, signal disposition).
22    Sched,
23    /// P3 — the handle table + paging (compression, page-in/out, renewal, long-term memory).
24    Mm,
25}
26
27impl Primitive {
28    pub fn label(self) -> &'static str {
29        match self {
30            Self::Syscall => "syscall",
31            Self::Sched => "sched",
32            Self::Mm => "mm",
33        }
34    }
35}
36
37/// Agent OS event category (kernel decision plane). Finer-grained than [`Primitive`]; retained as a
38/// stable wire field. Use [`KernelEventCategory::primitive`] for the three-primitives rollup.
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
40#[serde(rename_all = "snake_case")]
41pub enum KernelEventCategory {
42    /// Governance gates, capability mount/unmount, tool approval.
43    Syscall,
44    /// Scheduling, budgets, suspend/resume, checkpoints, milestones, rollback.
45    Sched,
46    /// Working ↔ long-term memory: compression, page-in/out, renewal.
47    Mm,
48    /// Sub-agent process table lifecycle.
49    Proc,
50    /// Signal routing and disposition.
51    Ipc,
52}
53
54impl KernelEventCategory {
55    /// Roll this fine-grained category up to its kernel primitive. `Proc` and `Ipc` are facets of
56    /// the P2 scheduler (process table = task table; signals feed the scheduler).
57    pub fn primitive(self) -> Primitive {
58        match self {
59            Self::Syscall => Primitive::Syscall,
60            Self::Sched | Self::Proc | Self::Ipc => Primitive::Sched,
61            Self::Mm => Primitive::Mm,
62        }
63    }
64}
65
66/// The kernel primitive an observation/session `kind` belongs to (three-primitives rollup).
67pub fn primitive_for_kind(kind: &str) -> Primitive {
68    category_for_kind(kind).primitive()
69}
70
71/// Snake_case observation / session `kind` string.
72pub fn category_for_kind(kind: &str) -> KernelEventCategory {
73    match kind {
74        "tool_gated" | "capability_changed" => KernelEventCategory::Syscall,
75        "suspended"
76        | "resumed"
77        | "budget_exceeded"
78        | "checkpoint_taken"
79        | "rollbacked"
80        | "milestone_advanced"
81        | "milestone_blocked"
82        | "milestone_evidence" => KernelEventCategory::Sched,
83        "compressed"
84        | "page_out"
85        | "page_in"
86        | "page_in_requested"
87        |         "renewed"
88        | "context_renewed"
89        | "large_result_spooled" => KernelEventCategory::Mm,
90        "agent_process_changed" | "agent_spawned" | "workflow_batch_spawned"
91        | "workflow_completed" | "agent_preempted" => KernelEventCategory::Proc,
92        "signal_disposed" => KernelEventCategory::Ipc,
93        "memory_written" | "memory_queried" | "memory_validation_failed" => KernelEventCategory::Mm,
94        _ => KernelEventCategory::Sched,
95    }
96}
97
98/// All kernel observation kinds that should appear in a unified OS event log.
99pub const KERNEL_OBSERVATION_KINDS: &[&str] = &[
100    "compressed",
101    "page_out",
102    "page_in_requested",
103    "renewed",
104    "rollbacked",
105    "capability_changed",
106    "milestone_advanced",
107    "milestone_blocked",
108    "milestone_evidence",
109    "checkpoint_taken",
110    "agent_process_changed",
111    "workflow_batch_spawned",
112    "workflow_completed",
113    "agent_preempted",
114    "tool_gated",
115    "signal_disposed",
116    "budget_exceeded",
117    "suspended",
118    "resumed",
119    "memory_written",
120    "memory_queried",
121    "memory_validation_failed",
122];
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn maps_observation_kinds_to_categories() {
130        assert_eq!(category_for_kind("tool_gated"), KernelEventCategory::Syscall);
131        assert_eq!(category_for_kind("page_out"), KernelEventCategory::Mm);
132        assert_eq!(category_for_kind("agent_process_changed"), KernelEventCategory::Proc);
133        assert_eq!(category_for_kind("signal_disposed"), KernelEventCategory::Ipc);
134        assert_eq!(category_for_kind("suspended"), KernelEventCategory::Sched);
135    }
136
137    #[test]
138    fn kernel_observation_kinds_cover_abi_surface() {
139        assert!(KERNEL_OBSERVATION_KINDS.contains(&"page_out"));
140        assert!(KERNEL_OBSERVATION_KINDS.contains(&"resumed"));
141    }
142
143    #[test]
144    fn categories_roll_up_to_three_primitives() {
145        // Proc and Ipc are facets of the P2 scheduler.
146        assert_eq!(KernelEventCategory::Syscall.primitive(), Primitive::Syscall);
147        assert_eq!(KernelEventCategory::Sched.primitive(), Primitive::Sched);
148        assert_eq!(KernelEventCategory::Proc.primitive(), Primitive::Sched);
149        assert_eq!(KernelEventCategory::Ipc.primitive(), Primitive::Sched);
150        assert_eq!(KernelEventCategory::Mm.primitive(), Primitive::Mm);
151    }
152
153    #[test]
154    fn every_kernel_observation_kind_maps_to_a_primitive() {
155        // syscall trap, scheduler, and paging cover the entire ABI surface — no orphans.
156        for kind in KERNEL_OBSERVATION_KINDS {
157            let p = primitive_for_kind(kind);
158            assert!(matches!(p, Primitive::Syscall | Primitive::Sched | Primitive::Mm));
159        }
160        assert_eq!(primitive_for_kind("agent_process_changed"), Primitive::Sched);
161        assert_eq!(primitive_for_kind("signal_disposed"), Primitive::Sched);
162        assert_eq!(primitive_for_kind("tool_gated"), Primitive::Syscall);
163        assert_eq!(primitive_for_kind("page_out"), Primitive::Mm);
164    }
165}