Eight tables, published by workflows:install (tag workflows-migrations, timestamped on publish; never auto-loaded). Names come from workflows.tables.*. Migrations are forward-only. Key types are fixed at migration time — set them before php artisan migrate:
- id and every package-internal key column (*_id pointing at a package table) follow primary_key_type (bigint auto-increment, or a time-ordered uuid / ulid primary key).
- A morph pair <name>_type + <name>_id (nullable) holds a host model. Its id column follows owner_key_type (owner_*), subject_key_type (subject_*) or key_type (publisher_*, starter_*, requester_*).
- Every timestamp is stored in UTC (created_at, updated_at, and deleted_at on soft-deleting tables).
workflow_definitions
| Column | Type | Notes |
|---|
id | key | — |
key | string(100) | Unique per owner (wdef_key_unique on owner_scope, key). |
name | string(200) | — |
description | text, nullable | — |
owner_type, owner_id | morph, nullable | Null = app-wide. |
owner_scope | string(191) | The owner as one string (<morph class>:<key>, * for app-wide), for the unique key. |
subject_key | string(100), nullable | The subject type’s registry key (never a class name). |
status | string(16) | DefinitionStatus. |
current_version_id | key, nullable, indexed | The published version new instances run. |
webhook_id | string(26), nullable, unique | The inbound hook id (minted at first publish, rotatable). |
timestamps, deleted_at | | Soft deletes; a trashed definition still holds its key. |
workflow_versions
| Column | Type | Notes |
|---|
definition_id | key | FK → definitions, restrict on delete. |
number | unsigned int | Unique per definition (wver_number_unique). |
status | string(16) | VersionStatus. |
schema_version | string(32) | roundly.workflows.graph/v1 |
graph / trigger / settings | json | The whole document, the trigger node’s config, the settings. |
trigger_type / trigger_key | string(16) / string(191), nullable | Indexed with status (wver_trigger_idx) for emit and webhook lookups. |
actor_mode | string(16) | ActorMode. |
checksum | char(64) | The canonical graph digest (expectedChecksum, sync, export). |
review_flags | json, nullable | Open template review flags. |
published_at | datetime, nullable | Once set, graph, trigger, actor, settings, checksum and publisher are frozen. |
publisher_type, publisher_id | morph, nullable | Who published. |
schedule_cursor_at | datetime, nullable | The last schedule slot handled. |
workflow_instances
| Column | Type | Notes |
|---|
ulid | char(26), unique | The public id (resources, commands, idempotency keys). |
definition_id / version_id | key | FKs → definitions / versions; the version never changes. |
owner_type, owner_id | morph, nullable | The instance owner. |
subject_key, subject_type, subject_id | string(100) / morph, nullable | Registry key and the subject. |
trigger_type / trigger_key / trigger_payload | string / json, nullable | How it started. |
idempotency_digest | char(64), nullable, indexed | — |
status | string(16) | InstanceStatus. |
current_node_id | string(64), nullable | — |
steps_count | unsigned int, default 0 | Counted against limits.max_steps. |
depth | unsigned tinyint, default 0 | start_workflow nesting. |
parent_instance_id, parent_step_run_id, root_instance_id | key, nullable | The parent (FK, null on delete) and the root. |
actor_mode, starter_type, starter_id | string(16) / morph, nullable | The actor and the triggering user. |
failure_code / failure_message / failed_node_id | string(64) / text / string(64), nullable | A FailureCode value or a host code; the message is stored for operators. |
started_at, ended_at | datetime, nullable | — |
lock_version | unsigned int, default 0 | Bumped on every state change. |
workflow_step_runs
| Column | Type | Notes |
|---|
instance_id | key | FK → instances, cascade on delete. |
node_id / node_type | string(64) | — |
visit / attempt | unsigned int / unsigned smallint | Unique with instance_id, node_id (wstep_attempt_unique). |
status | string(16) | StepRunStatus. |
idempotency_key | string(100) | wf:{instance ulid}:{node id}:{visit} |
input / output | json, nullable | The resolved config the handler saw (capped by steps_input.max_bytes) and its output. |
resume_signal | json, nullable | The signal the attempt was last resumed with; a retry of a failed resume carries it. |
outcome | string(32), nullable | — |
error_code / error_message / error_retryable | string(64) / text / bool, nullable | — |
claim_token / lease_until | string(26) / datetime, nullable | The compare-and-set claim and its lease (wstep_lease_idx). |
scheduled_for, dispatched_at, started_at, finished_at, probe_pushed_at | datetime, nullable | Timings; a pushed probe not claimed yet is re-pushed by wake. |
workflow_waits
| Column | Type | Notes |
|---|
instance_id / step_run_id | key | FKs, cascade on delete. |
kind | string(16) | WaitKind. |
due_at | datetime, nullable | wwait_due_idx (status, due_at). |
event_key | string(191), nullable | wwait_event_idx (status, event_key). |
match / condition / payload | json, nullable | Event matching, condition, delivered payload. |
outcome | string(32), nullable | A timeout’s outcome. |
status, claim_token, claimed_at, fired_at | | WaitStatus and the claim. |
workflow_escalations
| Column | Type | Notes |
|---|
instance_id / step_run_id | key | FKs, cascade on delete. |
level | unsigned smallint | 1 = the first. |
due_at | datetime | wesc_due_idx (status, due_at). |
actions | json | — |
status, attempts | string(16) / unsigned smallint | EscalationStatus and attempts made. |
claim_token, claimed_at, fired_at, cancelled_at, error_code, error_message | | — |
workflow_idempotency_keys
| Column | Type | Notes |
|---|
scope | string(16) | start or effect. |
key | char(64) | A digest, never the raw key; unique with scope (widem_unique). |
definition_id / instance_id | key, nullable | FKs (null / cascade on delete). |
result | json, nullable | A once() result. |
expires_at | datetime, nullable, indexed | Trigger keys only (idempotency.ttl). |
created_at | datetime | No soft deletes. |
workflow_dry_runs
| Column | Type | Notes |
|---|
definition_id / version_id | key / key, nullable | FKs (cascade / null on delete). |
owner_*, subject_*, requester_* | morphs, nullable | — |
input / result | json | The input and the DryRunResult. |
timestamps, deleted_at | | Pruned after prune.dry_runs_after. |
workflow_instances also has winst_overlay_idx (version_id, status, current_node_id) for the run overlay, winst_rate_idx (definition_id, created_at) for the start rate limit and winst_status_idx (status, updated_at) for the doctor’s stuck check.
Retention
workflows:prune (schedule daily) force-deletes ended instances older than prune.instances_after — never one with a running child — with their step runs, waits, escalations and effect keys; expired trigger keys; and dry-run logs older than prune.dry_runs_after.