@nodetool-ai/reliability-harness 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/dist/.nodetool-build-stamp +1 -0
- package/dist/cli.d.ts +73 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +201 -0
- package/dist/cli.js.map +1 -0
- package/dist/compare.d.ts +98 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/compare.js +267 -0
- package/dist/compare.js.map +1 -0
- package/dist/core/diff.d.ts +39 -0
- package/dist/core/diff.d.ts.map +1 -0
- package/dist/core/diff.js +114 -0
- package/dist/core/diff.js.map +1 -0
- package/dist/core/golden.d.ts +50 -0
- package/dist/core/golden.d.ts.map +1 -0
- package/dist/core/golden.js +150 -0
- package/dist/core/golden.js.map +1 -0
- package/dist/core/invariants/index.d.ts +32 -0
- package/dist/core/invariants/index.d.ts.map +1 -0
- package/dist/core/invariants/index.js +44 -0
- package/dist/core/invariants/index.js.map +1 -0
- package/dist/core/invariants/leaks.d.ts +22 -0
- package/dist/core/invariants/leaks.d.ts.map +1 -0
- package/dist/core/invariants/leaks.js +55 -0
- package/dist/core/invariants/leaks.js.map +1 -0
- package/dist/core/invariants/lifecycle.d.ts +19 -0
- package/dist/core/invariants/lifecycle.d.ts.map +1 -0
- package/dist/core/invariants/lifecycle.js +95 -0
- package/dist/core/invariants/lifecycle.js.map +1 -0
- package/dist/core/invariants/protocol-validity.d.ts +4 -0
- package/dist/core/invariants/protocol-validity.d.ts.map +1 -0
- package/dist/core/invariants/protocol-validity.js +131 -0
- package/dist/core/invariants/protocol-validity.js.map +1 -0
- package/dist/core/invariants/state-machine.d.ts +29 -0
- package/dist/core/invariants/state-machine.d.ts.map +1 -0
- package/dist/core/invariants/state-machine.js +79 -0
- package/dist/core/invariants/state-machine.js.map +1 -0
- package/dist/core/invariants/status-sets.d.ts +27 -0
- package/dist/core/invariants/status-sets.d.ts.map +1 -0
- package/dist/core/invariants/status-sets.js +51 -0
- package/dist/core/invariants/status-sets.js.map +1 -0
- package/dist/core/invariants/terminal-uniqueness.d.ts +23 -0
- package/dist/core/invariants/terminal-uniqueness.d.ts.map +1 -0
- package/dist/core/invariants/terminal-uniqueness.js +99 -0
- package/dist/core/invariants/terminal-uniqueness.js.map +1 -0
- package/dist/core/invariants/types.d.ts +25 -0
- package/dist/core/invariants/types.d.ts.map +1 -0
- package/dist/core/invariants/types.js +2 -0
- package/dist/core/invariants/types.js.map +1 -0
- package/dist/core/journey.d.ts +121 -0
- package/dist/core/journey.d.ts.map +1 -0
- package/dist/core/journey.js +166 -0
- package/dist/core/journey.js.map +1 -0
- package/dist/core/normalize.d.ts +86 -0
- package/dist/core/normalize.d.ts.map +1 -0
- package/dist/core/normalize.js +182 -0
- package/dist/core/normalize.js.map +1 -0
- package/dist/core/record.d.ts +160 -0
- package/dist/core/record.d.ts.map +1 -0
- package/dist/core/record.js +74 -0
- package/dist/core/record.js.map +1 -0
- package/dist/core/stable-json.d.ts +13 -0
- package/dist/core/stable-json.d.ts.map +1 -0
- package/dist/core/stable-json.js +28 -0
- package/dist/core/stable-json.js.map +1 -0
- package/dist/drivers/anchors.d.ts +29 -0
- package/dist/drivers/anchors.d.ts.map +1 -0
- package/dist/drivers/anchors.js +48 -0
- package/dist/drivers/anchors.js.map +1 -0
- package/dist/drivers/app-headless.d.ts +39 -0
- package/dist/drivers/app-headless.d.ts.map +1 -0
- package/dist/drivers/app-headless.js +123 -0
- package/dist/drivers/app-headless.js.map +1 -0
- package/dist/drivers/browser.d.ts +34 -0
- package/dist/drivers/browser.d.ts.map +1 -0
- package/dist/drivers/browser.js +176 -0
- package/dist/drivers/browser.js.map +1 -0
- package/dist/drivers/cli.d.ts +13 -0
- package/dist/drivers/cli.d.ts.map +1 -0
- package/dist/drivers/cli.js +82 -0
- package/dist/drivers/cli.js.map +1 -0
- package/dist/drivers/fixture-nodes.d.ts +124 -0
- package/dist/drivers/fixture-nodes.d.ts.map +1 -0
- package/dist/drivers/fixture-nodes.js +189 -0
- package/dist/drivers/fixture-nodes.js.map +1 -0
- package/dist/drivers/index.d.ts +18 -0
- package/dist/drivers/index.d.ts.map +1 -0
- package/dist/drivers/index.js +12 -0
- package/dist/drivers/index.js.map +1 -0
- package/dist/drivers/kernel.d.ts +8 -0
- package/dist/drivers/kernel.d.ts.map +1 -0
- package/dist/drivers/kernel.js +247 -0
- package/dist/drivers/kernel.js.map +1 -0
- package/dist/drivers/packaged.d.ts +28 -0
- package/dist/drivers/packaged.d.ts.map +1 -0
- package/dist/drivers/packaged.js +303 -0
- package/dist/drivers/packaged.js.map +1 -0
- package/dist/drivers/python-bridge.d.ts +22 -0
- package/dist/drivers/python-bridge.d.ts.map +1 -0
- package/dist/drivers/python-bridge.js +48 -0
- package/dist/drivers/python-bridge.js.map +1 -0
- package/dist/drivers/registry.d.ts +11 -0
- package/dist/drivers/registry.d.ts.map +1 -0
- package/dist/drivers/registry.js +18 -0
- package/dist/drivers/registry.js.map +1 -0
- package/dist/drivers/relay-fields.d.ts +70 -0
- package/dist/drivers/relay-fields.d.ts.map +1 -0
- package/dist/drivers/relay-fields.js +104 -0
- package/dist/drivers/relay-fields.js.map +1 -0
- package/dist/drivers/repo-root.d.ts +9 -0
- package/dist/drivers/repo-root.d.ts.map +1 -0
- package/dist/drivers/repo-root.js +39 -0
- package/dist/drivers/repo-root.js.map +1 -0
- package/dist/drivers/spawn-cli.d.ts +8 -0
- package/dist/drivers/spawn-cli.d.ts.map +1 -0
- package/dist/drivers/spawn-cli.js +41 -0
- package/dist/drivers/spawn-cli.js.map +1 -0
- package/dist/drivers/types.d.ts +18 -0
- package/dist/drivers/types.d.ts.map +1 -0
- package/dist/drivers/types.js +2 -0
- package/dist/drivers/types.js.map +1 -0
- package/dist/drivers/ws-server.d.ts +8 -0
- package/dist/drivers/ws-server.d.ts.map +1 -0
- package/dist/drivers/ws-server.js +382 -0
- package/dist/drivers/ws-server.js.map +1 -0
- package/dist/faults/bridge-faults.d.ts +7 -0
- package/dist/faults/bridge-faults.d.ts.map +1 -0
- package/dist/faults/bridge-faults.js +90 -0
- package/dist/faults/bridge-faults.js.map +1 -0
- package/dist/faults/host-faults.d.ts +40 -0
- package/dist/faults/host-faults.d.ts.map +1 -0
- package/dist/faults/host-faults.js +70 -0
- package/dist/faults/host-faults.js.map +1 -0
- package/dist/faults/index.d.ts +21 -0
- package/dist/faults/index.d.ts.map +1 -0
- package/dist/faults/index.js +29 -0
- package/dist/faults/index.js.map +1 -0
- package/dist/faults/net-proxy.d.ts +75 -0
- package/dist/faults/net-proxy.d.ts.map +1 -0
- package/dist/faults/net-proxy.js +305 -0
- package/dist/faults/net-proxy.js.map +1 -0
- package/dist/faults/provider-faults.d.ts +12 -0
- package/dist/faults/provider-faults.d.ts.map +1 -0
- package/dist/faults/provider-faults.js +110 -0
- package/dist/faults/provider-faults.js.map +1 -0
- package/dist/faults/registry.d.ts +17 -0
- package/dist/faults/registry.d.ts.map +1 -0
- package/dist/faults/registry.js +15 -0
- package/dist/faults/registry.js.map +1 -0
- package/dist/faults/types.d.ts +42 -0
- package/dist/faults/types.d.ts.map +1 -0
- package/dist/faults/types.js +2 -0
- package/dist/faults/types.js.map +1 -0
- package/dist/faults/ws-faults.d.ts +6 -0
- package/dist/faults/ws-faults.d.ts.map +1 -0
- package/dist/faults/ws-faults.js +48 -0
- package/dist/faults/ws-faults.js.map +1 -0
- package/dist/faults/ws-proxy.d.ts +36 -0
- package/dist/faults/ws-proxy.d.ts.map +1 -0
- package/dist/faults/ws-proxy.js +40 -0
- package/dist/faults/ws-proxy.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/package.json +75 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"golden.js","sourceRoot":"","sources":["../../src/core/golden.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §6 invariant modules (docs/RELIABILITY_ARCHITECTURE.md §6, §12; task C3).
|
|
3
|
+
*
|
|
4
|
+
* Each module is a pure `(record: RunRecord) => Violation[]`. This barrel is
|
|
5
|
+
* exported on its own — not yet re-exported from the harness's main
|
|
6
|
+
* `src/index.ts` — because C4 wires the main barrel; importing from
|
|
7
|
+
* `@nodetool-ai/reliability-harness/core/invariants` (or this file's
|
|
8
|
+
* relative path within the package) works today.
|
|
9
|
+
*/
|
|
10
|
+
export * from "./types.js";
|
|
11
|
+
export * from "./status-sets.js";
|
|
12
|
+
export * from "./lifecycle.js";
|
|
13
|
+
export * from "./terminal-uniqueness.js";
|
|
14
|
+
export * from "./leaks.js";
|
|
15
|
+
export * from "./state-machine.js";
|
|
16
|
+
export * from "./protocol-validity.js";
|
|
17
|
+
import type { InvariantCheck } from "./types.js";
|
|
18
|
+
/**
|
|
19
|
+
* Every invariant module keyed by the family name journeys declare in
|
|
20
|
+
* `journey.json`'s `assertions.invariants` (§4). C4's runner is expected to
|
|
21
|
+
* look names up here rather than importing each module by hand.
|
|
22
|
+
*
|
|
23
|
+
* `lifecycle-pairing` and `cleanup-leaks` are aliases for `lifecycle` and
|
|
24
|
+
* `leaks`: the seed journeys under `reliability/journeys/*.json` (C1/C2) were
|
|
25
|
+
* authored against §6's prose section headers ("Lifecycle", "Cleanup and
|
|
26
|
+
* leaks") rather than this module's own key names, and the manifest schema
|
|
27
|
+
* (`core/journey.ts`) only checks that `invariants` is a non-empty array of
|
|
28
|
+
* strings, not that each name resolves — so both spellings need to resolve
|
|
29
|
+
* for those journeys' declared invariants to actually run under `compare.ts`.
|
|
30
|
+
*/
|
|
31
|
+
export declare const INVARIANT_CHECKS: Readonly<Record<string, InvariantCheck>>;
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/invariants/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AAOvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAQrE,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §6 invariant modules (docs/RELIABILITY_ARCHITECTURE.md §6, §12; task C3).
|
|
3
|
+
*
|
|
4
|
+
* Each module is a pure `(record: RunRecord) => Violation[]`. This barrel is
|
|
5
|
+
* exported on its own — not yet re-exported from the harness's main
|
|
6
|
+
* `src/index.ts` — because C4 wires the main barrel; importing from
|
|
7
|
+
* `@nodetool-ai/reliability-harness/core/invariants` (or this file's
|
|
8
|
+
* relative path within the package) works today.
|
|
9
|
+
*/
|
|
10
|
+
export * from "./types.js";
|
|
11
|
+
export * from "./status-sets.js";
|
|
12
|
+
export * from "./lifecycle.js";
|
|
13
|
+
export * from "./terminal-uniqueness.js";
|
|
14
|
+
export * from "./leaks.js";
|
|
15
|
+
export * from "./state-machine.js";
|
|
16
|
+
export * from "./protocol-validity.js";
|
|
17
|
+
import { checkLifecycle } from "./lifecycle.js";
|
|
18
|
+
import { checkTerminalUniqueness } from "./terminal-uniqueness.js";
|
|
19
|
+
import { checkLeaks } from "./leaks.js";
|
|
20
|
+
import { checkStateMachine } from "./state-machine.js";
|
|
21
|
+
import { checkProtocolValidity } from "./protocol-validity.js";
|
|
22
|
+
/**
|
|
23
|
+
* Every invariant module keyed by the family name journeys declare in
|
|
24
|
+
* `journey.json`'s `assertions.invariants` (§4). C4's runner is expected to
|
|
25
|
+
* look names up here rather than importing each module by hand.
|
|
26
|
+
*
|
|
27
|
+
* `lifecycle-pairing` and `cleanup-leaks` are aliases for `lifecycle` and
|
|
28
|
+
* `leaks`: the seed journeys under `reliability/journeys/*.json` (C1/C2) were
|
|
29
|
+
* authored against §6's prose section headers ("Lifecycle", "Cleanup and
|
|
30
|
+
* leaks") rather than this module's own key names, and the manifest schema
|
|
31
|
+
* (`core/journey.ts`) only checks that `invariants` is a non-empty array of
|
|
32
|
+
* strings, not that each name resolves — so both spellings need to resolve
|
|
33
|
+
* for those journeys' declared invariants to actually run under `compare.ts`.
|
|
34
|
+
*/
|
|
35
|
+
export const INVARIANT_CHECKS = {
|
|
36
|
+
lifecycle: checkLifecycle,
|
|
37
|
+
"lifecycle-pairing": checkLifecycle,
|
|
38
|
+
"terminal-uniqueness": checkTerminalUniqueness,
|
|
39
|
+
leaks: checkLeaks,
|
|
40
|
+
"cleanup-leaks": checkLeaks,
|
|
41
|
+
"state-machine": checkStateMachine,
|
|
42
|
+
"protocol-validity": checkProtocolValidity
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/invariants/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AAEvC,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAG/D;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAA6C;IACxE,SAAS,EAAE,cAAc;IACzB,mBAAmB,EAAE,cAAc;IACnC,qBAAqB,EAAE,uBAAuB;IAC9C,KAAK,EAAE,UAAU;IACjB,eAAe,EAAE,UAAU;IAC3B,eAAe,EAAE,iBAAiB;IAClC,mBAAmB,EAAE,qBAAqB;CAC3C,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Leak-accounting invariants (docs/RELIABILITY_ARCHITECTURE.md §6 "Cleanup
|
|
3
|
+
* and leaks"): after the run, zero live actors, zero pending control
|
|
4
|
+
* responses, zero timers beyond the trigger timeout, zero pending Python
|
|
5
|
+
* bridge requests, and WS slot accounting (`activeJobs`/`startingJobs`) back
|
|
6
|
+
* to empty; and no growth in these counters across repeated journey
|
|
7
|
+
* iterations (leak journeys run N=20 and compare).
|
|
8
|
+
*
|
|
9
|
+
* Operates on `RunRecord.resourceCounters` (additive over C1's shape, see
|
|
10
|
+
* `record.ts`). A journey that declares this invariant but whose record
|
|
11
|
+
* carries no post-run snapshot fails on that: the check measured nothing, and
|
|
12
|
+
* "nothing measured" must not read as "no leaks". Drivers that can't
|
|
13
|
+
* instrument the counters should not declare `cleanup-leaks` for the surfaces
|
|
14
|
+
* they cover.
|
|
15
|
+
*/
|
|
16
|
+
import type { RunRecord } from "../record.js";
|
|
17
|
+
import type { Violation } from "./types.js";
|
|
18
|
+
declare const COUNTER_FIELDS: readonly ["liveActors", "pendingControlResponses", "pendingTimers", "pythonBridgePendingRequests", "activeJobs", "startingJobs"];
|
|
19
|
+
type CounterField = (typeof COUNTER_FIELDS)[number];
|
|
20
|
+
export declare function checkLeaks(record: RunRecord): Violation[];
|
|
21
|
+
export type { CounterField };
|
|
22
|
+
//# sourceMappingURL=leaks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leaks.d.ts","sourceRoot":"","sources":["../../../src/core/invariants/leaks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAA2B,SAAS,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,QAAA,MAAM,cAAc,kIAO2C,CAAC;AAEhE,KAAK,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpD,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,CAmDzD;AAED,YAAY,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const COUNTER_FIELDS = [
|
|
2
|
+
"liveActors",
|
|
3
|
+
"pendingControlResponses",
|
|
4
|
+
"pendingTimers",
|
|
5
|
+
"pythonBridgePendingRequests",
|
|
6
|
+
"activeJobs",
|
|
7
|
+
"startingJobs"
|
|
8
|
+
];
|
|
9
|
+
export function checkLeaks(record) {
|
|
10
|
+
const snapshots = record.resourceCounters ?? [];
|
|
11
|
+
const violations = [];
|
|
12
|
+
const afterSnapshots = snapshots.filter((s) => s.at === "after");
|
|
13
|
+
if (afterSnapshots.length === 0) {
|
|
14
|
+
return [
|
|
15
|
+
{
|
|
16
|
+
invariant: "leaks.not-instrumented",
|
|
17
|
+
message: `surface "${record.surface}" produced no post-run resource-counter ` +
|
|
18
|
+
"snapshot, so the cleanup-leaks invariant asserted nothing — " +
|
|
19
|
+
"instrument the driver (RunRecord.resourceCounters) or stop " +
|
|
20
|
+
"declaring cleanup-leaks for this surface",
|
|
21
|
+
details: { surface: record.surface, snapshots: snapshots.length }
|
|
22
|
+
}
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
afterSnapshots.forEach((snapshot, snapshotIndex) => {
|
|
26
|
+
for (const field of COUNTER_FIELDS) {
|
|
27
|
+
const value = snapshot[field];
|
|
28
|
+
if (value !== 0) {
|
|
29
|
+
violations.push({
|
|
30
|
+
invariant: `leaks.${field}-nonzero`,
|
|
31
|
+
message: `resource counter "${field}" is ${value} (expected 0) in post-run snapshot #${snapshotIndex}`,
|
|
32
|
+
details: { field, value, snapshotIndex }
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
// "No growth in open handles across repeated journey iterations": each
|
|
38
|
+
// field's post-run value must never exceed the previous iteration's.
|
|
39
|
+
for (const field of COUNTER_FIELDS) {
|
|
40
|
+
for (let i = 1; i < afterSnapshots.length; i++) {
|
|
41
|
+
const previous = afterSnapshots[i - 1][field];
|
|
42
|
+
const current = afterSnapshots[i][field];
|
|
43
|
+
if (current > previous) {
|
|
44
|
+
violations.push({
|
|
45
|
+
invariant: `leaks.${field}-growth`,
|
|
46
|
+
message: `resource counter "${field}" grew from ${previous} to ${current} ` +
|
|
47
|
+
`between post-run snapshots #${i - 1} and #${i}`,
|
|
48
|
+
details: { field, previous, current, snapshotIndex: i }
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return violations;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=leaks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leaks.js","sourceRoot":"","sources":["../../../src/core/invariants/leaks.ts"],"names":[],"mappings":"AAkBA,MAAM,cAAc,GAAG;IACrB,YAAY;IACZ,yBAAyB;IACzB,eAAe;IACf,6BAA6B;IAC7B,YAAY;IACZ,cAAc;CAC+C,CAAC;AAIhE,MAAM,UAAU,UAAU,CAAC,MAAiB;IAC1C,MAAM,SAAS,GAAG,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC;IAChD,MAAM,UAAU,GAAgB,EAAE,CAAC;IACnC,MAAM,cAAc,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;IAEjE,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO;YACL;gBACE,SAAS,EAAE,wBAAwB;gBACnC,OAAO,EACL,YAAY,MAAM,CAAC,OAAO,0CAA0C;oBACpE,8DAA8D;oBAC9D,6DAA6D;oBAC7D,0CAA0C;gBAC5C,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,MAAM,EAAE;aAClE;SACF,CAAC;IACJ,CAAC;IAED,cAAc,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE;QACjD,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;YAC9B,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;gBAChB,UAAU,CAAC,IAAI,CAAC;oBACd,SAAS,EAAE,SAAS,KAAK,UAAU;oBACnC,OAAO,EAAE,qBAAqB,KAAK,QAAQ,KAAK,uCAAuC,aAAa,EAAE;oBACtG,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE;iBACzC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,uEAAuE;IACvE,qEAAqE;IACrE,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;QACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/C,MAAM,QAAQ,GAAG,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YAC9C,MAAM,OAAO,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACzC,IAAI,OAAO,GAAG,QAAQ,EAAE,CAAC;gBACvB,UAAU,CAAC,IAAI,CAAC;oBACd,SAAS,EAAE,SAAS,KAAK,SAAS;oBAClC,OAAO,EACL,qBAAqB,KAAK,eAAe,QAAQ,OAAO,OAAO,GAAG;wBAClE,+BAA+B,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;oBAClD,OAAO,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE;iBACxD,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle invariants (docs/RELIABILITY_ARCHITECTURE.md §6 "Lifecycle").
|
|
3
|
+
*
|
|
4
|
+
* - Every `node_update running` is eventually paired with exactly one
|
|
5
|
+
* terminal `node_update` (`completed`/`error`/`cancelled`/`suspended`) for
|
|
6
|
+
* that invocation — a node can run more than once in a record (loops,
|
|
7
|
+
* re-entrant control events), so pairing is tracked as a per-node stack,
|
|
8
|
+
* not a single flag.
|
|
9
|
+
* - No node is left `running` after the `job_update` terminal.
|
|
10
|
+
*
|
|
11
|
+
* `terminal-uniqueness.ts` covers the job-level "exactly one terminal
|
|
12
|
+
* `job_update`" and precedence rules — kept separate so each §6 bullet has
|
|
13
|
+
* its own attributable module, per the task's "may fold into lifecycle if
|
|
14
|
+
* cleaner, but keep the checks individually attributable" note.
|
|
15
|
+
*/
|
|
16
|
+
import type { RunRecord } from "../record.js";
|
|
17
|
+
import type { Violation } from "./types.js";
|
|
18
|
+
export declare function checkLifecycle(record: RunRecord): Violation[];
|
|
19
|
+
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/core/invariants/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,CAgG7D"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { NODE_TERMINAL_STATUSES, JOB_TERMINAL_STATUSES, asString } from "./status-sets.js";
|
|
2
|
+
export function checkLifecycle(record) {
|
|
3
|
+
const violations = [];
|
|
4
|
+
/** node id -> stack of frame indices of unmatched "running" node_updates. */
|
|
5
|
+
const runningStack = new Map();
|
|
6
|
+
let jobTerminalIndex;
|
|
7
|
+
record.frames.forEach((frame, index) => {
|
|
8
|
+
const message = frame.message;
|
|
9
|
+
const type = asString(message, "type");
|
|
10
|
+
if (type === "job_update") {
|
|
11
|
+
const status = asString(message, "status");
|
|
12
|
+
if (jobTerminalIndex === undefined &&
|
|
13
|
+
status !== undefined &&
|
|
14
|
+
JOB_TERMINAL_STATUSES.has(status)) {
|
|
15
|
+
jobTerminalIndex = index;
|
|
16
|
+
}
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (type !== "node_update")
|
|
20
|
+
return;
|
|
21
|
+
const nodeId = asString(message, "node_id");
|
|
22
|
+
const status = asString(message, "status");
|
|
23
|
+
if (!nodeId || !status)
|
|
24
|
+
return;
|
|
25
|
+
if (status === "running") {
|
|
26
|
+
const stack = runningStack.get(nodeId) ?? [];
|
|
27
|
+
stack.push(index);
|
|
28
|
+
runningStack.set(nodeId, stack);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (NODE_TERMINAL_STATUSES.has(status)) {
|
|
32
|
+
const stack = runningStack.get(nodeId);
|
|
33
|
+
if (!stack || stack.length === 0) {
|
|
34
|
+
violations.push({
|
|
35
|
+
invariant: "lifecycle.terminal-without-running",
|
|
36
|
+
message: `node_update terminal ("${status}") for node "${nodeId}" has no ` +
|
|
37
|
+
"matching running node_update for its invocation",
|
|
38
|
+
frameIndex: index,
|
|
39
|
+
nodeId
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
stack.pop();
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
// Any invocation still unmatched by the end of the record.
|
|
47
|
+
for (const [nodeId, stack] of runningStack) {
|
|
48
|
+
if (stack.length === 0)
|
|
49
|
+
continue;
|
|
50
|
+
violations.push({
|
|
51
|
+
invariant: "lifecycle.unmatched-running",
|
|
52
|
+
message: `node "${nodeId}" has ${stack.length} running node_update(s) with ` +
|
|
53
|
+
"no terminal pairing by the end of the record",
|
|
54
|
+
nodeId,
|
|
55
|
+
frameIndex: stack[stack.length - 1],
|
|
56
|
+
details: { unmatchedCount: stack.length, frameIndices: stack }
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// "No node left running after job terminal": replay node_update
|
|
60
|
+
// running/terminal pairing only up to the terminal job_update frame, and
|
|
61
|
+
// flag any node whose running count is still open at that point.
|
|
62
|
+
if (jobTerminalIndex !== undefined) {
|
|
63
|
+
const openAtTerminal = new Map();
|
|
64
|
+
for (let i = 0; i <= jobTerminalIndex; i++) {
|
|
65
|
+
const message = record.frames[i].message;
|
|
66
|
+
if (asString(message, "type") !== "node_update")
|
|
67
|
+
continue;
|
|
68
|
+
const nodeId = asString(message, "node_id");
|
|
69
|
+
const status = asString(message, "status");
|
|
70
|
+
if (!nodeId || !status)
|
|
71
|
+
continue;
|
|
72
|
+
if (status === "running") {
|
|
73
|
+
openAtTerminal.set(nodeId, (openAtTerminal.get(nodeId) ?? 0) + 1);
|
|
74
|
+
}
|
|
75
|
+
else if (NODE_TERMINAL_STATUSES.has(status)) {
|
|
76
|
+
const current = openAtTerminal.get(nodeId) ?? 0;
|
|
77
|
+
if (current > 0)
|
|
78
|
+
openAtTerminal.set(nodeId, current - 1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
for (const [nodeId, openCount] of openAtTerminal) {
|
|
82
|
+
if (openCount <= 0)
|
|
83
|
+
continue;
|
|
84
|
+
violations.push({
|
|
85
|
+
invariant: "lifecycle.running-after-job-terminal",
|
|
86
|
+
message: `node "${nodeId}" is still "running" at the job_update terminal (frame ${jobTerminalIndex})`,
|
|
87
|
+
nodeId,
|
|
88
|
+
frameIndex: jobTerminalIndex,
|
|
89
|
+
details: { openCount }
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return violations;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../../src/core/invariants/lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol-validity.d.ts","sourceRoot":"","sources":["../../../src/core/invariants/protocol-validity.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAsH5C,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,CAWpE"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protocol-correctness invariants (docs/RELIABILITY_ARCHITECTURE.md §6
|
|
3
|
+
* "Protocol correctness"): every frame crossing every boundary validates
|
|
4
|
+
* against the generated Zod schema, in both directions.
|
|
5
|
+
*
|
|
6
|
+
* `server_to_client` frames validate against `processingMessageSchemas`
|
|
7
|
+
* (`@nodetool-ai/protocol`'s `messages.ts`, B1) for a `ProcessingMessage`
|
|
8
|
+
* `type`, or `outboundControlMessageSchemas` for the handful of recognized
|
|
9
|
+
* non-`ProcessingMessage` outbound shapes (`pong`, `rpc_response`,
|
|
10
|
+
* `system_stats`, `resource_change`). A frame whose `type` matches neither —
|
|
11
|
+
* or that has no `type` at all — is the ad hoc, type-less reply class
|
|
12
|
+
* `ws-commands.ts` documents as intentionally outside any schema (e.g.
|
|
13
|
+
* `{ error: "invalid_command" }`) and is left unvalidated, exactly as the WS
|
|
14
|
+
* runner's own outbound validation gate does.
|
|
15
|
+
*
|
|
16
|
+
* `client_to_server` frames validate against `webSocketCommandEnvelopeSchema`
|
|
17
|
+
* (and, when the command is a known `UnifiedCommandType`, that command's
|
|
18
|
+
* `commandDataSchemas` entry too) when the frame carries a `command`
|
|
19
|
+
* envelope, or `controlMessageInSchemas` for the un-enveloped control frames
|
|
20
|
+
* (`ping`, `client_tools_manifest`, `tool_result`, …).
|
|
21
|
+
*/
|
|
22
|
+
import { processingMessageSchemas, outboundControlMessageSchemas, webSocketCommandEnvelopeSchema, controlMessageInSchemas, commandDataSchemas } from "@nodetool-ai/protocol";
|
|
23
|
+
function messageType(message) {
|
|
24
|
+
const type = message["type"];
|
|
25
|
+
return typeof type === "string" ? type : undefined;
|
|
26
|
+
}
|
|
27
|
+
function issuesToString(issues) {
|
|
28
|
+
return issues
|
|
29
|
+
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
|
30
|
+
.join("; ");
|
|
31
|
+
}
|
|
32
|
+
function checkServerToClient(message, frameIndex) {
|
|
33
|
+
const type = messageType(message);
|
|
34
|
+
if (type !== undefined && type in processingMessageSchemas) {
|
|
35
|
+
const schema = processingMessageSchemas[type];
|
|
36
|
+
const result = schema.safeParse(message);
|
|
37
|
+
if (result.success)
|
|
38
|
+
return [];
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
invariant: "protocol-validity.server-to-client-schema",
|
|
42
|
+
message: `server_to_client frame of type "${type}" failed processingMessageSchema validation: ${issuesToString(result.error.issues)}`,
|
|
43
|
+
frameIndex,
|
|
44
|
+
details: { type, issues: result.error.issues }
|
|
45
|
+
}
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
if (type !== undefined && type in outboundControlMessageSchemas) {
|
|
49
|
+
const schema = outboundControlMessageSchemas[type];
|
|
50
|
+
const result = schema.safeParse(message);
|
|
51
|
+
if (result.success)
|
|
52
|
+
return [];
|
|
53
|
+
return [
|
|
54
|
+
{
|
|
55
|
+
invariant: "protocol-validity.server-to-client-schema",
|
|
56
|
+
message: `server_to_client frame of type "${type}" failed its outbound control schema: ${issuesToString(result.error.issues)}`,
|
|
57
|
+
frameIndex,
|
|
58
|
+
details: { type, issues: result.error.issues }
|
|
59
|
+
}
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
// Untyped, or a `type` neither schema map recognizes: the documented
|
|
63
|
+
// ad hoc reply class — not validated.
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
function checkClientToServer(message, frameIndex) {
|
|
67
|
+
if (typeof message["command"] === "string") {
|
|
68
|
+
const envelopeResult = webSocketCommandEnvelopeSchema.safeParse(message);
|
|
69
|
+
if (!envelopeResult.success) {
|
|
70
|
+
return [
|
|
71
|
+
{
|
|
72
|
+
invariant: "protocol-validity.client-to-server-envelope",
|
|
73
|
+
message: `client_to_server command frame failed webSocketCommandEnvelopeSchema: ${issuesToString(envelopeResult.error.issues)}`,
|
|
74
|
+
frameIndex,
|
|
75
|
+
details: { issues: envelopeResult.error.issues }
|
|
76
|
+
}
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
const command = message["command"];
|
|
80
|
+
const dataSchema = Object.prototype.hasOwnProperty.call(commandDataSchemas, command)
|
|
81
|
+
? commandDataSchemas[command]
|
|
82
|
+
: undefined;
|
|
83
|
+
if (dataSchema) {
|
|
84
|
+
const data = message["data"] ?? {};
|
|
85
|
+
const dataResult = dataSchema.safeParse(data);
|
|
86
|
+
if (!dataResult.success) {
|
|
87
|
+
return [
|
|
88
|
+
{
|
|
89
|
+
invariant: "protocol-validity.client-to-server-data",
|
|
90
|
+
message: `command "${command}" data failed its schema: ${issuesToString(dataResult.error.issues)}`,
|
|
91
|
+
frameIndex,
|
|
92
|
+
details: { command, issues: dataResult.error.issues }
|
|
93
|
+
}
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
const type = messageType(message);
|
|
100
|
+
if (type !== undefined && type in controlMessageInSchemas) {
|
|
101
|
+
const schema = controlMessageInSchemas[type];
|
|
102
|
+
const result = schema.safeParse(message);
|
|
103
|
+
if (result.success)
|
|
104
|
+
return [];
|
|
105
|
+
return [
|
|
106
|
+
{
|
|
107
|
+
invariant: "protocol-validity.client-to-server-control",
|
|
108
|
+
message: `client_to_server control frame of type "${type}" failed its schema: ${issuesToString(result.error.issues)}`,
|
|
109
|
+
frameIndex,
|
|
110
|
+
details: { type, issues: result.error.issues }
|
|
111
|
+
}
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
// No `command` envelope and no recognized control `type` — nothing to
|
|
115
|
+
// validate against.
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
export function checkProtocolValidity(record) {
|
|
119
|
+
const violations = [];
|
|
120
|
+
record.frames.forEach((frame, index) => {
|
|
121
|
+
const message = frame.message;
|
|
122
|
+
if (frame.direction === "server_to_client") {
|
|
123
|
+
violations.push(...checkServerToClient(message, index));
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
violations.push(...checkClientToServer(message, index));
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
return violations;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=protocol-validity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol-validity.js","sourceRoot":"","sources":["../../../src/core/invariants/protocol-validity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client state-machine conformance (docs/RELIABILITY_ARCHITECTURE.md §6
|
|
3
|
+
* "State transitions"): the client `WebSocketManager` transitions only along
|
|
4
|
+
* its declared state machine.
|
|
5
|
+
*
|
|
6
|
+
* `CLIENT_STATE_TRANSITIONS` below is a data copy of
|
|
7
|
+
* `web/src/lib/websocket/WebSocketManager.ts`'s `STATE_TRANSITIONS` table
|
|
8
|
+
* (~line 95) — the reliability harness package cannot depend on `web/`, so
|
|
9
|
+
* a real change to the client's state machine must be mirrored here too;
|
|
10
|
+
* `state-machine.test.ts` fixtures pin the exact transitions so a drift is
|
|
11
|
+
* at least visible in review even though nothing enforces the two files stay
|
|
12
|
+
* identical automatically.
|
|
13
|
+
*
|
|
14
|
+
* Operates on `RunRecord.transitions` (additive over C1's shape, see
|
|
15
|
+
* `record.ts`) — a driver that never touches a real client connection (e.g.
|
|
16
|
+
* the kernel driver) produces none, and this module reports no violations
|
|
17
|
+
* rather than treating absence itself as a violation.
|
|
18
|
+
*/
|
|
19
|
+
import type { RunRecord } from "../record.js";
|
|
20
|
+
import type { Violation } from "./types.js";
|
|
21
|
+
interface DeclaredTransition {
|
|
22
|
+
from: readonly string[];
|
|
23
|
+
to: string;
|
|
24
|
+
}
|
|
25
|
+
/** Mirrors `WebSocketManager.ts`'s `STATE_TRANSITIONS` exactly. */
|
|
26
|
+
export declare const CLIENT_STATE_TRANSITIONS: Readonly<Record<string, DeclaredTransition>>;
|
|
27
|
+
export declare function checkStateMachine(record: RunRecord): Violation[];
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=state-machine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-machine.d.ts","sourceRoot":"","sources":["../../../src/core/invariants/state-machine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAsB,SAAS,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,UAAU,kBAAkB;IAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,mEAAmE;AACnE,eAAO,MAAM,wBAAwB,EAAE,QAAQ,CAC7C,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAiBnC,CAAC;AA+DF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,CAWhE"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** Mirrors `WebSocketManager.ts`'s `STATE_TRANSITIONS` exactly. */
|
|
2
|
+
export const CLIENT_STATE_TRANSITIONS = {
|
|
3
|
+
connect: { from: ["disconnected", "failed"], to: "connecting" },
|
|
4
|
+
connected: { from: ["connecting", "reconnecting"], to: "connected" },
|
|
5
|
+
disconnect: {
|
|
6
|
+
from: ["connected", "connecting", "reconnecting"],
|
|
7
|
+
to: "disconnecting"
|
|
8
|
+
},
|
|
9
|
+
disconnected: {
|
|
10
|
+
from: ["disconnecting", "connecting", "connected", "reconnecting"],
|
|
11
|
+
to: "disconnected"
|
|
12
|
+
},
|
|
13
|
+
reconnect: { from: ["disconnected", "failed"], to: "reconnecting" },
|
|
14
|
+
failed: {
|
|
15
|
+
from: ["connecting", "reconnecting", "disconnected"],
|
|
16
|
+
to: "failed"
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
function checkOne(transition, index, priorTo) {
|
|
20
|
+
const violations = [];
|
|
21
|
+
const declared = CLIENT_STATE_TRANSITIONS[transition.action];
|
|
22
|
+
if (!declared) {
|
|
23
|
+
violations.push({
|
|
24
|
+
invariant: "state-machine.unknown-action",
|
|
25
|
+
message: `transition #${index} uses action "${transition.action}", which the declared state machine has no entry for`,
|
|
26
|
+
details: { index, action: transition.action }
|
|
27
|
+
});
|
|
28
|
+
return violations;
|
|
29
|
+
}
|
|
30
|
+
if (!declared.from.includes(transition.from)) {
|
|
31
|
+
violations.push({
|
|
32
|
+
invariant: "state-machine.invalid-from",
|
|
33
|
+
message: `action "${transition.action}" (transition #${index}) is not valid ` +
|
|
34
|
+
`from state "${transition.from}" — declared "from" states are: ` +
|
|
35
|
+
`${declared.from.join(", ")}`,
|
|
36
|
+
details: {
|
|
37
|
+
index,
|
|
38
|
+
action: transition.action,
|
|
39
|
+
from: transition.from,
|
|
40
|
+
declaredFrom: declared.from
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (declared.to !== transition.to) {
|
|
45
|
+
violations.push({
|
|
46
|
+
invariant: "state-machine.invalid-to",
|
|
47
|
+
message: `action "${transition.action}" (transition #${index}) declares "to": ` +
|
|
48
|
+
`"${declared.to}" but the record has "to": "${transition.to}"`,
|
|
49
|
+
details: {
|
|
50
|
+
index,
|
|
51
|
+
action: transition.action,
|
|
52
|
+
to: transition.to,
|
|
53
|
+
declaredTo: declared.to
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (priorTo !== undefined && priorTo !== transition.from) {
|
|
58
|
+
violations.push({
|
|
59
|
+
invariant: "state-machine.chain-gap",
|
|
60
|
+
message: `transition #${index} starts from "${transition.from}" but the ` +
|
|
61
|
+
`previous recorded transition ended in "${priorTo}"`,
|
|
62
|
+
details: { index, expectedFrom: priorTo, actualFrom: transition.from }
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return violations;
|
|
66
|
+
}
|
|
67
|
+
export function checkStateMachine(record) {
|
|
68
|
+
const transitions = record.transitions;
|
|
69
|
+
if (!transitions || transitions.length === 0)
|
|
70
|
+
return [];
|
|
71
|
+
const violations = [];
|
|
72
|
+
let priorTo;
|
|
73
|
+
transitions.forEach((transition, index) => {
|
|
74
|
+
violations.push(...checkOne(transition, index, priorTo));
|
|
75
|
+
priorTo = transition.to;
|
|
76
|
+
});
|
|
77
|
+
return violations;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=state-machine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-machine.js","sourceRoot":"","sources":["../../../src/core/invariants/state-machine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared status vocabulary for the lifecycle/terminal-uniqueness invariants.
|
|
3
|
+
* Not itself a §6 family — just the constants both modules need to stay in
|
|
4
|
+
* sync on what counts as "terminal" for a node vs. a job.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* `node_update` statuses that end an invocation. The kernel actor
|
|
8
|
+
* (`packages/kernel/src/actor.ts`) emits `"completed"`, `"error"`, and
|
|
9
|
+
* `"suspended"` — no node-level `"cancelled"`, since a cancelled run
|
|
10
|
+
* interrupts actors rather than walking each one to a terminal status; a
|
|
11
|
+
* `"cancelled"` node status is accepted too, for surfaces/fakes that do emit
|
|
12
|
+
* one. `"warning"` and `"running"` are deliberately excluded: `"warning"` is
|
|
13
|
+
* a mid-run aside, not a terminal status.
|
|
14
|
+
*/
|
|
15
|
+
export declare const NODE_TERMINAL_STATUSES: ReadonlySet<string>;
|
|
16
|
+
/** `job_update` statuses that end a run. `"running"`/`"queued"` are not terminal. */
|
|
17
|
+
export declare const JOB_TERMINAL_STATUSES: ReadonlySet<string>;
|
|
18
|
+
/**
|
|
19
|
+
* Terminal precedence, most to least important (`runner.ts:625`): cancel >
|
|
20
|
+
* suspend > failed > completed. Index doubles as precedence rank (lower
|
|
21
|
+
* wins).
|
|
22
|
+
*/
|
|
23
|
+
export declare const TERMINAL_PRECEDENCE: readonly string[];
|
|
24
|
+
/** Normalizes a status to the `TERMINAL_PRECEDENCE` vocabulary (`"error"` reads as `"failed"`). */
|
|
25
|
+
export declare function canonicalTerminalStatus(status: string): string;
|
|
26
|
+
export declare function asString(message: Record<string, unknown> | undefined, key: string): string | undefined;
|
|
27
|
+
//# sourceMappingURL=status-sets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-sets.d.ts","sourceRoot":"","sources":["../../../src/core/invariants/status-sets.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAMrD,CAAC;AAEH,qFAAqF;AACrF,eAAO,MAAM,qBAAqB,EAAE,WAAW,CAAC,MAAM,CAKpD,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAKhD,CAAC;AAEF,mGAAmG;AACnG,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE9D;AAQD,wBAAgB,QAAQ,CACtB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC5C,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAGpB"}
|