@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.
Files changed (90) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +270 -0
  3. package/dist/assert/index.d.ts +35 -0
  4. package/dist/assert/index.d.ts.map +1 -0
  5. package/dist/assert/index.js +398 -0
  6. package/dist/baselines/cost-thesis/index.d.ts +103 -0
  7. package/dist/baselines/cost-thesis/index.d.ts.map +1 -0
  8. package/dist/baselines/cost-thesis/index.js +337 -0
  9. package/dist/baselines/naive-loop/index.d.ts +46 -0
  10. package/dist/baselines/naive-loop/index.d.ts.map +1 -0
  11. package/dist/baselines/naive-loop/index.js +188 -0
  12. package/dist/baselines/no-memo/index.d.ts +84 -0
  13. package/dist/baselines/no-memo/index.d.ts.map +1 -0
  14. package/dist/baselines/no-memo/index.js +226 -0
  15. package/dist/doubles/clock.d.ts +9 -0
  16. package/dist/doubles/clock.d.ts.map +1 -0
  17. package/dist/doubles/clock.js +42 -0
  18. package/dist/doubles/storage.d.ts +24 -0
  19. package/dist/doubles/storage.d.ts.map +1 -0
  20. package/dist/doubles/storage.js +191 -0
  21. package/dist/eval/index.d.ts +204 -0
  22. package/dist/eval/index.d.ts.map +1 -0
  23. package/dist/eval/index.js +596 -0
  24. package/dist/index.d.ts +24 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +39 -0
  27. package/dist/policy-author/index.d.ts +103 -0
  28. package/dist/policy-author/index.d.ts.map +1 -0
  29. package/dist/policy-author/index.js +358 -0
  30. package/dist/policy-drift/index.d.ts +64 -0
  31. package/dist/policy-drift/index.d.ts.map +1 -0
  32. package/dist/policy-drift/index.js +495 -0
  33. package/dist/policy-replay/index.d.ts +106 -0
  34. package/dist/policy-replay/index.d.ts.map +1 -0
  35. package/dist/policy-replay/index.js +361 -0
  36. package/dist/provider-parity/index.d.ts +91 -0
  37. package/dist/provider-parity/index.d.ts.map +1 -0
  38. package/dist/provider-parity/index.js +439 -0
  39. package/dist/recompile/index.d.ts +161 -0
  40. package/dist/recompile/index.d.ts.map +1 -0
  41. package/dist/recompile/index.js +690 -0
  42. package/dist/release-candidate/index.d.ts +139 -0
  43. package/dist/release-candidate/index.d.ts.map +1 -0
  44. package/dist/release-candidate/index.js +553 -0
  45. package/dist/release-parity/index.d.ts +80 -0
  46. package/dist/release-parity/index.d.ts.map +1 -0
  47. package/dist/release-parity/index.js +1035 -0
  48. package/dist/replay/model-gateway.d.ts +25 -0
  49. package/dist/replay/model-gateway.d.ts.map +1 -0
  50. package/dist/replay/model-gateway.js +166 -0
  51. package/dist/replay/parity.d.ts +110 -0
  52. package/dist/replay/parity.d.ts.map +1 -0
  53. package/dist/replay/parity.js +232 -0
  54. package/dist/rollback/index.d.ts +106 -0
  55. package/dist/rollback/index.d.ts.map +1 -0
  56. package/dist/rollback/index.js +694 -0
  57. package/dist/scenario/parser.d.ts +11 -0
  58. package/dist/scenario/parser.d.ts.map +1 -0
  59. package/dist/scenario/parser.js +490 -0
  60. package/dist/scenario/runner.d.ts +12 -0
  61. package/dist/scenario/runner.d.ts.map +1 -0
  62. package/dist/scenario/runner.js +345 -0
  63. package/dist/scenario/synthetic-world-adapter.d.ts +4 -0
  64. package/dist/scenario/synthetic-world-adapter.d.ts.map +1 -0
  65. package/dist/scenario/synthetic-world-adapter.js +82 -0
  66. package/dist/scenario/time.d.ts +4 -0
  67. package/dist/scenario/time.d.ts.map +1 -0
  68. package/dist/scenario/time.js +45 -0
  69. package/dist/scenario/types.d.ts +149 -0
  70. package/dist/scenario/types.d.ts.map +1 -0
  71. package/dist/scenario/types.js +5 -0
  72. package/dist/spikes/index.d.ts +10 -0
  73. package/dist/spikes/index.d.ts.map +1 -0
  74. package/dist/spikes/index.js +29 -0
  75. package/dist/spikes/k1-ensemble-spread.d.ts +88 -0
  76. package/dist/spikes/k1-ensemble-spread.d.ts.map +1 -0
  77. package/dist/spikes/k1-ensemble-spread.js +396 -0
  78. package/dist/spikes/k2-policy-author.d.ts +25 -0
  79. package/dist/spikes/k2-policy-author.d.ts.map +1 -0
  80. package/dist/spikes/k2-policy-author.js +503 -0
  81. package/dist/spikes/live-refresh.d.ts +150 -0
  82. package/dist/spikes/live-refresh.d.ts.map +1 -0
  83. package/dist/spikes/live-refresh.js +511 -0
  84. package/dist/world/index.d.ts +2 -0
  85. package/dist/world/index.d.ts.map +1 -0
  86. package/dist/world/index.js +17 -0
  87. package/dist/world/synthetic-world.d.ts +104 -0
  88. package/dist/world/synthetic-world.d.ts.map +1 -0
  89. package/dist/world/synthetic-world.js +449 -0
  90. package/package.json +139 -0
@@ -0,0 +1,596 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderCradleEvalProjectionMarkdownReportV0 = exports.projectCradleEvalResultArtifactV0 = exports.createCradleEvalProjectionV0 = exports.renderCradleEvalMarkdownReportV0 = exports.buildCradleEvalResultArtifactV0 = exports.createCradleEvalResultArtifactV0 = exports.createCradleEvalResultV0 = exports.EVAL_RESULT_VERSION_V0 = exports.EVAL_RESULT_SCHEMA_V0 = exports.CRADLE_EVAL_PROJECTION_VERSION_V0 = exports.CRADLE_EVAL_PROJECTION_SCHEMA_V0 = exports.CRADLE_EVAL_RESULT_VERSION_V0 = exports.CRADLE_EVAL_RESULT_SCHEMA_V0 = void 0;
4
+ exports.buildCradleEvalResultV0 = buildCradleEvalResultV0;
5
+ exports.renderCradleEvalReportMarkdownV0 = renderCradleEvalReportMarkdownV0;
6
+ exports.projectCradleEvalResultV0 = projectCradleEvalResultV0;
7
+ exports.renderCradleEvalProjectionReportMarkdownV0 = renderCradleEvalProjectionReportMarkdownV0;
8
+ const node_crypto_1 = require("node:crypto");
9
+ exports.CRADLE_EVAL_RESULT_SCHEMA_V0 = "openprose.reactor-cradle.eval-result";
10
+ exports.CRADLE_EVAL_RESULT_VERSION_V0 = 0;
11
+ exports.CRADLE_EVAL_PROJECTION_SCHEMA_V0 = "openprose.reactor-cradle.eval-projection";
12
+ exports.CRADLE_EVAL_PROJECTION_VERSION_V0 = 0;
13
+ exports.EVAL_RESULT_SCHEMA_V0 = exports.CRADLE_EVAL_RESULT_SCHEMA_V0;
14
+ exports.EVAL_RESULT_VERSION_V0 = exports.CRADLE_EVAL_RESULT_VERSION_V0;
15
+ const ISO_INSTANT_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;
16
+ const CONTENT_HASH_PATTERN = /^sha256:[a-f0-9]{64}$/;
17
+ const REDACTED_TEXT = "[redacted-secret]";
18
+ const REDACTED_EMAIL_TEXT = "[redacted-email]";
19
+ const REDACTED_URL_TEXT = "[redacted-url]";
20
+ const REDACTED_PRIVATE_TEXT = "[redacted-private-text]";
21
+ const REDACTED_RUNTIME_TEXT = "[redacted-runtime-id]";
22
+ const REDACTED_RATIONALE_TEXT = "[redacted-rationale]";
23
+ const REDACTED_REPLAY_TEXT = "[redacted-replay-bytes]";
24
+ const SENSITIVE_ASSIGNMENT_VALUE_PATTERN = "\"[^\"]*\"|'[^']*'|\\{[^}\\n\\r]*\\}|\\[[^\\]\\n\\r]*\\]|[^\\s,;|]+";
25
+ const TEXT_REDACTION_PATTERNS = Object.freeze([
26
+ {
27
+ pattern: new RegExp(`\\b${["sk", "or"].join("-")}-[A-Za-z0-9._-]+`, "g"),
28
+ replacement: REDACTED_TEXT,
29
+ },
30
+ {
31
+ pattern: new RegExp(`\\b${["Bear", "er"].join("")} [A-Za-z0-9._-]+`, "g"),
32
+ replacement: REDACTED_TEXT,
33
+ },
34
+ {
35
+ pattern: new RegExp(`\\b${["api", "Key"].join("")}\\s*[:=]\\s*[A-Za-z0-9._-]+`, "gi"),
36
+ replacement: REDACTED_TEXT,
37
+ },
38
+ {
39
+ pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi,
40
+ replacement: REDACTED_EMAIL_TEXT,
41
+ },
42
+ {
43
+ pattern: /\bhttps?:\/\/[^\s<>)\]}"']+/gi,
44
+ replacement: REDACTED_URL_TEXT,
45
+ },
46
+ {
47
+ pattern: new RegExp(`\\b(?:customer|tenant|account|user)[_-]?(?:payload|data|record|id|email)\\b\\s*[:=]\\s*(?:${SENSITIVE_ASSIGNMENT_VALUE_PATTERN})`, "gi"),
48
+ replacement: REDACTED_PRIVATE_TEXT,
49
+ },
50
+ {
51
+ pattern: new RegExp(`\\b(?:raw\\s+)?judge[_ -]?rationale\\b\\s*[:=]\\s*(?:${SENSITIVE_ASSIGNMENT_VALUE_PATTERN})`, "gi"),
52
+ replacement: REDACTED_RATIONALE_TEXT,
53
+ },
54
+ {
55
+ pattern: new RegExp(`\\braw[_ -]?replay[_ -]?bytes\\b\\s*[:=]\\s*(?:${SENSITIVE_ASSIGNMENT_VALUE_PATTERN})`, "gi"),
56
+ replacement: REDACTED_REPLAY_TEXT,
57
+ },
58
+ {
59
+ pattern: new RegExp(`\\b(?:memo[_ -]?key|run[_ -]?id|tags?|provider[_ -]?norm(?:alized)?(?:[_ -]?payload)?)\\b\\s*[:=]\\s*(?:${SENSITIVE_ASSIGNMENT_VALUE_PATTERN})`, "gi"),
60
+ replacement: REDACTED_RUNTIME_TEXT,
61
+ },
62
+ ]);
63
+ function buildCradleEvalResultV0(input) {
64
+ assertNonEmptyString(input.suite_id, "suite_id");
65
+ assertIsoInstant(input.generated_at, "generated_at");
66
+ assertIsoInstant(input.as_of, "as_of");
67
+ assertModelMatrix(input.model_matrix);
68
+ if (input.cases.length === 0) {
69
+ throw new Error("cases must include at least one eval case");
70
+ }
71
+ const cases = Object.freeze(input.cases.map(normalizeCase));
72
+ const replayParitySummaryHashes = uniqueSortedHashes(cases.flatMap((item) => item.replay === undefined ? [] : [item.replay.summary_hash]));
73
+ const evidenceHashes = uniqueSortedHashes(cases.flatMap((item) => item.evidence_hashes));
74
+ const metrics = computeMetrics(cases, evidenceHashes.length);
75
+ const overallStatus = cases.some((item) => item.status === "fail") ? "fail" : "pass";
76
+ const payload = {
77
+ schema: exports.CRADLE_EVAL_RESULT_SCHEMA_V0,
78
+ v: exports.CRADLE_EVAL_RESULT_VERSION_V0,
79
+ generated_at: input.generated_at,
80
+ as_of: input.as_of,
81
+ suite_id: sanitizeText(input.suite_id),
82
+ overall_status: overallStatus,
83
+ metrics,
84
+ model_matrix: {
85
+ status: "not-run",
86
+ reason: sanitizeText(input.model_matrix.reason),
87
+ },
88
+ cases,
89
+ replay_parity_summary_hashes: replayParitySummaryHashes,
90
+ evidence_hashes: evidenceHashes,
91
+ };
92
+ return Object.freeze({
93
+ ...payload,
94
+ content_hash: hashCanonicalValue(payload),
95
+ });
96
+ }
97
+ exports.createCradleEvalResultV0 = buildCradleEvalResultV0;
98
+ exports.createCradleEvalResultArtifactV0 = buildCradleEvalResultV0;
99
+ exports.buildCradleEvalResultArtifactV0 = buildCradleEvalResultV0;
100
+ function renderCradleEvalReportMarkdownV0(result) {
101
+ const caseRows = result.cases.map((item) => {
102
+ const assertionSummary = `${item.assertions.pass_count}/${item.assertions.results.length} pass`;
103
+ const replaySummary = item.replay === undefined
104
+ ? "not run"
105
+ : `${item.replay.kind} ${item.replay.status} ${item.replay.summary_hash}`;
106
+ return [
107
+ item.case_id,
108
+ item.scenario_id,
109
+ item.status,
110
+ assertionSummary,
111
+ replaySummary,
112
+ item.evidence_hashes.join("<br>"),
113
+ ]
114
+ .map(markdownTableCell)
115
+ .join(" | ");
116
+ });
117
+ const evidenceHashLines = result.evidence_hashes.length === 0
118
+ ? ["- none"]
119
+ : result.evidence_hashes.map((hash) => `- \`${hash}\``);
120
+ const replayHashLines = result.replay_parity_summary_hashes.length === 0
121
+ ? ["- none"]
122
+ : result.replay_parity_summary_hashes.map((hash) => `- \`${hash}\``);
123
+ return [
124
+ "# Cradle Eval Report",
125
+ "",
126
+ `Artifact hash: \`${result.content_hash}\``,
127
+ `Suite: \`${markdownInline(result.suite_id)}\``,
128
+ `Generated at: \`${result.generated_at}\``,
129
+ `As of: \`${result.as_of}\``,
130
+ "",
131
+ "## Pass/Fail Summary",
132
+ `- Overall: ${result.overall_status}`,
133
+ `- Cases: ${result.metrics.case_pass_count}/${result.metrics.case_count} pass, ${result.metrics.case_fail_count} fail`,
134
+ `- Assertions: ${result.metrics.assertion_pass_count}/${result.metrics.assertion_count} pass, ${result.metrics.assertion_fail_count} fail`,
135
+ `- Replay/parity: ${result.metrics.replay_parity_pass_count}/${result.metrics.replay_parity_summary_count} pass, ${result.metrics.replay_parity_fail_count} fail, ${result.metrics.replay_parity_future_rows} future rows`,
136
+ "",
137
+ "## Case Table",
138
+ "| Case | Scenario | Status | Assertions | Replay/parity | Evidence hashes |",
139
+ "| --- | --- | --- | --- | --- | --- |",
140
+ ...caseRows.map((row) => `| ${row} |`),
141
+ "",
142
+ "## Deferred Live Matrix",
143
+ `Live model matrix is ${result.model_matrix.status}: ${sanitizeText(result.model_matrix.reason)}`,
144
+ "",
145
+ "## Replay/Parity Summary Hashes",
146
+ ...replayHashLines,
147
+ "",
148
+ "## Evidence Hashes",
149
+ ...evidenceHashLines,
150
+ "",
151
+ ].join("\n");
152
+ }
153
+ exports.renderCradleEvalMarkdownReportV0 = renderCradleEvalReportMarkdownV0;
154
+ function projectCradleEvalResultV0(result, tier) {
155
+ assertProjectionTier(tier);
156
+ assertContentHash(result.content_hash, "result.content_hash");
157
+ const payload = {
158
+ schema: exports.CRADLE_EVAL_PROJECTION_SCHEMA_V0,
159
+ v: exports.CRADLE_EVAL_PROJECTION_VERSION_V0,
160
+ tier,
161
+ source_content_hash: result.content_hash,
162
+ generated_at: result.generated_at,
163
+ as_of: result.as_of,
164
+ suite_id: sanitizeText(result.suite_id),
165
+ overall_status: normalizeStatus(result.overall_status, "overall_status"),
166
+ metrics: result.metrics,
167
+ model_matrix: {
168
+ status: "not-run",
169
+ reason: sanitizeText(result.model_matrix.reason),
170
+ },
171
+ cases: Object.freeze(result.cases.map(projectCase)),
172
+ replay_parity_summary_hashes: uniqueSortedHashes(result.replay_parity_summary_hashes),
173
+ evidence_hashes: uniqueSortedHashes(result.evidence_hashes),
174
+ };
175
+ return Object.freeze({
176
+ ...payload,
177
+ content_hash: hashCanonicalValue(payload),
178
+ });
179
+ }
180
+ exports.createCradleEvalProjectionV0 = projectCradleEvalResultV0;
181
+ exports.projectCradleEvalResultArtifactV0 = projectCradleEvalResultV0;
182
+ function renderCradleEvalProjectionReportMarkdownV0(projection) {
183
+ const caseRows = projection.cases.map((item) => {
184
+ const assertionSummary = `${item.assertions.pass_count}/${item.assertions.results.length} pass`;
185
+ const replaySummary = item.replay === undefined
186
+ ? "not run"
187
+ : `${item.replay.kind} ${item.replay.status} ${item.replay.summary_hash}`;
188
+ return [
189
+ item.case_id,
190
+ item.scenario_id,
191
+ item.status,
192
+ assertionSummary,
193
+ replaySummary,
194
+ item.evidence_hashes.join("<br>"),
195
+ ]
196
+ .map(markdownTableCell)
197
+ .join(" | ");
198
+ });
199
+ const evidenceHashLines = projection.evidence_hashes.length === 0
200
+ ? ["- none"]
201
+ : projection.evidence_hashes.map((hash) => `- \`${hash}\``);
202
+ const replayHashLines = projection.replay_parity_summary_hashes.length === 0
203
+ ? ["- none"]
204
+ : projection.replay_parity_summary_hashes.map((hash) => `- \`${hash}\``);
205
+ return [
206
+ "# Cradle Eval Projection",
207
+ "",
208
+ `Projection hash: \`${projection.content_hash}\``,
209
+ `Source artifact hash: \`${projection.source_content_hash}\``,
210
+ `Tier: \`${projection.tier}\``,
211
+ `Suite: \`${markdownInline(projection.suite_id)}\``,
212
+ `Generated at: \`${projection.generated_at}\``,
213
+ `As of: \`${projection.as_of}\``,
214
+ "",
215
+ "## Pass/Fail Summary",
216
+ `- Overall: ${projection.overall_status}`,
217
+ `- Cases: ${projection.metrics.case_pass_count}/${projection.metrics.case_count} pass, ${projection.metrics.case_fail_count} fail`,
218
+ `- Assertions: ${projection.metrics.assertion_pass_count}/${projection.metrics.assertion_count} pass, ${projection.metrics.assertion_fail_count} fail`,
219
+ `- Replay/parity: ${projection.metrics.replay_parity_pass_count}/${projection.metrics.replay_parity_summary_count} pass, ${projection.metrics.replay_parity_fail_count} fail, ${projection.metrics.replay_parity_future_rows} future rows`,
220
+ "",
221
+ "## Case Table",
222
+ "| Case | Scenario | Status | Assertions | Replay/parity | Evidence hashes |",
223
+ "| --- | --- | --- | --- | --- | --- |",
224
+ ...caseRows.map((row) => `| ${row} |`),
225
+ "",
226
+ "## Deferred Live Matrix",
227
+ `Live model matrix is ${projection.model_matrix.status}: ${sanitizeText(projection.model_matrix.reason)}`,
228
+ "",
229
+ "## Replay/Parity Summary Hashes",
230
+ ...replayHashLines,
231
+ "",
232
+ "## Evidence Hashes",
233
+ ...evidenceHashLines,
234
+ "",
235
+ ].join("\n");
236
+ }
237
+ exports.renderCradleEvalProjectionMarkdownReportV0 = renderCradleEvalProjectionReportMarkdownV0;
238
+ function normalizeCase(input) {
239
+ assertNonEmptyString(input.case_id, "case_id");
240
+ const assertions = normalizeAssertionSuite(input.assertions);
241
+ const replay = input.replay === undefined ? undefined : summarizeReplayInput(input.replay);
242
+ const caseEvidenceHashes = uniqueSortedHashes([
243
+ ...assertions.results.map((item) => item.result_hash),
244
+ ...(replay === undefined ? [] : [replay.summary_hash]),
245
+ ...(input.evidence_hashes ?? []),
246
+ ]);
247
+ const status = assertions.status === "fail" || replay?.status === "fail" ? "fail" : "pass";
248
+ return Object.freeze({
249
+ case_id: sanitizeText(input.case_id),
250
+ scenario_id: sanitizeText(input.scenario_id ?? input.case_id),
251
+ status,
252
+ assertions,
253
+ ...(replay === undefined ? {} : { replay }),
254
+ evidence_hashes: caseEvidenceHashes,
255
+ });
256
+ }
257
+ function normalizeAssertionSuite(input) {
258
+ const sourceResults = isAssertionResultArray(input)
259
+ ? input
260
+ : input.results;
261
+ if (sourceResults.length === 0) {
262
+ throw new Error("assertions must include at least one result");
263
+ }
264
+ const results = sourceResults.map(normalizeAssertionResult);
265
+ const failCount = results.filter((item) => item.status === "fail").length;
266
+ const passCount = results.length - failCount;
267
+ return Object.freeze({
268
+ status: failCount === 0 ? "pass" : "fail",
269
+ pass_count: passCount,
270
+ fail_count: failCount,
271
+ results: Object.freeze(results),
272
+ });
273
+ }
274
+ function normalizeAssertionResult(input) {
275
+ const evidence = input.evidence.map(normalizeAssertionEvidence);
276
+ const payload = {
277
+ family: sanitizeText(input.family),
278
+ relationship: sanitizeText(input.relationship),
279
+ status: normalizeStatus(input.status, "assertion status"),
280
+ summary: sanitizeText(input.summary),
281
+ evidence,
282
+ };
283
+ return Object.freeze({
284
+ ...payload,
285
+ result_hash: hashCanonicalValue(payload),
286
+ });
287
+ }
288
+ function normalizeAssertionEvidence(input) {
289
+ const observedHash = input.observed === undefined
290
+ ? undefined
291
+ : hashRedactedObservedValue(input.observed);
292
+ return Object.freeze({
293
+ path: sanitizeText(input.path),
294
+ message: sanitizeText(input.message),
295
+ ...(observedHash === undefined ? {} : { observed_hash: observedHash }),
296
+ });
297
+ }
298
+ function summarizeReplayInput(input) {
299
+ if (input.relationship === "replay-byte-identical") {
300
+ return summarizeReplayByteIdentical(input);
301
+ }
302
+ return summarizeReplayParityMatrix(input);
303
+ }
304
+ function summarizeReplayByteIdentical(input) {
305
+ const payload = {
306
+ kind: "replay-byte-identical",
307
+ ...summarizeReplayByteIdenticalWithoutKind(input),
308
+ };
309
+ return Object.freeze({
310
+ ...payload,
311
+ summary_hash: hashCanonicalValue(payload),
312
+ });
313
+ }
314
+ function summarizeReplayByteIdenticalWithoutKind(input) {
315
+ const payload = {
316
+ status: input.ok ? "pass" : "fail",
317
+ expected_hash: input.expected_hash,
318
+ actual_hash: input.actual_hash,
319
+ evidence_path: sanitizeText(input.evidence_path),
320
+ ...("byte_length" in input ? { byte_length: input.byte_length } : {}),
321
+ ...("reason" in input ? { reason: sanitizeText(input.reason) } : {}),
322
+ };
323
+ return Object.freeze(payload);
324
+ }
325
+ function projectCase(input) {
326
+ return Object.freeze({
327
+ case_id: sanitizeText(input.case_id),
328
+ scenario_id: sanitizeText(input.scenario_id),
329
+ status: normalizeStatus(input.status, "case status"),
330
+ assertions: projectAssertionSuite(input.assertions),
331
+ ...(input.replay === undefined ? {} : { replay: projectReplay(input.replay) }),
332
+ evidence_hashes: uniqueSortedHashes(input.evidence_hashes),
333
+ });
334
+ }
335
+ function projectAssertionSuite(input) {
336
+ return Object.freeze({
337
+ status: normalizeStatus(input.status, "assertion suite status"),
338
+ pass_count: input.pass_count,
339
+ fail_count: input.fail_count,
340
+ results: Object.freeze(input.results.map(projectAssertionResult)),
341
+ });
342
+ }
343
+ function projectAssertionResult(input) {
344
+ return Object.freeze({
345
+ family: sanitizeText(input.family),
346
+ relationship: sanitizeText(input.relationship),
347
+ status: normalizeStatus(input.status, "assertion status"),
348
+ summary: sanitizeText(input.summary),
349
+ result_hash: input.result_hash,
350
+ evidence_count: input.evidence.length,
351
+ observed_hashes: uniqueSortedHashes(input.evidence.flatMap((item) => item.observed_hash === undefined ? [] : [item.observed_hash])),
352
+ });
353
+ }
354
+ function projectReplay(input) {
355
+ if (input.kind === "replay-byte-identical") {
356
+ return projectReplayByteIdentical(input);
357
+ }
358
+ return projectReplayParityMatrix(input);
359
+ }
360
+ function projectReplayByteIdentical(input) {
361
+ return Object.freeze({
362
+ kind: "replay-byte-identical",
363
+ ...projectReplayByteIdenticalWithoutKind(input),
364
+ summary_hash: input.summary_hash,
365
+ });
366
+ }
367
+ function projectReplayByteIdenticalWithoutKind(input) {
368
+ return Object.freeze({
369
+ status: normalizeStatus(input.status, "replay status"),
370
+ expected_hash: input.expected_hash,
371
+ actual_hash: input.actual_hash,
372
+ ...("byte_length" in input ? { byte_length: input.byte_length } : {}),
373
+ ...("reason" in input ? { reason: sanitizeText(input.reason) } : {}),
374
+ });
375
+ }
376
+ function projectReplayParityMatrix(input) {
377
+ return Object.freeze({
378
+ kind: "cross-adapter-parity",
379
+ status: normalizeStatus(input.status, "replay parity status"),
380
+ ready_rows_run: input.ready_rows_run,
381
+ future_rows: input.future_rows,
382
+ rows: Object.freeze(input.rows.map(projectReplayParityRow)),
383
+ summary_hash: input.summary_hash,
384
+ });
385
+ }
386
+ function projectReplayParityRow(input) {
387
+ return Object.freeze({
388
+ adapter_name: sanitizeText(input.adapter_name),
389
+ storage_adapter: sanitizeText(input.storage_adapter),
390
+ status: sanitizeText(input.status),
391
+ ...("reason" in input ? { reason: sanitizeText(input.reason) } : {}),
392
+ ...("check" in input && input.check !== undefined
393
+ ? { check: projectReplayByteIdenticalWithoutKind(input.check) }
394
+ : {}),
395
+ });
396
+ }
397
+ function summarizeReplayParityMatrix(input) {
398
+ const status = input.ok ? "pass" : "fail";
399
+ const payload = {
400
+ kind: "cross-adapter-parity",
401
+ status,
402
+ ready_rows_run: input.ready_rows_run,
403
+ future_rows: input.future_rows,
404
+ rows: Object.freeze(input.rows.map(summarizeReplayParityRow)),
405
+ };
406
+ return Object.freeze({
407
+ ...payload,
408
+ summary_hash: hashCanonicalValue(payload),
409
+ });
410
+ }
411
+ function summarizeReplayParityRow(input) {
412
+ return Object.freeze({
413
+ adapter_name: sanitizeText(input.adapter_name),
414
+ storage_adapter: sanitizeText(input.storage_adapter),
415
+ status: sanitizeText(input.status),
416
+ ...("reason" in input ? { reason: sanitizeText(input.reason) } : {}),
417
+ ...("check" in input && input.check !== undefined
418
+ ? { check: summarizeReplayByteIdenticalWithoutKind(input.check) }
419
+ : {}),
420
+ });
421
+ }
422
+ function computeMetrics(cases, evidenceHashCount) {
423
+ const assertionResults = cases.flatMap((item) => item.assertions.results);
424
+ const replaySummaries = cases.flatMap((item) => item.replay === undefined ? [] : [item.replay]);
425
+ const replayParitySummaries = replaySummaries.filter((item) => item.kind === "cross-adapter-parity");
426
+ return Object.freeze({
427
+ case_count: cases.length,
428
+ case_pass_count: cases.filter((item) => item.status === "pass").length,
429
+ case_fail_count: cases.filter((item) => item.status === "fail").length,
430
+ assertion_count: assertionResults.length,
431
+ assertion_pass_count: assertionResults.filter((item) => item.status === "pass").length,
432
+ assertion_fail_count: assertionResults.filter((item) => item.status === "fail").length,
433
+ replay_parity_summary_count: replaySummaries.length,
434
+ replay_parity_pass_count: replaySummaries.filter((item) => item.status === "pass").length,
435
+ replay_parity_fail_count: replaySummaries.filter((item) => item.status === "fail").length,
436
+ replay_parity_ready_rows_run: replayParitySummaries.reduce((sum, item) => sum + item.ready_rows_run, 0),
437
+ replay_parity_future_rows: replayParitySummaries.reduce((sum, item) => sum + item.future_rows, 0),
438
+ evidence_hash_count: evidenceHashCount,
439
+ });
440
+ }
441
+ function hashRedactedObservedValue(value) {
442
+ try {
443
+ return hashCanonicalValue(redactJsonValue(value));
444
+ }
445
+ catch {
446
+ return undefined;
447
+ }
448
+ }
449
+ function hashCanonicalValue(value) {
450
+ return `sha256:${(0, node_crypto_1.createHash)("sha256").update(renderCanonical(value)).digest("hex")}`;
451
+ }
452
+ function redactJsonValue(value) {
453
+ if (value === null) {
454
+ return null;
455
+ }
456
+ switch (typeof value) {
457
+ case "boolean":
458
+ case "number":
459
+ return value;
460
+ case "string":
461
+ return sanitizeText(value);
462
+ case "object":
463
+ if (Array.isArray(value)) {
464
+ return value.map(redactJsonValue);
465
+ }
466
+ if (!isPlainRecord(value)) {
467
+ throw new TypeError("observed value is not canonical JSON");
468
+ }
469
+ return redactJsonObject(value);
470
+ case "undefined":
471
+ case "bigint":
472
+ case "function":
473
+ case "symbol":
474
+ throw new TypeError(`observed value cannot be ${typeof value}`);
475
+ }
476
+ }
477
+ function redactJsonObject(value) {
478
+ const result = {};
479
+ for (const key of Object.keys(value)) {
480
+ const item = value[key];
481
+ if (item === undefined) {
482
+ continue;
483
+ }
484
+ result[redactedObjectKey(result, sanitizeText(key))] = redactJsonValue(item);
485
+ }
486
+ return result;
487
+ }
488
+ function sanitizeText(value) {
489
+ return TEXT_REDACTION_PATTERNS.reduce((current, item) => current.replace(item.pattern, item.replacement), value);
490
+ }
491
+ function redactedObjectKey(result, key) {
492
+ if (!(key in result)) {
493
+ return key;
494
+ }
495
+ let index = 2;
496
+ let candidate = `${key}#${index}`;
497
+ while (candidate in result) {
498
+ index += 1;
499
+ candidate = `${key}#${index}`;
500
+ }
501
+ return candidate;
502
+ }
503
+ function normalizeStatus(status, label) {
504
+ if (status === "pass" || status === "fail") {
505
+ return status;
506
+ }
507
+ throw new Error(`${label} must be pass or fail`);
508
+ }
509
+ function uniqueSortedHashes(values) {
510
+ const seen = new Set();
511
+ for (const value of values) {
512
+ assertContentHash(value, "evidence hash");
513
+ seen.add(value);
514
+ }
515
+ return Object.freeze(Array.from(seen).sort());
516
+ }
517
+ function assertModelMatrix(value) {
518
+ if (value.status !== "not-run") {
519
+ throw new Error("model_matrix.status must be not-run");
520
+ }
521
+ assertNonEmptyString(value.reason, "model_matrix.reason");
522
+ }
523
+ function assertProjectionTier(value) {
524
+ if (value !== "owner" && value !== "subscriber" && value !== "public") {
525
+ throw new Error("projection tier must be owner, subscriber, or public");
526
+ }
527
+ }
528
+ function assertNonEmptyString(value, label) {
529
+ if (value.length === 0) {
530
+ throw new Error(`${label} must be non-empty`);
531
+ }
532
+ }
533
+ function assertIsoInstant(value, label) {
534
+ if (!ISO_INSTANT_PATTERN.test(value)) {
535
+ throw new Error(`${label} must be an ISO instant string`);
536
+ }
537
+ }
538
+ function assertContentHash(value, label) {
539
+ if (!CONTENT_HASH_PATTERN.test(value)) {
540
+ throw new Error(`${label} must be a sha256 content hash`);
541
+ }
542
+ }
543
+ function isAssertionResultArray(value) {
544
+ return Array.isArray(value);
545
+ }
546
+ function markdownTableCell(value) {
547
+ return sanitizeText(value).replace(/\|/g, "\\|").replace(/\n/g, "<br>");
548
+ }
549
+ function markdownInline(value) {
550
+ return sanitizeText(value).replace(/`/g, "\\`");
551
+ }
552
+ function renderCanonical(value) {
553
+ if (value === null) {
554
+ return "null";
555
+ }
556
+ switch (typeof value) {
557
+ case "boolean":
558
+ return value ? "true" : "false";
559
+ case "number":
560
+ if (!Number.isFinite(value)) {
561
+ throw new TypeError("Cannot canonicalize non-finite numbers");
562
+ }
563
+ return JSON.stringify(value);
564
+ case "string":
565
+ return JSON.stringify(value);
566
+ case "object":
567
+ if (Array.isArray(value)) {
568
+ return `[${value.map((item) => renderCanonical(item)).join(",")}]`;
569
+ }
570
+ if (!isPlainRecord(value)) {
571
+ throw new TypeError("Cannot canonicalize non-plain objects");
572
+ }
573
+ return renderCanonicalObject(value);
574
+ case "undefined":
575
+ case "bigint":
576
+ case "function":
577
+ case "symbol":
578
+ throw new TypeError(`Cannot canonicalize ${typeof value}`);
579
+ }
580
+ throw new TypeError("Cannot canonicalize unknown value");
581
+ }
582
+ function renderCanonicalObject(value) {
583
+ const fields = [];
584
+ for (const key of Object.keys(value).sort()) {
585
+ const item = value[key];
586
+ if (item === undefined) {
587
+ throw new TypeError(`Cannot canonicalize undefined field ${key}`);
588
+ }
589
+ fields.push(`${JSON.stringify(key)}:${renderCanonical(item)}`);
590
+ }
591
+ return `{${fields.join(",")}}`;
592
+ }
593
+ function isPlainRecord(value) {
594
+ const prototype = Object.getPrototypeOf(value);
595
+ return prototype === Object.prototype || prototype === null;
596
+ }
@@ -0,0 +1,24 @@
1
+ export * from "./assert";
2
+ export * from "./baselines/cost-thesis";
3
+ export * from "./baselines/no-memo";
4
+ export * from "./baselines/naive-loop";
5
+ export * from "./doubles/clock";
6
+ export * from "./doubles/storage";
7
+ export * from "./eval";
8
+ export * from "./policy-author";
9
+ export * from "./policy-drift";
10
+ export * from "./policy-replay";
11
+ export * from "./provider-parity";
12
+ export * from "./recompile";
13
+ export * from "./release-parity";
14
+ export * from "./release-candidate";
15
+ export * from "./rollback";
16
+ export * from "./replay/model-gateway";
17
+ export * from "./replay/parity";
18
+ export * from "./scenario/parser";
19
+ export * from "./scenario/runner";
20
+ export * from "./scenario/time";
21
+ export * from "./scenario/types";
22
+ export * from "./spikes";
23
+ export * from "./world";
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,QAAQ,CAAC;AACvB,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./assert"), exports);
18
+ __exportStar(require("./baselines/cost-thesis"), exports);
19
+ __exportStar(require("./baselines/no-memo"), exports);
20
+ __exportStar(require("./baselines/naive-loop"), exports);
21
+ __exportStar(require("./doubles/clock"), exports);
22
+ __exportStar(require("./doubles/storage"), exports);
23
+ __exportStar(require("./eval"), exports);
24
+ __exportStar(require("./policy-author"), exports);
25
+ __exportStar(require("./policy-drift"), exports);
26
+ __exportStar(require("./policy-replay"), exports);
27
+ __exportStar(require("./provider-parity"), exports);
28
+ __exportStar(require("./recompile"), exports);
29
+ __exportStar(require("./release-parity"), exports);
30
+ __exportStar(require("./release-candidate"), exports);
31
+ __exportStar(require("./rollback"), exports);
32
+ __exportStar(require("./replay/model-gateway"), exports);
33
+ __exportStar(require("./replay/parity"), exports);
34
+ __exportStar(require("./scenario/parser"), exports);
35
+ __exportStar(require("./scenario/runner"), exports);
36
+ __exportStar(require("./scenario/time"), exports);
37
+ __exportStar(require("./scenario/types"), exports);
38
+ __exportStar(require("./spikes"), exports);
39
+ __exportStar(require("./world"), exports);