Completes 3b83282273. The classifier and the self-healing reader landed there but
nothing stamped `leaseNodeId`, so the pre-boot reclaim path was unreachable.
Wiring: InProcessRuntime -> TaskExecutor -> WorkflowGraphTaskRunner ->
WorkflowGraphExecutor, which writes the field onto the pending lease.
The executor takes `getLocalNodeId`, a GETTER rather than a value, because the
runtime resolves the node id asynchronously (a CentralCore read) partway through
start() while `executorOptions` is built earlier in the same method. A snapshot
taken at construction would freeze `undefined` and silently disable attribution
forever -- the failure mode where the feature looks wired, typechecks, and never
fires. Reading it at runner-construction time picks up the resolved id.
With this, a review gate whose session dies to an engine restart is reclaimed on
the next self-healing pass instead of waiting out the 15-minute staleness floor.
Peer-owned and legacy unattributed leases still take the floor, so the
double-dispatch protection multi-node depends on is unchanged.
Adds five classifier cases: own-node pre-boot reclaims; peer-node, unattributed,
own-node-post-boot, and no-identity-supplied all still adopt. Verified the first
is not vacuous -- disabling the branch fails exactly that case (1 failed / 13
passed) and no other.
Verified: tsc clean on core and engine, pnpm lint clean, pnpm test:gate green
(299 + 10 + 70), plan-review-lease + plan-review-single-owner +
self-healing-orphaned-pending-step-results green (27).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Summary
The Coding (Ideas) workflow now behaves like the board it presents:
Ideas stays inert, Todo owns planning and plan review, In progress owns
implementation, and In review owns code review and merge. The restored
preset is intentionally limited to that five-stage path, while the
existing Coding workflow remains unchanged.
Workflow execution now suspends at Todo→In progress instead of running
the implementation node early. A durable, single-owner continuation
records the exact resume node and survives process restarts; the
scheduler remains the only component allowed to admit the task into WIP.
Disabled optional review groups traverse the same boundary without
invoking a reviewer, avoiding the prior stuck-task behavior.
Workflow validation also rejects capacity holds with no reachable WIP
destination, so deterministic lifecycle deadlocks fail at authoring time
rather than after a task is running.
Session-settled decisions carried from planning: columns are execution
invariants, scheduler-owned WIP admission is preserved, the existing
Coding (Ideas) preset is restored and simplified, and invalid release
topology is rejected (user-approved).
## Validation
- `pnpm lint`
- `pnpm verify:fast`
- `pnpm test:gate` (296 engine, 128 PostgreSQL core, and 63 CI-shape
tests)
- Focused workflow lifecycle tests (106 assertions)
- PostgreSQL regression coverage proves atomic continuation replacement
and database rejection of a second active owner
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added durable, resumable workflow execution across capacity boundaries
(including explicit suspend/resume at the correct node).
* Introduced Todo “plan review” workflow continuations and automated
planning/capacity draining.
* Restored Coding (Ideas) as a selectable built-in and updated its lane
placement; improved optional-step group enablement support.
* **Bug Fixes**
* User moves back to Todo now cancels active workflow continuations.
* Rejected workflow boundary transitions now surface as errors (instead
of silently continuing).
* Workflows with undriveable capacity-hold configurations are now
rejected.
* **Tests / Data**
* Expanded coverage for workflow suspension, continuations, and
continuation replacement; updated database schema to persist
continuation metadata and enforce single active continuation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Part **2 of 3** of the IR-driven lifecycle cutover (stacked on #2341;
top is #2335).
**Scope (80 files, packages/engine + cli/pi skill docs +
AGENTS/architecture):** graph-driven column moves via the
column-boundary controller (R1), single-mover scheduler/hold-release
trait cutover (KTD-2/KTD-9), trait re-keyed self-healing + merger with
the R7b confirmed-merge-must-finalize guarantee, graph-exclusive Plan
Review with leased dedup (R4/R5), the executeCore body-lift — zero
legacy re-entry — with fn_review_step + interceptor machinery deleted
and tombstone-ratcheted (R9), builtin workflow runtime fixes (missing
hold handler, unseamed-node column inheritance, no-merge completion
mover), the 6-column benchmark acceptance suite (11 tests) + 12-builtin
lifecycle sweep (94 assertions), and the executor test-harness
modernization. Also retires core's interpreter-cutover scaffolding whose
last consumer (the authoritative driver) dies here.
**Merge order:** #2341 → this → #2335. After #2341 merges, retarget this
to main.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Route workflow selections, model lanes, goals, skills, and reliability reads through project-scoped async stores. Recover heartbeat agents parked against an unrelated project model and preserve workflow JSONB patches atomically.
The step execution engine was already gone (runWorkflowSteps deleted,
workflow_steps table dropped in migration 132). This removes what remained:
the linear step compiler (compileWorkflowToSteps/validateLinearity/
WorkflowCompileError), which survived only as a validator + step-preview
generator.
parseWorkflowIr/validateV2 (which accepts branching graphs) is now the sole
workflow validity gate at save/select/refine and in the graph task runner.
Custom branching workflows are now selectable and run on the graph
interpreter instead of being rejected as non-linear.
- core: delete workflow-compiler.ts; rework store.validateWorkflowCompilable
onto parseWorkflowIr; move MERGE_REGION_NODE_KINDS into
workflow-lifecycle-validation; retag workflow-steps-to-ir as legacy lowering
- engine: drop the compiler double-validation in workflow-graph-task-runner
- dashboard: remove POST /api/workflows/:id/compile + client wrapper; drop the
interpreterOnly response field and editor banner; no post-save compile check
- i18n: remove the orphaned workflowNodes.interpreterOnly key across locales
- tests: reframe two workflow-selection tests whose premise inverted; fix a
pre-existing red in builtin-lead-generation (completion-summary node)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Classify write-capable graph nodes before handler dispatch and let executor adapters fulfill the declared worktree requirement, keeping custom-node execution out of lifecycle decision making.
Fusion-Task-Id: FN-7224
Move completed-step replay and proven-merge finalization into workflow-owned execution paths so graph restarts continue from live task projection instead of failing stale step nodes.
Fusion-Task-Id: FN-7224
Removes the legacy workflow-step EXECUTION path now that the graph records results
(U2): delete runWorkflowSteps(), the workflow-step seam + runWorkflowStep primitive
(runtime-primitives, workflow-node-handlers, authoritative-driver), and the legacy
execute() step blocks. Keeps task.workflowStepResults + its store write path (the
graph's sink) and executeWorkflowStep/executeScriptWorkflowStep (reused by the graph).
- Watchdog recoverCompletedTask now re-enters via maybeExecuteWorkflowGraph so the
graph re-runs pending gates, records results, and owns the in-review/back-for-fix
transition (KTD-2).
- maybeExecuteWorkflowGraph fails CLOSED (parks) when a store lacks
getTaskWorkflowSelection AND the task has enabled pre-merge steps — closing the
FN-7039 silent-skip class without changing minimal-store implementation runs (KTD-5).
KNOWN GAP (follow-up): the FN-4343 per-step workflowStepScopeEnforcement leak check
lived only in runWorkflowSteps and is NOT yet replicated on the graph path. Merge-time
File Scope enforcement (FileScopeViolationError, squash overlap) is unaffected.
Plan U4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enabled optional-group nodes now upsert their outcome into the existing
task.workflowStepResults field keyed by node id, and emit [pre-merge] logs at
parity with the legacy runWorkflowSteps path. Disabled groups stay byte-inert.
Reuses the existing WorkflowStepResult type + store.updateTask path (no new
table/type/store method). Unblocks the unified progress bar for graph-run steps.
Plan U2.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the three first-class PR workflow node kinds and their handlers via
createPrNodeHandlers(deps), registered in createDefaultNodeHandlers
(fail-closed pr-nodes-unwired when absent). GitHub ops are injected as
callbacks (PrNodeGithubOps) at all three CLI sites (daemon/serve/dashboard)
so the engine never imports the dashboard client (FN-3049). pr-create
routes open/failed as outcomes; pr-merge passes expectedHeadOid and never
writes 'merged' (reconcile corroborates); pr-respond delegates to an
injected respond callback (U5 fills the body). 10 tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Real engine seams: execute delegates to the legacy implementation phase via a
completion interceptor that stops execute() at the implementation-complete
boundary (no double review/merge); review performs the in-review handoff; merge
resolves through ProjectEngine.onMerge over the same serialized merge queue
(wired via a late-bound setMergeRequester, mirroring setMergeEnqueuer). Custom
graph nodes run on the proven WorkflowStep machinery (readonly tool policy,
verdict parsing). Adds a 'planning' seam to the vocabulary (no-op for
pre-specified tasks; custom planning is a prompt node today).
Entry point: execute() routes graph-selected tasks through the runner when
experimentalFeatures.workflowGraphExecutor is on, with process-wide routing
claims (FN-4811 posture), duplicate-dispatch dropping, pre-run errors falling
back to legacy, and mid-run errors parking the task in review (never re-running
the implementation, never stranding the task).
Flag off by default: all 587 executor tests pass unchanged.
Loads a task's selected workflow, runs the graph with injected legacy seams
(execute/review/merge) and a custom-node runner, and maps the terminal outcome
to completed/failed/fell-back. Any interpreter-level error falls back so the
caller can run the legacy pipeline — a task is never stranded. Covered with
fake seams: lifecycle ordering, failure routing, gate blocking, fallback
reasons, diagnostics isolation. Includes the interpreter-cutover plan doc.