//! SPEC-060: System Ontology - Event & Entity Structures //! Component: core/ontology //! Target: Ground Zero - Phase 2 const std = @import("std"); /// EventKind: Closed enumeration of system events (SPEC-060) pub const EventKind = enum(u16) { Null = 0, // Lifecycle Events SystemBoot = 1, SystemShutdown = 2, FiberSpawn = 3, FiberTerminate = 4, // Capability Events CapabilityGrant = 10, CapabilityRevoke = 11, CapabilityDelegate = 12, // I/O Events ChannelOpen = 20, ChannelClose = 21, ChannelRead = 22, ChannelWrite = 23, // Memory Events MemoryAllocate = 30, MemoryFree = 31, MemoryMap = 32, // Network Events NetworkPacketRx = 40, NetworkPacketTx = 41, // Security Events AccessDenied = 50, PolicyViolation = 51, }; /// Event: Immutable record of a system occurrence (58 bytes, packed) /// NOTE: Packed to 58 bytes for Zig compatibility (packed structs can't contain arrays) pub const Event = packed struct { kind: EventKind, // 2 bytes - Event type _reserved: u16, // 2 bytes - Alignment timestamp_ns: u64, // 8 bytes - Nanosecond timestamp fiber_id: u64, // 8 bytes - Originating fiber entity_id: u64, // 8 bytes - Target entity (SipHash) cause_id: u64, // 8 bytes - Causal parent event ID data0: u64, // 8 bytes - Event-specific data data1: u64, // 8 bytes - Event-specific data data2: u64, // 8 bytes - Event-specific data // Total: 58 bytes (packed) /// Create a null event pub fn null_event() Event { return .{ .kind = .Null, ._reserved = 0, .timestamp_ns = 0, .fiber_id = 0, .entity_id = 0, .cause_id = 0, .data0 = 0, .data1 = 0, .data2 = 0, }; } /// Check if event is null pub fn is_null(self: *const Event) bool { return self.kind == .Null; } }; /// EntityKind: Types of system entities (SPEC-060) pub const EntityKind = enum(u8) { Null = 0, Fiber = 1, Channel = 2, Memory = 3, File = 4, Network = 5, Device = 6, }; /// Entity: Represents a system resource (32 bytes, cache-aligned) pub const Entity = extern struct { kind: EntityKind, // 1 byte - Entity type _reserved: [7]u8, // 7 bytes - Alignment entity_id: u64, // 8 bytes - Unique ID (SipHash) parent_id: u64, // 8 bytes - Parent entity (for hierarchy) metadata: u64, // 8 bytes - Entity-specific metadata comptime { if (@sizeOf(Entity) != 32) { @compileError("Entity must be exactly 32 bytes"); } } /// Create a null entity pub fn null_entity() Entity { return .{ .kind = .Null, ._reserved = [_]u8{0} ** 7, .entity_id = 0, .parent_id = 0, .metadata = 0, }; } /// Check if entity is null pub fn is_null(self: *const Entity) bool { return self.kind == .Null; } }; /// System Truth Ledger: Append-only event log pub const STL_SIZE = 4096; // Maximum events in ring buffer pub const SystemTruthLedger = struct { events: [STL_SIZE]Event, head: u32, // Next write position tail: u32, // Oldest event position epoch: u32, // Wraparound counter _padding: u32, // Alignment /// Initialize empty STL pub fn init() SystemTruthLedger { var stl = SystemTruthLedger{ .events = undefined, .head = 0, .tail = 0, .epoch = 0, ._padding = 0, }; // Initialize all events to Null for (&stl.events) |*event| { event.* = Event.null_event(); } return stl; } /// Append event to ledger (returns event ID) pub fn append(self: *SystemTruthLedger, event: Event) u64 { const idx = self.head; self.events[idx] = event; // Advance head self.head = (self.head + 1) % STL_SIZE; // If we wrapped, advance tail if (self.head == self.tail) { self.tail = (self.tail + 1) % STL_SIZE; self.epoch +%= 1; } // Event ID = epoch << 32 | index return (@as(u64, self.epoch) << 32) | @as(u64, idx); } /// Lookup event by ID pub fn lookup(self: *const SystemTruthLedger, event_id: u64) ?*const Event { const idx = @as(u32, @truncate(event_id & 0xFFFFFFFF)); const epoch = @as(u32, @truncate(event_id >> 32)); if (idx >= STL_SIZE) return null; if (epoch != self.epoch and idx >= self.head) return null; const event = &self.events[idx]; if (event.is_null()) return null; return event; } /// Get current event count pub fn count(self: *const SystemTruthLedger) u32 { if (self.epoch > 0) return STL_SIZE; return self.head; } }; /// Global System Truth Ledger pub var global_stl: SystemTruthLedger = undefined; pub var stl_initialized: bool = false; /// Initialize STL subsystem pub export fn stl_init() void { if (stl_initialized) return; global_stl = SystemTruthLedger.init(); stl_initialized = true; } /// Get current timestamp (placeholder - will be replaced by HAL timer) fn get_timestamp_ns() u64 { // TODO: Integrate with HAL timer return 0; } /// Emit event to STL (C ABI) pub export fn stl_emit( kind: u16, fiber_id: u64, entity_id: u64, cause_id: u64, data0: u64, data1: u64, data2: u64, ) u64 { if (!stl_initialized) return 0; const event = Event{ .kind = @enumFromInt(kind), ._reserved = 0, .timestamp_ns = get_timestamp_ns(), .fiber_id = fiber_id, .entity_id = entity_id, .cause_id = cause_id, .data0 = data0, .data1 = data1, .data2 = data2, }; return global_stl.append(event); } /// Lookup event by ID (C ABI) pub export fn stl_lookup(event_id: u64) ?*const Event { if (!stl_initialized) return null; return global_stl.lookup(event_id); } /// Get event count (C ABI) pub export fn stl_count() u32 { if (!stl_initialized) return 0; return global_stl.count(); } /// Query result structure for event filtering pub const QueryResult = extern struct { count: u32, events: [64]*const Event, // Max 64 results per query }; /// Query events by fiber ID (C ABI) pub export fn stl_query_by_fiber(fiber_id: u64, result: *QueryResult) void { if (!stl_initialized) { result.count = 0; return; } var count: u32 = 0; var idx = global_stl.tail; while (idx != global_stl.head and count < 64) : (idx = (idx + 1) % STL_SIZE) { const event = &global_stl.events[idx]; if (!event.is_null() and event.fiber_id == fiber_id) { result.events[count] = event; count += 1; } } result.count = count; } /// Query events by kind (C ABI) pub export fn stl_query_by_kind(kind: u16, result: *QueryResult) void { if (!stl_initialized) { result.count = 0; return; } var count: u32 = 0; var idx = global_stl.tail; while (idx != global_stl.head and count < 64) : (idx = (idx + 1) % STL_SIZE) { const event = &global_stl.events[idx]; if (!event.is_null() and @intFromEnum(event.kind) == kind) { result.events[count] = event; count += 1; } } result.count = count; } /// Get recent events (last N) (C ABI) pub export fn stl_get_recent(max_count: u32, result: *QueryResult) void { if (!stl_initialized) { result.count = 0; return; } const actual_count = @min(max_count, @min(global_stl.count(), 64)); var count: u32 = 0; // Start from most recent (head - 1) var idx: u32 = if (global_stl.head == 0) STL_SIZE - 1 else global_stl.head - 1; while (count < actual_count) { const event = &global_stl.events[idx]; if (!event.is_null()) { result.events[count] = event; count += 1; } if (idx == global_stl.tail) break; idx = if (idx == 0) STL_SIZE - 1 else idx - 1; } result.count = count; } /// Lineage result structure for causal tracing pub const LineageResult = extern struct { count: u32, event_ids: [16]u64, // Maximum depth of 16 for causal chains }; /// Trace the causal lineage of an event (C ABI) pub export fn stl_trace_lineage(event_id: u64, result: *LineageResult) void { if (!stl_initialized) { result.count = 0; return; } var count: u32 = 0; var current_id = event_id; while (count < 16) { const event = global_stl.lookup(current_id) orelse break; result.event_ids[count] = current_id; count += 1; // Stop if we reach an event with no parent (or self-referencing parent) if (event.cause_id == current_id) break; // In our system, the root event (SystemBoot) has ID 0 and cause_id 0 if (current_id == 0 and event.cause_id == 0) break; current_id = event.cause_id; } result.count = count; } /// Query events by time range (C ABI) pub export fn stl_query_by_time_range(start_ns: u64, end_ns: u64, result: *QueryResult) void { if (!stl_initialized) { result.count = 0; return; } var count: u32 = 0; var idx = global_stl.tail; while (idx != global_stl.head and count < 64) : (idx = (idx + 1) % STL_SIZE) { const event = &global_stl.events[idx]; if (!event.is_null() and event.timestamp_ns >= start_ns and event.timestamp_ns <= end_ns) { result.events[count] = event; count += 1; } } result.count = count; } /// System statistics structure pub const SystemStats = extern struct { total_events: u32, boot_events: u32, fiber_events: u32, cap_events: u32, io_events: u32, mem_events: u32, net_events: u32, security_events: u32, }; /// Get system statistics from STL (C ABI) pub export fn stl_get_stats(stats: *SystemStats) void { if (!stl_initialized) { stats.* = .{ .total_events = 0, .boot_events = 0, .fiber_events = 0, .cap_events = 0, .io_events = 0, .mem_events = 0, .net_events = 0, .security_events = 0, }; return; } var s = SystemStats{ .total_events = global_stl.count(), .boot_events = 0, .fiber_events = 0, .cap_events = 0, .io_events = 0, .mem_events = 0, .net_events = 0, .security_events = 0, }; var idx = global_stl.tail; while (idx != global_stl.head) : (idx = (idx + 1) % STL_SIZE) { const event = &global_stl.events[idx]; if (event.is_null()) continue; switch (event.kind) { .SystemBoot, .SystemShutdown => s.boot_events += 1, .FiberSpawn, .FiberTerminate => s.fiber_events += 1, .CapabilityGrant, .CapabilityRevoke, .CapabilityDelegate => s.cap_events += 1, .ChannelOpen, .ChannelClose, .ChannelRead, .ChannelWrite => s.io_events += 1, .MemoryAllocate, .MemoryFree, .MemoryMap => s.mem_events += 1, .NetworkPacketRx, .NetworkPacketTx => s.net_events += 1, .AccessDenied, .PolicyViolation => s.security_events += 1, else => {}, } } stats.* = s; } /// Binary Header for STL Export pub const STLHeader = extern struct { magic: u32 = 0x53544C21, // "STL!" version: u16 = 1, event_count: u16, event_size: u8 = @sizeOf(Event), _reserved: [23]u8 = [_]u8{0} ** 23, // Pad to 32 bytes for alignment }; /// Export all events as a contiguous binary blob (C ABI) /// Returns number of bytes written pub export fn stl_export_binary(dest: [*]u8, max_size: usize) usize { if (!stl_initialized) return 0; const count = global_stl.count(); const required_size = @sizeOf(STLHeader) + (count * @sizeOf(Event)); if (max_size < required_size) return 0; var ptr = dest; // Write Header const header = STLHeader{ .event_count = @as(u16, @intCast(count)), }; @memcpy(ptr, @as([*]const u8, @ptrCast(&header))[0..@sizeOf(STLHeader)]); ptr += @sizeOf(STLHeader); // Write Events (in chronological order from tail to head) var idx = global_stl.tail; while (idx != global_stl.head) : (idx = (idx + 1) % STL_SIZE) { const event = &global_stl.events[idx]; if (event.is_null()) continue; @memcpy(ptr, @as([*]const u8, @ptrCast(event))[0..@sizeOf(Event)]); ptr += @sizeOf(Event); } return required_size; } // Unit tests test "Event creation and validation" { const event = Event{ .kind = .FiberSpawn, ._reserved = 0, .timestamp_ns = 1000, .fiber_id = 42, .entity_id = 0x1234, .cause_id = 0, .data0 = 0, .data1 = 0, .data2 = 0, }; try std.testing.expect(!event.is_null()); try std.testing.expect(event.kind == .FiberSpawn); try std.testing.expect(event.fiber_id == 42); } test "STL operations" { var stl = SystemTruthLedger.init(); const event1 = Event{ .kind = .SystemBoot, ._reserved = 0, .timestamp_ns = 1000, .fiber_id = 0, .entity_id = 0, .cause_id = 0, .data0 = 0, .data1 = 0, .data2 = 0, }; // Append event const id1 = stl.append(event1); try std.testing.expect(id1 == 0); try std.testing.expect(stl.count() == 1); // Lookup event const retrieved = stl.lookup(id1).?; try std.testing.expect(retrieved.kind == .SystemBoot); } test "STL wraparound" { var stl = SystemTruthLedger.init(); // Fill the buffer var i: u32 = 0; while (i < STL_SIZE + 10) : (i += 1) { const event = Event{ .kind = .FiberSpawn, ._reserved = 0, .timestamp_ns = i, .fiber_id = i, .entity_id = 0, .cause_id = 0, .data0 = 0, .data1 = 0, .data2 = 0, }; _ = stl.append(event); } // Should have wrapped try std.testing.expect(stl.epoch > 0); try std.testing.expect(stl.count() == STL_SIZE); } test "STL binary export" { var stl = SystemTruthLedger.init(); _ = stl.append(Event{ .kind = .SystemBoot, ._reserved = 0, .timestamp_ns = 100, .fiber_id = 0, .entity_id = 0, .cause_id = 0, .data0 = 1, .data1 = 2, .data2 = 3, }); // Mock global STL for export test (since export uses global_stl) global_stl = stl; stl_initialized = true; var buf: [512]u8 align(16) = undefined; const written = stl_export_binary(&buf, buf.len); try std.testing.expect(written > @sizeOf(STLHeader)); const header = @as(*const STLHeader, @ptrCast(@alignCast(&buf))).*; try std.testing.expect(header.magic == 0x53544C21); try std.testing.expect(header.event_count == 1); const first_ev = @as(*const Event, @ptrCast(@alignCast(&buf[@sizeOf(STLHeader)]))); try std.testing.expect(first_ev.kind == .SystemBoot); try std.testing.expect(first_ev.data0 == 1); }