Skip to main content

arkhe_kernel/state/
op.rs

1//! `Op` — kernel-level effect payload (ABI sub-enums).
2//!
3//! Each variant is the "what to do" intent; `Effect<S, 'i>` wraps it with
4//! authorization state, instance brand, and originating principal.
5//! Ships a flat enum; sub-categorization is reserved (deferred).
6
7use bytes::Bytes;
8use serde::{Deserialize, Serialize};
9
10use crate::abi::{EntityId, InstanceId, Principal, RouteId, Tick, TypeCode};
11
12/// Kernel-level effect intent. Each variant is "what to do"; the
13/// kernel wraps it in [`Effect`](crate::state::Effect) for
14/// authorization, then dispatches into the per-step `StepStage`.
15#[derive(Debug, Clone, Serialize, Deserialize)]
16#[non_exhaustive]
17pub enum Op {
18    /// Register a new entity with the given id and owner.
19    SpawnEntity {
20        /// Entity id to register.
21        id: EntityId,
22        /// Owning principal — recorded in `EntityMeta`.
23        owner: Principal,
24    },
25    /// Remove an entity from the instance.
26    DespawnEntity {
27        /// Entity to remove.
28        id: EntityId,
29    },
30    /// Attach (or replace) a component on `entity` under `type_code`.
31    SetComponent {
32        /// Entity that owns the component.
33        entity: EntityId,
34        /// Component type discriminant.
35        type_code: TypeCode,
36        /// Canonical-postcard bytes of the component value.
37        bytes: Bytes,
38        /// Approximate size in bytes — used by the resource ledger
39        /// (memory budget enforcement).
40        size: u64,
41    },
42    /// Detach a component from `entity`.
43    RemoveComponent {
44        /// Entity to detach from.
45        entity: EntityId,
46        /// Component type discriminant.
47        type_code: TypeCode,
48        /// Approximate size in bytes — must match the original
49        /// `SetComponent` size for the ledger to balance.
50        size: u64,
51    },
52    /// Emit a domain event. Surfaces as `KernelEvent::DomainEventEmitted`
53    /// to observers post-commit.
54    EmitEvent {
55        /// Optional originating entity.
56        actor: Option<EntityId>,
57        /// Event type discriminant.
58        event_type_code: TypeCode,
59        /// Canonical-postcard bytes of the event payload.
60        event_bytes: Bytes,
61    },
62    /// Enqueue another action for a future tick. The scheduled action
63    /// inherits the *scheduling* action's principal (captured at dispatch)
64    /// and a `caps_ceiling` equal to the parent's effective capabilities —
65    /// privilege can only narrow across a schedule, never widen, so a domain
66    /// action cannot mint a more-privileged future action (no time-shifted
67    /// escalation). Kernel-internal System-origin scheduling goes through a
68    /// kernel API, not this domain Op.
69    ScheduleAction {
70        /// Tick at which the scheduled action becomes due.
71        at: Tick,
72        /// Optional originating entity.
73        actor: Option<EntityId>,
74        /// Type code of the scheduled action.
75        action_type_code: TypeCode,
76        /// Canonical-postcard bytes of the scheduled action.
77        action_bytes: Bytes,
78    },
79    /// Cross-instance signal. Routed by the kernel to the target
80    /// instance's IPC queue post-commit.
81    SendSignal {
82        /// Receiving instance.
83        target: InstanceId,
84        /// Route discriminant — the receiving instance dispatches by
85        /// this id.
86        route: RouteId,
87        /// Canonical-postcard bytes of the signal payload.
88        payload: Bytes,
89    },
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn op_variants_clone() {
98        let id = EntityId::new(1).unwrap();
99        let _ = Op::SpawnEntity {
100            id,
101            owner: Principal::System,
102        }
103        .clone();
104        let _ = Op::DespawnEntity { id }.clone();
105        let _ = Op::SetComponent {
106            entity: id,
107            type_code: TypeCode(1),
108            bytes: Bytes::from_static(b"x"),
109            size: 1,
110        }
111        .clone();
112        let _ = Op::RemoveComponent {
113            entity: id,
114            type_code: TypeCode(1),
115            size: 1,
116        }
117        .clone();
118        let _ = Op::EmitEvent {
119            actor: Some(id),
120            event_type_code: TypeCode(2),
121            event_bytes: Bytes::new(),
122        }
123        .clone();
124        let _ = Op::ScheduleAction {
125            at: Tick(0),
126            actor: None,
127            action_type_code: TypeCode(3),
128            action_bytes: Bytes::new(),
129        }
130        .clone();
131        let _ = Op::SendSignal {
132            target: InstanceId::new(1).unwrap(),
133            route: RouteId(1),
134            payload: Bytes::new(),
135        }
136        .clone();
137    }
138}