feat(openworkflow, dashboard, docs): add workflow signals#385
feat(openworkflow, dashboard, docs): add workflow signals#385jamescmartinez wants to merge 2 commits intomainfrom
Conversation
commit: |
There was a problem hiding this comment.
Pull request overview
Adds first-class “workflow signals” to OpenWorkflow so runs can durably wait for external/workflow-originated events and resume from buffered delivery after replay/restart.
Changes:
- Introduce
ow.sendSignal(),step.sendSignal(), andstep.waitForSignal()with durable buffering + schema validation + timeout behavior. - Persist/consume buffered signals in both SQLite and Postgres backends (including migrations) and update run wake-up logic to account for pending signals.
- Expand docs, architecture notes, dashboard labeling, and add comprehensive tests across worker + backend + client.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/openworkflow/worker/execution.ts | Implements step.sendSignal / step.waitForSignal, tracks active signal waits, and integrates wait timeouts into sleep/resume logic. |
| packages/openworkflow/worker/execution.test.ts | Adds worker-level integration tests for buffering, parking/resume, timeout, schema failure, and concurrency rejection. |
| packages/openworkflow/testing/backend.testsuite.ts | Adds backend conformance tests for signal FIFO buffering, idempotency, wake behavior, and error cases. |
| packages/openworkflow/sqlite/sqlite.ts | Adds SQLite migration for workflow_signals table + indexes. |
| packages/openworkflow/sqlite/sqlite.test.ts | Verifies migrations create workflow_signals and migrate() applies it. |
| packages/openworkflow/sqlite/backend.ts | Implements SQLite sendWorkflowSignal / consumeWorkflowSignal and wake reconciliation for parked signal waits. |
| packages/openworkflow/postgres/postgres.ts | Adds Postgres migration for workflow_signals table + indexes. |
| packages/openworkflow/postgres/postgres.test.ts | Asserts latest migration includes workflow_signals. |
| packages/openworkflow/postgres/backend.ts | Implements Postgres sendWorkflowSignal / consumeWorkflowSignal and wake reconciliation for parked signal waits. |
| packages/openworkflow/core/workflow-function.ts | Extends StepApi with signal methods and introduces StepWaitTimeout. |
| packages/openworkflow/core/step-attempt.ts | Adds new step kinds (signal-send, signal-wait) and a persisted signal-wait context helper. |
| packages/openworkflow/core/step-attempt.test.ts | Tests createSignalWaitContext. |
| packages/openworkflow/core/backend.ts | Extends backend interface with signal send/consume APIs. |
| packages/openworkflow/client/client.ts | Adds OpenWorkflow.sendSignal() client API (with optional idempotency key). |
| packages/openworkflow/client/client.test.ts | Tests client forwards signals to backend. |
| packages/docs/docs/workflows.mdx | Documents external sends via ow.sendSignal() and updates StepApi description. |
| packages/docs/docs/steps.mdx | Documents new step types (step.sendSignal, step.waitForSignal). |
| packages/docs/docs/signals.mdx | New end-to-end Signals documentation page. |
| packages/docs/docs/roadmap.mdx | Marks Signals as completed. |
| packages/docs/docs/overview.mdx | Updates overview to mention durable signal waiting. |
| packages/docs/docs/openworkflow-vs-temporal.mdx | Updates feature comparison now that signals exist. |
| packages/docs/docs.json | Adds Signals page to docs nav. |
| packages/dashboard/src/routes/runs/$runId.tsx | Improves display label for new step kinds and adds “Step Kind” metadata field. |
| ARCHITECTURE.md | Documents workflow_signals table and signal step APIs/semantics. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
This PR adds durable workflow signal support so workflow runs can buffer external events and resume deterministically when step.waitForSignal() consumes a matching signal.
Changes:
- Adds
ow.sendSignal(),step.sendSignal(), andstep.waitForSignal()APIs (including schema validation + timeouts). - Persists buffered signals in new
workflow_signalstables (SQLite/Postgres) and wakes parked runs when signals arrive. - Updates dashboard labeling and docs/architecture to describe signal behavior.
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/openworkflow/worker/execution.ts | Implements step.sendSignal() / step.waitForSignal() and integrates signal-wait into wake-up logic. |
| packages/openworkflow/worker/execution.test.ts | Adds extensive unit/integration tests for signal buffering, waiting, timeouts, replay behavior. |
| packages/openworkflow/testing/backend.testsuite.ts | Extends backend conformance tests for signal buffering, idempotency, wake behavior. |
| packages/openworkflow/sqlite/sqlite.ts | Adds SQLite migration v5 creating workflow_signals table + indexes. |
| packages/openworkflow/sqlite/sqlite.test.ts | Verifies migrations create workflow_signals and migrate() applies it. |
| packages/openworkflow/sqlite/backend.ts | Implements SQLite sendWorkflowSignal / consumeWorkflowSignal and wake reconciliation for pending signals. |
| packages/openworkflow/sqlite/backend.test.ts | Adds targeted tests for SQLite signal-send fallback/idempotency branches. |
| packages/openworkflow/postgres/postgres.ts | Adds Postgres migration v5 creating workflow_signals table + indexes. |
| packages/openworkflow/postgres/postgres.test.ts | Verifies latest migration includes workflow_signals. |
| packages/openworkflow/postgres/backend.ts | Implements Postgres sendWorkflowSignal / consumeWorkflowSignal and wake reconciliation for pending signals. |
| packages/openworkflow/postgres/backend.test.ts | Adds targeted tests for Postgres signal-send fallbacks and consume edge cases. |
| packages/openworkflow/core/workflow-function.ts | Extends StepApi with signal methods and introduces StepWaitTimeout alias. |
| packages/openworkflow/core/step-attempt.ts | Adds signal-send / signal-wait step kinds and createSignalWaitContext. |
| packages/openworkflow/core/step-attempt.test.ts | Tests createSignalWaitContext. |
| packages/openworkflow/core/backend.ts | Extends Backend interface with sendWorkflowSignal / consumeWorkflowSignal. |
| packages/openworkflow/client/client.ts | Adds OpenWorkflow.sendSignal() client API. |
| packages/openworkflow/client/client.test.ts | Tests OpenWorkflow.sendSignal() delegates correctly to backend. |
| packages/docs/docs/workflows.mdx | Documents external sends via ow.sendSignal() and updates StepApi parameter table. |
| packages/docs/docs/steps.mdx | Documents new step types for sending/waiting signals. |
| packages/docs/docs/signals.mdx | Adds new Signals documentation page with usage + semantics. |
| packages/docs/docs/roadmap.mdx | Marks signals as shipped. |
| packages/docs/docs/overview.mdx | Updates overview to mention durable signal waiting. |
| packages/docs/docs/openworkflow-vs-temporal.mdx | Updates comparison now that signals exist. |
| packages/docs/docs.json | Adds signals page to docs navigation. |
| packages/dashboard/src/routes/runs/$runId.tsx | Improves step kind labeling for signal steps and shows Step Kind in inspector. |
| ARCHITECTURE.md | Updates architecture docs to include workflow_signals and signal step APIs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Just gave the PR a quick read, in my opinion I don't think that requiring the workflow run ID to send a signal is the right approach. In many integrations the external system already has its own identifier, and forcing the workflow run ID means we now need to maintain a mapping between the two. For example with Resend: after sending (queuing) an email you get an ID, and their webhook (if the email was successfully sent) later sends that same ID. If signals require the workflow run ID, I now need to store a mapping between the email ID and the workflow run ID just to route the signal. For a suggestion, I like the approach used by Upstash Workflow where you wait on an event using a user-defined ID and then notify using that same ID. The workflow just does something like |
|
@Nic13Gamer thanks, this is really helpful feedback - it's something I've gone back and forth on. I started with run-addressed signals because it was simplest version to ship to testers first. That said, I'm still flexible on the shape and want to see how these integration patterns show up in practice (your Resend example was great). |
🚧 wip
This PR adds workflow signal support so runs can durably wait events and resume when a matching signal arrives.
Signals are workflow run-addressed and durably buffered (not waiter-only), which means a signal can be sent before a workflow reaches
waitForSignal(), and it will still be delivered after replay/restart.Changes
ow.sendSignal(),step.sendSignal(), andstep.waitForSignal()