@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,123 @@
1
+ /**
2
+ * Headless-app driver (§12) — wraps `nodetool app debug` (packages/cli's
3
+ * app-debug runtime, `packages/cli/src/app-debug/`) for journeys whose
4
+ * workflow carries an app document. Spawns the real CLI (`spawn-cli.ts`)
5
+ * rather than importing the app-debug runtime directly: `@nodetool-ai/cli`'s
6
+ * own `reliability` command (item 6) depends on this package, so a static
7
+ * import the other way would be a circular workspace dependency — and
8
+ * spawning the entry point is what every non-oracle driver does anyway
9
+ * (§12: "drivers speak entry points, never internals").
10
+ *
11
+ * A journey "carries an app" when its workflow JSON has a top-level
12
+ * `app_doc` field — the same shape `nodetool debug`/`workflows run` read
13
+ * (`packages/cli/src/debug/target.ts`). None of the seed journeys (§5) do
14
+ * yet, so `supports()` reports `false` for all of them today and the driver
15
+ * is skipped as not-applicable — the "headless-app or n/a-clean" leg of C4's
16
+ * done-when (item 8). The conversion half (`runRecordFromAppDebugReport`) is
17
+ * still fully implemented and unit-tested against a fabricated report, so a
18
+ * future app-carrying journey exercises real, proven code — not a stub.
19
+ */
20
+ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
21
+ import { tmpdir } from "node:os";
22
+ import { join } from "node:path";
23
+ import { makeFrame } from "../core/record.js";
24
+ import { spawnNodetoolCli } from "./spawn-cli.js";
25
+ /** True when a journey's workflow carries an `app_doc` the app-headless
26
+ * driver can run — pure, no IO, so `compare.ts` and tests can check
27
+ * applicability without spawning a process. */
28
+ export function journeyHasAppDoc(journey) {
29
+ return journey.workflow["app_doc"] != null;
30
+ }
31
+ /**
32
+ * Folds a decoded `AppDebugReport` (`report.json`) plus each triggered run's
33
+ * own `server/run-N.messages.jsonl` lines (already read, in `runs[]` order)
34
+ * into one `RunRecord`. Pure — mirrors `record.ts`'s
35
+ * `runRecordFromDebugBundle` split between the pure conversion and the IO
36
+ * that reads the bundle off disk, so this half is unit-testable without a
37
+ * real app document or a spawned process.
38
+ */
39
+ export function runRecordFromAppDebugReport(report, runMessages, options = {}) {
40
+ const startedAt = options.startedAt ?? 0;
41
+ const frames = [];
42
+ let seq = 0;
43
+ for (const messages of runMessages) {
44
+ for (const message of messages) {
45
+ frames.push(makeFrame(seq, "app-headless", "server_to_client", message, startedAt + seq));
46
+ seq++;
47
+ }
48
+ }
49
+ const runs = report.runs ?? [];
50
+ const firstFailure = runs.find((r) => r.status && r.status !== "completed");
51
+ const status = runs.length === 0 ? "unknown" : firstFailure?.status ?? "completed";
52
+ const lastRun = runs[runs.length - 1];
53
+ return {
54
+ journeyId: options.journeyId,
55
+ surface: "app-headless",
56
+ jobId: null,
57
+ workflowId: report.target?.workflowId ?? null,
58
+ startedAt: frames.length > 0 ? startedAt : null,
59
+ finishedAt: frames.length > 0 ? startedAt + Math.max(frames.length - 1, 0) : null,
60
+ durationMs: lastRun?.durationMs ?? null,
61
+ status,
62
+ error: firstFailure?.error ?? runs.find((r) => r.error)?.error ?? null,
63
+ params: {},
64
+ frames
65
+ };
66
+ }
67
+ export class AppHeadlessDriver {
68
+ name = "app-headless";
69
+ supports(journey) {
70
+ return journeyHasAppDoc(journey);
71
+ }
72
+ async run(journey) {
73
+ if (!this.supports(journey)) {
74
+ throw new Error(`journey "${journey.manifest.name}": app-headless driver only runs ` +
75
+ `journeys whose workflow carries an "app_doc" — this journey has none (n/a)`);
76
+ }
77
+ const bundleDir = await mkdtemp(join(tmpdir(), "nodetool-reliability-app-"));
78
+ try {
79
+ const workflowPath = join(bundleDir, "workflow.json");
80
+ await writeFile(workflowPath, JSON.stringify(journey.workflow), "utf8");
81
+ const timeoutMs = journey.manifest.timeoutMs + 30_000;
82
+ const { code, stdout, stderr } = await spawnNodetoolCli([
83
+ "app",
84
+ "debug",
85
+ workflowPath,
86
+ "--params",
87
+ JSON.stringify(journey.manifest.params ?? {}),
88
+ "--out",
89
+ bundleDir,
90
+ "--json"
91
+ ], timeoutMs);
92
+ let report;
93
+ try {
94
+ report = JSON.parse(await readFile(join(bundleDir, "report.json"), "utf8"));
95
+ }
96
+ catch (err) {
97
+ throw new Error(`journey "${journey.manifest.name}": app-headless driver produced no ` +
98
+ `readable report (exit ${code})\n--- stdout ---\n${stdout}\n` +
99
+ `--- stderr ---\n${stderr}\noriginal error: ${String(err)}`);
100
+ }
101
+ const runMessages = [];
102
+ for (const run of report.runs ?? []) {
103
+ if (!run.messagesFile) {
104
+ runMessages.push([]);
105
+ continue;
106
+ }
107
+ const raw = await readFile(join(bundleDir, run.messagesFile), "utf8").catch(() => "");
108
+ runMessages.push(raw
109
+ .split("\n")
110
+ .map((line) => line.trim())
111
+ .filter((line) => line.length > 0)
112
+ .map((line) => JSON.parse(line)));
113
+ }
114
+ return runRecordFromAppDebugReport(report, runMessages, {
115
+ journeyId: journey.manifest.name
116
+ });
117
+ }
118
+ finally {
119
+ await rm(bundleDir, { recursive: true, force: true }).catch(() => { });
120
+ }
121
+ }
122
+ }
123
+ //# sourceMappingURL=app-headless.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-headless.js","sourceRoot":"","sources":["../../src/drivers/app-headless.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { Journey } from "../core/journey.js";
2
+ import { type RunRecord } from "../core/record.js";
3
+ import type { RunDriver } from "./types.js";
4
+ /** Minimal shape this driver needs from a decoded `browser/record.json`
5
+ * (`web/src/e2e_runner/types.ts`'s `RunRecord`). */
6
+ export interface BrowserRunRecordLike {
7
+ status?: string;
8
+ error?: string | null;
9
+ durationMs?: number | null;
10
+ jobId?: string | null;
11
+ events?: Array<Record<string, unknown>>;
12
+ }
13
+ /** Folds a decoded `browser/record.json` into a `RunRecord`. Pure — the IO
14
+ * (spawning Playwright, reading the file back) lives in {@link BrowserDriver},
15
+ * so a captured fixture round-trips through this without a browser. */
16
+ export declare function runRecordFromBrowserRecord(record: BrowserRunRecordLike, options?: {
17
+ journeyId?: string;
18
+ startedAt?: number;
19
+ }): RunRecord;
20
+ export declare class BrowserDriver implements RunDriver {
21
+ readonly name = "browser";
22
+ /** A single, unscripted `run` (see the module doc comment) AND Playwright
23
+ * actually available in this environment. Folding `isAvailable()` in here
24
+ * — rather than leaving it a separate check `run()` fails on — is what lets
25
+ * `compare.ts` report a sandbox without a matching Chromium build as "not
26
+ * applicable" instead of a false "diverges from the oracle" failure. */
27
+ supports(journey: Journey): boolean;
28
+ /** True when Playwright and its Chromium binary are actually available in
29
+ * this environment — lets callers (`compare.ts`, tests) skip cleanly
30
+ * instead of failing the whole run. */
31
+ isAvailable(): boolean;
32
+ run(journey: Journey): Promise<RunRecord>;
33
+ }
34
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/drivers/browser.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAA4B,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAM5C;oDACoD;AACpD,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACzC;AAoCD;;uEAEuE;AACvE,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,oBAAoB,EAC5B,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACvD,SAAS,CAoBX;AAED,qBAAa,aAAc,YAAW,SAAS;IAC7C,QAAQ,CAAC,IAAI,aAAa;IAE1B;;;;4EAIwE;IACxE,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAKlC;IAED;;2CAEuC;IACvC,WAAW,IAAI,OAAO,CAOrB;IAEK,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CA4E9C;CACF"}
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Browser driver (§12) — wraps the existing `e2e_runner` Playwright harness
3
+ * (`web/tests/debug-harness/debug.spec.ts`, the same spec `nodetool debug
4
+ * --browser` and `npm run test:debug-harness` drive) to run a journey's
5
+ * workflow through the real web stack: real ReactFlow canvas, real
6
+ * `GlobalWebSocketManager`, real WS protocol, hermetic backend
7
+ * (`packages/websocket/src/e2e-server.ts`, spawned by the spec's
8
+ * `globalSetup`).
9
+ *
10
+ * Spawns Playwright directly (root `node_modules/.bin/playwright`, `cwd:
11
+ * web/`) rather than importing `packages/cli/src/debug/browser-runner.ts`:
12
+ * that module lives in `@nodetool-ai/cli`, which depends on this package for
13
+ * the `reliability` command (item 6), so importing it here would be a
14
+ * circular workspace dependency — and going through the real Playwright
15
+ * binary is what "drivers speak entry points, never internals" (§12) means
16
+ * for a non-oracle surface anyway.
17
+ *
18
+ * Scripted interactions (`cancel`/`stream_input`/`end_input_stream`) are not
19
+ * yet wired through `NODETOOL_DEBUG_*` env — the spec runs one `runGraph`
20
+ * call start-to-finish, same limitation as the CLI driver. `supports()`
21
+ * reflects that.
22
+ */
23
+ import { spawn } from "node:child_process";
24
+ import { existsSync } from "node:fs";
25
+ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
26
+ import { tmpdir } from "node:os";
27
+ import { join } from "node:path";
28
+ import { makeFrame } from "../core/record.js";
29
+ import { findRepoRoot } from "./repo-root.js";
30
+ const CONFIG_REL = "playwright.debug-harness.config.ts";
31
+ const DEFAULT_TIMEOUT_MS = 6 * 60_000;
32
+ const GROUP_KILL_GRACE_MS = 5_000;
33
+ /** Locates the workspace-hoisted Playwright binary a fresh `web/`
34
+ * `node_modules/.bin` doesn't necessarily carry (root `npm install` hoists
35
+ * it), falling back to `web`'s own copy when present. */
36
+ function resolvePlaywrightBin(repoRoot, webDir) {
37
+ const local = join(webDir, "node_modules", ".bin", "playwright");
38
+ if (existsSync(local))
39
+ return local;
40
+ const hoisted = join(repoRoot, "node_modules", ".bin", "playwright");
41
+ return existsSync(hoisted) ? hoisted : null;
42
+ }
43
+ function killChild(child) {
44
+ if (process.platform !== "win32" && child.pid !== undefined) {
45
+ const pgid = child.pid;
46
+ try {
47
+ process.kill(-pgid, "SIGTERM");
48
+ }
49
+ catch {
50
+ // Process group may already be gone.
51
+ }
52
+ setTimeout(() => {
53
+ try {
54
+ process.kill(-pgid, "SIGKILL");
55
+ }
56
+ catch {
57
+ // Process group may already be gone.
58
+ }
59
+ }, GROUP_KILL_GRACE_MS).unref();
60
+ return;
61
+ }
62
+ try {
63
+ child.kill("SIGKILL");
64
+ }
65
+ catch {
66
+ // Already exited.
67
+ }
68
+ }
69
+ /** Folds a decoded `browser/record.json` into a `RunRecord`. Pure — the IO
70
+ * (spawning Playwright, reading the file back) lives in {@link BrowserDriver},
71
+ * so a captured fixture round-trips through this without a browser. */
72
+ export function runRecordFromBrowserRecord(record, options = {}) {
73
+ const startedAt = options.startedAt ?? 0;
74
+ const events = record.events ?? [];
75
+ const frames = events.map((message, index) => makeFrame(index, "browser", "server_to_client", message, startedAt + index));
76
+ return {
77
+ journeyId: options.journeyId,
78
+ surface: "browser",
79
+ jobId: record.jobId ?? null,
80
+ workflowId: null,
81
+ startedAt: frames.length > 0 ? startedAt : null,
82
+ finishedAt: frames.length > 0 ? startedAt + (frames.length - 1) : null,
83
+ durationMs: record.durationMs ?? null,
84
+ status: record.status ?? "unknown",
85
+ error: record.error ?? null,
86
+ params: {},
87
+ frames
88
+ };
89
+ }
90
+ export class BrowserDriver {
91
+ name = "browser";
92
+ /** A single, unscripted `run` (see the module doc comment) AND Playwright
93
+ * actually available in this environment. Folding `isAvailable()` in here
94
+ * — rather than leaving it a separate check `run()` fails on — is what lets
95
+ * `compare.ts` report a sandbox without a matching Chromium build as "not
96
+ * applicable" instead of a false "diverges from the oracle" failure. */
97
+ supports(journey) {
98
+ const interactions = journey.interactions.length > 0 ? journey.interactions : [{ action: "run" }];
99
+ const shapeOk = interactions.length === 1 && interactions[0].action === "run";
100
+ return shapeOk && this.isAvailable();
101
+ }
102
+ /** True when Playwright and its Chromium binary are actually available in
103
+ * this environment — lets callers (`compare.ts`, tests) skip cleanly
104
+ * instead of failing the whole run. */
105
+ isAvailable() {
106
+ const repoRoot = findRepoRoot();
107
+ const webDir = join(repoRoot, "web");
108
+ return (existsSync(join(webDir, CONFIG_REL)) &&
109
+ resolvePlaywrightBin(repoRoot, webDir) !== null);
110
+ }
111
+ async run(journey) {
112
+ if (!this.supports(journey)) {
113
+ throw new Error(`journey "${journey.manifest.name}": browser driver only supports a ` +
114
+ `single, unscripted "run" interaction`);
115
+ }
116
+ const repoRoot = findRepoRoot();
117
+ const webDir = join(repoRoot, "web");
118
+ const playwrightBin = resolvePlaywrightBin(repoRoot, webDir);
119
+ if (!playwrightBin || !existsSync(join(webDir, CONFIG_REL))) {
120
+ throw new Error(`journey "${journey.manifest.name}": browser driver unavailable — ` +
121
+ `Playwright is not installed (root and web node_modules/.bin both ` +
122
+ `missing "playwright") or ${CONFIG_REL} is missing from web/`);
123
+ }
124
+ const outDir = await mkdtemp(join(tmpdir(), "nodetool-reliability-browser-"));
125
+ try {
126
+ const graphPath = join(outDir, "_graph.json");
127
+ await writeFile(graphPath, JSON.stringify({ graph: journey.workflow }), "utf8");
128
+ const timeoutMs = journey.manifest.timeoutMs;
129
+ const env = {
130
+ ...process.env,
131
+ NODETOOL_DEBUG_GRAPH: graphPath,
132
+ NODETOOL_DEBUG_OUT: outDir,
133
+ NODETOOL_DEBUG_PARAMS: JSON.stringify(journey.manifest.params ?? {}),
134
+ NODETOOL_DEBUG_TIMEOUT: String(timeoutMs)
135
+ };
136
+ const killTimeoutMs = Math.max(timeoutMs + 60_000, DEFAULT_TIMEOUT_MS);
137
+ const isWindows = process.platform === "win32";
138
+ let output = "";
139
+ await new Promise((resolvePromise) => {
140
+ const child = spawn(playwrightBin, ["test", "-c", CONFIG_REL], {
141
+ cwd: webDir,
142
+ env,
143
+ stdio: ["ignore", "pipe", "pipe"],
144
+ ...(isWindows ? { shell: true } : { detached: true })
145
+ });
146
+ const onChunk = (buf) => (output += buf.toString());
147
+ child.stdout?.on("data", onChunk);
148
+ child.stderr?.on("data", onChunk);
149
+ const timer = setTimeout(() => {
150
+ killChild(child);
151
+ resolvePromise();
152
+ }, killTimeoutMs);
153
+ child.on("error", () => {
154
+ clearTimeout(timer);
155
+ resolvePromise();
156
+ });
157
+ child.on("exit", () => {
158
+ clearTimeout(timer);
159
+ resolvePromise();
160
+ });
161
+ });
162
+ const recordPath = join(outDir, "record.json");
163
+ if (!existsSync(recordPath)) {
164
+ throw new Error(`journey "${journey.manifest.name}": browser driver produced no ` +
165
+ `record.json — check that the web app builds and Chromium is ` +
166
+ `installed\n--- playwright output ---\n${output.trimEnd()}`);
167
+ }
168
+ const record = JSON.parse(await readFile(recordPath, "utf8"));
169
+ return runRecordFromBrowserRecord(record, { journeyId: journey.manifest.name });
170
+ }
171
+ finally {
172
+ await rm(outDir, { recursive: true, force: true }).catch(() => { });
173
+ }
174
+ }
175
+ }
176
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/drivers/browser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Journey } from "../core/journey.js";
2
+ import { type RunRecord } from "../core/record.js";
3
+ import type { RunDriver } from "./types.js";
4
+ /** True when a journey is a single, unscripted `run` — the only shape
5
+ * `nodetool debug` (and so this driver) can execute. Exported for `compare.ts`
6
+ * and tests to check without constructing a driver. */
7
+ export declare function cliDriverSupports(journey: Journey): boolean;
8
+ export declare class CliDriver implements RunDriver {
9
+ readonly name = "cli";
10
+ supports(journey: Journey): boolean;
11
+ run(journey: Journey): Promise<RunRecord>;
12
+ }
13
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/drivers/cli.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,OAAO,EAAsB,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAM5C;;uDAEuD;AACvD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAG3D;AAED,qBAAa,SAAU,YAAW,SAAS;IACzC,QAAQ,CAAC,IAAI,SAAS;IAEtB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAElC;IAEK,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAsD9C;CACF"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * CLI driver (§12) — spawns the real `nodetool debug` command (via `tsx`,
3
+ * `spawn-cli.ts`) against the journey's workflow, then converts the debug
4
+ * bundle it writes (`report.json` + `server/messages.jsonl`) into a
5
+ * `RunRecord` with `loadRunRecordFromDebugBundle` (`core/record.ts`, grown by
6
+ * C1 for exactly this "debug bundle round-trips into a RunRecord" purpose).
7
+ *
8
+ * `nodetool debug` has no scripted-interaction surface of its own — it runs a
9
+ * workflow once, start to finish, with a fixed params bag. `supports()`
10
+ * reflects that: only journeys whose interactions are a single `"run"` (or
11
+ * the implicit one every driver assumes when a journey declares none) can
12
+ * run on this surface; `cancel`/`stream_input`/`reconnect` journeys report
13
+ * "not applicable" instead of erroring.
14
+ */
15
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
16
+ import { tmpdir } from "node:os";
17
+ import { join } from "node:path";
18
+ import { loadRunRecordFromDebugBundle } from "../core/record.js";
19
+ import { spawnNodetoolCli } from "./spawn-cli.js";
20
+ function effectiveInteractions(journey) {
21
+ return journey.interactions.length > 0 ? journey.interactions : [{ action: "run" }];
22
+ }
23
+ /** True when a journey is a single, unscripted `run` — the only shape
24
+ * `nodetool debug` (and so this driver) can execute. Exported for `compare.ts`
25
+ * and tests to check without constructing a driver. */
26
+ export function cliDriverSupports(journey) {
27
+ const interactions = effectiveInteractions(journey);
28
+ return interactions.length === 1 && interactions[0].action === "run";
29
+ }
30
+ export class CliDriver {
31
+ name = "cli";
32
+ supports(journey) {
33
+ return cliDriverSupports(journey);
34
+ }
35
+ async run(journey) {
36
+ if (!this.supports(journey)) {
37
+ throw new Error(`journey "${journey.manifest.name}": cli driver only supports a single, ` +
38
+ `unscripted "run" interaction — "nodetool debug" has no cancel/` +
39
+ `stream_input/reconnect surface`);
40
+ }
41
+ const bundleDir = await mkdtemp(join(tmpdir(), "nodetool-reliability-cli-"));
42
+ try {
43
+ const workflowPath = join(bundleDir, "workflow.json");
44
+ await writeFile(workflowPath, JSON.stringify(journey.workflow), "utf8");
45
+ const timeoutMs = journey.manifest.timeoutMs + 30_000;
46
+ const { code, stdout, stderr } = await spawnNodetoolCli([
47
+ "debug",
48
+ workflowPath,
49
+ "--params",
50
+ JSON.stringify(journey.manifest.params ?? {}),
51
+ "--out",
52
+ bundleDir,
53
+ "--json"
54
+ ], timeoutMs);
55
+ // `nodetool debug` exits non-zero on a failed/cancelled *journey*
56
+ // outcome too (e.g. error-in-one-branch legitimately expects "failed")
57
+ // — that isn't a driver failure. Only a bundle the harness can't read
58
+ // back is fatal here.
59
+ let record;
60
+ try {
61
+ record = await loadRunRecordFromDebugBundle(bundleDir, {
62
+ journeyId: journey.manifest.name
63
+ });
64
+ }
65
+ catch (err) {
66
+ throw new Error(`journey "${journey.manifest.name}": cli driver produced no readable ` +
67
+ `debug bundle (exit ${code})\n--- stdout ---\n${stdout}\n` +
68
+ `--- stderr ---\n${stderr}\noriginal error: ${String(err)}`);
69
+ }
70
+ return {
71
+ ...record,
72
+ surface: this.name,
73
+ params: journey.manifest.params,
74
+ frames: record.frames.map((frame) => ({ ...frame, surface: this.name }))
75
+ };
76
+ }
77
+ finally {
78
+ await rm(bundleDir, { recursive: true, force: true }).catch(() => { });
79
+ }
80
+ }
81
+ }
82
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/drivers/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Hermetic fixture nodes for journeys the real node registry has no node
3
+ * for: a controllable long-running actor (mid-run cancellation while a node
4
+ * is `running`) and an externally-fed streaming input (mid-run cancellation
5
+ * while a node is mid-stream). Every other C2 journey uses real registered
6
+ * `nodetool.*` node classes from `@nodetool-ai/base-nodes` (§5: "prefer core
7
+ * nodes... so the kernel driver can run them too") — these three fill the
8
+ * one gap: no shipped node exposes a controllable busy-loop or a
9
+ * `pushInput`-fed external stream.
10
+ *
11
+ * Registered identically into both drivers' registries (`registry.ts` for
12
+ * the kernel driver, `configureRegistry` for the ws-server driver) so their
13
+ * behavior is byte-identical on both surfaces — modeled on the equivalent
14
+ * fixtures in `packages/execution/tests/fixtures.ts`.
15
+ */
16
+ import { BaseNode, type NodeRegistry, type StreamingInputs, type StreamingOutputs } from "@nodetool-ai/node-sdk";
17
+ import type { ProcessingContext } from "@nodetool-ai/runtime";
18
+ /**
19
+ * External input node whose output is structurally streaming (the trivial
20
+ * `genProcess` override flips `hasStreamingOutput()`), so the kernel keeps
21
+ * its downstream inbox open for `pushInput`/`finishInputStream` (and the
22
+ * ws-server's `stream_input`/`end_input_stream` commands) instead of
23
+ * dispatching a one-shot default. The generator itself is never invoked when
24
+ * the node has no incoming edges — the kernel dispatches it as an external
25
+ * input, not a spawned actor.
26
+ *
27
+ * The `nodetool.input.` prefix is load-bearing, not cosmetic: the kernel's
28
+ * `_isExternalInputNode`/`_dispatchInputs` (`runner.ts`) recognize external
29
+ * inputs by type-prefix, not by the `genProcess` override alone. A type
30
+ * outside that prefix (e.g. an early `test.reliability.*` draft of this
31
+ * fixture) gets spawned as an ordinary zero-input actor instead — it runs
32
+ * `genProcess` once to completion immediately, which defeats the whole
33
+ * point of a `pushInput`-fed external stream.
34
+ */
35
+ export declare class ReliabilityStreamingInputNode extends BaseNode {
36
+ static readonly nodeType = "nodetool.input.ReliabilityStreamingInput";
37
+ static readonly title = "Reliability Streaming Input";
38
+ static readonly description = "Test fixture: external input fed by pushInput/stream_input.";
39
+ value: unknown;
40
+ process(): Promise<Record<string, unknown>>;
41
+ genProcess(): AsyncGenerator<Record<string, unknown>>;
42
+ }
43
+ /** Collects every value streamed on its `value` input into one terminal array. */
44
+ export declare class ReliabilityStreamSinkNode extends BaseNode {
45
+ static readonly nodeType = "test.reliability.StreamSink";
46
+ static readonly title = "Reliability Stream Sink";
47
+ static readonly description = "Test fixture: collects streamed values.";
48
+ static readonly isStreamingInput = true;
49
+ /** Never invoked directly — the kernel dispatches streaming-input nodes
50
+ * through `run()`. Required only to satisfy `BaseNode`'s abstract member. */
51
+ process(): Promise<Record<string, unknown>>;
52
+ run(inputs: StreamingInputs, outputs: StreamingOutputs): Promise<void>;
53
+ }
54
+ /**
55
+ * A busy node with no incoming edges: parked on the run's own
56
+ * `AbortSignal` — busy from the graph's perspective, distinct from journey
57
+ * 6b's `nodetool.input.*`-fed streaming node — until the run is cancelled.
58
+ *
59
+ * Two earlier designs both raced:
60
+ * - Polling `inputs.signal.aborted` in a `setTimeout`-paced loop raced the
61
+ * runner's own cancel bookkeeping — whether this actor's `run()`
62
+ * returned before or after `WorkflowRunner` finished processing
63
+ * `cancel()` depended on `setTimeout` scheduling granularity, so the
64
+ * exact frame sequence differed from run to run *on the same driver*,
65
+ * not just across drivers (§6: "two runs ... produce identical
66
+ * normalized streams").
67
+ * - Blocking on `inputs.stream()` for a handle no edge ever targets
68
+ * doesn't block at all: the inbox has zero registered writers for it
69
+ * from the start, so `stream()` resolves as an immediate EOS and the
70
+ * actor "completes" before any cancel command can possibly land.
71
+ *
72
+ * Listening for the signal's `abort` event is neither: it makes no progress
73
+ * of its own to race, and resolves only once, however long that takes.
74
+ */
75
+ export declare class ReliabilityCancelProbeNode extends BaseNode {
76
+ static readonly nodeType = "test.reliability.CancelProbe";
77
+ static readonly title = "Reliability Cancel Probe";
78
+ static readonly description = "Test fixture: blocks until cancelled.";
79
+ static readonly isStreamingInput = true;
80
+ /** Never invoked directly — see {@link ReliabilityStreamSinkNode.process}. */
81
+ process(): Promise<Record<string, unknown>>;
82
+ run(inputs: StreamingInputs): Promise<void>;
83
+ }
84
+ /**
85
+ * Writes bytes through `context.storage` — the one write op every real
86
+ * asset-producing node eventually calls. Exists purely as a deterministic,
87
+ * hermetic target for task D3's `host-disk-full` fault: no shipped node
88
+ * writes an asset without also touching media-ref plumbing this fixture
89
+ * doesn't need to exercise. A missing `context.storage` (the normal
90
+ * fault-free hermetic default — `ExecutionSession`'s own minimal context
91
+ * wires none) is a configuration error for a journey using this node, not a
92
+ * silent no-op, so it throws rather than skipping the write.
93
+ */
94
+ export declare class ReliabilityStorageWriteNode extends BaseNode {
95
+ static readonly nodeType = "test.reliability.StorageWrite";
96
+ static readonly title = "Reliability Storage Write";
97
+ static readonly description = "Test fixture: writes bytes through context.storage (task D3's host-disk-full fault).";
98
+ key: string;
99
+ process(context?: ProcessingContext): Promise<Record<string, unknown>>;
100
+ }
101
+ /**
102
+ * Joins a `list[str]` property — the shared-list-handle multi-edge fan-in
103
+ * target for the `fan-in-shared-list-handle` journey (A5,
104
+ * docs/RELIABILITY_TASKS.md Track A). Unlike `fan-out-fan-in-dag`'s `join`
105
+ * node (two edges into two *distinct* handles, a workaround for the
106
+ * hydration gap where a hydration path without `propertyTypes` couldn't
107
+ * distinguish a genuine list fan-in from an invalid one), this node takes
108
+ * two edges into the SAME `items` handle — the case that gap used to reject
109
+ * outright on any surface hydrating without a metadata resolver.
110
+ */
111
+ export declare class ReliabilityListJoinNode extends BaseNode {
112
+ static readonly nodeType = "test.reliability.ListJoin";
113
+ static readonly title = "Reliability List Join";
114
+ static readonly description = "Test fixture: joins a list[str] input fed by a shared multi-edge handle.";
115
+ items: unknown;
116
+ process(): Promise<Record<string, unknown>>;
117
+ }
118
+ export declare const RELIABILITY_FIXTURE_NODES: readonly [typeof ReliabilityStreamingInputNode, typeof ReliabilityStreamSinkNode, typeof ReliabilityCancelProbeNode, typeof ReliabilityStorageWriteNode, typeof ReliabilityListJoinNode];
119
+ /** Registers the harness's own fixture nodes into a live registry. Idempotent
120
+ * (last-write-wins, matching `NodeRegistry.register`), so it's safe to call
121
+ * on both a fresh kernel-driver registry and a shared ws-server registry via
122
+ * `configureRegistry`. */
123
+ export declare function registerFixtureNodes(registry: NodeRegistry): void;
124
+ //# sourceMappingURL=fixture-nodes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixture-nodes.d.ts","sourceRoot":"","sources":["../../src/drivers/fixture-nodes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EACL,QAAQ,EAER,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,6BAA8B,SAAQ,QAAQ;IACzD,MAAM,CAAC,QAAQ,CAAC,QAAQ,8CAA8C;IACtE,MAAM,CAAC,QAAQ,CAAC,KAAK,iCAAiC;IACtD,MAAM,CAAC,QAAQ,CAAC,WAAW,iEACqC;IAGxD,KAAK,EAAE,OAAO,CAAC;IAEjB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAEhD;IAEM,UAAU,IAAI,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAE3D;CACF;AAED,kFAAkF;AAClF,qBAAa,yBAA0B,SAAQ,QAAQ;IACrD,MAAM,CAAC,QAAQ,CAAC,QAAQ,iCAAiC;IACzD,MAAM,CAAC,QAAQ,CAAC,KAAK,6BAA6B;IAClD,MAAM,CAAC,QAAQ,CAAC,WAAW,6CAA6C;IACxE,MAAM,CAAC,QAAQ,CAAC,gBAAgB,QAAQ;IAExC;iFAC6E;IACvE,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAEhD;IAEK,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3E;CACF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,0BAA2B,SAAQ,QAAQ;IACtD,MAAM,CAAC,QAAQ,CAAC,QAAQ,kCAAkC;IAC1D,MAAM,CAAC,QAAQ,CAAC,KAAK,8BAA8B;IACnD,MAAM,CAAC,QAAQ,CAAC,WAAW,2CAA2C;IACtE,MAAM,CAAC,QAAQ,CAAC,gBAAgB,QAAQ;IAExC,8EAA8E;IACxE,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAEhD;IAEK,GAAG,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhD;CACF;AAED;;;;;;;;;GASG;AACH,qBAAa,2BAA4B,SAAQ,QAAQ;IACvD,MAAM,CAAC,QAAQ,CAAC,QAAQ,mCAAmC;IAC3D,MAAM,CAAC,QAAQ,CAAC,KAAK,+BAA+B;IACpD,MAAM,CAAC,QAAQ,CAAC,WAAW,0FAC8D;IAGjF,GAAG,EAAE,MAAM,CAAC;IAEd,OAAO,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAY3E;CACF;AAED;;;;;;;;;GASG;AACH,qBAAa,uBAAwB,SAAQ,QAAQ;IACnD,MAAM,CAAC,QAAQ,CAAC,QAAQ,+BAA+B;IACvD,MAAM,CAAC,QAAQ,CAAC,KAAK,2BAA2B;IAChD,MAAM,CAAC,QAAQ,CAAC,WAAW,8EACkD;IAGrE,KAAK,EAAE,OAAO,CAAC;IAEjB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAGhD;CACF;AAED,eAAO,MAAM,yBAAyB,0LAM5B,CAAC;AAEX;;;0BAG0B;AAC1B,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAIjE"}