const std = @import("std");const permission_request = @import("../permissions/permission_request.zig");const permissions = @import("../permissions/permissions.zig");const session_permission_state = @import("../permissions/session_permission_state.zig");const types = @import("../shared/types.zig");const domain = @import("domain.zig");const tool_result = @import("tool_result.zig"); const Allocator = std.mem.Allocator; pub const max_deliveries: usize = 256;pub const max_consumers: usize = 16;pub const max_retention_targets: usize = max_deliveries;pub const max_approvals: usize = 64;pub const max_active_work_notifications: usize = 8;pub const max_active_work_notification_bytes: usize = 48 * 1024;pub const max_live_approvals: usize = max_approvals;pub const max_authority_grants: usize = domain.max_admission_items;pub const max_consumer_cursor_bytes: usize = 16 * 1024;pub const max_retention_target_bytes: usize = 64 * 1024;pub const max_retained_delivery_canonical_bytes: usize = 96 * 1024;pub const capacity_contract_version: u64 = 2;pub const max_delivery_content_bytes: usize = domain.max_message_bytes;pub const max_approval_projection_bytes: usize = domain.max_message_bytes + 2 * std.Io.Dir.max_path_bytes + 1024;pub const max_live_approval_bytes: usize = 192 * 1024;pub const max_authority_grant_bytes: usize = 128 * 1024;pub const max_irreducible_canonical_bytes: usize = 224 * 1024;pub const max_trusted_context_bytes: usize = 16 * 1024;pub const max_delivery_page: usize = domain.max_page_limit; pub const DeliveryKind = enum { message, milestone, terminal, interval, approval, tool_activity,}; pub const Projection = enum { human, parent_turn,}; pub const ToolActivityPhase = enum { started, succeeded, failed, denied,}; pub const ToolActivity = struct { tool_name: []u8, phase: ToolActivityPhase,}; pub const DeliveryPayload = union(DeliveryKind) { message: []u8, milestone: []u8, terminal: domain.State, interval: struct { state: domain.State, coalesced_ticks: u32, }, approval: []u8, tool_activity: ToolActivity, pub fn deinit(self: *DeliveryPayload, alloc: Allocator) void { switch (self.*) { .message, .milestone, .approval => |value| alloc.free(value), .tool_activity => |value| alloc.free(value.tool_name), .terminal, .interval => {}, } self.* = undefined; } pub fn clone(self: DeliveryPayload, alloc: Allocator) !DeliveryPayload { return switch (self) { .message => |value| .{ .message = try alloc.dupe(u8, value) }, .milestone => |value| .{ .milestone = try alloc.dupe(u8, value) }, .terminal => |value| .{ .terminal = value }, .interval => |value| .{ .interval = value }, .approval => |value| .{ .approval = try alloc.dupe(u8, value) }, .tool_activity => |value| .{ .tool_activity = .{ .tool_name = try alloc.dupe(u8, value.tool_name), .phase = value.phase, } }, }; }}; /// One immutable, allocator-owned communication envelope. It is manager/// metadata and is never appended to either session transcript.pub const Delivery = struct { sequence: u64, revision: u64, id: []u8, source_id: []u8, target_id: []u8, work_id: ?[]u8 = null, operation_id: ?[]u8 = null, timestamp_ms: i64, payload: DeliveryPayload, pub fn deinit(self: *Delivery, alloc: Allocator) void { alloc.free(self.id); alloc.free(self.source_id); alloc.free(self.target_id); if (self.work_id) |value| alloc.free(value); if (self.operation_id) |value| alloc.free(value); self.payload.deinit(alloc); self.* = undefined; } pub fn clone(self: Delivery, alloc: Allocator) !Delivery { const id = try alloc.dupe(u8, self.id); errdefer alloc.free(id); const source_id = try alloc.dupe(u8, self.source_id); errdefer alloc.free(source_id); const target_id = try alloc.dupe(u8, self.target_id); errdefer alloc.free(target_id); const work_id = if (self.work_id) |value| try alloc.dupe(u8, value) else null; errdefer if (work_id) |value| alloc.free(value); const operation_id = if (self.operation_id) |value| try alloc.dupe(u8, value) else null; errdefer if (operation_id) |value| alloc.free(value); return .{ .sequence = self.sequence, .revision = self.revision, .id = id, .source_id = source_id, .target_id = target_id, .work_id = work_id, .operation_id = operation_id, .timestamp_ms = self.timestamp_ms, .payload = try self.payload.clone(alloc), }; }}; pub const DeliveryInput = struct { id: []const u8, source_id: []const u8, target_id: []const u8, work_id: ?[]const u8 = null, operation_id: ?[]const u8 = null, operation_identity_admitted: bool = false, timestamp_ms: i64, payload: union(DeliveryKind) { message: []const u8, milestone: []const u8, terminal: domain.State, interval: struct { state: domain.State, coalesced_ticks: u32 }, approval: []const u8, tool_activity: struct { tool_name: []const u8, phase: ToolActivityPhase, }, },}; pub const ConsumerCursor = struct { consumer_id: []u8, target_id: []u8, projection: Projection = .human, acknowledged_sequence: u64 = 0, partial_message_sequence: u64 = 0, partial_message_offset: u64 = 0, stale: bool = false, pub fn deinit(self: *ConsumerCursor, alloc: Allocator) void { alloc.free(self.consumer_id); alloc.free(self.target_id); self.* = undefined; } pub fn clone(self: ConsumerCursor, alloc: Allocator) !ConsumerCursor { const consumer_id = try alloc.dupe(u8, self.consumer_id); errdefer alloc.free(consumer_id); return .{ .consumer_id = consumer_id, .target_id = try alloc.dupe(u8, self.target_id), .projection = self.projection, .acknowledged_sequence = self.acknowledged_sequence, .partial_message_sequence = self.partial_message_sequence, .partial_message_offset = self.partial_message_offset, .stale = self.stale, }; }}; /// Bounded target-scoped evidence that a projection has lost retained data./// This also protects consumers whose first read happens after the eviction.pub const RetentionTarget = struct { target_id: []u8, human_evicted_through: u64 = 0, parent_turn_evicted_through: u64 = 0, pub fn deinit(self: *RetentionTarget, alloc: Allocator) void { alloc.free(self.target_id); self.* = undefined; } pub fn clone(self: RetentionTarget, alloc: Allocator) !RetentionTarget { return .{ .target_id = try alloc.dupe(u8, self.target_id), .human_evicted_through = self.human_evicted_through, .parent_turn_evicted_through = self.parent_turn_evicted_through, }; }}; pub const WorkNotification = struct { work_id: []u8, policy: domain.NotificationPolicy, started_at_ms: i64, next_due_ms: ?i64, stopped: bool = false, pub fn deinit(self: *WorkNotification, alloc: Allocator) void { alloc.free(self.work_id); self.policy.deinit(alloc); self.* = undefined; } pub fn clone(self: WorkNotification, alloc: Allocator) !WorkNotification { const work_id = try alloc.dupe(u8, self.work_id); errdefer alloc.free(work_id); return .{ .work_id = work_id, .policy = try self.policy.clone(alloc), .started_at_ms = self.started_at_ms, .next_due_ms = self.next_due_ms, .stopped = self.stopped, }; }}; pub const ApprovalKind = enum { tool, relationship };pub const ApprovalStatus = enum { pending, allowed_once, allowed_always, denied, cancelled, stale, consumed,}; pub const RelationshipApproval = struct { action: domain.RelationshipAction, prospective_parent_id: []u8, operation_id: []u8, pub fn deinit(self: *RelationshipApproval, alloc: Allocator) void { alloc.free(self.prospective_parent_id); alloc.free(self.operation_id); self.* = undefined; } pub fn clone(self: RelationshipApproval, alloc: Allocator) !RelationshipApproval { const prospective_parent_id = try alloc.dupe(u8, self.prospective_parent_id); errdefer alloc.free(prospective_parent_id); return .{ .action = self.action, .prospective_parent_id = prospective_parent_id, .operation_id = try alloc.dupe(u8, self.operation_id), }; }}; /// Durable projection of one canonical prepared request. The executable/// payload is represented by its canonical digest; human surfaces receive only/// the bounded label, explanation, command projection, and optional file review/// exposed by fx.pub const Approval = struct { id: []u8, kind: ApprovalKind, child_id: []u8, root_id: []u8, work_id: ?[]u8, relationship: ?RelationshipApproval = null, prepared_fingerprint: [32]u8, identity_fingerprint: [32]u8 = [_]u8{0} ** 32, label: []u8, explanation: ?[]u8, command: ?[]u8 = null, file: ?permission_request.FileApprovalRequest = null, grants: []types.PermissionGrant, status: ApprovalStatus, created_at_ms: i64, resolved_at_ms: ?i64 = null, resolved_revision: ?u64 = null, pub fn deinit(self: *Approval, alloc: Allocator) void { alloc.free(self.id); alloc.free(self.child_id); alloc.free(self.root_id); if (self.work_id) |value| alloc.free(value); if (self.relationship) |*value| value.deinit(alloc); alloc.free(self.label); if (self.explanation) |value| alloc.free(value); if (self.command) |value| alloc.free(value); if (self.file) |value| { permission_request.deinitFileApprovalRequest(alloc, value); } types.freePermissionGrantSlice(alloc, self.grants); self.* = undefined; } pub fn clone(self: Approval, alloc: Allocator) !Approval { const id = try alloc.dupe(u8, self.id); errdefer alloc.free(id); const child_id = try alloc.dupe(u8, self.child_id); errdefer alloc.free(child_id); const root_id = try alloc.dupe(u8, self.root_id); errdefer alloc.free(root_id); const work_id = if (self.work_id) |value| try alloc.dupe(u8, value) else null; errdefer if (work_id) |value| alloc.free(value); var relationship = if (self.relationship) |value| try value.clone(alloc) else null; errdefer if (relationship) |*value| value.deinit(alloc); const label = try alloc.dupe(u8, self.label); errdefer alloc.free(label); const explanation = if (self.explanation) |value| try alloc.dupe(u8, value) else null; errdefer if (explanation) |value| alloc.free(value); const command = if (self.command) |value| try alloc.dupe(u8, value) else null; errdefer if (command) |value| alloc.free(value); const file = if (self.file) |value| try permission_request.dupeFileApprovalRequest(alloc, value) else null; errdefer if (file) |value| { permission_request.deinitFileApprovalRequest(alloc, value); }; return .{ .id = id, .kind = self.kind, .child_id = child_id, .root_id = root_id, .work_id = work_id, .relationship = relationship, .prepared_fingerprint = self.prepared_fingerprint, .identity_fingerprint = self.identity_fingerprint, .label = label, .explanation = explanation, .command = command, .file = file, .grants = try types.dupePermissionGrantSlice(alloc, self.grants), .status = self.status, .created_at_ms = self.created_at_ms, .resolved_at_ms = self.resolved_at_ms, .resolved_revision = self.resolved_revision, }; }}; pub const Ledger = struct { session_id: []u8, capacity_version: u64 = 0, generation: u64 = 0, next_sequence: u64 = 1, deliveries: []Delivery, cursors: []ConsumerCursor, /// Optional so schema-v1 records written before target-scoped retention /// evidence deserialize without a migration branch. retention_targets: ?[]RetentionTarget = null, work_notifications: []WorkNotification, approvals: []Approval, /// Retained for schema-v1 compatibility. Route-scoped `ConsumerCursor.stale` /// is authoritative for retention loss. parent_turn_evicted_through: u64 = 0, authority_generation: u64 = 0, authority_grants: []types.PermissionGrant, legacy_operation_replay_closed: bool = false, model_replay_floor: u64 = 0, human_replay_floor: u64 = 0, model_epoch_high: u64 = 0, human_epoch_high: u64 = 0, pub fn init(alloc: Allocator, session_id: []const u8) !Ledger { try domain.validateId(session_id); const owned_session_id = try alloc.dupe(u8, session_id); errdefer alloc.free(owned_session_id); const deliveries = try alloc.alloc(Delivery, 0); errdefer alloc.free(deliveries); const cursors = try alloc.alloc(ConsumerCursor, 0); errdefer alloc.free(cursors); const retention_targets = try alloc.alloc(RetentionTarget, 0); errdefer alloc.free(retention_targets); const work_notifications = try alloc.alloc(WorkNotification, 0); errdefer alloc.free(work_notifications); const approvals = try alloc.alloc(Approval, 0); errdefer alloc.free(approvals); return .{ .session_id = owned_session_id, .capacity_version = capacity_contract_version, .deliveries = deliveries, .cursors = cursors, .retention_targets = retention_targets, .work_notifications = work_notifications, .approvals = approvals, .authority_grants = try alloc.alloc(types.PermissionGrant, 0), }; } pub fn deinit(self: *Ledger, alloc: Allocator) void { alloc.free(self.session_id); for (self.deliveries) |*value| value.deinit(alloc); alloc.free(self.deliveries); for (self.cursors) |*value| value.deinit(alloc); alloc.free(self.cursors); if (self.retention_targets) |targets| { for (targets) |*value| value.deinit(alloc); alloc.free(targets); } for (self.work_notifications) |*value| value.deinit(alloc); alloc.free(self.work_notifications); for (self.approvals) |*value| value.deinit(alloc); alloc.free(self.approvals); types.freePermissionGrantSlice(alloc, self.authority_grants); self.* = undefined; } pub fn clone(self: Ledger, alloc: Allocator) !Ledger { const session_id = try alloc.dupe(u8, self.session_id); errdefer alloc.free(session_id); const deliveries = try cloneSlice(Delivery, alloc, self.deliveries); errdefer freeSlice(Delivery, alloc, deliveries); const cursors = try cloneSlice(ConsumerCursor, alloc, self.cursors); errdefer freeSlice(ConsumerCursor, alloc, cursors); const retention_targets = if (self.retention_targets) |targets| try cloneSlice(RetentionTarget, alloc, targets) else null; errdefer if (retention_targets) |targets| freeSlice(RetentionTarget, alloc, targets); const work_notifications = try cloneSlice(WorkNotification, alloc, self.work_notifications); errdefer freeSlice(WorkNotification, alloc, work_notifications); const approvals = try cloneSlice(Approval, alloc, self.approvals); errdefer freeSlice(Approval, alloc, approvals); return .{ .session_id = session_id, .capacity_version = self.capacity_version, .generation = self.generation, .next_sequence = self.next_sequence, .deliveries = deliveries, .cursors = cursors, .retention_targets = retention_targets, .work_notifications = work_notifications, .approvals = approvals, .parent_turn_evicted_through = self.parent_turn_evicted_through, .authority_generation = self.authority_generation, .authority_grants = try types.dupePermissionGrantSlice(alloc, self.authority_grants), .legacy_operation_replay_closed = self.legacy_operation_replay_closed, .model_replay_floor = self.model_replay_floor, .human_replay_floor = self.human_replay_floor, .model_epoch_high = self.model_epoch_high, .human_epoch_high = self.human_epoch_high, }; }}; pub const MutationError = error{ OutOfMemory, InvalidDelivery, GenerationExhausted, SequenceExhausted, TooManyConsumers, TooManyRetentionTargets, InvalidCursor, StaleCursor, InvalidNotification, UndeclaredMilestone, DuplicateMilestone, InvalidApproval, ApprovalConflict, AuthorityExhausted, CapacityExceeded, ReplayExpired,}; pub const ValidationError = error{InvalidLedger}; /// Pure semantic validation for the durable communication boundary.pub fn validateLedger(ledger: Ledger) ValidationError!void { domain.validateId(ledger.session_id) catch return error.InvalidLedger; if (ledger.capacity_version > capacity_contract_version) { return error.InvalidLedger; } const retention_target_count = if (ledger.retention_targets) |targets| targets.len else 0; if (ledger.next_sequence == 0 or ledger.deliveries.len > max_deliveries or ledger.cursors.len > max_consumers or retention_target_count > max_retention_targets or ledger.approvals.len > max_approvals) { return error.InvalidLedger;Lines 1–500 of 5997. Load more
const std = @import("std"); const permission_request = @import("../permissions/permission_request.zig"); const permissions = @import("../permissions/permissions.zig"); const session_permission_state = @import("../permissions/session_permission_state.zig"); const types = @import("../shared/types.zig"); const domain = @import("domain.zig"); const tool_result = @import("tool_result.zig"); const Allocator = std.mem.Allocator; pub const max_deliveries: usize = 256; pub const max_consumers: usize = 16; pub const max_retention_targets: usize = max_deliveries; pub const max_approvals: usize = 64; pub const max_active_work_notifications: usize = 8; pub const max_active_work_notification_bytes: usize = 48 * 1024; pub const max_live_approvals: usize = max_approvals; pub const max_authority_grants: usize = domain.max_admission_items; pub const max_consumer_cursor_bytes: usize = 16 * 1024; pub const max_retention_target_bytes: usize = 64 * 1024; pub const max_retained_delivery_canonical_bytes: usize = 96 * 1024; pub const capacity_contract_version: u64 = 2; pub const max_delivery_content_bytes: usize = domain.max_message_bytes; pub const max_approval_projection_bytes: usize = domain.max_message_bytes + 2 * std.Io.Dir.max_path_bytes + 1024; pub const max_live_approval_bytes: usize = 192 * 1024; pub const max_authority_grant_bytes: usize = 128 * 1024; pub const max_irreducible_canonical_bytes: usize = 224 * 1024; pub const max_trusted_context_bytes: usize = 16 * 1024; pub const max_delivery_page: usize = domain.max_page_limit; pub const DeliveryKind = enum { message, milestone, terminal, interval, approval, tool_activity, }; pub const Projection = enum { human, parent_turn, }; pub const ToolActivityPhase = enum { started, succeeded, failed, denied, }; pub const ToolActivity = struct { tool_name: []u8, phase: ToolActivityPhase, }; pub const DeliveryPayload = union(DeliveryKind) { message: []u8, milestone: []u8, terminal: domain.State, interval: struct { state: domain.State, coalesced_ticks: u32, }, approval: []u8, tool_activity: ToolActivity, pub fn deinit(self: *DeliveryPayload, alloc: Allocator) void { switch (self.*) { .message, .milestone, .approval => |value| alloc.free(value), .tool_activity => |value| alloc.free(value.tool_name), .terminal, .interval => {}, } self.* = undefined; } pub fn clone(self: DeliveryPayload, alloc: Allocator) !DeliveryPayload { return switch (self) { .message => |value| .{ .message = try alloc.dupe(u8, value) }, .milestone => |value| .{ .milestone = try alloc.dupe(u8, value) }, .terminal => |value| .{ .terminal = value }, .interval => |value| .{ .interval = value }, .approval => |value| .{ .approval = try alloc.dupe(u8, value) }, .tool_activity => |value| .{ .tool_activity = .{ .tool_name = try alloc.dupe(u8, value.tool_name), .phase = value.phase, } }, }; } }; /// One immutable, allocator-owned communication envelope. It is manager /// metadata and is never appended to either session transcript. pub const Delivery = struct { sequence: u64, revision: u64, id: []u8, source_id: []u8, target_id: []u8, work_id: ?[]u8 = null, operation_id: ?[]u8 = null, timestamp_ms: i64, payload: DeliveryPayload, pub fn deinit(self: *Delivery, alloc: Allocator) void { alloc.free(self.id); alloc.free(self.source_id); alloc.free(self.target_id); if (self.work_id) |value| alloc.free(value); if (self.operation_id) |value| alloc.free(value); self.payload.deinit(alloc); self.* = undefined; } pub fn clone(self: Delivery, alloc: Allocator) !Delivery { const id = try alloc.dupe(u8, self.id); errdefer alloc.free(id); const source_id = try alloc.dupe(u8, self.source_id); errdefer alloc.free(source_id); const target_id = try alloc.dupe(u8, self.target_id); errdefer alloc.free(target_id); const work_id = if (self.work_id) |value| try alloc.dupe(u8, value) else null; errdefer if (work_id) |value| alloc.free(value); const operation_id = if (self.operation_id) |value| try alloc.dupe(u8, value) else null; errdefer if (operation_id) |value| alloc.free(value); return .{ .sequence = self.sequence, .revision = self.revision, .id = id, .source_id = source_id, .target_id = target_id, .work_id = work_id, .operation_id = operation_id, .timestamp_ms = self.timestamp_ms, .payload = try self.payload.clone(alloc), }; } }; pub const DeliveryInput = struct { id: []const u8, source_id: []const u8, target_id: []const u8, work_id: ?[]const u8 = null, operation_id: ?[]const u8 = null, operation_identity_admitted: bool = false, timestamp_ms: i64, payload: union(DeliveryKind) { message: []const u8, milestone: []const u8, terminal: domain.State, interval: struct { state: domain.State, coalesced_ticks: u32 }, approval: []const u8, tool_activity: struct { tool_name: []const u8, phase: ToolActivityPhase, }, }, }; pub const ConsumerCursor = struct { consumer_id: []u8, target_id: []u8, projection: Projection = .human, acknowledged_sequence: u64 = 0, partial_message_sequence: u64 = 0, partial_message_offset: u64 = 0, stale: bool = false, pub fn deinit(self: *ConsumerCursor, alloc: Allocator) void { alloc.free(self.consumer_id); alloc.free(self.target_id); self.* = undefined; } pub fn clone(self: ConsumerCursor, alloc: Allocator) !ConsumerCursor { const consumer_id = try alloc.dupe(u8, self.consumer_id); errdefer alloc.free(consumer_id); return .{ .consumer_id = consumer_id, .target_id = try alloc.dupe(u8, self.target_id), .projection = self.projection, .acknowledged_sequence = self.acknowledged_sequence, .partial_message_sequence = self.partial_message_sequence, .partial_message_offset = self.partial_message_offset, .stale = self.stale, }; } }; /// Bounded target-scoped evidence that a projection has lost retained data. /// This also protects consumers whose first read happens after the eviction. pub const RetentionTarget = struct { target_id: []u8, human_evicted_through: u64 = 0, parent_turn_evicted_through: u64 = 0, pub fn deinit(self: *RetentionTarget, alloc: Allocator) void { alloc.free(self.target_id); self.* = undefined; } pub fn clone(self: RetentionTarget, alloc: Allocator) !RetentionTarget { return .{ .target_id = try alloc.dupe(u8, self.target_id), .human_evicted_through = self.human_evicted_through, .parent_turn_evicted_through = self.parent_turn_evicted_through, }; } }; pub const WorkNotification = struct { work_id: []u8, policy: domain.NotificationPolicy, started_at_ms: i64, next_due_ms: ?i64, stopped: bool = false, pub fn deinit(self: *WorkNotification, alloc: Allocator) void { alloc.free(self.work_id); self.policy.deinit(alloc); self.* = undefined; } pub fn clone(self: WorkNotification, alloc: Allocator) !WorkNotification { const work_id = try alloc.dupe(u8, self.work_id); errdefer alloc.free(work_id); return .{ .work_id = work_id, .policy = try self.policy.clone(alloc), .started_at_ms = self.started_at_ms, .next_due_ms = self.next_due_ms, .stopped = self.stopped, }; } }; pub const ApprovalKind = enum { tool, relationship }; pub const ApprovalStatus = enum { pending, allowed_once, allowed_always, denied, cancelled, stale, consumed, }; pub const RelationshipApproval = struct { action: domain.RelationshipAction, prospective_parent_id: []u8, operation_id: []u8, pub fn deinit(self: *RelationshipApproval, alloc: Allocator) void { alloc.free(self.prospective_parent_id); alloc.free(self.operation_id); self.* = undefined; } pub fn clone(self: RelationshipApproval, alloc: Allocator) !RelationshipApproval { const prospective_parent_id = try alloc.dupe(u8, self.prospective_parent_id); errdefer alloc.free(prospective_parent_id); return .{ .action = self.action, .prospective_parent_id = prospective_parent_id, .operation_id = try alloc.dupe(u8, self.operation_id), }; } }; /// Durable projection of one canonical prepared request. The executable /// payload is represented by its canonical digest; human surfaces receive only /// the bounded label, explanation, command projection, and optional file review /// exposed by fx. pub const Approval = struct { id: []u8, kind: ApprovalKind, child_id: []u8, root_id: []u8, work_id: ?[]u8, relationship: ?RelationshipApproval = null, prepared_fingerprint: [32]u8, identity_fingerprint: [32]u8 = [_]u8{0} ** 32, label: []u8, explanation: ?[]u8, command: ?[]u8 = null, file: ?permission_request.FileApprovalRequest = null, grants: []types.PermissionGrant, status: ApprovalStatus, created_at_ms: i64, resolved_at_ms: ?i64 = null, resolved_revision: ?u64 = null, pub fn deinit(self: *Approval, alloc: Allocator) void { alloc.free(self.id); alloc.free(self.child_id); alloc.free(self.root_id); if (self.work_id) |value| alloc.free(value); if (self.relationship) |*value| value.deinit(alloc); alloc.free(self.label); if (self.explanation) |value| alloc.free(value); if (self.command) |value| alloc.free(value); if (self.file) |value| { permission_request.deinitFileApprovalRequest(alloc, value); } types.freePermissionGrantSlice(alloc, self.grants); self.* = undefined; } pub fn clone(self: Approval, alloc: Allocator) !Approval { const id = try alloc.dupe(u8, self.id); errdefer alloc.free(id); const child_id = try alloc.dupe(u8, self.child_id); errdefer alloc.free(child_id); const root_id = try alloc.dupe(u8, self.root_id); errdefer alloc.free(root_id); const work_id = if (self.work_id) |value| try alloc.dupe(u8, value) else null; errdefer if (work_id) |value| alloc.free(value); var relationship = if (self.relationship) |value| try value.clone(alloc) else null; errdefer if (relationship) |*value| value.deinit(alloc); const label = try alloc.dupe(u8, self.label); errdefer alloc.free(label); const explanation = if (self.explanation) |value| try alloc.dupe(u8, value) else null; errdefer if (explanation) |value| alloc.free(value); const command = if (self.command) |value| try alloc.dupe(u8, value) else null; errdefer if (command) |value| alloc.free(value); const file = if (self.file) |value| try permission_request.dupeFileApprovalRequest(alloc, value) else null; errdefer if (file) |value| { permission_request.deinitFileApprovalRequest(alloc, value); }; return .{ .id = id, .kind = self.kind, .child_id = child_id, .root_id = root_id, .work_id = work_id, .relationship = relationship, .prepared_fingerprint = self.prepared_fingerprint, .identity_fingerprint = self.identity_fingerprint, .label = label, .explanation = explanation, .command = command, .file = file, .grants = try types.dupePermissionGrantSlice(alloc, self.grants), .status = self.status, .created_at_ms = self.created_at_ms, .resolved_at_ms = self.resolved_at_ms, .resolved_revision = self.resolved_revision, }; } }; pub const Ledger = struct { session_id: []u8, capacity_version: u64 = 0, generation: u64 = 0, next_sequence: u64 = 1, deliveries: []Delivery, cursors: []ConsumerCursor, /// Optional so schema-v1 records written before target-scoped retention /// evidence deserialize without a migration branch. retention_targets: ?[]RetentionTarget = null, work_notifications: []WorkNotification, approvals: []Approval, /// Retained for schema-v1 compatibility. Route-scoped `ConsumerCursor.stale` /// is authoritative for retention loss. parent_turn_evicted_through: u64 = 0, authority_generation: u64 = 0, authority_grants: []types.PermissionGrant, legacy_operation_replay_closed: bool = false, model_replay_floor: u64 = 0, human_replay_floor: u64 = 0, model_epoch_high: u64 = 0, human_epoch_high: u64 = 0, pub fn init(alloc: Allocator, session_id: []const u8) !Ledger { try domain.validateId(session_id); const owned_session_id = try alloc.dupe(u8, session_id); errdefer alloc.free(owned_session_id); const deliveries = try alloc.alloc(Delivery, 0); errdefer alloc.free(deliveries); const cursors = try alloc.alloc(ConsumerCursor, 0); errdefer alloc.free(cursors); const retention_targets = try alloc.alloc(RetentionTarget, 0); errdefer alloc.free(retention_targets); const work_notifications = try alloc.alloc(WorkNotification, 0); errdefer alloc.free(work_notifications); const approvals = try alloc.alloc(Approval, 0); errdefer alloc.free(approvals); return .{ .session_id = owned_session_id, .capacity_version = capacity_contract_version, .deliveries = deliveries, .cursors = cursors, .retention_targets = retention_targets, .work_notifications = work_notifications, .approvals = approvals, .authority_grants = try alloc.alloc(types.PermissionGrant, 0), }; } pub fn deinit(self: *Ledger, alloc: Allocator) void { alloc.free(self.session_id); for (self.deliveries) |*value| value.deinit(alloc); alloc.free(self.deliveries); for (self.cursors) |*value| value.deinit(alloc); alloc.free(self.cursors); if (self.retention_targets) |targets| { for (targets) |*value| value.deinit(alloc); alloc.free(targets); } for (self.work_notifications) |*value| value.deinit(alloc); alloc.free(self.work_notifications); for (self.approvals) |*value| value.deinit(alloc); alloc.free(self.approvals); types.freePermissionGrantSlice(alloc, self.authority_grants); self.* = undefined; } pub fn clone(self: Ledger, alloc: Allocator) !Ledger { const session_id = try alloc.dupe(u8, self.session_id); errdefer alloc.free(session_id); const deliveries = try cloneSlice(Delivery, alloc, self.deliveries); errdefer freeSlice(Delivery, alloc, deliveries); const cursors = try cloneSlice(ConsumerCursor, alloc, self.cursors); errdefer freeSlice(ConsumerCursor, alloc, cursors); const retention_targets = if (self.retention_targets) |targets| try cloneSlice(RetentionTarget, alloc, targets) else null; errdefer if (retention_targets) |targets| freeSlice(RetentionTarget, alloc, targets); const work_notifications = try cloneSlice(WorkNotification, alloc, self.work_notifications); errdefer freeSlice(WorkNotification, alloc, work_notifications); const approvals = try cloneSlice(Approval, alloc, self.approvals); errdefer freeSlice(Approval, alloc, approvals); return .{ .session_id = session_id, .capacity_version = self.capacity_version, .generation = self.generation, .next_sequence = self.next_sequence, .deliveries = deliveries, .cursors = cursors, .retention_targets = retention_targets, .work_notifications = work_notifications, .approvals = approvals, .parent_turn_evicted_through = self.parent_turn_evicted_through, .authority_generation = self.authority_generation, .authority_grants = try types.dupePermissionGrantSlice(alloc, self.authority_grants), .legacy_operation_replay_closed = self.legacy_operation_replay_closed, .model_replay_floor = self.model_replay_floor, .human_replay_floor = self.human_replay_floor, .model_epoch_high = self.model_epoch_high, .human_epoch_high = self.human_epoch_high, }; } }; pub const MutationError = error{ OutOfMemory, InvalidDelivery, GenerationExhausted, SequenceExhausted, TooManyConsumers, TooManyRetentionTargets, InvalidCursor, StaleCursor, InvalidNotification, UndeclaredMilestone, DuplicateMilestone, InvalidApproval, ApprovalConflict, AuthorityExhausted, CapacityExceeded, ReplayExpired, }; pub const ValidationError = error{InvalidLedger}; /// Pure semantic validation for the durable communication boundary. pub fn validateLedger(ledger: Ledger) ValidationError!void { domain.validateId(ledger.session_id) catch return error.InvalidLedger; if (ledger.capacity_version > capacity_contract_version) { return error.InvalidLedger; } const retention_target_count = if (ledger.retention_targets) |targets| targets.len else 0; if (ledger.next_sequence == 0 or ledger.deliveries.len > max_deliveries or ledger.cursors.len > max_consumers or retention_target_count > max_retention_targets or ledger.approvals.len > max_approvals) { return error.InvalidLedger;