@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,51 @@
1
+ /**
2
+ * Shared status vocabulary for the lifecycle/terminal-uniqueness invariants.
3
+ * Not itself a §6 family — just the constants both modules need to stay in
4
+ * sync on what counts as "terminal" for a node vs. a job.
5
+ */
6
+ /**
7
+ * `node_update` statuses that end an invocation. The kernel actor
8
+ * (`packages/kernel/src/actor.ts`) emits `"completed"`, `"error"`, and
9
+ * `"suspended"` — no node-level `"cancelled"`, since a cancelled run
10
+ * interrupts actors rather than walking each one to a terminal status; a
11
+ * `"cancelled"` node status is accepted too, for surfaces/fakes that do emit
12
+ * one. `"warning"` and `"running"` are deliberately excluded: `"warning"` is
13
+ * a mid-run aside, not a terminal status.
14
+ */
15
+ export const NODE_TERMINAL_STATUSES = new Set([
16
+ "completed",
17
+ "error",
18
+ "failed",
19
+ "cancelled",
20
+ "suspended"
21
+ ]);
22
+ /** `job_update` statuses that end a run. `"running"`/`"queued"` are not terminal. */
23
+ export const JOB_TERMINAL_STATUSES = new Set([
24
+ "completed",
25
+ "failed",
26
+ "cancelled",
27
+ "suspended"
28
+ ]);
29
+ /**
30
+ * Terminal precedence, most to least important (`runner.ts:625`): cancel >
31
+ * suspend > failed > completed. Index doubles as precedence rank (lower
32
+ * wins).
33
+ */
34
+ export const TERMINAL_PRECEDENCE = [
35
+ "cancelled",
36
+ "suspended",
37
+ "failed",
38
+ "completed"
39
+ ];
40
+ /** Normalizes a status to the `TERMINAL_PRECEDENCE` vocabulary (`"error"` reads as `"failed"`). */
41
+ export function canonicalTerminalStatus(status) {
42
+ return status === "error" ? "failed" : status;
43
+ }
44
+ function messageRecord(message) {
45
+ return message ?? {};
46
+ }
47
+ export function asString(message, key) {
48
+ const value = messageRecord(message)[key];
49
+ return typeof value === "string" ? value : undefined;
50
+ }
51
+ //# sourceMappingURL=status-sets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-sets.js","sourceRoot":"","sources":["../../../src/core/invariants/status-sets.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAwB,IAAI,GAAG,CAAC;IACjE,WAAW;IACX,OAAO;IACP,QAAQ;IACR,WAAW;IACX,WAAW;CACZ,CAAC,CAAC;AAEH,qFAAqF;AACrF,MAAM,CAAC,MAAM,qBAAqB,GAAwB,IAAI,GAAG,CAAC;IAChE,WAAW;IACX,QAAQ;IACR,WAAW;IACX,WAAW;CACZ,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAsB;IACpD,WAAW;IACX,WAAW;IACX,QAAQ;IACR,WAAW;CACZ,CAAC;AAEF,mGAAmG;AACnG,MAAM,UAAU,uBAAuB,CAAC,MAAc;IACpD,OAAO,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;AAChD,CAAC;AAED,SAAS,aAAa,CACpB,OAA4C;IAE5C,OAAO,OAAO,IAAI,EAAE,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,QAAQ,CACtB,OAA4C,EAC5C,GAAW;IAEX,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC;IAC1C,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Terminal-uniqueness invariants (docs/RELIABILITY_ARCHITECTURE.md §6
3
+ * "Lifecycle"):
4
+ *
5
+ * - Exactly one terminal `job_update` per run, and nothing follows it on
6
+ * that job's stream.
7
+ * - Terminal precedence is exactly cancel > suspend > failed > completed
8
+ * (`runner.ts:625`) — asserted, not assumed.
9
+ *
10
+ * Split from `lifecycle.ts` (which covers the node-level running/terminal
11
+ * pairing) so each §6 bullet stays independently attributable.
12
+ *
13
+ * Frames a driver tagged `redundant` (see `RunFrame.redundant`) are discounted
14
+ * here: they are that surface's documented client-convenience repeats (the
15
+ * ws-server's eager "running" ack and its authoritative terminal snapshot),
16
+ * recognized one-by-one by the driver that speaks the protocol. Every other
17
+ * duplicate is untagged and therefore still a violation — which is the point
18
+ * of recording repeats instead of dropping them at the driver.
19
+ */
20
+ import type { RunRecord } from "../record.js";
21
+ import type { Violation } from "./types.js";
22
+ export declare function checkTerminalUniqueness(record: RunRecord): Violation[];
23
+ //# sourceMappingURL=terminal-uniqueness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-uniqueness.d.ts","sourceRoot":"","sources":["../../../src/core/invariants/terminal-uniqueness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAY5C,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,CAyGtE"}
@@ -0,0 +1,99 @@
1
+ import { JOB_TERMINAL_STATUSES, TERMINAL_PRECEDENCE, canonicalTerminalStatus, asString } from "./status-sets.js";
2
+ function isCancelCommand(message) {
3
+ return message["command"] === "cancel_job";
4
+ }
5
+ export function checkTerminalUniqueness(record) {
6
+ const violations = [];
7
+ const terminalIndices = [];
8
+ record.frames.forEach((frame, index) => {
9
+ if (frame.redundant !== undefined)
10
+ return;
11
+ const message = frame.message;
12
+ if (asString(message, "type") !== "job_update")
13
+ return;
14
+ const status = asString(message, "status");
15
+ if (status !== undefined && JOB_TERMINAL_STATUSES.has(status)) {
16
+ terminalIndices.push(index);
17
+ }
18
+ });
19
+ if (terminalIndices.length === 0) {
20
+ violations.push({
21
+ invariant: "terminal-uniqueness.missing",
22
+ message: "no terminal job_update found in the record"
23
+ });
24
+ return violations;
25
+ }
26
+ if (terminalIndices.length > 1) {
27
+ violations.push({
28
+ invariant: "terminal-uniqueness.multiple",
29
+ message: `${terminalIndices.length} terminal job_update frames found (expected exactly 1)`,
30
+ details: { frameIndices: terminalIndices }
31
+ });
32
+ }
33
+ const firstTerminal = terminalIndices[0];
34
+ const framesAfter = record.frames
35
+ .slice(firstTerminal + 1)
36
+ .filter((frame) => frame.redundant === undefined &&
37
+ asString(frame.message, "type") === "job_update");
38
+ if (framesAfter.length > 0) {
39
+ violations.push({
40
+ invariant: "terminal-uniqueness.frames-after-terminal",
41
+ message: `${framesAfter.length} job_update frame(s) appear after the terminal job_update at frame ${firstTerminal}`,
42
+ frameIndex: firstTerminal,
43
+ details: { count: framesAfter.length }
44
+ });
45
+ }
46
+ // Terminal precedence: cancel > suspend > failed > completed.
47
+ const terminalMessage = record.frames[firstTerminal].message;
48
+ const terminalStatus = asString(terminalMessage, "status") ?? "";
49
+ const hasCancelRequest = record.frames.some((frame) => frame.direction === "client_to_server" &&
50
+ isCancelCommand(frame.message));
51
+ const hasSuspendedNode = record.frames.some((frame) => {
52
+ const message = frame.message;
53
+ return asString(message, "type") === "node_update" && asString(message, "status") === "suspended";
54
+ });
55
+ const hasNodeError = record.frames.some((frame) => {
56
+ const message = frame.message;
57
+ if (asString(message, "type") !== "node_update")
58
+ return false;
59
+ const status = asString(message, "status");
60
+ return status === "error" || status === "failed";
61
+ });
62
+ // Task D3: some failures never reach a per-node lifecycle event at all —
63
+ // e.g. an unresolvable executor (a Python bridge that failed to connect,
64
+ // `python-node-workflow`'s `bridge-never-ready`/`bridge-epipe`/
65
+ // `bridge-version-mismatch` faults) throws while *resolving* the node,
66
+ // before the actor ever emits its own `node_update running`. That's a
67
+ // legitimate "failed" terminal with real evidence — the terminal
68
+ // `job_update` itself carries a non-empty `error` string explaining why —
69
+ // just not evidence `hasNodeError` (a `node_update` scan) can see. Gated on
70
+ // `terminalStatus === "failed"` already being the actual value, so this can
71
+ // only ever confirm an already-"failed" terminal as expected — it can never
72
+ // flip what a "completed"/"cancelled"/"suspended" run expects.
73
+ const hasJobLevelError = terminalStatus === "failed" &&
74
+ typeof terminalMessage["error"] === "string" &&
75
+ terminalMessage["error"].trim().length > 0;
76
+ let expected;
77
+ if (hasCancelRequest)
78
+ expected = "cancelled";
79
+ else if (hasSuspendedNode)
80
+ expected = "suspended";
81
+ else if (hasNodeError || hasJobLevelError)
82
+ expected = "failed";
83
+ else
84
+ expected = "completed";
85
+ const actual = canonicalTerminalStatus(terminalStatus);
86
+ if (actual !== expected) {
87
+ violations.push({
88
+ invariant: "terminal-uniqueness.precedence",
89
+ message: `terminal job_update status is "${terminalStatus}" but precedence ` +
90
+ `(${TERMINAL_PRECEDENCE.join(" > ")}) expects "${expected}" given ` +
91
+ `the record's evidence (cancelRequest=${hasCancelRequest}, ` +
92
+ `suspendedNode=${hasSuspendedNode}, nodeError=${hasNodeError})`,
93
+ frameIndex: firstTerminal,
94
+ details: { actual: terminalStatus, expected, hasCancelRequest, hasSuspendedNode, hasNodeError }
95
+ });
96
+ }
97
+ return violations;
98
+ }
99
+ //# sourceMappingURL=terminal-uniqueness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-uniqueness.js","sourceRoot":"","sources":["../../../src/core/invariants/terminal-uniqueness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Shared shape for §6 invariant modules (docs/RELIABILITY_ARCHITECTURE.md §6,
3
+ * §12). Each module in this directory is a pure `(record: RunRecord) =>
4
+ * Violation[]` — no IO, no driver dependency, so the same check runs
5
+ * identically in CI, in `nodetool reliability`, and over a `RunRecord`
6
+ * recovered from a user's field debug bundle.
7
+ */
8
+ import type { RunRecord } from "../record.js";
9
+ export interface Violation {
10
+ /** Stable id for the specific rule that failed, e.g.
11
+ * "lifecycle.unmatched-running" or "protocol-validity.schema-mismatch" —
12
+ * `<module>.<rule>` so a report can group by module. */
13
+ invariant: string;
14
+ /** Human-readable explanation, safe to print directly in a report. */
15
+ message: string;
16
+ /** Index into `record.frames`, when the violation is anchored to one frame. */
17
+ frameIndex?: number;
18
+ /** Node id the violation concerns, when applicable. */
19
+ nodeId?: string;
20
+ /** Free-form extra context (offending values, counts, schema issues, …). */
21
+ details?: Record<string, unknown>;
22
+ }
23
+ /** The shape every invariant module exports. */
24
+ export type InvariantCheck = (record: RunRecord) => Violation[];
25
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/invariants/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,WAAW,SAAS;IACxB;;4DAEwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,gDAAgD;AAChD,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,SAAS,KAAK,SAAS,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/invariants/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,121 @@
1
+ import { z } from "zod";
2
+ /** Execution surfaces a journey may run on (§12 drivers). */
3
+ export declare const journeySurfaceSchema: z.ZodEnum<{
4
+ "app-headless": "app-headless";
5
+ browser: "browser";
6
+ cli: "cli";
7
+ electron: "electron";
8
+ kernel: "kernel";
9
+ packaged: "packaged";
10
+ "ws-server": "ws-server";
11
+ }>;
12
+ export type JourneySurface = z.infer<typeof journeySurfaceSchema>;
13
+ /**
14
+ * A scripted interaction. `at` names the event to anchor on (e.g.
15
+ * `"chunk:2"`, `"node_update:upper1:completed"`) — never a wall-clock delay,
16
+ * per §4's "event-anchored, not time-anchored" rule.
17
+ */
18
+ export declare const journeyInteractionSchema: z.ZodObject<{
19
+ action: z.ZodEnum<{
20
+ cancel: "cancel";
21
+ end_input_stream: "end_input_stream";
22
+ reconnect: "reconnect";
23
+ run: "run";
24
+ stream_input: "stream_input";
25
+ }>;
26
+ at: z.ZodOptional<z.ZodString>;
27
+ nodeId: z.ZodOptional<z.ZodString>;
28
+ value: z.ZodOptional<z.ZodUnknown>;
29
+ }, z.core.$strip>;
30
+ export type JourneyInteraction = z.infer<typeof journeyInteractionSchema>;
31
+ /** One entry in the fault matrix (§9). */
32
+ export declare const journeyFaultSchema: z.ZodObject<{
33
+ surface: z.ZodOptional<z.ZodEnum<{
34
+ "app-headless": "app-headless";
35
+ browser: "browser";
36
+ cli: "cli";
37
+ electron: "electron";
38
+ kernel: "kernel";
39
+ packaged: "packaged";
40
+ "ws-server": "ws-server";
41
+ }>>;
42
+ type: z.ZodString;
43
+ params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
44
+ }, z.core.$strip>;
45
+ export type JourneyFault = z.infer<typeof journeyFaultSchema>;
46
+ /**
47
+ * What a journey asserts beyond "the run reached a terminal status". At
48
+ * least one of these must be non-trivial (§4's last rule).
49
+ */
50
+ export declare const journeyAssertionsSchema: z.ZodObject<{
51
+ invariants: z.ZodDefault<z.ZodArray<z.ZodString>>;
52
+ outputs: z.ZodDefault<z.ZodBoolean>;
53
+ streamShape: z.ZodDefault<z.ZodBoolean>;
54
+ }, z.core.$strip>;
55
+ export type JourneyAssertions = z.infer<typeof journeyAssertionsSchema>;
56
+ export declare const journeyManifestSchema: z.ZodObject<{
57
+ name: z.ZodString;
58
+ description: z.ZodOptional<z.ZodString>;
59
+ workflow: z.ZodObject<{
60
+ ref: z.ZodString;
61
+ }, z.core.$strip>;
62
+ params: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
63
+ interactions: z.ZodDefault<z.ZodString>;
64
+ surfaces: z.ZodArray<z.ZodEnum<{
65
+ "app-headless": "app-headless";
66
+ browser: "browser";
67
+ cli: "cli";
68
+ electron: "electron";
69
+ kernel: "kernel";
70
+ packaged: "packaged";
71
+ "ws-server": "ws-server";
72
+ }>>;
73
+ faults: z.ZodDefault<z.ZodArray<z.ZodObject<{
74
+ surface: z.ZodOptional<z.ZodEnum<{
75
+ "app-headless": "app-headless";
76
+ browser: "browser";
77
+ cli: "cli";
78
+ electron: "electron";
79
+ kernel: "kernel";
80
+ packaged: "packaged";
81
+ "ws-server": "ws-server";
82
+ }>>;
83
+ type: z.ZodString;
84
+ params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
85
+ }, z.core.$strip>>>;
86
+ timeoutMs: z.ZodDefault<z.ZodNumber>;
87
+ assertions: z.ZodObject<{
88
+ invariants: z.ZodDefault<z.ZodArray<z.ZodString>>;
89
+ outputs: z.ZodDefault<z.ZodBoolean>;
90
+ streamShape: z.ZodDefault<z.ZodBoolean>;
91
+ }, z.core.$strip>;
92
+ }, z.core.$strip>;
93
+ export type JourneyManifest = z.infer<typeof journeyManifestSchema>;
94
+ export declare class JourneyValidationError extends Error {
95
+ readonly issues: string[];
96
+ constructor(message: string, issues?: string[]);
97
+ }
98
+ /** A fully loaded journey: manifest, graph, interactions, and expected fixtures. */
99
+ export interface Journey {
100
+ dir: string;
101
+ manifest: JourneyManifest;
102
+ workflow: Record<string, unknown>;
103
+ interactions: JourneyInteraction[];
104
+ expected: {
105
+ streamShape: unknown | null;
106
+ outputs: unknown | null;
107
+ };
108
+ }
109
+ /** True when a manifest asserts something beyond "completed" (§4's last rule). */
110
+ export declare function assertsSomething(assertions: JourneyAssertions): boolean;
111
+ /**
112
+ * Parses and validates a raw `journey.json` payload. Split out from
113
+ * {@link loadJourney} so the "asserts nothing" rejection is unit-testable
114
+ * without touching the filesystem.
115
+ */
116
+ export declare function parseJourneyManifest(raw: unknown): JourneyManifest;
117
+ /** Loads and validates a journey directory (§4 layout). */
118
+ export declare function loadJourney(journeyDir: string): Promise<Journey>;
119
+ /** Directory a journey ref lives in, given its manifest path — small helper for drivers. */
120
+ export declare function journeyDirOf(manifestPath: string): string;
121
+ //# sourceMappingURL=journey.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journey.d.ts","sourceRoot":"","sources":["../../src/core/journey.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,6DAA6D;AAC7D,eAAO,MAAM,oBAAoB;;;;;;;;EAQ/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;iBAWnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,0CAA0C;AAC1C,eAAO,MAAM,kBAAkB;;;;;;;;;;;;iBAK7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,uBAAuB;;;;iBAOlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAchC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,qBAAa,sBAAuB,SAAQ,KAAK;IAG7C,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE;IAF3B,YACE,OAAO,EAAE,MAAM,EACN,MAAM,GAAE,MAAM,EAAO,EAI/B;CACF;AAED,oFAAoF;AACpF,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,YAAY,EAAE,kBAAkB,EAAE,CAAC;IACnC,QAAQ,EAAE;QACR,WAAW,EAAE,OAAO,GAAG,IAAI,CAAC;QAC5B,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;KACzB,CAAC;CACH;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,iBAAiB,GAAG,OAAO,CAMvE;AAWD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,CAgBlE;AAED,2DAA2D;AAC3D,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA8DtE;AAED,4FAA4F;AAC5F,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEzD"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Journey manifest loader (docs/RELIABILITY_ARCHITECTURE.md §4).
3
+ *
4
+ * A journey is a directory under `reliability/journeys/<name>/` carrying
5
+ * `journey.json` (the manifest), `workflow.json` (or a ref to a shipped
6
+ * example), `interactions.json` (scripted actions), and `expected/` (the
7
+ * normalized stream shape + terminal outputs a passing run must match).
8
+ *
9
+ * The loader is Zod-validated and refuses a journey that asserts nothing
10
+ * beyond "completed" — every journey must declare at least one invariant
11
+ * family, or opt into an outputs/stream-shape comparison (§4's last rule).
12
+ */
13
+ import { readFile } from "node:fs/promises";
14
+ import { dirname, join, resolve } from "node:path";
15
+ import { z } from "zod";
16
+ /** Execution surfaces a journey may run on (§12 drivers). */
17
+ export const journeySurfaceSchema = z.enum([
18
+ "kernel",
19
+ "ws-server",
20
+ "cli",
21
+ "browser",
22
+ "app-headless",
23
+ "packaged",
24
+ "electron"
25
+ ]);
26
+ /**
27
+ * A scripted interaction. `at` names the event to anchor on (e.g.
28
+ * `"chunk:2"`, `"node_update:upper1:completed"`) — never a wall-clock delay,
29
+ * per §4's "event-anchored, not time-anchored" rule.
30
+ */
31
+ export const journeyInteractionSchema = z.object({
32
+ action: z.enum([
33
+ "run",
34
+ "cancel",
35
+ "stream_input",
36
+ "end_input_stream",
37
+ "reconnect"
38
+ ]),
39
+ at: z.string().optional(),
40
+ nodeId: z.string().optional(),
41
+ value: z.unknown().optional()
42
+ });
43
+ /** One entry in the fault matrix (§9). */
44
+ export const journeyFaultSchema = z.object({
45
+ /** Restrict the fault to one surface; omitted = every surface that runs it. */
46
+ surface: journeySurfaceSchema.optional(),
47
+ type: z.string().min(1),
48
+ params: z.record(z.string(), z.unknown()).optional()
49
+ });
50
+ /**
51
+ * What a journey asserts beyond "the run reached a terminal status". At
52
+ * least one of these must be non-trivial (§4's last rule).
53
+ */
54
+ export const journeyAssertionsSchema = z.object({
55
+ /** §6 invariant family names (e.g. "lifecycle-pairing", "terminal-uniqueness"). */
56
+ invariants: z.array(z.string()).default([]),
57
+ /** Compare terminal outputs against `expected/outputs.json`. */
58
+ outputs: z.boolean().default(false),
59
+ /** Compare the normalized message stream against `expected/stream.shape.json`. */
60
+ streamShape: z.boolean().default(false)
61
+ });
62
+ export const journeyManifestSchema = z.object({
63
+ name: z.string().min(1),
64
+ description: z.string().optional(),
65
+ workflow: z.object({
66
+ /** Path to a workflow JSON file (relative to the journey dir), or a shipped-example ref. */
67
+ ref: z.string().min(1)
68
+ }),
69
+ params: z.record(z.string(), z.unknown()).default({}),
70
+ /** Filename (relative to the journey dir) carrying scripted interactions. */
71
+ interactions: z.string().default("interactions.json"),
72
+ surfaces: z.array(journeySurfaceSchema).min(1),
73
+ faults: z.array(journeyFaultSchema).default([]),
74
+ timeoutMs: z.number().positive().default(30000),
75
+ assertions: journeyAssertionsSchema
76
+ });
77
+ export class JourneyValidationError extends Error {
78
+ issues;
79
+ constructor(message, issues = []) {
80
+ super(message);
81
+ this.issues = issues;
82
+ this.name = "JourneyValidationError";
83
+ }
84
+ }
85
+ /** True when a manifest asserts something beyond "completed" (§4's last rule). */
86
+ export function assertsSomething(assertions) {
87
+ return (assertions.invariants.length > 0 ||
88
+ assertions.outputs ||
89
+ assertions.streamShape);
90
+ }
91
+ async function readJsonIfExists(path) {
92
+ try {
93
+ return JSON.parse(await readFile(path, "utf8"));
94
+ }
95
+ catch (err) {
96
+ if (err.code === "ENOENT")
97
+ return null;
98
+ throw err;
99
+ }
100
+ }
101
+ /**
102
+ * Parses and validates a raw `journey.json` payload. Split out from
103
+ * {@link loadJourney} so the "asserts nothing" rejection is unit-testable
104
+ * without touching the filesystem.
105
+ */
106
+ export function parseJourneyManifest(raw) {
107
+ const result = journeyManifestSchema.safeParse(raw);
108
+ if (!result.success) {
109
+ throw new JourneyValidationError("journey.json failed schema validation", result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`));
110
+ }
111
+ if (!assertsSomething(result.data.assertions)) {
112
+ throw new JourneyValidationError(`journey "${result.data.name}" asserts nothing beyond "completed" — ` +
113
+ "declare at least one invariant family (assertions.invariants), or " +
114
+ "opt into assertions.outputs / assertions.streamShape (§4).");
115
+ }
116
+ return result.data;
117
+ }
118
+ /** Loads and validates a journey directory (§4 layout). */
119
+ export async function loadJourney(journeyDir) {
120
+ const dir = resolve(journeyDir);
121
+ const manifestPath = join(dir, "journey.json");
122
+ let raw;
123
+ try {
124
+ raw = JSON.parse(await readFile(manifestPath, "utf8"));
125
+ }
126
+ catch (err) {
127
+ if (err.code === "ENOENT") {
128
+ throw new JourneyValidationError(`no journey.json in ${dir}`);
129
+ }
130
+ throw err;
131
+ }
132
+ const manifest = parseJourneyManifest(raw);
133
+ const workflowPath = resolve(dir, manifest.workflow.ref);
134
+ const workflow = JSON.parse(await readFile(workflowPath, "utf8"));
135
+ const interactionsPath = join(dir, manifest.interactions);
136
+ const interactionsRaw = await readJsonIfExists(interactionsPath);
137
+ const interactionsResult = z
138
+ .array(journeyInteractionSchema)
139
+ .safeParse(interactionsRaw ?? []);
140
+ if (!interactionsResult.success) {
141
+ throw new JourneyValidationError(`${interactionsPath} failed schema validation`, interactionsResult.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`));
142
+ }
143
+ const expectedDir = join(dir, "expected");
144
+ const streamShape = await readJsonIfExists(join(expectedDir, "stream.shape.json"));
145
+ const outputs = await readJsonIfExists(join(expectedDir, "outputs.json"));
146
+ if (manifest.assertions.streamShape && streamShape === null) {
147
+ throw new JourneyValidationError(`journey "${manifest.name}" declares assertions.streamShape but has no ` +
148
+ "expected/stream.shape.json");
149
+ }
150
+ if (manifest.assertions.outputs && outputs === null) {
151
+ throw new JourneyValidationError(`journey "${manifest.name}" declares assertions.outputs but has no ` +
152
+ "expected/outputs.json");
153
+ }
154
+ return {
155
+ dir,
156
+ manifest,
157
+ workflow,
158
+ interactions: interactionsResult.data,
159
+ expected: { streamShape, outputs }
160
+ };
161
+ }
162
+ /** Directory a journey ref lives in, given its manifest path — small helper for drivers. */
163
+ export function journeyDirOf(manifestPath) {
164
+ return dirname(manifestPath);
165
+ }
166
+ //# sourceMappingURL=journey.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journey.js","sourceRoot":"","sources":["../../src/core/journey.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Stream normalization (§4, §12): strips/canonicalizes timestamps, remaps
3
+ * ids (job/node/edge/asset ids) to stable placeholders, and canonicalizes
4
+ * durations and asset URLs, so two runs of the same journey on different
5
+ * surfaces — with different job ids, different wall-clock timing, different
6
+ * asset storage paths — normalize to the same shape and diff clean.
7
+ *
8
+ * Declarative: the field lists below are the whole policy. A driver that
9
+ * needs an extra field normalized adds it here, once, for every consumer
10
+ * (diff, invariants, `nodetool reliability`).
11
+ */
12
+ import type { RunFrame, RunRecord } from "./record.js";
13
+ /** Field names (anywhere in a message, at any depth) treated as ids that get
14
+ * remapped to a stable `<class:N>` placeholder, keyed by their remap class. */
15
+ export declare const ID_FIELD_CLASSES: Readonly<Record<string, string>>;
16
+ /** Field names replaced wholesale with `<ts>` — the value never matters, only presence. */
17
+ export declare const TIMESTAMP_FIELDS: readonly string[];
18
+ /** Field names replaced wholesale with `<duration>`. */
19
+ export declare const DURATION_FIELDS: readonly string[];
20
+ /**
21
+ * Field values treated as interchangeable "nothing here" signals (added by
22
+ * C2): a node with zero declared `@prop` fields (the harness's own fixture
23
+ * nodes in `drivers/fixture-nodes.ts` — no shipped node has none) serializes
24
+ * its `properties` as `null` on the raw kernel stream but as `{}` once
25
+ * relayed through the ws-server. Canonicalizing `null` to `{}` for these
26
+ * keys make the two the same "no properties" value.
27
+ */
28
+ export declare const NULLABLE_OBJECT_FIELDS: readonly string[];
29
+ /**
30
+ * Top-level fields the WebSocket transport stamps on a frame so a dropped
31
+ * client can replay what it missed (`job-run-registry.ts`,
32
+ * `chat-turn-registry.ts`). They describe the delivery, not the run, and the
33
+ * raw kernel stream has no counterpart — dropped from the comparison view so
34
+ * a resumable surface still diffs clean against a non-resumable one.
35
+ */
36
+ export declare const TRANSPORT_ONLY_FIELDS: readonly string[];
37
+ /**
38
+ * Node type prefixes whose `node_update`/`generation_complete` frames are
39
+ * dropped from comparison (added by C2). This mirrors a real, documented,
40
+ * intentional ws-server behavior (`websocket-client-session.ts`: "Skip
41
+ * messages for constant/input nodes — they produce trivial outputs that
42
+ * don't need to be relayed to the frontend") that the raw kernel stream has
43
+ * no equivalent for — the kernel emits every node's lifecycle unconditionally.
44
+ * Normalizing both sides down to what a real frontend actually receives is
45
+ * the honest comparison; dropping the frames here (not filtering in
46
+ * `diff.ts`) keeps the diff itself surface-agnostic.
47
+ */
48
+ export declare const TRIVIAL_NODE_TYPE_PREFIXES: readonly string[];
49
+ /**
50
+ * Assigns each distinct original id value a stable, order-of-first-sight
51
+ * placeholder within one normalization pass. Two records normalized
52
+ * independently line up as long as ids first appear in the same relative
53
+ * order — which is exactly the "same journey, different surface" case this
54
+ * exists for.
55
+ */
56
+ export declare class IdMapper {
57
+ private readonly maps;
58
+ placeholderFor(cls: string, value: string): string;
59
+ }
60
+ /** Normalizes one message (a frame's payload) in place-equivalent (returns a new object). */
61
+ export declare function normalizeMessage(message: Record<string, unknown>, mapper: IdMapper): Record<string, unknown>;
62
+ export interface NormalizedRunFrame {
63
+ seq: number;
64
+ channel: string;
65
+ direction: RunFrame["direction"];
66
+ surface: string;
67
+ message: Record<string, unknown>;
68
+ }
69
+ export interface NormalizedRunRecord {
70
+ journeyId?: string;
71
+ surface: string;
72
+ status: string;
73
+ error: string | null;
74
+ jobId: string | null;
75
+ workflowId: string | null;
76
+ frames: NormalizedRunFrame[];
77
+ }
78
+ /**
79
+ * Normalizes a whole `RunRecord`: every frame's message, plus the record's
80
+ * own `jobId`/`workflowId` (through the same mapper, so they match the
81
+ * placeholders used inside the frames). `startedAt`/`finishedAt`/`durationMs`
82
+ * are dropped — wall-clock fields carry no comparison value once
83
+ * normalized, per §4.
84
+ */
85
+ export declare function normalizeRunRecord(record: RunRecord, mapper?: IdMapper): NormalizedRunRecord;
86
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/core/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEvD;+EAC+E;AAC/E,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAM7D,CAAC;AAEF,2FAA2F;AAC3F,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAK7C,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAI5C,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAAmB,CAAC;AAExE;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAGlD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAGvD,CAAC;AAqBF;;;;;;GAMG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA0C;IAE/D,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAYjD;CACF;AA+CD,6FAA6F;AAC7F,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,MAAM,EAAE,QAAQ,GACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAIzB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,kBAAkB,EAAE,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,MAAM,GAAE,QAAyB,GAChC,mBAAmB,CA+BrB"}