automategraph 1.5.0 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/UPGRADE-PROPOSAL.md +141 -0
- package/docs/diagrams/c19-resilience-scenario.md +61 -0
- package/docs/diagrams/c19-resilience-scenario.mmd +28 -0
- package/docs/diagrams/c19-resilience-scenario.svg +181 -0
- package/examples/README.md +206 -0
- package/examples/branching-edges.ts +192 -0
- package/examples/branching-graph.ts +333 -0
- package/examples/branching-router.ts +475 -0
- package/examples/json-graph.ts +231 -0
- package/examples/json-scripts.ts +152 -0
- package/examples/order-flow.functions.cjs +32 -0
- package/examples/order-flow.graph.json +27 -0
- package/examples/order-flow.scripted.graph.json +68 -0
- package/js/examples/branching-edges.js +133 -0
- package/js/examples/branching-edges.js.map +1 -0
- package/js/examples/branching-graph.js +232 -0
- package/js/examples/branching-graph.js.map +1 -0
- package/js/examples/branching-router.js +305 -0
- package/js/examples/branching-router.js.map +1 -0
- package/js/examples/json-graph.js +197 -0
- package/js/examples/json-graph.js.map +1 -0
- package/js/examples/json-scripts.js +131 -0
- package/js/examples/json-scripts.js.map +1 -0
- package/js/tests/async.test.js +592 -0
- package/js/tests/async.test.js.map +1 -0
- package/js/tests/complex.test.js +972 -0
- package/js/tests/complex.test.js.map +1 -0
- package/js/tests/conformance.test.js +365 -0
- package/js/tests/conformance.test.js.map +1 -0
- package/js/tests/edges.test.js +702 -0
- package/js/tests/edges.test.js.map +1 -0
- package/js/tests/graphjson.test.js +504 -0
- package/js/tests/graphjson.test.js.map +1 -0
- package/js/tests/harness.js +192 -0
- package/js/tests/harness.js.map +1 -0
- package/js/tests/helpers.js +136 -0
- package/js/tests/helpers.js.map +1 -0
- package/js/tests/lint.test.js +317 -0
- package/js/tests/lint.test.js.map +1 -0
- package/js/tests/registry.test.js +705 -0
- package/js/tests/registry.test.js.map +1 -0
- package/js/tests/report.js +422 -0
- package/js/tests/report.js.map +1 -0
- package/js/tests/runner.js +67 -0
- package/js/tests/runner.js.map +1 -0
- package/js/tests/scripts.test.js +552 -0
- package/js/tests/scripts.test.js.map +1 -0
- package/js/tests/unit.test.js +1239 -0
- package/js/tests/unit.test.js.map +1 -0
- package/js/tests/upgrade.test.js +618 -0
- package/js/tests/upgrade.test.js.map +1 -0
- package/package.json +2 -9
- package/report/index.html +3727 -0
- package/report/report.json +3053 -0
- package/tests/async.test.ts +668 -0
- package/tests/complex.test.ts +1123 -0
- package/tests/conformance.test.ts +442 -0
- package/tests/edges.test.ts +848 -0
- package/tests/fixtures/scripts/bad-registration.cjs +4 -0
- package/tests/fixtures/scripts/broken.cjs +2 -0
- package/tests/fixtures/scripts/cjs-default.js +4 -0
- package/tests/fixtures/scripts/collide-a.cjs +4 -0
- package/tests/fixtures/scripts/collide-b.cjs +4 -0
- package/tests/fixtures/scripts/empty.cjs +2 -0
- package/tests/fixtures/scripts/esm-functions.mjs +10 -0
- package/tests/fixtures/scripts/esm-syntax.js +3 -0
- package/tests/fixtures/scripts/global-channel.cjs +6 -0
- package/tests/fixtures/scripts/global-channel.mjs +7 -0
- package/tests/fixtures/scripts/mismatch.cjs +4 -0
- package/tests/fixtures/scripts/needs-sibling.cjs +7 -0
- package/tests/fixtures/scripts/order.cjs +14 -0
- package/tests/fixtures/scripts/sibling-helper.cjs +3 -0
- package/tests/fixtures/scripts/throwing-global.mjs +6 -0
- package/tests/graphjson.test.ts +737 -0
- package/tests/harness.ts +291 -0
- package/tests/helpers.ts +216 -0
- package/tests/lint.test.ts +381 -0
- package/tests/registry.test.ts +1134 -0
- package/tests/report.ts +452 -0
- package/tests/runner.ts +58 -0
- package/tests/scripts.test.ts +970 -0
- package/tests/unit.test.ts +1192 -0
- package/tests/upgrade.test.ts +725 -0
- package/tools/server.mjs +152 -0
- package/tsconfig.browser.json +19 -0
- package/tsconfig.json +18 -0
- package/tsconfig.publish.json +13 -0
|
@@ -0,0 +1,3053 @@
|
|
|
1
|
+
{
|
|
2
|
+
"generatedAt": "2026-10-13T14:22:25.409Z",
|
|
3
|
+
"nodeVersion": "v20.20.2",
|
|
4
|
+
"durationMs": 4019.24,
|
|
5
|
+
"totals": {
|
|
6
|
+
"total": 282,
|
|
7
|
+
"passed": 282,
|
|
8
|
+
"failed": 0,
|
|
9
|
+
"byKind": {
|
|
10
|
+
"unit": {
|
|
11
|
+
"total": 100,
|
|
12
|
+
"passed": 100,
|
|
13
|
+
"failed": 0
|
|
14
|
+
},
|
|
15
|
+
"integration": {
|
|
16
|
+
"total": 20,
|
|
17
|
+
"passed": 20,
|
|
18
|
+
"failed": 0
|
|
19
|
+
},
|
|
20
|
+
"async": {
|
|
21
|
+
"total": 20,
|
|
22
|
+
"passed": 20,
|
|
23
|
+
"failed": 0
|
|
24
|
+
},
|
|
25
|
+
"upgrade": {
|
|
26
|
+
"total": 19,
|
|
27
|
+
"passed": 19,
|
|
28
|
+
"failed": 0
|
|
29
|
+
},
|
|
30
|
+
"conformance": {
|
|
31
|
+
"total": 11,
|
|
32
|
+
"passed": 11,
|
|
33
|
+
"failed": 0
|
|
34
|
+
},
|
|
35
|
+
"lint": {
|
|
36
|
+
"total": 13,
|
|
37
|
+
"passed": 13,
|
|
38
|
+
"failed": 0
|
|
39
|
+
},
|
|
40
|
+
"edges": {
|
|
41
|
+
"total": 26,
|
|
42
|
+
"passed": 26,
|
|
43
|
+
"failed": 0
|
|
44
|
+
},
|
|
45
|
+
"registry": {
|
|
46
|
+
"total": 28,
|
|
47
|
+
"passed": 28,
|
|
48
|
+
"failed": 0
|
|
49
|
+
},
|
|
50
|
+
"graphjson": {
|
|
51
|
+
"total": 19,
|
|
52
|
+
"passed": 19,
|
|
53
|
+
"failed": 0
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"total": 26,
|
|
57
|
+
"passed": 26,
|
|
58
|
+
"failed": 0
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"byCategory": [
|
|
63
|
+
{
|
|
64
|
+
"category": "Graph validation",
|
|
65
|
+
"kind": "unit",
|
|
66
|
+
"total": 12,
|
|
67
|
+
"passed": 12,
|
|
68
|
+
"failed": 0,
|
|
69
|
+
"durationMs": 4.14
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"category": "Error classes & utilities",
|
|
73
|
+
"kind": "unit",
|
|
74
|
+
"total": 9,
|
|
75
|
+
"passed": 9,
|
|
76
|
+
"failed": 0,
|
|
77
|
+
"durationMs": 0.56
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"category": "Utilities",
|
|
81
|
+
"kind": "unit",
|
|
82
|
+
"total": 10,
|
|
83
|
+
"passed": 10,
|
|
84
|
+
"failed": 0,
|
|
85
|
+
"durationMs": 4.74
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"category": "EventBus",
|
|
89
|
+
"kind": "unit",
|
|
90
|
+
"total": 12,
|
|
91
|
+
"passed": 12,
|
|
92
|
+
"failed": 0,
|
|
93
|
+
"durationMs": 114.16
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"category": "InMemoryStorage",
|
|
97
|
+
"kind": "unit",
|
|
98
|
+
"total": 8,
|
|
99
|
+
"passed": 8,
|
|
100
|
+
"failed": 0,
|
|
101
|
+
"durationMs": 0.81
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"category": "Graph freezing",
|
|
105
|
+
"kind": "unit",
|
|
106
|
+
"total": 8,
|
|
107
|
+
"passed": 8,
|
|
108
|
+
"failed": 0,
|
|
109
|
+
"durationMs": 1.75
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"category": "Engine lifecycle",
|
|
113
|
+
"kind": "unit",
|
|
114
|
+
"total": 10,
|
|
115
|
+
"passed": 10,
|
|
116
|
+
"failed": 0,
|
|
117
|
+
"durationMs": 2.94
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"category": "Execution & scheduler",
|
|
121
|
+
"kind": "unit",
|
|
122
|
+
"total": 20,
|
|
123
|
+
"passed": 20,
|
|
124
|
+
"failed": 0,
|
|
125
|
+
"durationMs": 19.49
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"category": "Retry",
|
|
129
|
+
"kind": "unit",
|
|
130
|
+
"total": 6,
|
|
131
|
+
"passed": 6,
|
|
132
|
+
"failed": 0,
|
|
133
|
+
"durationMs": 90.62
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"category": "Cancellation",
|
|
137
|
+
"kind": "unit",
|
|
138
|
+
"total": 5,
|
|
139
|
+
"passed": 5,
|
|
140
|
+
"failed": 0,
|
|
141
|
+
"durationMs": 71.66
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"category": "E2E pipelines",
|
|
145
|
+
"kind": "integration",
|
|
146
|
+
"total": 3,
|
|
147
|
+
"passed": 3,
|
|
148
|
+
"failed": 0,
|
|
149
|
+
"durationMs": 58.96
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"category": "Failure semantics",
|
|
153
|
+
"kind": "integration",
|
|
154
|
+
"total": 7,
|
|
155
|
+
"passed": 7,
|
|
156
|
+
"failed": 0,
|
|
157
|
+
"durationMs": 399.37
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"category": "Concurrency & scale",
|
|
161
|
+
"kind": "integration",
|
|
162
|
+
"total": 3,
|
|
163
|
+
"passed": 3,
|
|
164
|
+
"failed": 0,
|
|
165
|
+
"durationMs": 91.98
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"category": "Cancellation & cycles",
|
|
169
|
+
"kind": "integration",
|
|
170
|
+
"total": 2,
|
|
171
|
+
"passed": 2,
|
|
172
|
+
"failed": 0,
|
|
173
|
+
"durationMs": 202.55
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"category": "Events & listeners",
|
|
177
|
+
"kind": "integration",
|
|
178
|
+
"total": 2,
|
|
179
|
+
"passed": 2,
|
|
180
|
+
"failed": 0,
|
|
181
|
+
"durationMs": 23.6
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"category": "Lifecycle & plugins",
|
|
185
|
+
"kind": "integration",
|
|
186
|
+
"total": 3,
|
|
187
|
+
"passed": 3,
|
|
188
|
+
"failed": 0,
|
|
189
|
+
"durationMs": 54.22
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"category": "Node callbacks (async)",
|
|
193
|
+
"kind": "async",
|
|
194
|
+
"total": 20,
|
|
195
|
+
"passed": 20,
|
|
196
|
+
"failed": 0,
|
|
197
|
+
"durationMs": 619.64
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"category": "A2 · drain deadline",
|
|
201
|
+
"kind": "upgrade",
|
|
202
|
+
"total": 2,
|
|
203
|
+
"passed": 2,
|
|
204
|
+
"failed": 0,
|
|
205
|
+
"durationMs": 542.36
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"category": "A3 · completion policy",
|
|
209
|
+
"kind": "upgrade",
|
|
210
|
+
"total": 4,
|
|
211
|
+
"passed": 4,
|
|
212
|
+
"failed": 0,
|
|
213
|
+
"durationMs": 78.41
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"category": "A4/A5 · FAIL_FAST & FALLBACK",
|
|
217
|
+
"kind": "upgrade",
|
|
218
|
+
"total": 4,
|
|
219
|
+
"passed": 4,
|
|
220
|
+
"failed": 0,
|
|
221
|
+
"durationMs": 30.91
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"category": "A6/A10 · results & hygiene",
|
|
225
|
+
"kind": "upgrade",
|
|
226
|
+
"total": 3,
|
|
227
|
+
"passed": 3,
|
|
228
|
+
"failed": 0,
|
|
229
|
+
"durationMs": 147.37
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"category": "A8 · concurrency & budget",
|
|
233
|
+
"kind": "upgrade",
|
|
234
|
+
"total": 3,
|
|
235
|
+
"passed": 3,
|
|
236
|
+
"failed": 0,
|
|
237
|
+
"durationMs": 29.41
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"category": "A9 · graph linting",
|
|
241
|
+
"kind": "upgrade",
|
|
242
|
+
"total": 2,
|
|
243
|
+
"passed": 2,
|
|
244
|
+
"failed": 0,
|
|
245
|
+
"durationMs": 1.06
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"category": "Isomorphic build",
|
|
249
|
+
"kind": "upgrade",
|
|
250
|
+
"total": 1,
|
|
251
|
+
"passed": 1,
|
|
252
|
+
"failed": 0,
|
|
253
|
+
"durationMs": 1292.28
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"category": "Storage adapter contract",
|
|
257
|
+
"kind": "conformance",
|
|
258
|
+
"total": 7,
|
|
259
|
+
"passed": 7,
|
|
260
|
+
"failed": 0,
|
|
261
|
+
"durationMs": 4.14
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"category": "Snapshot wire format",
|
|
265
|
+
"kind": "conformance",
|
|
266
|
+
"total": 4,
|
|
267
|
+
"passed": 4,
|
|
268
|
+
"failed": 0,
|
|
269
|
+
"durationMs": 0.56
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"category": "Success dead-end rule",
|
|
273
|
+
"kind": "lint",
|
|
274
|
+
"total": 13,
|
|
275
|
+
"passed": 13,
|
|
276
|
+
"failed": 0,
|
|
277
|
+
"durationMs": 28.47
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
"category": "Typed edges & routing",
|
|
281
|
+
"kind": "edges",
|
|
282
|
+
"total": 26,
|
|
283
|
+
"passed": 26,
|
|
284
|
+
"failed": 0,
|
|
285
|
+
"durationMs": 23.34
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"category": "Function registry",
|
|
289
|
+
"kind": "registry",
|
|
290
|
+
"total": 28,
|
|
291
|
+
"passed": 28,
|
|
292
|
+
"failed": 0,
|
|
293
|
+
"durationMs": 36.5
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
"category": "Graph JSON loader",
|
|
297
|
+
"kind": "graphjson",
|
|
298
|
+
"total": 19,
|
|
299
|
+
"passed": 19,
|
|
300
|
+
"failed": 0,
|
|
301
|
+
"durationMs": 5.72
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
"category": "Script loading",
|
|
305
|
+
"kind": "scripts",
|
|
306
|
+
"total": 26,
|
|
307
|
+
"passed": 26,
|
|
308
|
+
"failed": 0,
|
|
309
|
+
"durationMs": 25.46
|
|
310
|
+
}
|
|
311
|
+
],
|
|
312
|
+
"unhandledRejections": [],
|
|
313
|
+
"tests": [
|
|
314
|
+
{
|
|
315
|
+
"id": "U1",
|
|
316
|
+
"kind": "unit",
|
|
317
|
+
"category": "Graph validation",
|
|
318
|
+
"name": "throws GraphValidationError when graph.id is empty",
|
|
319
|
+
"status": "passed",
|
|
320
|
+
"durationMs": 2.91,
|
|
321
|
+
"logs": []
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
"id": "U2",
|
|
325
|
+
"kind": "unit",
|
|
326
|
+
"category": "Graph validation",
|
|
327
|
+
"name": "throws when graph.id is only whitespace",
|
|
328
|
+
"status": "passed",
|
|
329
|
+
"durationMs": 0.11,
|
|
330
|
+
"logs": []
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
"id": "U3",
|
|
334
|
+
"kind": "unit",
|
|
335
|
+
"category": "Graph validation",
|
|
336
|
+
"name": "throws when graph.version is empty",
|
|
337
|
+
"status": "passed",
|
|
338
|
+
"durationMs": 0.08,
|
|
339
|
+
"logs": []
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
"id": "U4",
|
|
343
|
+
"kind": "unit",
|
|
344
|
+
"category": "Graph validation",
|
|
345
|
+
"name": "throws when there are no entry nodes",
|
|
346
|
+
"status": "passed",
|
|
347
|
+
"durationMs": 0.07,
|
|
348
|
+
"logs": []
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
"id": "U5",
|
|
352
|
+
"kind": "unit",
|
|
353
|
+
"category": "Graph validation",
|
|
354
|
+
"name": "throws when there are no terminal nodes",
|
|
355
|
+
"status": "passed",
|
|
356
|
+
"durationMs": 0.07,
|
|
357
|
+
"logs": []
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
"id": "U6",
|
|
361
|
+
"kind": "unit",
|
|
362
|
+
"category": "Graph validation",
|
|
363
|
+
"name": "throws when a node key does not match Node.id",
|
|
364
|
+
"status": "passed",
|
|
365
|
+
"durationMs": 0.09,
|
|
366
|
+
"logs": []
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"id": "U7",
|
|
370
|
+
"kind": "unit",
|
|
371
|
+
"category": "Graph validation",
|
|
372
|
+
"name": "throws when next references an unknown node",
|
|
373
|
+
"status": "passed",
|
|
374
|
+
"durationMs": 0.09,
|
|
375
|
+
"logs": []
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
"id": "U8",
|
|
379
|
+
"kind": "unit",
|
|
380
|
+
"category": "Graph validation",
|
|
381
|
+
"name": "throws when onThrow references an unknown node",
|
|
382
|
+
"status": "passed",
|
|
383
|
+
"durationMs": 0.08,
|
|
384
|
+
"logs": []
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"id": "U9",
|
|
388
|
+
"kind": "unit",
|
|
389
|
+
"category": "Graph validation",
|
|
390
|
+
"name": "throws when node timeoutMs is zero",
|
|
391
|
+
"status": "passed",
|
|
392
|
+
"durationMs": 0.17,
|
|
393
|
+
"logs": []
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
"id": "U10",
|
|
397
|
+
"kind": "unit",
|
|
398
|
+
"category": "Graph validation",
|
|
399
|
+
"name": "throws when node timeoutMs is negative",
|
|
400
|
+
"status": "passed",
|
|
401
|
+
"durationMs": 0.19,
|
|
402
|
+
"logs": []
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
"id": "U11",
|
|
406
|
+
"kind": "unit",
|
|
407
|
+
"category": "Graph validation",
|
|
408
|
+
"name": "throws when retry.maxAttempts is not an integer",
|
|
409
|
+
"status": "passed",
|
|
410
|
+
"durationMs": 0.15,
|
|
411
|
+
"logs": []
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
"id": "U12",
|
|
415
|
+
"kind": "unit",
|
|
416
|
+
"category": "Graph validation",
|
|
417
|
+
"name": "throws when entry or terminal node is unknown",
|
|
418
|
+
"status": "passed",
|
|
419
|
+
"durationMs": 0.13,
|
|
420
|
+
"logs": []
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
"id": "U13",
|
|
424
|
+
"kind": "unit",
|
|
425
|
+
"category": "Error classes & utilities",
|
|
426
|
+
"name": "AutomateGraphError carries message, code and details",
|
|
427
|
+
"status": "passed",
|
|
428
|
+
"durationMs": 0.1,
|
|
429
|
+
"logs": []
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
"id": "U14",
|
|
433
|
+
"kind": "unit",
|
|
434
|
+
"category": "Error classes & utilities",
|
|
435
|
+
"name": "GraphValidationError uses the GRAPH_VALIDATION_ERROR code",
|
|
436
|
+
"status": "passed",
|
|
437
|
+
"durationMs": 0.05,
|
|
438
|
+
"logs": []
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
"id": "U15",
|
|
442
|
+
"kind": "unit",
|
|
443
|
+
"category": "Error classes & utilities",
|
|
444
|
+
"name": "TimeoutError composes a readable message and details",
|
|
445
|
+
"status": "passed",
|
|
446
|
+
"durationMs": 0.08,
|
|
447
|
+
"logs": []
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
"id": "U16",
|
|
451
|
+
"kind": "unit",
|
|
452
|
+
"category": "Error classes & utilities",
|
|
453
|
+
"name": "PluginInitializationError reports the plugin id and keeps the cause",
|
|
454
|
+
"status": "passed",
|
|
455
|
+
"durationMs": 0.07,
|
|
456
|
+
"logs": []
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
"id": "U17",
|
|
460
|
+
"kind": "unit",
|
|
461
|
+
"category": "Error classes & utilities",
|
|
462
|
+
"name": "inheritance chain: TimeoutError -> AutomateGraphError -> Error",
|
|
463
|
+
"status": "passed",
|
|
464
|
+
"durationMs": 0.04,
|
|
465
|
+
"logs": []
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
"id": "U18",
|
|
469
|
+
"kind": "unit",
|
|
470
|
+
"category": "Error classes & utilities",
|
|
471
|
+
"name": "PluginInitializationError is an AutomateGraphError",
|
|
472
|
+
"status": "passed",
|
|
473
|
+
"durationMs": 0.11,
|
|
474
|
+
"logs": []
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
"id": "U19",
|
|
478
|
+
"kind": "unit",
|
|
479
|
+
"category": "Error classes & utilities",
|
|
480
|
+
"name": "AutomateGraphError is not a GraphValidationError (distinct subclass)",
|
|
481
|
+
"status": "passed",
|
|
482
|
+
"durationMs": 0.04,
|
|
483
|
+
"logs": []
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
"id": "U20",
|
|
487
|
+
"kind": "unit",
|
|
488
|
+
"category": "Error classes & utilities",
|
|
489
|
+
"name": "AutomateGraphError allows optional details to be omitted",
|
|
490
|
+
"status": "passed",
|
|
491
|
+
"durationMs": 0.03,
|
|
492
|
+
"logs": []
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
"id": "U21",
|
|
496
|
+
"kind": "unit",
|
|
497
|
+
"category": "Error classes & utilities",
|
|
498
|
+
"name": "Error classes expose stable constructor.name values",
|
|
499
|
+
"status": "passed",
|
|
500
|
+
"durationMs": 0.04,
|
|
501
|
+
"logs": []
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
"id": "U22",
|
|
505
|
+
"kind": "unit",
|
|
506
|
+
"category": "Utilities",
|
|
507
|
+
"name": "engine executionId is a stable 36-char UUID-ish string",
|
|
508
|
+
"status": "passed",
|
|
509
|
+
"durationMs": 0.32,
|
|
510
|
+
"logs": []
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
"id": "U23",
|
|
514
|
+
"kind": "unit",
|
|
515
|
+
"category": "Utilities",
|
|
516
|
+
"name": "executionIds are unique across engines",
|
|
517
|
+
"status": "passed",
|
|
518
|
+
"durationMs": 0.88,
|
|
519
|
+
"logs": []
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
"id": "U24",
|
|
523
|
+
"kind": "unit",
|
|
524
|
+
"category": "Utilities",
|
|
525
|
+
"name": "report.startedAt is an ISO-8601 timestamp",
|
|
526
|
+
"status": "passed",
|
|
527
|
+
"durationMs": 1.28,
|
|
528
|
+
"logs": []
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
"id": "U25",
|
|
532
|
+
"kind": "unit",
|
|
533
|
+
"category": "Utilities",
|
|
534
|
+
"name": "report.finishedAt >= report.startedAt",
|
|
535
|
+
"status": "passed",
|
|
536
|
+
"durationMs": 0.17,
|
|
537
|
+
"logs": []
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
"id": "U26",
|
|
541
|
+
"kind": "unit",
|
|
542
|
+
"category": "Utilities",
|
|
543
|
+
"name": "node result startedAt <= finishedAt",
|
|
544
|
+
"status": "passed",
|
|
545
|
+
"durationMs": 0.15,
|
|
546
|
+
"logs": []
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
"id": "U27",
|
|
550
|
+
"kind": "unit",
|
|
551
|
+
"category": "Utilities",
|
|
552
|
+
"name": "error records carry ISO timestamps",
|
|
553
|
+
"status": "passed",
|
|
554
|
+
"durationMs": 0.79,
|
|
555
|
+
"logs": []
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
"id": "U28",
|
|
559
|
+
"kind": "unit",
|
|
560
|
+
"category": "Utilities",
|
|
561
|
+
"name": "string throws are converted to Error instances",
|
|
562
|
+
"status": "passed",
|
|
563
|
+
"durationMs": 0.47,
|
|
564
|
+
"logs": []
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
"id": "U29",
|
|
568
|
+
"kind": "unit",
|
|
569
|
+
"category": "Utilities",
|
|
570
|
+
"name": "throwing a non-Error object stringifies via toError",
|
|
571
|
+
"status": "passed",
|
|
572
|
+
"durationMs": 0.25,
|
|
573
|
+
"logs": []
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
"id": "U30",
|
|
577
|
+
"kind": "unit",
|
|
578
|
+
"category": "Utilities",
|
|
579
|
+
"name": "error records expose a stack trace for real errors",
|
|
580
|
+
"status": "passed",
|
|
581
|
+
"durationMs": 0.21,
|
|
582
|
+
"logs": []
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
"id": "U31",
|
|
586
|
+
"kind": "unit",
|
|
587
|
+
"category": "Utilities",
|
|
588
|
+
"name": "graph id/version of the report mirror the definition",
|
|
589
|
+
"status": "passed",
|
|
590
|
+
"durationMs": 0.22,
|
|
591
|
+
"logs": []
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
"id": "U32",
|
|
595
|
+
"kind": "unit",
|
|
596
|
+
"category": "EventBus",
|
|
597
|
+
"name": "emit with no listeners resolves",
|
|
598
|
+
"status": "passed",
|
|
599
|
+
"durationMs": 0.06,
|
|
600
|
+
"logs": []
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
"id": "U33",
|
|
604
|
+
"kind": "unit",
|
|
605
|
+
"category": "EventBus",
|
|
606
|
+
"name": "required listeners receive the payload and are awaited",
|
|
607
|
+
"status": "passed",
|
|
608
|
+
"durationMs": 5.69,
|
|
609
|
+
"logs": []
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
"id": "U34",
|
|
613
|
+
"kind": "unit",
|
|
614
|
+
"category": "EventBus",
|
|
615
|
+
"name": "required listeners run in registration order",
|
|
616
|
+
"status": "passed",
|
|
617
|
+
"durationMs": 0.15,
|
|
618
|
+
"logs": []
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
"id": "U35",
|
|
622
|
+
"kind": "unit",
|
|
623
|
+
"category": "EventBus",
|
|
624
|
+
"name": "plain listeners are NOT awaited — emit resolves first",
|
|
625
|
+
"status": "passed",
|
|
626
|
+
"durationMs": 60.58,
|
|
627
|
+
"logs": []
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
"id": "U36",
|
|
631
|
+
"kind": "unit",
|
|
632
|
+
"category": "EventBus",
|
|
633
|
+
"name": "unsubscribe stops delivery",
|
|
634
|
+
"status": "passed",
|
|
635
|
+
"durationMs": 0.22,
|
|
636
|
+
"logs": []
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
"id": "U37",
|
|
640
|
+
"kind": "unit",
|
|
641
|
+
"category": "EventBus",
|
|
642
|
+
"name": "unsubscribing the last listener removes the event key",
|
|
643
|
+
"status": "passed",
|
|
644
|
+
"durationMs": 0.05,
|
|
645
|
+
"logs": []
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
"id": "U38",
|
|
649
|
+
"kind": "unit",
|
|
650
|
+
"category": "EventBus",
|
|
651
|
+
"name": "duplicate handlers registered as distinct listeners both fire",
|
|
652
|
+
"status": "passed",
|
|
653
|
+
"durationMs": 0.18,
|
|
654
|
+
"logs": []
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
"id": "U39",
|
|
658
|
+
"kind": "unit",
|
|
659
|
+
"category": "EventBus",
|
|
660
|
+
"name": "a throwing plain listener does not reject emit (isolated)",
|
|
661
|
+
"status": "passed",
|
|
662
|
+
"durationMs": 10.65,
|
|
663
|
+
"logs": []
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
"id": "U40",
|
|
667
|
+
"kind": "unit",
|
|
668
|
+
"category": "EventBus",
|
|
669
|
+
"name": "a failing plain listener is reported through onLog",
|
|
670
|
+
"status": "passed",
|
|
671
|
+
"durationMs": 15.53,
|
|
672
|
+
"logs": []
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
"id": "U41",
|
|
676
|
+
"kind": "unit",
|
|
677
|
+
"category": "EventBus",
|
|
678
|
+
"name": "a throwing REQUIRED listener propagates to the emitter",
|
|
679
|
+
"status": "passed",
|
|
680
|
+
"durationMs": 0.27,
|
|
681
|
+
"logs": []
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
"id": "U42",
|
|
685
|
+
"kind": "unit",
|
|
686
|
+
"category": "EventBus",
|
|
687
|
+
"name": "mixed required/plain: required first, plain afterwards (async)",
|
|
688
|
+
"status": "passed",
|
|
689
|
+
"durationMs": 20.58,
|
|
690
|
+
"logs": []
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
"id": "U43",
|
|
694
|
+
"kind": "unit",
|
|
695
|
+
"category": "EventBus",
|
|
696
|
+
"name": "clear() removes every listener",
|
|
697
|
+
"status": "passed",
|
|
698
|
+
"durationMs": 0.2,
|
|
699
|
+
"logs": []
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
"id": "U44",
|
|
703
|
+
"kind": "unit",
|
|
704
|
+
"category": "InMemoryStorage",
|
|
705
|
+
"name": "storage advertises a stable id and version",
|
|
706
|
+
"status": "passed",
|
|
707
|
+
"durationMs": 0.04,
|
|
708
|
+
"logs": []
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
"id": "U45",
|
|
712
|
+
"kind": "unit",
|
|
713
|
+
"category": "InMemoryStorage",
|
|
714
|
+
"name": "load returns null for an unknown executionId",
|
|
715
|
+
"status": "passed",
|
|
716
|
+
"durationMs": 0.11,
|
|
717
|
+
"logs": []
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
"id": "U46",
|
|
721
|
+
"kind": "unit",
|
|
722
|
+
"category": "InMemoryStorage",
|
|
723
|
+
"name": "saveAtomic then load returns the snapshot",
|
|
724
|
+
"status": "passed",
|
|
725
|
+
"durationMs": 0.14,
|
|
726
|
+
"logs": []
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
"id": "U47",
|
|
730
|
+
"kind": "unit",
|
|
731
|
+
"category": "InMemoryStorage",
|
|
732
|
+
"name": "saveAtomic deep-copies the incoming snapshot (caller later mutates)",
|
|
733
|
+
"status": "passed",
|
|
734
|
+
"durationMs": 0.09,
|
|
735
|
+
"logs": []
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
"id": "U48",
|
|
739
|
+
"kind": "unit",
|
|
740
|
+
"category": "InMemoryStorage",
|
|
741
|
+
"name": "load returns an isolated copy (mutation of the returned object is inert)",
|
|
742
|
+
"status": "passed",
|
|
743
|
+
"durationMs": 0.06,
|
|
744
|
+
"logs": []
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
"id": "U49",
|
|
748
|
+
"kind": "unit",
|
|
749
|
+
"category": "InMemoryStorage",
|
|
750
|
+
"name": "repeated saves replace the previous snapshot",
|
|
751
|
+
"status": "passed",
|
|
752
|
+
"durationMs": 0.07,
|
|
753
|
+
"logs": []
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
"id": "U50",
|
|
757
|
+
"kind": "unit",
|
|
758
|
+
"category": "InMemoryStorage",
|
|
759
|
+
"name": "snapshots for different executions stay isolated",
|
|
760
|
+
"status": "passed",
|
|
761
|
+
"durationMs": 0.08,
|
|
762
|
+
"logs": []
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
"id": "U51",
|
|
766
|
+
"kind": "unit",
|
|
767
|
+
"category": "InMemoryStorage",
|
|
768
|
+
"name": "concurrent saveAtomic calls never interleave partial data",
|
|
769
|
+
"status": "passed",
|
|
770
|
+
"durationMs": 0.22,
|
|
771
|
+
"logs": []
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
"id": "U52",
|
|
775
|
+
"kind": "unit",
|
|
776
|
+
"category": "Graph freezing",
|
|
777
|
+
"name": "getGraphDefinition returns a frozen object",
|
|
778
|
+
"status": "passed",
|
|
779
|
+
"durationMs": 0.24,
|
|
780
|
+
"logs": []
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
"id": "U53",
|
|
784
|
+
"kind": "unit",
|
|
785
|
+
"category": "Graph freezing",
|
|
786
|
+
"name": "entryNodes and terminalNodes arrays are frozen",
|
|
787
|
+
"status": "passed",
|
|
788
|
+
"durationMs": 0.1,
|
|
789
|
+
"logs": []
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
"id": "U54",
|
|
793
|
+
"kind": "unit",
|
|
794
|
+
"category": "Graph freezing",
|
|
795
|
+
"name": "the nodes record is frozen",
|
|
796
|
+
"status": "passed",
|
|
797
|
+
"durationMs": 0.13,
|
|
798
|
+
"logs": []
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
"id": "U55",
|
|
802
|
+
"kind": "unit",
|
|
803
|
+
"category": "Graph freezing",
|
|
804
|
+
"name": "mutating the definition after construction does not affect the engine",
|
|
805
|
+
"status": "passed",
|
|
806
|
+
"durationMs": 0.45,
|
|
807
|
+
"logs": []
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
"id": "U56",
|
|
811
|
+
"kind": "unit",
|
|
812
|
+
"category": "Graph freezing",
|
|
813
|
+
"name": "mutating a node's next array after construction is ignored",
|
|
814
|
+
"status": "passed",
|
|
815
|
+
"durationMs": 0.32,
|
|
816
|
+
"logs": []
|
|
817
|
+
},
|
|
818
|
+
{
|
|
819
|
+
"id": "U57",
|
|
820
|
+
"kind": "unit",
|
|
821
|
+
"category": "Graph freezing",
|
|
822
|
+
"name": "getGraphDefinition is referentially identical across calls",
|
|
823
|
+
"status": "passed",
|
|
824
|
+
"durationMs": 0.12,
|
|
825
|
+
"logs": []
|
|
826
|
+
},
|
|
827
|
+
{
|
|
828
|
+
"id": "U58",
|
|
829
|
+
"kind": "unit",
|
|
830
|
+
"category": "Graph freezing",
|
|
831
|
+
"name": "deep-frozen graphs are accepted (idempotent freezing)",
|
|
832
|
+
"status": "passed",
|
|
833
|
+
"durationMs": 0.29,
|
|
834
|
+
"logs": []
|
|
835
|
+
},
|
|
836
|
+
{
|
|
837
|
+
"id": "U59",
|
|
838
|
+
"kind": "unit",
|
|
839
|
+
"category": "Graph freezing",
|
|
840
|
+
"name": "frozen node objects reject mutation at runtime (strict mode)",
|
|
841
|
+
"status": "passed",
|
|
842
|
+
"durationMs": 0.1,
|
|
843
|
+
"logs": []
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
"id": "U60",
|
|
847
|
+
"kind": "unit",
|
|
848
|
+
"category": "Engine lifecycle",
|
|
849
|
+
"name": "getState() is CREATED before run()",
|
|
850
|
+
"status": "passed",
|
|
851
|
+
"durationMs": 0.08,
|
|
852
|
+
"logs": []
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
"id": "U61",
|
|
856
|
+
"kind": "unit",
|
|
857
|
+
"category": "Engine lifecycle",
|
|
858
|
+
"name": "calling run() twice throws EXECUTION_ALREADY_STARTED",
|
|
859
|
+
"status": "passed",
|
|
860
|
+
"durationMs": 0.2,
|
|
861
|
+
"logs": []
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
"id": "U62",
|
|
865
|
+
"kind": "unit",
|
|
866
|
+
"category": "Engine lifecycle",
|
|
867
|
+
"name": "run() returns a Promise",
|
|
868
|
+
"status": "passed",
|
|
869
|
+
"durationMs": 0.17,
|
|
870
|
+
"logs": []
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
"id": "U63",
|
|
874
|
+
"kind": "unit",
|
|
875
|
+
"category": "Engine lifecycle",
|
|
876
|
+
"name": "read-only accessors return defensive copies of errors",
|
|
877
|
+
"status": "passed",
|
|
878
|
+
"durationMs": 0.24,
|
|
879
|
+
"logs": []
|
|
880
|
+
},
|
|
881
|
+
{
|
|
882
|
+
"id": "U64",
|
|
883
|
+
"kind": "unit",
|
|
884
|
+
"category": "Engine lifecycle",
|
|
885
|
+
"name": "read-only accessors return defensive copies of results",
|
|
886
|
+
"status": "passed",
|
|
887
|
+
"durationMs": 0.4,
|
|
888
|
+
"logs": []
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
"id": "U65",
|
|
892
|
+
"kind": "unit",
|
|
893
|
+
"category": "Engine lifecycle",
|
|
894
|
+
"name": "duplicate plugin ids are rejected with DUPLICATE_PLUGIN",
|
|
895
|
+
"status": "passed",
|
|
896
|
+
"durationMs": 0.4,
|
|
897
|
+
"logs": []
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
"id": "U66",
|
|
901
|
+
"kind": "unit",
|
|
902
|
+
"category": "Engine lifecycle",
|
|
903
|
+
"name": "a plugin reusing the storage id is rejected in the constructor",
|
|
904
|
+
"status": "passed",
|
|
905
|
+
"durationMs": 0.17,
|
|
906
|
+
"logs": []
|
|
907
|
+
},
|
|
908
|
+
{
|
|
909
|
+
"id": "U67",
|
|
910
|
+
"kind": "unit",
|
|
911
|
+
"category": "Engine lifecycle",
|
|
912
|
+
"name": "optional plugin failure is tolerated and logged",
|
|
913
|
+
"status": "passed",
|
|
914
|
+
"durationMs": 0.54,
|
|
915
|
+
"logs": []
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
"id": "U68",
|
|
919
|
+
"kind": "unit",
|
|
920
|
+
"category": "Engine lifecycle",
|
|
921
|
+
"name": "a required plugin failure REJECTS the run and shuts down initialized plugins",
|
|
922
|
+
"status": "passed",
|
|
923
|
+
"durationMs": 0.36,
|
|
924
|
+
"logs": []
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
"id": "U69",
|
|
928
|
+
"kind": "unit",
|
|
929
|
+
"category": "Engine lifecycle",
|
|
930
|
+
"name": "plugins close in reverse initialization order after a successful run",
|
|
931
|
+
"status": "passed",
|
|
932
|
+
"durationMs": 0.38,
|
|
933
|
+
"logs": []
|
|
934
|
+
},
|
|
935
|
+
{
|
|
936
|
+
"id": "U70",
|
|
937
|
+
"kind": "unit",
|
|
938
|
+
"category": "Execution & scheduler",
|
|
939
|
+
"name": "single-node graph completes and stores its output",
|
|
940
|
+
"status": "passed",
|
|
941
|
+
"durationMs": 0.15,
|
|
942
|
+
"logs": []
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
"id": "U71",
|
|
946
|
+
"kind": "unit",
|
|
947
|
+
"category": "Execution & scheduler",
|
|
948
|
+
"name": "linear graph runs every node in order",
|
|
949
|
+
"status": "passed",
|
|
950
|
+
"durationMs": 0.27,
|
|
951
|
+
"logs": []
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
"id": "U72",
|
|
955
|
+
"kind": "unit",
|
|
956
|
+
"category": "Execution & scheduler",
|
|
957
|
+
"name": "diamond graph fans out and converges",
|
|
958
|
+
"status": "passed",
|
|
959
|
+
"durationMs": 0.3,
|
|
960
|
+
"logs": []
|
|
961
|
+
},
|
|
962
|
+
{
|
|
963
|
+
"id": "U73",
|
|
964
|
+
"kind": "unit",
|
|
965
|
+
"category": "Execution & scheduler",
|
|
966
|
+
"name": "independent entry nodes both execute",
|
|
967
|
+
"status": "passed",
|
|
968
|
+
"durationMs": 0.22,
|
|
969
|
+
"logs": []
|
|
970
|
+
},
|
|
971
|
+
{
|
|
972
|
+
"id": "U74",
|
|
973
|
+
"kind": "unit",
|
|
974
|
+
"category": "Execution & scheduler",
|
|
975
|
+
"name": "multiple terminal nodes are both recognized",
|
|
976
|
+
"status": "passed",
|
|
977
|
+
"durationMs": 0.27,
|
|
978
|
+
"logs": []
|
|
979
|
+
},
|
|
980
|
+
{
|
|
981
|
+
"id": "U75",
|
|
982
|
+
"kind": "unit",
|
|
983
|
+
"category": "Execution & scheduler",
|
|
984
|
+
"name": "node context exposes execution metadata",
|
|
985
|
+
"status": "passed",
|
|
986
|
+
"durationMs": 0.19,
|
|
987
|
+
"logs": []
|
|
988
|
+
},
|
|
989
|
+
{
|
|
990
|
+
"id": "U76",
|
|
991
|
+
"kind": "unit",
|
|
992
|
+
"category": "Execution & scheduler",
|
|
993
|
+
"name": "activationId is unique per activation",
|
|
994
|
+
"status": "passed",
|
|
995
|
+
"durationMs": 0.45,
|
|
996
|
+
"logs": []
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
"id": "U77",
|
|
1000
|
+
"kind": "unit",
|
|
1001
|
+
"category": "Execution & scheduler",
|
|
1002
|
+
"name": "node output can be read from a successor's context.outputs",
|
|
1003
|
+
"status": "passed",
|
|
1004
|
+
"durationMs": 0.27,
|
|
1005
|
+
"logs": []
|
|
1006
|
+
},
|
|
1007
|
+
{
|
|
1008
|
+
"id": "U78",
|
|
1009
|
+
"kind": "unit",
|
|
1010
|
+
"category": "Execution & scheduler",
|
|
1011
|
+
"name": "context.outputs freezing blocks mutation (strict runtime)",
|
|
1012
|
+
"status": "passed",
|
|
1013
|
+
"durationMs": 0.2,
|
|
1014
|
+
"logs": []
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
"id": "U79",
|
|
1018
|
+
"kind": "unit",
|
|
1019
|
+
"category": "Execution & scheduler",
|
|
1020
|
+
"name": "async node functions are awaited",
|
|
1021
|
+
"status": "passed",
|
|
1022
|
+
"durationMs": 15.05,
|
|
1023
|
+
"logs": []
|
|
1024
|
+
},
|
|
1025
|
+
{
|
|
1026
|
+
"id": "U80",
|
|
1027
|
+
"kind": "unit",
|
|
1028
|
+
"category": "Execution & scheduler",
|
|
1029
|
+
"name": "reaching the terminal marks logicallyCompleted and stops scheduling",
|
|
1030
|
+
"status": "passed",
|
|
1031
|
+
"durationMs": 0.29,
|
|
1032
|
+
"logs": []
|
|
1033
|
+
},
|
|
1034
|
+
{
|
|
1035
|
+
"id": "U81",
|
|
1036
|
+
"kind": "unit",
|
|
1037
|
+
"category": "Execution & scheduler",
|
|
1038
|
+
"name": "the graph exhausts without a terminal => FAILED + GRAPH_TERMINAL_NOT_REACHED",
|
|
1039
|
+
"status": "passed",
|
|
1040
|
+
"durationMs": 0.27,
|
|
1041
|
+
"logs": []
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
"id": "U82",
|
|
1045
|
+
"kind": "unit",
|
|
1046
|
+
"category": "Execution & scheduler",
|
|
1047
|
+
"name": "node.started / node.succeeded events fire with payloads",
|
|
1048
|
+
"status": "passed",
|
|
1049
|
+
"durationMs": 0.27,
|
|
1050
|
+
"logs": []
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
"id": "U83",
|
|
1054
|
+
"kind": "unit",
|
|
1055
|
+
"category": "Execution & scheduler",
|
|
1056
|
+
"name": "node.started precedes node.succeeded per node",
|
|
1057
|
+
"status": "passed",
|
|
1058
|
+
"durationMs": 0.12,
|
|
1059
|
+
"logs": []
|
|
1060
|
+
},
|
|
1061
|
+
{
|
|
1062
|
+
"id": "U84",
|
|
1063
|
+
"kind": "unit",
|
|
1064
|
+
"category": "Execution & scheduler",
|
|
1065
|
+
"name": "context.emit() forwards to the bus",
|
|
1066
|
+
"status": "passed",
|
|
1067
|
+
"durationMs": 0.16,
|
|
1068
|
+
"logs": []
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
"id": "U85",
|
|
1072
|
+
"kind": "unit",
|
|
1073
|
+
"category": "Execution & scheduler",
|
|
1074
|
+
"name": "checkpoints are written once per successful node by default",
|
|
1075
|
+
"status": "passed",
|
|
1076
|
+
"durationMs": 0.32,
|
|
1077
|
+
"logs": []
|
|
1078
|
+
},
|
|
1079
|
+
{
|
|
1080
|
+
"id": "U86",
|
|
1081
|
+
"kind": "unit",
|
|
1082
|
+
"category": "Execution & scheduler",
|
|
1083
|
+
"name": "checkpointEveryNode: false disables node checkpoints",
|
|
1084
|
+
"status": "passed",
|
|
1085
|
+
"durationMs": 0.17,
|
|
1086
|
+
"logs": []
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
"id": "U87",
|
|
1090
|
+
"kind": "unit",
|
|
1091
|
+
"category": "Execution & scheduler",
|
|
1092
|
+
"name": "snapshots record outputs, plugin versions and the execution id",
|
|
1093
|
+
"status": "passed",
|
|
1094
|
+
"durationMs": 0.22,
|
|
1095
|
+
"logs": []
|
|
1096
|
+
},
|
|
1097
|
+
{
|
|
1098
|
+
"id": "U88",
|
|
1099
|
+
"kind": "unit",
|
|
1100
|
+
"category": "Execution & scheduler",
|
|
1101
|
+
"name": "successful run yields zero errors and cloneable outputs",
|
|
1102
|
+
"status": "passed",
|
|
1103
|
+
"durationMs": 0.15,
|
|
1104
|
+
"logs": []
|
|
1105
|
+
},
|
|
1106
|
+
{
|
|
1107
|
+
"id": "U89",
|
|
1108
|
+
"kind": "unit",
|
|
1109
|
+
"category": "Execution & scheduler",
|
|
1110
|
+
"name": "node.started listener sees the context emitted by NodeContext.emit",
|
|
1111
|
+
"status": "passed",
|
|
1112
|
+
"durationMs": 0.15,
|
|
1113
|
+
"logs": []
|
|
1114
|
+
},
|
|
1115
|
+
{
|
|
1116
|
+
"id": "U90",
|
|
1117
|
+
"kind": "unit",
|
|
1118
|
+
"category": "Retry",
|
|
1119
|
+
"name": "errorPolicy RETRY re-runs until success and records the final attempt",
|
|
1120
|
+
"status": "passed",
|
|
1121
|
+
"durationMs": 2.89,
|
|
1122
|
+
"logs": []
|
|
1123
|
+
},
|
|
1124
|
+
{
|
|
1125
|
+
"id": "U91",
|
|
1126
|
+
"kind": "unit",
|
|
1127
|
+
"category": "Retry",
|
|
1128
|
+
"name": "RETRY respects delayMs between attempts",
|
|
1129
|
+
"status": "passed",
|
|
1130
|
+
"durationMs": 81.1,
|
|
1131
|
+
"logs": []
|
|
1132
|
+
},
|
|
1133
|
+
{
|
|
1134
|
+
"id": "U92",
|
|
1135
|
+
"kind": "unit",
|
|
1136
|
+
"category": "Retry",
|
|
1137
|
+
"name": "exhausting maxAttempts records one unhandled error and terminates",
|
|
1138
|
+
"status": "passed",
|
|
1139
|
+
"durationMs": 3.02,
|
|
1140
|
+
"logs": []
|
|
1141
|
+
},
|
|
1142
|
+
{
|
|
1143
|
+
"id": "U93",
|
|
1144
|
+
"kind": "unit",
|
|
1145
|
+
"category": "Retry",
|
|
1146
|
+
"name": "RETRY without delayMs retries without added latency",
|
|
1147
|
+
"status": "passed",
|
|
1148
|
+
"durationMs": 1.55,
|
|
1149
|
+
"logs": []
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
"id": "U94",
|
|
1153
|
+
"kind": "unit",
|
|
1154
|
+
"category": "Retry",
|
|
1155
|
+
"name": "retry maxAttempts=1 means a single execution",
|
|
1156
|
+
"status": "passed",
|
|
1157
|
+
"durationMs": 0.5,
|
|
1158
|
+
"logs": []
|
|
1159
|
+
},
|
|
1160
|
+
{
|
|
1161
|
+
"id": "U95",
|
|
1162
|
+
"kind": "unit",
|
|
1163
|
+
"category": "Retry",
|
|
1164
|
+
"name": "once retries succeed the successor nodes are scheduled",
|
|
1165
|
+
"status": "passed",
|
|
1166
|
+
"durationMs": 1.56,
|
|
1167
|
+
"logs": []
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
"id": "U96",
|
|
1171
|
+
"kind": "unit",
|
|
1172
|
+
"category": "Cancellation",
|
|
1173
|
+
"name": "cancel() on a CREATED engine produces CANCELLED",
|
|
1174
|
+
"status": "passed",
|
|
1175
|
+
"durationMs": 0.34,
|
|
1176
|
+
"logs": []
|
|
1177
|
+
},
|
|
1178
|
+
{
|
|
1179
|
+
"id": "U97",
|
|
1180
|
+
"kind": "unit",
|
|
1181
|
+
"category": "Cancellation",
|
|
1182
|
+
"name": "cancel() aborts the execution signal exposed to running nodes",
|
|
1183
|
+
"status": "passed",
|
|
1184
|
+
"durationMs": 30.18,
|
|
1185
|
+
"logs": []
|
|
1186
|
+
},
|
|
1187
|
+
{
|
|
1188
|
+
"id": "U98",
|
|
1189
|
+
"kind": "unit",
|
|
1190
|
+
"category": "Cancellation",
|
|
1191
|
+
"name": "cancel() emits graph.cancellationRequested",
|
|
1192
|
+
"status": "passed",
|
|
1193
|
+
"durationMs": 0.15,
|
|
1194
|
+
"logs": []
|
|
1195
|
+
},
|
|
1196
|
+
{
|
|
1197
|
+
"id": "U99",
|
|
1198
|
+
"kind": "unit",
|
|
1199
|
+
"category": "Cancellation",
|
|
1200
|
+
"name": "nodes in flight when cancelling settle as CANCELLED",
|
|
1201
|
+
"status": "passed",
|
|
1202
|
+
"durationMs": 40.73,
|
|
1203
|
+
"logs": []
|
|
1204
|
+
},
|
|
1205
|
+
{
|
|
1206
|
+
"id": "U100",
|
|
1207
|
+
"kind": "unit",
|
|
1208
|
+
"category": "Cancellation",
|
|
1209
|
+
"name": "cancel() after COMPLETED is a no-op",
|
|
1210
|
+
"status": "passed",
|
|
1211
|
+
"durationMs": 0.26,
|
|
1212
|
+
"logs": []
|
|
1213
|
+
},
|
|
1214
|
+
{
|
|
1215
|
+
"id": "I1",
|
|
1216
|
+
"kind": "integration",
|
|
1217
|
+
"category": "E2E pipelines",
|
|
1218
|
+
"name": "multi-branch order pipeline completes with a checkpoint per successful node",
|
|
1219
|
+
"status": "passed",
|
|
1220
|
+
"durationMs": 0.73,
|
|
1221
|
+
"logs": [
|
|
1222
|
+
"checkpoints written :: 6",
|
|
1223
|
+
"critical path :: \"validate -> fraud -> charge -> ship -> notify\""
|
|
1224
|
+
]
|
|
1225
|
+
},
|
|
1226
|
+
{
|
|
1227
|
+
"id": "I2",
|
|
1228
|
+
"kind": "integration",
|
|
1229
|
+
"category": "E2E pipelines",
|
|
1230
|
+
"name": "chaos graph: retry recovery plus ONTHROW repair still completes",
|
|
1231
|
+
"status": "passed",
|
|
1232
|
+
"durationMs": 11.54,
|
|
1233
|
+
"logs": [
|
|
1234
|
+
"error path :: [\"root\",\"risky\",\"check\"]",
|
|
1235
|
+
"handled :: true"
|
|
1236
|
+
]
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
"id": "I3",
|
|
1240
|
+
"kind": "integration",
|
|
1241
|
+
"category": "E2E pipelines",
|
|
1242
|
+
"name": "resilience scenario: timeout is routed to a fallback so the run completes",
|
|
1243
|
+
"status": "passed",
|
|
1244
|
+
"durationMs": 46.69,
|
|
1245
|
+
"logs": [
|
|
1246
|
+
"timeout details :: {\"nodeId\":\"slow-vendor\",\"timeoutMs\":25}",
|
|
1247
|
+
"succeeded nodes :: 7",
|
|
1248
|
+
"finding: fan-in runs the join node once per incoming edge (decide runs = 2)"
|
|
1249
|
+
]
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
"id": "I4",
|
|
1253
|
+
"kind": "integration",
|
|
1254
|
+
"category": "Failure semantics",
|
|
1255
|
+
"name": "graph-level FAIL_FAST stops not-yet-scheduled successors on independent branches",
|
|
1256
|
+
"status": "passed",
|
|
1257
|
+
"durationMs": 31.03,
|
|
1258
|
+
"logs": [
|
|
1259
|
+
"finding: an already-started sibling still observes its own completion before the graph settles as FAILED"
|
|
1260
|
+
]
|
|
1261
|
+
},
|
|
1262
|
+
{
|
|
1263
|
+
"id": "I5",
|
|
1264
|
+
"kind": "integration",
|
|
1265
|
+
"category": "Failure semantics",
|
|
1266
|
+
"name": "ONTHROW routes to a repair node and the failing node is never re-attempted",
|
|
1267
|
+
"status": "passed",
|
|
1268
|
+
"durationMs": 0.65,
|
|
1269
|
+
"logs": [
|
|
1270
|
+
"activation order :: \"root,risky,repair,verify,finish\""
|
|
1271
|
+
]
|
|
1272
|
+
},
|
|
1273
|
+
{
|
|
1274
|
+
"id": "I6",
|
|
1275
|
+
"kind": "integration",
|
|
1276
|
+
"category": "Concurrency & scale",
|
|
1277
|
+
"name": "diamond fan-in executes the join node once per incoming activation (no dedup)",
|
|
1278
|
+
"status": "passed",
|
|
1279
|
+
"durationMs": 0.52,
|
|
1280
|
+
"logs": [
|
|
1281
|
+
"finding: no join/dedup semantics — the join node ran once per incoming edge",
|
|
1282
|
+
"final outputs.d (last writer wins) :: \"d(seen b=B, c=C)\""
|
|
1283
|
+
]
|
|
1284
|
+
},
|
|
1285
|
+
{
|
|
1286
|
+
"id": "I7",
|
|
1287
|
+
"kind": "integration",
|
|
1288
|
+
"category": "Cancellation & cycles",
|
|
1289
|
+
"name": "cyclic graph iterates via next + ONTHROW exit and keeps a growing path",
|
|
1290
|
+
"status": "passed",
|
|
1291
|
+
"durationMs": 0.43,
|
|
1292
|
+
"logs": [
|
|
1293
|
+
"finding: cycles are legal (no validation) and terminate only via ONTHROW",
|
|
1294
|
+
"loop error path :: \"loop -> loop -> loop -> loop -> loop -> loop\""
|
|
1295
|
+
]
|
|
1296
|
+
},
|
|
1297
|
+
{
|
|
1298
|
+
"id": "I8",
|
|
1299
|
+
"kind": "integration",
|
|
1300
|
+
"category": "Concurrency & scale",
|
|
1301
|
+
"name": "250-node fan-out satisfies a full-concurrency barrier (no hidden cap)",
|
|
1302
|
+
"status": "passed",
|
|
1303
|
+
"durationMs": 88.81,
|
|
1304
|
+
"logs": [
|
|
1305
|
+
"finding: scheduler concurrency is unbounded (MAX_SAFE_INTEGER)",
|
|
1306
|
+
"throughput proof: all 250 nodes were simultaneously in flight (the barrier could only be released once every leaf had started), so wall time is ~1 node, not 250",
|
|
1307
|
+
"wall time :: \"89ms (serial lower bound would be >= 1250ms if capped)\""
|
|
1308
|
+
]
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
"id": "I9",
|
|
1312
|
+
"kind": "integration",
|
|
1313
|
+
"category": "Concurrency & scale",
|
|
1314
|
+
"name": "40-node chain produces monotone, gap-free checkpoints in execution order",
|
|
1315
|
+
"status": "passed",
|
|
1316
|
+
"durationMs": 2.65,
|
|
1317
|
+
"logs": [
|
|
1318
|
+
"snapshots :: 40",
|
|
1319
|
+
"final outputs :: 40"
|
|
1320
|
+
]
|
|
1321
|
+
},
|
|
1322
|
+
{
|
|
1323
|
+
"id": "I10",
|
|
1324
|
+
"kind": "integration",
|
|
1325
|
+
"category": "Events & listeners",
|
|
1326
|
+
"name": "required listeners observe a deterministic lifecycle event order",
|
|
1327
|
+
"status": "passed",
|
|
1328
|
+
"durationMs": 2.17,
|
|
1329
|
+
"logs": [
|
|
1330
|
+
"event sequence :: \"graph.ready -> node.started:a -> node.succeeded:a -> node.started:b -> node.succeeded:b -> graph.logicallyCompleted -> graph.finished\"",
|
|
1331
|
+
"run duration :: \"2ms\""
|
|
1332
|
+
]
|
|
1333
|
+
},
|
|
1334
|
+
{
|
|
1335
|
+
"id": "I11",
|
|
1336
|
+
"kind": "integration",
|
|
1337
|
+
"category": "Events & listeners",
|
|
1338
|
+
"name": "faulty non-required listeners are isolated; a failing required listener is contained",
|
|
1339
|
+
"status": "passed",
|
|
1340
|
+
"durationMs": 21.43,
|
|
1341
|
+
"logs": [
|
|
1342
|
+
"isolated listener failures :: 4",
|
|
1343
|
+
"finding: graph.finished is the only emit whose rejection is caught by the engine; a failing required listener elsewhere (e.g. node.failed) escapes into the node error path"
|
|
1344
|
+
]
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
"id": "I12",
|
|
1348
|
+
"kind": "integration",
|
|
1349
|
+
"category": "Lifecycle & plugins",
|
|
1350
|
+
"name": "plugins close in reverse order and shutdown failures are collected, not fatal",
|
|
1351
|
+
"status": "passed",
|
|
1352
|
+
"durationMs": 6.33,
|
|
1353
|
+
"logs": [
|
|
1354
|
+
"shutdown errors :: [\"Plugin \\\"telemetry\\\" shutdown failed: socket already closed\"]"
|
|
1355
|
+
]
|
|
1356
|
+
},
|
|
1357
|
+
{
|
|
1358
|
+
"id": "I13",
|
|
1359
|
+
"kind": "integration",
|
|
1360
|
+
"category": "Lifecycle & plugins",
|
|
1361
|
+
"name": "plugin shutdown is time-boxed by cancellationTimeoutMs",
|
|
1362
|
+
"status": "passed",
|
|
1363
|
+
"durationMs": 31.36,
|
|
1364
|
+
"logs": [
|
|
1365
|
+
"run duration :: \"31ms\"",
|
|
1366
|
+
"shutdown errors :: 1"
|
|
1367
|
+
]
|
|
1368
|
+
},
|
|
1369
|
+
{
|
|
1370
|
+
"id": "I14",
|
|
1371
|
+
"kind": "integration",
|
|
1372
|
+
"category": "Lifecycle & plugins",
|
|
1373
|
+
"name": "two concurrent executions keep ids, snapshots and results isolated",
|
|
1374
|
+
"status": "passed",
|
|
1375
|
+
"durationMs": 16.53,
|
|
1376
|
+
"logs": [
|
|
1377
|
+
"execution ids :: [\"35eddd7b\",\"d5609b14\"]"
|
|
1378
|
+
]
|
|
1379
|
+
},
|
|
1380
|
+
{
|
|
1381
|
+
"id": "I15",
|
|
1382
|
+
"kind": "integration",
|
|
1383
|
+
"category": "Failure semantics",
|
|
1384
|
+
"name": "checkpoint failure on the 2nd save blocks scheduling and freezes the last valid snapshot",
|
|
1385
|
+
"status": "passed",
|
|
1386
|
+
"durationMs": 15.28,
|
|
1387
|
+
"logs": [
|
|
1388
|
+
"checkpoint errors :: 2",
|
|
1389
|
+
"finding: the checkpoint failure surfaces twice per node (CHECKPOINT_SAVE_FAILED plus a NODE_EXECUTION_ERROR for the raw persistence error) and marks those nodes FAILED"
|
|
1390
|
+
]
|
|
1391
|
+
},
|
|
1392
|
+
{
|
|
1393
|
+
"id": "I16",
|
|
1394
|
+
"kind": "integration",
|
|
1395
|
+
"category": "Failure semantics",
|
|
1396
|
+
"name": "node timeout settles the graph while the abandoned node keeps running (cooperative abort)",
|
|
1397
|
+
"status": "passed",
|
|
1398
|
+
"durationMs": 240.69,
|
|
1399
|
+
"logs": [
|
|
1400
|
+
"run duration :: \"41ms (node keeps running for 150ms)\"",
|
|
1401
|
+
"abort reason :: \"TimeoutError\"",
|
|
1402
|
+
"finding: JavaScript cannot kill a running node; the engine reports TIMED_OUT and moves on"
|
|
1403
|
+
]
|
|
1404
|
+
},
|
|
1405
|
+
{
|
|
1406
|
+
"id": "I17",
|
|
1407
|
+
"kind": "integration",
|
|
1408
|
+
"category": "Failure semantics",
|
|
1409
|
+
"name": "policy matrix: RETRY loops, CONTINUE ignores maxAttempts, FALLBACK has no distinct path",
|
|
1410
|
+
"status": "passed",
|
|
1411
|
+
"durationMs": 21.88,
|
|
1412
|
+
"logs": [
|
|
1413
|
+
"(a) RETRY attempts :: 3",
|
|
1414
|
+
"(b) CONTINUE attempts :: 1",
|
|
1415
|
+
"(c) FALLBACK attempts :: 1",
|
|
1416
|
+
"finding: FALLBACK is accepted but treated as CONTINUE — no alternative-route mechanism exists"
|
|
1417
|
+
]
|
|
1418
|
+
},
|
|
1419
|
+
{
|
|
1420
|
+
"id": "I18",
|
|
1421
|
+
"kind": "integration",
|
|
1422
|
+
"category": "Failure semantics",
|
|
1423
|
+
"name": "final-state precedence: terminal reached + unhandled errors still yields FAILED",
|
|
1424
|
+
"status": "passed",
|
|
1425
|
+
"durationMs": 36.37,
|
|
1426
|
+
"logs": [
|
|
1427
|
+
"(1) dead-end status :: \"FAILED\"",
|
|
1428
|
+
"(2) logical completion :: true ; \"status\" ; \"FAILED\"",
|
|
1429
|
+
"(4) recovery dropped but error marked handled :: true",
|
|
1430
|
+
"finding: settlement order is cancellation > checkpoint failure > unhandled errors > logical completion; FAILED silently wins over a completed terminal",
|
|
1431
|
+
"finding: once any branch reaches a terminal node, pending activations (including ONTHROW recovery paths) are cleared and never executed, yet their errors stay marked handled"
|
|
1432
|
+
]
|
|
1433
|
+
},
|
|
1434
|
+
{
|
|
1435
|
+
"id": "I19",
|
|
1436
|
+
"kind": "integration",
|
|
1437
|
+
"category": "Failure semantics",
|
|
1438
|
+
"name": "policy resolution: every FAIL_FAST is sticky and halts the scheduler",
|
|
1439
|
+
"status": "passed",
|
|
1440
|
+
"durationMs": 53.47,
|
|
1441
|
+
"logs": [
|
|
1442
|
+
"finding: FAIL_FAST is sticky at both levels since v1.1 (A4) — the old node-level behaviour was a one-off queue flush that did nothing under unbounded concurrency; a node-level CONTINUE still cannot shield other branches from a graph-level FAIL_FAST",
|
|
1443
|
+
"(a) node override FAIL_FAST -> sibling successor dropped :: true",
|
|
1444
|
+
"(b) graph FAIL_FAST -> sibling successor dropped :: true",
|
|
1445
|
+
"(c) retry attempts under node override :: 2"
|
|
1446
|
+
]
|
|
1447
|
+
},
|
|
1448
|
+
{
|
|
1449
|
+
"id": "I20",
|
|
1450
|
+
"kind": "integration",
|
|
1451
|
+
"category": "Cancellation & cycles",
|
|
1452
|
+
"name": "cancelling a fan-out aborts in-flight nodes and never starts their successors",
|
|
1453
|
+
"status": "passed",
|
|
1454
|
+
"durationMs": 202.12,
|
|
1455
|
+
"logs": [
|
|
1456
|
+
"cancel() latency :: \"1ms\"",
|
|
1457
|
+
"run() drain time :: \"201ms (workers sleep 200ms)\"",
|
|
1458
|
+
"finding: cancel() returns immediately but the report waits for cooperative drain"
|
|
1459
|
+
]
|
|
1460
|
+
},
|
|
1461
|
+
{
|
|
1462
|
+
"id": "A1",
|
|
1463
|
+
"kind": "async",
|
|
1464
|
+
"category": "Node callbacks (async)",
|
|
1465
|
+
"name": "an async node callback is awaited and its resolved value becomes the output",
|
|
1466
|
+
"status": "passed",
|
|
1467
|
+
"durationMs": 10.64,
|
|
1468
|
+
"logs": [
|
|
1469
|
+
"finding: NodeContext signals are typed as AbortSignal even in older @types/node DOM-less libs, so async callbacks can consume context.signal without casting"
|
|
1470
|
+
]
|
|
1471
|
+
},
|
|
1472
|
+
{
|
|
1473
|
+
"id": "A2",
|
|
1474
|
+
"kind": "async",
|
|
1475
|
+
"category": "Node callbacks (async)",
|
|
1476
|
+
"name": "a non-async callback that returns a thenable is also awaited",
|
|
1477
|
+
"status": "passed",
|
|
1478
|
+
"durationMs": 5.65,
|
|
1479
|
+
"logs": []
|
|
1480
|
+
},
|
|
1481
|
+
{
|
|
1482
|
+
"id": "A3",
|
|
1483
|
+
"kind": "async",
|
|
1484
|
+
"category": "Node callbacks (async)",
|
|
1485
|
+
"name": "async callback with no return value produces an undefined output",
|
|
1486
|
+
"status": "passed",
|
|
1487
|
+
"durationMs": 5.56,
|
|
1488
|
+
"logs": []
|
|
1489
|
+
},
|
|
1490
|
+
{
|
|
1491
|
+
"id": "A4",
|
|
1492
|
+
"kind": "async",
|
|
1493
|
+
"category": "Node callbacks (async)",
|
|
1494
|
+
"name": "a rejected async callback is captured with code, stack and path",
|
|
1495
|
+
"status": "passed",
|
|
1496
|
+
"durationMs": 5.87,
|
|
1497
|
+
"logs": [
|
|
1498
|
+
"captured stack head :: \"Error: async-rejection\""
|
|
1499
|
+
]
|
|
1500
|
+
},
|
|
1501
|
+
{
|
|
1502
|
+
"id": "A5",
|
|
1503
|
+
"kind": "async",
|
|
1504
|
+
"category": "Node callbacks (async)",
|
|
1505
|
+
"name": "async rejections with AutomateGraphError preserve code and details",
|
|
1506
|
+
"status": "passed",
|
|
1507
|
+
"durationMs": 5.58,
|
|
1508
|
+
"logs": [
|
|
1509
|
+
"finding: TimeoutError is identified by class OR by a NODE_TIMEOUT-family code, so async rejections carrying that code are classified as TIMED_OUT"
|
|
1510
|
+
]
|
|
1511
|
+
},
|
|
1512
|
+
{
|
|
1513
|
+
"id": "A6",
|
|
1514
|
+
"kind": "async",
|
|
1515
|
+
"category": "Node callbacks (async)",
|
|
1516
|
+
"name": "async retry callback recovers on a later attempt inside one activation",
|
|
1517
|
+
"status": "passed",
|
|
1518
|
+
"durationMs": 26.59,
|
|
1519
|
+
"logs": []
|
|
1520
|
+
},
|
|
1521
|
+
{
|
|
1522
|
+
"id": "A7",
|
|
1523
|
+
"kind": "async",
|
|
1524
|
+
"category": "Node callbacks (async)",
|
|
1525
|
+
"name": "async callback can be retried by a custom node errorPolicy (RETRY)",
|
|
1526
|
+
"status": "passed",
|
|
1527
|
+
"durationMs": 8.06,
|
|
1528
|
+
"logs": []
|
|
1529
|
+
},
|
|
1530
|
+
{
|
|
1531
|
+
"id": "A8",
|
|
1532
|
+
"kind": "async",
|
|
1533
|
+
"category": "Node callbacks (async)",
|
|
1534
|
+
"name": "async rejection routes through ONTHROW into an async repair callback",
|
|
1535
|
+
"status": "passed",
|
|
1536
|
+
"durationMs": 11.06,
|
|
1537
|
+
"logs": []
|
|
1538
|
+
},
|
|
1539
|
+
{
|
|
1540
|
+
"id": "A9",
|
|
1541
|
+
"kind": "async",
|
|
1542
|
+
"category": "Node callbacks (async)",
|
|
1543
|
+
"name": "a slow async callback is preempted by timeoutMs and reported TIMED_OUT",
|
|
1544
|
+
"status": "passed",
|
|
1545
|
+
"durationMs": 165.49,
|
|
1546
|
+
"logs": [
|
|
1547
|
+
"run duration :: \"26ms (callback keeps running for 120ms)\"",
|
|
1548
|
+
"finding: on timeout the async callback's later resolution is discarded — no output is recorded"
|
|
1549
|
+
]
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
"id": "A10",
|
|
1553
|
+
"kind": "async",
|
|
1554
|
+
"category": "Node callbacks (async)",
|
|
1555
|
+
"name": "a cooperative async callback observes the abort and rejects; the run settles CANCELLED",
|
|
1556
|
+
"status": "passed",
|
|
1557
|
+
"durationMs": 16.63,
|
|
1558
|
+
"logs": [
|
|
1559
|
+
"graph status :: \"CANCELLED\" ; \"| node status\" ; \"CANCELLED\"",
|
|
1560
|
+
"error record :: \"GRAPH_CANCELLED handled=true\"",
|
|
1561
|
+
"finding: since v1.1 (A1) cooperative cancellation is symmetric — a callback that rejects because of the abort settles CANCELLED with a handled GRAPH_CANCELLED record, exactly like one that resolves after it; result.error now carries the record (A6)"
|
|
1562
|
+
]
|
|
1563
|
+
},
|
|
1564
|
+
{
|
|
1565
|
+
"id": "A11",
|
|
1566
|
+
"kind": "async",
|
|
1567
|
+
"category": "Node callbacks (async)",
|
|
1568
|
+
"name": "an async callback that ignores the signal delays settlement; successors stay unscheduled",
|
|
1569
|
+
"status": "passed",
|
|
1570
|
+
"durationMs": 60.17,
|
|
1571
|
+
"logs": [
|
|
1572
|
+
"drain time :: \"59ms (callback sleeps 60ms and ignores the abort)\""
|
|
1573
|
+
]
|
|
1574
|
+
},
|
|
1575
|
+
{
|
|
1576
|
+
"id": "A12",
|
|
1577
|
+
"kind": "async",
|
|
1578
|
+
"category": "Node callbacks (async)",
|
|
1579
|
+
"name": "async completion triggers exactly one checkpoint per successful node",
|
|
1580
|
+
"status": "passed",
|
|
1581
|
+
"durationMs": 16.28,
|
|
1582
|
+
"logs": []
|
|
1583
|
+
},
|
|
1584
|
+
{
|
|
1585
|
+
"id": "A13",
|
|
1586
|
+
"kind": "async",
|
|
1587
|
+
"category": "Node callbacks (async)",
|
|
1588
|
+
"name": "context.emit from an async callback is awaited and ordered",
|
|
1589
|
+
"status": "passed",
|
|
1590
|
+
"durationMs": 3.7,
|
|
1591
|
+
"logs": []
|
|
1592
|
+
},
|
|
1593
|
+
{
|
|
1594
|
+
"id": "A14",
|
|
1595
|
+
"kind": "async",
|
|
1596
|
+
"category": "Node callbacks (async)",
|
|
1597
|
+
"name": "async callbacks on independent branches execute in parallel",
|
|
1598
|
+
"status": "passed",
|
|
1599
|
+
"durationMs": 41.03,
|
|
1600
|
+
"logs": [
|
|
1601
|
+
"wall time :: \"41ms (serial would be ~160ms)\""
|
|
1602
|
+
]
|
|
1603
|
+
},
|
|
1604
|
+
{
|
|
1605
|
+
"id": "A15",
|
|
1606
|
+
"kind": "async",
|
|
1607
|
+
"category": "Node callbacks (async)",
|
|
1608
|
+
"name": "mixed sync and async nodes run together in one pipeline",
|
|
1609
|
+
"status": "passed",
|
|
1610
|
+
"durationMs": 11.09,
|
|
1611
|
+
"logs": []
|
|
1612
|
+
},
|
|
1613
|
+
{
|
|
1614
|
+
"id": "A16",
|
|
1615
|
+
"kind": "async",
|
|
1616
|
+
"category": "Node callbacks (async)",
|
|
1617
|
+
"name": "async callback composes success, retry and failure across a three-branch graph",
|
|
1618
|
+
"status": "passed",
|
|
1619
|
+
"durationMs": 12.39,
|
|
1620
|
+
"logs": [
|
|
1621
|
+
"fatal error path :: \"root/async-fatal\""
|
|
1622
|
+
]
|
|
1623
|
+
},
|
|
1624
|
+
{
|
|
1625
|
+
"id": "A17",
|
|
1626
|
+
"kind": "async",
|
|
1627
|
+
"category": "Node callbacks (async)",
|
|
1628
|
+
"name": "an async callback returning a marker drives a synchronous state-machine successor",
|
|
1629
|
+
"status": "passed",
|
|
1630
|
+
"durationMs": 5.7,
|
|
1631
|
+
"logs": []
|
|
1632
|
+
},
|
|
1633
|
+
{
|
|
1634
|
+
"id": "A18",
|
|
1635
|
+
"kind": "async",
|
|
1636
|
+
"category": "Node callbacks (async)",
|
|
1637
|
+
"name": "an unrelated execution started after an abandoned async node is unaffected",
|
|
1638
|
+
"status": "passed",
|
|
1639
|
+
"durationMs": 181.69,
|
|
1640
|
+
"logs": [
|
|
1641
|
+
"finding: the abandoned async node still performed its side effect after settlement — async cancellation is cooperative only; nothing can stop an in-flight promise"
|
|
1642
|
+
]
|
|
1643
|
+
},
|
|
1644
|
+
{
|
|
1645
|
+
"id": "A19",
|
|
1646
|
+
"kind": "async",
|
|
1647
|
+
"category": "Node callbacks (async)",
|
|
1648
|
+
"name": "diverse async callbacks (timers, microtasks, thenables) are classified correctly",
|
|
1649
|
+
"status": "passed",
|
|
1650
|
+
"durationMs": 3.24,
|
|
1651
|
+
"logs": []
|
|
1652
|
+
},
|
|
1653
|
+
{
|
|
1654
|
+
"id": "A20",
|
|
1655
|
+
"kind": "async",
|
|
1656
|
+
"category": "Node callbacks (async)",
|
|
1657
|
+
"name": "async callbacks integrate with plugins, storage snapshots and the execution report",
|
|
1658
|
+
"status": "passed",
|
|
1659
|
+
"durationMs": 23.22,
|
|
1660
|
+
"logs": [
|
|
1661
|
+
"checkpoints :: 3",
|
|
1662
|
+
"plugin lifecycle :: \"init -> close\""
|
|
1663
|
+
]
|
|
1664
|
+
},
|
|
1665
|
+
{
|
|
1666
|
+
"id": "U1",
|
|
1667
|
+
"kind": "upgrade",
|
|
1668
|
+
"category": "A2 · drain deadline",
|
|
1669
|
+
"name": "cancellationTimeoutMs bounds the drain and abandons in-flight work",
|
|
1670
|
+
"status": "passed",
|
|
1671
|
+
"durationMs": 481.07,
|
|
1672
|
+
"logs": [
|
|
1673
|
+
"run duration :: \"51ms (node sleeps 400ms)\"",
|
|
1674
|
+
"finding: without cancellationTimeoutMs the engine still waits forever (v1.0 behaviour); setting it converts an unbounded drain into a bounded, reported one"
|
|
1675
|
+
]
|
|
1676
|
+
},
|
|
1677
|
+
{
|
|
1678
|
+
"id": "U2",
|
|
1679
|
+
"kind": "upgrade",
|
|
1680
|
+
"category": "A2 · drain deadline",
|
|
1681
|
+
"name": "without cancellationTimeoutMs the run waits for cooperative drain",
|
|
1682
|
+
"status": "passed",
|
|
1683
|
+
"durationMs": 61.29,
|
|
1684
|
+
"logs": []
|
|
1685
|
+
},
|
|
1686
|
+
{
|
|
1687
|
+
"id": "U3",
|
|
1688
|
+
"kind": "upgrade",
|
|
1689
|
+
"category": "A3 · completion policy",
|
|
1690
|
+
"name": "ALL_TERMINALS waits for every declared terminal before latching completion",
|
|
1691
|
+
"status": "passed",
|
|
1692
|
+
"durationMs": 26.35,
|
|
1693
|
+
"logs": [
|
|
1694
|
+
"finding: ALL_TERMINALS gives fan-in style completion semantics without extra plumbing"
|
|
1695
|
+
]
|
|
1696
|
+
},
|
|
1697
|
+
{
|
|
1698
|
+
"id": "U4",
|
|
1699
|
+
"kind": "upgrade",
|
|
1700
|
+
"category": "A3 · completion policy",
|
|
1701
|
+
"name": "DRAIN keeps scheduling after a terminal, so ONTHROW recovery is no longer dropped",
|
|
1702
|
+
"status": "passed",
|
|
1703
|
+
"durationMs": 26.01,
|
|
1704
|
+
"logs": [
|
|
1705
|
+
"finding: DRAIN is the opt-in fix for the v1.0 silent-drop behaviour (finding C-15/C-19) — the run executes every reachable activation before settling"
|
|
1706
|
+
]
|
|
1707
|
+
},
|
|
1708
|
+
{
|
|
1709
|
+
"id": "U5",
|
|
1710
|
+
"kind": "upgrade",
|
|
1711
|
+
"category": "A3 · completion policy",
|
|
1712
|
+
"name": "FIRST_TERMINAL (default) drops pending work but now reports it via activation.dropped",
|
|
1713
|
+
"status": "passed",
|
|
1714
|
+
"durationMs": 25.88,
|
|
1715
|
+
"logs": [
|
|
1716
|
+
"finding: dropped work is now observable, so silent data loss in the default mode is detectable"
|
|
1717
|
+
]
|
|
1718
|
+
},
|
|
1719
|
+
{
|
|
1720
|
+
"id": "U6",
|
|
1721
|
+
"kind": "upgrade",
|
|
1722
|
+
"category": "A3 · completion policy",
|
|
1723
|
+
"name": "an unknown completionPolicy is rejected at construction time",
|
|
1724
|
+
"status": "passed",
|
|
1725
|
+
"durationMs": 0.17,
|
|
1726
|
+
"logs": []
|
|
1727
|
+
},
|
|
1728
|
+
{
|
|
1729
|
+
"id": "U7",
|
|
1730
|
+
"kind": "upgrade",
|
|
1731
|
+
"category": "A4/A5 · FAIL_FAST & FALLBACK",
|
|
1732
|
+
"name": "node-level FAIL_FAST is sticky and drops successors via activation.dropped",
|
|
1733
|
+
"status": "passed",
|
|
1734
|
+
"durationMs": 30.1,
|
|
1735
|
+
"logs": []
|
|
1736
|
+
},
|
|
1737
|
+
{
|
|
1738
|
+
"id": "U8",
|
|
1739
|
+
"kind": "upgrade",
|
|
1740
|
+
"category": "A4/A5 · FAIL_FAST & FALLBACK",
|
|
1741
|
+
"name": "FALLBACK routes to the fallback node and marks the error handled",
|
|
1742
|
+
"status": "passed",
|
|
1743
|
+
"durationMs": 0.43,
|
|
1744
|
+
"logs": []
|
|
1745
|
+
},
|
|
1746
|
+
{
|
|
1747
|
+
"id": "U9",
|
|
1748
|
+
"kind": "upgrade",
|
|
1749
|
+
"category": "A4/A5 · FAIL_FAST & FALLBACK",
|
|
1750
|
+
"name": "a fallback target is ignored unless the effective policy is FALLBACK",
|
|
1751
|
+
"status": "passed",
|
|
1752
|
+
"durationMs": 0.25,
|
|
1753
|
+
"logs": []
|
|
1754
|
+
},
|
|
1755
|
+
{
|
|
1756
|
+
"id": "U10",
|
|
1757
|
+
"kind": "upgrade",
|
|
1758
|
+
"category": "A4/A5 · FAIL_FAST & FALLBACK",
|
|
1759
|
+
"name": "an unknown fallback target is rejected at construction time",
|
|
1760
|
+
"status": "passed",
|
|
1761
|
+
"durationMs": 0.13,
|
|
1762
|
+
"logs": []
|
|
1763
|
+
},
|
|
1764
|
+
{
|
|
1765
|
+
"id": "U11",
|
|
1766
|
+
"kind": "upgrade",
|
|
1767
|
+
"category": "A6/A10 · results & hygiene",
|
|
1768
|
+
"name": "NodeResult.error carries the failure record and matches the report errors",
|
|
1769
|
+
"status": "passed",
|
|
1770
|
+
"durationMs": 0.18,
|
|
1771
|
+
"logs": []
|
|
1772
|
+
},
|
|
1773
|
+
{
|
|
1774
|
+
"id": "U12",
|
|
1775
|
+
"kind": "upgrade",
|
|
1776
|
+
"category": "A6/A10 · results & hygiene",
|
|
1777
|
+
"name": "a timed-out node exposes NODE_TIMEOUT on its own result",
|
|
1778
|
+
"status": "passed",
|
|
1779
|
+
"durationMs": 116.21,
|
|
1780
|
+
"logs": []
|
|
1781
|
+
},
|
|
1782
|
+
{
|
|
1783
|
+
"id": "U13",
|
|
1784
|
+
"kind": "upgrade",
|
|
1785
|
+
"category": "A6/A10 · results & hygiene",
|
|
1786
|
+
"name": "in-flight activation tracking is empty before and after a run",
|
|
1787
|
+
"status": "passed",
|
|
1788
|
+
"durationMs": 30.98,
|
|
1789
|
+
"logs": []
|
|
1790
|
+
},
|
|
1791
|
+
{
|
|
1792
|
+
"id": "U14",
|
|
1793
|
+
"kind": "upgrade",
|
|
1794
|
+
"category": "A8 · concurrency & budget",
|
|
1795
|
+
"name": "maxConcurrency bounds parallelism and preserves completion",
|
|
1796
|
+
"status": "passed",
|
|
1797
|
+
"durationMs": 28.62,
|
|
1798
|
+
"logs": [
|
|
1799
|
+
"bounded :: 2 ; \"| unbounded default\" ; 8"
|
|
1800
|
+
]
|
|
1801
|
+
},
|
|
1802
|
+
{
|
|
1803
|
+
"id": "U15",
|
|
1804
|
+
"kind": "upgrade",
|
|
1805
|
+
"category": "A8 · concurrency & budget",
|
|
1806
|
+
"name": "maxActivations stops a runaway cycle with STEP_BUDGET_EXCEEDED",
|
|
1807
|
+
"status": "passed",
|
|
1808
|
+
"durationMs": 0.59,
|
|
1809
|
+
"logs": [
|
|
1810
|
+
"activations executed :: 6 ; \"| duration\" ; \"0ms\""
|
|
1811
|
+
]
|
|
1812
|
+
},
|
|
1813
|
+
{
|
|
1814
|
+
"id": "U16",
|
|
1815
|
+
"kind": "upgrade",
|
|
1816
|
+
"category": "A8 · concurrency & budget",
|
|
1817
|
+
"name": "invalid concurrency and budget options are rejected",
|
|
1818
|
+
"status": "passed",
|
|
1819
|
+
"durationMs": 0.2,
|
|
1820
|
+
"logs": []
|
|
1821
|
+
},
|
|
1822
|
+
{
|
|
1823
|
+
"id": "U17",
|
|
1824
|
+
"kind": "upgrade",
|
|
1825
|
+
"category": "A9 · graph linting",
|
|
1826
|
+
"name": "lintGraph reports unreachable nodes, unreachable terminals, dead ends and duplicates",
|
|
1827
|
+
"status": "passed",
|
|
1828
|
+
"durationMs": 0.43,
|
|
1829
|
+
"logs": []
|
|
1830
|
+
},
|
|
1831
|
+
{
|
|
1832
|
+
"id": "U18",
|
|
1833
|
+
"kind": "upgrade",
|
|
1834
|
+
"category": "A9 · graph linting",
|
|
1835
|
+
"name": "lint: \"warn\" logs issues, lint: \"error\" rejects only on error-level issues",
|
|
1836
|
+
"status": "passed",
|
|
1837
|
+
"durationMs": 0.63,
|
|
1838
|
+
"logs": [
|
|
1839
|
+
"lint issues in the rejected report :: \"DEAD_END_NODE\""
|
|
1840
|
+
]
|
|
1841
|
+
},
|
|
1842
|
+
{
|
|
1843
|
+
"id": "U19",
|
|
1844
|
+
"kind": "upgrade",
|
|
1845
|
+
"category": "Isomorphic build",
|
|
1846
|
+
"name": "the engine core compiles with DOM-only libs and no Node types (browser-safe)",
|
|
1847
|
+
"status": "passed",
|
|
1848
|
+
"durationMs": 1292.28,
|
|
1849
|
+
"logs": [
|
|
1850
|
+
"finding: the engine core is isomorphic — no Node APIs, no Buffers, timers via setTimeout only"
|
|
1851
|
+
]
|
|
1852
|
+
},
|
|
1853
|
+
{
|
|
1854
|
+
"id": "C1",
|
|
1855
|
+
"kind": "conformance",
|
|
1856
|
+
"category": "Storage adapter contract",
|
|
1857
|
+
"name": "InMemoryStorage passes the full conformance suite with zero violations",
|
|
1858
|
+
"status": "passed",
|
|
1859
|
+
"durationMs": 1.84,
|
|
1860
|
+
"logs": [
|
|
1861
|
+
"checks executed :: 12",
|
|
1862
|
+
"skipped :: 1 ; [\"FAILED_SAVE_PRESERVES_PREVIOUS (fixture provides no failNextSave)\"]"
|
|
1863
|
+
]
|
|
1864
|
+
},
|
|
1865
|
+
{
|
|
1866
|
+
"id": "C2",
|
|
1867
|
+
"kind": "conformance",
|
|
1868
|
+
"category": "Storage adapter contract",
|
|
1869
|
+
"name": "an adapter with fault injection proves atomicity instead of skipping it",
|
|
1870
|
+
"status": "passed",
|
|
1871
|
+
"durationMs": 0.52,
|
|
1872
|
+
"logs": [
|
|
1873
|
+
"checks executed :: 12",
|
|
1874
|
+
"skipped :: []"
|
|
1875
|
+
]
|
|
1876
|
+
},
|
|
1877
|
+
{
|
|
1878
|
+
"id": "C3",
|
|
1879
|
+
"kind": "conformance",
|
|
1880
|
+
"category": "Storage adapter contract",
|
|
1881
|
+
"name": "the checker catches aliasing, broken listing and broken deletion",
|
|
1882
|
+
"status": "passed",
|
|
1883
|
+
"durationMs": 0.41,
|
|
1884
|
+
"logs": [
|
|
1885
|
+
"violations detected :: \"NO_ALIASING_ON_SAVE, NO_ALIASING_ON_SAVE, NO_ALIASING_ON_LOAD, NO_ALIASING_ON_LOAD, LIST, LIST, LIST, DELETE_IDEMPOTENT, DELETE_IDEMPOTENT\""
|
|
1886
|
+
]
|
|
1887
|
+
},
|
|
1888
|
+
{
|
|
1889
|
+
"id": "C4",
|
|
1890
|
+
"kind": "conformance",
|
|
1891
|
+
"category": "Storage adapter contract",
|
|
1892
|
+
"name": "the checker catches a torn write that commits before failing",
|
|
1893
|
+
"status": "passed",
|
|
1894
|
+
"durationMs": 0.68,
|
|
1895
|
+
"logs": [
|
|
1896
|
+
"finding: the conformance suite distinguishes 'rejects' from 'rejects without corrupting state' — adapters must checkpoint before commit, not after"
|
|
1897
|
+
]
|
|
1898
|
+
},
|
|
1899
|
+
{
|
|
1900
|
+
"id": "C5",
|
|
1901
|
+
"kind": "conformance",
|
|
1902
|
+
"category": "Storage adapter contract",
|
|
1903
|
+
"name": "missing required members are reported as SHAPE violations",
|
|
1904
|
+
"status": "passed",
|
|
1905
|
+
"durationMs": 0.12,
|
|
1906
|
+
"logs": []
|
|
1907
|
+
},
|
|
1908
|
+
{
|
|
1909
|
+
"id": "C6",
|
|
1910
|
+
"kind": "conformance",
|
|
1911
|
+
"category": "Storage adapter contract",
|
|
1912
|
+
"name": "an adapter that claims a capability it does not implement is rejected (defineStorageAdapter)",
|
|
1913
|
+
"status": "passed",
|
|
1914
|
+
"durationMs": 0.11,
|
|
1915
|
+
"logs": []
|
|
1916
|
+
},
|
|
1917
|
+
{
|
|
1918
|
+
"id": "C7",
|
|
1919
|
+
"kind": "conformance",
|
|
1920
|
+
"category": "Storage adapter contract",
|
|
1921
|
+
"name": "the engine accepts any conformant adapter as its storage plugin",
|
|
1922
|
+
"status": "passed",
|
|
1923
|
+
"durationMs": 0.46,
|
|
1924
|
+
"logs": [
|
|
1925
|
+
"snapshots stored :: 1"
|
|
1926
|
+
]
|
|
1927
|
+
},
|
|
1928
|
+
{
|
|
1929
|
+
"id": "C8",
|
|
1930
|
+
"kind": "conformance",
|
|
1931
|
+
"category": "Snapshot wire format",
|
|
1932
|
+
"name": "serializeSnapshot is deterministic and round-trips through JSON",
|
|
1933
|
+
"status": "passed",
|
|
1934
|
+
"durationMs": 0.17,
|
|
1935
|
+
"logs": []
|
|
1936
|
+
},
|
|
1937
|
+
{
|
|
1938
|
+
"id": "C9",
|
|
1939
|
+
"kind": "conformance",
|
|
1940
|
+
"category": "Snapshot wire format",
|
|
1941
|
+
"name": "corrupt or unsupported snapshots are rejected with typed errors",
|
|
1942
|
+
"status": "passed",
|
|
1943
|
+
"durationMs": 0.15,
|
|
1944
|
+
"logs": []
|
|
1945
|
+
},
|
|
1946
|
+
{
|
|
1947
|
+
"id": "C10",
|
|
1948
|
+
"kind": "conformance",
|
|
1949
|
+
"category": "Snapshot wire format",
|
|
1950
|
+
"name": "snapshotShapeViolations pinpoints every malformed field",
|
|
1951
|
+
"status": "passed",
|
|
1952
|
+
"durationMs": 0.09,
|
|
1953
|
+
"logs": []
|
|
1954
|
+
},
|
|
1955
|
+
{
|
|
1956
|
+
"id": "C11",
|
|
1957
|
+
"kind": "conformance",
|
|
1958
|
+
"category": "Snapshot wire format",
|
|
1959
|
+
"name": "compareSnapshots reports structural differences precisely",
|
|
1960
|
+
"status": "passed",
|
|
1961
|
+
"durationMs": 0.15,
|
|
1962
|
+
"logs": []
|
|
1963
|
+
},
|
|
1964
|
+
{
|
|
1965
|
+
"id": "L1",
|
|
1966
|
+
"kind": "lint",
|
|
1967
|
+
"category": "Success dead-end rule",
|
|
1968
|
+
"name": "the exact C-19 fixture is flagged: slow-vendor succeeds into a void",
|
|
1969
|
+
"status": "passed",
|
|
1970
|
+
"durationMs": 0.52,
|
|
1971
|
+
"logs": [
|
|
1972
|
+
"issue :: \"error/SUCCESS_DEAD_END @ slow-vendor\"",
|
|
1973
|
+
"finding: a node can declare onThrow and still be unreachable on its success path — the previous DEAD_END_NODE rule only asked whether *any* transition existed"
|
|
1974
|
+
]
|
|
1975
|
+
},
|
|
1976
|
+
{
|
|
1977
|
+
"id": "L2",
|
|
1978
|
+
"kind": "lint",
|
|
1979
|
+
"category": "Success dead-end rule",
|
|
1980
|
+
"name": "the fix suggested by the message clears the issue (next declared)",
|
|
1981
|
+
"status": "passed",
|
|
1982
|
+
"durationMs": 0.13,
|
|
1983
|
+
"logs": [
|
|
1984
|
+
"slow-vendor issues after adding next :: 0"
|
|
1985
|
+
]
|
|
1986
|
+
},
|
|
1987
|
+
{
|
|
1988
|
+
"id": "L3",
|
|
1989
|
+
"kind": "lint",
|
|
1990
|
+
"category": "Success dead-end rule",
|
|
1991
|
+
"name": "declared terminal nodes are never flagged, even without next",
|
|
1992
|
+
"status": "passed",
|
|
1993
|
+
"durationMs": 0.08,
|
|
1994
|
+
"logs": []
|
|
1995
|
+
},
|
|
1996
|
+
{
|
|
1997
|
+
"id": "L4",
|
|
1998
|
+
"kind": "lint",
|
|
1999
|
+
"category": "Success dead-end rule",
|
|
2000
|
+
"name": "a node with no transitions at all is DEAD_END_NODE, not SUCCESS_DEAD_END",
|
|
2001
|
+
"status": "passed",
|
|
2002
|
+
"durationMs": 0.07,
|
|
2003
|
+
"logs": []
|
|
2004
|
+
},
|
|
2005
|
+
{
|
|
2006
|
+
"id": "L5",
|
|
2007
|
+
"kind": "lint",
|
|
2008
|
+
"category": "Success dead-end rule",
|
|
2009
|
+
"name": "an ignored fallback does not count as a live error transition",
|
|
2010
|
+
"status": "passed",
|
|
2011
|
+
"durationMs": 0.1,
|
|
2012
|
+
"logs": []
|
|
2013
|
+
},
|
|
2014
|
+
{
|
|
2015
|
+
"id": "L6",
|
|
2016
|
+
"kind": "lint",
|
|
2017
|
+
"category": "Success dead-end rule",
|
|
2018
|
+
"name": "a live fallback (policy FALLBACK) makes it SUCCESS_DEAD_END, not a total dead end",
|
|
2019
|
+
"status": "passed",
|
|
2020
|
+
"durationMs": 0.09,
|
|
2021
|
+
"logs": []
|
|
2022
|
+
},
|
|
2023
|
+
{
|
|
2024
|
+
"id": "L7",
|
|
2025
|
+
"kind": "lint",
|
|
2026
|
+
"category": "Success dead-end rule",
|
|
2027
|
+
"name": "onThrow alone is treated as a live error transition",
|
|
2028
|
+
"status": "passed",
|
|
2029
|
+
"durationMs": 0.06,
|
|
2030
|
+
"logs": []
|
|
2031
|
+
},
|
|
2032
|
+
{
|
|
2033
|
+
"id": "L8",
|
|
2034
|
+
"kind": "lint",
|
|
2035
|
+
"category": "Success dead-end rule",
|
|
2036
|
+
"name": "unreachable nodes with a success path stay unreachable-only (no noise)",
|
|
2037
|
+
"status": "passed",
|
|
2038
|
+
"durationMs": 0.06,
|
|
2039
|
+
"logs": []
|
|
2040
|
+
},
|
|
2041
|
+
{
|
|
2042
|
+
"id": "L9",
|
|
2043
|
+
"kind": "lint",
|
|
2044
|
+
"category": "Success dead-end rule",
|
|
2045
|
+
"name": "every issue has a stable shape and a known code",
|
|
2046
|
+
"status": "passed",
|
|
2047
|
+
"durationMs": 0.18,
|
|
2048
|
+
"logs": [
|
|
2049
|
+
"codes observed :: \"DUPLICATE_ENTRY, SUCCESS_DEAD_END, DEAD_END_NODE\""
|
|
2050
|
+
]
|
|
2051
|
+
},
|
|
2052
|
+
{
|
|
2053
|
+
"id": "L10",
|
|
2054
|
+
"kind": "lint",
|
|
2055
|
+
"category": "Success dead-end rule",
|
|
2056
|
+
"name": "lint: \"warn\" logs SUCCESS_DEAD_END and still completes the C-19 run",
|
|
2057
|
+
"status": "passed",
|
|
2058
|
+
"durationMs": 26.19,
|
|
2059
|
+
"logs": [
|
|
2060
|
+
"run status :: \"COMPLETED\" ; \"| issue logged at severity 'error' but not enforced in warn mode\""
|
|
2061
|
+
]
|
|
2062
|
+
},
|
|
2063
|
+
{
|
|
2064
|
+
"id": "L11",
|
|
2065
|
+
"kind": "lint",
|
|
2066
|
+
"category": "Success dead-end rule",
|
|
2067
|
+
"name": "lint: \"error\" rejects the C-19 graph before any node runs",
|
|
2068
|
+
"status": "passed",
|
|
2069
|
+
"durationMs": 0.33,
|
|
2070
|
+
"logs": [
|
|
2071
|
+
"finding: lint enforcement is a pre-flight gate — the graph is rejected before initialization finishes, so no side effects from node callbacks can occur"
|
|
2072
|
+
]
|
|
2073
|
+
},
|
|
2074
|
+
{
|
|
2075
|
+
"id": "L12",
|
|
2076
|
+
"kind": "lint",
|
|
2077
|
+
"category": "Success dead-end rule",
|
|
2078
|
+
"name": "the C-01 order-pipeline fixture flags audit as DEAD_END_NODE (documented gotcha)",
|
|
2079
|
+
"status": "passed",
|
|
2080
|
+
"durationMs": 0.41,
|
|
2081
|
+
"logs": [
|
|
2082
|
+
"audit :: \"error/DEAD_END_NODE\" ; \"| run\" ; \"COMPLETED\""
|
|
2083
|
+
]
|
|
2084
|
+
},
|
|
2085
|
+
{
|
|
2086
|
+
"id": "L13",
|
|
2087
|
+
"kind": "lint",
|
|
2088
|
+
"category": "Success dead-end rule",
|
|
2089
|
+
"name": "a graph with no transition problems lints clean and runs",
|
|
2090
|
+
"status": "passed",
|
|
2091
|
+
"durationMs": 0.25,
|
|
2092
|
+
"logs": []
|
|
2093
|
+
},
|
|
2094
|
+
{
|
|
2095
|
+
"id": "E1",
|
|
2096
|
+
"kind": "edges",
|
|
2097
|
+
"category": "Typed edges & routing",
|
|
2098
|
+
"name": "string edges keep the v1.0 meaning: unconditional fan-out",
|
|
2099
|
+
"status": "passed",
|
|
2100
|
+
"durationMs": 0.24,
|
|
2101
|
+
"logs": [
|
|
2102
|
+
"finding: `next: [\"b\",\"c\"]` normalizes to two parallel edges — every existing graph keeps its behaviour"
|
|
2103
|
+
]
|
|
2104
|
+
},
|
|
2105
|
+
{
|
|
2106
|
+
"id": "E2",
|
|
2107
|
+
"kind": "edges",
|
|
2108
|
+
"category": "Typed edges & routing",
|
|
2109
|
+
"name": "result matches by deep equality and only the first matching edge fires",
|
|
2110
|
+
"status": "passed",
|
|
2111
|
+
"durationMs": 0.41,
|
|
2112
|
+
"logs": []
|
|
2113
|
+
},
|
|
2114
|
+
{
|
|
2115
|
+
"id": "E3",
|
|
2116
|
+
"kind": "edges",
|
|
2117
|
+
"category": "Typed edges & routing",
|
|
2118
|
+
"name": "deep equality matches nested objects regardless of key order",
|
|
2119
|
+
"status": "passed",
|
|
2120
|
+
"durationMs": 0.23,
|
|
2121
|
+
"logs": [
|
|
2122
|
+
"finding: result uses structural equality (key order, nesting and array order aware)"
|
|
2123
|
+
]
|
|
2124
|
+
},
|
|
2125
|
+
{
|
|
2126
|
+
"id": "E4",
|
|
2127
|
+
"kind": "edges",
|
|
2128
|
+
"category": "Typed edges & routing",
|
|
2129
|
+
"name": "result: NaN matches a NaN output (Object.is semantics)",
|
|
2130
|
+
"status": "passed",
|
|
2131
|
+
"durationMs": 0.22,
|
|
2132
|
+
"logs": []
|
|
2133
|
+
},
|
|
2134
|
+
{
|
|
2135
|
+
"id": "E5",
|
|
2136
|
+
"kind": "edges",
|
|
2137
|
+
"category": "Typed edges & routing",
|
|
2138
|
+
"name": "result: undefined matches an undefined output (presence, not truthiness)",
|
|
2139
|
+
"status": "passed",
|
|
2140
|
+
"durationMs": 0.21,
|
|
2141
|
+
"logs": [
|
|
2142
|
+
"finding: hasResult tracks key presence, so an undefined output is matchable"
|
|
2143
|
+
]
|
|
2144
|
+
},
|
|
2145
|
+
{
|
|
2146
|
+
"id": "E6",
|
|
2147
|
+
"kind": "edges",
|
|
2148
|
+
"category": "Typed edges & routing",
|
|
2149
|
+
"name": "test predicate routes the activation",
|
|
2150
|
+
"status": "passed",
|
|
2151
|
+
"durationMs": 0.18,
|
|
2152
|
+
"logs": []
|
|
2153
|
+
},
|
|
2154
|
+
{
|
|
2155
|
+
"id": "E7",
|
|
2156
|
+
"kind": "edges",
|
|
2157
|
+
"category": "Typed edges & routing",
|
|
2158
|
+
"name": "two matching conditionals: declaration order wins",
|
|
2159
|
+
"status": "passed",
|
|
2160
|
+
"durationMs": 0.17,
|
|
2161
|
+
"logs": []
|
|
2162
|
+
},
|
|
2163
|
+
{
|
|
2164
|
+
"id": "E8",
|
|
2165
|
+
"kind": "edges",
|
|
2166
|
+
"category": "Typed edges & routing",
|
|
2167
|
+
"name": "result + test on one edge must both pass (AND)",
|
|
2168
|
+
"status": "passed",
|
|
2169
|
+
"durationMs": 0.23,
|
|
2170
|
+
"logs": []
|
|
2171
|
+
},
|
|
2172
|
+
{
|
|
2173
|
+
"id": "E9",
|
|
2174
|
+
"kind": "edges",
|
|
2175
|
+
"category": "Typed edges & routing",
|
|
2176
|
+
"name": "the else edge (no matcher) fires when nothing earlier matched",
|
|
2177
|
+
"status": "passed",
|
|
2178
|
+
"durationMs": 0.19,
|
|
2179
|
+
"logs": []
|
|
2180
|
+
},
|
|
2181
|
+
{
|
|
2182
|
+
"id": "E10",
|
|
2183
|
+
"kind": "edges",
|
|
2184
|
+
"category": "Typed edges & routing",
|
|
2185
|
+
"name": "no match is silent: nothing is scheduled, no routing error is recorded",
|
|
2186
|
+
"status": "passed",
|
|
2187
|
+
"durationMs": 0.27,
|
|
2188
|
+
"logs": [
|
|
2189
|
+
"finding: an unmatched conditional chain is silent by design (no NO_EDGE_MATCHED error)"
|
|
2190
|
+
]
|
|
2191
|
+
},
|
|
2192
|
+
{
|
|
2193
|
+
"id": "E11",
|
|
2194
|
+
"kind": "edges",
|
|
2195
|
+
"category": "Typed edges & routing",
|
|
2196
|
+
"name": "parallel edges fire alongside the winning conditional",
|
|
2197
|
+
"status": "passed",
|
|
2198
|
+
"durationMs": 0.22,
|
|
2199
|
+
"logs": []
|
|
2200
|
+
},
|
|
2201
|
+
{
|
|
2202
|
+
"id": "E12",
|
|
2203
|
+
"kind": "edges",
|
|
2204
|
+
"category": "Typed edges & routing",
|
|
2205
|
+
"name": "a parallel edge is never blocked, even after a conditional match",
|
|
2206
|
+
"status": "passed",
|
|
2207
|
+
"durationMs": 0.24,
|
|
2208
|
+
"logs": []
|
|
2209
|
+
},
|
|
2210
|
+
{
|
|
2211
|
+
"id": "E13",
|
|
2212
|
+
"kind": "edges",
|
|
2213
|
+
"category": "Typed edges & routing",
|
|
2214
|
+
"name": "a parallel edge with a matcher is still unconditional (matcher ignored, lint warns)",
|
|
2215
|
+
"status": "passed",
|
|
2216
|
+
"durationMs": 0.25,
|
|
2217
|
+
"logs": []
|
|
2218
|
+
},
|
|
2219
|
+
{
|
|
2220
|
+
"id": "E14",
|
|
2221
|
+
"kind": "edges",
|
|
2222
|
+
"category": "Typed edges & routing",
|
|
2223
|
+
"name": "returning new Next(target, value) routes and successors see the unwrapped value",
|
|
2224
|
+
"status": "passed",
|
|
2225
|
+
"durationMs": 0.25,
|
|
2226
|
+
"logs": [
|
|
2227
|
+
"finding: Next carries both destination and payload — successors and matchers see the payload"
|
|
2228
|
+
]
|
|
2229
|
+
},
|
|
2230
|
+
{
|
|
2231
|
+
"id": "E15",
|
|
2232
|
+
"kind": "edges",
|
|
2233
|
+
"category": "Typed edges & routing",
|
|
2234
|
+
"name": "context.goto(target) + return value routes for void/imperative nodes",
|
|
2235
|
+
"status": "passed",
|
|
2236
|
+
"durationMs": 0.16,
|
|
2237
|
+
"logs": []
|
|
2238
|
+
},
|
|
2239
|
+
{
|
|
2240
|
+
"id": "E16",
|
|
2241
|
+
"kind": "edges",
|
|
2242
|
+
"category": "Typed edges & routing",
|
|
2243
|
+
"name": "a void node can route with goto alone",
|
|
2244
|
+
"status": "passed",
|
|
2245
|
+
"durationMs": 0.2,
|
|
2246
|
+
"logs": []
|
|
2247
|
+
},
|
|
2248
|
+
{
|
|
2249
|
+
"id": "E17",
|
|
2250
|
+
"kind": "edges",
|
|
2251
|
+
"category": "Typed edges & routing",
|
|
2252
|
+
"name": "an explicit route replaces the whole edge list, parallel edges included",
|
|
2253
|
+
"status": "passed",
|
|
2254
|
+
"durationMs": 0.2,
|
|
2255
|
+
"logs": [
|
|
2256
|
+
"finding: an explicit route is authoritative — declared edges (and parallel ones) are ignored"
|
|
2257
|
+
]
|
|
2258
|
+
},
|
|
2259
|
+
{
|
|
2260
|
+
"id": "E18",
|
|
2261
|
+
"kind": "edges",
|
|
2262
|
+
"category": "Typed edges & routing",
|
|
2263
|
+
"name": "return new Next wins over context.goto when both are used",
|
|
2264
|
+
"status": "passed",
|
|
2265
|
+
"durationMs": 0.15,
|
|
2266
|
+
"logs": []
|
|
2267
|
+
},
|
|
2268
|
+
{
|
|
2269
|
+
"id": "E19",
|
|
2270
|
+
"kind": "edges",
|
|
2271
|
+
"category": "Typed edges & routing",
|
|
2272
|
+
"name": "goto works per attempt: the route is taken by the successful attempt",
|
|
2273
|
+
"status": "passed",
|
|
2274
|
+
"durationMs": 5.6,
|
|
2275
|
+
"logs": []
|
|
2276
|
+
},
|
|
2277
|
+
{
|
|
2278
|
+
"id": "E20",
|
|
2279
|
+
"kind": "edges",
|
|
2280
|
+
"category": "Typed edges & routing",
|
|
2281
|
+
"name": "routing to an unknown node records INVALID_GOTO_TARGET and schedules nothing",
|
|
2282
|
+
"status": "passed",
|
|
2283
|
+
"durationMs": 0.45,
|
|
2284
|
+
"logs": []
|
|
2285
|
+
},
|
|
2286
|
+
{
|
|
2287
|
+
"id": "E21",
|
|
2288
|
+
"kind": "edges",
|
|
2289
|
+
"category": "Typed edges & routing",
|
|
2290
|
+
"name": "new Next with an empty target throws INVALID_NEXT_TARGET immediately",
|
|
2291
|
+
"status": "passed",
|
|
2292
|
+
"durationMs": 0.06,
|
|
2293
|
+
"logs": []
|
|
2294
|
+
},
|
|
2295
|
+
{
|
|
2296
|
+
"id": "E22",
|
|
2297
|
+
"kind": "edges",
|
|
2298
|
+
"category": "Typed edges & routing",
|
|
2299
|
+
"name": "a terminal node ignores its edges and its routing, and lint warns about it",
|
|
2300
|
+
"status": "passed",
|
|
2301
|
+
"durationMs": 0.22,
|
|
2302
|
+
"logs": [
|
|
2303
|
+
"finding: terminal detection happens before routing — a terminal node never schedules successors"
|
|
2304
|
+
]
|
|
2305
|
+
},
|
|
2306
|
+
{
|
|
2307
|
+
"id": "E23",
|
|
2308
|
+
"kind": "edges",
|
|
2309
|
+
"category": "Typed edges & routing",
|
|
2310
|
+
"name": "lint flags an exclusive edge declared after an else edge as unreachable",
|
|
2311
|
+
"status": "passed",
|
|
2312
|
+
"durationMs": 0.34,
|
|
2313
|
+
"logs": [
|
|
2314
|
+
"finding: an else edge shadows every exclusive edge declared after it — runtime agrees with lint"
|
|
2315
|
+
]
|
|
2316
|
+
},
|
|
2317
|
+
{
|
|
2318
|
+
"id": "E24",
|
|
2319
|
+
"kind": "edges",
|
|
2320
|
+
"category": "Typed edges & routing",
|
|
2321
|
+
"name": "malformed edge entries are rejected at construction time",
|
|
2322
|
+
"status": "passed",
|
|
2323
|
+
"durationMs": 0.41,
|
|
2324
|
+
"logs": []
|
|
2325
|
+
},
|
|
2326
|
+
{
|
|
2327
|
+
"id": "E25",
|
|
2328
|
+
"kind": "edges",
|
|
2329
|
+
"category": "Typed edges & routing",
|
|
2330
|
+
"name": "the a → b/c/d/e/f picture runs as ONE execution — the router is no longer needed",
|
|
2331
|
+
"status": "passed",
|
|
2332
|
+
"durationMs": 11.8,
|
|
2333
|
+
"logs": [
|
|
2334
|
+
"seed 1 :: \"a,b,f (attempts 1)\"",
|
|
2335
|
+
"seed 2 :: \"a,c (attempts 1)\"",
|
|
2336
|
+
"seed 3 :: \"a,d (attempts 1)\"",
|
|
2337
|
+
"seed \"throw\" :: \"a,e (attempts 2)\"",
|
|
2338
|
+
"seed \"retry\" :: \"a,f (attempts 2)\"",
|
|
2339
|
+
"finding: with typed edges the whole picture is ONE execution and lint-clean — no router, no gate, no DRAIN policy needed"
|
|
2340
|
+
]
|
|
2341
|
+
},
|
|
2342
|
+
{
|
|
2343
|
+
"id": "E26",
|
|
2344
|
+
"kind": "edges",
|
|
2345
|
+
"category": "Typed edges & routing",
|
|
2346
|
+
"name": "lint is clean for the payoff graph (routes declared, no dead edges)",
|
|
2347
|
+
"status": "passed",
|
|
2348
|
+
"durationMs": 0.44,
|
|
2349
|
+
"logs": [
|
|
2350
|
+
"graph linted clean and ran under lint: \"error\""
|
|
2351
|
+
]
|
|
2352
|
+
},
|
|
2353
|
+
{
|
|
2354
|
+
"id": "R1",
|
|
2355
|
+
"kind": "registry",
|
|
2356
|
+
"category": "Function registry",
|
|
2357
|
+
"name": "a node whose run is a registered NAME executes that function",
|
|
2358
|
+
"status": "passed",
|
|
2359
|
+
"durationMs": 0.67,
|
|
2360
|
+
"logs": [
|
|
2361
|
+
"finding: report.results[i].fn — not report.results[i].output — is the audit link to the registered source"
|
|
2362
|
+
]
|
|
2363
|
+
},
|
|
2364
|
+
{
|
|
2365
|
+
"id": "R2",
|
|
2366
|
+
"kind": "registry",
|
|
2367
|
+
"category": "Function registry",
|
|
2368
|
+
"name": "a graph-local function overrides the engine's shared entry",
|
|
2369
|
+
"status": "passed",
|
|
2370
|
+
"durationMs": 0.45,
|
|
2371
|
+
"logs": []
|
|
2372
|
+
},
|
|
2373
|
+
{
|
|
2374
|
+
"id": "R3",
|
|
2375
|
+
"kind": "registry",
|
|
2376
|
+
"category": "Function registry",
|
|
2377
|
+
"name": "graph-local functions work with no engine registry at all",
|
|
2378
|
+
"status": "passed",
|
|
2379
|
+
"durationMs": 0.2,
|
|
2380
|
+
"logs": []
|
|
2381
|
+
},
|
|
2382
|
+
{
|
|
2383
|
+
"id": "R4",
|
|
2384
|
+
"kind": "registry",
|
|
2385
|
+
"category": "Function registry",
|
|
2386
|
+
"name": "registerFunction() after construction is honoured (resolution is lazy)",
|
|
2387
|
+
"status": "passed",
|
|
2388
|
+
"durationMs": 0.26,
|
|
2389
|
+
"logs": [
|
|
2390
|
+
"finding: an engine with an empty registry reports no `registry` field, but registerFunction() makes it appear"
|
|
2391
|
+
]
|
|
2392
|
+
},
|
|
2393
|
+
{
|
|
2394
|
+
"id": "R5",
|
|
2395
|
+
"kind": "registry",
|
|
2396
|
+
"category": "Function registry",
|
|
2397
|
+
"name": "inline functions and registered names coexist in one graph",
|
|
2398
|
+
"status": "passed",
|
|
2399
|
+
"durationMs": 0.24,
|
|
2400
|
+
"logs": []
|
|
2401
|
+
},
|
|
2402
|
+
{
|
|
2403
|
+
"id": "R6",
|
|
2404
|
+
"kind": "registry",
|
|
2405
|
+
"category": "Function registry",
|
|
2406
|
+
"name": "a registered node function receives a full NodeContext",
|
|
2407
|
+
"status": "passed",
|
|
2408
|
+
"durationMs": 0.24,
|
|
2409
|
+
"logs": []
|
|
2410
|
+
},
|
|
2411
|
+
{
|
|
2412
|
+
"id": "R7",
|
|
2413
|
+
"kind": "registry",
|
|
2414
|
+
"category": "Function registry",
|
|
2415
|
+
"name": "a registered async function is awaited",
|
|
2416
|
+
"status": "passed",
|
|
2417
|
+
"durationMs": 5.47,
|
|
2418
|
+
"logs": []
|
|
2419
|
+
},
|
|
2420
|
+
{
|
|
2421
|
+
"id": "R8",
|
|
2422
|
+
"kind": "registry",
|
|
2423
|
+
"category": "Function registry",
|
|
2424
|
+
"name": "a registered node can route explicitly with Next and with context.goto",
|
|
2425
|
+
"status": "passed",
|
|
2426
|
+
"durationMs": 0.67,
|
|
2427
|
+
"logs": [
|
|
2428
|
+
"finding: `new Next(target, value)` sets the ROUTER's output to `value` (that is what edge matchers and context.outputs see); the target node still executes its own function"
|
|
2429
|
+
]
|
|
2430
|
+
},
|
|
2431
|
+
{
|
|
2432
|
+
"id": "R9",
|
|
2433
|
+
"kind": "registry",
|
|
2434
|
+
"category": "Function registry",
|
|
2435
|
+
"name": "timeouts and retries apply to registered functions exactly as to inline ones",
|
|
2436
|
+
"status": "passed",
|
|
2437
|
+
"durationMs": 21.6,
|
|
2438
|
+
"logs": []
|
|
2439
|
+
},
|
|
2440
|
+
{
|
|
2441
|
+
"id": "R10",
|
|
2442
|
+
"kind": "registry",
|
|
2443
|
+
"category": "Function registry",
|
|
2444
|
+
"name": "a missing run name records UNKNOWN_FUNCTION (unhandled) and schedules nothing",
|
|
2445
|
+
"status": "passed",
|
|
2446
|
+
"durationMs": 0.48,
|
|
2447
|
+
"logs": []
|
|
2448
|
+
},
|
|
2449
|
+
{
|
|
2450
|
+
"id": "R11",
|
|
2451
|
+
"kind": "registry",
|
|
2452
|
+
"category": "Function registry",
|
|
2453
|
+
"name": "a missing run name is PERMANENT: no retry, no onThrow, no fallback",
|
|
2454
|
+
"status": "passed",
|
|
2455
|
+
"durationMs": 0.26,
|
|
2456
|
+
"logs": [
|
|
2457
|
+
"finding: retry/onThrow/fallback are error RECOVERY; a name that was never registered is a wiring defect, so the engine refuses to recover from it"
|
|
2458
|
+
]
|
|
2459
|
+
},
|
|
2460
|
+
{
|
|
2461
|
+
"id": "R12",
|
|
2462
|
+
"kind": "registry",
|
|
2463
|
+
"category": "Function registry",
|
|
2464
|
+
"name": "a missing run name ignores FALLBACK policy as well",
|
|
2465
|
+
"status": "passed",
|
|
2466
|
+
"durationMs": 0.19,
|
|
2467
|
+
"logs": []
|
|
2468
|
+
},
|
|
2469
|
+
{
|
|
2470
|
+
"id": "R13",
|
|
2471
|
+
"kind": "registry",
|
|
2472
|
+
"category": "Function registry",
|
|
2473
|
+
"name": "registering the missing name mid-run lets the next activation succeed",
|
|
2474
|
+
"status": "passed",
|
|
2475
|
+
"durationMs": 0.28,
|
|
2476
|
+
"logs": []
|
|
2477
|
+
},
|
|
2478
|
+
{
|
|
2479
|
+
"id": "R14",
|
|
2480
|
+
"kind": "registry",
|
|
2481
|
+
"category": "Function registry",
|
|
2482
|
+
"name": "a named edge test decides the branch, and result + test still AND",
|
|
2483
|
+
"status": "passed",
|
|
2484
|
+
"durationMs": 0.35,
|
|
2485
|
+
"logs": []
|
|
2486
|
+
},
|
|
2487
|
+
{
|
|
2488
|
+
"id": "R15",
|
|
2489
|
+
"kind": "registry",
|
|
2490
|
+
"category": "Function registry",
|
|
2491
|
+
"name": "inline and named tests mix in one chain, in declaration order",
|
|
2492
|
+
"status": "passed",
|
|
2493
|
+
"durationMs": 0.28,
|
|
2494
|
+
"logs": []
|
|
2495
|
+
},
|
|
2496
|
+
{
|
|
2497
|
+
"id": "R16",
|
|
2498
|
+
"kind": "registry",
|
|
2499
|
+
"category": "Function registry",
|
|
2500
|
+
"name": "an unresolvable edge test never matches, is recorded once, and lets the else edge run",
|
|
2501
|
+
"status": "passed",
|
|
2502
|
+
"durationMs": 0.3,
|
|
2503
|
+
"logs": [
|
|
2504
|
+
"finding: an unresolvable test is loud but not fatal to ROUTING — the rest of the chain still evaluates; the run itself settles FAILED because the error stays unhandled (terminal-reached + unhandled error = FAILED, as in v1.2)"
|
|
2505
|
+
]
|
|
2506
|
+
},
|
|
2507
|
+
{
|
|
2508
|
+
"id": "R17",
|
|
2509
|
+
"kind": "registry",
|
|
2510
|
+
"category": "Function registry",
|
|
2511
|
+
"name": "a repeated evaluation reports a missing test only once (retry path)",
|
|
2512
|
+
"status": "passed",
|
|
2513
|
+
"durationMs": 1.65,
|
|
2514
|
+
"logs": []
|
|
2515
|
+
},
|
|
2516
|
+
{
|
|
2517
|
+
"id": "R18",
|
|
2518
|
+
"kind": "registry",
|
|
2519
|
+
"category": "Function registry",
|
|
2520
|
+
"name": "lint treats a NAMED test as conditional — no false UNREACHABLE_EDGE",
|
|
2521
|
+
"status": "passed",
|
|
2522
|
+
"durationMs": 0.3,
|
|
2523
|
+
"logs": [
|
|
2524
|
+
"finding: regression guard — before edgeHasMatcher() was shared, a named test looked like an else edge to the lint and every later edge was reported UNREACHABLE_EDGE"
|
|
2525
|
+
]
|
|
2526
|
+
},
|
|
2527
|
+
{
|
|
2528
|
+
"id": "R19",
|
|
2529
|
+
"kind": "registry",
|
|
2530
|
+
"category": "Function registry",
|
|
2531
|
+
"name": "a PARALLEL edge with a named test still warns PARALLEL_EDGE_MATCHER_IGNORED",
|
|
2532
|
+
"status": "passed",
|
|
2533
|
+
"durationMs": 0.27,
|
|
2534
|
+
"logs": []
|
|
2535
|
+
},
|
|
2536
|
+
{
|
|
2537
|
+
"id": "R20",
|
|
2538
|
+
"kind": "registry",
|
|
2539
|
+
"category": "Function registry",
|
|
2540
|
+
"name": "graph.ready and the report carry { fingerprint, names }; both are absent without a registry",
|
|
2541
|
+
"status": "passed",
|
|
2542
|
+
"durationMs": 0.4,
|
|
2543
|
+
"logs": []
|
|
2544
|
+
},
|
|
2545
|
+
{
|
|
2546
|
+
"id": "R21",
|
|
2547
|
+
"kind": "registry",
|
|
2548
|
+
"category": "Function registry",
|
|
2549
|
+
"name": "every checkpoint carries the registry identity",
|
|
2550
|
+
"status": "passed",
|
|
2551
|
+
"durationMs": 0.35,
|
|
2552
|
+
"logs": []
|
|
2553
|
+
},
|
|
2554
|
+
{
|
|
2555
|
+
"id": "R22",
|
|
2556
|
+
"kind": "registry",
|
|
2557
|
+
"category": "Function registry",
|
|
2558
|
+
"name": "FunctionRegistry: register/extend/has/get/names/snapshot stay consistent",
|
|
2559
|
+
"status": "passed",
|
|
2560
|
+
"durationMs": 0.18,
|
|
2561
|
+
"logs": []
|
|
2562
|
+
},
|
|
2563
|
+
{
|
|
2564
|
+
"id": "R23",
|
|
2565
|
+
"kind": "registry",
|
|
2566
|
+
"category": "Function registry",
|
|
2567
|
+
"name": "the fingerprint is stable, order-independent, and source-sensitive",
|
|
2568
|
+
"status": "passed",
|
|
2569
|
+
"durationMs": 0.19,
|
|
2570
|
+
"logs": [
|
|
2571
|
+
"finding: the fingerprint covers names + function SOURCE TEXT — two closures of the same source share it, while a captured value that changes at runtime does not show up"
|
|
2572
|
+
]
|
|
2573
|
+
},
|
|
2574
|
+
{
|
|
2575
|
+
"id": "R24",
|
|
2576
|
+
"kind": "registry",
|
|
2577
|
+
"category": "Function registry",
|
|
2578
|
+
"name": "invalid registrations fail immediately",
|
|
2579
|
+
"status": "passed",
|
|
2580
|
+
"durationMs": 0.16,
|
|
2581
|
+
"logs": []
|
|
2582
|
+
},
|
|
2583
|
+
{
|
|
2584
|
+
"id": "R25",
|
|
2585
|
+
"kind": "registry",
|
|
2586
|
+
"category": "Function registry",
|
|
2587
|
+
"name": "malformed run values and graph tables are rejected at construction",
|
|
2588
|
+
"status": "passed",
|
|
2589
|
+
"durationMs": 0.2,
|
|
2590
|
+
"logs": []
|
|
2591
|
+
},
|
|
2592
|
+
{
|
|
2593
|
+
"id": "R26",
|
|
2594
|
+
"kind": "registry",
|
|
2595
|
+
"category": "Function registry",
|
|
2596
|
+
"name": "nodeFunctions()/edgeTests() restore per-signature inference",
|
|
2597
|
+
"status": "passed",
|
|
2598
|
+
"durationMs": 0.28,
|
|
2599
|
+
"logs": [
|
|
2600
|
+
"finding: one table holds both signatures; a bare literal cannot infer a parameter type from the union, hence nodeFunctions()/edgeTests()"
|
|
2601
|
+
]
|
|
2602
|
+
},
|
|
2603
|
+
{
|
|
2604
|
+
"id": "R27",
|
|
2605
|
+
"kind": "registry",
|
|
2606
|
+
"category": "Function registry",
|
|
2607
|
+
"name": "the report stays JSON-serializable with a registry configured",
|
|
2608
|
+
"status": "passed",
|
|
2609
|
+
"durationMs": 0.31,
|
|
2610
|
+
"logs": []
|
|
2611
|
+
},
|
|
2612
|
+
{
|
|
2613
|
+
"id": "R28",
|
|
2614
|
+
"kind": "registry",
|
|
2615
|
+
"category": "Function registry",
|
|
2616
|
+
"name": "the UNKNOWN_FUNCTION record is self-describing",
|
|
2617
|
+
"status": "passed",
|
|
2618
|
+
"durationMs": 0.27,
|
|
2619
|
+
"logs": []
|
|
2620
|
+
},
|
|
2621
|
+
{
|
|
2622
|
+
"id": "G1",
|
|
2623
|
+
"kind": "graphjson",
|
|
2624
|
+
"category": "Graph JSON loader",
|
|
2625
|
+
"name": "a valid document parses into a GraphDefinition (string and object input agree)",
|
|
2626
|
+
"status": "passed",
|
|
2627
|
+
"durationMs": 1.22,
|
|
2628
|
+
"logs": [
|
|
2629
|
+
"finding: parsing never needs the registry — a data file is valid even when a function name is unknown; the engine decides that at run time"
|
|
2630
|
+
]
|
|
2631
|
+
},
|
|
2632
|
+
{
|
|
2633
|
+
"id": "G2",
|
|
2634
|
+
"kind": "graphjson",
|
|
2635
|
+
"category": "Graph JSON loader",
|
|
2636
|
+
"name": "node ids default to their keys, and a mismatching explicit id is an issue",
|
|
2637
|
+
"status": "passed",
|
|
2638
|
+
"durationMs": 0.29,
|
|
2639
|
+
"logs": []
|
|
2640
|
+
},
|
|
2641
|
+
{
|
|
2642
|
+
"id": "G3",
|
|
2643
|
+
"kind": "graphjson",
|
|
2644
|
+
"category": "Graph JSON loader",
|
|
2645
|
+
"name": "deep-equality results, named tests and parallel flags survive the round trip",
|
|
2646
|
+
"status": "passed",
|
|
2647
|
+
"durationMs": 0.14,
|
|
2648
|
+
"logs": []
|
|
2649
|
+
},
|
|
2650
|
+
{
|
|
2651
|
+
"id": "G4",
|
|
2652
|
+
"kind": "graphjson",
|
|
2653
|
+
"category": "Graph JSON loader",
|
|
2654
|
+
"name": "createEngineFromJSON runs the data file and reports the registry fingerprint",
|
|
2655
|
+
"status": "passed",
|
|
2656
|
+
"durationMs": 0.56,
|
|
2657
|
+
"logs": []
|
|
2658
|
+
},
|
|
2659
|
+
{
|
|
2660
|
+
"id": "G5",
|
|
2661
|
+
"kind": "graphjson",
|
|
2662
|
+
"category": "Graph JSON loader",
|
|
2663
|
+
"name": "a named test in the document routes via the supplied registry",
|
|
2664
|
+
"status": "passed",
|
|
2665
|
+
"durationMs": 0.33,
|
|
2666
|
+
"logs": []
|
|
2667
|
+
},
|
|
2668
|
+
{
|
|
2669
|
+
"id": "G6",
|
|
2670
|
+
"kind": "graphjson",
|
|
2671
|
+
"category": "Graph JSON loader",
|
|
2672
|
+
"name": "a data file with an unknown name runs and fails with UNKNOWN_FUNCTION",
|
|
2673
|
+
"status": "passed",
|
|
2674
|
+
"durationMs": 0.57,
|
|
2675
|
+
"logs": []
|
|
2676
|
+
},
|
|
2677
|
+
{
|
|
2678
|
+
"id": "G7",
|
|
2679
|
+
"kind": "graphjson",
|
|
2680
|
+
"category": "Graph JSON loader",
|
|
2681
|
+
"name": "the parsed definition is accepted by the engine unchanged",
|
|
2682
|
+
"status": "passed",
|
|
2683
|
+
"durationMs": 0.36,
|
|
2684
|
+
"logs": []
|
|
2685
|
+
},
|
|
2686
|
+
{
|
|
2687
|
+
"id": "G8",
|
|
2688
|
+
"kind": "graphjson",
|
|
2689
|
+
"category": "Graph JSON loader",
|
|
2690
|
+
"name": "invalid JSON text is a single, clear GraphJSONError",
|
|
2691
|
+
"status": "passed",
|
|
2692
|
+
"durationMs": 0.12,
|
|
2693
|
+
"logs": []
|
|
2694
|
+
},
|
|
2695
|
+
{
|
|
2696
|
+
"id": "G9",
|
|
2697
|
+
"kind": "graphjson",
|
|
2698
|
+
"category": "Graph JSON loader",
|
|
2699
|
+
"name": "a non-object document is rejected with one issue",
|
|
2700
|
+
"status": "passed",
|
|
2701
|
+
"durationMs": 0.08,
|
|
2702
|
+
"logs": []
|
|
2703
|
+
},
|
|
2704
|
+
{
|
|
2705
|
+
"id": "G10",
|
|
2706
|
+
"kind": "graphjson",
|
|
2707
|
+
"category": "Graph JSON loader",
|
|
2708
|
+
"name": "unknown properties are rejected at every level",
|
|
2709
|
+
"status": "passed",
|
|
2710
|
+
"durationMs": 0.18,
|
|
2711
|
+
"logs": []
|
|
2712
|
+
},
|
|
2713
|
+
{
|
|
2714
|
+
"id": "G11",
|
|
2715
|
+
"kind": "graphjson",
|
|
2716
|
+
"category": "Graph JSON loader",
|
|
2717
|
+
"name": "functions cannot live in the document — the issue points at the engine options",
|
|
2718
|
+
"status": "passed",
|
|
2719
|
+
"durationMs": 0.11,
|
|
2720
|
+
"logs": []
|
|
2721
|
+
},
|
|
2722
|
+
{
|
|
2723
|
+
"id": "G12",
|
|
2724
|
+
"kind": "graphjson",
|
|
2725
|
+
"category": "Graph JSON loader",
|
|
2726
|
+
"name": "dangling references are reported with their paths",
|
|
2727
|
+
"status": "passed",
|
|
2728
|
+
"durationMs": 0.13,
|
|
2729
|
+
"logs": []
|
|
2730
|
+
},
|
|
2731
|
+
{
|
|
2732
|
+
"id": "G13",
|
|
2733
|
+
"kind": "graphjson",
|
|
2734
|
+
"category": "Graph JSON loader",
|
|
2735
|
+
"name": "malformed edges are rejected entry by entry",
|
|
2736
|
+
"status": "passed",
|
|
2737
|
+
"durationMs": 0.19,
|
|
2738
|
+
"logs": []
|
|
2739
|
+
},
|
|
2740
|
+
{
|
|
2741
|
+
"id": "G14",
|
|
2742
|
+
"kind": "graphjson",
|
|
2743
|
+
"category": "Graph JSON loader",
|
|
2744
|
+
"name": "invalid scalars, policies and retry blocks are rejected",
|
|
2745
|
+
"status": "passed",
|
|
2746
|
+
"durationMs": 0.19,
|
|
2747
|
+
"logs": []
|
|
2748
|
+
},
|
|
2749
|
+
{
|
|
2750
|
+
"id": "G15",
|
|
2751
|
+
"kind": "graphjson",
|
|
2752
|
+
"category": "Graph JSON loader",
|
|
2753
|
+
"name": "EVERY issue is collected — one throw carries them all",
|
|
2754
|
+
"status": "passed",
|
|
2755
|
+
"durationMs": 0.18,
|
|
2756
|
+
"logs": [
|
|
2757
|
+
"finding: one document reported 5 problems in a single throw — no fix-one-rerun loop"
|
|
2758
|
+
]
|
|
2759
|
+
},
|
|
2760
|
+
{
|
|
2761
|
+
"id": "G16",
|
|
2762
|
+
"kind": "graphjson",
|
|
2763
|
+
"category": "Graph JSON loader",
|
|
2764
|
+
"name": "isGraphJSON() is a cheap shape probe, not a validator",
|
|
2765
|
+
"status": "passed",
|
|
2766
|
+
"durationMs": 0.13,
|
|
2767
|
+
"logs": []
|
|
2768
|
+
},
|
|
2769
|
+
{
|
|
2770
|
+
"id": "G17",
|
|
2771
|
+
"kind": "graphjson",
|
|
2772
|
+
"category": "Graph JSON loader",
|
|
2773
|
+
"name": "an in-memory object may pass real functions through (escape hatch)",
|
|
2774
|
+
"status": "passed",
|
|
2775
|
+
"durationMs": 0.24,
|
|
2776
|
+
"logs": []
|
|
2777
|
+
},
|
|
2778
|
+
{
|
|
2779
|
+
"id": "G18",
|
|
2780
|
+
"kind": "graphjson",
|
|
2781
|
+
"category": "Graph JSON loader",
|
|
2782
|
+
"name": "a parsed document with dangling ids is still caught by the engine if it is hand-edited",
|
|
2783
|
+
"status": "passed",
|
|
2784
|
+
"durationMs": 0.17,
|
|
2785
|
+
"logs": [
|
|
2786
|
+
"finding: the loader and the engine validate different layers (paths vs. final shape) — both stay in force"
|
|
2787
|
+
]
|
|
2788
|
+
},
|
|
2789
|
+
{
|
|
2790
|
+
"id": "G19",
|
|
2791
|
+
"kind": "graphjson",
|
|
2792
|
+
"category": "Graph JSON loader",
|
|
2793
|
+
"name": "the loader example document is valid (regression: the shipped .json file)",
|
|
2794
|
+
"status": "passed",
|
|
2795
|
+
"durationMs": 0.53,
|
|
2796
|
+
"logs": []
|
|
2797
|
+
},
|
|
2798
|
+
{
|
|
2799
|
+
"id": "S1",
|
|
2800
|
+
"kind": "scripts",
|
|
2801
|
+
"category": "Script loading",
|
|
2802
|
+
"name": "a .cjs script is required and its export table becomes the registry",
|
|
2803
|
+
"status": "passed",
|
|
2804
|
+
"durationMs": 1.62,
|
|
2805
|
+
"logs": [
|
|
2806
|
+
"finding: a registered name is not rewritten — `classifyOrder` stays `classifyOrder`, the namespace only owns the claim"
|
|
2807
|
+
]
|
|
2808
|
+
},
|
|
2809
|
+
{
|
|
2810
|
+
"id": "S2",
|
|
2811
|
+
"kind": "scripts",
|
|
2812
|
+
"category": "Script loading",
|
|
2813
|
+
"name": "an .mjs script is imported as an ES module",
|
|
2814
|
+
"status": "passed",
|
|
2815
|
+
"durationMs": 5.98,
|
|
2816
|
+
"logs": []
|
|
2817
|
+
},
|
|
2818
|
+
{
|
|
2819
|
+
"id": "S3",
|
|
2820
|
+
"kind": "scripts",
|
|
2821
|
+
"category": "Script loading",
|
|
2822
|
+
"name": "a .js script with ESM syntax is detected as ESM; without it, as CommonJS",
|
|
2823
|
+
"status": "passed",
|
|
2824
|
+
"durationMs": 2.16,
|
|
2825
|
+
"logs": [
|
|
2826
|
+
"finding: .js detection is a source hint with a fallback — the other format is attempted before failing, so a mis-hint costs one retry, never a failed load"
|
|
2827
|
+
]
|
|
2828
|
+
},
|
|
2829
|
+
{
|
|
2830
|
+
"id": "S4",
|
|
2831
|
+
"kind": "scripts",
|
|
2832
|
+
"category": "Script loading",
|
|
2833
|
+
"name": "an explicit format is honoured and never retried in the other format",
|
|
2834
|
+
"status": "passed",
|
|
2835
|
+
"durationMs": 4.02,
|
|
2836
|
+
"logs": []
|
|
2837
|
+
},
|
|
2838
|
+
{
|
|
2839
|
+
"id": "S5",
|
|
2840
|
+
"kind": "scripts",
|
|
2841
|
+
"category": "Script loading",
|
|
2842
|
+
"name": "inline CommonJS source runs with a real require and module",
|
|
2843
|
+
"status": "passed",
|
|
2844
|
+
"durationMs": 0.44,
|
|
2845
|
+
"logs": []
|
|
2846
|
+
},
|
|
2847
|
+
{
|
|
2848
|
+
"id": "S6",
|
|
2849
|
+
"kind": "scripts",
|
|
2850
|
+
"category": "Script loading",
|
|
2851
|
+
"name": "inline ESM source is evaluated from a data URL",
|
|
2852
|
+
"status": "passed",
|
|
2853
|
+
"durationMs": 0.69,
|
|
2854
|
+
"logs": []
|
|
2855
|
+
},
|
|
2856
|
+
{
|
|
2857
|
+
"id": "S7",
|
|
2858
|
+
"kind": "scripts",
|
|
2859
|
+
"category": "Script loading",
|
|
2860
|
+
"name": "URL scripts are fetched and evaluated (CJS by default, ESM by hint)",
|
|
2861
|
+
"status": "passed",
|
|
2862
|
+
"durationMs": 0.83,
|
|
2863
|
+
"logs": []
|
|
2864
|
+
},
|
|
2865
|
+
{
|
|
2866
|
+
"id": "S8",
|
|
2867
|
+
"kind": "scripts",
|
|
2868
|
+
"category": "Script loading",
|
|
2869
|
+
"name": "a script can register itself through globalThis.GraphEngine",
|
|
2870
|
+
"status": "passed",
|
|
2871
|
+
"durationMs": 0.65,
|
|
2872
|
+
"logs": [
|
|
2873
|
+
"finding: the engine global is a per-CALL slot (registerEngineNamespace), not a shared mutable table — a script can only ever add its own namespace, and only once"
|
|
2874
|
+
]
|
|
2875
|
+
},
|
|
2876
|
+
{
|
|
2877
|
+
"id": "S9",
|
|
2878
|
+
"kind": "scripts",
|
|
2879
|
+
"category": "Script loading",
|
|
2880
|
+
"name": "the realm is restored after the load — the engine global does not leak",
|
|
2881
|
+
"status": "passed",
|
|
2882
|
+
"durationMs": 0.83,
|
|
2883
|
+
"logs": []
|
|
2884
|
+
},
|
|
2885
|
+
{
|
|
2886
|
+
"id": "S10",
|
|
2887
|
+
"kind": "scripts",
|
|
2888
|
+
"category": "Script loading",
|
|
2889
|
+
"name": "a script can both self-register and export; both land in the namespace",
|
|
2890
|
+
"status": "passed",
|
|
2891
|
+
"durationMs": 0.84,
|
|
2892
|
+
"logs": []
|
|
2893
|
+
},
|
|
2894
|
+
{
|
|
2895
|
+
"id": "S11",
|
|
2896
|
+
"kind": "scripts",
|
|
2897
|
+
"category": "Script loading",
|
|
2898
|
+
"name": "the declared namespace must match what the script registers",
|
|
2899
|
+
"status": "passed",
|
|
2900
|
+
"durationMs": 0.31,
|
|
2901
|
+
"logs": []
|
|
2902
|
+
},
|
|
2903
|
+
{
|
|
2904
|
+
"id": "S12",
|
|
2905
|
+
"kind": "scripts",
|
|
2906
|
+
"category": "Script loading",
|
|
2907
|
+
"name": "a registration error from inside the script surfaces unwrapped",
|
|
2908
|
+
"status": "passed",
|
|
2909
|
+
"durationMs": 0.38,
|
|
2910
|
+
"logs": []
|
|
2911
|
+
},
|
|
2912
|
+
{
|
|
2913
|
+
"id": "S13",
|
|
2914
|
+
"kind": "scripts",
|
|
2915
|
+
"category": "Script loading",
|
|
2916
|
+
"name": "a namespace can be claimed only once per process (exports path)",
|
|
2917
|
+
"status": "passed",
|
|
2918
|
+
"durationMs": 0.67,
|
|
2919
|
+
"logs": [
|
|
2920
|
+
"finding: the freeze is PROCESS-global by design — one document, one engine per namespace. Loading the same graph file into a second engine throws until the claims are reset"
|
|
2921
|
+
]
|
|
2922
|
+
},
|
|
2923
|
+
{
|
|
2924
|
+
"id": "S14",
|
|
2925
|
+
"kind": "scripts",
|
|
2926
|
+
"category": "Script loading",
|
|
2927
|
+
"name": "a self-registering script throws on the duplicate claim FROM INSIDE the script",
|
|
2928
|
+
"status": "passed",
|
|
2929
|
+
"durationMs": 0.46,
|
|
2930
|
+
"logs": []
|
|
2931
|
+
},
|
|
2932
|
+
{
|
|
2933
|
+
"id": "S15",
|
|
2934
|
+
"kind": "scripts",
|
|
2935
|
+
"category": "Script loading",
|
|
2936
|
+
"name": "a claim made before a crashing script survives; the load still fails",
|
|
2937
|
+
"status": "passed",
|
|
2938
|
+
"durationMs": 0.99,
|
|
2939
|
+
"logs": [
|
|
2940
|
+
"finding: claims are made at registration time, not at load end — a script that registers and then throws leaves its namespace claimed (reload needs resetGraphScriptNamespaces)"
|
|
2941
|
+
]
|
|
2942
|
+
},
|
|
2943
|
+
{
|
|
2944
|
+
"id": "S16",
|
|
2945
|
+
"kind": "scripts",
|
|
2946
|
+
"category": "Script loading",
|
|
2947
|
+
"name": "a script that throws is not replayed in the other format",
|
|
2948
|
+
"status": "passed",
|
|
2949
|
+
"durationMs": 0.43,
|
|
2950
|
+
"logs": [
|
|
2951
|
+
"finding: format fallback replays the script body, so it is reserved for module-format failures (ERR_REQUIRE_ESM, ESM syntax in CJS). A script that throws — or registers and then throws — is never re-executed; it ran once and its claim stands"
|
|
2952
|
+
]
|
|
2953
|
+
},
|
|
2954
|
+
{
|
|
2955
|
+
"id": "S17",
|
|
2956
|
+
"kind": "scripts",
|
|
2957
|
+
"category": "Script loading",
|
|
2958
|
+
"name": "two scripts providing the same name collide; override is opt-in",
|
|
2959
|
+
"status": "passed",
|
|
2960
|
+
"durationMs": 0.73,
|
|
2961
|
+
"logs": [
|
|
2962
|
+
"finding: claims and name collisions are separate gates — the freeze stops the namespace, collisions stop the merged table, and only the second one has an override"
|
|
2963
|
+
]
|
|
2964
|
+
},
|
|
2965
|
+
{
|
|
2966
|
+
"id": "S18",
|
|
2967
|
+
"kind": "scripts",
|
|
2968
|
+
"category": "Script loading",
|
|
2969
|
+
"name": "script declarations are validated, with JSON paths",
|
|
2970
|
+
"status": "passed",
|
|
2971
|
+
"durationMs": 0.32,
|
|
2972
|
+
"logs": []
|
|
2973
|
+
},
|
|
2974
|
+
{
|
|
2975
|
+
"id": "S19",
|
|
2976
|
+
"kind": "scripts",
|
|
2977
|
+
"category": "Script loading",
|
|
2978
|
+
"name": "the same namespace twice in one document is a parse issue",
|
|
2979
|
+
"status": "passed",
|
|
2980
|
+
"durationMs": 0.1,
|
|
2981
|
+
"logs": []
|
|
2982
|
+
},
|
|
2983
|
+
{
|
|
2984
|
+
"id": "S20",
|
|
2985
|
+
"kind": "scripts",
|
|
2986
|
+
"category": "Script loading",
|
|
2987
|
+
"name": "a scripted document still parses with the v1.4 entry point",
|
|
2988
|
+
"status": "passed",
|
|
2989
|
+
"durationMs": 0.12,
|
|
2990
|
+
"logs": [
|
|
2991
|
+
"finding: `scripts` is validated by the isomorphic parser and executed by the Node loader — parseGraphJSON (sync, v1.4) keeps working on scripted documents"
|
|
2992
|
+
]
|
|
2993
|
+
},
|
|
2994
|
+
{
|
|
2995
|
+
"id": "S21",
|
|
2996
|
+
"kind": "scripts",
|
|
2997
|
+
"category": "Script loading",
|
|
2998
|
+
"name": "the document is validated BEFORE any script executes",
|
|
2999
|
+
"status": "passed",
|
|
3000
|
+
"durationMs": 0.14,
|
|
3001
|
+
"logs": []
|
|
3002
|
+
},
|
|
3003
|
+
{
|
|
3004
|
+
"id": "S22",
|
|
3005
|
+
"kind": "scripts",
|
|
3006
|
+
"category": "Script loading",
|
|
3007
|
+
"name": "a missing file, a crashing script and a function-less script fail clearly",
|
|
3008
|
+
"status": "passed",
|
|
3009
|
+
"durationMs": 0.65,
|
|
3010
|
+
"logs": []
|
|
3011
|
+
},
|
|
3012
|
+
{
|
|
3013
|
+
"id": "S23",
|
|
3014
|
+
"kind": "scripts",
|
|
3015
|
+
"category": "Script loading",
|
|
3016
|
+
"name": "a scripted document runs: the script provides the node and edge functions",
|
|
3017
|
+
"status": "passed",
|
|
3018
|
+
"durationMs": 0.65,
|
|
3019
|
+
"logs": [
|
|
3020
|
+
"finding: end-to-end, 6 functions arrive from path:./order.cjs and the engine reports fnr1:6:b43eadc7"
|
|
3021
|
+
]
|
|
3022
|
+
},
|
|
3023
|
+
{
|
|
3024
|
+
"id": "S24",
|
|
3025
|
+
"kind": "scripts",
|
|
3026
|
+
"category": "Script loading",
|
|
3027
|
+
"name": "a script's own sibling requires resolve from the script's directory",
|
|
3028
|
+
"status": "passed",
|
|
3029
|
+
"durationMs": 0.56,
|
|
3030
|
+
"logs": []
|
|
3031
|
+
},
|
|
3032
|
+
{
|
|
3033
|
+
"id": "S25",
|
|
3034
|
+
"kind": "scripts",
|
|
3035
|
+
"category": "Script loading",
|
|
3036
|
+
"name": "inline scripts compose with path scripts in one document",
|
|
3037
|
+
"status": "passed",
|
|
3038
|
+
"durationMs": 0.43,
|
|
3039
|
+
"logs": [
|
|
3040
|
+
"finding: the three media (path, source, url) are interchangeable — one pipeline merges them into a single table with one fingerprint"
|
|
3041
|
+
]
|
|
3042
|
+
},
|
|
3043
|
+
{
|
|
3044
|
+
"id": "S26",
|
|
3045
|
+
"kind": "scripts",
|
|
3046
|
+
"category": "Script loading",
|
|
3047
|
+
"name": "the loaded table is frozen and the engine can be built from it directly",
|
|
3048
|
+
"status": "passed",
|
|
3049
|
+
"durationMs": 0.46,
|
|
3050
|
+
"logs": []
|
|
3051
|
+
}
|
|
3052
|
+
]
|
|
3053
|
+
}
|