@openprose/reactor-cradle 0.1.0-rc.1
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/LICENSE +21 -0
- package/README.md +270 -0
- package/dist/assert/index.d.ts +35 -0
- package/dist/assert/index.d.ts.map +1 -0
- package/dist/assert/index.js +398 -0
- package/dist/baselines/cost-thesis/index.d.ts +103 -0
- package/dist/baselines/cost-thesis/index.d.ts.map +1 -0
- package/dist/baselines/cost-thesis/index.js +337 -0
- package/dist/baselines/naive-loop/index.d.ts +46 -0
- package/dist/baselines/naive-loop/index.d.ts.map +1 -0
- package/dist/baselines/naive-loop/index.js +188 -0
- package/dist/baselines/no-memo/index.d.ts +84 -0
- package/dist/baselines/no-memo/index.d.ts.map +1 -0
- package/dist/baselines/no-memo/index.js +226 -0
- package/dist/doubles/clock.d.ts +9 -0
- package/dist/doubles/clock.d.ts.map +1 -0
- package/dist/doubles/clock.js +42 -0
- package/dist/doubles/storage.d.ts +24 -0
- package/dist/doubles/storage.d.ts.map +1 -0
- package/dist/doubles/storage.js +191 -0
- package/dist/eval/index.d.ts +204 -0
- package/dist/eval/index.d.ts.map +1 -0
- package/dist/eval/index.js +596 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/policy-author/index.d.ts +103 -0
- package/dist/policy-author/index.d.ts.map +1 -0
- package/dist/policy-author/index.js +358 -0
- package/dist/policy-drift/index.d.ts +64 -0
- package/dist/policy-drift/index.d.ts.map +1 -0
- package/dist/policy-drift/index.js +495 -0
- package/dist/policy-replay/index.d.ts +106 -0
- package/dist/policy-replay/index.d.ts.map +1 -0
- package/dist/policy-replay/index.js +361 -0
- package/dist/provider-parity/index.d.ts +91 -0
- package/dist/provider-parity/index.d.ts.map +1 -0
- package/dist/provider-parity/index.js +439 -0
- package/dist/recompile/index.d.ts +161 -0
- package/dist/recompile/index.d.ts.map +1 -0
- package/dist/recompile/index.js +690 -0
- package/dist/release-candidate/index.d.ts +139 -0
- package/dist/release-candidate/index.d.ts.map +1 -0
- package/dist/release-candidate/index.js +553 -0
- package/dist/release-parity/index.d.ts +80 -0
- package/dist/release-parity/index.d.ts.map +1 -0
- package/dist/release-parity/index.js +1035 -0
- package/dist/replay/model-gateway.d.ts +25 -0
- package/dist/replay/model-gateway.d.ts.map +1 -0
- package/dist/replay/model-gateway.js +166 -0
- package/dist/replay/parity.d.ts +110 -0
- package/dist/replay/parity.d.ts.map +1 -0
- package/dist/replay/parity.js +232 -0
- package/dist/rollback/index.d.ts +106 -0
- package/dist/rollback/index.d.ts.map +1 -0
- package/dist/rollback/index.js +694 -0
- package/dist/scenario/parser.d.ts +11 -0
- package/dist/scenario/parser.d.ts.map +1 -0
- package/dist/scenario/parser.js +490 -0
- package/dist/scenario/runner.d.ts +12 -0
- package/dist/scenario/runner.d.ts.map +1 -0
- package/dist/scenario/runner.js +345 -0
- package/dist/scenario/synthetic-world-adapter.d.ts +4 -0
- package/dist/scenario/synthetic-world-adapter.d.ts.map +1 -0
- package/dist/scenario/synthetic-world-adapter.js +82 -0
- package/dist/scenario/time.d.ts +4 -0
- package/dist/scenario/time.d.ts.map +1 -0
- package/dist/scenario/time.js +45 -0
- package/dist/scenario/types.d.ts +149 -0
- package/dist/scenario/types.d.ts.map +1 -0
- package/dist/scenario/types.js +5 -0
- package/dist/spikes/index.d.ts +10 -0
- package/dist/spikes/index.d.ts.map +1 -0
- package/dist/spikes/index.js +29 -0
- package/dist/spikes/k1-ensemble-spread.d.ts +88 -0
- package/dist/spikes/k1-ensemble-spread.d.ts.map +1 -0
- package/dist/spikes/k1-ensemble-spread.js +396 -0
- package/dist/spikes/k2-policy-author.d.ts +25 -0
- package/dist/spikes/k2-policy-author.d.ts.map +1 -0
- package/dist/spikes/k2-policy-author.js +503 -0
- package/dist/spikes/live-refresh.d.ts +150 -0
- package/dist/spikes/live-refresh.d.ts.map +1 -0
- package/dist/spikes/live-refresh.js +511 -0
- package/dist/world/index.d.ts +2 -0
- package/dist/world/index.d.ts.map +1 -0
- package/dist/world/index.js +17 -0
- package/dist/world/synthetic-world.d.ts +104 -0
- package/dist/world/synthetic-world.d.ts.map +1 -0
- package/dist/world/synthetic-world.js +449 -0
- package/package.json +139 -0
|
@@ -0,0 +1,1035 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildRecordedR6ReleaseParityEvalResultV0 = exports.R6_RELEASE_PARITY_DEFERRED_CASE_IDS_V0 = exports.R6_RELEASE_PARITY_CASE_IDS_V0 = exports.R6_RELEASE_PARITY_PROOF_VERSION_V0 = exports.R6_RELEASE_PARITY_PROOF_SCHEMA_V0 = exports.R6_RELEASE_PARITY_SUITE_VERSION_V0 = exports.R6_RELEASE_PARITY_SUITE_SCHEMA_V0 = void 0;
|
|
4
|
+
exports.makeRecordedR6ReleaseParitySuiteV0 = makeRecordedR6ReleaseParitySuiteV0;
|
|
5
|
+
exports.makeR6ReleaseParityReplayInputV0 = makeR6ReleaseParityReplayInputV0;
|
|
6
|
+
exports.runRecordedR6ReleaseParityProofV0 = runRecordedR6ReleaseParityProofV0;
|
|
7
|
+
exports.buildR6ReleaseParityEvalResultV0 = buildR6ReleaseParityEvalResultV0;
|
|
8
|
+
exports.assertRecordedR6ReleaseParitySuiteV0 = assertRecordedR6ReleaseParitySuiteV0;
|
|
9
|
+
exports.assertR6ReleaseParityCaseV0 = assertR6ReleaseParityCaseV0;
|
|
10
|
+
const node_crypto_1 = require("node:crypto");
|
|
11
|
+
const eval_1 = require("../eval");
|
|
12
|
+
const parity_1 = require("../replay/parity");
|
|
13
|
+
const { dependencyReceiptPinFromVerifiedReceiptV0, evaluateTransitiveFreshnessV0, verifyUpstreamReceiptDependencyPinV0, } = loadReactorComposition();
|
|
14
|
+
const { evaluateForecastScheduleV0 } = loadReactorForecast();
|
|
15
|
+
const { computeMemoKeyV0, createMemoHitReceiptV0 } = loadReactorMemo();
|
|
16
|
+
const { canonicalizeForReceiptV0, createReceiptV0, hashCanonicalReceiptV0, verifyReceiptV0, } = loadReactorReceipt();
|
|
17
|
+
exports.R6_RELEASE_PARITY_SUITE_SCHEMA_V0 = "openprose.reactor-cradle.r6-release-parity-suite";
|
|
18
|
+
exports.R6_RELEASE_PARITY_SUITE_VERSION_V0 = 0;
|
|
19
|
+
exports.R6_RELEASE_PARITY_PROOF_SCHEMA_V0 = "openprose.reactor-cradle.r6-release-parity-proof";
|
|
20
|
+
exports.R6_RELEASE_PARITY_PROOF_VERSION_V0 = 0;
|
|
21
|
+
exports.R6_RELEASE_PARITY_CASE_IDS_V0 = [
|
|
22
|
+
"healthy-quiet",
|
|
23
|
+
"drifting-schedules-fulfillment",
|
|
24
|
+
"blocked-human-review",
|
|
25
|
+
"forecast-pulls-judge-earlier",
|
|
26
|
+
"hysteresis-prevents-flip-flop",
|
|
27
|
+
"duplicate-event-idempotency",
|
|
28
|
+
"stale-status-fencing",
|
|
29
|
+
"contract-revision-fencing",
|
|
30
|
+
"policy-recompile-byte-identical-registry",
|
|
31
|
+
"memoized-verdict-zero-fresh-tokens",
|
|
32
|
+
];
|
|
33
|
+
exports.R6_RELEASE_PARITY_DEFERRED_CASE_IDS_V0 = [
|
|
34
|
+
"down-after-budget-exhaustion",
|
|
35
|
+
];
|
|
36
|
+
const SUITE_ID = "v0.4-r6-release-parity-fixture-floor";
|
|
37
|
+
const GENERATED_AT = "2026-05-18T22:30:00.000Z";
|
|
38
|
+
const INITIAL_INSTANT = "2026-05-18T12:00:00.000Z";
|
|
39
|
+
const FINAL_INSTANT = "2026-05-19T12:00:00.000Z";
|
|
40
|
+
const CONTRACT_REVISION = hashText("r6-release-parity-contract-v0");
|
|
41
|
+
const ATTACKER_CONTRACT_REVISION = hashText("r6-release-parity-attacker-contract-v0");
|
|
42
|
+
const POLICY_ARTIFACT_BYTES = canonicalizeForReceiptV0({
|
|
43
|
+
schema: "openprose.reactor.policy-artifact",
|
|
44
|
+
v: 0,
|
|
45
|
+
name: "r6.release-parity.recorded",
|
|
46
|
+
cadence: "recorded-fixture-floor",
|
|
47
|
+
});
|
|
48
|
+
const POLICY_ARTIFACT_CONTENT_HASH = hashCanonicalReceiptV0(POLICY_ARTIFACT_BYTES);
|
|
49
|
+
const TRANSITIVE_FRESHNESS_POLICY_REF = "policy://cradle.r6.release-parity@policy-v0#transitive-freshness-v0";
|
|
50
|
+
const MEMO_NAMESPACE = {
|
|
51
|
+
policy_artifact_namespace: "cradle.r6.release-parity",
|
|
52
|
+
policy_artifact_revision: "policy-v0",
|
|
53
|
+
};
|
|
54
|
+
function makeRecordedR6ReleaseParitySuiteV0() {
|
|
55
|
+
const cases = Object.freeze([
|
|
56
|
+
makeHealthyQuietCase(),
|
|
57
|
+
makeDriftingSchedulesFulfillmentCase(),
|
|
58
|
+
makeBlockedHumanReviewCase(),
|
|
59
|
+
makeForecastPullsJudgeEarlierCase(),
|
|
60
|
+
makeHysteresisPreventsFlipFlopCase(),
|
|
61
|
+
makeDuplicateEventIdempotencyCase(),
|
|
62
|
+
makeStaleStatusFencingCase(),
|
|
63
|
+
makeContractRevisionFencingCase(),
|
|
64
|
+
makePolicyRecompileByteIdenticalRegistryCase(),
|
|
65
|
+
makeMemoizedVerdictZeroFreshTokensCase(),
|
|
66
|
+
]);
|
|
67
|
+
const registry = makeRegistrySnapshot();
|
|
68
|
+
return Object.freeze({
|
|
69
|
+
schema: exports.R6_RELEASE_PARITY_SUITE_SCHEMA_V0,
|
|
70
|
+
v: exports.R6_RELEASE_PARITY_SUITE_VERSION_V0,
|
|
71
|
+
suite_id: SUITE_ID,
|
|
72
|
+
generated_at: GENERATED_AT,
|
|
73
|
+
as_of: FINAL_INSTANT,
|
|
74
|
+
initial_instant: INITIAL_INSTANT,
|
|
75
|
+
final_instant: FINAL_INSTANT,
|
|
76
|
+
world_profile: "recorded-release-parity",
|
|
77
|
+
cassette_path: "recorded://openprose/reactor-cradle/r6-release-parity-v0",
|
|
78
|
+
registry,
|
|
79
|
+
registry_content_hash: hashCanonicalValue(registry),
|
|
80
|
+
cases,
|
|
81
|
+
deferred_cases: Object.freeze([
|
|
82
|
+
Object.freeze({
|
|
83
|
+
case_id: "down-after-budget-exhaustion",
|
|
84
|
+
reason: "Current Reactor/Cradle APIs do not yet expose a typed retry-budget or pressure-dispatch primitive, so R6 records the gap instead of synthesizing a false down proof.",
|
|
85
|
+
missing_primitives: Object.freeze([
|
|
86
|
+
"typed retry/budget ledger",
|
|
87
|
+
"pressure-dispatch claim with exhausted-budget receipt evidence",
|
|
88
|
+
]),
|
|
89
|
+
boundary: "not-represented-in-r6",
|
|
90
|
+
}),
|
|
91
|
+
]),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function makeR6ReleaseParityReplayInputV0(suite = makeRecordedR6ReleaseParitySuiteV0()) {
|
|
95
|
+
assertRecordedR6ReleaseParitySuiteV0(suite);
|
|
96
|
+
return {
|
|
97
|
+
scenario_id: suite.suite_id,
|
|
98
|
+
world_profile: suite.world_profile,
|
|
99
|
+
cassette_path: suite.cassette_path,
|
|
100
|
+
initial_instant: suite.initial_instant,
|
|
101
|
+
final_instant: suite.final_instant,
|
|
102
|
+
trace: suite.cases.map((item) => ({
|
|
103
|
+
case_id: item.case_id,
|
|
104
|
+
trace: item.trace,
|
|
105
|
+
})),
|
|
106
|
+
receipt_log: {
|
|
107
|
+
entries: suite.cases.flatMap((item) => item.receipts),
|
|
108
|
+
},
|
|
109
|
+
expected_relationships: suite.cases.map((item) => ({
|
|
110
|
+
case_id: item.case_id,
|
|
111
|
+
relationships: item.expected_relationships,
|
|
112
|
+
})),
|
|
113
|
+
decisions: {
|
|
114
|
+
entries: suite.cases.flatMap((item) => item.decisions),
|
|
115
|
+
deferred_cases: suite.deferred_cases,
|
|
116
|
+
},
|
|
117
|
+
registry: suite.registry,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function runRecordedR6ReleaseParityProofV0(suite = makeRecordedR6ReleaseParitySuiteV0()) {
|
|
121
|
+
assertRecordedR6ReleaseParitySuiteV0(suite);
|
|
122
|
+
const replayInput = makeR6ReleaseParityReplayInputV0(suite);
|
|
123
|
+
const assertions = Object.freeze(suite.cases.map(assertR6ReleaseParityCaseV0));
|
|
124
|
+
const parity = (0, parity_1.runReplayParityMatrixV0)({
|
|
125
|
+
baseline: replayInput,
|
|
126
|
+
runAdapter() {
|
|
127
|
+
return makeR6ReleaseParityReplayInputV0(suite);
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
if (!parity.ok) {
|
|
131
|
+
throw new Error("R6 release parity fixture floor must be byte-identical");
|
|
132
|
+
}
|
|
133
|
+
const failedAssertion = assertions.find((item) => item.status === "fail");
|
|
134
|
+
if (failedAssertion !== undefined) {
|
|
135
|
+
throw new Error(`R6 release parity assertion failed: ${failedAssertion.summary}`);
|
|
136
|
+
}
|
|
137
|
+
return Object.freeze({
|
|
138
|
+
schema: exports.R6_RELEASE_PARITY_PROOF_SCHEMA_V0,
|
|
139
|
+
v: exports.R6_RELEASE_PARITY_PROOF_VERSION_V0,
|
|
140
|
+
suite,
|
|
141
|
+
replay_snapshot: (0, parity_1.snapshotReplayComparableOutputV0)(replayInput),
|
|
142
|
+
parity,
|
|
143
|
+
assertions,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function buildR6ReleaseParityEvalResultV0(proof = runRecordedR6ReleaseParityProofV0()) {
|
|
147
|
+
return (0, eval_1.buildCradleEvalResultV0)({
|
|
148
|
+
suite_id: proof.suite.suite_id,
|
|
149
|
+
generated_at: proof.suite.generated_at,
|
|
150
|
+
as_of: proof.suite.as_of,
|
|
151
|
+
model_matrix: {
|
|
152
|
+
status: "not-run",
|
|
153
|
+
reason: "R6 release parity uses deterministic recorded Cradle fixtures; live provider/model matrix remains deferred.",
|
|
154
|
+
},
|
|
155
|
+
cases: proof.suite.cases.map((item, index) => {
|
|
156
|
+
const assertion = proof.assertions[index];
|
|
157
|
+
if (assertion === undefined) {
|
|
158
|
+
throw new Error(`missing release parity assertion for ${item.case_id}`);
|
|
159
|
+
}
|
|
160
|
+
const base = {
|
|
161
|
+
case_id: item.case_id,
|
|
162
|
+
scenario_id: proof.suite.suite_id,
|
|
163
|
+
assertions: [assertion],
|
|
164
|
+
evidence_hashes: item.evidence_hashes,
|
|
165
|
+
};
|
|
166
|
+
return index === 0 ? { ...base, replay: proof.parity } : base;
|
|
167
|
+
}),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
exports.buildRecordedR6ReleaseParityEvalResultV0 = buildR6ReleaseParityEvalResultV0;
|
|
171
|
+
function assertRecordedR6ReleaseParitySuiteV0(suite) {
|
|
172
|
+
if (suite.schema !== exports.R6_RELEASE_PARITY_SUITE_SCHEMA_V0) {
|
|
173
|
+
throw new Error("R6 release parity suite schema is malformed");
|
|
174
|
+
}
|
|
175
|
+
if (suite.v !== exports.R6_RELEASE_PARITY_SUITE_VERSION_V0) {
|
|
176
|
+
throw new Error("R6 release parity suite version must be 0");
|
|
177
|
+
}
|
|
178
|
+
assertRequiredCaseCoverage(suite.cases);
|
|
179
|
+
if (!suite.deferred_cases.some((item) => item.case_id === "down-after-budget-exhaustion")) {
|
|
180
|
+
throw new Error("R6 suite must explicitly defer down-after-budget-exhaustion");
|
|
181
|
+
}
|
|
182
|
+
for (const item of suite.cases) {
|
|
183
|
+
assertR6ReleaseParityCaseV0(item);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function assertR6ReleaseParityCaseV0(item) {
|
|
187
|
+
const failures = [];
|
|
188
|
+
const receiptHashes = new Set(item.receipts.map((receipt) => receipt.content_hash));
|
|
189
|
+
if (item.decisions.length === 0) {
|
|
190
|
+
failures.push({
|
|
191
|
+
path: `${item.case_id}.decisions`,
|
|
192
|
+
message: "release-parity case must include at least one recorded decision",
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
if (item.trace.length === 0) {
|
|
196
|
+
failures.push({
|
|
197
|
+
path: `${item.case_id}.trace`,
|
|
198
|
+
message: "release-parity case must include recorded trace evidence",
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
for (const [index, receipt] of item.receipts.entries()) {
|
|
202
|
+
const verification = verifyReceiptV0(receipt);
|
|
203
|
+
if (!verification.ok) {
|
|
204
|
+
failures.push({
|
|
205
|
+
path: `${item.case_id}.receipts[${index}]`,
|
|
206
|
+
message: "recorded receipt must verify",
|
|
207
|
+
observed: verification.errors,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
else if (verification.content_hash !== receipt.content_hash) {
|
|
211
|
+
failures.push({
|
|
212
|
+
path: `${item.case_id}.receipts[${index}].content_hash`,
|
|
213
|
+
message: "recorded receipt content hash must match canonical hash",
|
|
214
|
+
observed: {
|
|
215
|
+
expected: verification.content_hash,
|
|
216
|
+
actual: receipt.content_hash,
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
for (const [index, decision] of item.decisions.entries()) {
|
|
222
|
+
if (decision.case_id !== item.case_id) {
|
|
223
|
+
failures.push({
|
|
224
|
+
path: `${item.case_id}.decisions[${index}].case_id`,
|
|
225
|
+
message: "decision must be scoped to its release-parity case",
|
|
226
|
+
observed: decision.case_id,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
for (const receiptHash of decision.receipt_hashes) {
|
|
230
|
+
if (!receiptHashes.has(receiptHash)) {
|
|
231
|
+
failures.push({
|
|
232
|
+
path: `${item.case_id}.decisions[${index}].receipt_hashes`,
|
|
233
|
+
message: "decision referenced a receipt outside the case",
|
|
234
|
+
observed: receiptHash,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const tokenSummary = sumTokens(item.receipts);
|
|
239
|
+
if (decision.token_summary.fresh !== tokenSummary.fresh ||
|
|
240
|
+
decision.token_summary.reused !== tokenSummary.reused) {
|
|
241
|
+
failures.push({
|
|
242
|
+
path: `${item.case_id}.decisions[${index}].token_summary`,
|
|
243
|
+
message: "decision token summary must match recorded receipts",
|
|
244
|
+
observed: {
|
|
245
|
+
expected: tokenSummary,
|
|
246
|
+
actual: decision.token_summary,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (failures.length > 0) {
|
|
252
|
+
return releaseParityResult("fail", `${item.case_id} release-parity fixture is malformed`, failures);
|
|
253
|
+
}
|
|
254
|
+
return releaseParityResult("pass", `${item.case_id} is represented by deterministic recorded ${item.represented_by}`, [
|
|
255
|
+
{
|
|
256
|
+
path: item.case_id,
|
|
257
|
+
message: "checked release-parity recorded fixture",
|
|
258
|
+
observed: {
|
|
259
|
+
receipts: item.receipts.length,
|
|
260
|
+
decisions: item.decisions.length,
|
|
261
|
+
trace_entries: item.trace.length,
|
|
262
|
+
relationships: item.expected_relationships,
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
]);
|
|
266
|
+
}
|
|
267
|
+
function makeHealthyQuietCase() {
|
|
268
|
+
const evidence = hashText("r6 healthy quiet incident feed unchanged");
|
|
269
|
+
const receipt = makeReceipt({
|
|
270
|
+
case_id: "healthy-quiet",
|
|
271
|
+
memo_key: "r6:healthy-quiet:bootstrap",
|
|
272
|
+
evidence_input_ids: [evidence],
|
|
273
|
+
as_of: "2026-05-18T12:00:00.000Z",
|
|
274
|
+
next_forecast_recheck: "2026-05-18T18:00:00.000Z",
|
|
275
|
+
event_cause: "real-input",
|
|
276
|
+
status: "up",
|
|
277
|
+
tokens: { fresh: 13, reused: 0 },
|
|
278
|
+
tags: ["r6", "healthy-quiet"],
|
|
279
|
+
});
|
|
280
|
+
return makeCase({
|
|
281
|
+
case_id: "healthy-quiet",
|
|
282
|
+
title: "Healthy quiet path keeps the responsibility up",
|
|
283
|
+
represented_by: "up receipt and no-op decision",
|
|
284
|
+
receipts: [receipt],
|
|
285
|
+
decisions: [
|
|
286
|
+
makeDecision({
|
|
287
|
+
case_id: "healthy-quiet",
|
|
288
|
+
as_of: receipt.core.as_of,
|
|
289
|
+
outcome: "kept-up",
|
|
290
|
+
reason: "quiet source evidence preserved the up verdict",
|
|
291
|
+
receipts: [receipt],
|
|
292
|
+
evidence_hashes: [evidence],
|
|
293
|
+
guardrails: ["receipt-verification", "no-live-gateway"],
|
|
294
|
+
}),
|
|
295
|
+
],
|
|
296
|
+
trace: [
|
|
297
|
+
{
|
|
298
|
+
as_of: receipt.core.as_of,
|
|
299
|
+
event: "source-read",
|
|
300
|
+
observed: { source: "incident-feed", status: "quiet" },
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
expected_relationships: ["healthy-quiet-up"],
|
|
304
|
+
notes: ["baseline release candidate path remains quiet and deterministic"],
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
function makeDriftingSchedulesFulfillmentCase() {
|
|
308
|
+
const evidence = hashText("r6 drifting schedule reached fulfillment");
|
|
309
|
+
const drifting = makeReceipt({
|
|
310
|
+
case_id: "drifting-schedules-fulfillment",
|
|
311
|
+
memo_key: "r6:drifting:schedule",
|
|
312
|
+
evidence_input_ids: [evidence],
|
|
313
|
+
as_of: "2026-05-18T12:10:00.000Z",
|
|
314
|
+
next_forecast_recheck: "2026-05-18T12:30:00.000Z",
|
|
315
|
+
event_cause: "real-input",
|
|
316
|
+
status: "drifting",
|
|
317
|
+
tokens: { fresh: 8, reused: 0 },
|
|
318
|
+
tags: ["r6", "drifting", "schedule"],
|
|
319
|
+
});
|
|
320
|
+
const fulfilled = makeReceipt({
|
|
321
|
+
case_id: "drifting-schedules-fulfillment",
|
|
322
|
+
memo_key: "r6:drifting:fulfilled",
|
|
323
|
+
evidence_input_ids: [evidence],
|
|
324
|
+
as_of: "2026-05-18T12:30:00.000Z",
|
|
325
|
+
next_forecast_recheck: "2026-05-18T18:00:00.000Z",
|
|
326
|
+
event_cause: "forecast-recheck",
|
|
327
|
+
recheck_kind: "evidence-age",
|
|
328
|
+
role: "fulfill",
|
|
329
|
+
status: "up",
|
|
330
|
+
tokens: { fresh: 0, reused: 8 },
|
|
331
|
+
tags: ["r6", "drifting", "fulfilled"],
|
|
332
|
+
});
|
|
333
|
+
return makeCase({
|
|
334
|
+
case_id: "drifting-schedules-fulfillment",
|
|
335
|
+
title: "Drifting schedules are fulfilled by recorded decision evidence",
|
|
336
|
+
represented_by: "drifting then fulfilled receipts",
|
|
337
|
+
receipts: [drifting, fulfilled],
|
|
338
|
+
decisions: [
|
|
339
|
+
makeDecision({
|
|
340
|
+
case_id: "drifting-schedules-fulfillment",
|
|
341
|
+
as_of: fulfilled.core.as_of,
|
|
342
|
+
outcome: "fulfilled-scheduled-work",
|
|
343
|
+
reason: "scheduled fulfillment consumed the drifting receipt and returned up",
|
|
344
|
+
receipts: [drifting, fulfilled],
|
|
345
|
+
evidence_hashes: [evidence],
|
|
346
|
+
guardrails: ["receipt-log-order", "forecast-schedule"],
|
|
347
|
+
}),
|
|
348
|
+
],
|
|
349
|
+
trace: [
|
|
350
|
+
{
|
|
351
|
+
as_of: drifting.core.as_of,
|
|
352
|
+
event: "schedule-opened",
|
|
353
|
+
observed: { status: "drifting" },
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
as_of: fulfilled.core.as_of,
|
|
357
|
+
event: "schedule-fulfilled",
|
|
358
|
+
observed: { status: "up", fresh_tokens: 0 },
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
expected_relationships: ["drift-has-fulfillment-decision"],
|
|
362
|
+
notes: ["represented as decision evidence only; no live fulfillment side effect"],
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
function makeBlockedHumanReviewCase() {
|
|
366
|
+
const evidence = hashText("r6 blocked human review evidence");
|
|
367
|
+
const receipt = makeReceipt({
|
|
368
|
+
case_id: "blocked-human-review",
|
|
369
|
+
memo_key: "r6:blocked:human-review",
|
|
370
|
+
evidence_input_ids: [evidence],
|
|
371
|
+
as_of: "2026-05-18T12:20:00.000Z",
|
|
372
|
+
next_forecast_recheck: "2026-05-18T12:20:00.000Z",
|
|
373
|
+
event_cause: "escalation",
|
|
374
|
+
status: "blocked",
|
|
375
|
+
blocked: {
|
|
376
|
+
reason: "human review required before responsibility can continue",
|
|
377
|
+
fix_target: "operator-review",
|
|
378
|
+
interrupt_cause: "needs-judgment",
|
|
379
|
+
},
|
|
380
|
+
tokens: { fresh: 0, reused: 0 },
|
|
381
|
+
tags: ["r6", "blocked", "human-review"],
|
|
382
|
+
});
|
|
383
|
+
return makeCase({
|
|
384
|
+
case_id: "blocked-human-review",
|
|
385
|
+
title: "Blocked requests expose human-review escalation",
|
|
386
|
+
represented_by: "blocked safety receipt",
|
|
387
|
+
receipts: [receipt],
|
|
388
|
+
decisions: [
|
|
389
|
+
makeDecision({
|
|
390
|
+
case_id: "blocked-human-review",
|
|
391
|
+
as_of: receipt.core.as_of,
|
|
392
|
+
outcome: "blocked-for-human-review",
|
|
393
|
+
reason: receipt.verdict.blocked?.reason ?? "blocked",
|
|
394
|
+
receipts: [receipt],
|
|
395
|
+
evidence_hashes: [evidence],
|
|
396
|
+
guardrails: ["interrupt-cause", "non-droppable-blocked-receipt"],
|
|
397
|
+
}),
|
|
398
|
+
],
|
|
399
|
+
trace: [
|
|
400
|
+
{
|
|
401
|
+
as_of: receipt.core.as_of,
|
|
402
|
+
event: "manual-review-required",
|
|
403
|
+
observed: {
|
|
404
|
+
interrupt_cause: receipt.verdict.blocked?.interrupt_cause ?? null,
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
expected_relationships: ["blocked-human-review-is-non-droppable"],
|
|
409
|
+
notes: ["no private review rationale is embedded in the fixture"],
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
function makeForecastPullsJudgeEarlierCase() {
|
|
413
|
+
const evidence = hashText("r6 forecast schedule synthetic evidence");
|
|
414
|
+
const schedule = evaluateForecastScheduleV0({
|
|
415
|
+
as_of: "2026-05-18T12:10:00.000Z",
|
|
416
|
+
real_input_observed: true,
|
|
417
|
+
schedule: {
|
|
418
|
+
responsibility_id: "r6.forecast-pulls-judge-earlier",
|
|
419
|
+
contract_revision: CONTRACT_REVISION,
|
|
420
|
+
memo_key: "r6:forecast:pull-earlier",
|
|
421
|
+
evidence_input_ids: [evidence],
|
|
422
|
+
next_evidence_recheck: "2026-05-18T12:05:00.000Z",
|
|
423
|
+
next_plan_recheck: "2026-05-18T18:00:00.000Z",
|
|
424
|
+
},
|
|
425
|
+
});
|
|
426
|
+
if (schedule.outcome !== "manufacture-recheck") {
|
|
427
|
+
throw new Error("forecast pull fixture must manufacture a recheck receipt");
|
|
428
|
+
}
|
|
429
|
+
return makeCase({
|
|
430
|
+
case_id: "forecast-pulls-judge-earlier",
|
|
431
|
+
title: "Forecast schedule pulls judge earlier than the normal plan audit",
|
|
432
|
+
represented_by: "forecast manufactured recheck receipt",
|
|
433
|
+
receipts: schedule.receipts,
|
|
434
|
+
decisions: [
|
|
435
|
+
makeDecision({
|
|
436
|
+
case_id: "forecast-pulls-judge-earlier",
|
|
437
|
+
as_of: "2026-05-18T12:10:00.000Z",
|
|
438
|
+
outcome: schedule.reason,
|
|
439
|
+
reason: "evidence-age forecast clock crossed before plan-age audit",
|
|
440
|
+
receipts: schedule.receipts,
|
|
441
|
+
evidence_hashes: [evidence],
|
|
442
|
+
guardrails: ["forecast-clock", "zero-fresh-synthetic-recheck"],
|
|
443
|
+
}),
|
|
444
|
+
],
|
|
445
|
+
trace: [
|
|
446
|
+
{
|
|
447
|
+
as_of: "2026-05-18T12:10:00.000Z",
|
|
448
|
+
event: "forecast-clock-crossed",
|
|
449
|
+
observed: {
|
|
450
|
+
due_rechecks: schedule.due_rechecks,
|
|
451
|
+
token_bearing_receipts: schedule.token_bearing_receipts.length,
|
|
452
|
+
},
|
|
453
|
+
},
|
|
454
|
+
],
|
|
455
|
+
expected_relationships: ["forecast-can-pull-judge-earlier"],
|
|
456
|
+
notes: ["forecast recheck receipt is blocked until a judge acts"],
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
function makeHysteresisPreventsFlipFlopCase() {
|
|
460
|
+
const evidence = hashText("r6 hysteresis noisy verdict band");
|
|
461
|
+
const noisyDrift = makeReceipt({
|
|
462
|
+
case_id: "hysteresis-prevents-flip-flop",
|
|
463
|
+
memo_key: "r6:hysteresis:noisy-drift",
|
|
464
|
+
evidence_input_ids: [evidence],
|
|
465
|
+
as_of: "2026-05-18T12:25:00.000Z",
|
|
466
|
+
next_forecast_recheck: "2026-05-18T13:25:00.000Z",
|
|
467
|
+
event_cause: "forecast-recheck",
|
|
468
|
+
recheck_kind: "plan-age",
|
|
469
|
+
status: "drifting",
|
|
470
|
+
tokens: { fresh: 0, reused: 17 },
|
|
471
|
+
tags: ["r6", "hysteresis", "noisy-verdict"],
|
|
472
|
+
});
|
|
473
|
+
const heldCurrent = makeReceipt({
|
|
474
|
+
case_id: "hysteresis-prevents-flip-flop",
|
|
475
|
+
memo_key: "r6:hysteresis:held-current",
|
|
476
|
+
evidence_input_ids: [evidence],
|
|
477
|
+
as_of: "2026-05-18T12:40:00.000Z",
|
|
478
|
+
next_forecast_recheck: "2026-05-18T13:25:00.000Z",
|
|
479
|
+
event_cause: "forecast-recheck",
|
|
480
|
+
recheck_kind: "plan-age",
|
|
481
|
+
status: "up",
|
|
482
|
+
tokens: { fresh: 0, reused: 17 },
|
|
483
|
+
tags: ["r6", "hysteresis", "held-current"],
|
|
484
|
+
});
|
|
485
|
+
return makeCase({
|
|
486
|
+
case_id: "hysteresis-prevents-flip-flop",
|
|
487
|
+
title: "Hysteresis prevents noisy verdict flip-flop",
|
|
488
|
+
represented_by: "noisy verdict receipts and held-current decision",
|
|
489
|
+
receipts: [noisyDrift, heldCurrent],
|
|
490
|
+
decisions: [
|
|
491
|
+
makeDecision({
|
|
492
|
+
case_id: "hysteresis-prevents-flip-flop",
|
|
493
|
+
as_of: heldCurrent.core.as_of,
|
|
494
|
+
outcome: "held-current-policy",
|
|
495
|
+
reason: "noisy drifting verdict stayed inside the fixed recompile interval and activation band",
|
|
496
|
+
receipts: [noisyDrift, heldCurrent],
|
|
497
|
+
evidence_hashes: [evidence],
|
|
498
|
+
guardrails: ["min-recompile-interval", "judged-activation-band"],
|
|
499
|
+
}),
|
|
500
|
+
],
|
|
501
|
+
trace: [
|
|
502
|
+
{
|
|
503
|
+
as_of: noisyDrift.core.as_of,
|
|
504
|
+
event: "noisy-drift-observed",
|
|
505
|
+
observed: { status: "drifting", confidence: 0.51 },
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
as_of: heldCurrent.core.as_of,
|
|
509
|
+
event: "policy-held-current",
|
|
510
|
+
observed: { status: "up", policy_revision_changed: false },
|
|
511
|
+
},
|
|
512
|
+
],
|
|
513
|
+
expected_relationships: ["hysteresis-prevents-flip-flop"],
|
|
514
|
+
notes: ["fixture records the fixed guard decision; no policy author is invoked"],
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
function makeDuplicateEventIdempotencyCase() {
|
|
518
|
+
const evidence = hashText("r6 duplicate event input");
|
|
519
|
+
const memoKey = computeMemoKeyV0({
|
|
520
|
+
contract_revision: CONTRACT_REVISION,
|
|
521
|
+
evidence_receipts: [evidence],
|
|
522
|
+
dependency_receipts: [],
|
|
523
|
+
});
|
|
524
|
+
const source = makeReceipt({
|
|
525
|
+
case_id: "duplicate-event-idempotency",
|
|
526
|
+
memo_key: memoKey,
|
|
527
|
+
evidence_input_ids: [evidence],
|
|
528
|
+
as_of: "2026-05-18T13:00:00.000Z",
|
|
529
|
+
next_forecast_recheck: "2026-05-18T19:00:00.000Z",
|
|
530
|
+
event_cause: "real-input",
|
|
531
|
+
status: "up",
|
|
532
|
+
tokens: { fresh: 21, reused: 0 },
|
|
533
|
+
tags: ["r6", "duplicate", "source"],
|
|
534
|
+
});
|
|
535
|
+
const duplicate = createMemoHitReceiptV0({
|
|
536
|
+
source_receipt: source,
|
|
537
|
+
as_of: "2026-05-18T13:00:00.000Z",
|
|
538
|
+
next_forecast_recheck: "2026-05-18T19:00:00.000Z",
|
|
539
|
+
event_cause: "real-input",
|
|
540
|
+
});
|
|
541
|
+
return makeCase({
|
|
542
|
+
case_id: "duplicate-event-idempotency",
|
|
543
|
+
title: "Duplicate event is idempotent",
|
|
544
|
+
represented_by: "source receipt plus memo-hit duplicate receipt",
|
|
545
|
+
receipts: [source, duplicate],
|
|
546
|
+
decisions: [
|
|
547
|
+
makeDecision({
|
|
548
|
+
case_id: "duplicate-event-idempotency",
|
|
549
|
+
as_of: duplicate.core.as_of,
|
|
550
|
+
outcome: "one-decision-for-duplicate-event",
|
|
551
|
+
reason: "duplicate event reused the memoized verdict instead of spending fresh work",
|
|
552
|
+
receipts: [source, duplicate],
|
|
553
|
+
evidence_hashes: [evidence],
|
|
554
|
+
guardrails: ["memo-key", "idempotent-event-processing"],
|
|
555
|
+
}),
|
|
556
|
+
],
|
|
557
|
+
trace: [
|
|
558
|
+
{
|
|
559
|
+
as_of: source.core.as_of,
|
|
560
|
+
event: "event-received",
|
|
561
|
+
observed: { event_id: "evt-r6-duplicate", ordinal: 1 },
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
as_of: duplicate.core.as_of,
|
|
565
|
+
event: "duplicate-event-received",
|
|
566
|
+
observed: {
|
|
567
|
+
event_id: "evt-r6-duplicate",
|
|
568
|
+
emitted_decisions: 1,
|
|
569
|
+
duplicate_fresh_tokens: duplicate.cost.tokens.fresh,
|
|
570
|
+
},
|
|
571
|
+
},
|
|
572
|
+
],
|
|
573
|
+
expected_relationships: ["duplicate-event-idempotent"],
|
|
574
|
+
notes: ["current SDK events do not expose a typed event id; fixture records it in trace evidence"],
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
function makeStaleStatusFencingCase() {
|
|
578
|
+
const upstreamEvidence = hashText("r6 stale upstream receipt");
|
|
579
|
+
const downstreamEvidence = hashText("r6 stale downstream blocked receipt");
|
|
580
|
+
const upstream = makeReceipt({
|
|
581
|
+
case_id: "stale-status-fencing",
|
|
582
|
+
memo_key: "r6:stale:upstream",
|
|
583
|
+
evidence_input_ids: [upstreamEvidence],
|
|
584
|
+
as_of: "2026-05-18T11:00:00.000Z",
|
|
585
|
+
next_forecast_recheck: "2026-05-18T12:00:00.000Z",
|
|
586
|
+
event_cause: "real-input",
|
|
587
|
+
status: "up",
|
|
588
|
+
tokens: { fresh: 16, reused: 0 },
|
|
589
|
+
tags: ["r6", "stale", "upstream"],
|
|
590
|
+
});
|
|
591
|
+
const pin = dependencyReceiptPinFromVerifiedReceiptV0({
|
|
592
|
+
upstream_receipt: upstream,
|
|
593
|
+
acceptable_signer_set: ["none"],
|
|
594
|
+
});
|
|
595
|
+
const evaluation = evaluateTransitiveFreshnessV0({
|
|
596
|
+
as_of: "2026-05-18T12:10:00.000Z",
|
|
597
|
+
transitive_freshness_policy_ref: TRANSITIVE_FRESHNESS_POLICY_REF,
|
|
598
|
+
consumed_receipts: [
|
|
599
|
+
{
|
|
600
|
+
upstream_receipt: upstream,
|
|
601
|
+
dependency_pin: pin,
|
|
602
|
+
},
|
|
603
|
+
],
|
|
604
|
+
});
|
|
605
|
+
if (evaluation.outcome !== "stale-blocked") {
|
|
606
|
+
throw new Error("stale status fencing fixture must block stale upstream receipts");
|
|
607
|
+
}
|
|
608
|
+
const blocked = makeReceipt({
|
|
609
|
+
case_id: "stale-status-fencing",
|
|
610
|
+
memo_key: "r6:stale:downstream-blocked",
|
|
611
|
+
evidence_input_ids: [downstreamEvidence],
|
|
612
|
+
as_of: "2026-05-18T12:10:00.000Z",
|
|
613
|
+
next_forecast_recheck: "2026-05-18T12:10:00.000Z",
|
|
614
|
+
event_cause: "escalation",
|
|
615
|
+
status: "blocked",
|
|
616
|
+
blocked: {
|
|
617
|
+
reason: "stale upstream receipt blocked transitive freshness",
|
|
618
|
+
fix_target: "upstream-refetch",
|
|
619
|
+
interrupt_cause: "needs-input",
|
|
620
|
+
},
|
|
621
|
+
freshness: {
|
|
622
|
+
as_of: evaluation.as_of,
|
|
623
|
+
next_forecast_recheck: evaluation.as_of,
|
|
624
|
+
transitive_freshness_policy_ref: evaluation.transitive_freshness_policy_ref,
|
|
625
|
+
consumed_freshness_evaluated: evaluation.consumed_freshness_evaluated,
|
|
626
|
+
},
|
|
627
|
+
consumed_receipts: [pin],
|
|
628
|
+
tokens: { fresh: 0, reused: 0 },
|
|
629
|
+
tags: ["r6", "stale", "blocked"],
|
|
630
|
+
});
|
|
631
|
+
return makeCase({
|
|
632
|
+
case_id: "stale-status-fencing",
|
|
633
|
+
title: "Stale upstream status is fenced before downstream use",
|
|
634
|
+
represented_by: "transitive freshness blocked receipt",
|
|
635
|
+
receipts: [upstream, blocked],
|
|
636
|
+
decisions: [
|
|
637
|
+
makeDecision({
|
|
638
|
+
case_id: "stale-status-fencing",
|
|
639
|
+
as_of: blocked.core.as_of,
|
|
640
|
+
outcome: "stale-blocked",
|
|
641
|
+
reason: "downstream receipt carried consumed freshness evaluation",
|
|
642
|
+
receipts: [upstream, blocked],
|
|
643
|
+
evidence_hashes: [upstreamEvidence, downstreamEvidence],
|
|
644
|
+
guardrails: ["transitive-freshness", "dependency-pin"],
|
|
645
|
+
}),
|
|
646
|
+
],
|
|
647
|
+
trace: [
|
|
648
|
+
{
|
|
649
|
+
as_of: blocked.core.as_of,
|
|
650
|
+
event: "transitive-freshness-evaluated",
|
|
651
|
+
observed: summarizeFreshnessEvaluation(evaluation),
|
|
652
|
+
},
|
|
653
|
+
],
|
|
654
|
+
expected_relationships: ["stale-status-fenced"],
|
|
655
|
+
notes: ["stale state is carried as blocked receipt evidence"],
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
function makeContractRevisionFencingCase() {
|
|
659
|
+
const evidence = hashText("r6 contract revision mismatch");
|
|
660
|
+
const upstream = makeReceipt({
|
|
661
|
+
case_id: "contract-revision-fencing",
|
|
662
|
+
memo_key: "r6:contract:fence",
|
|
663
|
+
evidence_input_ids: [evidence],
|
|
664
|
+
as_of: "2026-05-18T13:10:00.000Z",
|
|
665
|
+
next_forecast_recheck: "2026-05-18T19:00:00.000Z",
|
|
666
|
+
event_cause: "real-input",
|
|
667
|
+
status: "up",
|
|
668
|
+
tokens: { fresh: 11, reused: 0 },
|
|
669
|
+
tags: ["r6", "contract", "upstream"],
|
|
670
|
+
});
|
|
671
|
+
const validPin = dependencyReceiptPinFromVerifiedReceiptV0({
|
|
672
|
+
upstream_receipt: upstream,
|
|
673
|
+
acceptable_signer_set: ["none"],
|
|
674
|
+
});
|
|
675
|
+
const attackerPin = {
|
|
676
|
+
...validPin,
|
|
677
|
+
contract_revision: ATTACKER_CONTRACT_REVISION,
|
|
678
|
+
};
|
|
679
|
+
let mismatchReason = "contract revision mismatch was not checked";
|
|
680
|
+
try {
|
|
681
|
+
verifyUpstreamReceiptDependencyPinV0({
|
|
682
|
+
upstream_receipt: upstream,
|
|
683
|
+
expected_dependency_pin: attackerPin,
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
catch (error) {
|
|
687
|
+
mismatchReason =
|
|
688
|
+
error instanceof Error ? error.message : "contract revision mismatch";
|
|
689
|
+
}
|
|
690
|
+
const blocked = makeReceipt({
|
|
691
|
+
case_id: "contract-revision-fencing",
|
|
692
|
+
memo_key: "r6:contract:mismatch-blocked",
|
|
693
|
+
evidence_input_ids: [evidence],
|
|
694
|
+
as_of: "2026-05-18T13:11:00.000Z",
|
|
695
|
+
next_forecast_recheck: "2026-05-18T13:11:00.000Z",
|
|
696
|
+
event_cause: "escalation",
|
|
697
|
+
status: "blocked",
|
|
698
|
+
blocked: {
|
|
699
|
+
reason: mismatchReason,
|
|
700
|
+
fix_target: "dependency-pin",
|
|
701
|
+
interrupt_cause: "contract-declared",
|
|
702
|
+
},
|
|
703
|
+
tokens: { fresh: 0, reused: 0 },
|
|
704
|
+
tags: ["r6", "contract", "blocked"],
|
|
705
|
+
});
|
|
706
|
+
return makeCase({
|
|
707
|
+
case_id: "contract-revision-fencing",
|
|
708
|
+
title: "Contract revision mismatch is fenced",
|
|
709
|
+
represented_by: "dependency pin verification and blocked receipt",
|
|
710
|
+
receipts: [upstream, blocked],
|
|
711
|
+
decisions: [
|
|
712
|
+
makeDecision({
|
|
713
|
+
case_id: "contract-revision-fencing",
|
|
714
|
+
as_of: blocked.core.as_of,
|
|
715
|
+
outcome: "contract-mismatch-blocked",
|
|
716
|
+
reason: mismatchReason,
|
|
717
|
+
receipts: [upstream, blocked],
|
|
718
|
+
evidence_hashes: [evidence],
|
|
719
|
+
guardrails: ["contract-revision-pin", "acceptable-signer-set"],
|
|
720
|
+
}),
|
|
721
|
+
],
|
|
722
|
+
trace: [
|
|
723
|
+
{
|
|
724
|
+
as_of: blocked.core.as_of,
|
|
725
|
+
event: "dependency-pin-rejected",
|
|
726
|
+
observed: {
|
|
727
|
+
expected_contract_revision: validPin.contract_revision,
|
|
728
|
+
supplied_contract_revision: attackerPin.contract_revision,
|
|
729
|
+
},
|
|
730
|
+
},
|
|
731
|
+
],
|
|
732
|
+
expected_relationships: ["contract-revision-fenced"],
|
|
733
|
+
notes: ["mismatched contract does not reach downstream evaluation"],
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
function makePolicyRecompileByteIdenticalRegistryCase() {
|
|
737
|
+
const evidence = hashText("r6 policy recompile identical registry");
|
|
738
|
+
const registry = makeRegistrySnapshot();
|
|
739
|
+
const registryHash = hashCanonicalValue(registry);
|
|
740
|
+
const receipt = makeReceipt({
|
|
741
|
+
case_id: "policy-recompile-byte-identical-registry",
|
|
742
|
+
memo_key: "r6:policy-recompile:byte-identical",
|
|
743
|
+
evidence_input_ids: [evidence],
|
|
744
|
+
as_of: "2026-05-18T14:00:00.000Z",
|
|
745
|
+
next_forecast_recheck: "2026-05-19T00:00:00.000Z",
|
|
746
|
+
event_cause: "forecast-recheck",
|
|
747
|
+
recheck_kind: "plan-age",
|
|
748
|
+
role: "policy-compile",
|
|
749
|
+
status: "up",
|
|
750
|
+
tokens: { fresh: 0, reused: 0 },
|
|
751
|
+
tags: ["r6", "policy-recompile", "byte-identical"],
|
|
752
|
+
});
|
|
753
|
+
return makeCase({
|
|
754
|
+
case_id: "policy-recompile-byte-identical-registry",
|
|
755
|
+
title: "Policy recompile preserves byte-identical registry artifact",
|
|
756
|
+
represented_by: "registry hash before/after decision",
|
|
757
|
+
receipts: [receipt],
|
|
758
|
+
decisions: [
|
|
759
|
+
makeDecision({
|
|
760
|
+
case_id: "policy-recompile-byte-identical-registry",
|
|
761
|
+
as_of: receipt.core.as_of,
|
|
762
|
+
outcome: "registry-byte-identical",
|
|
763
|
+
reason: "recorded policy recompile produced the same registry bytes",
|
|
764
|
+
receipts: [receipt],
|
|
765
|
+
evidence_hashes: [evidence, registryHash],
|
|
766
|
+
guardrails: ["canonical-policy-artifact", "registry-content-hash"],
|
|
767
|
+
}),
|
|
768
|
+
],
|
|
769
|
+
trace: [
|
|
770
|
+
{
|
|
771
|
+
as_of: receipt.core.as_of,
|
|
772
|
+
event: "policy-registry-compared",
|
|
773
|
+
observed: {
|
|
774
|
+
before_registry_hash: registryHash,
|
|
775
|
+
after_registry_hash: registryHash,
|
|
776
|
+
byte_identical: true,
|
|
777
|
+
},
|
|
778
|
+
},
|
|
779
|
+
],
|
|
780
|
+
expected_relationships: ["policy-registry-byte-identical"],
|
|
781
|
+
notes: ["recorded proof does not invoke the policy author or model gateway"],
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
function makeMemoizedVerdictZeroFreshTokensCase() {
|
|
785
|
+
const evidence = hashText("r6 memoized verdict zero fresh evidence");
|
|
786
|
+
const memoKey = computeMemoKeyV0({
|
|
787
|
+
contract_revision: CONTRACT_REVISION,
|
|
788
|
+
evidence_receipts: [evidence],
|
|
789
|
+
dependency_receipts: [],
|
|
790
|
+
});
|
|
791
|
+
const source = makeReceipt({
|
|
792
|
+
case_id: "memoized-verdict-zero-fresh-tokens",
|
|
793
|
+
memo_key: memoKey,
|
|
794
|
+
evidence_input_ids: [evidence],
|
|
795
|
+
as_of: "2026-05-18T15:00:00.000Z",
|
|
796
|
+
next_forecast_recheck: "2026-05-19T00:00:00.000Z",
|
|
797
|
+
event_cause: "real-input",
|
|
798
|
+
status: "up",
|
|
799
|
+
tokens: { fresh: 33, reused: 0 },
|
|
800
|
+
tags: ["r6", "memo", "source"],
|
|
801
|
+
});
|
|
802
|
+
const memoHit = createMemoHitReceiptV0({
|
|
803
|
+
source_receipt: source,
|
|
804
|
+
as_of: "2026-05-18T16:00:00.000Z",
|
|
805
|
+
next_forecast_recheck: "2026-05-19T00:00:00.000Z",
|
|
806
|
+
event_cause: "forecast-recheck",
|
|
807
|
+
recheck_kind: "evidence-age",
|
|
808
|
+
});
|
|
809
|
+
return makeCase({
|
|
810
|
+
case_id: "memoized-verdict-zero-fresh-tokens",
|
|
811
|
+
title: "Memoized verdict reuse spends zero fresh judge tokens",
|
|
812
|
+
represented_by: "memo-hit receipt",
|
|
813
|
+
receipts: [source, memoHit],
|
|
814
|
+
decisions: [
|
|
815
|
+
makeDecision({
|
|
816
|
+
case_id: "memoized-verdict-zero-fresh-tokens",
|
|
817
|
+
as_of: memoHit.core.as_of,
|
|
818
|
+
outcome: "memo-hit-zero-fresh",
|
|
819
|
+
reason: "memoized verdict receipt reused prior tokens with fresh=0",
|
|
820
|
+
receipts: [source, memoHit],
|
|
821
|
+
evidence_hashes: [evidence],
|
|
822
|
+
guardrails: ["memo-namespace", "zero-fresh-memo-hit"],
|
|
823
|
+
}),
|
|
824
|
+
],
|
|
825
|
+
trace: [
|
|
826
|
+
{
|
|
827
|
+
as_of: source.core.as_of,
|
|
828
|
+
event: "memo-source-stored",
|
|
829
|
+
observed: { fresh: source.cost.tokens.fresh, namespace: MEMO_NAMESPACE },
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
as_of: memoHit.core.as_of,
|
|
833
|
+
event: "memo-hit-reused",
|
|
834
|
+
observed: {
|
|
835
|
+
fresh: memoHit.cost.tokens.fresh,
|
|
836
|
+
reused: memoHit.cost.tokens.reused,
|
|
837
|
+
},
|
|
838
|
+
},
|
|
839
|
+
],
|
|
840
|
+
expected_relationships: ["memo-hit-has-zero-fresh-tokens"],
|
|
841
|
+
notes: ["replay uses existing memo-hit receipt helper"],
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
function makeCase(input) {
|
|
845
|
+
return Object.freeze({
|
|
846
|
+
...input,
|
|
847
|
+
receipts: Object.freeze([...input.receipts]),
|
|
848
|
+
decisions: Object.freeze([...input.decisions]),
|
|
849
|
+
trace: Object.freeze([...input.trace]),
|
|
850
|
+
expected_relationships: Object.freeze([...input.expected_relationships]),
|
|
851
|
+
evidence_hashes: uniqueSortedHashes([
|
|
852
|
+
...input.receipts.map((receipt) => receipt.content_hash),
|
|
853
|
+
...input.decisions.flatMap((decision) => decision.evidence_hashes),
|
|
854
|
+
]),
|
|
855
|
+
notes: Object.freeze([...input.notes]),
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
function makeDecision(input) {
|
|
859
|
+
return Object.freeze({
|
|
860
|
+
decision_id: `${input.case_id}:${hashText(`${input.outcome}:${input.as_of}`).slice(7, 19)}`,
|
|
861
|
+
case_id: input.case_id,
|
|
862
|
+
as_of: input.as_of,
|
|
863
|
+
outcome: input.outcome,
|
|
864
|
+
reason: input.reason,
|
|
865
|
+
receipt_hashes: Object.freeze(input.receipts.map((receipt) => receipt.content_hash)),
|
|
866
|
+
evidence_hashes: uniqueSortedHashes(input.evidence_hashes),
|
|
867
|
+
token_summary: sumTokens(input.receipts),
|
|
868
|
+
guardrails: Object.freeze([...input.guardrails]),
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
function makeReceipt(input) {
|
|
872
|
+
const role = input.role ?? "judge";
|
|
873
|
+
const contractRevision = input.contract_revision ?? CONTRACT_REVISION;
|
|
874
|
+
const freshness = input.freshness ??
|
|
875
|
+
{
|
|
876
|
+
as_of: input.as_of,
|
|
877
|
+
next_forecast_recheck: input.next_forecast_recheck,
|
|
878
|
+
};
|
|
879
|
+
return createReceiptV0({
|
|
880
|
+
core: {
|
|
881
|
+
responsibility_id: `r6.${input.case_id}`,
|
|
882
|
+
contract_revision: contractRevision,
|
|
883
|
+
event_cause: input.event_cause,
|
|
884
|
+
...(input.recheck_kind === undefined
|
|
885
|
+
? {}
|
|
886
|
+
: { recheck_kind: input.recheck_kind }),
|
|
887
|
+
memo_key: input.memo_key,
|
|
888
|
+
evidence_input_ids: input.evidence_input_ids,
|
|
889
|
+
as_of: input.as_of,
|
|
890
|
+
role,
|
|
891
|
+
},
|
|
892
|
+
sig: {
|
|
893
|
+
scheme: "none",
|
|
894
|
+
null_reason: "cradle R6 recorded release-parity fixture",
|
|
895
|
+
},
|
|
896
|
+
verdict: {
|
|
897
|
+
status: input.status,
|
|
898
|
+
confidence: {
|
|
899
|
+
value: input.status === "blocked" ? 0 : 0.86,
|
|
900
|
+
derivation_method: "recorded-r6-release-parity-fixture",
|
|
901
|
+
calibration_grade: "none",
|
|
902
|
+
label_source: "fixture",
|
|
903
|
+
},
|
|
904
|
+
...(input.blocked === undefined ? {} : { blocked: input.blocked }),
|
|
905
|
+
},
|
|
906
|
+
freshness,
|
|
907
|
+
composition: {
|
|
908
|
+
consumed_receipts: input.consumed_receipts ?? [],
|
|
909
|
+
cycle_checked: true,
|
|
910
|
+
},
|
|
911
|
+
cost: {
|
|
912
|
+
provider: "cradle",
|
|
913
|
+
model: "recorded-release-parity",
|
|
914
|
+
role,
|
|
915
|
+
tags: input.tags,
|
|
916
|
+
responsibility_id: `r6.${input.case_id}`,
|
|
917
|
+
run_id: `r6-${input.case_id}`,
|
|
918
|
+
as_of: input.as_of,
|
|
919
|
+
tokens: input.tokens,
|
|
920
|
+
surprise_cause: input.event_cause,
|
|
921
|
+
},
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
function makeRegistrySnapshot() {
|
|
925
|
+
const validationState = {
|
|
926
|
+
status: "validated",
|
|
927
|
+
validator_id: "r6-release-parity-recorded-fixture",
|
|
928
|
+
};
|
|
929
|
+
return Object.freeze({
|
|
930
|
+
contract_revision: CONTRACT_REVISION,
|
|
931
|
+
policy_artifact_id: "policy.r6.release-parity.recorded",
|
|
932
|
+
policy_artifact_identity: "policy.r6.release-parity.recorded",
|
|
933
|
+
policy_artifact_namespace: MEMO_NAMESPACE.policy_artifact_namespace,
|
|
934
|
+
policy_artifact_revision: MEMO_NAMESPACE.policy_artifact_revision,
|
|
935
|
+
policy_artifact_validation_state: validationState,
|
|
936
|
+
validation_state: validationState,
|
|
937
|
+
policy_artifact_bytes: POLICY_ARTIFACT_BYTES,
|
|
938
|
+
policy_artifact_content_hash: POLICY_ARTIFACT_CONTENT_HASH,
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
function summarizeFreshnessEvaluation(evaluation) {
|
|
942
|
+
return {
|
|
943
|
+
outcome: evaluation.outcome,
|
|
944
|
+
stale_receipt_hashes: evaluation.stale_receipt_hashes,
|
|
945
|
+
consumed_freshness_evaluated: evaluation.consumed_freshness_evaluated,
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
function releaseParityResult(status, summary, evidence) {
|
|
949
|
+
return {
|
|
950
|
+
ok: status === "pass",
|
|
951
|
+
relationship: "release-parity-fixture",
|
|
952
|
+
family: "release-parity-fixture",
|
|
953
|
+
status,
|
|
954
|
+
summary,
|
|
955
|
+
evidence: Object.freeze([...evidence]),
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
function assertRequiredCaseCoverage(cases) {
|
|
959
|
+
const actual = new Set(cases.map((item) => item.case_id));
|
|
960
|
+
const missing = exports.R6_RELEASE_PARITY_CASE_IDS_V0.filter((caseId) => !actual.has(caseId));
|
|
961
|
+
const extra = cases
|
|
962
|
+
.map((item) => item.case_id)
|
|
963
|
+
.filter((caseId) => !exports.R6_RELEASE_PARITY_CASE_IDS_V0.includes(caseId));
|
|
964
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
965
|
+
throw new Error(`R6 release parity case coverage mismatch: missing=${missing.join(",")}; extra=${extra.join(",")}`);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
function sumTokens(receipts) {
|
|
969
|
+
return Object.freeze(receipts.reduce((sum, receipt) => ({
|
|
970
|
+
fresh: sum.fresh + receipt.cost.tokens.fresh,
|
|
971
|
+
reused: sum.reused + receipt.cost.tokens.reused,
|
|
972
|
+
}), { fresh: 0, reused: 0 }));
|
|
973
|
+
}
|
|
974
|
+
function uniqueSortedHashes(values) {
|
|
975
|
+
return Object.freeze(Array.from(new Set(values)).sort());
|
|
976
|
+
}
|
|
977
|
+
function hashText(value) {
|
|
978
|
+
return `sha256:${(0, node_crypto_1.createHash)("sha256").update(value).digest("hex")}`;
|
|
979
|
+
}
|
|
980
|
+
function hashCanonicalValue(value) {
|
|
981
|
+
return hashCanonicalReceiptV0(canonicalizeForReceiptV0(value));
|
|
982
|
+
}
|
|
983
|
+
function loadReactorComposition() {
|
|
984
|
+
try {
|
|
985
|
+
return require("@openprose/reactor/composition");
|
|
986
|
+
}
|
|
987
|
+
catch (error) {
|
|
988
|
+
if (isMissingReactorSubpath(error, "@openprose/reactor/composition")) {
|
|
989
|
+
return require("../../../reactor/dist/composition");
|
|
990
|
+
}
|
|
991
|
+
throw error;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
function loadReactorForecast() {
|
|
995
|
+
try {
|
|
996
|
+
return require("@openprose/reactor/forecast");
|
|
997
|
+
}
|
|
998
|
+
catch (error) {
|
|
999
|
+
if (isMissingReactorSubpath(error, "@openprose/reactor/forecast")) {
|
|
1000
|
+
return require("../../../reactor/dist/forecast");
|
|
1001
|
+
}
|
|
1002
|
+
throw error;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
function loadReactorMemo() {
|
|
1006
|
+
try {
|
|
1007
|
+
return require("@openprose/reactor/memo");
|
|
1008
|
+
}
|
|
1009
|
+
catch (error) {
|
|
1010
|
+
if (isMissingReactorSubpath(error, "@openprose/reactor/memo")) {
|
|
1011
|
+
return require("../../../reactor/dist/memo");
|
|
1012
|
+
}
|
|
1013
|
+
throw error;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
function loadReactorReceipt() {
|
|
1017
|
+
try {
|
|
1018
|
+
return require("@openprose/reactor/receipt");
|
|
1019
|
+
}
|
|
1020
|
+
catch (error) {
|
|
1021
|
+
if (isMissingReactorSubpath(error, "@openprose/reactor/receipt")) {
|
|
1022
|
+
return require("../../../reactor/dist/receipt");
|
|
1023
|
+
}
|
|
1024
|
+
throw error;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
function isMissingReactorSubpath(error, subpath) {
|
|
1028
|
+
return (isRecord(error) &&
|
|
1029
|
+
error["code"] === "MODULE_NOT_FOUND" &&
|
|
1030
|
+
typeof error["message"] === "string" &&
|
|
1031
|
+
error["message"].includes(subpath));
|
|
1032
|
+
}
|
|
1033
|
+
function isRecord(value) {
|
|
1034
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1035
|
+
}
|