feat: implement node status signal management with computed preconditions and blockedByFailure

- Add computePreconditions and computeBlockedByFailure functions to node-status.ts
- Add registerStartEffect and registerAbortEffect for automatic state transitions
- Start effect: idle/waiting -> ready when preconditions met
- Abort effect: idle/waiting -> aborted when blockedByFailure true
- Refactor WorkflowReactiveRoot to use node-status.ts functions
- Root nodes auto-transition from idle to ready (no predecessors = preconditions true)
- Add AbortEffectOptions with abortDependents policy support
- Add comprehensive unit tests for all precondition and failure isolation scenarios
This commit is contained in:
2026-05-21 22:16:46 +00:00
parent 18999fb38e
commit e98204161d
5 changed files with 781 additions and 39 deletions

View File

@@ -48,11 +48,11 @@ function makeForkJoinGraph(): DirectedGraph {
describe("WorkflowReactiveRoot", () => {
describe("constructor and initializeSignals", () => {
it("initializes all nodes with idle status", () => {
it("root node starts as ready, downstream nodes start as idle", () => {
const graph = makeSimpleGraph();
const root = new WorkflowReactiveRoot(graph);
expect(root.statusMap.get("a")!.value).toBe("idle");
expect(root.statusMap.get("a")!.value).toBe("ready");
expect(root.statusMap.get("b")!.value).toBe("idle");
expect(root.statusMap.get("c")!.value).toBe("idle");
@@ -256,7 +256,7 @@ describe("WorkflowReactiveRoot", () => {
timestamp: "2026-01-01T00:00:00Z",
});
expect(root.statusMap.get("a")!.value).toBe("idle");
expect(root.statusMap.get("a")!.value).toBe("ready");
root.dispose();
});
@@ -291,11 +291,11 @@ describe("WorkflowReactiveRoot", () => {
});
describe("getStatus", () => {
it("returns idle for nodes with no events", () => {
it("returns ready for root nodes with no events", () => {
const graph = makeSimpleGraph();
const root = new WorkflowReactiveRoot(graph);
expect(root.getStatus("a")).toBe("idle");
expect(root.getStatus("a")).toBe("ready");
root.dispose();
});
@@ -322,9 +322,9 @@ describe("WorkflowReactiveRoot", () => {
const graph = makeSimpleGraph();
const root = new WorkflowReactiveRoot(graph);
root.statusMap.get("a")!.value = "waiting";
root.statusMap.get("b")!.value = "waiting";
expect(root.getStatus("a")).toBe("waiting");
expect(root.getStatus("b")).toBe("waiting");
root.dispose();
});
@@ -845,7 +845,7 @@ describe("WorkflowReactiveRoot", () => {
root.setRequestId("b", "req-b");
root.setRequestId("c", "req-c");
expect(root.getStatus("a")).toBe("idle");
expect(root.getStatus("a")).toBe("ready");
expect(root.getStatus("b")).toBe("idle");
expect(root.getStatus("c")).toBe("idle");