Skip to main content

a3s_code_core/state_graph/
event.rs

1use super::{GraphPatch, ObjectId, RelationId};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4
5pub const GRAPH_EVENT_SCHEMA_VERSION: u32 = 1;
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8#[serde(tag = "type", rename_all = "snake_case")]
9pub enum GraphEvent {
10    GoalCreated {
11        goal: String,
12    },
13    ObjectCreated {
14        id: ObjectId,
15        object_type: String,
16        data: Value,
17    },
18    ObjectUpdated {
19        id: ObjectId,
20        version: u64,
21        data: Value,
22    },
23    ObjectRemoved {
24        id: ObjectId,
25        version: u64,
26    },
27    RelationCreated {
28        id: RelationId,
29        relation_type: String,
30        source: ObjectId,
31        target: ObjectId,
32        data: Value,
33    },
34    RelationUpdated {
35        id: RelationId,
36        version: u64,
37        data: Value,
38    },
39    RelationRemoved {
40        id: RelationId,
41        version: u64,
42    },
43    PatchProposed {
44        patch_id: String,
45        patch: GraphPatch,
46    },
47    PatchApplied {
48        patch_id: String,
49    },
50    PatchRejected {
51        patch_id: String,
52        reason: String,
53    },
54    BehaviorStarted {
55        name: String,
56    },
57    BehaviorCompleted {
58        name: String,
59    },
60    BehaviorFailed {
61        name: String,
62        error: String,
63    },
64    BranchForked {
65        parent_branch_id: String,
66        fork_sequence: u64,
67    },
68    Custom {
69        name: String,
70        payload: Value,
71    },
72}
73
74impl GraphEvent {
75    pub fn event_type(&self) -> &str {
76        match self {
77            Self::GoalCreated { .. } => "goal.created",
78            Self::ObjectCreated { .. } => "object.created",
79            Self::ObjectUpdated { .. } => "object.updated",
80            Self::ObjectRemoved { .. } => "object.removed",
81            Self::RelationCreated { .. } => "relation.created",
82            Self::RelationUpdated { .. } => "relation.updated",
83            Self::RelationRemoved { .. } => "relation.removed",
84            Self::PatchProposed { .. } => "patch.proposed",
85            Self::PatchApplied { .. } => "patch.applied",
86            Self::PatchRejected { .. } => "patch.rejected",
87            Self::BehaviorStarted { .. } => "behavior.started",
88            Self::BehaviorCompleted { .. } => "behavior.completed",
89            Self::BehaviorFailed { .. } => "behavior.failed",
90            Self::BranchForked { .. } => "branch.forked",
91            Self::Custom { name, .. } => name,
92        }
93    }
94}
95
96#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
97pub struct GraphEventRecord {
98    pub schema_version: u32,
99    pub id: String,
100    pub sequence: u64,
101    pub timestamp_ms: u64,
102    pub branch_id: String,
103    #[serde(skip_serializing_if = "Option::is_none")]
104    pub causation_id: Option<String>,
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub correlation_id: Option<String>,
107    pub state_version_before: u64,
108    pub state_version_after: u64,
109    /// SHA-256 of the deterministic graph projection after this event.
110    pub state_hash_after: String,
111    /// Hash of the preceding record, forming a tamper-evident event chain.
112    #[serde(skip_serializing_if = "Option::is_none")]
113    pub previous_record_hash: Option<String>,
114    /// SHA-256 over this record's immutable fields and `previous_record_hash`.
115    pub record_hash: String,
116    pub event: GraphEvent,
117}