Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions docs/server-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1258,10 +1258,38 @@ with at most 64 Unicode scalars / 256 UTF-8 bytes. The nullable UTC timestamp
preserves six fractional digits, for example `2026-11-05T12:00:01.123456Z`, and
matches describe's `info.last_skipped_at`. Invalid or oversized stored values
return HTTP 503 `storage_unavailable`. A later successful fire preserves an
earlier skip, so this pair is not the latest overall dispatch outcome. Full
last-dispatch/buffer summaries and published capability remain pending Server
#1014. An empty list cannot prove pair support; consumers retain describe
fallback and their current polling policy.
earlier skip, so this pair is not the latest overall dispatch outcome. The
dispatch summary has its own authority below. An empty list cannot prove
support for either contract.

Native list rows and describe responses also carry a top-level
`dispatch_summary` v1. Older producers omit it. The complete object contains
`version`, `state`, `last_outcome` and `buffered_actions_count`:

| State | Last outcome | Current buffer count |
| --- | --- | --- |
| `known` | Complete recorded dispatch | Integer from 0 through 9007199254740991 |
| `never` | `null`, established only by new creation | `0` |
| `unavailable` | `null`, including legacy or invalid stored authority | `null` |

A known outcome has exactly `id`, `outcome`, `recorded_at`, `reason`,
`workflow_instance_id` and `workflow_run_id`. Outcomes are `started`, `buffered`,
`skipped` or `failed`. Started outcomes name both new uppercase ULIDs and have
a null reason. Other outcomes have null workflow identities. Skipped and failed
outcomes keep the complete reason, including empty strings and whitespace;
buffered outcomes have a null reason. Times are UTC with six fractional digits.
The complete summary is bounded to 4096 serialized UTF-8 bytes. An outcome that
cannot fit the complete contract replaces earlier authority with unavailable
without changing the underlying scheduling operation or truncating its reason.

A valid unavailable summary overrides older legacy outcome evidence. Absent,
null, partial or unsupported summaries provide no v1 authority. A later describe
owns the whole summary, including absence or null; do not merge it with a list
snapshot. Count-only buffer changes retain the outcome ID and time, so a buffered
outcome can coexist with a current count of zero. Neither timestamp ranking nor
the latest-workflow pointer determines the latest dispatch. Current workflow
execution status remains separate. Native schema upgrades leave existing rows
unknown and do not reconstruct summaries from legacy arrays.

Schedule listing is namespace-scoped and never returns deleted schedules. The
optional `status` (`active` or `paused`) and `workflow_type` filters are exact
Expand Down
64 changes: 64 additions & 0 deletions resources/platform-protocol-specs/control-plane-api.openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1217,10 +1217,74 @@ components:
spec: { type: [object, "null"], additionalProperties: true }
action: { type: [object, "null"], additionalProperties: true }
search_attributes: { type: [object, "null"], additionalProperties: true }
dispatch_summary: { $ref: "#/components/schemas/ScheduleDispatchSummary" }
created_at: { type: string, format: date-time }
updated_at: { type: string, format: date-time }
schedule_count: { type: integer, minimum: 0, maximum: 200 }
next_page_token: { type: [string, "null"] }
ScheduleDispatchSummary:
description: >-
Native bounded dispatch authority, also at the top level of describe.
Older producers omit this member. Partial, null or unsupported objects
supply no v1 authority. A valid unavailable object overrides legacy
outcome evidence. A later describe owns its entire summary.
type: object
additionalProperties: false
required: [version, state, last_outcome, buffered_actions_count]
properties:
version: { type: integer, const: 1 }
state: { type: string, enum: [known, never, unavailable] }
last_outcome:
oneOf:
- { $ref: "#/components/schemas/ScheduleDispatchOutcome" }
- { type: "null" }
buffered_actions_count: { type: [integer, "null"], minimum: 0, maximum: 9007199254740991 }
oneOf:
- properties:
state: { const: known }
last_outcome: { $ref: "#/components/schemas/ScheduleDispatchOutcome" }
buffered_actions_count: { type: integer }
- properties:
state: { const: never }
last_outcome: { type: "null" }
buffered_actions_count: { const: 0 }
- properties:
state: { const: unavailable }
last_outcome: { type: "null" }
buffered_actions_count: { type: "null" }
ScheduleDispatchOutcome:
description: >-
Last committed dispatch, independently of current execution status.
Reason strings retain all text within 64 Unicode scalars and 256 UTF-8
bytes. Oversized or incomplete outcomes produce unavailable, without
changing the underlying scheduling operation. The complete summary is
at most 4096 serialized UTF-8 bytes.
type: object
additionalProperties: false
required: [id, outcome, recorded_at, reason, workflow_instance_id, workflow_run_id]
properties:
id: { type: string, pattern: "^[0-7][0-9A-HJKMNP-TV-Z]{25}$" }
outcome: { type: string, enum: [started, buffered, skipped, failed] }
recorded_at: { type: string, format: date-time, pattern: '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}Z$' }
reason: { type: [string, "null"], maxLength: 64 }
workflow_instance_id: { type: [string, "null"], pattern: "^[0-7][0-9A-HJKMNP-TV-Z]{25}$" }
workflow_run_id: { type: [string, "null"], pattern: "^[0-7][0-9A-HJKMNP-TV-Z]{25}$" }
oneOf:
- properties:
outcome: { const: started }
reason: { type: "null" }
workflow_instance_id: { type: string }
workflow_run_id: { type: string }
- properties:
outcome: { const: buffered }
reason: { type: "null" }
workflow_instance_id: { type: "null" }
workflow_run_id: { type: "null" }
- properties:
outcome: { enum: [skipped, failed] }
reason: { type: string }
workflow_instance_id: { type: "null" }
workflow_run_id: { type: "null" }
ScheduleListError:
type: object
additionalProperties: true
Expand Down
2 changes: 2 additions & 0 deletions rust/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,8 @@ table; commands admitted earlier have no digest. Stop the native nodes, back up
the database and run `schema-upgrade` before starting this binary on an
earlier schema; rolling back means restoring the backup.

Schedule dispatch summaries add a nullable scalar on `workflow_schedules`: SQLite schema 14, PostgreSQL and MariaDB 15, MySQL 16. Existing rows remain unknown, without an array scan or backfill. New dispatches and buffer mutations maintain the summary atomically. This step also requires stopped nodes, a backup and `schema-upgrade`; restore the backup to roll back. The wire states and bounds are documented in [Schedules](../docs/server-reference.md#schedules).

Implemented paths cover health/schema readiness, limited capability discovery,
workflow start/describe/paginated history, worker registration/heartbeat/removal,
workflow/activity polling, workflow lease renewal and both completion paths.
Expand Down
Loading
Loading