@agentplat/mesh-sim 0.3.0-beta.2 → 0.3.0-beta.4
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.
- package/README.md +447 -2
- package/dist/collective-closed-loop-action.d.ts +80 -0
- package/dist/collective-closed-loop-action.d.ts.map +1 -0
- package/dist/collective-closed-loop-action.js +471 -0
- package/dist/collective-closed-loop-action.js.map +1 -0
- package/dist/collective-closed-loop-contracts.d.ts +75 -0
- package/dist/collective-closed-loop-contracts.d.ts.map +1 -0
- package/dist/collective-closed-loop-contracts.js +420 -0
- package/dist/collective-closed-loop-contracts.js.map +1 -0
- package/dist/collective-closed-loop-fault-matrix.d.ts +101 -0
- package/dist/collective-closed-loop-fault-matrix.d.ts.map +1 -0
- package/dist/collective-closed-loop-fault-matrix.js +253 -0
- package/dist/collective-closed-loop-fault-matrix.js.map +1 -0
- package/dist/collective-closed-loop-recovery.d.ts +70 -0
- package/dist/collective-closed-loop-recovery.d.ts.map +1 -0
- package/dist/collective-closed-loop-recovery.js +823 -0
- package/dist/collective-closed-loop-recovery.js.map +1 -0
- package/dist/collective-closed-loop-reference.d.ts +54 -0
- package/dist/collective-closed-loop-reference.d.ts.map +1 -0
- package/dist/collective-closed-loop-reference.js +1696 -0
- package/dist/collective-closed-loop-reference.js.map +1 -0
- package/dist/collective-closed-loop-replanning.d.ts +27 -0
- package/dist/collective-closed-loop-replanning.d.ts.map +1 -0
- package/dist/collective-closed-loop-replanning.js +151 -0
- package/dist/collective-closed-loop-replanning.js.map +1 -0
- package/dist/collective-closed-loop-resilience-campaign.d.ts +22 -0
- package/dist/collective-closed-loop-resilience-campaign.d.ts.map +1 -0
- package/dist/collective-closed-loop-resilience-campaign.js +196 -0
- package/dist/collective-closed-loop-resilience-campaign.js.map +1 -0
- package/dist/collective-closed-loop-resilience-contracts.d.ts +138 -0
- package/dist/collective-closed-loop-resilience-contracts.d.ts.map +1 -0
- package/dist/collective-closed-loop-resilience-contracts.js +677 -0
- package/dist/collective-closed-loop-resilience-contracts.js.map +1 -0
- package/dist/collective-closed-loop-runner.d.ts +133 -0
- package/dist/collective-closed-loop-runner.d.ts.map +1 -0
- package/dist/collective-closed-loop-runner.js +1429 -0
- package/dist/collective-closed-loop-runner.js.map +1 -0
- package/dist/collective-closed-loop-runtime.d.ts +95 -0
- package/dist/collective-closed-loop-runtime.d.ts.map +1 -0
- package/dist/collective-closed-loop-runtime.js +1192 -0
- package/dist/collective-closed-loop-runtime.js.map +1 -0
- package/dist/collective-effect-provenance.d.ts +5 -0
- package/dist/collective-effect-provenance.d.ts.map +1 -0
- package/dist/collective-effect-provenance.js +32 -0
- package/dist/collective-effect-provenance.js.map +1 -0
- package/dist/collective-environment.d.ts +49 -0
- package/dist/collective-environment.d.ts.map +1 -0
- package/dist/collective-environment.js +907 -0
- package/dist/collective-environment.js.map +1 -0
- package/dist/collective-progressive-scale-overlay.d.ts +39 -0
- package/dist/collective-progressive-scale-overlay.d.ts.map +1 -0
- package/dist/collective-progressive-scale-overlay.js +147 -0
- package/dist/collective-progressive-scale-overlay.js.map +1 -0
- package/dist/collective-progressive-scale.d.ts +202 -0
- package/dist/collective-progressive-scale.d.ts.map +1 -0
- package/dist/collective-progressive-scale.js +755 -0
- package/dist/collective-progressive-scale.js.map +1 -0
- package/dist/collective-statistical-campaign-aggregation.d.ts +97 -0
- package/dist/collective-statistical-campaign-aggregation.d.ts.map +1 -0
- package/dist/collective-statistical-campaign-aggregation.js +446 -0
- package/dist/collective-statistical-campaign-aggregation.js.map +1 -0
- package/dist/collective-statistical-campaign-artifact-stream.d.ts +66 -0
- package/dist/collective-statistical-campaign-artifact-stream.d.ts.map +1 -0
- package/dist/collective-statistical-campaign-artifact-stream.js +207 -0
- package/dist/collective-statistical-campaign-artifact-stream.js.map +1 -0
- package/dist/collective-statistical-campaign-bundle.d.ts +151 -0
- package/dist/collective-statistical-campaign-bundle.d.ts.map +1 -0
- package/dist/collective-statistical-campaign-bundle.js +885 -0
- package/dist/collective-statistical-campaign-bundle.js.map +1 -0
- package/dist/collective-statistical-campaign-executor.d.ts +131 -0
- package/dist/collective-statistical-campaign-executor.d.ts.map +1 -0
- package/dist/collective-statistical-campaign-executor.js +856 -0
- package/dist/collective-statistical-campaign-executor.js.map +1 -0
- package/dist/collective-statistical-campaign-normative-analysis.d.ts +85 -0
- package/dist/collective-statistical-campaign-normative-analysis.d.ts.map +1 -0
- package/dist/collective-statistical-campaign-normative-analysis.js +483 -0
- package/dist/collective-statistical-campaign-normative-analysis.js.map +1 -0
- package/dist/collective-statistical-campaign-normative-operation.d.ts +89 -0
- package/dist/collective-statistical-campaign-normative-operation.d.ts.map +1 -0
- package/dist/collective-statistical-campaign-normative-operation.js +266 -0
- package/dist/collective-statistical-campaign-normative-operation.js.map +1 -0
- package/dist/collective-statistical-campaign-normative-registry.d.ts +24 -0
- package/dist/collective-statistical-campaign-normative-registry.d.ts.map +1 -0
- package/dist/collective-statistical-campaign-normative-registry.js +163 -0
- package/dist/collective-statistical-campaign-normative-registry.js.map +1 -0
- package/dist/collective-statistical-campaign-registered-adapter.d.ts +16 -0
- package/dist/collective-statistical-campaign-registered-adapter.d.ts.map +1 -0
- package/dist/collective-statistical-campaign-registered-adapter.js +518 -0
- package/dist/collective-statistical-campaign-registered-adapter.js.map +1 -0
- package/dist/collective-statistical-campaign-scale.d.ts +85 -0
- package/dist/collective-statistical-campaign-scale.d.ts.map +1 -0
- package/dist/collective-statistical-campaign-scale.js +349 -0
- package/dist/collective-statistical-campaign-scale.js.map +1 -0
- package/dist/collective-trace-journal.d.ts +20 -0
- package/dist/collective-trace-journal.d.ts.map +1 -0
- package/dist/collective-trace-journal.js +105 -0
- package/dist/collective-trace-journal.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -1
- package/dist/multi-domain-environment-contracts.d.ts +122 -0
- package/dist/multi-domain-environment-contracts.d.ts.map +1 -0
- package/dist/multi-domain-environment-contracts.js +14 -0
- package/dist/multi-domain-environment-contracts.js.map +1 -0
- package/dist/multi-domain-environment-runtime.d.ts +25 -0
- package/dist/multi-domain-environment-runtime.d.ts.map +1 -0
- package/dist/multi-domain-environment-runtime.js +493 -0
- package/dist/multi-domain-environment-runtime.js.map +1 -0
- package/dist/multi-domain-environment-validation.d.ts +33 -0
- package/dist/multi-domain-environment-validation.d.ts.map +1 -0
- package/dist/multi-domain-environment-validation.js +415 -0
- package/dist/multi-domain-environment-validation.js.map +1 -0
- package/dist/multi-domain-environment.d.ts +4 -0
- package/dist/multi-domain-environment.d.ts.map +1 -0
- package/dist/multi-domain-environment.js +4 -0
- package/dist/multi-domain-environment.js.map +1 -0
- package/dist/reducer-scenario.d.ts.map +1 -1
- package/dist/reducer-scenario.js +67 -19
- package/dist/reducer-scenario.js.map +1 -1
- package/dist/scalable-evaluation-contracts.d.ts +835 -0
- package/dist/scalable-evaluation-contracts.d.ts.map +1 -0
- package/dist/scalable-evaluation-contracts.js +22 -0
- package/dist/scalable-evaluation-contracts.js.map +1 -0
- package/dist/scalable-evaluation-durability.d.ts +16 -0
- package/dist/scalable-evaluation-durability.d.ts.map +1 -0
- package/dist/scalable-evaluation-durability.js +339 -0
- package/dist/scalable-evaluation-durability.js.map +1 -0
- package/dist/scalable-evaluation-evidence.d.ts +93 -0
- package/dist/scalable-evaluation-evidence.d.ts.map +1 -0
- package/dist/scalable-evaluation-evidence.js +470 -0
- package/dist/scalable-evaluation-evidence.js.map +1 -0
- package/dist/scalable-evaluation-reference-integrated.d.ts +87 -0
- package/dist/scalable-evaluation-reference-integrated.d.ts.map +1 -0
- package/dist/scalable-evaluation-reference-integrated.js +1318 -0
- package/dist/scalable-evaluation-reference-integrated.js.map +1 -0
- package/dist/scalable-evaluation-runner.d.ts +8 -0
- package/dist/scalable-evaluation-runner.d.ts.map +1 -0
- package/dist/scalable-evaluation-runner.js +1810 -0
- package/dist/scalable-evaluation-runner.js.map +1 -0
- package/dist/scalable-evaluation-runtime.d.ts +86 -0
- package/dist/scalable-evaluation-runtime.d.ts.map +1 -0
- package/dist/scalable-evaluation-runtime.js +1310 -0
- package/dist/scalable-evaluation-runtime.js.map +1 -0
- package/dist/scalable-evaluation-validation.d.ts +59 -0
- package/dist/scalable-evaluation-validation.d.ts.map +1 -0
- package/dist/scalable-evaluation-validation.js +377 -0
- package/dist/scalable-evaluation-validation.js.map +1 -0
- package/dist/scalable-evaluation.d.ts +8 -0
- package/dist/scalable-evaluation.d.ts.map +1 -0
- package/dist/scalable-evaluation.js +8 -0
- package/dist/scalable-evaluation.js.map +1 -0
- package/dist/sharded-simulation-contracts.d.ts +240 -0
- package/dist/sharded-simulation-contracts.d.ts.map +1 -0
- package/dist/sharded-simulation-contracts.js +199 -0
- package/dist/sharded-simulation-contracts.js.map +1 -0
- package/dist/sharded-simulation-runtime.d.ts +64 -0
- package/dist/sharded-simulation-runtime.d.ts.map +1 -0
- package/dist/sharded-simulation-runtime.js +755 -0
- package/dist/sharded-simulation-runtime.js.map +1 -0
- package/dist/sharded-simulation-validation.d.ts +10 -0
- package/dist/sharded-simulation-validation.d.ts.map +1 -0
- package/dist/sharded-simulation-validation.js +250 -0
- package/dist/sharded-simulation-validation.js.map +1 -0
- package/dist/sharded-simulation.d.ts +46 -0
- package/dist/sharded-simulation.d.ts.map +1 -0
- package/dist/sharded-simulation.js +32 -0
- package/dist/sharded-simulation.js.map +1 -0
- package/dist/verified-benchmark-contracts.d.ts +132 -0
- package/dist/verified-benchmark-contracts.d.ts.map +1 -0
- package/dist/verified-benchmark-contracts.js +2 -0
- package/dist/verified-benchmark-contracts.js.map +1 -0
- package/dist/verified-benchmark-runtime.d.ts +20 -0
- package/dist/verified-benchmark-runtime.d.ts.map +1 -0
- package/dist/verified-benchmark-runtime.js +199 -0
- package/dist/verified-benchmark-runtime.js.map +1 -0
- package/dist/verified-benchmark-validation.d.ts +64 -0
- package/dist/verified-benchmark-validation.d.ts.map +1 -0
- package/dist/verified-benchmark-validation.js +481 -0
- package/dist/verified-benchmark-validation.js.map +1 -0
- package/dist/verified-benchmark.d.ts +4 -0
- package/dist/verified-benchmark.d.ts.map +1 -0
- package/dist/verified-benchmark.js +4 -0
- package/dist/verified-benchmark.js.map +1 -0
- package/package.json +18 -8
|
@@ -0,0 +1,856 @@
|
|
|
1
|
+
import { canonicalizePlanningJsonV1, deepFreezePlanning, } from "@agentplat/collective-planning";
|
|
2
|
+
import { claimCollectiveEvaluationCellV1, collectiveEvaluationRunKeyV1, createCollectiveEvaluationCampaignExecutionV1, finalizeCollectiveEvaluationCampaignExecutionV1, renewCollectiveEvaluationCellLeaseV1, reconcileCollectiveEvaluationRunV1, settleCollectiveEvaluationRunV1, startCollectiveEvaluationRunV1, validateCollectiveEvaluationCampaignExecutionV1, validateCollectiveEvaluationCampaignRegistrationV1, } from "@agentplat/collective-planning/evaluation";
|
|
3
|
+
import { createCollectiveStatisticalCampaignExecutionArtifactsV1, } from "./collective-statistical-campaign-aggregation.js";
|
|
4
|
+
const runners = Object.freeze([
|
|
5
|
+
"adaptive_collective",
|
|
6
|
+
"centralized_planner",
|
|
7
|
+
]);
|
|
8
|
+
const attempts = Object.freeze(["first", "replay"]);
|
|
9
|
+
const MAXIMUM_EXECUTION_STATE_CAS_RETRIES = 256;
|
|
10
|
+
/**
|
|
11
|
+
* Runs one deterministic shard. Disjoint workers converge through bounded
|
|
12
|
+
* revision-CAS retries; every logical run remains idempotent through runKey
|
|
13
|
+
* and an immutable commit.
|
|
14
|
+
* A crash after commit but before settlement is resumed from the stored commit.
|
|
15
|
+
*/
|
|
16
|
+
export async function runCollectiveStatisticalCampaignShardV1(input) {
|
|
17
|
+
validateExecutorInput(input);
|
|
18
|
+
const registration = validateCollectiveEvaluationCampaignRegistrationV1(input.registration);
|
|
19
|
+
let state = await loadOrCreateExecutionState(input, registration);
|
|
20
|
+
const transition = async (apply) => {
|
|
21
|
+
for (let retry = 0; retry < MAXIMUM_EXECUTION_STATE_CAS_RETRIES; retry += 1) {
|
|
22
|
+
const previous = state;
|
|
23
|
+
const next = apply(previous);
|
|
24
|
+
const status = await compareAndSwapExecutionState(input, {
|
|
25
|
+
executionId: input.executionId,
|
|
26
|
+
expectedExecutionDigest: previous.executionDigest,
|
|
27
|
+
state: next,
|
|
28
|
+
});
|
|
29
|
+
if (status === "expired")
|
|
30
|
+
throw new TypeError("collective_statistical_campaign_operation_authorization_expired");
|
|
31
|
+
if (status === "committed") {
|
|
32
|
+
state = next;
|
|
33
|
+
return Object.freeze({ state, outcome: "committed" });
|
|
34
|
+
}
|
|
35
|
+
const canonical = await readExecutionState(input, registration);
|
|
36
|
+
if (canonical === null)
|
|
37
|
+
throw new TypeError("collective_statistical_campaign_state_conflict_missing");
|
|
38
|
+
state = canonical;
|
|
39
|
+
if (status === "duplicate") {
|
|
40
|
+
// The store linearized an exact duplicate. The mandatory reread may
|
|
41
|
+
// already observe a later CAS committed by another shard.
|
|
42
|
+
return Object.freeze({ state, outcome: "duplicate" });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
throw new TypeError("collective_statistical_campaign_state_retry_limit_exceeded");
|
|
46
|
+
};
|
|
47
|
+
const selected = input.cellIds === undefined
|
|
48
|
+
? registration.cells.filter((_cell, index) => index % input.shard.count === input.shard.index)
|
|
49
|
+
: selectExplicitCells(registration, input.cellIds);
|
|
50
|
+
if (selected.length > input.maximumCells)
|
|
51
|
+
throw new RangeError("collective_statistical_campaign_shard_cell_limit_exceeded");
|
|
52
|
+
let executedSlotCount = 0;
|
|
53
|
+
let resumedSlotCount = 0;
|
|
54
|
+
const completed = [];
|
|
55
|
+
for (const cell of selected) {
|
|
56
|
+
const leaseToken = `lease:${input.workerId}:${input.shard.index}:${cell.peerCount}:${cell.stratum}:${cell.seed}`;
|
|
57
|
+
let ownedFence = null;
|
|
58
|
+
for (const runner of runners) {
|
|
59
|
+
for (const attempt of attempts) {
|
|
60
|
+
const runKey = runKeyFor(state, cell.cellId, runner, attempt);
|
|
61
|
+
let currentCell = cellFor(state, cell.cellId);
|
|
62
|
+
let currentSlot = runFor(currentCell, runner, attempt);
|
|
63
|
+
if (currentSlot.status === "succeeded" ||
|
|
64
|
+
currentSlot.status === "failed") {
|
|
65
|
+
if (currentSlot.settlementFence === null)
|
|
66
|
+
throw new TypeError("collective_statistical_campaign_terminal_settlement_invalid");
|
|
67
|
+
const durable = await readOne(input.store, runKey, {
|
|
68
|
+
executionId: input.executionId,
|
|
69
|
+
registrationDigest: registration.registrationDigest,
|
|
70
|
+
cellId: cell.cellId,
|
|
71
|
+
fence: currentSlot.settlementFence,
|
|
72
|
+
operationExpiresAtMs: input.operationExpiresAtMs ?? null,
|
|
73
|
+
});
|
|
74
|
+
if (durable === null)
|
|
75
|
+
throw new TypeError("collective_statistical_campaign_terminal_commit_missing");
|
|
76
|
+
completed.push(validateStoredExecution(registration, input.executionId, cell, runner, attempt, runKey, durable));
|
|
77
|
+
resumedSlotCount += 1;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (currentSlot.status === "running") {
|
|
81
|
+
const activeFence = fenceFor(state, cell.cellId);
|
|
82
|
+
let durable = await readOne(input.store, runKey, {
|
|
83
|
+
executionId: input.executionId,
|
|
84
|
+
registrationDigest: registration.registrationDigest,
|
|
85
|
+
cellId: cell.cellId,
|
|
86
|
+
fence: activeFence,
|
|
87
|
+
operationExpiresAtMs: input.operationExpiresAtMs ?? null,
|
|
88
|
+
});
|
|
89
|
+
const inspectionNow = clock(input.now);
|
|
90
|
+
if (durable === null && activeFence.expiresAtMs > inspectionNow)
|
|
91
|
+
throw new TypeError("collective_statistical_campaign_running_lease_active");
|
|
92
|
+
if (durable === null) {
|
|
93
|
+
durable = createFailedExecution(input.executionId, registration.registrationDigest, runKey, cell, runner, attempt, "indeterminate_external_effect");
|
|
94
|
+
const commit = await commitExecution(input, registration.registrationDigest, cell.cellId, runKey, activeFence, durable);
|
|
95
|
+
if (commit === "committed")
|
|
96
|
+
executedSlotCount += 1;
|
|
97
|
+
else {
|
|
98
|
+
const canonical = await readOne(input.store, runKey, {
|
|
99
|
+
executionId: input.executionId,
|
|
100
|
+
registrationDigest: registration.registrationDigest,
|
|
101
|
+
cellId: cell.cellId,
|
|
102
|
+
fence: activeFence,
|
|
103
|
+
operationExpiresAtMs: input.operationExpiresAtMs ?? null,
|
|
104
|
+
});
|
|
105
|
+
if (canonical === null)
|
|
106
|
+
throw new TypeError("collective_statistical_campaign_duplicate_commit_missing");
|
|
107
|
+
durable = canonical;
|
|
108
|
+
resumedSlotCount += 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
resumedSlotCount += 1;
|
|
113
|
+
}
|
|
114
|
+
const execution = validateStoredExecution(registration, input.executionId, cell, runner, attempt, runKey, durable);
|
|
115
|
+
await transition((current) => settleStoredExecution(current, input.executionId, cell, runner, attempt, runKey, clock(input.now), execution));
|
|
116
|
+
completed.push(execution);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
assertOperationActive(input, clock(input.now));
|
|
120
|
+
const claim = await transition((current) => {
|
|
121
|
+
const candidate = cellFor(current, cell.cellId);
|
|
122
|
+
const claimNow = clock(input.now);
|
|
123
|
+
if (candidate.status === "pending" ||
|
|
124
|
+
(candidate.status === "leased" &&
|
|
125
|
+
candidate.lease !== null &&
|
|
126
|
+
candidate.lease.expiresAtMs <= claimNow)) {
|
|
127
|
+
return claimCollectiveEvaluationCellV1(current, {
|
|
128
|
+
executionId: input.executionId,
|
|
129
|
+
expectedRevision: current.revision,
|
|
130
|
+
cellId: cell.cellId,
|
|
131
|
+
nowMs: claimNow,
|
|
132
|
+
lease: {
|
|
133
|
+
workerId: input.workerId,
|
|
134
|
+
leaseToken,
|
|
135
|
+
expiresAtMs: boundedLeaseExpiry(input, claimNow),
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
if ((candidate.status === "leased" || candidate.status === "running") &&
|
|
140
|
+
candidate.lease !== null &&
|
|
141
|
+
candidate.lease.workerId === input.workerId &&
|
|
142
|
+
candidate.lease.leaseToken === leaseToken)
|
|
143
|
+
return current;
|
|
144
|
+
throw new TypeError("collective_statistical_campaign_cell_lease_active");
|
|
145
|
+
});
|
|
146
|
+
if (claim.outcome === "duplicate") {
|
|
147
|
+
if (ownedFence === null)
|
|
148
|
+
throw new TypeError("collective_statistical_campaign_cell_lease_active");
|
|
149
|
+
const canonicalFence = fenceFor(state, cell.cellId);
|
|
150
|
+
if (!sameExecutionFence(ownedFence, canonicalFence))
|
|
151
|
+
throw new TypeError("collective_statistical_campaign_cell_lease_active");
|
|
152
|
+
}
|
|
153
|
+
const acquiredFence = fenceFor(state, cell.cellId);
|
|
154
|
+
ownedFence = acquiredFence;
|
|
155
|
+
await transition((current) => {
|
|
156
|
+
const startNow = clock(input.now);
|
|
157
|
+
assertOperationActive(input, startNow);
|
|
158
|
+
return startCollectiveEvaluationRunV1(current, {
|
|
159
|
+
executionId: input.executionId,
|
|
160
|
+
expectedRevision: current.revision,
|
|
161
|
+
cellId: cell.cellId,
|
|
162
|
+
runner,
|
|
163
|
+
attempt,
|
|
164
|
+
nowMs: startNow,
|
|
165
|
+
fence: acquiredFence,
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
const renewLeaseV1 = async (expiresAtMs) => {
|
|
169
|
+
assertOperationActive(input, clock(input.now));
|
|
170
|
+
if (input.operationExpiresAtMs !== undefined &&
|
|
171
|
+
expiresAtMs > input.operationExpiresAtMs)
|
|
172
|
+
throw new TypeError("collective_statistical_campaign_operation_expiry_exceeded");
|
|
173
|
+
const renewalFence = ownedFence;
|
|
174
|
+
if (renewalFence === null)
|
|
175
|
+
throw new TypeError("collective_statistical_campaign_cell_lease_missing");
|
|
176
|
+
await transition((current) => renewCollectiveEvaluationCellLeaseV1(current, {
|
|
177
|
+
executionId: input.executionId,
|
|
178
|
+
expectedRevision: current.revision,
|
|
179
|
+
cellId: cell.cellId,
|
|
180
|
+
nowMs: clock(input.now),
|
|
181
|
+
fence: renewalFence,
|
|
182
|
+
expiresAtMs,
|
|
183
|
+
}));
|
|
184
|
+
ownedFence = fenceFor(state, cell.cellId);
|
|
185
|
+
};
|
|
186
|
+
const stored = await readOne(input.store, runKey, {
|
|
187
|
+
executionId: input.executionId,
|
|
188
|
+
registrationDigest: registration.registrationDigest,
|
|
189
|
+
cellId: cell.cellId,
|
|
190
|
+
fence: acquiredFence,
|
|
191
|
+
operationExpiresAtMs: input.operationExpiresAtMs ?? null,
|
|
192
|
+
});
|
|
193
|
+
let execution;
|
|
194
|
+
if (stored !== null) {
|
|
195
|
+
execution = validateStoredExecution(registration, input.executionId, cell, runner, attempt, runKey, stored);
|
|
196
|
+
resumedSlotCount += 1;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
let output;
|
|
200
|
+
// Runners are supplied by adapters and must not need to know the
|
|
201
|
+
// storage lease duration. Keep the fenced cell alive while an
|
|
202
|
+
// otherwise healthy long-running execution is in progress. The
|
|
203
|
+
// renewal chain deliberately serializes CAS transitions so it cannot
|
|
204
|
+
// race the runner's optional explicit renewal hook.
|
|
205
|
+
let renewalChain = Promise.resolve();
|
|
206
|
+
const renewalIntervalMs = Math.max(1_000, Math.floor(input.leaseDurationMs / 3));
|
|
207
|
+
const renewalTimer = setInterval(() => {
|
|
208
|
+
renewalChain = renewalChain.then(() => renewLeaseV1(safeAdd(clock(input.now), input.leaseDurationMs)));
|
|
209
|
+
}, renewalIntervalMs);
|
|
210
|
+
try {
|
|
211
|
+
assertOperationActive(input, clock(input.now));
|
|
212
|
+
output = await input.execute(deepFreezePlanning({
|
|
213
|
+
schemaVersion: 1,
|
|
214
|
+
executionId: input.executionId,
|
|
215
|
+
registrationDigest: registration.registrationDigest,
|
|
216
|
+
runKey,
|
|
217
|
+
cell,
|
|
218
|
+
runner,
|
|
219
|
+
attempt,
|
|
220
|
+
maximumInteractions: cell.maximumInteractions,
|
|
221
|
+
renewLeaseV1,
|
|
222
|
+
}));
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
// A runner exception is not an empirical outcome. Persisting a
|
|
226
|
+
// synthetic failed sample here used to hide the actual execution
|
|
227
|
+
// fault until the evaluator rejected the projection. Stop before
|
|
228
|
+
// any durable sample is committed, retaining only a tokenized,
|
|
229
|
+
// public-safe failure code for the supervisor.
|
|
230
|
+
throw new Error(`collective_statistical_campaign_runner_execution_${publicRunnerErrorCode(error)}`);
|
|
231
|
+
}
|
|
232
|
+
finally {
|
|
233
|
+
clearInterval(renewalTimer);
|
|
234
|
+
await renewalChain;
|
|
235
|
+
}
|
|
236
|
+
execution = createCollectiveStatisticalCampaignExecutionArtifactsV1({
|
|
237
|
+
schemaVersion: 1,
|
|
238
|
+
executionId: input.executionId,
|
|
239
|
+
runKey,
|
|
240
|
+
registrationDigest: registration.registrationDigest,
|
|
241
|
+
cell,
|
|
242
|
+
runner,
|
|
243
|
+
attempt,
|
|
244
|
+
output,
|
|
245
|
+
});
|
|
246
|
+
const commit = await commitExecution(input, registration.registrationDigest, cell.cellId, runKey, ownedFence, execution);
|
|
247
|
+
if (commit === "committed") {
|
|
248
|
+
executedSlotCount += 1;
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
const durable = await readOne(input.store, runKey, {
|
|
252
|
+
executionId: input.executionId,
|
|
253
|
+
registrationDigest: registration.registrationDigest,
|
|
254
|
+
cellId: cell.cellId,
|
|
255
|
+
fence: ownedFence,
|
|
256
|
+
operationExpiresAtMs: input.operationExpiresAtMs ?? null,
|
|
257
|
+
});
|
|
258
|
+
if (durable === null)
|
|
259
|
+
throw new TypeError("collective_statistical_campaign_duplicate_commit_missing");
|
|
260
|
+
execution = validateStoredExecution(registration, input.executionId, cell, runner, attempt, runKey, durable);
|
|
261
|
+
resumedSlotCount += 1;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
await transition((current) => settleStoredExecution(current, input.executionId, cell, runner, attempt, runKey, clock(input.now), execution, ownedFence));
|
|
265
|
+
completed.push(execution);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const passedSlotCount = completed.filter((execution) => execution.sample.status === "passed").length;
|
|
270
|
+
return deepFreezePlanning({
|
|
271
|
+
schemaVersion: 1,
|
|
272
|
+
executionId: input.executionId,
|
|
273
|
+
registrationDigest: registration.registrationDigest,
|
|
274
|
+
shardIndex: input.shard.index,
|
|
275
|
+
shardCount: input.shard.count,
|
|
276
|
+
selectedCellCount: selected.length,
|
|
277
|
+
executedSlotCount,
|
|
278
|
+
resumedSlotCount,
|
|
279
|
+
passedSlotCount,
|
|
280
|
+
failedSlotCount: completed.length - passedSlotCount,
|
|
281
|
+
state,
|
|
282
|
+
executions: Object.freeze(completed),
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
async function loadOrCreateExecutionState(input, registration) {
|
|
286
|
+
const loaded = await readExecutionState(input, registration);
|
|
287
|
+
if (loaded !== null)
|
|
288
|
+
return loaded;
|
|
289
|
+
const created = createCollectiveEvaluationCampaignExecutionV1({
|
|
290
|
+
schemaVersion: 1,
|
|
291
|
+
executionId: input.executionId,
|
|
292
|
+
registration,
|
|
293
|
+
});
|
|
294
|
+
const status = await compareAndSwapExecutionState(input, {
|
|
295
|
+
executionId: input.executionId,
|
|
296
|
+
expectedExecutionDigest: null,
|
|
297
|
+
state: created,
|
|
298
|
+
});
|
|
299
|
+
if (status === "expired")
|
|
300
|
+
throw new TypeError("collective_statistical_campaign_operation_authorization_expired");
|
|
301
|
+
if (status === "committed")
|
|
302
|
+
return created;
|
|
303
|
+
const raced = await readExecutionState(input, registration);
|
|
304
|
+
if (raced === null)
|
|
305
|
+
throw new TypeError("collective_statistical_campaign_state_conflict");
|
|
306
|
+
return raced;
|
|
307
|
+
}
|
|
308
|
+
async function readExecutionState(input, registration) {
|
|
309
|
+
const state = await input.store.readExecutionStateV1({
|
|
310
|
+
executionId: input.executionId,
|
|
311
|
+
registrationDigest: registration.registrationDigest,
|
|
312
|
+
});
|
|
313
|
+
return state === null
|
|
314
|
+
? null
|
|
315
|
+
: validatePersistedState(state, input.executionId, registration);
|
|
316
|
+
}
|
|
317
|
+
async function compareAndSwapExecutionState(input, mutation) {
|
|
318
|
+
if (input.operationExpiresAtMs === undefined)
|
|
319
|
+
return input.store.compareAndSwapExecutionStateV1(mutation);
|
|
320
|
+
const fenced = input.store;
|
|
321
|
+
if (typeof fenced.compareAndSwapExecutionStateWithDeadlineV1 !== "function")
|
|
322
|
+
throw new TypeError("collective_statistical_campaign_deadline_state_store_required");
|
|
323
|
+
return fenced.compareAndSwapExecutionStateWithDeadlineV1({
|
|
324
|
+
...mutation,
|
|
325
|
+
operationExpiresAtMs: input.operationExpiresAtMs,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
function validatePersistedState(state, executionId, registration) {
|
|
329
|
+
validateCollectiveEvaluationCampaignExecutionV1(state);
|
|
330
|
+
if (state.executionId !== executionId ||
|
|
331
|
+
state.registrationDigest !== registration.registrationDigest ||
|
|
332
|
+
state.cells.length !== registration.cells.length ||
|
|
333
|
+
state.cells.some((cell, index) => cell.cellId !== registration.cells[index]?.cellId ||
|
|
334
|
+
cell.runs.length !== 4))
|
|
335
|
+
throw new TypeError("collective_statistical_campaign_persisted_state_invalid");
|
|
336
|
+
for (const cell of registration.cells) {
|
|
337
|
+
for (const runner of runners) {
|
|
338
|
+
for (const attempt of attempts) {
|
|
339
|
+
const expected = collectiveEvaluationRunKeyV1({
|
|
340
|
+
executionId: state.executionId,
|
|
341
|
+
registrationDigest: registration.registrationDigest,
|
|
342
|
+
cellId: cell.cellId,
|
|
343
|
+
runner,
|
|
344
|
+
attempt,
|
|
345
|
+
});
|
|
346
|
+
if (runKeyFor(state, cell.cellId, runner, attempt) !== expected)
|
|
347
|
+
throw new TypeError("collective_statistical_campaign_persisted_state_run_key_invalid");
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return state;
|
|
352
|
+
}
|
|
353
|
+
function settlementFields(execution, cell) {
|
|
354
|
+
return execution.sample.status === "passed"
|
|
355
|
+
? {
|
|
356
|
+
result: {
|
|
357
|
+
resultDigest: execution.sample.sampleDigest,
|
|
358
|
+
traceDigest: execution.trace.traceDigest,
|
|
359
|
+
ledgerDigest: execution.ledger.ledgerDigest,
|
|
360
|
+
evidenceDigest: execution.evidence.evidenceDigest,
|
|
361
|
+
fairnessDigest: cell.scaleConfigurationDigest,
|
|
362
|
+
},
|
|
363
|
+
reasonCode: null,
|
|
364
|
+
}
|
|
365
|
+
: {
|
|
366
|
+
result: null,
|
|
367
|
+
reasonCode: execution.reasonCode ?? "runner_failed",
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
function settleStoredExecution(state, executionId, cell, runner, attempt, runKey, nowMs, execution, expectedFence = null) {
|
|
371
|
+
const currentCell = cellFor(state, cell.cellId);
|
|
372
|
+
const slot = runFor(currentCell, runner, attempt);
|
|
373
|
+
const fields = settlementFields(execution, cell);
|
|
374
|
+
if (slot.status === "succeeded" || slot.status === "failed") {
|
|
375
|
+
if (slot.settlementFence === null || slot.settledFromRevision === null)
|
|
376
|
+
throw new TypeError("collective_statistical_campaign_terminal_settlement_invalid");
|
|
377
|
+
return settleCollectiveEvaluationRunV1(state, {
|
|
378
|
+
executionId,
|
|
379
|
+
expectedRevision: slot.settledFromRevision,
|
|
380
|
+
cellId: cell.cellId,
|
|
381
|
+
runner,
|
|
382
|
+
attempt,
|
|
383
|
+
nowMs,
|
|
384
|
+
fence: slot.settlementFence,
|
|
385
|
+
...fields,
|
|
386
|
+
}).state;
|
|
387
|
+
}
|
|
388
|
+
if (slot.status !== "running" || slot.runKey !== runKey)
|
|
389
|
+
throw new TypeError("collective_statistical_campaign_run_not_settleable");
|
|
390
|
+
const currentFence = fenceFor(state, cell.cellId);
|
|
391
|
+
if (expectedFence !== null &&
|
|
392
|
+
!sameExecutionFence(expectedFence, currentFence))
|
|
393
|
+
throw new TypeError("collective_statistical_campaign_run_fence_changed");
|
|
394
|
+
return currentFence.expiresAtMs <= nowMs
|
|
395
|
+
? reconcileCollectiveEvaluationRunV1(state, {
|
|
396
|
+
executionId,
|
|
397
|
+
expectedRevision: state.revision,
|
|
398
|
+
cellId: cell.cellId,
|
|
399
|
+
runner,
|
|
400
|
+
attempt,
|
|
401
|
+
runKey: runKey,
|
|
402
|
+
nowMs,
|
|
403
|
+
...fields,
|
|
404
|
+
}).state
|
|
405
|
+
: settleCollectiveEvaluationRunV1(state, {
|
|
406
|
+
executionId,
|
|
407
|
+
expectedRevision: state.revision,
|
|
408
|
+
cellId: cell.cellId,
|
|
409
|
+
runner,
|
|
410
|
+
attempt,
|
|
411
|
+
nowMs,
|
|
412
|
+
fence: currentFence,
|
|
413
|
+
...fields,
|
|
414
|
+
}).state;
|
|
415
|
+
}
|
|
416
|
+
function sameExecutionFence(left, right) {
|
|
417
|
+
return (left.workerId === right.workerId &&
|
|
418
|
+
left.leaseToken === right.leaseToken &&
|
|
419
|
+
left.generation === right.generation &&
|
|
420
|
+
left.expiresAtMs === right.expiresAtMs);
|
|
421
|
+
}
|
|
422
|
+
function createFailedExecution(executionId, registrationDigest, runKey, cell, runner, attempt, reasonCode) {
|
|
423
|
+
return createCollectiveStatisticalCampaignExecutionArtifactsV1({
|
|
424
|
+
schemaVersion: 1,
|
|
425
|
+
executionId,
|
|
426
|
+
runKey,
|
|
427
|
+
registrationDigest,
|
|
428
|
+
cell,
|
|
429
|
+
runner,
|
|
430
|
+
attempt,
|
|
431
|
+
output: {
|
|
432
|
+
schemaVersion: 1,
|
|
433
|
+
status: "failed",
|
|
434
|
+
reasonCode,
|
|
435
|
+
outcome: { reasonCode },
|
|
436
|
+
traceRecords: [],
|
|
437
|
+
ledgerRecords: [],
|
|
438
|
+
observations: [],
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
function cellFor(state, cellId) {
|
|
443
|
+
const cell = state.cells.find((entry) => entry.cellId === cellId);
|
|
444
|
+
if (!cell)
|
|
445
|
+
throw new TypeError("collective_statistical_campaign_cell_missing");
|
|
446
|
+
return cell;
|
|
447
|
+
}
|
|
448
|
+
function runFor(cell, runner, attempt) {
|
|
449
|
+
const run = cell.runs.find((entry) => entry.runner === runner && entry.attempt === attempt);
|
|
450
|
+
if (!run)
|
|
451
|
+
throw new TypeError("collective_statistical_campaign_run_slot_missing");
|
|
452
|
+
return run;
|
|
453
|
+
}
|
|
454
|
+
/** Replays immutable commits into a fresh state and finalizes only a full closure. */
|
|
455
|
+
export function reconstructCollectiveStatisticalCampaignExecutionV1(input) {
|
|
456
|
+
if (input.schemaVersion !== 1)
|
|
457
|
+
throw new TypeError("collective_statistical_campaign_reconstruction_invalid");
|
|
458
|
+
const registration = validateCollectiveEvaluationCampaignRegistrationV1(input.registration);
|
|
459
|
+
const expectedSlotCount = registration.cells.length * 4;
|
|
460
|
+
if (input.executions.length !== expectedSlotCount)
|
|
461
|
+
throw new TypeError("collective_statistical_campaign_reconstruction_incomplete");
|
|
462
|
+
const bySlot = new Map();
|
|
463
|
+
for (const execution of input.executions) {
|
|
464
|
+
const slotKey = `${execution.cellId}\0${execution.runner}\0${execution.attempt}`;
|
|
465
|
+
if (bySlot.has(slotKey))
|
|
466
|
+
throw new TypeError("collective_statistical_campaign_reconstruction_duplicate_slot");
|
|
467
|
+
bySlot.set(slotKey, execution);
|
|
468
|
+
}
|
|
469
|
+
if (bySlot.size !== expectedSlotCount)
|
|
470
|
+
throw new TypeError("collective_statistical_campaign_reconstruction_incomplete");
|
|
471
|
+
let state = createCollectiveEvaluationCampaignExecutionV1({
|
|
472
|
+
schemaVersion: 1,
|
|
473
|
+
executionId: input.executionId,
|
|
474
|
+
registration,
|
|
475
|
+
});
|
|
476
|
+
let logicalTimeMs = 1;
|
|
477
|
+
for (const cell of registration.cells) {
|
|
478
|
+
state = claimCollectiveEvaluationCellV1(state, {
|
|
479
|
+
executionId: input.executionId,
|
|
480
|
+
expectedRevision: state.revision,
|
|
481
|
+
cellId: cell.cellId,
|
|
482
|
+
nowMs: logicalTimeMs,
|
|
483
|
+
lease: {
|
|
484
|
+
workerId: "worker:reconstruction",
|
|
485
|
+
leaseToken: `lease:reconstruction:${cell.peerCount}:${cell.stratum}:${cell.seed}`,
|
|
486
|
+
expiresAtMs: logicalTimeMs + 10_000,
|
|
487
|
+
},
|
|
488
|
+
});
|
|
489
|
+
for (const runner of runners) {
|
|
490
|
+
for (const attempt of attempts) {
|
|
491
|
+
const execution = bySlot.get(`${cell.cellId}\0${runner}\0${attempt}`);
|
|
492
|
+
if (!execution)
|
|
493
|
+
throw new TypeError("collective_statistical_campaign_reconstruction_missing_slot");
|
|
494
|
+
const runKey = runKeyFor(state, cell.cellId, runner, attempt);
|
|
495
|
+
const validatedExecution = validateStoredExecution(registration, input.executionId, cell, runner, attempt, runKey, execution);
|
|
496
|
+
const fence = fenceFor(state, cell.cellId);
|
|
497
|
+
state = startCollectiveEvaluationRunV1(state, {
|
|
498
|
+
executionId: input.executionId,
|
|
499
|
+
expectedRevision: state.revision,
|
|
500
|
+
cellId: cell.cellId,
|
|
501
|
+
runner,
|
|
502
|
+
attempt,
|
|
503
|
+
nowMs: logicalTimeMs,
|
|
504
|
+
fence,
|
|
505
|
+
});
|
|
506
|
+
state = settleCollectiveEvaluationRunV1(state, {
|
|
507
|
+
executionId: input.executionId,
|
|
508
|
+
expectedRevision: state.revision,
|
|
509
|
+
cellId: cell.cellId,
|
|
510
|
+
runner,
|
|
511
|
+
attempt,
|
|
512
|
+
nowMs: logicalTimeMs,
|
|
513
|
+
fence,
|
|
514
|
+
result: validatedExecution.sample.status === "passed"
|
|
515
|
+
? {
|
|
516
|
+
resultDigest: validatedExecution.sample.sampleDigest,
|
|
517
|
+
traceDigest: validatedExecution.trace.traceDigest,
|
|
518
|
+
ledgerDigest: validatedExecution.ledger.ledgerDigest,
|
|
519
|
+
evidenceDigest: validatedExecution.evidence.evidenceDigest,
|
|
520
|
+
fairnessDigest: cell.scaleConfigurationDigest,
|
|
521
|
+
}
|
|
522
|
+
: null,
|
|
523
|
+
reasonCode: validatedExecution.sample.status === "failed"
|
|
524
|
+
? (validatedExecution.reasonCode ?? "runner_failed")
|
|
525
|
+
: null,
|
|
526
|
+
}).state;
|
|
527
|
+
logicalTimeMs += 1;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return finalizeCollectiveEvaluationCampaignExecutionV1(state, registration, state.revision);
|
|
532
|
+
}
|
|
533
|
+
export function createMemoryCollectiveStatisticalCampaignExecutionStoreV1(clockSource = Date.now) {
|
|
534
|
+
if (typeof clockSource !== "function")
|
|
535
|
+
throw new TypeError("collective_statistical_campaign_store_clock_invalid");
|
|
536
|
+
const values = new Map();
|
|
537
|
+
const provenances = new Map();
|
|
538
|
+
const states = new Map();
|
|
539
|
+
const compareAndSwapState = (input) => {
|
|
540
|
+
if (input.state.executionId !== input.executionId)
|
|
541
|
+
throw new TypeError("collective_statistical_campaign_state_execution_conflict");
|
|
542
|
+
const existing = states.get(input.executionId);
|
|
543
|
+
if (existing === undefined) {
|
|
544
|
+
if (input.expectedExecutionDigest !== null)
|
|
545
|
+
return "conflict";
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
const current = JSON.parse(existing);
|
|
549
|
+
if (current.executionDigest !== input.expectedExecutionDigest)
|
|
550
|
+
return "conflict";
|
|
551
|
+
const serialized = json(input.state);
|
|
552
|
+
if (serialized === existing)
|
|
553
|
+
return "duplicate";
|
|
554
|
+
states.set(input.executionId, serialized);
|
|
555
|
+
return "committed";
|
|
556
|
+
}
|
|
557
|
+
states.set(input.executionId, json(input.state));
|
|
558
|
+
return "committed";
|
|
559
|
+
};
|
|
560
|
+
return Object.freeze({
|
|
561
|
+
schemaVersion: 1,
|
|
562
|
+
async readExecutionStateV1({ executionId, registrationDigest, }) {
|
|
563
|
+
const serialized = states.get(executionId);
|
|
564
|
+
if (serialized === undefined)
|
|
565
|
+
return null;
|
|
566
|
+
const state = JSON.parse(serialized);
|
|
567
|
+
if (state.registrationDigest !== registrationDigest)
|
|
568
|
+
throw new TypeError("collective_statistical_campaign_state_registration_conflict");
|
|
569
|
+
return state;
|
|
570
|
+
},
|
|
571
|
+
async compareAndSwapExecutionStateV1(input) {
|
|
572
|
+
return compareAndSwapState(input);
|
|
573
|
+
},
|
|
574
|
+
async compareAndSwapExecutionStateWithDeadlineV1(input) {
|
|
575
|
+
if (!Number.isSafeInteger(input.operationExpiresAtMs) ||
|
|
576
|
+
clock(clockSource) >= input.operationExpiresAtMs)
|
|
577
|
+
return "expired";
|
|
578
|
+
return compareAndSwapState(input);
|
|
579
|
+
},
|
|
580
|
+
async readExecutionsV1(runKeys) {
|
|
581
|
+
if (!Array.isArray(runKeys) || runKeys.length > 4_096)
|
|
582
|
+
throw new RangeError("collective_statistical_campaign_store_read_limit_exceeded");
|
|
583
|
+
return Object.freeze(runKeys.map((runKey) => {
|
|
584
|
+
requireRunKey(runKey);
|
|
585
|
+
const serialized = values.get(runKey);
|
|
586
|
+
return Object.freeze({
|
|
587
|
+
runKey,
|
|
588
|
+
execution: serialized === undefined
|
|
589
|
+
? null
|
|
590
|
+
: JSON.parse(serialized),
|
|
591
|
+
});
|
|
592
|
+
}));
|
|
593
|
+
},
|
|
594
|
+
async commitExecutionV1({ runKey, execution, }) {
|
|
595
|
+
requireRunKey(runKey);
|
|
596
|
+
const serialized = json(execution);
|
|
597
|
+
const existing = values.get(runKey);
|
|
598
|
+
if (existing !== undefined) {
|
|
599
|
+
if (existing !== serialized)
|
|
600
|
+
throw new TypeError("collective_statistical_campaign_store_commit_conflict");
|
|
601
|
+
return "duplicate";
|
|
602
|
+
}
|
|
603
|
+
values.set(runKey, serialized);
|
|
604
|
+
return "committed";
|
|
605
|
+
},
|
|
606
|
+
async readExecutionWithFenceV1({ executionId, registrationDigest, cellId, runKey, fence, operationExpiresAtMs = null, }) {
|
|
607
|
+
requireRunKey(runKey);
|
|
608
|
+
const serialized = values.get(runKey);
|
|
609
|
+
if (serialized === undefined)
|
|
610
|
+
return null;
|
|
611
|
+
const provenance = provenances.get(runKey);
|
|
612
|
+
if (provenance === undefined ||
|
|
613
|
+
provenance.executionId !== executionId ||
|
|
614
|
+
provenance.registrationDigest !== registrationDigest ||
|
|
615
|
+
provenance.cellId !== cellId ||
|
|
616
|
+
provenance.operationExpiresAtMs !== operationExpiresAtMs ||
|
|
617
|
+
!sameExecutionFence(provenance.fence, fence))
|
|
618
|
+
throw new TypeError("collective_statistical_campaign_store_commit_provenance_invalid");
|
|
619
|
+
return JSON.parse(serialized);
|
|
620
|
+
},
|
|
621
|
+
async commitExecutionWithFenceV1({ executionId, registrationDigest, cellId, runKey, fence, operationExpiresAtMs, execution, }) {
|
|
622
|
+
requireRunKey(runKey);
|
|
623
|
+
if (execution.executionId !== executionId ||
|
|
624
|
+
execution.cellId !== cellId ||
|
|
625
|
+
execution.runKey !== runKey)
|
|
626
|
+
throw new TypeError("collective_statistical_campaign_store_commit_scope_invalid");
|
|
627
|
+
const serializedState = states.get(executionId);
|
|
628
|
+
const nowMs = clock(clockSource);
|
|
629
|
+
if (serializedState === undefined)
|
|
630
|
+
return "stale_fence";
|
|
631
|
+
const state = JSON.parse(serializedState);
|
|
632
|
+
if ((operationExpiresAtMs != null &&
|
|
633
|
+
(!Number.isSafeInteger(operationExpiresAtMs) ||
|
|
634
|
+
nowMs >= operationExpiresAtMs)) ||
|
|
635
|
+
state.registrationDigest !== registrationDigest ||
|
|
636
|
+
!activeExecutionFence(state, cellId, runKey, fence, nowMs))
|
|
637
|
+
return "stale_fence";
|
|
638
|
+
const serialized = json(execution);
|
|
639
|
+
const existing = values.get(runKey);
|
|
640
|
+
if (existing !== undefined) {
|
|
641
|
+
if (existing !== serialized)
|
|
642
|
+
throw new TypeError("collective_statistical_campaign_store_commit_conflict");
|
|
643
|
+
const provenance = provenances.get(runKey);
|
|
644
|
+
if (provenance === undefined ||
|
|
645
|
+
provenance.executionId !== executionId ||
|
|
646
|
+
provenance.registrationDigest !== registrationDigest ||
|
|
647
|
+
provenance.cellId !== cellId ||
|
|
648
|
+
provenance.operationExpiresAtMs !== (operationExpiresAtMs ?? null) ||
|
|
649
|
+
!sameExecutionFence(provenance.fence, fence))
|
|
650
|
+
throw new TypeError("collective_statistical_campaign_store_commit_provenance_invalid");
|
|
651
|
+
return "duplicate";
|
|
652
|
+
}
|
|
653
|
+
values.set(runKey, serialized);
|
|
654
|
+
provenances.set(runKey, Object.freeze({
|
|
655
|
+
executionId,
|
|
656
|
+
registrationDigest,
|
|
657
|
+
cellId,
|
|
658
|
+
operationExpiresAtMs: operationExpiresAtMs ?? null,
|
|
659
|
+
fence: Object.freeze({ ...fence }),
|
|
660
|
+
}));
|
|
661
|
+
return "committed";
|
|
662
|
+
},
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
async function commitExecution(input, registrationDigest, cellId, runKey, fence, execution) {
|
|
666
|
+
assertOperationActive(input, clock(input.now));
|
|
667
|
+
const store = input.store;
|
|
668
|
+
if (typeof store.commitExecutionWithFenceV1 !== "function")
|
|
669
|
+
return store.commitExecutionV1({ runKey, execution });
|
|
670
|
+
const status = await store.commitExecutionWithFenceV1({
|
|
671
|
+
executionId: input.executionId,
|
|
672
|
+
registrationDigest,
|
|
673
|
+
cellId,
|
|
674
|
+
runKey,
|
|
675
|
+
fence,
|
|
676
|
+
operationExpiresAtMs: input.operationExpiresAtMs ?? null,
|
|
677
|
+
execution,
|
|
678
|
+
});
|
|
679
|
+
if (status === "stale_fence")
|
|
680
|
+
throw new TypeError("collective_statistical_campaign_execution_commit_stale_fence");
|
|
681
|
+
if (status !== "committed" && status !== "duplicate")
|
|
682
|
+
throw new TypeError("collective_statistical_campaign_execution_commit_invalid");
|
|
683
|
+
return status;
|
|
684
|
+
}
|
|
685
|
+
function activeExecutionFence(state, cellId, runKey, fence, nowMs) {
|
|
686
|
+
const cell = state.cells.find((candidate) => candidate.cellId === cellId);
|
|
687
|
+
const current = cell?.lease;
|
|
688
|
+
const slot = cell?.runs.find((candidate) => candidate.runKey === runKey);
|
|
689
|
+
return (cell?.status === "running" &&
|
|
690
|
+
slot?.status === "running" &&
|
|
691
|
+
current !== null &&
|
|
692
|
+
current !== undefined &&
|
|
693
|
+
current.expiresAtMs > nowMs &&
|
|
694
|
+
sameExecutionFence(current, fence));
|
|
695
|
+
}
|
|
696
|
+
function validateExecutorInput(input) {
|
|
697
|
+
if (!input ||
|
|
698
|
+
input.schemaVersion !== 1 ||
|
|
699
|
+
typeof input.executionId !== "string" ||
|
|
700
|
+
!/^[A-Za-z0-9][A-Za-z0-9._:@/-]*$/u.test(input.executionId) ||
|
|
701
|
+
typeof input.workerId !== "string" ||
|
|
702
|
+
!/^[A-Za-z0-9][A-Za-z0-9._:@/-]*$/u.test(input.workerId) ||
|
|
703
|
+
!input.shard ||
|
|
704
|
+
input.shard.schemaVersion !== 1 ||
|
|
705
|
+
!Number.isSafeInteger(input.shard.index) ||
|
|
706
|
+
!Number.isSafeInteger(input.shard.count) ||
|
|
707
|
+
input.shard.count < 1 ||
|
|
708
|
+
input.shard.count > 256 ||
|
|
709
|
+
input.shard.index < 0 ||
|
|
710
|
+
input.shard.index >= input.shard.count ||
|
|
711
|
+
!Number.isSafeInteger(input.leaseDurationMs) ||
|
|
712
|
+
input.leaseDurationMs < 1_000 ||
|
|
713
|
+
input.leaseDurationMs > 86_400_000 ||
|
|
714
|
+
!Number.isSafeInteger(input.maximumCells) ||
|
|
715
|
+
input.maximumCells < 1 ||
|
|
716
|
+
input.maximumCells > 240 ||
|
|
717
|
+
(input.operationExpiresAtMs !== undefined &&
|
|
718
|
+
(!Number.isSafeInteger(input.operationExpiresAtMs) ||
|
|
719
|
+
input.operationExpiresAtMs < 0)) ||
|
|
720
|
+
!input.store ||
|
|
721
|
+
input.store.schemaVersion !== 1 ||
|
|
722
|
+
typeof input.store.readExecutionStateV1 !== "function" ||
|
|
723
|
+
typeof input.store.compareAndSwapExecutionStateV1 !== "function" ||
|
|
724
|
+
typeof input.store.readExecutionsV1 !== "function" ||
|
|
725
|
+
typeof input.store.commitExecutionV1 !== "function" ||
|
|
726
|
+
typeof input.now !== "function" ||
|
|
727
|
+
typeof input.execute !== "function")
|
|
728
|
+
throw new TypeError("collective_statistical_campaign_executor_input_invalid");
|
|
729
|
+
}
|
|
730
|
+
function selectExplicitCells(registration, cellIds) {
|
|
731
|
+
if (!Array.isArray(cellIds) || cellIds.length === 0 || cellIds.length > 240)
|
|
732
|
+
throw new TypeError("collective_statistical_campaign_executor_cell_ids_invalid");
|
|
733
|
+
const expected = new Set(registration.cells.map((cell) => cell.cellId));
|
|
734
|
+
const selected = [];
|
|
735
|
+
const seen = new Set();
|
|
736
|
+
for (const cellId of cellIds) {
|
|
737
|
+
if (typeof cellId !== "string" || !expected.has(cellId) || seen.has(cellId))
|
|
738
|
+
throw new TypeError("collective_statistical_campaign_executor_cell_ids_invalid");
|
|
739
|
+
seen.add(cellId);
|
|
740
|
+
const cell = registration.cells.find((entry) => entry.cellId === cellId);
|
|
741
|
+
if (!cell)
|
|
742
|
+
throw new TypeError("collective_statistical_campaign_executor_cell_ids_invalid");
|
|
743
|
+
selected.push(cell);
|
|
744
|
+
}
|
|
745
|
+
return Object.freeze(selected);
|
|
746
|
+
}
|
|
747
|
+
async function readOne(store, runKey, provenance) {
|
|
748
|
+
const fenced = store;
|
|
749
|
+
if (provenance !== undefined &&
|
|
750
|
+
typeof fenced.readExecutionWithFenceV1 === "function")
|
|
751
|
+
return fenced.readExecutionWithFenceV1({
|
|
752
|
+
...provenance,
|
|
753
|
+
runKey,
|
|
754
|
+
});
|
|
755
|
+
const result = await store.readExecutionsV1([runKey]);
|
|
756
|
+
if (!Array.isArray(result) ||
|
|
757
|
+
result.length !== 1 ||
|
|
758
|
+
result[0]?.runKey !== runKey)
|
|
759
|
+
throw new TypeError("collective_statistical_campaign_store_read_invalid");
|
|
760
|
+
return result[0].execution;
|
|
761
|
+
}
|
|
762
|
+
function assertOperationActive(input, nowMs) {
|
|
763
|
+
if (input.operationExpiresAtMs !== undefined &&
|
|
764
|
+
nowMs >= input.operationExpiresAtMs)
|
|
765
|
+
throw new TypeError("collective_statistical_campaign_operation_authorization_expired");
|
|
766
|
+
}
|
|
767
|
+
function boundedLeaseExpiry(input, nowMs) {
|
|
768
|
+
const requested = safeAdd(nowMs, input.leaseDurationMs);
|
|
769
|
+
const expiresAtMs = input.operationExpiresAtMs === undefined
|
|
770
|
+
? requested
|
|
771
|
+
: Math.min(requested, input.operationExpiresAtMs);
|
|
772
|
+
if (expiresAtMs <= nowMs)
|
|
773
|
+
throw new TypeError("collective_statistical_campaign_operation_authorization_expired");
|
|
774
|
+
return expiresAtMs;
|
|
775
|
+
}
|
|
776
|
+
function validateStoredExecution(registration, executionId, cell, runner, attempt, runKey, execution) {
|
|
777
|
+
if (runKey !==
|
|
778
|
+
collectiveEvaluationRunKeyV1({
|
|
779
|
+
executionId,
|
|
780
|
+
registrationDigest: registration.registrationDigest,
|
|
781
|
+
cellId: cell.cellId,
|
|
782
|
+
runner,
|
|
783
|
+
attempt,
|
|
784
|
+
}) ||
|
|
785
|
+
execution.executionId !== executionId ||
|
|
786
|
+
execution.runKey !== runKey ||
|
|
787
|
+
execution.cellId !== cell.cellId ||
|
|
788
|
+
execution.seed !== cell.seed ||
|
|
789
|
+
execution.runner !== runner ||
|
|
790
|
+
execution.attempt !== attempt)
|
|
791
|
+
throw new TypeError("collective_statistical_campaign_stored_execution_scope_invalid");
|
|
792
|
+
const validated = createCollectiveStatisticalCampaignExecutionArtifactsV1({
|
|
793
|
+
schemaVersion: 1,
|
|
794
|
+
executionId,
|
|
795
|
+
runKey,
|
|
796
|
+
registrationDigest: registration.registrationDigest,
|
|
797
|
+
cell,
|
|
798
|
+
runner,
|
|
799
|
+
attempt,
|
|
800
|
+
output: {
|
|
801
|
+
schemaVersion: 1,
|
|
802
|
+
status: execution.sample.status,
|
|
803
|
+
reasonCode: execution.reasonCode,
|
|
804
|
+
outcome: execution.sample.outcome,
|
|
805
|
+
traceRecords: execution.trace.records,
|
|
806
|
+
ledgerRecords: execution.ledger.records,
|
|
807
|
+
observations: execution.evidence.observations,
|
|
808
|
+
},
|
|
809
|
+
});
|
|
810
|
+
if (json(validated) !== json(execution))
|
|
811
|
+
throw new TypeError("collective_statistical_campaign_stored_execution_invalid");
|
|
812
|
+
return validated;
|
|
813
|
+
}
|
|
814
|
+
function runKeyFor(state, cellId, runner, attempt) {
|
|
815
|
+
const cell = state.cells.find((entry) => entry.cellId === cellId);
|
|
816
|
+
const slot = cell?.runs.find((entry) => entry.runner === runner && entry.attempt === attempt);
|
|
817
|
+
if (!slot)
|
|
818
|
+
throw new TypeError("collective_statistical_campaign_run_slot_missing");
|
|
819
|
+
return slot.runKey;
|
|
820
|
+
}
|
|
821
|
+
function fenceFor(state, cellId) {
|
|
822
|
+
const lease = state.cells.find((cell) => cell.cellId === cellId)?.lease;
|
|
823
|
+
if (!lease)
|
|
824
|
+
throw new TypeError("collective_statistical_campaign_cell_not_leased");
|
|
825
|
+
return lease;
|
|
826
|
+
}
|
|
827
|
+
function requireRunKey(value) {
|
|
828
|
+
if (typeof value !== "string" || !/^sha256:[0-9a-f]{64}$/u.test(value))
|
|
829
|
+
throw new TypeError("collective_statistical_campaign_run_key_invalid");
|
|
830
|
+
}
|
|
831
|
+
function clock(now) {
|
|
832
|
+
const value = now();
|
|
833
|
+
if (!Number.isSafeInteger(value) || value < 0)
|
|
834
|
+
throw new TypeError("collective_statistical_campaign_clock_invalid");
|
|
835
|
+
return value;
|
|
836
|
+
}
|
|
837
|
+
function safeAdd(left, right) {
|
|
838
|
+
const value = left + right;
|
|
839
|
+
if (!Number.isSafeInteger(value))
|
|
840
|
+
throw new RangeError("collective_statistical_campaign_time_overflow");
|
|
841
|
+
return value;
|
|
842
|
+
}
|
|
843
|
+
function publicRunnerErrorCode(error) {
|
|
844
|
+
const message = error instanceof Error ? error.message : "";
|
|
845
|
+
return /^[a-z0-9_]{1,120}$/u.test(message) ? message : "invalid";
|
|
846
|
+
}
|
|
847
|
+
function json(value) {
|
|
848
|
+
return canonicalizePlanningJsonV1(value, {
|
|
849
|
+
maximumBytes: 67_108_864,
|
|
850
|
+
maximumDepth: 64,
|
|
851
|
+
maximumNodes: 2_000_000,
|
|
852
|
+
maximumKeysPerObject: 4_096,
|
|
853
|
+
maximumItemsPerArray: 262_144,
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
//# sourceMappingURL=collective-statistical-campaign-executor.js.map
|