rumpk/hal/ontology.zig

421 lines
11 KiB
Zig

//! 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.head >= self.tail) {
return self.head - self.tail;
} else {
return (STL_SIZE - self.tail) + 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;
}
// 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);
}