@agentplat/mesh-sim 0.3.0-beta.2 → 0.3.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/README.md +447 -2
  2. package/dist/collective-closed-loop-action.d.ts +80 -0
  3. package/dist/collective-closed-loop-action.d.ts.map +1 -0
  4. package/dist/collective-closed-loop-action.js +471 -0
  5. package/dist/collective-closed-loop-action.js.map +1 -0
  6. package/dist/collective-closed-loop-contracts.d.ts +75 -0
  7. package/dist/collective-closed-loop-contracts.d.ts.map +1 -0
  8. package/dist/collective-closed-loop-contracts.js +420 -0
  9. package/dist/collective-closed-loop-contracts.js.map +1 -0
  10. package/dist/collective-closed-loop-fault-matrix.d.ts +101 -0
  11. package/dist/collective-closed-loop-fault-matrix.d.ts.map +1 -0
  12. package/dist/collective-closed-loop-fault-matrix.js +253 -0
  13. package/dist/collective-closed-loop-fault-matrix.js.map +1 -0
  14. package/dist/collective-closed-loop-recovery.d.ts +70 -0
  15. package/dist/collective-closed-loop-recovery.d.ts.map +1 -0
  16. package/dist/collective-closed-loop-recovery.js +823 -0
  17. package/dist/collective-closed-loop-recovery.js.map +1 -0
  18. package/dist/collective-closed-loop-reference.d.ts +54 -0
  19. package/dist/collective-closed-loop-reference.d.ts.map +1 -0
  20. package/dist/collective-closed-loop-reference.js +1696 -0
  21. package/dist/collective-closed-loop-reference.js.map +1 -0
  22. package/dist/collective-closed-loop-replanning.d.ts +27 -0
  23. package/dist/collective-closed-loop-replanning.d.ts.map +1 -0
  24. package/dist/collective-closed-loop-replanning.js +151 -0
  25. package/dist/collective-closed-loop-replanning.js.map +1 -0
  26. package/dist/collective-closed-loop-resilience-campaign.d.ts +22 -0
  27. package/dist/collective-closed-loop-resilience-campaign.d.ts.map +1 -0
  28. package/dist/collective-closed-loop-resilience-campaign.js +196 -0
  29. package/dist/collective-closed-loop-resilience-campaign.js.map +1 -0
  30. package/dist/collective-closed-loop-resilience-contracts.d.ts +138 -0
  31. package/dist/collective-closed-loop-resilience-contracts.d.ts.map +1 -0
  32. package/dist/collective-closed-loop-resilience-contracts.js +677 -0
  33. package/dist/collective-closed-loop-resilience-contracts.js.map +1 -0
  34. package/dist/collective-closed-loop-runner.d.ts +133 -0
  35. package/dist/collective-closed-loop-runner.d.ts.map +1 -0
  36. package/dist/collective-closed-loop-runner.js +1429 -0
  37. package/dist/collective-closed-loop-runner.js.map +1 -0
  38. package/dist/collective-closed-loop-runtime.d.ts +95 -0
  39. package/dist/collective-closed-loop-runtime.d.ts.map +1 -0
  40. package/dist/collective-closed-loop-runtime.js +1192 -0
  41. package/dist/collective-closed-loop-runtime.js.map +1 -0
  42. package/dist/collective-effect-provenance.d.ts +5 -0
  43. package/dist/collective-effect-provenance.d.ts.map +1 -0
  44. package/dist/collective-effect-provenance.js +32 -0
  45. package/dist/collective-effect-provenance.js.map +1 -0
  46. package/dist/collective-environment.d.ts +49 -0
  47. package/dist/collective-environment.d.ts.map +1 -0
  48. package/dist/collective-environment.js +907 -0
  49. package/dist/collective-environment.js.map +1 -0
  50. package/dist/collective-progressive-scale-overlay.d.ts +39 -0
  51. package/dist/collective-progressive-scale-overlay.d.ts.map +1 -0
  52. package/dist/collective-progressive-scale-overlay.js +147 -0
  53. package/dist/collective-progressive-scale-overlay.js.map +1 -0
  54. package/dist/collective-progressive-scale.d.ts +202 -0
  55. package/dist/collective-progressive-scale.d.ts.map +1 -0
  56. package/dist/collective-progressive-scale.js +755 -0
  57. package/dist/collective-progressive-scale.js.map +1 -0
  58. package/dist/collective-statistical-campaign-aggregation.d.ts +97 -0
  59. package/dist/collective-statistical-campaign-aggregation.d.ts.map +1 -0
  60. package/dist/collective-statistical-campaign-aggregation.js +446 -0
  61. package/dist/collective-statistical-campaign-aggregation.js.map +1 -0
  62. package/dist/collective-statistical-campaign-artifact-stream.d.ts +66 -0
  63. package/dist/collective-statistical-campaign-artifact-stream.d.ts.map +1 -0
  64. package/dist/collective-statistical-campaign-artifact-stream.js +207 -0
  65. package/dist/collective-statistical-campaign-artifact-stream.js.map +1 -0
  66. package/dist/collective-statistical-campaign-bundle.d.ts +151 -0
  67. package/dist/collective-statistical-campaign-bundle.d.ts.map +1 -0
  68. package/dist/collective-statistical-campaign-bundle.js +885 -0
  69. package/dist/collective-statistical-campaign-bundle.js.map +1 -0
  70. package/dist/collective-statistical-campaign-executor.d.ts +131 -0
  71. package/dist/collective-statistical-campaign-executor.d.ts.map +1 -0
  72. package/dist/collective-statistical-campaign-executor.js +856 -0
  73. package/dist/collective-statistical-campaign-executor.js.map +1 -0
  74. package/dist/collective-statistical-campaign-normative-analysis.d.ts +85 -0
  75. package/dist/collective-statistical-campaign-normative-analysis.d.ts.map +1 -0
  76. package/dist/collective-statistical-campaign-normative-analysis.js +483 -0
  77. package/dist/collective-statistical-campaign-normative-analysis.js.map +1 -0
  78. package/dist/collective-statistical-campaign-normative-operation.d.ts +89 -0
  79. package/dist/collective-statistical-campaign-normative-operation.d.ts.map +1 -0
  80. package/dist/collective-statistical-campaign-normative-operation.js +266 -0
  81. package/dist/collective-statistical-campaign-normative-operation.js.map +1 -0
  82. package/dist/collective-statistical-campaign-normative-registry.d.ts +24 -0
  83. package/dist/collective-statistical-campaign-normative-registry.d.ts.map +1 -0
  84. package/dist/collective-statistical-campaign-normative-registry.js +163 -0
  85. package/dist/collective-statistical-campaign-normative-registry.js.map +1 -0
  86. package/dist/collective-statistical-campaign-registered-adapter.d.ts +16 -0
  87. package/dist/collective-statistical-campaign-registered-adapter.d.ts.map +1 -0
  88. package/dist/collective-statistical-campaign-registered-adapter.js +518 -0
  89. package/dist/collective-statistical-campaign-registered-adapter.js.map +1 -0
  90. package/dist/collective-statistical-campaign-scale.d.ts +85 -0
  91. package/dist/collective-statistical-campaign-scale.d.ts.map +1 -0
  92. package/dist/collective-statistical-campaign-scale.js +349 -0
  93. package/dist/collective-statistical-campaign-scale.js.map +1 -0
  94. package/dist/collective-trace-journal.d.ts +20 -0
  95. package/dist/collective-trace-journal.d.ts.map +1 -0
  96. package/dist/collective-trace-journal.js +105 -0
  97. package/dist/collective-trace-journal.js.map +1 -0
  98. package/dist/index.d.ts +25 -0
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +25 -0
  101. package/dist/index.js.map +1 -1
  102. package/dist/multi-domain-environment-contracts.d.ts +122 -0
  103. package/dist/multi-domain-environment-contracts.d.ts.map +1 -0
  104. package/dist/multi-domain-environment-contracts.js +14 -0
  105. package/dist/multi-domain-environment-contracts.js.map +1 -0
  106. package/dist/multi-domain-environment-runtime.d.ts +25 -0
  107. package/dist/multi-domain-environment-runtime.d.ts.map +1 -0
  108. package/dist/multi-domain-environment-runtime.js +493 -0
  109. package/dist/multi-domain-environment-runtime.js.map +1 -0
  110. package/dist/multi-domain-environment-validation.d.ts +33 -0
  111. package/dist/multi-domain-environment-validation.d.ts.map +1 -0
  112. package/dist/multi-domain-environment-validation.js +415 -0
  113. package/dist/multi-domain-environment-validation.js.map +1 -0
  114. package/dist/multi-domain-environment.d.ts +4 -0
  115. package/dist/multi-domain-environment.d.ts.map +1 -0
  116. package/dist/multi-domain-environment.js +4 -0
  117. package/dist/multi-domain-environment.js.map +1 -0
  118. package/dist/reducer-scenario.d.ts.map +1 -1
  119. package/dist/reducer-scenario.js +67 -19
  120. package/dist/reducer-scenario.js.map +1 -1
  121. package/dist/scalable-evaluation-contracts.d.ts +835 -0
  122. package/dist/scalable-evaluation-contracts.d.ts.map +1 -0
  123. package/dist/scalable-evaluation-contracts.js +22 -0
  124. package/dist/scalable-evaluation-contracts.js.map +1 -0
  125. package/dist/scalable-evaluation-durability.d.ts +16 -0
  126. package/dist/scalable-evaluation-durability.d.ts.map +1 -0
  127. package/dist/scalable-evaluation-durability.js +339 -0
  128. package/dist/scalable-evaluation-durability.js.map +1 -0
  129. package/dist/scalable-evaluation-evidence.d.ts +93 -0
  130. package/dist/scalable-evaluation-evidence.d.ts.map +1 -0
  131. package/dist/scalable-evaluation-evidence.js +470 -0
  132. package/dist/scalable-evaluation-evidence.js.map +1 -0
  133. package/dist/scalable-evaluation-reference-integrated.d.ts +87 -0
  134. package/dist/scalable-evaluation-reference-integrated.d.ts.map +1 -0
  135. package/dist/scalable-evaluation-reference-integrated.js +1318 -0
  136. package/dist/scalable-evaluation-reference-integrated.js.map +1 -0
  137. package/dist/scalable-evaluation-runner.d.ts +8 -0
  138. package/dist/scalable-evaluation-runner.d.ts.map +1 -0
  139. package/dist/scalable-evaluation-runner.js +1810 -0
  140. package/dist/scalable-evaluation-runner.js.map +1 -0
  141. package/dist/scalable-evaluation-runtime.d.ts +86 -0
  142. package/dist/scalable-evaluation-runtime.d.ts.map +1 -0
  143. package/dist/scalable-evaluation-runtime.js +1310 -0
  144. package/dist/scalable-evaluation-runtime.js.map +1 -0
  145. package/dist/scalable-evaluation-validation.d.ts +59 -0
  146. package/dist/scalable-evaluation-validation.d.ts.map +1 -0
  147. package/dist/scalable-evaluation-validation.js +377 -0
  148. package/dist/scalable-evaluation-validation.js.map +1 -0
  149. package/dist/scalable-evaluation.d.ts +8 -0
  150. package/dist/scalable-evaluation.d.ts.map +1 -0
  151. package/dist/scalable-evaluation.js +8 -0
  152. package/dist/scalable-evaluation.js.map +1 -0
  153. package/dist/sharded-simulation-contracts.d.ts +240 -0
  154. package/dist/sharded-simulation-contracts.d.ts.map +1 -0
  155. package/dist/sharded-simulation-contracts.js +199 -0
  156. package/dist/sharded-simulation-contracts.js.map +1 -0
  157. package/dist/sharded-simulation-runtime.d.ts +64 -0
  158. package/dist/sharded-simulation-runtime.d.ts.map +1 -0
  159. package/dist/sharded-simulation-runtime.js +755 -0
  160. package/dist/sharded-simulation-runtime.js.map +1 -0
  161. package/dist/sharded-simulation-validation.d.ts +10 -0
  162. package/dist/sharded-simulation-validation.d.ts.map +1 -0
  163. package/dist/sharded-simulation-validation.js +250 -0
  164. package/dist/sharded-simulation-validation.js.map +1 -0
  165. package/dist/sharded-simulation.d.ts +46 -0
  166. package/dist/sharded-simulation.d.ts.map +1 -0
  167. package/dist/sharded-simulation.js +32 -0
  168. package/dist/sharded-simulation.js.map +1 -0
  169. package/dist/verified-benchmark-contracts.d.ts +132 -0
  170. package/dist/verified-benchmark-contracts.d.ts.map +1 -0
  171. package/dist/verified-benchmark-contracts.js +2 -0
  172. package/dist/verified-benchmark-contracts.js.map +1 -0
  173. package/dist/verified-benchmark-runtime.d.ts +20 -0
  174. package/dist/verified-benchmark-runtime.d.ts.map +1 -0
  175. package/dist/verified-benchmark-runtime.js +199 -0
  176. package/dist/verified-benchmark-runtime.js.map +1 -0
  177. package/dist/verified-benchmark-validation.d.ts +64 -0
  178. package/dist/verified-benchmark-validation.d.ts.map +1 -0
  179. package/dist/verified-benchmark-validation.js +481 -0
  180. package/dist/verified-benchmark-validation.js.map +1 -0
  181. package/dist/verified-benchmark.d.ts +4 -0
  182. package/dist/verified-benchmark.d.ts.map +1 -0
  183. package/dist/verified-benchmark.js +4 -0
  184. package/dist/verified-benchmark.js.map +1 -0
  185. package/package.json +18 -8
@@ -0,0 +1,1429 @@
1
+ import { createPlanningReducerCommandV1, createPlanningReducerStateV1, deepFreezePlanning, digestPlanningJsonV1, reducePlanningCommandV1, } from "@agentplat/collective-planning";
2
+ import { createCollectiveEnvironmentAdvanceRequestV1, createCollectiveEnvironmentInitializationV1, createCollectiveEnvironmentObservationRequestV1, } from "@agentplat/collective-planning/evaluation";
3
+ import { createCollectiveClosedLoopRunResultV1, validateCollectiveClosedLoopDefinitionV1, validateCollectiveCentralizedPlanningDecisionContextV1, validateCollectivePlanningDecisionContextV1, validateCollectivePlanningDecisionV1, } from "./collective-closed-loop-contracts.js";
4
+ import { COLLECTIVE_CLOSED_LOOP_RESILIENCE_LIMITS_V1, createCollectiveClosedLoopResilienceCampaignEvidenceV1, createCollectiveClosedLoopResilienceResultV1, validateCollectiveClosedLoopResilienceCampaignEvidenceForResultV1, validateCollectiveClosedLoopResilienceDefinitionV1, validateCollectiveClosedLoopResilienceResultForDefinitionV1, } from "./collective-closed-loop-resilience-contracts.js";
5
+ import { createCollectiveDeterministicEnvironmentHarnessV1, } from "./collective-environment.js";
6
+ import { runCollectiveClosedLoopActionV1, } from "./collective-closed-loop-action.js";
7
+ import { runCollectiveClosedLoopMeshRuntimeV1, } from "./collective-closed-loop-runtime.js";
8
+ import { recoverCollectiveClosedLoopAssignmentV1, } from "./collective-closed-loop-recovery.js";
9
+ import { runCollectiveClosedLoopFaultMatrixV1, } from "./collective-closed-loop-fault-matrix.js";
10
+ import { bindCollectiveTraceJournalV2, collectiveTraceJournalForOwnerV2, createCollectiveTraceJournalV2, } from "./collective-trace-journal.js";
11
+ import { bindCollectiveFinalizerEnvironmentV1 } from "./collective-effect-provenance.js";
12
+ const collectiveClosedLoopFaultMatrixRegistrations = new WeakMap();
13
+ export function createCollectiveClosedLoopFaultMatrixMissionBindingV1(input) {
14
+ if (!input ||
15
+ typeof input !== "object" ||
16
+ !input.preEffect ||
17
+ input.preEffect.schemaVersion !== 1 ||
18
+ typeof input.replacementPeerId !== "string" ||
19
+ input.replacementPeerId.length === 0)
20
+ throw new TypeError("closed_loop_fault_matrix_mission_binding_invalid");
21
+ return deepFreezePlanning({
22
+ schemaVersion: 1,
23
+ planningStateRoot: digestPlanningStates(input.preEffect.planningStates),
24
+ meshStateRoot: digestMeshStates(input.preEffect.meshStates),
25
+ workContractDigest: input.preEffect.workContract.workContractDigest,
26
+ checkpointDigest: digestValue(input.preEffect.checkpoint),
27
+ assignmentEpoch: input.preEffect.execution.assignmentEpoch,
28
+ assignmentAuthorityId: input.preEffect.execution.assignmentAuthorityId,
29
+ winnerPeerId: input.preEffect.winnerPeerId,
30
+ replacementPeerId: input.replacementPeerId,
31
+ });
32
+ }
33
+ export function createCollectiveClosedLoopFaultMatrixPortV1(input, binding) {
34
+ const ownedBinding = validateFaultMatrixMissionBinding(binding);
35
+ const ownedInput = Object.freeze({
36
+ schemaVersion: 1,
37
+ scenario: structuredClone(input.scenario),
38
+ runtime: Object.freeze({ ...input.runtime }),
39
+ faults: structuredClone(input.faults),
40
+ observations: Object.freeze(input.observations.map((observation) => Object.freeze({
41
+ faultId: observation.faultId,
42
+ observe: observation.observe,
43
+ }))),
44
+ });
45
+ const port = Object.freeze({
46
+ schemaVersion: 1,
47
+ bindingDigest: digestValue(ownedBinding),
48
+ });
49
+ const events = Object.freeze(ownedInput.scenario.events.map((event) => Object.freeze({
50
+ eventId: event.eventId,
51
+ targetPeerId: event.targetPeerId,
52
+ logicalTime: event.logicalTime,
53
+ })));
54
+ const simulationFaults = Object.freeze(ownedInput.scenario.faultPlan.faults.map((fault) => Object.freeze({
55
+ faultId: fault.faultId,
56
+ kind: fault.kind,
57
+ logicalTime: fault.logicalTime,
58
+ peerId: "peerId" in fault ? fault.peerId : null,
59
+ links: "links" in fault
60
+ ? Object.freeze(fault.links.map((link) => Object.freeze({
61
+ fromPeerId: link.fromPeerId,
62
+ toPeerId: link.toPeerId,
63
+ })))
64
+ : Object.freeze([]),
65
+ })));
66
+ collectiveClosedLoopFaultMatrixRegistrations.set(port, {
67
+ binding: ownedBinding,
68
+ async execute() {
69
+ return runCollectiveClosedLoopFaultMatrixV1(ownedInput);
70
+ },
71
+ faults: Object.freeze([...ownedInput.faults]),
72
+ events,
73
+ simulationFaults,
74
+ });
75
+ return port;
76
+ }
77
+ export async function runCollectiveClosedLoopFaultMatrixPortV1(port) {
78
+ const registration = collectiveClosedLoopFaultMatrixRegistrations.get(port);
79
+ if (!registration)
80
+ throw new TypeError("closed_loop_fault_matrix_port_not_registered");
81
+ return registration.execute();
82
+ }
83
+ /**
84
+ * Creates the evaluator-owned environment boundary. The returned runner-facing
85
+ * object exposes only the environment port and terminal evidence finalization;
86
+ * the hidden definition and monitor never enter a decision-policy context.
87
+ */
88
+ export function createCollectiveClosedLoopEvaluatorV1(input) {
89
+ const journal = createCollectiveTraceJournalV2(input.registration);
90
+ const ownedConfig = {
91
+ schemaVersion: 1,
92
+ registration: input.registration,
93
+ monitorPolicy: input.monitorPolicy,
94
+ definition: input.definition,
95
+ };
96
+ bindCollectiveTraceJournalV2(ownedConfig, journal);
97
+ const harness = createCollectiveDeterministicEnvironmentHarnessV1(ownedConfig);
98
+ bindCollectiveTraceJournalV2(harness.environment, journal);
99
+ return Object.freeze({
100
+ schemaVersion: 1,
101
+ environment: harness.environment,
102
+ finalize(publicArtifacts) {
103
+ return harness.finalize(publicArtifacts);
104
+ },
105
+ });
106
+ }
107
+ export function runAdaptiveCollectiveClosedLoopV1(input) {
108
+ return runClosedLoop(input, "adaptive_collective");
109
+ }
110
+ export function runCentralizedPlannerClosedLoopV1(input) {
111
+ return runClosedLoop(input, "centralized_planner");
112
+ }
113
+ export async function replayAdaptiveCollectiveClosedLoopV1(input) {
114
+ return replayClosedLoop(input, runAdaptiveCollectiveClosedLoopV1);
115
+ }
116
+ export async function replayCentralizedPlannerClosedLoopV1(input) {
117
+ return replayClosedLoop(input, runCentralizedPlannerClosedLoopV1);
118
+ }
119
+ export function runAdaptiveCollectiveClosedLoopResilienceV1(input) {
120
+ return runResilientClosedLoop(input, "adaptive_collective");
121
+ }
122
+ export function runCentralizedPlannerClosedLoopResilienceV1(input) {
123
+ return runResilientClosedLoop(input, "centralized_planner");
124
+ }
125
+ export async function replayAdaptiveCollectiveClosedLoopResilienceV1(input) {
126
+ return replayResilientClosedLoop(input, runAdaptiveCollectiveClosedLoopResilienceV1);
127
+ }
128
+ export async function replayCentralizedPlannerClosedLoopResilienceV1(input) {
129
+ return replayResilientClosedLoop(input, runCentralizedPlannerClosedLoopResilienceV1);
130
+ }
131
+ async function replayResilientClosedLoop(input, execute) {
132
+ assertExactRecord(input, ["schemaVersion", "createInput"], "closed-loop resilience replay input");
133
+ if (input.schemaVersion !== 1 || typeof input.createInput !== "function")
134
+ throw new TypeError("closed_loop_resilience_replay_input_invalid");
135
+ const first = await execute(await input.createInput());
136
+ const replay = await execute(await input.createInput());
137
+ const matched = first.resilience.resilienceResultDigest ===
138
+ replay.resilience.resilienceResultDigest &&
139
+ first.campaignEvidence.campaignEvidenceDigest ===
140
+ replay.campaignEvidence.campaignEvidenceDigest &&
141
+ first.trace.traceDigest === replay.trace.traceDigest &&
142
+ first.evidence.evidenceDigest === replay.evidence.evidenceDigest &&
143
+ first.faultMatrixBindingDigest === replay.faultMatrixBindingDigest &&
144
+ first.faultMatrix.matrixDigest === replay.faultMatrix.matrixDigest;
145
+ if (!matched)
146
+ throw new Error("closed_loop_resilience_replay_diverged");
147
+ return Object.freeze({ schemaVersion: 1, matched, first, replay });
148
+ }
149
+ async function replayClosedLoop(input, execute) {
150
+ if (input.schemaVersion !== 1 || typeof input.createInput !== "function")
151
+ throw new TypeError("closed_loop_replay_input_invalid");
152
+ const first = await execute(await input.createInput());
153
+ const replay = await execute(await input.createInput());
154
+ const matched = first.run.runDigest === replay.run.runDigest &&
155
+ first.trace.traceDigest === replay.trace.traceDigest &&
156
+ first.evidence.evidenceDigest === replay.evidence.evidenceDigest;
157
+ if (!matched)
158
+ throw new Error("closed_loop_replay_diverged");
159
+ return Object.freeze({ schemaVersion: 1, matched, first, replay });
160
+ }
161
+ async function runResilientClosedLoop(rawInput, runner) {
162
+ const input = validateResilienceExecutionInput(rawInput, runner);
163
+ const definition = validateCollectiveClosedLoopResilienceDefinitionV1(input.definition);
164
+ const nominalDefinition = definition.nominalDefinition;
165
+ const registration = nominalDefinition.registration;
166
+ const journal = journalFor(input.evaluator.environment);
167
+ const initialization = input.evaluator.environment.initialize(createCollectiveEnvironmentInitializationV1({
168
+ schemaVersion: 1,
169
+ initializationId: `closed-loop-resilience:${runner}:initialize`,
170
+ registration,
171
+ initializedAtLogicalMs: 0,
172
+ }));
173
+ if (initialization.status !== "initialized" &&
174
+ initialization.status !== "idempotent")
175
+ throw new Error(`closed_loop_environment_${initialization.reasonCode}`);
176
+ // Policies receive the nominal definition only. Fault schedules remain on
177
+ // the construction side of the runner and cannot become hidden look-ahead.
178
+ const nominalInput = {
179
+ schemaVersion: 1,
180
+ definition: nominalDefinition,
181
+ evaluator: input.evaluator,
182
+ runtime: input.runtime,
183
+ decisionPolicy: input.decisionPolicy,
184
+ actionClass: input.actionClass,
185
+ resultDigest: input.resultDigest,
186
+ resultSummary: input.resultSummary,
187
+ prepareAction: input.prepareAction,
188
+ };
189
+ const initialObservationBatch = collectObservations(nominalInput, 0);
190
+ const observations = initialObservationBatch.observations;
191
+ const owner = nominalDefinition.peers[0];
192
+ let planningProposal;
193
+ if (runner === "centralized_planner") {
194
+ appendCentralizedDirectives(journal, observations, 0);
195
+ planningProposal = await collectCentralizedDecision(nominalInput, observations, journal, owner.peerId, owner.peerInstanceId, 0);
196
+ }
197
+ else {
198
+ const decisions = await collectDecisions(nominalInput, observations, journal, 0);
199
+ const ownerDecision = decisions.get(owner.peerId);
200
+ if (!ownerDecision || ownerDecision.kind !== "proposal")
201
+ throw new Error("closed_loop_owner_proposal_required");
202
+ planningProposal = ownerDecision.proposal;
203
+ }
204
+ const instrumentedRuntime = Object.freeze({
205
+ signer: input.runtime.signer,
206
+ resolver: input.runtime.resolver,
207
+ cryptoPolicy: input.runtime.cryptoPolicy,
208
+ crypto: input.runtime.crypto,
209
+ privateKeys: input.runtime.privateKeys,
210
+ mandateVerification: input.runtime.mandateVerification,
211
+ });
212
+ bindCollectiveTraceJournalV2(instrumentedRuntime, journal);
213
+ const preEffect = await runCollectiveClosedLoopMeshRuntimeV1({
214
+ schemaVersion: 1,
215
+ missionIntent: nominalDefinition.missionIntent,
216
+ selectionPolicy: nominalDefinition.selectionPolicy,
217
+ mandate: nominalDefinition.mandate,
218
+ peers: nominalDefinition.peers,
219
+ observations,
220
+ planningProposal,
221
+ planningMode: runner,
222
+ runner: instrumentedRuntime,
223
+ seed: registration.seed,
224
+ maximumLogicalTimeMs: Math.min(nominalDefinition.maximumLogicalTimeMs, registration.limits.maximumInteractions),
225
+ });
226
+ bindCollectiveFinalizerEnvironmentV1(preEffect, input.evaluator.environment);
227
+ appendRuntimeEvidence(journal, preEffect);
228
+ const checkpointAdvanced = input.evaluator.environment.advance(createCollectiveEnvironmentAdvanceRequestV1({
229
+ schemaVersion: 1,
230
+ advanceId: `closed-loop-resilience:${runner}:checkpoint-time`,
231
+ registrationDigest: registration.bindingDigest,
232
+ targetLogicalTimeMs: preEffect.logicalTimeMs,
233
+ }));
234
+ if (checkpointAdvanced.status !== "advanced" &&
235
+ checkpointAdvanced.status !== "idempotent")
236
+ throw new Error(`closed_loop_environment_${checkpointAdvanced.reasonCode}`);
237
+ const checkpointObservationBatch = collectObservations(nominalInput, preEffect.logicalTimeMs, initialObservationBatch.cursors, "checkpoint");
238
+ const faultBeforePreEffect = definition.faultPlan.faults.find((fault) => fault.trigger.logicalTimeMs < preEffect.logicalTimeMs);
239
+ if (faultBeforePreEffect)
240
+ throw new Error(`closed_loop_fault_trigger_before_pre_effect:${faultBeforePreEffect.faultId}`);
241
+ const faultMatrixRegistration = bindFaultMatrixPortToPreEffect(input.faultMatrix, preEffect, input.replacementPeerId);
242
+ const faultMatrix = await faultMatrixRegistration.execute();
243
+ const faultRecords = bindFaultMatrixToPlan(definition, faultMatrix, faultMatrixRegistration);
244
+ let faultLogicalTimeMs = preEffect.logicalTimeMs;
245
+ const faultObservations = definition.faultPlan.faults.map((fault) => {
246
+ const record = faultRecords.get(fault.faultId);
247
+ assertFaultCausalTrigger(journal, fault);
248
+ faultLogicalTimeMs = Math.max(faultLogicalTimeMs, fault.trigger.logicalTimeMs);
249
+ const scheduled = appendFaultEvidence(journal, faultLogicalTimeMs, fault, record, "fault.scheduled", "accepted", record.scheduledEventDigest);
250
+ const injected = appendFaultEvidence(journal, faultLogicalTimeMs, fault, record, "fault.injected", "accepted", record.injectedEventDigest);
251
+ const observed = appendFaultEvidence(journal, faultLogicalTimeMs, fault, record, "fault.observed", "observed", record.observedEventDigest);
252
+ return {
253
+ schemaVersion: 1,
254
+ faultId: fault.faultId,
255
+ scheduledEventDigest: scheduled.eventDigest,
256
+ injectedEventDigest: injected.eventDigest,
257
+ observedEventDigest: observed.eventDigest,
258
+ };
259
+ });
260
+ const recoveryCrashFault = definition.faultPlan.faults.find((fault) => fault.family === "peer.crash" &&
261
+ fault.targets.length === 1 &&
262
+ fault.targets[0]?.peerId === preEffect.winnerPeerId);
263
+ const recoveryRestartFault = definition.faultPlan.faults.find((fault) => fault.family === "peer.restart" &&
264
+ fault.targets.length === 1 &&
265
+ fault.targets[0]?.peerId === preEffect.winnerPeerId &&
266
+ recoveryCrashFault !== undefined &&
267
+ fault.causalPredecessorFaultIds.includes(recoveryCrashFault.faultId));
268
+ if (!recoveryCrashFault || !recoveryRestartFault)
269
+ throw new Error("closed_loop_recovery_fault_cause_missing");
270
+ const recovery = await recoverCollectiveClosedLoopAssignmentV1({
271
+ schemaVersion: 1,
272
+ preEffect,
273
+ peers: nominalDefinition.peers,
274
+ runner: instrumentedRuntime,
275
+ missionIntent: nominalDefinition.missionIntent,
276
+ mandate: nominalDefinition.mandate,
277
+ failedWinnerPeerId: preEffect.winnerPeerId,
278
+ replacementPeerId: input.replacementPeerId,
279
+ faultLogicalTimeMs: Math.max(faultLogicalTimeMs + 1, preEffect.logicalTimeMs + 1),
280
+ });
281
+ appendRecoveryEvidence(journal, preEffect, recovery, faultMatrix.matrixDigest);
282
+ const staleFault = recoveryRestartFault;
283
+ const staleResultRejection = recovery.staleRejections.find((item) => item.recordType === "work.result");
284
+ if (!staleResultRejection)
285
+ throw new Error("closed_loop_stale_result_evidence_missing");
286
+ const staleFenceDigest = digestValue({
287
+ assignmentEpoch: preEffect.execution.assignmentEpoch,
288
+ assignmentAuthorityId: preEffect.execution.assignmentAuthorityId,
289
+ fencingToken: preEffect.execution.fencingToken,
290
+ });
291
+ const currentFenceDigest = digestValue({
292
+ assignmentEpoch: recovery.execution.assignmentEpoch,
293
+ assignmentAuthorityId: recovery.execution.assignmentAuthorityId,
294
+ fencingToken: recovery.execution.fencingToken,
295
+ });
296
+ const staleRejectionEvent = appendStaleResultRejectionEvidence(journal, preEffect, recovery, staleFault, staleResultRejection, staleFenceDigest, currentFenceDigest);
297
+ const actionLogicalTimeMs = recovery.recoveryLogicalTimeMs + 1;
298
+ if (actionLogicalTimeMs > nominalDefinition.maximumLogicalTimeMs)
299
+ throw new RangeError("closed_loop_logical_time_exceeded");
300
+ const advanced = input.evaluator.environment.advance(createCollectiveEnvironmentAdvanceRequestV1({
301
+ schemaVersion: 1,
302
+ advanceId: `closed-loop-resilience:${runner}:action-time`,
303
+ registrationDigest: registration.bindingDigest,
304
+ targetLogicalTimeMs: actionLogicalTimeMs,
305
+ }));
306
+ if (advanced.status !== "advanced" && advanced.status !== "idempotent")
307
+ throw new Error(`closed_loop_environment_${advanced.reasonCode}`);
308
+ const recoveredRoleBindingDigest = digestValue({
309
+ schemaVersion: 1,
310
+ kind: "closed-loop-recovered-role-binding",
311
+ priorRoleBindingDigest: preEffect.roleBinding.roleBindingDigest,
312
+ workContractDigest: recovery.workContract.workContractDigest,
313
+ });
314
+ const prepared = await input.prepareAction(deepFreezePlanning({
315
+ schemaVersion: 1,
316
+ winnerPeerId: recovery.replacementPeerId,
317
+ workContract: recovery.workContract,
318
+ roleBindingDigest: recoveredRoleBindingDigest,
319
+ logicalTimeMs: actionLogicalTimeMs,
320
+ }));
321
+ assertExactRecord(prepared, [
322
+ "actionBinding",
323
+ "actionInput",
324
+ "trustState",
325
+ "trustRequest",
326
+ "inferenceState",
327
+ "assessmentRequest",
328
+ "assessment",
329
+ "actionGrant",
330
+ "gatewayId",
331
+ "reservationId",
332
+ "permitId",
333
+ "decisionId",
334
+ ], "closed-loop prepared action");
335
+ const authorizedAt = actionWallTime(nominalDefinition.missionIntent.validFrom, nominalDefinition.missionIntent.validUntil, recovery.checkpoint.envelope.sentAt, actionLogicalTimeMs);
336
+ if (Date.parse(authorizedAt) < Date.parse(recovery.leaseStartsAt) ||
337
+ Date.parse(authorizedAt) >= Date.parse(recovery.execution.leaseExpiresAt))
338
+ throw new Error("closed_loop_recovery_lease_expired_before_action");
339
+ const action = await runCollectiveClosedLoopActionV1({
340
+ ...prepared,
341
+ mandate: nominalDefinition.mandate,
342
+ authorityState: preEffect.authorityState,
343
+ workContract: recovery.workContract,
344
+ mesh: currentMeshForWorkContract(recovery.workContract),
345
+ environment: input.evaluator.environment,
346
+ effect: {
347
+ registrationDigest: registration.bindingDigest,
348
+ missionIntentId: nominalDefinition.missionIntent.missionIntentId,
349
+ intentRevision: nominalDefinition.missionIntent.revision,
350
+ intentDigest: nominalDefinition.missionIntent.intentDigest,
351
+ actionClass: input.actionClass,
352
+ },
353
+ logicalTimeMs: actionLogicalTimeMs,
354
+ wallTime: authorizedAt,
355
+ });
356
+ if (!action.effectAttempt ||
357
+ !action.receipt ||
358
+ action.receipt.status !== "committed")
359
+ throw new Error("closed_loop_effect_not_committed");
360
+ const outcomeObservationBatch = collectObservations(nominalInput, actionLogicalTimeMs, checkpointObservationBatch.cursors, "outcome");
361
+ const allObservations = Object.freeze([
362
+ ...observations,
363
+ ...checkpointObservationBatch.observations,
364
+ ...outcomeObservationBatch.observations,
365
+ ]);
366
+ const finalized = await recovery.finalizeAfterCommittedEffect({
367
+ effectAttempt: action.effectAttempt,
368
+ effectReceipt: action.receipt,
369
+ resultDigest: input.resultDigest,
370
+ resultSummary: input.resultSummary,
371
+ });
372
+ appendRecoveredFinalizationEvidence(journal, finalized);
373
+ const publicArtifacts = publicArtifactsForRecovery(preEffect, recovery, action, finalized, input.faultMatrix.bindingDigest, faultMatrix, checkpointObservationBatch.observations, outcomeObservationBatch.observations);
374
+ const planningStateRoots = sortedRoots(Object.values(preEffect.planningStates).map((state) => state.planView.stateDigest));
375
+ const run = createCollectiveClosedLoopRunResultV1({
376
+ schemaVersion: 1,
377
+ registrationBindingDigest: registration.bindingDigest,
378
+ runner,
379
+ stopReason: "plan_completed",
380
+ finalLogicalTimeMs: finalized.logicalTimeMs,
381
+ planningStateRoots,
382
+ meshStateRoots: sortedRoots(Object.values(finalized.meshStates).map((state) => digestValue(state))),
383
+ governanceStateRoots: sortedRoots([
384
+ digestValue(preEffect.authorityState),
385
+ recovery.workContract.workContractDigest,
386
+ action.actionPermit.permitDigest,
387
+ action.executionStateDigest,
388
+ ]),
389
+ publicArtifacts,
390
+ });
391
+ const epochs = [
392
+ {
393
+ schemaVersion: 1,
394
+ epoch: 1,
395
+ startedAtLogicalMs: 0,
396
+ endedAtLogicalMs: recovery.recoveryLogicalTimeMs,
397
+ planningStateRoot: digestValue(planningStateRoots),
398
+ meshStateRoot: digestMeshStates(preEffect.meshStates),
399
+ governanceStateRoot: digestValue({
400
+ authorityState: preEffect.authorityState,
401
+ workContractDigest: preEffect.workContract.workContractDigest,
402
+ }),
403
+ },
404
+ {
405
+ schemaVersion: 1,
406
+ epoch: 2,
407
+ startedAtLogicalMs: recovery.recoveryLogicalTimeMs,
408
+ endedAtLogicalMs: finalized.logicalTimeMs,
409
+ planningStateRoot: digestValue(planningStateRoots),
410
+ meshStateRoot: digestMeshStates(finalized.meshStates),
411
+ governanceStateRoot: digestValue({
412
+ authorityState: preEffect.authorityState,
413
+ workContractDigest: recovery.workContract.workContractDigest,
414
+ permitDigest: action.actionPermit.permitDigest,
415
+ }),
416
+ },
417
+ ];
418
+ const resilience = validateCollectiveClosedLoopResilienceResultForDefinitionV1(createCollectiveClosedLoopResilienceResultV1({
419
+ schemaVersion: 1,
420
+ resilienceDefinitionDigest: definition.resilienceDefinitionDigest,
421
+ run,
422
+ epochs,
423
+ faultObservations,
424
+ staleResultRejections: [
425
+ {
426
+ schemaVersion: 1,
427
+ rejectionId: "stale-result-rejection:0001",
428
+ faultId: staleFault.faultId,
429
+ rejectedAtLogicalMs: staleResultRejection.logicalTimeMs,
430
+ staleFenceDigest,
431
+ currentFenceDigest,
432
+ rejectionEventDigest: staleRejectionEvent.eventDigest,
433
+ },
434
+ ],
435
+ }), definition);
436
+ const plannedFaultIds = definition.faultPlan.faults.map((fault) => fault.faultId);
437
+ const campaignEvidence = validateCollectiveClosedLoopResilienceCampaignEvidenceForResultV1(createCollectiveClosedLoopResilienceCampaignEvidenceV1({
438
+ schemaVersion: 1,
439
+ resilienceDefinitionDigest: definition.resilienceDefinitionDigest,
440
+ resilienceResultDigest: resilience.resilienceResultDigest,
441
+ runner,
442
+ seed: registration.seed,
443
+ limits: {
444
+ schemaVersion: 1,
445
+ maximumFaults: COLLECTIVE_CLOSED_LOOP_RESILIENCE_LIMITS_V1.maximumFaults,
446
+ maximumEpochs: definition.maximumEpochs,
447
+ maximumInteractions: registration.limits.maximumInteractions,
448
+ },
449
+ scheduledFaultIds: plannedFaultIds,
450
+ injectedFaultIds: plannedFaultIds,
451
+ observedFaultIds: plannedFaultIds,
452
+ staleResultRejectionIds: resilience.staleResultRejections.map((rejection) => rejection.rejectionId),
453
+ }), definition, resilience);
454
+ const evaluation = input.evaluator.finalize(publicArtifacts);
455
+ assertResilienceTerminalEvidence(evaluation, run, finalized, faultMatrix);
456
+ return Object.freeze({
457
+ schemaVersion: 1,
458
+ resilience,
459
+ campaignEvidence,
460
+ trace: evaluation.trace,
461
+ evidence: evaluation.evidence,
462
+ observations: allObservations,
463
+ preEffect,
464
+ recovery,
465
+ action,
466
+ finalized,
467
+ faultMatrixBindingDigest: input.faultMatrix.bindingDigest,
468
+ faultMatrix,
469
+ });
470
+ }
471
+ async function runClosedLoop(rawInput, runner) {
472
+ const input = validateExecutionInput(rawInput, runner);
473
+ const definition = validateCollectiveClosedLoopDefinitionV1(input.definition);
474
+ const registration = definition.registration;
475
+ const journal = journalFor(input.evaluator.environment);
476
+ const initialization = input.evaluator.environment.initialize(createCollectiveEnvironmentInitializationV1({
477
+ schemaVersion: 1,
478
+ initializationId: `closed-loop:${runner}:initialize`,
479
+ registration,
480
+ initializedAtLogicalMs: 0,
481
+ }));
482
+ if (initialization.status !== "initialized" &&
483
+ initialization.status !== "idempotent")
484
+ throw new Error(`closed_loop_environment_${initialization.reasonCode}`);
485
+ const initialObservationBatch = collectObservations(input, 0);
486
+ const observations = initialObservationBatch.observations;
487
+ const owner = definition.peers[0];
488
+ let planningProposal;
489
+ if (runner === "centralized_planner") {
490
+ appendCentralizedDirectives(journal, observations, 0);
491
+ planningProposal = await collectCentralizedDecision(input, observations, journal, owner.peerId, owner.peerInstanceId, 0);
492
+ }
493
+ else {
494
+ const decisions = await collectDecisions(input, observations, journal, 0);
495
+ const ownerDecision = decisions.get(owner.peerId);
496
+ if (!ownerDecision || ownerDecision.kind !== "proposal")
497
+ throw new Error("closed_loop_owner_proposal_required");
498
+ planningProposal = ownerDecision.proposal;
499
+ }
500
+ const instrumentedRuntime = Object.freeze({
501
+ signer: input.runtime.signer,
502
+ resolver: input.runtime.resolver,
503
+ cryptoPolicy: input.runtime.cryptoPolicy,
504
+ crypto: input.runtime.crypto,
505
+ privateKeys: input.runtime.privateKeys,
506
+ mandateVerification: input.runtime.mandateVerification,
507
+ });
508
+ bindCollectiveTraceJournalV2(instrumentedRuntime, journal);
509
+ const preEffect = await runCollectiveClosedLoopMeshRuntimeV1({
510
+ schemaVersion: 1,
511
+ missionIntent: definition.missionIntent,
512
+ selectionPolicy: definition.selectionPolicy,
513
+ mandate: definition.mandate,
514
+ peers: definition.peers,
515
+ observations,
516
+ planningProposal,
517
+ planningMode: runner,
518
+ runner: instrumentedRuntime,
519
+ seed: registration.seed,
520
+ maximumLogicalTimeMs: Math.min(definition.maximumLogicalTimeMs, registration.limits.maximumInteractions),
521
+ });
522
+ bindCollectiveFinalizerEnvironmentV1(preEffect, input.evaluator.environment);
523
+ appendRuntimeEvidence(journal, preEffect);
524
+ const checkpointAdvanced = input.evaluator.environment.advance(createCollectiveEnvironmentAdvanceRequestV1({
525
+ schemaVersion: 1,
526
+ advanceId: `closed-loop:${runner}:checkpoint-time`,
527
+ registrationDigest: registration.bindingDigest,
528
+ targetLogicalTimeMs: preEffect.logicalTimeMs,
529
+ }));
530
+ if (checkpointAdvanced.status !== "advanced" &&
531
+ checkpointAdvanced.status !== "idempotent")
532
+ throw new Error(`closed_loop_environment_${checkpointAdvanced.reasonCode}`);
533
+ const checkpointObservationBatch = collectObservations(input, preEffect.logicalTimeMs, initialObservationBatch.cursors, "checkpoint");
534
+ const actionLogicalTimeMs = preEffect.logicalTimeMs + 1;
535
+ if (actionLogicalTimeMs > definition.maximumLogicalTimeMs)
536
+ throw new RangeError("closed_loop_logical_time_exceeded");
537
+ const advanced = input.evaluator.environment.advance(createCollectiveEnvironmentAdvanceRequestV1({
538
+ schemaVersion: 1,
539
+ advanceId: `closed-loop:${runner}:action-time`,
540
+ registrationDigest: registration.bindingDigest,
541
+ targetLogicalTimeMs: actionLogicalTimeMs,
542
+ }));
543
+ if (advanced.status !== "advanced" && advanced.status !== "idempotent")
544
+ throw new Error(`closed_loop_environment_${advanced.reasonCode}`);
545
+ const prepared = await input.prepareAction(deepFreezePlanning({
546
+ schemaVersion: 1,
547
+ winnerPeerId: preEffect.winnerPeerId,
548
+ workContract: preEffect.workContract,
549
+ roleBindingDigest: preEffect.roleBinding.roleBindingDigest,
550
+ logicalTimeMs: actionLogicalTimeMs,
551
+ }));
552
+ assertExactRecord(prepared, [
553
+ "actionBinding",
554
+ "actionInput",
555
+ "trustState",
556
+ "trustRequest",
557
+ "inferenceState",
558
+ "assessmentRequest",
559
+ "assessment",
560
+ "actionGrant",
561
+ "gatewayId",
562
+ "reservationId",
563
+ "permitId",
564
+ "decisionId",
565
+ ], "closed-loop prepared action");
566
+ const action = await runCollectiveClosedLoopActionV1({
567
+ ...prepared,
568
+ mandate: definition.mandate,
569
+ authorityState: preEffect.authorityState,
570
+ workContract: preEffect.workContract,
571
+ mesh: currentMesh(preEffect),
572
+ environment: input.evaluator.environment,
573
+ effect: {
574
+ registrationDigest: registration.bindingDigest,
575
+ missionIntentId: definition.missionIntent.missionIntentId,
576
+ intentRevision: definition.missionIntent.revision,
577
+ intentDigest: definition.missionIntent.intentDigest,
578
+ actionClass: input.actionClass,
579
+ },
580
+ logicalTimeMs: actionLogicalTimeMs,
581
+ wallTime: actionWallTime(definition.missionIntent.validFrom, definition.missionIntent.validUntil, preEffect.checkpoint.envelope.sentAt, actionLogicalTimeMs),
582
+ });
583
+ if (!action.effectAttempt ||
584
+ !action.receipt ||
585
+ action.receipt.status !== "committed")
586
+ throw new Error("closed_loop_effect_not_committed");
587
+ const outcomeObservationBatch = collectObservations(input, actionLogicalTimeMs, checkpointObservationBatch.cursors, "outcome");
588
+ const allObservations = Object.freeze([
589
+ ...observations,
590
+ ...checkpointObservationBatch.observations,
591
+ ...outcomeObservationBatch.observations,
592
+ ]);
593
+ const finalized = await preEffect.finalizeAfterCommittedEffect({
594
+ effectAttempt: action.effectAttempt,
595
+ effectReceipt: action.receipt,
596
+ resultDigest: input.resultDigest,
597
+ resultSummary: input.resultSummary,
598
+ });
599
+ appendFinalizationEvidence(journal, finalized);
600
+ const publicArtifacts = publicArtifactsFor(preEffect, action, finalized, checkpointObservationBatch.observations, outcomeObservationBatch.observations);
601
+ const run = createCollectiveClosedLoopRunResultV1({
602
+ schemaVersion: 1,
603
+ registrationBindingDigest: registration.bindingDigest,
604
+ runner,
605
+ stopReason: "plan_completed",
606
+ finalLogicalTimeMs: finalized.logicalTimeMs,
607
+ planningStateRoots: sortedRoots(Object.values(finalized.planningStates).map((state) => state.planView.stateDigest)),
608
+ meshStateRoots: sortedRoots(Object.values(finalized.meshStates).map((state) => digestValue(state))),
609
+ governanceStateRoots: sortedRoots([
610
+ digestValue(preEffect.authorityState),
611
+ preEffect.workContract.workContractDigest,
612
+ action.actionPermit.permitDigest,
613
+ action.executionStateDigest,
614
+ ]),
615
+ publicArtifacts,
616
+ });
617
+ const evaluation = input.evaluator.finalize(publicArtifacts);
618
+ assertTerminalEvidence(evaluation, run, finalized);
619
+ return Object.freeze({
620
+ schemaVersion: 1,
621
+ run,
622
+ trace: evaluation.trace,
623
+ evidence: evaluation.evidence,
624
+ observations: allObservations,
625
+ preEffect,
626
+ action,
627
+ finalized,
628
+ });
629
+ }
630
+ function validateExecutionInput(input, runner) {
631
+ assertExactRecord(input, [
632
+ "schemaVersion",
633
+ "definition",
634
+ "evaluator",
635
+ "runtime",
636
+ "decisionPolicy",
637
+ "actionClass",
638
+ "resultDigest",
639
+ "resultSummary",
640
+ "prepareAction",
641
+ ], "closed-loop execution input");
642
+ if (input.schemaVersion !== 1 ||
643
+ input.evaluator?.schemaVersion !== 1 ||
644
+ typeof input.evaluator.environment?.initialize !== "function" ||
645
+ typeof input.evaluator.environment?.observe !== "function" ||
646
+ typeof input.evaluator.environment?.applyEffect !== "function" ||
647
+ typeof input.evaluator.environment?.advance !== "function" ||
648
+ typeof input.evaluator.finalize !== "function" ||
649
+ typeof input.decisionPolicy?.decide !== "function" ||
650
+ typeof input.decisionPolicy?.decideCentralized !== "function" ||
651
+ typeof input.prepareAction !== "function" ||
652
+ !/^sha256:[0-9a-f]{64}$/u.test(input.resultDigest) ||
653
+ input.actionClass.length === 0 ||
654
+ input.resultSummary.length === 0)
655
+ throw new TypeError("closed_loop_execution_input_invalid");
656
+ const definition = validateCollectiveClosedLoopDefinitionV1(input.definition);
657
+ if (definition.registration.runner !== runner ||
658
+ definition.registration.stratum !== "nominal" ||
659
+ input.decisionPolicy.policyDigest !==
660
+ definition.registration.observationPolicyDigest ||
661
+ !Number.isSafeInteger(input.decisionPolicy.policyVersion) ||
662
+ input.decisionPolicy.policyVersion < 1 ||
663
+ input.decisionPolicy.policyId.length === 0)
664
+ throw new TypeError("closed_loop_runner_binding_invalid");
665
+ return input;
666
+ }
667
+ function validateResilienceExecutionInput(input, runner) {
668
+ assertExactRecord(input, [
669
+ "schemaVersion",
670
+ "definition",
671
+ "evaluator",
672
+ "runtime",
673
+ "decisionPolicy",
674
+ "faultMatrix",
675
+ "replacementPeerId",
676
+ "actionClass",
677
+ "resultDigest",
678
+ "resultSummary",
679
+ "prepareAction",
680
+ ], "closed-loop resilience execution input");
681
+ if (input.schemaVersion !== 1 ||
682
+ input.evaluator?.schemaVersion !== 1 ||
683
+ typeof input.evaluator.environment?.initialize !== "function" ||
684
+ typeof input.evaluator.environment?.observe !== "function" ||
685
+ typeof input.evaluator.environment?.applyEffect !== "function" ||
686
+ typeof input.evaluator.environment?.advance !== "function" ||
687
+ typeof input.evaluator.finalize !== "function" ||
688
+ input.faultMatrix?.schemaVersion !== 1 ||
689
+ !/^sha256:[0-9a-f]{64}$/u.test(input.faultMatrix.bindingDigest) ||
690
+ !collectiveClosedLoopFaultMatrixRegistrations.has(input.faultMatrix) ||
691
+ typeof input.decisionPolicy?.decide !== "function" ||
692
+ typeof input.decisionPolicy?.decideCentralized !== "function" ||
693
+ typeof input.prepareAction !== "function" ||
694
+ typeof input.replacementPeerId !== "string" ||
695
+ input.replacementPeerId.length === 0 ||
696
+ !/^sha256:[0-9a-f]{64}$/u.test(input.resultDigest) ||
697
+ input.actionClass.length === 0 ||
698
+ input.resultSummary.length === 0)
699
+ throw new TypeError("closed_loop_resilience_execution_input_invalid");
700
+ const definition = validateCollectiveClosedLoopResilienceDefinitionV1(input.definition);
701
+ if (definition.nominalDefinition.registration.runner !== runner ||
702
+ definition.nominalDefinition.registration.stratum !== "nominal" ||
703
+ input.decisionPolicy.policyDigest !==
704
+ definition.nominalDefinition.registration.observationPolicyDigest ||
705
+ !Number.isSafeInteger(input.decisionPolicy.policyVersion) ||
706
+ input.decisionPolicy.policyVersion < 1 ||
707
+ input.decisionPolicy.policyId.length === 0 ||
708
+ !definition.nominalDefinition.peers.some((peer) => peer.peerId === input.replacementPeerId))
709
+ throw new TypeError("closed_loop_resilience_runner_binding_invalid");
710
+ return input;
711
+ }
712
+ function bindFaultMatrixPortToPreEffect(port, preEffect, replacementPeerId) {
713
+ const registration = collectiveClosedLoopFaultMatrixRegistrations.get(port);
714
+ if (!registration)
715
+ throw new TypeError("closed_loop_fault_matrix_port_not_registered");
716
+ const expected = createCollectiveClosedLoopFaultMatrixMissionBindingV1({
717
+ preEffect,
718
+ replacementPeerId,
719
+ });
720
+ if (port.bindingDigest !== digestValue(registration.binding) ||
721
+ digestValue(registration.binding) !== digestValue(expected))
722
+ throw new Error("closed_loop_fault_matrix_mission_binding_mismatch");
723
+ return registration;
724
+ }
725
+ function bindFaultMatrixToPlan(definition, result, registration) {
726
+ const records = result.records;
727
+ const expectedDriverFaultIds = registration.faults
728
+ .filter((fault) => fault.injection.kind === "driver_fault")
729
+ .map((fault) => fault.injection.kind === "driver_fault"
730
+ ? fault.injection.simulationFaultId
731
+ : "")
732
+ .sort();
733
+ const configuredDriverFaultIds = registration.simulationFaults
734
+ .map((fault) => fault.faultId)
735
+ .sort();
736
+ const executedDriverFaultIds = result.trace.faults
737
+ .map((fault) => fault.faultId)
738
+ .sort();
739
+ if (result.schemaVersion !== 1 ||
740
+ result.scenarioDigest !== result.trace.configurationDigest ||
741
+ !/^sha256:[0-9a-f]{64}$/u.test(result.matrixDigest) ||
742
+ result.trace.records.length === 0 ||
743
+ registration.faults.length !== definition.faultPlan.faults.length ||
744
+ !sameStringSet(expectedDriverFaultIds, configuredDriverFaultIds) ||
745
+ !sameStringSet(expectedDriverFaultIds, executedDriverFaultIds) ||
746
+ result.trace.faults.some((fault) => !fault.applied))
747
+ throw new Error("closed_loop_fault_matrix_result_invalid");
748
+ if (records.length !== definition.faultPlan.faults.length)
749
+ throw new Error("closed_loop_fault_matrix_coverage_invalid");
750
+ const byId = new Map(records.map((record) => [record.faultId, record]));
751
+ if (byId.size !== records.length)
752
+ throw new Error("closed_loop_fault_matrix_duplicate_fault");
753
+ for (const fault of definition.faultPlan.faults) {
754
+ const record = byId.get(fault.faultId);
755
+ const declared = registration.faults.find((candidate) => candidate.faultId === fault.faultId);
756
+ if (!record ||
757
+ !declared ||
758
+ record.schemaVersion !== 1 ||
759
+ record.family !== fault.family ||
760
+ declared.family !== fault.family ||
761
+ declared.logicalTime !== fault.trigger.logicalTimeMs ||
762
+ declared.causalPredecessorFaultId !==
763
+ (fault.causalPredecessorFaultIds[0] ?? null) ||
764
+ fault.causalPredecessorFaultIds.length > 1 ||
765
+ record.observed !== true ||
766
+ !/^sha256:[0-9a-f]{64}$/u.test(record.scheduledEventDigest) ||
767
+ !/^sha256:[0-9a-f]{64}$/u.test(record.injectedEventDigest) ||
768
+ !/^sha256:[0-9a-f]{64}$/u.test(record.observedEventDigest))
769
+ throw new Error(`closed_loop_fault_matrix_binding_invalid:${fault.faultId}`);
770
+ const injected = result.trace.records.find((candidate) => candidate.eventId === record.injectedRecordId);
771
+ if (!injected ||
772
+ !injected.accepted ||
773
+ injected.order.logicalTime !== fault.trigger.logicalTimeMs)
774
+ throw new Error(`closed_loop_fault_matrix_trace_binding_invalid:${fault.faultId}`);
775
+ assertFaultMatrixTargetBinding(fault, declared, registration);
776
+ }
777
+ return byId;
778
+ }
779
+ function sameStringSet(left, right) {
780
+ return (left.length === right.length &&
781
+ new Set(left).size === left.length &&
782
+ new Set(right).size === right.length &&
783
+ left.every((value, index) => value === right[index]));
784
+ }
785
+ function assertFaultMatrixTargetBinding(fault, declared, registration) {
786
+ const injection = declared.injection;
787
+ if (injection.kind === "reducer_event") {
788
+ const event = registration.events.find((candidate) => candidate.eventId === injection.eventId);
789
+ if (!event ||
790
+ event.logicalTime !== fault.trigger.logicalTimeMs ||
791
+ fault.targets.length !== 1 ||
792
+ fault.targets[0]?.peerId !== event.targetPeerId ||
793
+ fault.links.length !== 0)
794
+ throw new Error(`closed_loop_fault_matrix_target_binding_invalid:${fault.faultId}`);
795
+ return;
796
+ }
797
+ const simulationFault = registration.simulationFaults.find((candidate) => candidate.faultId === injection.simulationFaultId);
798
+ const expectedKind = fault.family === "peer.restart" ? "peer.resume" : fault.family;
799
+ if (!simulationFault ||
800
+ simulationFault.kind !== expectedKind ||
801
+ simulationFault.logicalTime !== fault.trigger.logicalTimeMs)
802
+ throw new Error(`closed_loop_fault_matrix_driver_binding_invalid:${fault.faultId}`);
803
+ if (fault.family === "peer.crash" || fault.family === "peer.restart") {
804
+ if (fault.targets.length !== 1 ||
805
+ fault.targets[0]?.peerId !== simulationFault.peerId ||
806
+ fault.links.length !== 0)
807
+ throw new Error(`closed_loop_fault_matrix_target_binding_invalid:${fault.faultId}`);
808
+ return;
809
+ }
810
+ const plannedLinks = [...fault.links]
811
+ .map((link) => `${link.fromPeerId}\u0000${link.toPeerId}`)
812
+ .sort();
813
+ const actualLinks = [...simulationFault.links]
814
+ .map((link) => `${link.fromPeerId}\u0000${link.toPeerId}`)
815
+ .sort();
816
+ if (fault.targets.length !== 0 ||
817
+ plannedLinks.length !== actualLinks.length ||
818
+ plannedLinks.some((link, index) => link !== actualLinks[index]))
819
+ throw new Error(`closed_loop_fault_matrix_link_binding_invalid:${fault.faultId}`);
820
+ }
821
+ function assertFaultCausalTrigger(journal, fault) {
822
+ if (fault.trigger.kind !== "trace_event")
823
+ return;
824
+ const causal = journal.events.find((event) => event.eventDigest === fault.trigger.causalEventDigest);
825
+ if (!causal ||
826
+ causal.logicalTimeMs > fault.trigger.logicalTimeMs ||
827
+ causal.kind === "fault.scheduled" ||
828
+ causal.kind === "fault.injected" ||
829
+ causal.kind === "fault.observed")
830
+ throw new Error(`closed_loop_fault_causal_event_missing:${fault.faultId}`);
831
+ }
832
+ function validateFaultMatrixMissionBinding(value) {
833
+ assertExactRecord(value, [
834
+ "schemaVersion",
835
+ "planningStateRoot",
836
+ "meshStateRoot",
837
+ "workContractDigest",
838
+ "checkpointDigest",
839
+ "assignmentEpoch",
840
+ "assignmentAuthorityId",
841
+ "winnerPeerId",
842
+ "replacementPeerId",
843
+ ], "closed-loop fault matrix mission binding");
844
+ if (value.schemaVersion !== 1 ||
845
+ !/^sha256:[0-9a-f]{64}$/u.test(value.planningStateRoot) ||
846
+ !/^sha256:[0-9a-f]{64}$/u.test(value.meshStateRoot) ||
847
+ !/^sha256:[0-9a-f]{64}$/u.test(value.workContractDigest) ||
848
+ !/^sha256:[0-9a-f]{64}$/u.test(value.checkpointDigest) ||
849
+ !Number.isSafeInteger(value.assignmentEpoch) ||
850
+ value.assignmentEpoch < 1 ||
851
+ value.assignmentAuthorityId.length === 0 ||
852
+ value.winnerPeerId.length === 0 ||
853
+ value.replacementPeerId.length === 0 ||
854
+ value.winnerPeerId === value.replacementPeerId)
855
+ throw new TypeError("closed_loop_fault_matrix_mission_binding_invalid");
856
+ return deepFreezePlanning(value);
857
+ }
858
+ function appendFaultEvidence(journal, logicalTimeMs, fault, record, kind, status, recordDigest) {
859
+ return journal.append({
860
+ logicalTimeMs,
861
+ peerId: fault.targets[0]?.peerId ?? null,
862
+ component: "fault",
863
+ kind,
864
+ status,
865
+ reasonCode: null,
866
+ recordDigest,
867
+ stateDigestBefore: null,
868
+ stateDigestAfter: null,
869
+ faultBinding: {
870
+ schemaVersion: 1,
871
+ faultFamily: fault.family,
872
+ scheduleId: fault.faultId,
873
+ injectionId: record.injectedRecordId,
874
+ },
875
+ });
876
+ }
877
+ function collectObservations(input, logicalTimeMs, priorCursors = Object.freeze({}), phase = "initial") {
878
+ const registration = input.definition.registration;
879
+ const observations = [];
880
+ const cursors = { ...priorCursors };
881
+ for (const peer of activePlanningPeers(input)) {
882
+ const environmentCursor = priorCursors[peer.peerId] ?? "cursor:0";
883
+ const result = input.evaluator.environment.observe(createCollectiveEnvironmentObservationRequestV1({
884
+ schemaVersion: 1,
885
+ requestId: `closed-loop:observe:${peer.peerId}:${phase}:${logicalTimeMs}`,
886
+ registrationDigest: registration.bindingDigest,
887
+ missionIntentId: input.definition.missionIntent.missionIntentId,
888
+ intentRevision: input.definition.missionIntent.revision,
889
+ intentDigest: input.definition.missionIntent.intentDigest,
890
+ peerId: peer.peerId,
891
+ peerInstanceId: peer.peerInstanceId,
892
+ environmentCursor,
893
+ maximumItems: registration.limits.maximumObservationBatch,
894
+ requestedAtLogicalMs: logicalTimeMs,
895
+ }));
896
+ if (result.receipt.status === "delivered") {
897
+ observations.push(...result.observations);
898
+ cursors[peer.peerId] = result.receipt.nextEnvironmentCursor;
899
+ }
900
+ else if (result.receipt.status === "idempotent") {
901
+ observations.push(...result.observations);
902
+ cursors[peer.peerId] = result.receipt.nextEnvironmentCursor;
903
+ }
904
+ else if (result.receipt.reasonCode !== "cursor_exhausted")
905
+ throw new Error(`closed_loop_observation_${result.receipt.reasonCode}`);
906
+ }
907
+ return Object.freeze({
908
+ observations: Object.freeze(observations),
909
+ cursors: Object.freeze(cursors),
910
+ });
911
+ }
912
+ async function collectDecisions(input, observations, journal, logicalTimeMs) {
913
+ const decisions = new Map();
914
+ for (const peer of activePlanningPeers(input)) {
915
+ const localObservations = observations.filter((observation) => observation.observerPeerId === peer.peerId &&
916
+ observation.observerInstanceId === peer.peerInstanceId);
917
+ const state = localPlanningState(input.definition, peer.peerId, localObservations);
918
+ const context = validateCollectivePlanningDecisionContextV1({
919
+ schemaVersion: 1,
920
+ peerId: peer.peerId,
921
+ peerInstanceId: peer.peerInstanceId,
922
+ missionIntent: input.definition.missionIntent,
923
+ observations: localObservations,
924
+ planView: state.planView,
925
+ logicalTimeMs,
926
+ });
927
+ const decision = validateCollectivePlanningDecisionV1(await input.decisionPolicy.decide(context));
928
+ assertDecisionIsLocal(decision, context.observations, peer.peerId, peer.peerInstanceId, input.definition);
929
+ decisions.set(peer.peerId, decision);
930
+ append(journal, {
931
+ logicalTimeMs,
932
+ peerId: peer.peerId,
933
+ component: "runner",
934
+ kind: "peer.decision.accepted",
935
+ recordDigest: digestValue(decision),
936
+ stateDigestBefore: state.planView.stateDigest,
937
+ stateDigestAfter: state.planView.stateDigest,
938
+ });
939
+ if (decision.kind === "proposal")
940
+ append(journal, {
941
+ logicalTimeMs,
942
+ peerId: peer.peerId,
943
+ component: "planning",
944
+ kind: "planning.proposal",
945
+ recordDigest: decision.proposal.proposalDigest,
946
+ stateDigestBefore: state.planView.stateDigest,
947
+ stateDigestAfter: state.planView.stateDigest,
948
+ });
949
+ }
950
+ return decisions;
951
+ }
952
+ /** The sparse reference mesh admits only the owner and its direct neighbors
953
+ * to a planning round. Other registered peers remain in the scale model but
954
+ * cannot observe or influence that round without an ingress path. */
955
+ function activePlanningPeers(input) {
956
+ const owner = input.definition.peers[0];
957
+ if (owner === undefined)
958
+ throw new Error('closed_loop_owner_missing');
959
+ const activeIds = new Set([owner.peerId, ...owner.neighborPeerIds]);
960
+ return input.definition.peers.filter((peer) => activeIds.has(peer.peerId));
961
+ }
962
+ async function collectCentralizedDecision(input, observations, journal, ownerPeerId, ownerPeerInstanceId, logicalTimeMs) {
963
+ const retainedObservations = [...observations]
964
+ .sort((left, right) => {
965
+ const leftKey = `${left.observerPeerId}\u0000${left.observerInstanceId}\u0000${left.observationDigest}`;
966
+ const rightKey = `${right.observerPeerId}\u0000${right.observerInstanceId}\u0000${right.observationDigest}`;
967
+ return leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0;
968
+ })
969
+ .slice(0, input.definition.missionIntent.planningLimits.maximumCandidateFragments);
970
+ const context = validateCollectiveCentralizedPlanningDecisionContextV1({
971
+ schemaVersion: 1,
972
+ ownerPeerId,
973
+ ownerPeerInstanceId,
974
+ missionIntent: input.definition.missionIntent,
975
+ observations: retainedObservations,
976
+ logicalTimeMs,
977
+ });
978
+ const decision = validateCollectivePlanningDecisionV1(await input.decisionPolicy.decideCentralized(context));
979
+ if (decision.kind !== "proposal")
980
+ throw new Error("closed_loop_centralized_proposal_required");
981
+ assertCentralizedDecision(decision, retainedObservations, ownerPeerId, ownerPeerInstanceId, input.definition);
982
+ append(journal, {
983
+ logicalTimeMs,
984
+ peerId: null,
985
+ component: "planning",
986
+ kind: "planning.decision",
987
+ recordDigest: digestValue(decision),
988
+ stateDigestBefore: null,
989
+ stateDigestAfter: null,
990
+ });
991
+ append(journal, {
992
+ logicalTimeMs,
993
+ peerId: ownerPeerId,
994
+ component: "planning",
995
+ kind: "planning.proposal",
996
+ recordDigest: decision.proposal.proposalDigest,
997
+ stateDigestBefore: null,
998
+ stateDigestAfter: null,
999
+ });
1000
+ return decision.proposal;
1001
+ }
1002
+ function localPlanningState(definition, peerId, observations) {
1003
+ const peer = definition.peers.find((candidate) => candidate.peerId === peerId);
1004
+ if (!peer)
1005
+ throw new Error("closed_loop_peer_missing");
1006
+ let state = createPlanningReducerStateV1({
1007
+ schemaVersion: 1,
1008
+ peerId: peer.peerId,
1009
+ peerInstanceId: peer.peerInstanceId,
1010
+ missionIntent: definition.missionIntent,
1011
+ selectionPolicy: definition.selectionPolicy,
1012
+ admittedSubjects: definition.peers.map((subject) => ({
1013
+ schemaVersion: 1,
1014
+ peerId: subject.peerId,
1015
+ peerInstanceId: subject.peerInstanceId,
1016
+ })),
1017
+ logicalTimeMs: 0,
1018
+ });
1019
+ for (const observation of observations) {
1020
+ const result = reducePlanningCommandV1(state, createPlanningReducerCommandV1({
1021
+ schemaVersion: 1,
1022
+ kind: "observation.record",
1023
+ expectedStateDigest: null,
1024
+ observation,
1025
+ }));
1026
+ if (result.status !== "applied")
1027
+ throw new Error(`closed_loop_planning_${result.error?.message}`);
1028
+ state = result.state;
1029
+ }
1030
+ return state;
1031
+ }
1032
+ function assertDecisionIsLocal(decision, observations, peerId, peerInstanceId, definition) {
1033
+ if (decision.kind !== "proposal")
1034
+ return;
1035
+ const localDigests = new Set(observations.map((item) => item.observationDigest));
1036
+ const proposal = decision.proposal;
1037
+ if (proposal.proposerPeerId !== peerId ||
1038
+ proposal.proposerInstanceId !== peerInstanceId ||
1039
+ proposal.missionIntentId !== definition.missionIntent.missionIntentId ||
1040
+ proposal.intentRevision !== definition.missionIntent.revision ||
1041
+ proposal.intentDigest !== definition.missionIntent.intentDigest ||
1042
+ proposal.basisObservationDigests.length === 0 ||
1043
+ proposal.basisObservationDigests.some((digest) => !localDigests.has(digest)))
1044
+ throw new TypeError("closed_loop_decision_not_peer_local");
1045
+ }
1046
+ function assertCentralizedDecision(decision, observations, ownerPeerId, ownerPeerInstanceId, definition) {
1047
+ if (decision.kind !== "proposal")
1048
+ return;
1049
+ const deliveredDigests = new Set(observations.map((observation) => observation.observationDigest));
1050
+ const proposal = decision.proposal;
1051
+ if (proposal.proposerPeerId !== ownerPeerId ||
1052
+ proposal.proposerInstanceId !== ownerPeerInstanceId ||
1053
+ proposal.missionIntentId !== definition.missionIntent.missionIntentId ||
1054
+ proposal.intentRevision !== definition.missionIntent.revision ||
1055
+ proposal.intentDigest !== definition.missionIntent.intentDigest ||
1056
+ proposal.basisObservationDigests.length === 0 ||
1057
+ proposal.basisObservationDigests.some((digest) => !deliveredDigests.has(digest)))
1058
+ throw new TypeError("closed_loop_centralized_decision_not_auditable");
1059
+ }
1060
+ function appendCentralizedDirectives(journal, observations, logicalTimeMs) {
1061
+ for (const observation of observations)
1062
+ append(journal, {
1063
+ logicalTimeMs,
1064
+ peerId: observation.observerPeerId,
1065
+ component: "runner",
1066
+ kind: "runner.directive.delivered",
1067
+ recordDigest: observation.observationDigest,
1068
+ stateDigestBefore: null,
1069
+ stateDigestAfter: null,
1070
+ });
1071
+ }
1072
+ function appendRuntimeEvidence(journal, value) {
1073
+ const roots = Object.values(value.meshStates).map((state) => digestValue(state));
1074
+ const stateRoot = digestValue(roots);
1075
+ for (const [kind, recordDigest] of [
1076
+ ["work.created", digestValue(value.workItem)],
1077
+ ["allocation.offer", digestValue(value.workItem)],
1078
+ ["allocation.award", value.workContract.workContractDigest],
1079
+ ["allocation.accepted", digestValue(value.execution)],
1080
+ ["work.checkpoint", digestValue(value.checkpoint)],
1081
+ ])
1082
+ append(journal, {
1083
+ logicalTimeMs: value.logicalTimeMs,
1084
+ peerId: value.winnerPeerId,
1085
+ component: "mesh",
1086
+ kind,
1087
+ recordDigest,
1088
+ stateDigestBefore: stateRoot,
1089
+ stateDigestAfter: stateRoot,
1090
+ });
1091
+ }
1092
+ function appendFinalizationEvidence(journal, value) {
1093
+ append(journal, {
1094
+ logicalTimeMs: value.logicalTimeMs,
1095
+ peerId: value.winnerPeerId,
1096
+ component: "mesh",
1097
+ kind: "work.result",
1098
+ recordDigest: digestValue(value.result),
1099
+ stateDigestBefore: null,
1100
+ stateDigestAfter: digestMeshStates(value.meshStates),
1101
+ });
1102
+ }
1103
+ function appendRecoveryEvidence(journal, preEffect, recovery, faultMatrixDigest) {
1104
+ const stateBefore = digestMeshStates(preEffect.meshStates);
1105
+ const stateAfter = digestMeshStates(recovery.meshStates);
1106
+ for (const [kind, recordDigest] of [
1107
+ [
1108
+ "recovery.directive",
1109
+ digestValue({
1110
+ takeoverProposalId: recovery.takeoverProposalId,
1111
+ leaseVoteIds: recovery.leaseVoteIds,
1112
+ certificateId: recovery.certificateId,
1113
+ causalFaultMatrixDigest: faultMatrixDigest,
1114
+ }),
1115
+ ],
1116
+ ["lease.recovered", digestValue(recovery.fenceHead)],
1117
+ ["work.checkpoint", digestValue(recovery.checkpoint)],
1118
+ ])
1119
+ append(journal, {
1120
+ logicalTimeMs: recovery.recoveryLogicalTimeMs,
1121
+ peerId: recovery.replacementPeerId,
1122
+ component: "mesh",
1123
+ kind,
1124
+ recordDigest,
1125
+ stateDigestBefore: stateBefore,
1126
+ stateDigestAfter: stateAfter,
1127
+ });
1128
+ }
1129
+ function appendStaleResultRejectionEvidence(journal, preEffect, recovery, fault, staleRejection, staleFenceDigest, currentFenceDigest) {
1130
+ const reasonCode = staleRejection.rejectionCode;
1131
+ const currentStateDigest = digestMeshStates(recovery.meshStates);
1132
+ return journal.append({
1133
+ logicalTimeMs: staleRejection.logicalTimeMs,
1134
+ peerId: preEffect.winnerPeerId,
1135
+ component: "mesh",
1136
+ kind: "mesh.message.rejected",
1137
+ status: "rejected",
1138
+ reasonCode,
1139
+ recordDigest: digestValue(staleRejection.envelope),
1140
+ stateDigestBefore: currentStateDigest,
1141
+ stateDigestAfter: currentStateDigest,
1142
+ faultBinding: {
1143
+ schemaVersion: 1,
1144
+ faultFamily: fault.family,
1145
+ scheduleId: fault.faultId,
1146
+ injectionId: staleRejection.recordId,
1147
+ },
1148
+ });
1149
+ }
1150
+ function appendRecoveredFinalizationEvidence(journal, value) {
1151
+ append(journal, {
1152
+ logicalTimeMs: value.logicalTimeMs,
1153
+ peerId: value.winnerPeerId,
1154
+ component: "mesh",
1155
+ kind: "work.result",
1156
+ recordDigest: digestValue(value.result),
1157
+ stateDigestBefore: null,
1158
+ stateDigestAfter: digestMeshStates(value.meshStates),
1159
+ });
1160
+ }
1161
+ function currentMesh(value) {
1162
+ return Object.freeze({
1163
+ meshId: value.workContract.objective.meshId,
1164
+ objectiveId: value.workContract.objective.objectiveId,
1165
+ objectiveRevision: value.workContract.objective.objectiveRevision,
1166
+ workItemId: value.workContract.assignment.workItemId,
1167
+ workItemRevision: value.workContract.assignment.workItemRevision,
1168
+ assignedPeerId: value.workContract.assignment.assignedPeerId,
1169
+ assignedInstanceId: value.workContract.assignment.assignedInstanceId,
1170
+ assignmentAuthorityId: value.workContract.assignment.assignmentAuthorityId,
1171
+ assignmentEpoch: value.workContract.assignment.assignmentEpoch,
1172
+ authorityGeneration: value.workContract.assignment.authorityGeneration,
1173
+ fencingToken: value.workContract.assignment.fencingToken,
1174
+ leaseExpiresAtLogicalMs: value.workContract.assignment.leaseExpiresAtLogicalMs,
1175
+ objectiveTerminal: false,
1176
+ workTerminal: false,
1177
+ });
1178
+ }
1179
+ function currentMeshForWorkContract(workContract) {
1180
+ return Object.freeze({
1181
+ meshId: workContract.objective.meshId,
1182
+ objectiveId: workContract.objective.objectiveId,
1183
+ objectiveRevision: workContract.objective.objectiveRevision,
1184
+ workItemId: workContract.assignment.workItemId,
1185
+ workItemRevision: workContract.assignment.workItemRevision,
1186
+ assignedPeerId: workContract.assignment.assignedPeerId,
1187
+ assignedInstanceId: workContract.assignment.assignedInstanceId,
1188
+ assignmentAuthorityId: workContract.assignment.assignmentAuthorityId,
1189
+ assignmentEpoch: workContract.assignment.assignmentEpoch,
1190
+ authorityGeneration: workContract.assignment.authorityGeneration,
1191
+ fencingToken: workContract.assignment.fencingToken,
1192
+ leaseExpiresAtLogicalMs: workContract.assignment.leaseExpiresAtLogicalMs,
1193
+ objectiveTerminal: false,
1194
+ workTerminal: false,
1195
+ });
1196
+ }
1197
+ function actionWallTime(validFrom, validUntil, checkpointSentAt, logicalTimeMs) {
1198
+ const lowerBoundMs = Date.parse(validFrom);
1199
+ const upperBoundMs = Date.parse(validUntil);
1200
+ const checkpointMs = Date.parse(checkpointSentAt);
1201
+ const logicalWallTimeMs = lowerBoundMs + logicalTimeMs;
1202
+ const authorizedAtMs = Math.max(logicalWallTimeMs, checkpointMs + 1);
1203
+ if (!Number.isSafeInteger(logicalTimeMs) ||
1204
+ logicalTimeMs < 0 ||
1205
+ !Number.isFinite(lowerBoundMs) ||
1206
+ !Number.isFinite(upperBoundMs) ||
1207
+ !Number.isFinite(checkpointMs) ||
1208
+ upperBoundMs <= lowerBoundMs ||
1209
+ checkpointMs < lowerBoundMs ||
1210
+ checkpointMs >= upperBoundMs ||
1211
+ !Number.isSafeInteger(authorizedAtMs) ||
1212
+ authorizedAtMs >= upperBoundMs)
1213
+ throw new RangeError("closed_loop_action_time_invalid");
1214
+ return new Date(authorizedAtMs).toISOString();
1215
+ }
1216
+ function publicArtifactsFor(preEffect, action, finalized, checkpointObservations, outcomeObservations) {
1217
+ return deepFreezePlanning([
1218
+ {
1219
+ schemaVersion: 1,
1220
+ kind: "closed-loop-assignment",
1221
+ winnerPeerId: preEffect.winnerPeerId,
1222
+ workContractId: preEffect.workContract.workContractId,
1223
+ workContractDigest: preEffect.workContract.workContractDigest,
1224
+ roleBindingDigest: preEffect.roleBinding.roleBindingDigest,
1225
+ },
1226
+ {
1227
+ schemaVersion: 1,
1228
+ kind: "closed-loop-mesh-participation",
1229
+ ownerPeerId: preEffect.workContract.assignment.ownerPeerId,
1230
+ discoveredPeerIds: preEffect.discoveredPeerIds,
1231
+ offerRecipientPeerIds: preEffect.offerRecipientPeerIds,
1232
+ bidderPeerIds: preEffect.bidderPeerIds,
1233
+ },
1234
+ {
1235
+ schemaVersion: 1,
1236
+ kind: "closed-loop-protected-effect",
1237
+ permitDigest: action.actionPermit.permitDigest,
1238
+ receiptDigest: action.receipt?.receiptDigest ?? null,
1239
+ status: action.receipt?.status ?? "missing",
1240
+ },
1241
+ {
1242
+ schemaVersion: 1,
1243
+ kind: "closed-loop-checkpoint-observations",
1244
+ observationDigests: checkpointObservations
1245
+ .map((observation) => observation.observationDigest)
1246
+ .sort(),
1247
+ },
1248
+ {
1249
+ schemaVersion: 1,
1250
+ kind: "closed-loop-outcome-observations",
1251
+ observationDigests: outcomeObservations
1252
+ .map((observation) => observation.observationDigest)
1253
+ .sort(),
1254
+ },
1255
+ {
1256
+ schemaVersion: 1,
1257
+ kind: "closed-loop-work-result",
1258
+ resultDigest: digestValue(finalized.result),
1259
+ logicalTimeMs: finalized.logicalTimeMs,
1260
+ interactionCount: finalized.interactionCount,
1261
+ },
1262
+ ]);
1263
+ }
1264
+ function publicArtifactsForRecovery(preEffect, recovery, action, finalized, faultMatrixBindingDigest, faultMatrix, checkpointObservations, outcomeObservations) {
1265
+ return deepFreezePlanning([
1266
+ {
1267
+ schemaVersion: 1,
1268
+ kind: "closed-loop-recovered-assignment",
1269
+ failedPeerId: recovery.failedWinnerPeerId,
1270
+ winnerPeerId: recovery.replacementPeerId,
1271
+ priorWorkContractDigest: preEffect.workContract.workContractDigest,
1272
+ workContractId: recovery.workContract.workContractId,
1273
+ workContractDigest: recovery.workContract.workContractDigest,
1274
+ assignmentEpoch: recovery.workContract.assignment.assignmentEpoch,
1275
+ certificateId: recovery.certificateId,
1276
+ },
1277
+ {
1278
+ schemaVersion: 1,
1279
+ kind: "closed-loop-fault-matrix",
1280
+ missionBindingDigest: faultMatrixBindingDigest,
1281
+ scenarioDigest: faultMatrix.scenarioDigest,
1282
+ matrixDigest: faultMatrix.matrixDigest,
1283
+ faultIds: faultMatrix.records.map((record) => record.faultId).sort(),
1284
+ observedEventDigests: faultMatrix.records
1285
+ .map((record) => record.observedEventDigest)
1286
+ .sort(),
1287
+ },
1288
+ {
1289
+ schemaVersion: 1,
1290
+ kind: "closed-loop-stale-authority-fencing",
1291
+ rejectionCodes: recovery.staleRejectionCodes,
1292
+ priorAssignmentEpoch: preEffect.execution.assignmentEpoch,
1293
+ currentAssignmentEpoch: recovery.execution.assignmentEpoch,
1294
+ },
1295
+ {
1296
+ schemaVersion: 1,
1297
+ kind: "closed-loop-protected-effect",
1298
+ permitDigest: action.actionPermit.permitDigest,
1299
+ receiptDigest: action.receipt?.receiptDigest ?? null,
1300
+ status: action.receipt?.status ?? "missing",
1301
+ },
1302
+ {
1303
+ schemaVersion: 1,
1304
+ kind: "closed-loop-checkpoint-observations",
1305
+ observationDigests: checkpointObservations
1306
+ .map((observation) => observation.observationDigest)
1307
+ .sort(),
1308
+ },
1309
+ {
1310
+ schemaVersion: 1,
1311
+ kind: "closed-loop-outcome-observations",
1312
+ observationDigests: outcomeObservations
1313
+ .map((observation) => observation.observationDigest)
1314
+ .sort(),
1315
+ },
1316
+ {
1317
+ schemaVersion: 1,
1318
+ kind: "closed-loop-work-result",
1319
+ resultDigest: digestValue(finalized.result),
1320
+ logicalTimeMs: finalized.logicalTimeMs,
1321
+ interactionCount: finalized.interactionCount,
1322
+ },
1323
+ ]);
1324
+ }
1325
+ function assertTerminalEvidence(evaluation, run, finalized) {
1326
+ if (evaluation.trace.runner !== run.runner ||
1327
+ evaluation.trace.registrationDigest !== run.registrationBindingDigest ||
1328
+ evaluation.trace.ledger.limitExceeded ||
1329
+ evaluation.trace.ledger.total >
1330
+ evaluation.evidence.registration.limits.maximumInteractions ||
1331
+ finalized.interactionCount >
1332
+ evaluation.evidence.registration.limits.maximumInteractions)
1333
+ throw new Error("closed_loop_terminal_evidence_invalid");
1334
+ }
1335
+ function assertResilienceTerminalEvidence(evaluation, run, finalized, faultMatrix) {
1336
+ if (evaluation.trace.runner !== run.runner ||
1337
+ evaluation.trace.registrationDigest !== run.registrationBindingDigest ||
1338
+ evaluation.trace.ledger.limitExceeded ||
1339
+ evaluation.trace.ledger.total >
1340
+ evaluation.evidence.registration.limits.maximumInteractions ||
1341
+ finalized.interactionCount >
1342
+ evaluation.evidence.registration.limits.maximumInteractions ||
1343
+ faultMatrix.records.length >
1344
+ COLLECTIVE_CLOSED_LOOP_RESILIENCE_LIMITS_V1.maximumFaults ||
1345
+ faultMatrix.trace.records.length > 5_000)
1346
+ throw new Error("closed_loop_resilience_terminal_evidence_invalid");
1347
+ }
1348
+ function journalFor(environment) {
1349
+ const journal = collectiveTraceJournalForOwnerV2(environment);
1350
+ if (!journal)
1351
+ throw new TypeError("closed_loop_evaluator_not_instrumented");
1352
+ return journal;
1353
+ }
1354
+ function append(journal, input) {
1355
+ journal.append({
1356
+ ...input,
1357
+ status: "accepted",
1358
+ reasonCode: null,
1359
+ faultBinding: null,
1360
+ });
1361
+ }
1362
+ function sortedRoots(values) {
1363
+ return Object.freeze([...new Set(values)].sort());
1364
+ }
1365
+ function digestValue(value) {
1366
+ return digestPlanningJsonV1("environment-state-v1", planningJsonProjection(value), {
1367
+ maximumBytes: 67_108_864,
1368
+ maximumDepth: 64,
1369
+ maximumNodes: 2_000_000,
1370
+ maximumKeysPerObject: 4_096,
1371
+ maximumItemsPerArray: 262_144,
1372
+ });
1373
+ }
1374
+ function digestMeshStates(states) {
1375
+ return digestStateCollection(states);
1376
+ }
1377
+ function digestPlanningStates(states) {
1378
+ return digestStateCollection(states);
1379
+ }
1380
+ function digestStateCollection(states) {
1381
+ return digestValue(Object.entries(states)
1382
+ .map(([peerId, state]) => ({ peerId, stateDigest: digestValue(state) }))
1383
+ .sort((left, right) => left.peerId.localeCompare(right.peerId)));
1384
+ }
1385
+ function planningJsonProjection(value, seen = new WeakSet()) {
1386
+ if (value === null ||
1387
+ typeof value === "string" ||
1388
+ typeof value === "boolean" ||
1389
+ (typeof value === "number" && Number.isFinite(value)))
1390
+ return value;
1391
+ if (typeof value !== "object")
1392
+ throw new TypeError("closed_loop_state_not_json_projectable");
1393
+ if (seen.has(value))
1394
+ throw new TypeError("closed_loop_state_projection_cycle");
1395
+ seen.add(value);
1396
+ if (Array.isArray(value)) {
1397
+ const projected = value.map((item) => planningJsonProjection(item, seen));
1398
+ seen.delete(value);
1399
+ return projected;
1400
+ }
1401
+ const projected = Object.create(null);
1402
+ for (const key of Object.keys(value).sort()) {
1403
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
1404
+ if (!descriptor || !("value" in descriptor))
1405
+ throw new TypeError("closed_loop_state_projection_accessor");
1406
+ if (descriptor.value !== undefined)
1407
+ projected[key] = planningJsonProjection(descriptor.value, seen);
1408
+ }
1409
+ seen.delete(value);
1410
+ return projected;
1411
+ }
1412
+ function assertExactRecord(value, expectedKeys, label) {
1413
+ if (value === null || typeof value !== "object" || Array.isArray(value))
1414
+ throw new TypeError(`${label} must be a plain record`);
1415
+ const prototype = Object.getPrototypeOf(value);
1416
+ const actual = Object.getOwnPropertyNames(value).sort();
1417
+ const expected = [...expectedKeys].sort();
1418
+ if ((prototype !== Object.prototype && prototype !== null) ||
1419
+ Object.getOwnPropertySymbols(value).length !== 0 ||
1420
+ actual.length !== expected.length ||
1421
+ actual.some((key, index) => key !== expected[index]))
1422
+ throw new TypeError(`${label} has an invalid shape`);
1423
+ for (const key of actual) {
1424
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
1425
+ if (!descriptor || !descriptor.enumerable || !("value" in descriptor))
1426
+ throw new TypeError(`${label} must contain enumerable data properties`);
1427
+ }
1428
+ }
1429
+ //# sourceMappingURL=collective-closed-loop-runner.js.map