@nodetool-ai/reliability-harness 0.8.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 (167) hide show
  1. package/README.md +34 -0
  2. package/dist/.nodetool-build-stamp +1 -0
  3. package/dist/cli.d.ts +73 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +201 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/compare.d.ts +98 -0
  8. package/dist/compare.d.ts.map +1 -0
  9. package/dist/compare.js +267 -0
  10. package/dist/compare.js.map +1 -0
  11. package/dist/core/diff.d.ts +39 -0
  12. package/dist/core/diff.d.ts.map +1 -0
  13. package/dist/core/diff.js +114 -0
  14. package/dist/core/diff.js.map +1 -0
  15. package/dist/core/golden.d.ts +50 -0
  16. package/dist/core/golden.d.ts.map +1 -0
  17. package/dist/core/golden.js +150 -0
  18. package/dist/core/golden.js.map +1 -0
  19. package/dist/core/invariants/index.d.ts +32 -0
  20. package/dist/core/invariants/index.d.ts.map +1 -0
  21. package/dist/core/invariants/index.js +44 -0
  22. package/dist/core/invariants/index.js.map +1 -0
  23. package/dist/core/invariants/leaks.d.ts +22 -0
  24. package/dist/core/invariants/leaks.d.ts.map +1 -0
  25. package/dist/core/invariants/leaks.js +55 -0
  26. package/dist/core/invariants/leaks.js.map +1 -0
  27. package/dist/core/invariants/lifecycle.d.ts +19 -0
  28. package/dist/core/invariants/lifecycle.d.ts.map +1 -0
  29. package/dist/core/invariants/lifecycle.js +95 -0
  30. package/dist/core/invariants/lifecycle.js.map +1 -0
  31. package/dist/core/invariants/protocol-validity.d.ts +4 -0
  32. package/dist/core/invariants/protocol-validity.d.ts.map +1 -0
  33. package/dist/core/invariants/protocol-validity.js +131 -0
  34. package/dist/core/invariants/protocol-validity.js.map +1 -0
  35. package/dist/core/invariants/state-machine.d.ts +29 -0
  36. package/dist/core/invariants/state-machine.d.ts.map +1 -0
  37. package/dist/core/invariants/state-machine.js +79 -0
  38. package/dist/core/invariants/state-machine.js.map +1 -0
  39. package/dist/core/invariants/status-sets.d.ts +27 -0
  40. package/dist/core/invariants/status-sets.d.ts.map +1 -0
  41. package/dist/core/invariants/status-sets.js +51 -0
  42. package/dist/core/invariants/status-sets.js.map +1 -0
  43. package/dist/core/invariants/terminal-uniqueness.d.ts +23 -0
  44. package/dist/core/invariants/terminal-uniqueness.d.ts.map +1 -0
  45. package/dist/core/invariants/terminal-uniqueness.js +99 -0
  46. package/dist/core/invariants/terminal-uniqueness.js.map +1 -0
  47. package/dist/core/invariants/types.d.ts +25 -0
  48. package/dist/core/invariants/types.d.ts.map +1 -0
  49. package/dist/core/invariants/types.js +2 -0
  50. package/dist/core/invariants/types.js.map +1 -0
  51. package/dist/core/journey.d.ts +121 -0
  52. package/dist/core/journey.d.ts.map +1 -0
  53. package/dist/core/journey.js +166 -0
  54. package/dist/core/journey.js.map +1 -0
  55. package/dist/core/normalize.d.ts +86 -0
  56. package/dist/core/normalize.d.ts.map +1 -0
  57. package/dist/core/normalize.js +182 -0
  58. package/dist/core/normalize.js.map +1 -0
  59. package/dist/core/record.d.ts +160 -0
  60. package/dist/core/record.d.ts.map +1 -0
  61. package/dist/core/record.js +74 -0
  62. package/dist/core/record.js.map +1 -0
  63. package/dist/core/stable-json.d.ts +13 -0
  64. package/dist/core/stable-json.d.ts.map +1 -0
  65. package/dist/core/stable-json.js +28 -0
  66. package/dist/core/stable-json.js.map +1 -0
  67. package/dist/drivers/anchors.d.ts +29 -0
  68. package/dist/drivers/anchors.d.ts.map +1 -0
  69. package/dist/drivers/anchors.js +48 -0
  70. package/dist/drivers/anchors.js.map +1 -0
  71. package/dist/drivers/app-headless.d.ts +39 -0
  72. package/dist/drivers/app-headless.d.ts.map +1 -0
  73. package/dist/drivers/app-headless.js +123 -0
  74. package/dist/drivers/app-headless.js.map +1 -0
  75. package/dist/drivers/browser.d.ts +34 -0
  76. package/dist/drivers/browser.d.ts.map +1 -0
  77. package/dist/drivers/browser.js +176 -0
  78. package/dist/drivers/browser.js.map +1 -0
  79. package/dist/drivers/cli.d.ts +13 -0
  80. package/dist/drivers/cli.d.ts.map +1 -0
  81. package/dist/drivers/cli.js +82 -0
  82. package/dist/drivers/cli.js.map +1 -0
  83. package/dist/drivers/fixture-nodes.d.ts +124 -0
  84. package/dist/drivers/fixture-nodes.d.ts.map +1 -0
  85. package/dist/drivers/fixture-nodes.js +189 -0
  86. package/dist/drivers/fixture-nodes.js.map +1 -0
  87. package/dist/drivers/index.d.ts +18 -0
  88. package/dist/drivers/index.d.ts.map +1 -0
  89. package/dist/drivers/index.js +12 -0
  90. package/dist/drivers/index.js.map +1 -0
  91. package/dist/drivers/kernel.d.ts +8 -0
  92. package/dist/drivers/kernel.d.ts.map +1 -0
  93. package/dist/drivers/kernel.js +247 -0
  94. package/dist/drivers/kernel.js.map +1 -0
  95. package/dist/drivers/packaged.d.ts +28 -0
  96. package/dist/drivers/packaged.d.ts.map +1 -0
  97. package/dist/drivers/packaged.js +303 -0
  98. package/dist/drivers/packaged.js.map +1 -0
  99. package/dist/drivers/python-bridge.d.ts +22 -0
  100. package/dist/drivers/python-bridge.d.ts.map +1 -0
  101. package/dist/drivers/python-bridge.js +48 -0
  102. package/dist/drivers/python-bridge.js.map +1 -0
  103. package/dist/drivers/registry.d.ts +11 -0
  104. package/dist/drivers/registry.d.ts.map +1 -0
  105. package/dist/drivers/registry.js +18 -0
  106. package/dist/drivers/registry.js.map +1 -0
  107. package/dist/drivers/relay-fields.d.ts +70 -0
  108. package/dist/drivers/relay-fields.d.ts.map +1 -0
  109. package/dist/drivers/relay-fields.js +104 -0
  110. package/dist/drivers/relay-fields.js.map +1 -0
  111. package/dist/drivers/repo-root.d.ts +9 -0
  112. package/dist/drivers/repo-root.d.ts.map +1 -0
  113. package/dist/drivers/repo-root.js +39 -0
  114. package/dist/drivers/repo-root.js.map +1 -0
  115. package/dist/drivers/spawn-cli.d.ts +8 -0
  116. package/dist/drivers/spawn-cli.d.ts.map +1 -0
  117. package/dist/drivers/spawn-cli.js +41 -0
  118. package/dist/drivers/spawn-cli.js.map +1 -0
  119. package/dist/drivers/types.d.ts +18 -0
  120. package/dist/drivers/types.d.ts.map +1 -0
  121. package/dist/drivers/types.js +2 -0
  122. package/dist/drivers/types.js.map +1 -0
  123. package/dist/drivers/ws-server.d.ts +8 -0
  124. package/dist/drivers/ws-server.d.ts.map +1 -0
  125. package/dist/drivers/ws-server.js +382 -0
  126. package/dist/drivers/ws-server.js.map +1 -0
  127. package/dist/faults/bridge-faults.d.ts +7 -0
  128. package/dist/faults/bridge-faults.d.ts.map +1 -0
  129. package/dist/faults/bridge-faults.js +90 -0
  130. package/dist/faults/bridge-faults.js.map +1 -0
  131. package/dist/faults/host-faults.d.ts +40 -0
  132. package/dist/faults/host-faults.d.ts.map +1 -0
  133. package/dist/faults/host-faults.js +70 -0
  134. package/dist/faults/host-faults.js.map +1 -0
  135. package/dist/faults/index.d.ts +21 -0
  136. package/dist/faults/index.d.ts.map +1 -0
  137. package/dist/faults/index.js +29 -0
  138. package/dist/faults/index.js.map +1 -0
  139. package/dist/faults/net-proxy.d.ts +75 -0
  140. package/dist/faults/net-proxy.d.ts.map +1 -0
  141. package/dist/faults/net-proxy.js +305 -0
  142. package/dist/faults/net-proxy.js.map +1 -0
  143. package/dist/faults/provider-faults.d.ts +12 -0
  144. package/dist/faults/provider-faults.d.ts.map +1 -0
  145. package/dist/faults/provider-faults.js +110 -0
  146. package/dist/faults/provider-faults.js.map +1 -0
  147. package/dist/faults/registry.d.ts +17 -0
  148. package/dist/faults/registry.d.ts.map +1 -0
  149. package/dist/faults/registry.js +15 -0
  150. package/dist/faults/registry.js.map +1 -0
  151. package/dist/faults/types.d.ts +42 -0
  152. package/dist/faults/types.d.ts.map +1 -0
  153. package/dist/faults/types.js +2 -0
  154. package/dist/faults/types.js.map +1 -0
  155. package/dist/faults/ws-faults.d.ts +6 -0
  156. package/dist/faults/ws-faults.d.ts.map +1 -0
  157. package/dist/faults/ws-faults.js +48 -0
  158. package/dist/faults/ws-faults.js.map +1 -0
  159. package/dist/faults/ws-proxy.d.ts +36 -0
  160. package/dist/faults/ws-proxy.d.ts.map +1 -0
  161. package/dist/faults/ws-proxy.js +40 -0
  162. package/dist/faults/ws-proxy.js.map +1 -0
  163. package/dist/index.d.ts +12 -0
  164. package/dist/index.d.ts.map +1 -0
  165. package/dist/index.js +12 -0
  166. package/dist/index.js.map +1 -0
  167. package/package.json +75 -0
@@ -0,0 +1,267 @@
1
+ import { diffNormalizedRecords, formatStreamDiff, streamDiffIsEmpty } from "./core/diff.js";
2
+ import { checkGoldens } from "./core/golden.js";
3
+ import { INVARIANT_CHECKS } from "./core/invariants/index.js";
4
+ import { normalizeRunRecord } from "./core/normalize.js";
5
+ import { getFaultModule } from "./faults/registry.js";
6
+ // Side-effecting import: registers every built-in fault module (task D1's
7
+ // "faults/index.ts registers on import" contract) so `getFaultModule` below
8
+ // resolves journey-declared fault names without every caller of
9
+ // `compareJourney` remembering to import the fault barrel itself.
10
+ import "./faults/index.js";
11
+ function onlyServerToClient(record) {
12
+ return {
13
+ ...record,
14
+ frames: record.frames.filter((f) => f.direction === "server_to_client")
15
+ };
16
+ }
17
+ /**
18
+ * Applies every fault in `faults` whose optional `surface` restriction either
19
+ * matches `surfaceName` or is unset (applies everywhere), in order, and
20
+ * returns one teardown that undoes them all in reverse — so a single
21
+ * `finally` block at each call site fully unwinds the fault's process-global
22
+ * side effects (today: the provider registry) regardless of how the driver
23
+ * run in between went. A fault name with no registered `FaultModule` is
24
+ * skipped and reported back via `unknownFaults` (its own return value), never
25
+ * silently ignored or thrown.
26
+ */
27
+ async function applyFaults(journey, surfaceName, faults) {
28
+ const applicable = faults.filter((f) => f.surface === undefined || f.surface === surfaceName);
29
+ const applied = [];
30
+ const unknown = [];
31
+ const teardowns = [];
32
+ for (const fault of applicable) {
33
+ const module = getFaultModule(fault.type);
34
+ if (!module) {
35
+ unknown.push(fault.type);
36
+ continue;
37
+ }
38
+ const teardown = await module.configure({ journey, surface: surfaceName, fault });
39
+ teardowns.push(teardown);
40
+ applied.push(fault.type);
41
+ }
42
+ return {
43
+ applied,
44
+ unknown,
45
+ teardown: async () => {
46
+ for (const teardown of teardowns.reverse()) {
47
+ await teardown();
48
+ }
49
+ }
50
+ };
51
+ }
52
+ /**
53
+ * Runs `journey` on every driver in `drivers`, diffing each non-oracle
54
+ * surface's normalized `server_to_client` stream against the oracle's (the
55
+ * one driver named `options.oracleName`, default `"kernel"` — §12's "the
56
+ * kernel surface is the oracle"). `drivers` must include the oracle driver
57
+ * itself; it both anchors the diff and is reported as its own (trivially
58
+ * clean) surface.
59
+ *
60
+ * `options.faults` (task D1) selects the fault matrix for this run — default
61
+ * is the journey's own declared `faults` block (`journey.json`), so callers
62
+ * that don't care about fault injection get the journey's authored behavior
63
+ * unchanged. Each applicable fault (its `surface` restriction, if any,
64
+ * matched against the driver about to run) is configured immediately before
65
+ * that driver's `run()` and torn down immediately after, so faults never
66
+ * leak across drivers or across `compareJourney` calls.
67
+ */
68
+ export async function compareJourney(journey, drivers, options = {}) {
69
+ const oracleName = options.oracleName ?? "kernel";
70
+ const oracleDriver = drivers.find((d) => d.name === oracleName);
71
+ if (!oracleDriver) {
72
+ throw new Error(`compareJourney: no driver named "${oracleName}" among [${drivers
73
+ .map((d) => d.name)
74
+ .join(", ")}] — the oracle driver must be included`);
75
+ }
76
+ const faults = options.faults ?? journey.manifest.faults;
77
+ const invariantNames = journey.manifest.assertions.invariants;
78
+ const checks = invariantNames
79
+ .map((name) => ({ name, check: INVARIANT_CHECKS[name] }))
80
+ .filter((c) => !!c.check);
81
+ const unknownInvariants = invariantNames.filter((name) => !INVARIANT_CHECKS[name]);
82
+ const issues = [];
83
+ if (unknownInvariants.length > 0) {
84
+ issues.push(`journey declares unknown invariant(s), skipped: ${unknownInvariants.join(", ")}`);
85
+ }
86
+ const unknownFaultsSeen = new Set();
87
+ /** Runs one driver with its applicable faults configured/torn down around
88
+ * the call — shared by the oracle (run once, up front) and every other
89
+ * surface's loop iteration below. */
90
+ const runWithFaults = async (driver) => {
91
+ const { applied, unknown, teardown } = await applyFaults(journey, driver.name, faults);
92
+ for (const name of unknown)
93
+ unknownFaultsSeen.add(name);
94
+ try {
95
+ const record = await driver.run(journey);
96
+ return { record, faultsApplied: applied };
97
+ }
98
+ finally {
99
+ await teardown();
100
+ }
101
+ };
102
+ // Run the oracle first — every other surface's diff needs its normalized
103
+ // record, and running it once (not per-surface) keeps an N-driver compare
104
+ // to N+1 runs, not 2N.
105
+ const { record: oracleRecord, faultsApplied: oracleFaultsApplied } = await runWithFaults(oracleDriver);
106
+ const oracleNormalized = onlyServerToClient(normalizeRunRecord(oracleRecord));
107
+ const surfaces = [];
108
+ for (const driver of drivers) {
109
+ if (driver.supports && !driver.supports(journey)) {
110
+ surfaces.push({
111
+ surface: driver.name,
112
+ ran: false,
113
+ skipReason: "not applicable to this journey",
114
+ violations: [],
115
+ goldens: [],
116
+ diffVsOracle: null,
117
+ diffVsOracleEmpty: true,
118
+ notApplicable: true,
119
+ faultsApplied: [],
120
+ ok: true
121
+ });
122
+ continue;
123
+ }
124
+ let record;
125
+ let faultsApplied;
126
+ if (driver.name === oracleDriver.name) {
127
+ record = oracleRecord;
128
+ faultsApplied = oracleFaultsApplied;
129
+ }
130
+ else {
131
+ try {
132
+ const result = await runWithFaults(driver);
133
+ record = result.record;
134
+ faultsApplied = result.faultsApplied;
135
+ }
136
+ catch (err) {
137
+ const skipReason = `run failed: ${err instanceof Error ? err.message : String(err)}`;
138
+ surfaces.push({
139
+ surface: driver.name,
140
+ ran: false,
141
+ skipReason,
142
+ violations: [],
143
+ goldens: [],
144
+ diffVsOracle: null,
145
+ diffVsOracleEmpty: false,
146
+ notApplicable: false,
147
+ faultsApplied: [],
148
+ ok: false
149
+ });
150
+ issues.push(`${driver.name}: ${skipReason}`);
151
+ continue;
152
+ }
153
+ }
154
+ const violations = checks.flatMap((c) => c.check(record));
155
+ // §4's golden fixtures: the only assertion on what the run actually
156
+ // produced. Every surface is held to them, not just the oracle — a
157
+ // surface can agree with the oracle frame-for-frame and still be wrong.
158
+ const goldens = checkGoldens(journey, record, { faultsApplied });
159
+ const goldenFailures = goldens.flatMap((g) => g.failures);
160
+ let diff = null;
161
+ let diffEmpty = true;
162
+ if (driver.name !== oracleDriver.name) {
163
+ const normalized = onlyServerToClient(normalizeRunRecord(record));
164
+ diff = diffNormalizedRecords(oracleNormalized, normalized);
165
+ diffEmpty = streamDiffIsEmpty(diff);
166
+ }
167
+ const ok = violations.length === 0 && diffEmpty && goldenFailures.length === 0;
168
+ if (violations.length > 0) {
169
+ issues.push(`${driver.name}: ${violations.length} invariant violation(s)`);
170
+ }
171
+ for (const golden of goldens) {
172
+ if (golden.failures.length === 0)
173
+ continue;
174
+ issues.push(`${driver.name}: ${golden.kind} golden mismatch (${golden.failures.length})`);
175
+ }
176
+ if (!diffEmpty) {
177
+ issues.push(`${driver.name}: diverges from ${oracleDriver.name}`);
178
+ }
179
+ surfaces.push({
180
+ surface: driver.name,
181
+ ran: true,
182
+ status: record.status,
183
+ error: record.error,
184
+ violations,
185
+ goldens,
186
+ diffVsOracle: diff,
187
+ diffVsOracleEmpty: diffEmpty,
188
+ notApplicable: false,
189
+ faultsApplied,
190
+ ok
191
+ });
192
+ }
193
+ const unknownFaults = Array.from(unknownFaultsSeen);
194
+ if (unknownFaults.length > 0) {
195
+ issues.push(`fault(s) requested with no registered module: ${unknownFaults.join(", ")}`);
196
+ }
197
+ return {
198
+ journeyId: journey.manifest.name,
199
+ oracle: oracleDriver.name,
200
+ unknownInvariants,
201
+ unknownFaults,
202
+ surfaces,
203
+ verdict: { ok: surfaces.every((s) => s.ok) && unknownFaults.length === 0, issues }
204
+ };
205
+ }
206
+ /** Human-readable rendering of a `CompareReport` for `nodetool reliability run`.
207
+ * `showDiff` (default `true`) controls whether a diverging surface's full
208
+ * per-channel stream diff is inlined under its summary line, or just noted —
209
+ * `nodetool reliability run`'s `--diff` flag maps straight to this. */
210
+ export function formatCompareReport(report, options = {}) {
211
+ const showDiff = options.showDiff ?? true;
212
+ const lines = [];
213
+ lines.push(`journey: ${report.journeyId} (oracle: ${report.oracle})`);
214
+ for (const s of report.surfaces) {
215
+ if (!s.ran) {
216
+ const mark = s.notApplicable ? "·" : "✗";
217
+ lines.push(` ${mark} ${s.surface}: ${s.skipReason}`);
218
+ continue;
219
+ }
220
+ const mark = s.ok ? "✓" : "✗";
221
+ const bits = [String(s.status)];
222
+ if (s.error)
223
+ bits.push(`error: ${s.error}`);
224
+ if (s.faultsApplied.length > 0)
225
+ bits.push(`faults: ${s.faultsApplied.join(", ")}`);
226
+ if (s.violations.length > 0)
227
+ bits.push(`${s.violations.length} violation(s)`);
228
+ for (const golden of s.goldens) {
229
+ if (!golden.checked)
230
+ bits.push(`${golden.kind} golden skipped`);
231
+ else if (golden.failures.length > 0) {
232
+ bits.push(`${golden.kind} golden: ${golden.failures.length} mismatch(es)`);
233
+ }
234
+ }
235
+ if (!s.diffVsOracleEmpty) {
236
+ bits.push(showDiff ? `diverges from ${report.oracle}` : `diverges from ${report.oracle} (--diff for detail)`);
237
+ }
238
+ lines.push(` ${mark} ${s.surface}: ${bits.join(" — ")}`);
239
+ for (const v of s.violations) {
240
+ lines.push(` [${v.invariant}] ${v.message}`);
241
+ }
242
+ for (const golden of s.goldens) {
243
+ if (!golden.checked && golden.skipReason) {
244
+ lines.push(` [${golden.kind}] ${golden.skipReason}`);
245
+ }
246
+ for (const failure of golden.failures) {
247
+ lines.push(` [${golden.kind}] ${failure}`);
248
+ }
249
+ }
250
+ if (showDiff && !s.diffVsOracleEmpty && s.diffVsOracle) {
251
+ for (const line of formatStreamDiff(s.diffVsOracle).split("\n")) {
252
+ lines.push(` ${line}`);
253
+ }
254
+ }
255
+ }
256
+ if (report.unknownInvariants.length > 0) {
257
+ lines.push(` ! unknown invariant(s) declared: ${report.unknownInvariants.join(", ")}`);
258
+ }
259
+ if (report.unknownFaults.length > 0) {
260
+ lines.push(` ! unknown fault(s) requested: ${report.unknownFaults.join(", ")}`);
261
+ }
262
+ lines.push(report.verdict.ok
263
+ ? "verdict: OK"
264
+ : `verdict: FAIL — ${report.verdict.issues.join("; ")}`);
265
+ return lines.join("\n");
266
+ }
267
+ //# sourceMappingURL=compare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare.js","sourceRoot":"","sources":["../src/compare.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Per-node-channel stream diff (§4, §12).
3
+ *
4
+ * Grown from `packages/cli/src/debug/diff.ts`, which diffs two
5
+ * `DebugReport`s at the verdict/summary level (issues, token/cost deltas)
6
+ * for the debug harness's `--watch` mode. This module diffs two
7
+ * `NormalizedRunRecord`s at the *message* level, one node-channel at a time
8
+ * — because the actor model legitimately interleaves frames across nodes,
9
+ * a global stream diff would flag reorderings that are not bugs.
10
+ * `diffIsEmpty`/`formatDiff` there and `streamDiffIsEmpty`/`formatStreamDiff`
11
+ * here are deliberately parallel APIs; the debug-harness copy is untouched
12
+ * so its own tests and callers stay green.
13
+ */
14
+ import type { NormalizedRunRecord } from "./normalize.js";
15
+ interface ChannelDiffEntry {
16
+ kind: "added" | "removed" | "changed" | "unchanged";
17
+ /** Index within the side the entry belongs to (baseline for removed/changed, candidate for added). */
18
+ baselineMessage?: Record<string, unknown>;
19
+ candidateMessage?: Record<string, unknown>;
20
+ }
21
+ interface ChannelDiff {
22
+ channel: string;
23
+ entries: ChannelDiffEntry[];
24
+ }
25
+ export interface StreamDiff {
26
+ channels: ChannelDiff[];
27
+ }
28
+ /**
29
+ * Diffs two normalized records channel by channel. `baseline` is
30
+ * conventionally the kernel (oracle) surface (§12: "the kernel surface is
31
+ * the oracle"), but the function is symmetric in shape — either side can be
32
+ * either record.
33
+ */
34
+ export declare function diffNormalizedRecords(baseline: NormalizedRunRecord, candidate: NormalizedRunRecord): StreamDiff;
35
+ /** True when no channel diverged — the C1 done-when: a record diffed against itself is empty. */
36
+ export declare function streamDiffIsEmpty(diff: StreamDiff): boolean;
37
+ export declare function formatStreamDiff(diff: StreamDiff): string;
38
+ export {};
39
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/core/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAsB,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAG9E,UAAU,gBAAgB;IACxB,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC;IACpD,sGAAsG;IACtG,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAED,UAAU,WAAW;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AA6ED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,mBAAmB,EAC7B,SAAS,EAAE,mBAAmB,GAC7B,UAAU,CAwBZ;AAED,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAE3D;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAczD"}
@@ -0,0 +1,114 @@
1
+ import { stableStringify } from "./stable-json.js";
2
+ function canonicalKey(message) {
3
+ return stableStringify(message);
4
+ }
5
+ function groupByChannel(frames) {
6
+ const groups = new Map();
7
+ for (const frame of frames) {
8
+ const group = groups.get(frame.channel);
9
+ if (group)
10
+ group.push(frame);
11
+ else
12
+ groups.set(frame.channel, [frame]);
13
+ }
14
+ return groups;
15
+ }
16
+ /**
17
+ * Longest-common-subsequence diff over two message sequences (by canonical
18
+ * JSON equality), yielding `equal`/`remove`/`add` runs in original order.
19
+ * O(n·m); channels are short (one node's messages in one run), so this is
20
+ * cheap in practice.
21
+ */
22
+ function lcsDiff(baseline, candidate) {
23
+ const bKeys = baseline.map(canonicalKey);
24
+ const cKeys = candidate.map(canonicalKey);
25
+ const n = baseline.length;
26
+ const m = candidate.length;
27
+ // dp[i][j] = length of LCS of bKeys[i:] and cKeys[j:]
28
+ const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
29
+ for (let i = n - 1; i >= 0; i--) {
30
+ for (let j = m - 1; j >= 0; j--) {
31
+ dp[i][j] =
32
+ bKeys[i] === cKeys[j]
33
+ ? dp[i + 1][j + 1] + 1
34
+ : Math.max(dp[i + 1][j], dp[i][j + 1]);
35
+ }
36
+ }
37
+ const entries = [];
38
+ let i = 0;
39
+ let j = 0;
40
+ while (i < n && j < m) {
41
+ if (bKeys[i] === cKeys[j]) {
42
+ entries.push({
43
+ kind: "unchanged",
44
+ baselineMessage: baseline[i],
45
+ candidateMessage: candidate[j]
46
+ });
47
+ i++;
48
+ j++;
49
+ }
50
+ else if (dp[i + 1][j] >= dp[i][j + 1]) {
51
+ entries.push({ kind: "removed", baselineMessage: baseline[i] });
52
+ i++;
53
+ }
54
+ else {
55
+ entries.push({ kind: "added", candidateMessage: candidate[j] });
56
+ j++;
57
+ }
58
+ }
59
+ while (i < n) {
60
+ entries.push({ kind: "removed", baselineMessage: baseline[i] });
61
+ i++;
62
+ }
63
+ while (j < m) {
64
+ entries.push({ kind: "added", candidateMessage: candidate[j] });
65
+ j++;
66
+ }
67
+ return entries;
68
+ }
69
+ /**
70
+ * Diffs two normalized records channel by channel. `baseline` is
71
+ * conventionally the kernel (oracle) surface (§12: "the kernel surface is
72
+ * the oracle"), but the function is symmetric in shape — either side can be
73
+ * either record.
74
+ */
75
+ export function diffNormalizedRecords(baseline, candidate) {
76
+ const baselineGroups = groupByChannel(baseline.frames);
77
+ const candidateGroups = groupByChannel(candidate.frames);
78
+ const channels = new Set([
79
+ ...baselineGroups.keys(),
80
+ ...candidateGroups.keys()
81
+ ]);
82
+ const result = [];
83
+ for (const channel of Array.from(channels).sort()) {
84
+ const baselineMessages = (baselineGroups.get(channel) ?? []).map((f) => f.message);
85
+ const candidateMessages = (candidateGroups.get(channel) ?? []).map((f) => f.message);
86
+ const entries = lcsDiff(baselineMessages, candidateMessages).filter((e) => e.kind !== "unchanged");
87
+ if (entries.length > 0) {
88
+ result.push({ channel, entries });
89
+ }
90
+ }
91
+ return { channels: result };
92
+ }
93
+ /** True when no channel diverged — the C1 done-when: a record diffed against itself is empty. */
94
+ export function streamDiffIsEmpty(diff) {
95
+ return diff.channels.length === 0;
96
+ }
97
+ export function formatStreamDiff(diff) {
98
+ if (streamDiffIsEmpty(diff))
99
+ return "No divergence.";
100
+ const lines = [];
101
+ for (const channel of diff.channels) {
102
+ lines.push(`channel ${channel.channel}:`);
103
+ for (const entry of channel.entries) {
104
+ if (entry.kind === "removed") {
105
+ lines.push(` - ${canonicalKey(entry.baselineMessage)}`);
106
+ }
107
+ else if (entry.kind === "added") {
108
+ lines.push(` + ${canonicalKey(entry.candidateMessage)}`);
109
+ }
110
+ }
111
+ }
112
+ return lines.join("\n");
113
+ }
114
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/core/diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Golden-fixture assertions (§4): the `expected/outputs.json` and
3
+ * `expected/stream.shape.json` a journey opts into via
4
+ * `assertions.outputs` / `assertions.streamShape`.
5
+ *
6
+ * These are the only checks that pin down *what a run produced*. Invariants
7
+ * describe stream shape and lifecycle; the cross-surface diff only says the
8
+ * surfaces agree — both pass happily when every surface returns the same
9
+ * wrong answer. A journey that declares a golden must have it compared, or
10
+ * declaring it means nothing.
11
+ */
12
+ import type { Journey } from "./journey.js";
13
+ import type { RunRecord } from "./record.js";
14
+ import { type NormalizedRunRecord } from "./normalize.js";
15
+ /** One golden entry, matching `expected/stream.shape.json`'s element shape. */
16
+ interface StreamShapeEntry {
17
+ channel: string;
18
+ message: Record<string, unknown>;
19
+ }
20
+ export interface GoldenCheckResult {
21
+ kind: "outputs" | "streamShape";
22
+ /** False when the journey didn't declare it, or the run couldn't be compared. */
23
+ checked: boolean;
24
+ /** Why it wasn't checked. Only set when `!checked`. */
25
+ skipReason?: string;
26
+ /** Human-readable mismatches; empty when the golden matched. */
27
+ failures: string[];
28
+ }
29
+ /**
30
+ * A run's terminal outputs, keyed the way `expected/outputs.json` is: by an
31
+ * Output node's public `output_name`, last value wins (a node that streams
32
+ * several values ends on the one a client would display).
33
+ */
34
+ export declare function terminalOutputsOf(record: RunRecord): Record<string, unknown>;
35
+ /** A run's normalized `server_to_client` stream in golden-fixture shape. */
36
+ export declare function streamShapeOf(record: RunRecord, normalized?: NormalizedRunRecord): StreamShapeEntry[];
37
+ /**
38
+ * Runs the golden comparisons a journey declared against one surface's record.
39
+ *
40
+ * Faulted runs are skipped rather than compared: a golden describes what the
41
+ * journey produces when nothing is broken, and every fault journey that opts
42
+ * into outputs (`host-disk-full-write`, `python-node-workflow`) declares its
43
+ * faults precisely because the run is *supposed* to fail under them. The skip
44
+ * is reported, never silent.
45
+ */
46
+ export declare function checkGoldens(journey: Journey, record: RunRecord, options?: {
47
+ faultsApplied?: readonly string[];
48
+ }): GoldenCheckResult[];
49
+ export {};
50
+ //# sourceMappingURL=golden.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"golden.d.ts","sourceRoot":"","sources":["../../src/core/golden.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAsB,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAG9E,+EAA+E;AAC/E,UAAU,gBAAgB;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,SAAS,GAAG,aAAa,CAAC;IAChC,iFAAiF;IACjF,OAAO,EAAE,OAAO,CAAC;IACjB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAKD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAW5E;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAC3B,MAAM,EAAE,SAAS,EACjB,UAAU,GAAE,mBAAgD,GAC3D,gBAAgB,EAAE,CAIpB;AAyFD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,SAAS,EACjB,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAO,GAClD,iBAAiB,EAAE,CA0CrB"}
@@ -0,0 +1,150 @@
1
+ import { normalizeRunRecord } from "./normalize.js";
2
+ import { stableStringify } from "./stable-json.js";
3
+ /** Max mismatching stream-shape entries reported before truncating. */
4
+ const MAX_REPORTED_ENTRIES = 10;
5
+ /**
6
+ * A run's terminal outputs, keyed the way `expected/outputs.json` is: by an
7
+ * Output node's public `output_name`, last value wins (a node that streams
8
+ * several values ends on the one a client would display).
9
+ */
10
+ export function terminalOutputsOf(record) {
11
+ const outputs = {};
12
+ for (const frame of record.frames) {
13
+ if (frame.direction !== "server_to_client")
14
+ continue;
15
+ const message = frame.message;
16
+ if (message["type"] !== "output_update")
17
+ continue;
18
+ const name = message["output_name"];
19
+ if (typeof name !== "string")
20
+ continue;
21
+ outputs[name] = message["value"];
22
+ }
23
+ return outputs;
24
+ }
25
+ /** A run's normalized `server_to_client` stream in golden-fixture shape. */
26
+ export function streamShapeOf(record, normalized = normalizeRunRecord(record)) {
27
+ return normalized.frames
28
+ .filter((frame) => frame.direction === "server_to_client")
29
+ .map((frame) => ({ channel: frame.channel, message: frame.message }));
30
+ }
31
+ function compareOutputs(expected, record) {
32
+ const actual = terminalOutputsOf(record);
33
+ const expectedRecord = (expected ?? {});
34
+ const failures = [];
35
+ const keys = new Set([
36
+ ...Object.keys(expectedRecord),
37
+ ...Object.keys(actual)
38
+ ]);
39
+ for (const key of Array.from(keys).sort()) {
40
+ const want = stableStringify(expectedRecord[key]);
41
+ const got = stableStringify(actual[key]);
42
+ if (want === got)
43
+ continue;
44
+ if (!(key in expectedRecord)) {
45
+ failures.push(`unexpected output "${key}": ${got}`);
46
+ }
47
+ else if (!(key in actual)) {
48
+ failures.push(`missing output "${key}" (expected ${want})`);
49
+ }
50
+ else {
51
+ failures.push(`output "${key}": expected ${want}, got ${got}`);
52
+ }
53
+ }
54
+ return failures;
55
+ }
56
+ function byChannel(entries) {
57
+ const groups = new Map();
58
+ for (const entry of entries) {
59
+ const serialized = stableStringify(entry.message);
60
+ const group = groups.get(entry.channel);
61
+ if (group)
62
+ group.push(serialized);
63
+ else
64
+ groups.set(entry.channel, [serialized]);
65
+ }
66
+ return groups;
67
+ }
68
+ /**
69
+ * Compared per node-channel, in channel order — the same rule the
70
+ * cross-surface diff uses (§4: "per-node-channel, not globally"). The actor
71
+ * model legitimately interleaves frames across nodes run to run, so a global
72
+ * index-wise comparison would flag reorderings that are not regressions;
73
+ * within one channel the order is the node's own and is asserted exactly.
74
+ *
75
+ * One limit is inherited from normalization: id placeholders are numbered by
76
+ * order of first sight (`IdMapper`), so a run where a *different* node emits
77
+ * first renumbers them and mismatches. The cross-surface diff has the same
78
+ * property; journeys whose node start order isn't structurally fixed should
79
+ * not declare `streamShape`.
80
+ */
81
+ function compareStreamShape(expected, record, normalized) {
82
+ if (!Array.isArray(expected)) {
83
+ return ["expected/stream.shape.json is not an array of {channel, message}"];
84
+ }
85
+ const golden = byChannel(expected);
86
+ const actual = byChannel(streamShapeOf(record, normalized));
87
+ const failures = [];
88
+ const channels = Array.from(new Set([...golden.keys(), ...actual.keys()])).sort();
89
+ for (const channel of channels) {
90
+ const want = golden.get(channel) ?? [];
91
+ const got = actual.get(channel) ?? [];
92
+ for (let i = 0; i < Math.max(want.length, got.length); i++) {
93
+ if (failures.length >= MAX_REPORTED_ENTRIES) {
94
+ failures.push("… further mismatches not reported");
95
+ return failures;
96
+ }
97
+ if (want[i] === got[i])
98
+ continue;
99
+ if (want[i] === undefined) {
100
+ failures.push(`${channel}[${i}]: unexpected ${got[i]}`);
101
+ }
102
+ else if (got[i] === undefined) {
103
+ failures.push(`${channel}[${i}]: missing ${want[i]}`);
104
+ }
105
+ else {
106
+ failures.push(`${channel}[${i}]: expected ${want[i]}, got ${got[i]}`);
107
+ }
108
+ }
109
+ }
110
+ return failures;
111
+ }
112
+ /**
113
+ * Runs the golden comparisons a journey declared against one surface's record.
114
+ *
115
+ * Faulted runs are skipped rather than compared: a golden describes what the
116
+ * journey produces when nothing is broken, and every fault journey that opts
117
+ * into outputs (`host-disk-full-write`, `python-node-workflow`) declares its
118
+ * faults precisely because the run is *supposed* to fail under them. The skip
119
+ * is reported, never silent.
120
+ */
121
+ export function checkGoldens(journey, record, options = {}) {
122
+ const { assertions } = journey.manifest;
123
+ const faults = options.faultsApplied ?? [];
124
+ const results = [];
125
+ const skipReason = faults.length > 0
126
+ ? `not compared under fault(s): ${faults.join(", ")}`
127
+ : undefined;
128
+ // Normalize once — both goldens read from the same view.
129
+ const normalized = assertions.streamShape ? normalizeRunRecord(record) : null;
130
+ if (assertions.outputs) {
131
+ results.push(skipReason
132
+ ? { kind: "outputs", checked: false, skipReason, failures: [] }
133
+ : {
134
+ kind: "outputs",
135
+ checked: true,
136
+ failures: compareOutputs(journey.expected.outputs, record)
137
+ });
138
+ }
139
+ if (assertions.streamShape) {
140
+ results.push(skipReason
141
+ ? { kind: "streamShape", checked: false, skipReason, failures: [] }
142
+ : {
143
+ kind: "streamShape",
144
+ checked: true,
145
+ failures: compareStreamShape(journey.expected.streamShape, record, normalized)
146
+ });
147
+ }
148
+ return results;
149
+ }
150
+ //# sourceMappingURL=golden.js.map