feat(flows): node registry + executor (NF-2) #346

Closed
code-lead wants to merge 1 commit from boss/323 into main
Collaborator

Typed NodeRegistry (ten built-in node types) and a graph executor that walks a frozen plan in topological order with parallel fan-out, per-node timeout, external AbortSignal cancel, router.try error capture, and router.race first-arrived with loser-abort — no HTTP, no SQLite, no agent dispatch.

Test plan

  • 88 tests across registry.test.ts + executor.test.ts (66 executor, 22 registry)
  • Topo order, parallel fan-out, router.all fan-in, router.race loser cancel
  • Error propagation + router.try recovery path
  • Per-node timeout (default + args.timeout_ms override) + run-level cancel
  • Port-type compat, cycle detection, duplicate-id / unwired-port / multi-source rejection
  • just qa green (typecheck + biome check + biome format + full test suite — 1482 pass)

Closes #323

Typed `NodeRegistry` (ten built-in node types) and a graph executor that walks a frozen plan in topological order with parallel fan-out, per-node timeout, external `AbortSignal` cancel, `router.try` error capture, and `router.race` first-arrived with loser-abort — no HTTP, no SQLite, no agent dispatch. ## Test plan - [x] 88 tests across `registry.test.ts` + `executor.test.ts` (66 executor, 22 registry) - [x] Topo order, parallel fan-out, `router.all` fan-in, `router.race` loser cancel - [x] Error propagation + `router.try` recovery path - [x] Per-node timeout (default + `args.timeout_ms` override) + run-level cancel - [x] Port-type compat, cycle detection, duplicate-id / unwired-port / multi-source rejection - [x] `just qa` green (typecheck + biome check + biome format + full test suite — 1482 pass) Closes #323
feat(flows): node registry + executor (NF-2)
All checks were successful
qa / qa (pull_request) Successful in 4m14s
qa / dockerfile (pull_request) Successful in 10s
7213fe4640
Lands the foundational pair for M21 node flows: a typed, closed
NodeRegistry over ten primitive node types (source, router.filter /
switch / all / race / try, template.string, util.log / guard / delay),
and an executor that walks a frozen plan in topological order with
parallel fan-out, per-node timeout (60 s default, `args.timeout_ms`
override), external `AbortSignal` cancellation, router.try error
capture, and router.race first-arrived + loser-abort semantics.

Tests drive everything — no HTTP, no SQLite, no agent dispatch surface
here (those land in NF-3 / NF-4 / NF-7). 88 tests across two files
cover every acceptance-criteria bullet: topo order, parallel fan-out,
router.all fan-in, router.race winner + loser cancel, error
propagation with router.try escape hatch, skip propagation through
router.filter, per-node and default timeouts, pre-abort and in-flight
cancel, port-type compatibility at load time, unknown-type / missing-
source / multiple-source / duplicate-id / unwired-port / port-arity /
cycle detection.

Design notes on the executor:

- Errors only leak to the flow-level status when the erroring node has
  at least one non-catching direct successor. A leaf error is always
  uncaught; `router.try` downstream captures the error into a data
  edge and the flow stays ok.
- Per-node abort classification distinguishes (a) external cancel ->
  skipped with reason "run cancelled", (b) timeout -> error with the
  TimeoutError (timeout IS a failure), (c) race-loser cancel ->
  skipped with the race reason.
- The plan is frozen after load — topoOrder is stable (id-sorted
  waves) so deterministic tests and future replay / trace viewing
  hold.

Closes #323
Author
Collaborator

Rebase abandoned — NF-2 was already landed on main as #345 (commit cb65caa) while this PR was open. All five files (registry.ts, registry.test.ts, executor.ts, executor.test.ts, types.ts) come back as add/add conflicts against an equivalent implementation. Resolving them would mean either duplicating what's on main or wholesale replacing it — neither is a legitimate rebase outcome. Closing / superseding by #345 is the right call.

Rebase abandoned — NF-2 was already landed on `main` as #345 (commit `cb65caa`) while this PR was open. All five files (`registry.ts`, `registry.test.ts`, `executor.ts`, `executor.test.ts`, `types.ts`) come back as add/add conflicts against an equivalent implementation. Resolving them would mean either duplicating what's on `main` or wholesale replacing it — neither is a legitimate rebase outcome. Closing / superseding by #345 is the right call.
Author
Collaborator

Rebase on origin/main is not possible — the PR's design has been invalidated.

PR #345 already landed a competing NF-2 implementation and closed the same issue (#323). It introduced the exact same five files (executor.ts, executor.test.ts, registry.ts, registry.test.ts, types.ts under apps/server/src/domain/flows/), so every file in this PR conflicts as add/add against main. There is no shared-code surface to merge onto — the two commits are competing implementations of the same feature.

Suggested next step: close this PR. If any of the design choices here (e.g. per-node abort classification, id-sorted topo waves, the larger test matrix) are worth salvaging, open a follow-up issue against the already-merged implementation.

Rebase on `origin/main` is not possible — the PR's design has been invalidated. PR #345 already landed a competing NF-2 implementation and closed the same issue (#323). It introduced the exact same five files (`executor.ts`, `executor.test.ts`, `registry.ts`, `registry.test.ts`, `types.ts` under `apps/server/src/domain/flows/`), so every file in this PR conflicts as `add/add` against `main`. There is no shared-code surface to merge onto — the two commits are competing implementations of the same feature. Suggested next step: close this PR. If any of the design choices here (e.g. per-node abort classification, id-sorted topo waves, the larger test matrix) are worth salvaging, open a follow-up issue against the already-merged implementation.
Author
Collaborator

Closing as superseded by #345 which landed NF-2 first and is already on main. Thanks for the parallel attempt — the ticket is closed and the implementation that merged is the canonical one. Closing this branch to keep the PR list clean.

Closing as superseded by #345 which landed NF-2 first and is already on main. Thanks for the parallel attempt — the ticket is closed and the implementation that merged is the canonical one. Closing this branch to keep the PR list clean.
code-lead closed this pull request 2026-04-24 14:08:43 +00:00
All checks were successful
qa / qa (pull_request) Successful in 4m14s
Required
Details
qa / dockerfile (pull_request) Successful in 10s

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks!346
No description provided.