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.
63 ScheduleAction {
64 /// Tick at which the scheduled action becomes due.
65 at: Tick,
66 /// Optional originating entity.
67 actor: Option<EntityId>,
68 /// Type code of the scheduled action.
69 action_type_code: TypeCode,
70 /// Canonical-postcard bytes of the scheduled action.
71 action_bytes: Bytes,
72 /// Principal under which the scheduled action will be authorized.
73 action_principal: Principal,
74 },
75 /// Cross-instance signal. Routed by the kernel to the target
76 /// instance's IPC queue post-commit.
77 SendSignal {
78 /// Receiving instance.
79 target: InstanceId,
80 /// Route discriminant — the receiving instance dispatches by
81 /// this id.
82 route: RouteId,
83 /// Canonical-postcard bytes of the signal payload.
84 payload: Bytes,
85 },
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn op_variants_clone() {
94 let id = EntityId::new(1).unwrap();
95 let _ = Op::SpawnEntity {
96 id,
97 owner: Principal::System,
98 }
99 .clone();
100 let _ = Op::DespawnEntity { id }.clone();
101 let _ = Op::SetComponent {
102 entity: id,
103 type_code: TypeCode(1),
104 bytes: Bytes::from_static(b"x"),
105 size: 1,
106 }
107 .clone();
108 let _ = Op::RemoveComponent {
109 entity: id,
110 type_code: TypeCode(1),
111 size: 1,
112 }
113 .clone();
114 let _ = Op::EmitEvent {
115 actor: Some(id),
116 event_type_code: TypeCode(2),
117 event_bytes: Bytes::new(),
118 }
119 .clone();
120 let _ = Op::ScheduleAction {
121 at: Tick(0),
122 actor: None,
123 action_type_code: TypeCode(3),
124 action_bytes: Bytes::new(),
125 action_principal: Principal::System,
126 }
127 .clone();
128 let _ = Op::SendSignal {
129 target: InstanceId::new(1).unwrap(),
130 route: RouteId(1),
131 payload: Bytes::new(),
132 }
133 .clone();
134 }
135}