tiny.machine.explore
Defined in tiny.machine.
Some bugs show up only under one particular order of inputs, scheduling, and faults, so finding one means running a deterministic machine many times with different choices, checking each run against rules written beforehand, and keeping every bound fixed in advance.
API (109)
Actions
Public operations.
CapsuleCapsuleIdentity.hexCapsuleReducerChoiceSiteEventSequenceFaultGeneratorGenerationInputGeneratorScheduleGeneratorSearch: Returns a search type that keeps two arrays for each pair of capacities, so the whole search fits storage fixed at compile time, and a failing run can be walked back to the root through its parents.SearchDecision.validSearchRunner.runSeed.digestSeed.fromU64Seed.suggestSeed.wordTopologyGeneratorevaluateLivenessevaluateSafetyfaultOriginmatchesEventorigin
Types and contracts
Public types and contracts.
BoundBoundedLivenessCapsuleBuildIdentityCapsuleCapacityCapsuleErrorCapsuleExternalResultCapsuleFrameCapsuleIdentityCapsulePreparationCapsuleRejectionCapsuleRejectionReasonCapsuleReplayCapsuleRunnerCapsuleStatisticsDiagnosticEvaluationPatternEvaluationReasonEventEventCapacityEventClassEventValueExternalAdmissionFaultActionFaultAlternativeFaultCapacityFaultPlanGeneratedFaultGeneratedInputGeneratedScheduleGeneratedTopologyGeneratorErrorIncompleteReasonInputAlternativeInputCapacityInputKindInputPhaseInputValueObservationOperationOperationOutcomeOperationPatternOriginPatternPropertyDeclarationPropertyEvaluationPropertyKindSafetyScheduleAlternativeScheduleCapacityScheduleKindSchedulePlanScheduleValueSearchBranchSearchBranchIdSearchBudgetSearchCandidateSearchCapacitySearchChoiceSearchDecisionSearchErrorSearchExecutionSearchExecutionIncompleteSearchExhaustedReasonSearchExpansionSearchIncompleteReasonSearchOutcomeSearchProgressSearchPrunedReasonSearchRunnerSearchSettlementSeedSeedDialectSemanticIdSiteIdStreamTemporalPlanTopologyAlternativeTopologyCapacityTopologyKindTopologyNodeTopologyPlanTopologyValueTraceCompletionTraceStateVerdict
Namespaces
Public namespaces.
distributed: A tool that searches for bugs earns trust by finding a real one in a running system and replaying it on demand.query: Once a run fails, the next questions are which choice, message, or fault led there, and where the failing run first parted from one that passed.
Source
Source: lib/machine/src/explore/capsule/types.zig:20
pub const BuildIdentity = struct { node: fabric.NodeId, execution: instance.ExecutionFingerprint,};Source: lib/machine/src/explore/capsule/types.zig:7
pub const Capacity = struct { frames: u16, builds: u8,};Source: lib/machine/src/explore/capsule/types.zig:25
pub const ExternalResult = struct { frame: u16, origin: explore.Origin, outcome: explore.OperationOutcome,};Source: lib/machine/src/explore/capsule/types.zig:31
pub const Frame = struct { decision: explore.SearchDecision, expected: world.Moment,};Source: lib/machine/src/explore/capsule/types.zig:12
pub const Identity = struct { digest: [32]u8, pub fn hex(self: Identity) [64]u8 { return @import("std").fmt.bytesToHex(self.digest, .lower); }};Source: lib/machine/src/explore/capsule/types.zig:36
pub const Preparation = union(enum) { ready: world.Moment, incomplete: explore.SearchExecutionIncomplete,};Source: lib/machine/src/explore/capsule/types.zig:63
pub const Rejection = struct { frame: u16, reason: RejectionReason, incomplete: ?explore.SearchExecutionIncomplete = null,};Source: lib/machine/src/explore/capsule/types.zig:53
pub const RejectionReason = enum(u8) { preparation_incomplete = 1, start_mismatch = 2, step_incomplete = 3, moment_mismatch = 4, unexpected_failure = 5, expected_failure_missing = 6, failure_mismatch = 7,};Source: lib/machine/src/explore/capsule/types.zig:69
pub const Replay = union(enum) { reproduced: explore.PropertyEvaluation, rejected: Rejection,};Source: lib/machine/src/explore/capsule/types.zig:41
pub const Runner = struct { context: *anyopaque, prepare: *const fn ( *anyopaque, profile.Profile, world.Moment, []const BuildIdentity, []const ExternalResult, ) Preparation, step: explore.SearchRunner,};Source: lib/machine/src/explore/capsule/types.zig:74
pub const Statistics = struct { attempts: u32, accepted: u16, rejected: u32,};Source: lib/machine/src/explore/model.zig:294
pub const Bound = union(enum) { steps: u16, virtual_time: u64,};Source: lib/machine/src/explore/model.zig:411
pub const BoundedLiveness = struct { property: SemanticId, trigger: Pattern, response: Pattern, bound: Bound,};Source: lib/machine/src/explore/model.zig:340
pub const Diagnostic = struct { id: SemanticId, code: u32,};Source: lib/machine/src/explore/model.zig:387
pub const EvaluationPattern = struct { id: SemanticId, verdict: ?Verdict = null,};Source: lib/machine/src/explore/model.zig:306
pub const EvaluationReason = enum(u8) { satisfied = 1, forbidden_event = 2, response_missing = 3, bound_exceeded = 4, trigger_not_seen = 5, trace_incomplete = 6, declaration_missing = 7, declaration_mismatch = 8,};Source: lib/machine/src/explore/model.zig:363
pub const Event = struct { sequence: u16, virtual_time_tick: u64, value: EventValue,};Source: lib/machine/src/explore/model.zig:176
pub const EventCapacity = struct { events: u16,};Source: lib/machine/src/explore/model.zig:369
pub const EventClass = enum(u8) { controlled_input = 1, schedule_choice = 2, topology_choice = 3, observation = 4, property_declaration = 5, property_evaluation = 6, injected_fault = 7, diagnostic = 8, external_admission = 9, operation = 10,};Source: lib/machine/src/explore/model.zig:350
pub const EventValue = union(enum) { controlled_input: GeneratedInput, schedule_choice: GeneratedSchedule, topology_choice: GeneratedTopology, observation: Observation, property_declaration: PropertyDeclaration, property_evaluation: PropertyEvaluation, injected_fault: GeneratedFault, diagnostic: Diagnostic, external_admission: ExternalAdmission, operation: Operation,};Source: lib/machine/src/explore/model.zig:345
pub const ExternalAdmission = struct { id: SemanticId, origin: Origin,};Source: lib/machine/src/explore/model.zig:258
pub const FaultAction = union(enum) { healthy, inject: fault.Kind, persist: fault.Kind, recover: fault.Kind,};Source: lib/machine/src/explore/model.zig:265
pub const FaultAlternative = struct { choice_origin: Origin, effect_origin: ?Origin, virtual_time_tick: u64, action: FaultAction,};Source: lib/machine/src/explore/model.zig:165
pub const FaultCapacity = struct { steps: u16, kinds: u8, alternatives: u8,};Source: lib/machine/src/explore/model.zig:171
pub const FaultPlan = struct { temporal: TemporalPlan, kinds: []const fault.Kind,};Source: lib/machine/src/explore/model.zig:98
pub const IncompleteReason = enum(u8) { step_capacity = 1, alternative_capacity = 2, node_capacity = 3, kind_capacity = 4, event_capacity = 5, virtual_time_bound = 6,};Source: lib/machine/src/explore/model.zig:209
pub const InputAlternative = struct { origin: Origin, virtual_time_tick: u64, value: InputValue,};Source: lib/machine/src/explore/model.zig:138
pub const InputCapacity = struct { steps: u16, alternatives: u8,};Source: lib/machine/src/explore/model.zig:191
pub const InputKind = enum(u8) { wait = 1, terminal = 2, entropy = 3, packet = 4, service_result = 5, effect_result = 6,};Source: lib/machine/src/explore/model.zig:186
pub const InputPhase = enum(u8) { quiet = 1, burst = 2,};Source: lib/machine/src/explore/model.zig:200
pub const InputValue = union(InputKind) { wait: u64, terminal: u64, entropy: u64, packet: u64, service_result: OperationOutcome, effect_result: OperationOutcome,};Source: lib/machine/src/explore/model.zig:330
pub const Observation = struct { id: SemanticId, value: u64,};Source: lib/machine/src/explore/model.zig:335
pub const Operation = struct { id: SemanticId, outcome: OperationOutcome,};Source: lib/machine/src/explore/model.zig:180
pub const OperationOutcome = enum(u8) { succeeded = 1, failed = 2, uncertain = 3,};Source: lib/machine/src/explore/model.zig:382
pub const OperationPattern = struct { id: SemanticId, outcome: ?OperationOutcome = null,};Source: lib/machine/src/explore/model.zig:86
pub const Origin = struct { source: profile.DeterminismSource, version: u16,};Source: lib/machine/src/explore/model.zig:392
pub const Pattern = union(enum) { event_class: EventClass, input: InputKind, schedule: ScheduleKind, topology: TopologyKind, observation: SemanticId, property_declaration: SemanticId, property_evaluation: EvaluationPattern, fault: fault.Kind, diagnostic: SemanticId, external_admission: profile.DeterminismSource, operation: OperationPattern,};Source: lib/machine/src/explore/model.zig:317
pub const PropertyDeclaration = struct { id: SemanticId, kind: PropertyKind, bound: ?Bound,};Source: lib/machine/src/explore/model.zig:323
pub const PropertyEvaluation = struct { id: SemanticId, verdict: Verdict, reason: EvaluationReason, witness: ?u16,};Source: lib/machine/src/explore/model.zig:289
pub const PropertyKind = enum(u8) { safety = 1, bounded_liveness = 2,};Source: lib/machine/src/explore/model.zig:406
pub const Safety = struct { property: SemanticId, forbidden: Pattern,};Source: lib/machine/src/explore/model.zig:227
pub const ScheduleAlternative = struct { origin: Origin, virtual_time_tick: u64, value: ScheduleValue,};Source: lib/machine/src/explore/model.zig:143
pub const ScheduleCapacity = struct { steps: u16, nodes: u8, alternatives: u8,};Source: lib/machine/src/explore/model.zig:217
pub const ScheduleKind = enum(u8) { turn = 1, idle = 2,};Source: lib/machine/src/explore/model.zig:149
pub const SchedulePlan = struct { temporal: TemporalPlan, nodes: u8,};Source: lib/machine/src/explore/model.zig:222
pub const ScheduleValue = union(ScheduleKind) { turn: u8, idle,};Source: lib/machine/src/explore/model.zig:26
pub const Seed = struct { dialect: SeedDialect = .sha256_v1, bytes: [32]u8, pub fn fromU64(value: u64) Seed { var bytes: [32]u8 = @splat(0); std.mem.writeInt(u64, bytes[0..8], value, .little); return .{ .bytes = bytes }; } pub fn digest( self: Seed, stream: Stream, sequence: u16, lane: u8, ) [Sha256.digest_length]u8 { var hasher = Sha256.init(.{}); hasher.update("tiny.machine.explore.seed/v1"); hasher.update(&.{@backingInt(self.dialect)}); hasher.update(&self.bytes); hasher.update(&.{@backingInt(stream)}); var encoded: [3]u8 = undefined; std.mem.writeInt(u16, encoded[0..2], sequence, .little); encoded[2] = lane; hasher.update(&encoded); var result: [Sha256.digest_length]u8 = undefined; hasher.final(&result); return result; } pub fn word( self: Seed, stream: Stream, sequence: u16, lane: u8, ) u64 { const bytes = self.digest(stream, sequence, lane); return std.mem.readInt(u64, bytes[0..8], .little); } pub fn suggest( self: Seed, stream: Stream, sequence: u16, alternative_count: u8, ) u8 { std.debug.assert(alternative_count > 0); const count: u64 = alternative_count; const selected = self.word(stream, sequence, 0) % count; std.debug.assert(selected < alternative_count); return @intCast(selected); }};Source: lib/machine/src/explore/model.zig:22
pub const SeedDialect = enum(u8) { sha256_v1 = 1,};Source: lib/machine/src/explore/model.zig:80
pub const SiteId = struct { stream: Stream, sequence: u16, virtual_time_tick: u64,};Source: lib/machine/src/explore/model.zig:15
pub const Stream = enum(u8) { input = 1, schedule = 2, topology = 3, fault = 4,};Source: lib/machine/src/explore/model.zig:132
pub const TemporalPlan = struct { steps: u16, start_tick: u64, end_tick: u64,};Source: lib/machine/src/explore/model.zig:250
pub const TopologyAlternative = struct { origin: Origin, virtual_time_tick: u64, value: TopologyValue,};Source: lib/machine/src/explore/model.zig:154
pub const TopologyCapacity = struct { steps: u16, nodes: u8, alternatives: u8,};Source: lib/machine/src/explore/model.zig:235
pub const TopologyKind = enum(u8) { node = 1, complete = 2,};Source: lib/machine/src/explore/model.zig:240
Source: lib/machine/src/explore/model.zig:160
pub const TopologyPlan = struct { temporal: TemporalPlan, nodes: u8,};Source: lib/machine/src/explore/model.zig:245
pub const TopologyValue = union(TopologyKind) { node: TopologyNode, complete,};Source: lib/machine/src/explore/model.zig:424
pub const TraceCompletion = enum(u8) { exhausted = 1, incomplete = 2,};Source: lib/machine/src/explore/model.zig:418
pub const TraceState = enum(u8) { open = 1, exhausted = 2, incomplete = 3,};Source: lib/machine/src/explore/model.zig:299
pub const Verdict = enum(u8) { holds = 1, violated = 2, unreached = 3, incomplete = 4,};Source: lib/machine/src/explore/search/types.zig:43
pub const Branch = struct { root: world.Moment, parent: ?BranchId = null, decision: ?Decision = null, depth: u16 = 0, expanded: bool = false, settlement: Settlement = .origin,};Source: lib/machine/src/explore/search/types.zig:12
pub const Budget = struct { executions: u32, depth: u16,};Source: lib/machine/src/explore/search/types.zig:52
Source: lib/machine/src/explore/search/types.zig:7
pub const Capacity = struct { frontier: u16, retained: u16,};Source: lib/machine/src/explore/search/types.zig:17
pub const Choice = union(explore.Stream) { input: explore.GeneratedInput, schedule: explore.GeneratedSchedule, topology: explore.GeneratedTopology, fault: explore.GeneratedFault,};Source: lib/machine/src/explore/search/types.zig:24
pub const Decision = struct { site: explore.SiteId, alternative: u8, alternative_count: u8, choice: Choice, pub fn valid(self: Decision) bool { return self.alternative_count > 0 and self.alternative < self.alternative_count and self.site.stream == std.meta.activeTag(self.choice); }};Source: lib/machine/src/explore/search/types.zig:70
pub const Execution = union(enum) { completed: world.Moment, failed: FailedExecution, incomplete: ExecutionIncomplete,};Source: lib/machine/src/explore/search/types.zig:57
pub const ExecutionIncomplete = enum(u8) { replay_rejected = 1, replay_invalidated = 2, trace_incomplete = 3, invalid_moment = 4, invalid_failure = 5,};Source: lib/machine/src/explore/search/types.zig:95
pub const ExhaustedReason = enum(u8) { frontier_empty = 1, execution_budget = 2, depth_budget = 3,};Source: lib/machine/src/explore/search/types.zig:151
pub const Expansion = union(enum) { expanded: Expanded, exhausted: Exhausted, pruned: Pruned,};Source: lib/machine/src/explore/search/types.zig:126
pub const IncompleteReason = union(enum) { interrupted, retained_capacity, execution: ExecutionIncomplete, generation: explore.IncompleteReason,};Source: lib/machine/src/explore/search/types.zig:138
pub const Outcome = union(enum) { completed: Completed, exhausted: Exhausted, pruned: Pruned, failed: Failed, incomplete: Incomplete,};Source: lib/machine/src/explore/search/types.zig:157
pub const Progress = struct { executions: u32, retained: u16, frontier: u16, pruned: u64,};Source: lib/machine/src/explore/search/types.zig:107
pub const PrunedReason = enum(u8) { strategy = 1, frontier_capacity = 2,};Source: lib/machine/src/explore/search/types.zig:76
pub const Runner = struct { context: *anyopaque, execute: *const fn (*anyopaque, world.Moment, Decision) Execution, pub fn run( self: Runner, parent: world.Moment, decision: Decision, ) Execution { return self.execute(self.context, parent, decision); }};Source: lib/machine/src/explore/search/types.zig:37
pub const Settlement = union(enum) { origin, completed, failed: explore.PropertyEvaluation,};Source: lib/machine/src/explore/capsule/owner.zig:8
pub fn Capsule(comptime capacity_value: types.Capacity) type { requireCapacity(capacity_value); return struct { selected_profile: profile.Profile, start: world.Moment, expected_failure: explore.PropertyEvaluation, builds_storage: [capacity.builds]types.BuildIdentity, frames_storage: [capacity.frames]types.Frame, external_storage: [capacity.frames]types.ExternalResult, build_count: u8, frame_count: u16, external_count: u16, const Self = @This(); pub const capacity: types.Capacity = capacity_value; pub const Error = types.Error; pub const Wire = canon.Wire(capacity); pub const Capture = union(enum) { published: Self, rejected: types.Rejection, }; pub fn capture( search: anytype, failure: anytype, selected_profile: profile.Profile, build_identities: []const types.BuildIdentity, runner: types.Runner, ) Error!Capture { if (build_identities.len > capacity.builds) { return error.BuildCapacityExceeded; } const failed = try search.branch(failure.branch); try validateFailureBranch(failed, failure); if (failed.depth > capacity.frames) { return error.FrameCapacityExceeded; } var result: Self = undefined; result.selected_profile = selected_profile; result.start = (try search.branch(0)).root; result.expected_failure = failure.evaluation; result.build_count = @intCast(build_identities.len); result.frame_count = failed.depth; @memcpy( result.builds_storage[0..build_identities.len], build_identities, ); try result.copyHistory(search, failure.branch); return result.publishOwned(runner); } pub fn publish( selected_profile: profile.Profile, start: world.Moment, expected_failure: explore.PropertyEvaluation, build_identities: []const types.BuildIdentity, replay_frames: []const types.Frame, runner: types.Runner, ) Error!Capture { if (build_identities.len > capacity.builds) { return error.BuildCapacityExceeded; } if (replay_frames.len > capacity.frames) { return error.FrameCapacityExceeded; } var result: Self = undefined; result.selected_profile = selected_profile; result.start = start; result.expected_failure = expected_failure; result.build_count = @intCast(build_identities.len); result.frame_count = @intCast(replay_frames.len); @memcpy( result.builds_storage[0..build_identities.len], build_identities, ); @memcpy( result.frames_storage[0..replay_frames.len], replay_frames, ); return result.publishOwned(runner); } fn publishOwned(self: *Self, runner: types.Runner) Error!Capture { self.rebuildExternalLedger(); try self.validate(); return switch (try self.replay(runner)) { .reproduced => .{ .published = self.* }, .rejected => |rejection| .{ .rejected = rejection }, }; } pub fn builds(self: *const Self) []const types.BuildIdentity { std.debug.assert(self.build_count <= capacity.builds); return self.builds_storage[0..self.build_count]; } pub fn frames(self: *const Self) []const types.Frame { std.debug.assert(self.frame_count <= capacity.frames); return self.frames_storage[0..self.frame_count]; } pub fn externalResults(self: *const Self) []const types.ExternalResult { std.debug.assert(self.external_count <= capacity.frames); return self.external_storage[0..self.external_count]; } pub fn replay(self: *const Self, runner: types.Runner) Error!types.Replay { try self.validate(); const prepared = runner.prepare( runner.context, self.selected_profile, self.start, self.builds(), self.externalResults(), ); var current = switch (prepared) { .ready => |ready| ready, .incomplete => |reason| return rejected( 0, .preparation_incomplete, reason, ), }; if (!std.meta.eql(current, self.start)) { return rejected(0, .start_mismatch, null); } for (self.frames(), 0..) |frame, index| { const result = runner.step.run(current, frame.decision); if (index + 1 == self.frame_count) { return self.finishReplay(frame, result, index); } current = switch (result) { .completed => |next| next, .failed => return rejected(index, .unexpected_failure, null), .incomplete => |reason| return rejected( index, .step_incomplete, reason, ), }; if (!std.meta.eql(current, frame.expected)) { return rejected(index, .moment_mismatch, null); } } unreachable; } pub fn encode(self: *const Self, output: *Wire) Error!void { try canon.encode(capacity, self, output); } pub fn decode(input: *const Wire) Error!Self { var result: Self = undefined; try canon.decode(capacity, input, &result); return result; } pub fn identity(self: *const Self) Error!types.Identity { var wire: Wire = undefined; try self.encode(&wire); return canon.identity(capacity, &wire); } pub fn validate(self: *const Self) Error!void { try profile.validate(self.selected_profile); try world.verifyMoment(self.start, self.start.origin, self.start.fabric); const contract = try profile.contractFingerprint(self.selected_profile); if (!std.meta.eql(contract, self.start.origin.machine_contract)) { return error.InitialContractMismatch; } if (self.frame_count == 0 or self.frame_count > capacity.frames) { return error.FrameInvalid; } if (self.build_count != self.start.origin.node_count or self.build_count > capacity.builds) { return error.BuildCountMismatch; } if (self.expected_failure.verdict != .violated) { return error.FailureExpected; } try self.validateBuilds(); try self.validateFrames(); try self.validateExternalLedger(); } fn copyHistory(self: *Self, search: anytype, branch_id: anytype) Error!void { var cursor = branch_id; var remaining = self.frame_count; while (remaining > 0) { const branch = try search.branch(cursor); remaining -= 1; self.frames_storage[remaining] = .{ .decision = branch.decision.?, .expected = branch.root, }; cursor = branch.parent.?; } std.debug.assert(cursor == 0); } fn finishReplay( self: *const Self, frame: types.Frame, result: explore.SearchExecution, index: usize, ) types.Replay { return switch (result) { .completed => rejected(index, .expected_failure_missing, null), .incomplete => |reason| rejected(index, .step_incomplete, reason), .failed => |failure| if (!std.meta.eql(failure.root, frame.expected)) rejected(index, .moment_mismatch, null) else if (!std.meta.eql(failure.evaluation, self.expected_failure)) rejected(index, .failure_mismatch, null) else .{ .reproduced = failure.evaluation }, }; } fn validateBuilds(self: *const Self) Error!void { for (self.builds(), 0..) |build, index| { if (allZero(&build.node.bytes) or allZero(&build.execution.digest)) { return error.BuildIdentityInvalid; } if (index > 0 and !std.mem.lessThan( u8, &self.builds_storage[index - 1].node.bytes, &build.node.bytes, )) return error.BuildOrderInvalid; } } fn validateFrames(self: *const Self) Error!void { var previous = self.start; for (self.frames()) |frame| { if (!frame.decision.valid()) return error.FrameInvalid; try validateDecisionOrigins(frame.decision); try world.verifyMoment( frame.expected, self.start.origin, frame.expected.fabric, ); if (!momentFollows(previous, frame.expected)) { return error.FrameInvalid; } previous = frame.expected; } } fn rebuildExternalLedger(self: *Self) void { self.external_count = 0; for (self.frames(), 0..) |frame, index| { const value = externalResult(frame.decision, @intCast(index)) orelse continue; std.debug.assert(self.external_count < capacity.frames); self.external_storage[self.external_count] = value; self.external_count += 1; } } fn validateExternalLedger(self: *const Self) Error!void { if (self.external_count > self.frame_count) { return error.ExternalLedgerInvalid; } var expected: [capacity.frames]types.ExternalResult = undefined; var count: u16 = 0; for (self.frames(), 0..) |frame, index| { const value = externalResult(frame.decision, @intCast(index)) orelse continue; expected[count] = value; count += 1; } if (count != self.external_count) return error.ExternalLedgerInvalid; for (expected[0..count], self.externalResults()) |left, right| { if (!std.meta.eql(left, right)) { return error.ExternalLedgerInvalid; } } } };}Source: lib/machine/src/explore/capsule/owner.zig:291
pub fn Reducer(comptime capacity_value: types.Capacity) type { const CapsuleType = Capsule(capacity_value); const attempt_limit = reductionAttemptLimit(capacity_value.frames); return struct { accepted: CapsuleType, scratch: CapsuleType, statistics: types.Statistics = .{ .attempts = 0, .accepted = 0, .rejected = 0 }, const Self = @This(); pub const capacity: types.Capacity = capacity_value; pub const Init = union(enum) { ready: Self, rejected: types.Rejection, }; pub const Attempt = union(enum) { accepted, rejected: types.Rejection, }; pub const Reduced = struct { capsule: CapsuleType, statistics: types.Statistics, }; pub fn init(value: CapsuleType, runner: types.Runner) types.Error!Init { return switch (try value.replay(runner)) { .reproduced => .{ .ready = .{ .accepted = value, .scratch = value } }, .rejected => |rejection| .{ .rejected = rejection }, }; } pub fn tryRemove( self: *Self, frame: u16, runner: types.Runner, ) types.Error!Attempt { if (frame >= self.accepted.frame_count) return error.FrameUnknown; if (self.accepted.frame_count == 1) return error.FailureExpected; self.scratch = self.accepted; const remaining = self.scratch.frame_count - frame - 1; std.mem.copyForwards( types.Frame, self.scratch.frames_storage[frame .. frame + remaining], self.scratch.frames_storage[frame + 1 .. self.scratch.frame_count], ); self.scratch.frame_count -= 1; self.scratch.rebuildExternalLedger(); self.statistics.attempts += 1; return switch (try self.scratch.replay(runner)) { .reproduced => { self.accepted = self.scratch; self.statistics.accepted += 1; return .accepted; }, .rejected => |rejection| { self.statistics.rejected += 1; return .{ .rejected = rejection }; }, }; } pub fn reduce(self: *Self, runner: types.Runner) types.Error!Reduced { var frame: u16 = 0; for (0..attempt_limit) |_| { if (self.accepted.frame_count == 1 or frame == self.accepted.frame_count) { return .{ .capsule = self.accepted, .statistics = self.statistics, }; } switch (try self.tryRemove(frame, runner)) { .accepted => frame = 0, .rejected => frame += 1, } } unreachable; } };}Source: lib/machine/src/explore/capsule/types.zig:80
pub const Error = explore.SearchError || profile.Error || world.MomentError || error{ BuildCapacityExceeded, BuildCountMismatch, BuildIdentityInvalid, BuildOrderInvalid, CapsuleDigestMismatch, CapsuleNonCanonical, CapsuleWireBadMagic, CapsuleWireCapacityMismatch, CapsuleWireFieldInvalid, CapsuleWireFlagsUnsupported, CapsuleWireSizeMismatch, CapsuleWireVersionUnsupported, ExternalLedgerInvalid, FailureBranchMismatch, FailureExpected, FrameCapacityExceeded, FrameInvalid, FrameUnknown, InitialContractMismatch,};Source: lib/machine/src/explore/fault.zig:6
pub fn Generator(comptime capacity_value: explore.FaultCapacity) type { return struct { seed: explore.Seed, temporal: explore.TemporalPlan, kinds: [capacity.kinds]fault.Kind, kind_count: u8, sequence: u16 = 0, virtual_time_tick: u64, active: ?fault.Kind = null, const Self = @This(); pub const capacity: explore.FaultCapacity = capacity_value; pub const Error: type = explore.GeneratorError; pub const InitResult: type = explore.Generation(Self); pub const Site: type = explore.ChoiceSite( explore.FaultAlternative, capacity.alternatives, ); pub const NextResult: type = explore.Generation(Site); pub fn init( selected: profile.Profile, seed: explore.Seed, plan: explore.FaultPlan, ) Error!InitResult { try profile.validate(selected); if (plan.temporal.end_tick < plan.temporal.start_tick or plan.kinds.len == 0) { return error.InvalidPlan; } if (plan.temporal.steps == 0 or plan.temporal.start_tick == plan.temporal.end_tick) { return .exhausted; } if (plan.temporal.steps > capacity.steps) { return .{ .incomplete = .step_capacity }; } if (plan.kinds.len > capacity.kinds) { return .{ .incomplete = .kind_capacity }; } if (plan.kinds.len + 1 > capacity.alternatives) { return .{ .incomplete = .alternative_capacity }; } try validateKinds(plan.kinds); var kinds: [capacity.kinds]fault.Kind = undefined; @memcpy(kinds[0..plan.kinds.len], plan.kinds); return .{ .item = .{ .seed = seed, .temporal = plan.temporal, .kinds = kinds, .kind_count = @intCast(plan.kinds.len), .virtual_time_tick = plan.temporal.start_tick, } }; } pub fn next(self: *const Self) NextResult { self.assertValid(); if (self.sequence == self.temporal.steps or self.virtual_time_tick == self.temporal.end_tick) { return .exhausted; } var site = Site{ .id = self.siteId(), .alternatives = undefined, .count = 0, .suggested = 0, }; if (self.active) |kind| { self.activeAlternatives(&site, kind); } else { self.healthyAlternatives(&site); } std.debug.assert(site.count > 0); site.suggested = self.seed.suggest(.fault, self.sequence, site.count); return .{ .item = site }; } pub fn choose( self: *Self, site: Site, index: u8, ) Error!explore.GeneratedFault { const current = switch (self.next()) { .item => |value| value, .exhausted, .incomplete => return error.StaleChoiceSite, }; if (!sameSite(current, site)) return error.StaleChoiceSite; if (index >= site.count) return error.ChoiceOutOfRange; const selected = site.alternatives[index]; switch (selected.action) { .healthy => self.active = null, .inject => |kind| self.active = kind, .persist => |kind| std.debug.assert(self.active.? == kind), .recover => |kind| { std.debug.assert(self.active.? == kind); self.active = null; }, } self.virtual_time_tick = selected.virtual_time_tick; self.sequence += 1; self.assertValid(); return selected; } pub fn activeFault(self: *const Self) ?fault.Kind { self.assertValid(); return self.active; } fn healthyAlternatives(self: *const Self, site: *Site) void { self.addAt(site, self.longDelay(), null, .{ .healthy = {} }); for (self.kinds[0..self.kind_count]) |kind| { self.addAt(site, 1, explore.faultOrigin(kind), .{ .inject = kind }); } } fn activeAlternatives(self: *const Self, site: *Site, kind: fault.Kind) void { const persistent_delay = 1 + self.seed.word(.fault, self.sequence, 1) % 4; self.addAt( site, persistent_delay, explore.faultOrigin(kind), .{ .persist = kind }, ); self.addAt(site, 1, explore.faultOrigin(kind), .{ .recover = kind }); } fn addAt( self: *const Self, site: *Site, delta: u64, effect_origin: ?explore.Origin, action: explore.FaultAction, ) void { const tick = self.futureTick(delta) orelse return; std.debug.assert(site.count < site.alternatives.len); site.alternatives[site.count] = .{ .choice_origin = explore.origin(.fault_choice), .effect_origin = effect_origin, .virtual_time_tick = tick, .action = action, }; site.count += 1; } fn futureTick(self: *const Self, delta: u64) ?u64 { std.debug.assert(self.virtual_time_tick <= self.temporal.end_tick); const remaining = self.temporal.end_tick - self.virtual_time_tick; if (delta == 0 or delta > remaining) return null; return self.virtual_time_tick + delta; } fn longDelay(self: *const Self) u64 { return 4 + self.seed.word(.fault, self.sequence, 2) % 13; } fn siteId(self: *const Self) explore.SiteId { return .{ .stream = .fault, .sequence = self.sequence, .virtual_time_tick = self.virtual_time_tick, }; } fn assertValid(self: *const Self) void { std.debug.assert(self.kind_count > 0); std.debug.assert(self.kind_count <= capacity.kinds); std.debug.assert(self.sequence <= self.temporal.steps); std.debug.assert(self.sequence <= capacity.steps); std.debug.assert(self.virtual_time_tick >= self.temporal.start_tick); std.debug.assert(self.virtual_time_tick <= self.temporal.end_tick); } fn sameSite(expected: Site, actual: Site) bool { if (!std.meta.eql(expected.id, actual.id)) return false; if (expected.count != actual.count) return false; if (expected.suggested != actual.suggested) return false; for (expected.values(), actual.values()) |left, right| { if (!std.meta.eql(left, right)) return false; } return true; } };}Source: lib/machine/src/explore/input.zig:7
pub fn Generator(comptime capacity_value: explore.InputCapacity) type { return struct { seed: explore.Seed, plan: explore.TemporalPlan, sequence: u16 = 0, virtual_time_tick: u64, phase: explore.InputPhase = .quiet, burst_remaining: u8 = 0, const Self = @This(); pub const capacity: explore.InputCapacity = capacity_value; pub const Error: type = explore.GeneratorError; pub const InitResult: type = explore.Generation(Self); pub const Site: type = explore.ChoiceSite( explore.InputAlternative, capacity.alternatives, ); pub const NextResult: type = explore.Generation(Site); pub fn init( selected: profile.Profile, seed: explore.Seed, plan: explore.TemporalPlan, ) Error!InitResult { try profile.validate(selected); if (plan.end_tick < plan.start_tick) return error.InvalidPlan; if (plan.steps == 0 or plan.start_tick == plan.end_tick) { return .exhausted; } if (plan.steps > capacity.steps) { return .{ .incomplete = .step_capacity }; } if (capacity.alternatives < alternative_limit) { return .{ .incomplete = .alternative_capacity }; } return .{ .item = .{ .seed = seed, .plan = plan, .virtual_time_tick = plan.start_tick, } }; } pub fn next(self: *const Self) NextResult { self.assertValid(); if (self.sequence == self.plan.steps or self.virtual_time_tick == self.plan.end_tick) { return .exhausted; } var site = Site{ .id = self.siteId(), .alternatives = undefined, .count = 0, .suggested = 0, }; switch (self.phase) { .quiet => self.quietAlternatives(&site), .burst => self.burstAlternatives(&site), } std.debug.assert(site.count > 0); site.suggested = self.seed.suggest(.input, self.sequence, site.count); return .{ .item = site }; } pub fn choose( self: *Self, site: Site, index: u8, ) Error!explore.GeneratedInput { const current = switch (self.next()) { .item => |value| value, .exhausted, .incomplete => return error.StaleChoiceSite, }; if (!sameSite(current, site)) return error.StaleChoiceSite; if (index >= site.count) return error.ChoiceOutOfRange; const selected = site.alternatives[index]; self.advance(selected); self.assertValid(); return selected; } pub fn workload(self: *const Self) explore.InputPhase { self.assertValid(); return self.phase; } fn quietAlternatives(self: *const Self, site: *Site) void { self.addAt(site, self.longDelay(), .{ .wait = self.longDelay() }); self.addAt(site, 1, .{ .terminal = self.seed.word(.input, self.sequence, 1) }); self.addAt(site, 1, .{ .entropy = self.seed.word(.input, self.sequence, 2) }); self.addAt(site, 1, .{ .service_result = .failed }); self.addAt(site, 1, .{ .effect_result = .uncertain }); } fn burstAlternatives(self: *const Self, site: *Site) void { self.addAt(site, 1, .{ .terminal = self.seed.word(.input, self.sequence, 3) }); self.addAt(site, 1, .{ .packet = self.seed.word(.input, self.sequence, 4) }); self.addAt(site, 1, .{ .entropy = self.seed.word(.input, self.sequence, 5) }); self.addAt(site, 1, .{ .service_result = .succeeded }); self.addAt(site, self.longDelay(), .{ .wait = self.longDelay() }); } fn addAt(self: *const Self, site: *Site, delta: u64, value: explore.InputValue) void { const tick = self.futureTick(delta) orelse return; std.debug.assert(site.count < site.alternatives.len); site.alternatives[site.count] = .{ .origin = explore.origin(inputSource(value)), .virtual_time_tick = tick, .value = value, }; site.count += 1; } fn advance(self: *Self, selected: explore.GeneratedInput) void { std.debug.assert(selected.virtual_time_tick > self.virtual_time_tick); std.debug.assert(selected.virtual_time_tick <= self.plan.end_tick); self.virtual_time_tick = selected.virtual_time_tick; self.sequence += 1; switch (selected.value) { .wait => { self.phase = .quiet; self.burst_remaining = 0; }, .terminal, .entropy, .packet, .service_result, .effect_result => { self.advanceBurst(); }, } } fn advanceBurst(self: *Self) void { switch (self.phase) { .quiet => { const width = self.seed.word(.input, self.sequence, 6) % 3; self.phase = .burst; self.burst_remaining = 2 + @as(u8, @intCast(width)); }, .burst => { std.debug.assert(self.burst_remaining > 0); self.burst_remaining -= 1; if (self.burst_remaining == 0) self.phase = .quiet; }, } } fn futureTick(self: *const Self, delta: u64) ?u64 { std.debug.assert(self.virtual_time_tick <= self.plan.end_tick); const remaining = self.plan.end_tick - self.virtual_time_tick; if (delta == 0 or delta > remaining) return null; return self.virtual_time_tick + delta; } fn longDelay(self: *const Self) u64 { return 4 + self.seed.word(.input, self.sequence, 7) % 13; } fn siteId(self: *const Self) explore.SiteId { return .{ .stream = .input, .sequence = self.sequence, .virtual_time_tick = self.virtual_time_tick, }; } fn assertValid(self: *const Self) void { std.debug.assert(self.sequence <= self.plan.steps); std.debug.assert(self.sequence <= capacity.steps); std.debug.assert(self.virtual_time_tick >= self.plan.start_tick); std.debug.assert(self.virtual_time_tick <= self.plan.end_tick); if (self.phase == .quiet) std.debug.assert(self.burst_remaining == 0); } fn sameSite(expected: Site, actual: Site) bool { if (!std.meta.eql(expected.id, actual.id)) return false; if (expected.count != actual.count) return false; if (expected.suggested != actual.suggested) return false; for (expected.values(), actual.values()) |left, right| { if (!std.meta.eql(left, right)) return false; } return true; } };}Source: lib/machine/src/explore/model.zig:115
pub fn ChoiceSite( comptime Alternative: type, comptime alternative_capacity: u8,) type { return struct { id: SiteId, alternatives: [alternative_capacity]Alternative, count: u8, suggested: u8, pub fn values(self: *const @This()) []const Alternative { std.debug.assert(self.count <= self.alternatives.len); return self.alternatives[0..self.count]; } };}Source: lib/machine/src/explore/model.zig:107
pub fn Generation(comptime T: type) type { return union(enum) { item: T, exhausted, incomplete: IncompleteReason, };}Source: lib/machine/src/explore/model.zig:13
Source: lib/machine/src/explore/model.zig:287
pub const SemanticId = u64;Source: lib/machine/src/explore/model.zig:274
pub fn faultOrigin(kind: fault.Kind) Origin { return origin(switch (kind) { .machine_crash => .machine_crash, .process_crash => .process_crash, .io_error => .effect_result, .packet_loss, .packet_delay, .packet_reorder, .partition => .packet_fault, .clock_jump => .virtual_time, .entropy_choice => .entropy_input, .capacity_exhaustion => .capacity_fault, .host_service_failure => .host_service_result, });}Source: lib/machine/src/explore/model.zig:91
pub fn origin(source: profile.DeterminismSource) Origin { const declared = profile.determinism.entry(source); std.debug.assert(declared.source == source); std.debug.assert(declared.version > 0); return .{ .source = source, .version = declared.version };}Source: lib/machine/src/explore/schedule.zig:6
pub fn Generator(comptime capacity_value: explore.ScheduleCapacity) type { return struct { seed: explore.Seed, plan: explore.SchedulePlan, sequence: u16 = 0, virtual_time_tick: u64, last_node: ?u8 = null, const Self = @This(); pub const capacity: explore.ScheduleCapacity = capacity_value; pub const Error: type = explore.GeneratorError; pub const InitResult: type = explore.Generation(Self); pub const Site: type = explore.ChoiceSite( explore.ScheduleAlternative, capacity.alternatives, ); pub const NextResult: type = explore.Generation(Site); pub fn init( selected: profile.Profile, seed: explore.Seed, plan: explore.SchedulePlan, ) Error!InitResult { try profile.validate(selected); if (plan.nodes == 0 or plan.nodes > fabric.node_limit) { return error.InvalidPlan; } if (plan.temporal.end_tick < plan.temporal.start_tick) { return error.InvalidPlan; } if (plan.temporal.steps == 0 or plan.temporal.start_tick == plan.temporal.end_tick) { return .exhausted; } if (plan.temporal.steps > capacity.steps) { return .{ .incomplete = .step_capacity }; } if (plan.nodes > capacity.nodes) { return .{ .incomplete = .node_capacity }; } if (capacity.alternatives < plan.nodes + 1) { return .{ .incomplete = .alternative_capacity }; } return .{ .item = .{ .seed = seed, .plan = plan, .virtual_time_tick = plan.temporal.start_tick, } }; } pub fn next(self: *const Self) NextResult { self.assertValid(); if (self.sequence == self.plan.temporal.steps or self.virtual_time_tick == self.plan.temporal.end_tick) { return .exhausted; } var site = Site{ .id = self.siteId(), .alternatives = undefined, .count = 0, .suggested = 0, }; self.addTurns(&site); self.addIdle(&site); std.debug.assert(site.count == self.plan.nodes + 1); site.suggested = self.seed.suggest(.schedule, self.sequence, site.count); return .{ .item = site }; } pub fn choose( self: *Self, site: Site, index: u8, ) Error!explore.GeneratedSchedule { const current = switch (self.next()) { .item => |value| value, .exhausted, .incomplete => return error.StaleChoiceSite, }; if (!sameSite(current, site)) return error.StaleChoiceSite; if (index >= site.count) return error.ChoiceOutOfRange; const selected = site.alternatives[index]; switch (selected.value) { .turn => |node| self.last_node = node, .idle => {}, } self.virtual_time_tick = selected.virtual_time_tick; self.sequence += 1; self.assertValid(); return selected; } fn addTurns(self: *const Self, site: *Site) void { const node_count: usize = self.plan.nodes; const first: usize = if (self.last_node) |node| (@as(usize, node) + 1) % node_count else 0; for (0..node_count) |offset| { const node: u8 = @intCast((first + offset) % node_count); self.add(site, .turn_selection, .{ .turn = node }); } } fn addIdle(self: *const Self, site: *Site) void { self.add(site, .guest_schedule, .{ .idle = {} }); } fn add( self: *const Self, site: *Site, source: profile.DeterminismSource, value: explore.ScheduleValue, ) void { std.debug.assert(site.count < site.alternatives.len); site.alternatives[site.count] = .{ .origin = explore.origin(source), .virtual_time_tick = self.virtual_time_tick + 1, .value = value, }; site.count += 1; } fn siteId(self: *const Self) explore.SiteId { return .{ .stream = .schedule, .sequence = self.sequence, .virtual_time_tick = self.virtual_time_tick, }; } fn assertValid(self: *const Self) void { std.debug.assert(self.sequence <= self.plan.temporal.steps); std.debug.assert(self.sequence <= capacity.steps); std.debug.assert(self.virtual_time_tick >= self.plan.temporal.start_tick); std.debug.assert(self.virtual_time_tick <= self.plan.temporal.end_tick); if (self.last_node) |node| std.debug.assert(node < self.plan.nodes); } fn sameSite(expected: Site, actual: Site) bool { if (!std.meta.eql(expected.id, actual.id)) return false; if (expected.count != actual.count) return false; if (expected.suggested != actual.suggested) return false; for (expected.values(), actual.values()) |left, right| { if (!std.meta.eql(left, right)) return false; } return true; } };}Source: lib/machine/src/explore/search/owner.zig:21
/// Returns a search type that keeps two arrays for each pair of capacities, so/// the whole search fits storage fixed at compile time, and a failing run can/// be walked back to the root through its parents. The first array holds the/// queue of choices waiting to run (*frontier*), up to the frontier capacity,/// and the second holds every settled run the search keeps, up to the retained/// capacity. A frontier or retained capacity of zero stops the build with a/// compile error. `init` verifies the root position in a world's history/// (*moment*) and stores it as branch 0. Each settled run keeps its complete/// moment, its parent, and the alternative taken at a step (*decision*), so/// `history` walks a run back to the root and returns its decisions in order./// `findExact` matches a settled run only when every field of its moment is/// equal, so two runs whose digests agree stay two runs. The search stores both/// arrays inside the value and holds no pointers, so `checkpoint` returns a/// plain copy of it. `restoreCheckpoint` validates a saved copy and returns/// `CheckpointInvalid` for a corrupt one.pub fn Search(comptime capacity_value: types.Capacity) type { if (capacity_value.frontier == 0) { @compileError("search frontier capacity must be positive"); } if (capacity_value.retained == 0) { @compileError("search retained capacity must be positive"); } return struct { branches_storage: [capacity.retained]types.Branch = undefined, frontier_storage: [capacity.frontier]types.Candidate = undefined, branch_count: u16 = 0, frontier_count: u16 = 0, budget: types.Budget, execution_count: u32 = 0, pruned_count: u64 = 0, const Self = @This(); pub const capacity: types.Capacity = capacity_value; pub const Checkpoint = Self; pub const Error = types.Error; pub fn init(root: world.Moment, budget: types.Budget) Error!Self { try world.verifyMoment(root, root.origin, root.fabric); var self = Self{ .budget = budget }; self.branches_storage[0] = .{ .root = root }; self.branch_count = 1; self.assertValid(); return self; } pub fn checkpoint(self: *const Self) Checkpoint { self.validateCheckpoint() catch unreachable; return self.*; } pub fn restoreCheckpoint(value: Checkpoint) Error!Self { try value.validateCheckpoint(); return value; } pub fn branches(self: *const Self) []const types.Branch { self.assertValid(); return self.branches_storage[0..self.branch_count]; } pub fn frontier(self: *const Self) []const types.Candidate { self.assertValid(); return self.frontier_storage[0..self.frontier_count]; } pub fn progress(self: *const Self) types.Progress { self.assertValid(); return .{ .executions = self.execution_count, .retained = self.branch_count, .frontier = self.frontier_count, .pruned = self.pruned_count, }; } pub fn branch(self: *const Self, id: types.BranchId) Error!types.Branch { self.assertValid(); if (id >= self.branch_count) return error.BranchUnknown; return self.branches_storage[id]; } pub fn findExact( self: *const Self, root: world.Moment, ) ?types.BranchId { self.assertValid(); for (self.branches(), 0..) |retained, index| { if (std.meta.eql(retained.root, root)) return @intCast(index); } return null; } pub fn history( self: *const Self, id: types.BranchId, destination: []types.Decision, ) Error![]const types.Decision { const selected = try self.branch(id); const depth: usize = selected.depth; if (destination.len < depth) return error.HistoryCapacityExceeded; var cursor = id; var remaining = depth; while (remaining > 0) { const current = self.branches_storage[cursor]; remaining -= 1; destination[remaining] = current.decision.?; cursor = current.parent.?; } std.debug.assert(cursor == 0); return destination[0..depth]; } pub fn expandInput( self: *Self, parent: types.BranchId, site: anytype, ) Error!types.Expansion { return self.expand(parent, site, .input); } pub fn expandSchedule( self: *Self, parent: types.BranchId, site: anytype, ) Error!types.Expansion { return self.expand(parent, site, .schedule); } pub fn expandTopology( self: *Self, parent: types.BranchId, site: anytype, ) Error!types.Expansion { return self.expand(parent, site, .topology); } pub fn expandFault( self: *Self, parent: types.BranchId, site: anytype, ) Error!types.Expansion { return self.expand(parent, site, .fault); } pub fn step(self: *Self, runner: types.Runner) types.Outcome { self.assertValid(); if (self.execution_count == self.budget.executions) { return self.exhausted(.execution_budget); } if (self.frontier_count == 0) return self.exhausted(.frontier_empty); if (self.branch_count == capacity.retained) { return incompleteOutcome(.retained_capacity, self.frontier_storage[0]); } const candidate = self.frontier_storage[0]; const parent = self.branches_storage[candidate.parent]; const result = runner.run(parent.root, candidate.decision); self.execution_count += 1; const outcome = switch (result) { .completed => |root| if (momentFollows(parent.root, root)) self.complete(candidate, root) else incompleteOutcome(.{ .execution = .invalid_moment }, candidate), .failed => |failure| if (!momentFollows(parent.root, failure.root)) incompleteOutcome(.{ .execution = .invalid_moment }, candidate) else if (failure.evaluation.verdict != .violated) incompleteOutcome(.{ .execution = .invalid_failure }, candidate) else self.fail(candidate, failure), .incomplete => |reason| incompleteOutcome( .{ .execution = reason }, candidate, ), }; self.assertValid(); return outcome; } pub fn pruneNext(self: *Self) types.Outcome { self.assertValid(); if (self.frontier_count == 0) return self.exhausted(.frontier_empty); const candidate = self.removeCandidate(); self.pruned_count += 1; self.assertValid(); return .{ .pruned = .{ .reason = .strategy, .parent = candidate.parent, .decision = candidate.decision, .count = 1, } }; } pub fn interrupt(self: *const Self) types.Outcome { self.assertValid(); const candidate = if (self.frontier_count == 0) null else self.frontier_storage[0]; return incompleteOutcome(.interrupted, candidate); } pub fn generationIncomplete( self: *const Self, reason: explore.IncompleteReason, ) types.Outcome { self.assertValid(); const candidate = if (self.frontier_count == 0) null else self.frontier_storage[0]; return incompleteOutcome(.{ .generation = reason }, candidate); } fn expand( self: *Self, parent_id: types.BranchId, site: anytype, comptime stream: explore.Stream, ) Error!types.Expansion { self.assertValid(); if (parent_id >= self.branch_count) return error.BranchUnknown; const parent = &self.branches_storage[parent_id]; if (parent.expanded) return error.BranchAlreadyExpanded; if (!siteValid(site, stream)) return error.ChoiceSiteInvalid; if (parent.depth >= self.budget.depth) { parent.expanded = true; return .{ .exhausted = self.exhaustedValue(.depth_budget) }; } const available = capacity.frontier - self.frontier_count; const admitted: u8 = @intCast(@min(site.count, available)); self.addSite(parent_id, site, stream, admitted); parent.expanded = true; if (admitted == site.count) { return .{ .expanded = .{ .parent = parent_id, .admitted = admitted } }; } const discarded: u16 = site.count - admitted; self.pruned_count += discarded; return .{ .pruned = .{ .reason = .frontier_capacity, .parent = parent_id, .decision = null, .count = discarded, } }; } fn addSite( self: *Self, parent: types.BranchId, site: anytype, comptime stream: explore.Stream, admitted: u8, ) void { if (admitted == 0) return; self.addAlternative(parent, site, stream, site.suggested); var added: u8 = 1; var index: u8 = 0; while (index < site.count and added < admitted) : (index += 1) { if (index == site.suggested) continue; self.addAlternative(parent, site, stream, index); added += 1; } std.debug.assert(added == admitted); } fn addAlternative( self: *Self, parent: types.BranchId, site: anytype, comptime stream: explore.Stream, index: u8, ) void { std.debug.assert(self.frontier_count < capacity.frontier); self.frontier_storage[self.frontier_count] = .{ .parent = parent, .decision = .{ .site = site.id, .alternative = index, .alternative_count = site.count, .choice = @unionInit( types.Choice, @tagName(stream), site.alternatives[index], ), }, }; self.frontier_count += 1; } fn complete( self: *Self, candidate: types.Candidate, root: world.Moment, ) types.Outcome { _ = self.removeCandidate(); const branch_id = self.retain(candidate, root, .completed); return .{ .completed = .{ .branch = branch_id, .parent = candidate.parent, .decision = candidate.decision, } }; } fn fail( self: *Self, candidate: types.Candidate, failure: types.FailedExecution, ) types.Outcome { _ = self.removeCandidate(); const branch_id = self.retain( candidate, failure.root, .{ .failed = failure.evaluation }, ); return .{ .failed = .{ .branch = branch_id, .parent = candidate.parent, .decision = candidate.decision, .evaluation = failure.evaluation, } }; } fn retain( self: *Self, candidate: types.Candidate, root: world.Moment, settlement: types.Settlement, ) types.BranchId { std.debug.assert(self.branch_count < capacity.retained); const parent = self.branches_storage[candidate.parent]; const id = self.branch_count; self.branches_storage[id] = .{ .root = root, .parent = candidate.parent, .decision = candidate.decision, .depth = parent.depth + 1, .settlement = settlement, }; self.branch_count += 1; return id; } fn removeCandidate(self: *Self) types.Candidate { std.debug.assert(self.frontier_count > 0); const candidate = self.frontier_storage[0]; const remaining = self.frontier_count - 1; std.mem.copyForwards( types.Candidate, self.frontier_storage[0..remaining], self.frontier_storage[1..self.frontier_count], ); self.frontier_count = remaining; return candidate; } fn exhausted( self: *const Self, reason: types.ExhaustedReason, ) types.Outcome { return .{ .exhausted = self.exhaustedValue(reason) }; } fn exhaustedValue( self: *const Self, reason: types.ExhaustedReason, ) types.Exhausted { return .{ .reason = reason, .executions = self.execution_count, .frontier = self.frontier_count, }; } fn incompleteOutcome( reason: types.IncompleteReason, candidate: ?types.Candidate, ) types.Outcome { return .{ .incomplete = .{ .reason = reason, .candidate = candidate, } }; } fn validateCheckpoint(self: *const Self) Error!void { if (self.branch_count == 0 or self.branch_count > capacity.retained or self.frontier_count > capacity.frontier or self.execution_count > self.budget.executions or self.execution_count < self.branch_count - 1) { return error.CheckpointInvalid; } try self.validateBranches(); try self.validateFrontier(); } fn validateBranches(self: *const Self) Error!void { const root = self.branches_storage[0]; if (root.parent != null or root.decision != null or root.depth != 0 or std.meta.activeTag(root.settlement) != .origin) { return error.CheckpointInvalid; } world.verifyMoment(root.root, root.root.origin, root.root.fabric) catch return error.CheckpointInvalid; for (self.branches_storage[1..self.branch_count], 1..) |branch_value, index| { const parent = branch_value.parent orelse return error.CheckpointInvalid; const decision = branch_value.decision orelse return error.CheckpointInvalid; if (parent >= index or !decision.valid()) { return error.CheckpointInvalid; } if (!momentFollows( self.branches_storage[parent].root, branch_value.root, )) return error.CheckpointInvalid; const parent_depth = self.branches_storage[parent].depth; if (branch_value.depth != parent_depth + 1 or branch_value.depth > self.budget.depth or !self.branches_storage[parent].expanded or std.meta.activeTag(branch_value.settlement) == .origin) { return error.CheckpointInvalid; } if (branchFailureInvalid(branch_value.settlement)) { return error.CheckpointInvalid; } for (self.branches_storage[1..index]) |prior| { if (sameBranchWork(prior, branch_value)) { return error.CheckpointInvalid; } } } } fn validateFrontier(self: *const Self) Error!void { for ( self.frontier_storage[0..self.frontier_count], 0.., ) |candidate, index| { if (candidate.parent >= self.branch_count or !candidate.decision.valid() or !self.branches_storage[candidate.parent].expanded or self.branches_storage[candidate.parent].depth >= self.budget.depth) { return error.CheckpointInvalid; } for (self.frontier_storage[0..index]) |prior| { if (sameCandidateWork(prior, candidate)) { return error.CheckpointInvalid; } } for (self.branches_storage[1..self.branch_count]) |settled| { if (sameSettledWork(settled, candidate)) { return error.CheckpointInvalid; } } } } fn assertValid(self: *const Self) void { std.debug.assert(self.branch_count > 0); std.debug.assert(self.branch_count <= capacity.retained); std.debug.assert(self.frontier_count <= capacity.frontier); std.debug.assert(self.execution_count <= self.budget.executions); std.debug.assert(self.execution_count >= self.branch_count - 1); } };}Source: lib/machine/src/explore/search/types.zig:5
pub const BranchId = u16;Source: lib/machine/src/explore/search/types.zig:164
pub const Error = world.MomentError || error{ BranchAlreadyExpanded, BranchUnknown, CheckpointInvalid, ChoiceSiteInvalid, HistoryCapacityExceeded,};Source: lib/machine/src/explore/temporal.zig:11
pub fn Sequence(comptime capacity_value: explore.EventCapacity) type { return struct { storage: [capacity.events]explore.Event = undefined, count: u16 = 0, state: explore.TraceState = .open, const Self = @This(); pub const capacity: explore.EventCapacity = capacity_value; pub const Error: type = SequenceError; pub fn append( self: *Self, virtual_time_tick: u64, value: explore.EventValue, ) Error!void { self.assertValid(); if (self.state != .open) return error.SequenceClosed; if (self.count == self.storage.len) { self.state = .incomplete; return error.CapacityExceeded; } if (self.count > 0 and virtual_time_tick < self.storage[self.count - 1].virtual_time_tick) { return error.VirtualTimeRegressed; } self.storage[self.count] = .{ .sequence = self.count, .virtual_time_tick = virtual_time_tick, .value = value, }; self.count += 1; self.assertValid(); } pub fn finish( self: *Self, completion: explore.TraceCompletion, ) error{SequenceClosed}!void { self.assertValid(); if (self.state != .open) return error.SequenceClosed; self.state = switch (completion) { .exhausted => .exhausted, .incomplete => .incomplete, }; self.assertValid(); } pub fn events(self: *const Self) []const explore.Event { self.assertValid(); return self.storage[0..self.count]; } pub fn traceState(self: *const Self) explore.TraceState { self.assertValid(); return self.state; } fn assertValid(self: *const Self) void { std.debug.assert(self.count <= self.storage.len); std.debug.assert(self.count <= capacity.events); } };}Source: lib/machine/src/explore/temporal.zig:110
pub fn evaluateLiveness( sequence: anytype, rule: explore.BoundedLiveness,) explore.PropertyEvaluation { const events = sequence.events(); const declaration = findDeclaration(events, rule.property) orelse return evaluation(rule.property, .incomplete, .declaration_missing, null); if (declaration.value.kind != .bounded_liveness or declaration.value.bound == null or !std.meta.eql(declaration.value.bound.?, rule.bound)) { return evaluation( rule.property, .incomplete, .declaration_mismatch, declaration.sequence, ); } return evaluateTriggers( events, declaration.offset + 1, sequence.traceState(), rule, );}Source: lib/machine/src/explore/temporal.zig:77
pub fn evaluateSafety(sequence: anytype, rule: explore.Safety) explore.PropertyEvaluation { const events = sequence.events(); const declaration = findDeclaration(events, rule.property) orelse return evaluation(rule.property, .incomplete, .declaration_missing, null); if (declaration.value.kind != .safety or declaration.value.bound != null) { return evaluation( rule.property, .incomplete, .declaration_mismatch, declaration.sequence, ); } for (events[declaration.offset + 1 ..]) |event| { if (matches(rule.forbidden, event)) { return evaluation( rule.property, .violated, .forbidden_event, event.sequence, ); } } return switch (sequence.traceState()) { .exhausted => evaluation(rule.property, .holds, .satisfied, null), .open, .incomplete => evaluation( rule.property, .incomplete, .trace_incomplete, null, ), };}Source: lib/machine/src/explore/temporal.zig:254
pub fn matches(pattern: explore.Pattern, event: explore.Event) bool { return switch (pattern) { .event_class => |class| class == eventClass(event.value), .input => |kind| switch (event.value) { .controlled_input => |value| kind == std.meta.activeTag(value.value), else => false, }, .schedule => |kind| switch (event.value) { .schedule_choice => |value| kind == std.meta.activeTag(value.value), else => false, }, .topology => |kind| switch (event.value) { .topology_choice => |value| kind == std.meta.activeTag(value.value), else => false, }, .observation => |id| switch (event.value) { .observation => |value| id == value.id, else => false, }, .property_declaration => |id| switch (event.value) { .property_declaration => |value| id == value.id, else => false, }, .property_evaluation => |expected| matchEvaluation(expected, event.value), .fault => |kind| matchFault(kind, event.value), .diagnostic => |id| switch (event.value) { .diagnostic => |value| id == value.id, else => false, }, .external_admission => |source| switch (event.value) { .external_admission => |value| source == value.origin.source, else => false, }, .operation => |expected| matchOperation(expected, event.value), };}Source: lib/machine/src/explore/topology.zig:6
pub fn Generator(comptime capacity_value: explore.TopologyCapacity) type { return struct { seed: explore.Seed, plan: explore.TopologyPlan, sequence: u16 = 0, virtual_time_tick: u64, included: [capacity.nodes]bool = @splat(false), done: bool = false, const Self = @This(); pub const capacity: explore.TopologyCapacity = capacity_value; pub const Error: type = explore.GeneratorError; pub const InitResult: type = explore.Generation(Self); pub const Site: type = explore.ChoiceSite( explore.TopologyAlternative, capacity.alternatives, ); pub const NextResult: type = explore.Generation(Site); pub fn init( selected: profile.Profile, seed: explore.Seed, plan: explore.TopologyPlan, ) Error!InitResult { try profile.validate(selected); if (plan.nodes == 0 or plan.nodes > fabric.node_limit) { return error.InvalidPlan; } if (plan.temporal.end_tick < plan.temporal.start_tick) { return error.InvalidPlan; } if (plan.temporal.steps == 0) { return .{ .incomplete = .step_capacity }; } if (plan.temporal.start_tick == plan.temporal.end_tick) { return .{ .incomplete = .virtual_time_bound }; } if (plan.temporal.steps > capacity.steps) { return .{ .incomplete = .step_capacity }; } if (plan.nodes > capacity.nodes) { return .{ .incomplete = .node_capacity }; } if (capacity.alternatives < plan.nodes + 1) { return .{ .incomplete = .alternative_capacity }; } return .{ .item = .{ .seed = seed, .plan = plan, .virtual_time_tick = plan.temporal.start_tick, } }; } pub fn next(self: *const Self) NextResult { self.assertValid(); if (self.done) return .exhausted; if (self.sequence == self.plan.temporal.steps) { return .{ .incomplete = .step_capacity }; } if (self.virtual_time_tick == self.plan.temporal.end_tick) { return .{ .incomplete = .virtual_time_bound }; } var site = Site{ .id = self.siteId(), .alternatives = undefined, .count = 0, .suggested = 0, }; self.addNodes(&site); if (self.includedCount() > 0) self.addComplete(&site); std.debug.assert(site.count > 0); site.suggested = self.seed.suggest(.topology, self.sequence, site.count); return .{ .item = site }; } pub fn choose( self: *Self, site: Site, index: u8, ) Error!explore.GeneratedTopology { const current = switch (self.next()) { .item => |value| value, .exhausted, .incomplete => return error.StaleChoiceSite, }; if (!sameSite(current, site)) return error.StaleChoiceSite; if (index >= site.count) return error.ChoiceOutOfRange; const selected = site.alternatives[index]; switch (selected.value) { .node => |node| self.applyNode(node), .complete => self.done = true, } self.virtual_time_tick = selected.virtual_time_tick; self.sequence += 1; self.assertValid(); return selected; } pub fn nodeIncluded(self: *const Self, node: u8) bool { std.debug.assert(node < self.plan.nodes); self.assertValid(); return self.included[node]; } fn addNodes(self: *const Self, site: *Site) void { const active = self.includedCount(); for (0..self.plan.nodes) |index| { if (active == 1 and self.included[index]) continue; const node: u8 = @intCast(index); self.add(site, .backend_availability, .{ .node = .{ .id = self.nodeId(node), .included = !self.included[index], } }); } } fn addComplete(self: *const Self, site: *Site) void { self.add(site, .backend_selection, .{ .complete = {} }); } fn add( self: *const Self, site: *Site, source: profile.DeterminismSource, value: explore.TopologyValue, ) void { std.debug.assert(site.count < site.alternatives.len); site.alternatives[site.count] = .{ .origin = explore.origin(source), .virtual_time_tick = self.virtual_time_tick + 1, .value = value, }; site.count += 1; } fn applyNode(self: *Self, selected: explore.TopologyNode) void { for (0..self.plan.nodes) |index| { const node: u8 = @intCast(index); if (!std.meta.eql(self.nodeId(node), selected.id)) continue; self.included[index] = selected.included; return; } unreachable; } fn nodeId(self: *const Self, node: u8) fabric.NodeId { const digest = self.seed.digest(.topology, node, 1); var bytes: [16]u8 = undefined; @memcpy(&bytes, digest[0..bytes.len]); return .{ .bytes = bytes }; } fn includedCount(self: *const Self) u8 { var result: u8 = 0; for (self.included[0..self.plan.nodes]) |included| { if (included) result += 1; } std.debug.assert(result <= self.plan.nodes); return result; } fn siteId(self: *const Self) explore.SiteId { return .{ .stream = .topology, .sequence = self.sequence, .virtual_time_tick = self.virtual_time_tick, }; } fn assertValid(self: *const Self) void { std.debug.assert(self.sequence <= self.plan.temporal.steps); std.debug.assert(self.sequence <= capacity.steps); std.debug.assert(self.virtual_time_tick >= self.plan.temporal.start_tick); std.debug.assert(self.virtual_time_tick <= self.plan.temporal.end_tick); std.debug.assert(self.includedCount() <= self.plan.nodes); } fn sameSite(expected: Site, actual: Site) bool { if (!std.meta.eql(expected.id, actual.id)) return false; if (expected.count != actual.count) return false; if (expected.suggested != actual.suggested) return false; for (expected.values(), actual.values()) |left, right| { if (!std.meta.eql(left, right)) return false; } return true; } };}Source: lib/machine/src/explore/root.zig
//! Some bugs show up only under one particular order of inputs, scheduling, and//! faults, so finding one means running a deterministic machine many times with//! different choices, checking each run against rules written beforehand, and//! keeping every bound fixed in advance. At each step the namespace offers the//! possible next inputs, scheduling choices, sets of participating nodes, and//! faults, and the same seed always offers the same possibilities. It checks//! the events of a run against two kinds of rule: an event that must never//! happen, and a triggering event that must be answered within a bound. It//! searches the possibilities, keeps every settled run together with the path//! that reached it, and resumes after an interruption. It keeps a failing run//! in a form that replays exactly and shrinks to the steps the failure needs.//! It records what happened in a run, step by step, so a caller can ask which//! step caused what and where two runs first differ. It demonstrates all of//! this on a three-node protocol with a bug planted in it: the bug is found,//! shrunk, replayed, and the fixed protocol is checked under the same search.//!//! A failing run can be repeated only when every place where two runs could//! differ is under explicit control, so each offered possibility has to say//! which of those places it stands for, and under which version. Fixed bounds//! mean a generator, an event list, or a search can run out of room, and//! running out has to read differently from finishing. A rule checked against a//! run that was cut short cannot report that the rule holds, and a rule whose//! trigger never happened says nothing about its response. A rule written after//! seeing the failure proves nothing, so each rule has to be declared in the//! event list ahead of the events it judges. Time in these runs is a simulated//! clock that moves forward only, and a response bound may count either steps//! or ticks of that clock.//!//! The namespace keeps what can happen next apart from which run to try next://! four bounded, stateful generators, one each for inputs, scheduling,//! participating nodes, and faults, offer the alternatives for one kind of//! choice, one step at a time (each a *tactic*), and a separate search decides//! which of them to run. At each step a generator offers a numbered list of//! alternatives, marks the one the seed suggests, and moves on only when the//! caller picks one of the alternatives it offered. Each alternative names the//! place it draws on, a place where two runs of the same program could differ//! (a *divergence source*), with the version that the machine's declared list//! of those places gives it. A generator that has used up its steps or its//! clock window reports itself exhausted, and one whose capacity cannot hold//! its plan reports itself incomplete with the reason.//!//! Runs are checked over a fixed-capacity list of numbered, timestamped events//! (each a *semantic event*): the choices taken, observations, rule//! declarations and verdicts, injected faults, diagnostics, outside admissions,//! and operation outcomes. The rules come in two kinds, each a rule over a//! run's events (a *temporal invariant*): a safety rule forbids an event//! pattern after its declaration, and a bounded liveness rule requires every//! trigger to be answered by a response within a bound of steps or clock ticks.//! A check returns one of four verdicts: holds, violated, unreached, or//! incomplete, each with a reason and, where one exists, the number of the//! event that decided it. An event list appended past its capacity turns//! incomplete, and a check over it reports incomplete where the full list might//! have held.//!//! The search keeps every settled run as the exact position the run reached,//! and it never merges two runs whose digests agree. A failing run becomes a//! self-contained record of fixed capacity that replays exactly and shrinks one//! step at a time, a *capsule*. A run's record becomes a sealed history with//! its own identity, built append-only step by step (a *causal history*), and//! bounded queries and comparisons read it through references into it. The//! `distributed` namespace carries a three-node commit protocol with a planted//! bug through the whole path, and the `query` namespace holds the histories,//! queries, and comparisons.//!//! - *choice site*: one step at which a generator offers a numbered list of//! alternatives, identified by its kind of choice, its step number, and its//! clock tick, with one alternative marked as the seed's suggestion.//! - *moment*: a position in one world's history, pairing the world root the//! world grew from with the ledger root it has reached since.//! - *trace* (the code's `EventSequence`): the fixed-capacity event list//! itself, open until finished as exhausted or incomplete.const capsule_owner = @import("capsule/root.zig");const fault_owner = @import("fault.zig");const input_owner = @import("input.zig");const model = @import("model.zig");const schedule_owner = @import("schedule.zig");const search_owner = @import("search/root.zig");const temporal_owner = @import("temporal.zig");const topology_owner = @import("topology.zig");pub const distributed = @import("distributed/root.zig");pub const query = @import("query/root.zig");pub const Bound = model.Bound;pub const BoundedLiveness = model.BoundedLiveness;pub const Capsule = capsule_owner.Capsule;pub const CapsuleBuildIdentity = capsule_owner.BuildIdentity;pub const CapsuleCapacity = capsule_owner.Capacity;pub const CapsuleError = capsule_owner.Error;pub const CapsuleExternalResult = capsule_owner.ExternalResult;pub const CapsuleFrame = capsule_owner.Frame;pub const CapsuleIdentity = capsule_owner.Identity;pub const CapsulePreparation = capsule_owner.Preparation;pub const CapsuleReducer = capsule_owner.Reducer;pub const CapsuleRejection = capsule_owner.Rejection;pub const CapsuleRejectionReason = capsule_owner.RejectionReason;pub const CapsuleReplay = capsule_owner.Replay;pub const CapsuleRunner = capsule_owner.Runner;pub const CapsuleStatistics = capsule_owner.Statistics;pub const ChoiceSite = model.ChoiceSite;pub const Diagnostic = model.Diagnostic;pub const EvaluationPattern = model.EvaluationPattern;pub const EvaluationReason = model.EvaluationReason;pub const Event = model.Event;pub const EventCapacity = model.EventCapacity;pub const EventClass = model.EventClass;pub const EventSequence = temporal_owner.Sequence;pub const EventValue = model.EventValue;pub const ExternalAdmission = model.ExternalAdmission;pub const FaultAction = model.FaultAction;pub const FaultAlternative = model.FaultAlternative;pub const FaultCapacity = model.FaultCapacity;pub const FaultGenerator = fault_owner.Generator;pub const FaultPlan = model.FaultPlan;pub const GeneratedFault = model.GeneratedFault;pub const GeneratedInput = model.GeneratedInput;pub const GeneratedSchedule = model.GeneratedSchedule;pub const GeneratedTopology = model.GeneratedTopology;pub const Generation = model.Generation;pub const GeneratorError = model.GeneratorError;pub const IncompleteReason = model.IncompleteReason;pub const InputAlternative = model.InputAlternative;pub const InputCapacity = model.InputCapacity;pub const InputGenerator = input_owner.Generator;pub const InputKind = model.InputKind;pub const InputPhase = model.InputPhase;pub const InputValue = model.InputValue;pub const Observation = model.Observation;pub const Operation = model.Operation;pub const OperationOutcome = model.OperationOutcome;pub const OperationPattern = model.OperationPattern;pub const Origin = model.Origin;pub const Pattern = model.Pattern;pub const PropertyDeclaration = model.PropertyDeclaration;pub const PropertyEvaluation = model.PropertyEvaluation;pub const PropertyKind = model.PropertyKind;pub const Safety = model.Safety;pub const ScheduleAlternative = model.ScheduleAlternative;pub const ScheduleCapacity = model.ScheduleCapacity;pub const ScheduleGenerator = schedule_owner.Generator;pub const ScheduleKind = model.ScheduleKind;pub const SchedulePlan = model.SchedulePlan;pub const ScheduleValue = model.ScheduleValue;pub const Search = search_owner.Search;pub const SearchBranch = search_owner.Branch;pub const SearchBranchId = search_owner.BranchId;pub const SearchBudget = search_owner.Budget;pub const SearchCandidate = search_owner.Candidate;pub const SearchCapacity = search_owner.Capacity;pub const SearchChoice = search_owner.Choice;pub const SearchDecision = search_owner.Decision;pub const SearchError = search_owner.Error;pub const SearchExhaustedReason = search_owner.ExhaustedReason;pub const SearchExecution = search_owner.Execution;pub const SearchExecutionIncomplete = search_owner.ExecutionIncomplete;pub const SearchExpansion = search_owner.Expansion;pub const SearchIncompleteReason = search_owner.IncompleteReason;pub const SearchOutcome = search_owner.Outcome;pub const SearchProgress = search_owner.Progress;pub const SearchPrunedReason = search_owner.PrunedReason;pub const SearchRunner = search_owner.Runner;pub const SearchSettlement = search_owner.Settlement;pub const Seed = model.Seed;pub const SeedDialect = model.SeedDialect;pub const SemanticId = model.SemanticId;pub const SiteId = model.SiteId;pub const Stream = model.Stream;pub const TemporalPlan = model.TemporalPlan;pub const TopologyAlternative = model.TopologyAlternative;pub const TopologyCapacity = model.TopologyCapacity;pub const TopologyGenerator = topology_owner.Generator;pub const TopologyKind = model.TopologyKind;pub const TopologyNode = model.TopologyNode;pub const TopologyPlan = model.TopologyPlan;pub const TopologyValue = model.TopologyValue;pub const TraceCompletion = model.TraceCompletion;pub const TraceState = model.TraceState;pub const Verdict = model.Verdict;pub const evaluateLiveness = temporal_owner.evaluateLiveness;pub const evaluateSafety = temporal_owner.evaluateSafety;pub const matchesEvent = temporal_owner.matches;pub const faultOrigin = model.faultOrigin;pub const origin = model.origin;Source: lib/machine/src/root.zig:67
pub const explore = @import("explore/root.zig");Complete call list for explore.Capsule
7 direct calls.
lib.machine.src.explore.capsule.owner.allZero[function] — private source atlib/machine/src/explore/capsule/owner.zig:439in nearest public ownerlib.machine.src.explore.capsule.ownerlib.machine.src.explore.capsule.owner.externalResult[function] — private source atlib/machine/src/explore/capsule/owner.zig:416in nearest public ownerlib.machine.src.explore.capsule.ownerlib.machine.src.explore.capsule.owner.momentFollows[function] — private source atlib/machine/src/explore/capsule/owner.zig:432in nearest public ownerlib.machine.src.explore.capsule.ownerlib.machine.src.explore.capsule.owner.rejected[function] — private source atlib/machine/src/explore/capsule/owner.zig:387in nearest public ownerlib.machine.src.explore.capsule.ownerlib.machine.src.explore.capsule.owner.requireCapacity[function] — private source atlib/machine/src/explore/capsule/owner.zig:444in nearest public ownerlib.machine.src.explore.capsule.ownerlib.machine.src.explore.capsule.owner.validateDecisionOrigins[function] — private source atlib/machine/src/explore/capsule/owner.zig:399in nearest public ownerlib.machine.src.explore.capsule.ownerlib.machine.src.explore.capsule.owner.validateFailureBranch[function] — private source atlib/machine/src/explore/capsule/owner.zig:373in nearest public ownerlib.machine.src.explore.capsule.owner
Audit
| Definitions | 104 |
|---|---|
| Public names | 108 |
| Members | 247 |
| Version | 26.7.0 |
| Revision | daab053ee433 |