Skip to content

Commit 3ae768c

Browse files
Enumerate every v2 history event wire format in the API stability contract.
Enumerate v2 history event wire formats
1 parent 2c2c72e commit 3ae768c

2 files changed

Lines changed: 68 additions & 6 deletions

File tree

docs/api-stability.md

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,22 +149,62 @@ cross-SDK replay:
149149

150150
| event | frozen payload keys | primary replay / projection consumers |
151151
| --- | --- | --- |
152+
| `StartAccepted` | `workflow_command_id`, `workflow_instance_id`, `workflow_run_id`, `workflow_class`, `workflow_type`, `business_key`, `visibility_labels`, `memo`, `search_attributes`, `outcome`, `rejection_reason` | `HistoryTimeline`, `HistoryExport`, `RunCommandContract`, operator command projections |
153+
| `StartRejected` | `workflow_command_id`, `workflow_instance_id`, `workflow_run_id`, `workflow_class`, `workflow_type`, `business_key`, `visibility_labels`, `memo`, `search_attributes`, `outcome`, `rejection_reason` | `HistoryTimeline`, `HistoryExport`, `RunCommandContract`, operator command projections |
152154
| `WorkflowStarted` | `workflow_class`, `workflow_type`, `workflow_instance_id`, `workflow_run_id`, `workflow_command_id`, `business_key`, `visibility_labels`, `memo`, `search_attributes`, `execution_timeout_seconds`, `run_timeout_seconds`, `execution_deadline_at`, `run_deadline_at`, `workflow_definition_fingerprint`, `declared_queries`, `declared_query_contracts`, `declared_signals`, `declared_signal_contracts`, `declared_updates`, `declared_update_contracts`, `declared_entry_method`, `declared_entry_mode`, `declared_entry_declaring_class` | `WorkflowDefinitionFingerprint`, `RunLineageView`, worker history payload consumers |
155+
| `WorkflowContinuedAsNew` | `sequence`, `continued_to_run_id`, `continued_to_run_number`, `workflow_link_id`, `closed_reason` | `WorkflowStepHistory`, `RunLineageView`, `HistoryTimeline`, operator detail projections |
153156
| `ActivityScheduled` | `activity_execution_id`, `activity_class`, `activity_type`, `sequence`, `activity` | `WorkflowStepHistory`, `WorkflowExecutor`, `QueryStateReplayer`, `ActivityRecovery` |
157+
| `ActivityStarted` | `activity_execution_id`, `activity_attempt_id`, `activity_class`, `activity_type`, `sequence`, `attempt_number`, `activity`, `parallel_group_path` | `ActivitySnapshot`, `ActivityAttemptSnapshots`, `HistoryTimeline`, `RunActivityView` |
158+
| `ActivityHeartbeatRecorded` | `activity_execution_id`, `activity_attempt_id`, `activity_class`, `activity_type`, `sequence`, `attempt_number`, `heartbeat_at`, `lease_expires_at`, `activity`, `activity_attempt`, `progress` | `ActivitySnapshot`, `ActivityAttemptSnapshots`, `HistoryTimeline`, `RunActivityView` |
159+
| `ActivityRetryScheduled` | `activity_execution_id`, `activity_attempt_id`, `activity_class`, `activity_type`, `sequence`, `retry_task_id`, `retry_of_task_id`, `retry_available_at`, `retry_backoff_seconds`, `retry_after_attempt_id`, `retry_after_attempt`, `max_attempts`, `retry_policy`, `timeout_kind`, `exception_type`, `exception_class`, `message`, `code`, `exception`, `activity`, `parallel_group_path` | `ActivitySnapshot`, `ActivityAttemptSnapshots`, `HistoryTimeline`, `RunTaskView` |
154160
| `ActivityCompleted` | `activity_execution_id`, `activity_attempt_id`, `activity_class`, `activity_type`, `sequence`, `attempt_number`, `result`, `payload_codec`, `activity`, `parallel_group_path` | `WorkflowExecutor`, `QueryStateReplayer`, `ParallelChildGroup`, `ActivityRecovery` |
161+
| `ActivityFailed` | `activity_execution_id`, `activity_attempt_id`, `activity_class`, `activity_type`, `sequence`, `attempt_number`, `failure_id`, `failure_category`, `non_retryable`, `exception_type`, `exception_class`, `message`, `code`, `exception`, `activity`, `parallel_group_path`, `structural_limit_kind`, `structural_limit_value`, `structural_limit_configured` | `ActivitySnapshot`, `FailureSnapshots`, `HistoryTimeline`, `ParallelFailureSelector` |
162+
| `ActivityCancelled` | `workflow_command_id`, `activity_execution_id`, `activity_attempt_id`, `activity_class`, `activity_type`, `sequence`, `attempt_number`, `cancelled_at`, `activity`, `activity_attempt` | `ActivitySnapshot`, `ActivityAttemptSnapshots`, `HistoryTimeline`, cancellation repair projections |
163+
| `ActivityTimedOut` | `activity_execution_id`, `activity_attempt_id`, `activity_class`, `activity_type`, `sequence`, `attempt_number`, `failure_id`, `failure_category`, `timeout_kind`, `message`, `exception_class`, `schedule_deadline_at`, `close_deadline_at`, `schedule_to_close_deadline_at`, `heartbeat_deadline_at`, `activity`, `parallel_group_path` | `ActivitySnapshot`, `FailureSnapshots`, `HistoryTimeline`, timeout repair projections |
155164
| `TimerScheduled` | `timer_id`, `sequence`, `delay_seconds`, `fire_at`, `timer_kind`, `condition_wait_id`, `condition_key`, `condition_definition_fingerprint`, `signal_wait_id`, `signal_name` | `WorkflowStepHistory`, `QueryStateReplayer`, `RunTimerView`, `ConditionWaits`, `SignalWaits` |
156165
| `TimerFired` | `timer_id`, `sequence`, `delay_seconds`, `fired_at`, `timer_kind`, `condition_wait_id`, `condition_key`, `condition_definition_fingerprint`, `signal_wait_id`, `signal_name` | `WorkflowStepHistory`, `QueryStateReplayer`, `RunTimerView`, `ConditionWaits`, `SignalWaits` |
166+
| `TimerCancelled` | `timer_id`, `sequence`, `delay_seconds`, `fire_at`, `timer_kind`, `condition_wait_id`, `condition_key`, `condition_definition_fingerprint`, `signal_wait_id`, `signal_name`, `cancelled_at` | `WorkflowStepHistory`, `RunTimerView`, `ConditionWaits`, `SignalWaits`, `HistoryTimeline` |
157167
| `SignalReceived` | `workflow_command_id`, `signal_id`, `workflow_instance_id`, `workflow_run_id`, `signal_name`, `signal_wait_id` | `SignalWaits`, `RunSignalView`, worker history payload consumers |
158168
| `SignalApplied` | `workflow_command_id`, `signal_id`, `signal_name`, `signal_wait_id`, `sequence`, `value` | `WorkflowStepHistory`, `SignalWaits`, `RunSignalView`, `QueryStateReplayer` |
169+
| `SignalWaitOpened` | `signal_name`, `signal_wait_id`, `sequence`, `timeout_seconds` | `WorkflowStepHistory`, `SignalWaits`, `RunSignalView`, `HistoryTimeline` |
159170
| `UpdateAccepted` | `workflow_command_id`, `update_id`, `workflow_instance_id`, `workflow_run_id`, `update_name`, `arguments` | `RunUpdateView`, worker history payload consumers |
171+
| `UpdateRejected` | `workflow_command_id`, `update_id`, `workflow_instance_id`, `workflow_run_id`, `update_name`, `arguments`, `validation_errors` | `RunUpdateView`, `HistoryTimeline`, command-contract projections |
160172
| `UpdateApplied` | `workflow_command_id`, `update_id`, `workflow_instance_id`, `workflow_run_id`, `update_name`, `arguments`, `sequence` | `QueryStateReplayer`, `RunUpdateView`, worker history payload consumers |
161-
| `UpdateCompleted` | `workflow_command_id`, `update_id`, `workflow_instance_id`, `workflow_run_id`, `update_name`, `sequence`, `result` | `RunUpdateView`, worker history payload consumers |
173+
| `UpdateCompleted` | `workflow_command_id`, `update_id`, `workflow_instance_id`, `workflow_run_id`, `update_name`, `sequence`, `result`, `failure_id`, `failure_category`, `non_retryable`, `exception_type`, `exception_class`, `message`, `code`, `exception` | `RunUpdateView`, worker history payload consumers |
162174
| `ConditionWaitOpened` | `condition_wait_id`, `condition_key`, `condition_definition_fingerprint`, `sequence`, `timeout_seconds` | `WorkflowStepHistory`, `ConditionWaits`, worker history payload consumers |
175+
| `ConditionWaitSatisfied` | `condition_wait_id`, `condition_key`, `condition_definition_fingerprint`, `sequence`, `timer_id`, `timeout_seconds` | `WorkflowStepHistory`, `ConditionWaits`, `QueryStateReplayer`, `HistoryTimeline` |
176+
| `ConditionWaitTimedOut` | `condition_wait_id`, `condition_key`, `condition_definition_fingerprint`, `sequence`, `timer_id`, `timeout_seconds` | `WorkflowStepHistory`, `ConditionWaits`, `QueryStateReplayer`, `HistoryTimeline` |
163177
| `SideEffectRecorded` | `sequence`, `result` | `WorkflowStepHistory`, `WorkflowExecutor`, `QueryStateReplayer` |
164178
| `VersionMarkerRecorded` | `sequence`, `change_id`, `version`, `min_supported`, `max_supported` | `WorkflowStepHistory`, `WorkflowExecutor`, `QueryStateReplayer` |
165179
| `ChildWorkflowScheduled` | `sequence`, `workflow_link_id`, `child_call_id`, `child_workflow_instance_id`, `child_workflow_run_id`, `child_workflow_class`, `child_workflow_type`, `parent_close_policy`, `retry_policy`, `timeout_policy` | `WorkflowStepHistory`, `WorkflowExecutor`, `QueryStateReplayer`, `ChildRunHistory`, `RunLineageView` |
166180
| `ChildRunStarted` | `sequence`, `workflow_link_id`, `child_call_id`, `child_workflow_instance_id`, `child_workflow_run_id`, `child_workflow_class`, `child_workflow_type`, `child_run_number`, `retry_policy`, `timeout_policy`, `execution_timeout_seconds`, `run_timeout_seconds`, `execution_deadline_at`, `run_deadline_at` | `ChildRunHistory`, `RunLineageView`, worker history payload consumers |
167181
| `ChildRunCompleted` | `sequence`, `workflow_link_id`, `child_call_id`, `child_workflow_instance_id`, `child_workflow_run_id`, `child_workflow_class`, `child_workflow_type`, `child_run_number`, `child_status`, `closed_reason`, `closed_at`, `output`, `parallel_group_path` | `WorkflowExecutor`, `QueryStateReplayer`, `ChildRunHistory`, `ParallelChildGroup`, `RunLineageView` |
182+
| `ChildRunFailed` | `sequence`, `workflow_link_id`, `child_call_id`, `child_workflow_instance_id`, `child_workflow_run_id`, `child_workflow_class`, `child_workflow_type`, `child_run_number`, `child_status`, `closed_reason`, `closed_at`, `failure_id`, `failure_category`, `exception_type`, `exception_class`, `message`, `exception`, `parallel_group_path` | `ChildRunHistory`, `FailureSnapshots`, `ParallelFailureSelector`, `RunLineageView` |
183+
| `ChildRunCancelled` | `sequence`, `workflow_link_id`, `child_call_id`, `child_workflow_instance_id`, `child_workflow_run_id`, `child_workflow_class`, `child_workflow_type`, `child_run_number`, `child_status`, `closed_reason`, `closed_at`, `parallel_group_path` | `ChildRunHistory`, `FailureSnapshots`, `RunLineageView`, parent-close projections |
184+
| `ChildRunTerminated` | `sequence`, `workflow_link_id`, `child_call_id`, `child_workflow_instance_id`, `child_workflow_run_id`, `child_workflow_class`, `child_workflow_type`, `child_run_number`, `child_status`, `closed_reason`, `closed_at`, `parallel_group_path` | `ChildRunHistory`, `FailureSnapshots`, `RunLineageView`, parent-close projections |
185+
| `SearchAttributesUpserted` | `sequence`, `attributes`, `merged` | `WorkflowStepHistory`, `HistoryTimeline`, visibility/search projections, history export |
186+
| `MemoUpserted` | `sequence`, `entries`, `merged` | `WorkflowStepHistory`, `HistoryTimeline`, run detail projections, history export |
187+
| `RepairRequested` | `workflow_command_id`, `workflow_instance_id`, `workflow_run_id`, `command_type`, `outcome`, `liveness_state`, `wait_kind`, `task_id`, `task_type` | `HistoryTimeline`, `RunCommandContract`, repair diagnostics, operator detail projections |
188+
| `CancelRequested` | `workflow_command_id`, `workflow_instance_id`, `workflow_run_id`, `command_type`, `reason` | `HistoryTimeline`, `RunCommandContract`, cancellation projections |
189+
| `WorkflowCancelled` | `workflow_command_id`, `workflow_instance_id`, `workflow_run_id`, `failure_id`, `failure_category`, `closed_reason`, `exception_class`, `message`, `reason` | `HistoryTimeline`, `FailureSnapshots`, `ChildRunHistory`, cancellation projections |
190+
| `TerminateRequested` | `workflow_command_id`, `workflow_instance_id`, `workflow_run_id`, `command_type`, `reason` | `HistoryTimeline`, `RunCommandContract`, termination projections |
191+
| `WorkflowTerminated` | `workflow_command_id`, `workflow_instance_id`, `workflow_run_id`, `failure_id`, `failure_category`, `closed_reason`, `exception_class`, `message`, `reason` | `HistoryTimeline`, `FailureSnapshots`, `ChildRunHistory`, termination projections |
192+
| `ArchiveRequested` | `workflow_command_id`, `workflow_instance_id`, `workflow_run_id`, `command_type`, `outcome`, `reason` | `HistoryTimeline`, `RunCommandContract`, archive/export projections |
193+
| `WorkflowArchived` | `workflow_command_id`, `workflow_instance_id`, `workflow_run_id`, `archive_command_id`, `reason` | `HistoryTimeline`, archive/export projections, operator detail projections |
194+
| `WorkflowTimedOut` | `failure_id`, `timeout_kind`, `failure_category`, `message`, `exception_class`, `execution_deadline_at`, `run_deadline_at` | `FailureSnapshots`, `HistoryTimeline`, `ChildRunHistory`, timeout repair projections |
195+
| `WorkflowCompleted` | `output` | `ChildRunHistory`, `HistoryTimeline`, history export, parent resume projections |
196+
| `WorkflowFailed` | `failure_id`, `source_kind`, `source_id`, `failure_category`, `non_retryable`, `exception_type`, `exception_class`, `message`, `exception`, `structural_limit_kind`, `structural_limit_value`, `structural_limit_configured` | `FailureSnapshots`, `HistoryTimeline`, `ChildRunHistory`, failure repair projections |
197+
| `FailureHandled` | `failure_id`, `sequence`, `failure_category`, `source_kind`, `source_id`, `propagation_kind`, `exception_class`, `exception_type`, `message`, `handled` | `FailureSnapshots`, `HistoryTimeline`, `QueryStateReplayer`, operator detail projections |
198+
| `ParentClosePolicyApplied` | `child_instance_id`, `child_run_id`, `policy`, `reason` | `HistoryTimeline`, parent-close diagnostics, history export |
199+
| `ParentClosePolicyFailed` | `child_instance_id`, `child_run_id`, `policy`, `reason`, `error` | `HistoryTimeline`, parent-close diagnostics, history export |
200+
| `MessageCursorAdvanced` | `stream_key`, `previous_position`, `new_position` | `MessageStreamCursor`, `HistoryTimeline`, signal/update interleave diagnostics |
201+
| `ScheduleCreated` | `spec`, `action`, `overlap_policy`, `next_fire_at`, `command_context` | `WorkflowScheduleHistoryEvent`, schedule audit views, history export |
202+
| `SchedulePaused` | `reason`, `paused_at`, `command_context` | `WorkflowScheduleHistoryEvent`, schedule audit views, history export |
203+
| `ScheduleResumed` | `next_fire_at`, `command_context` | `WorkflowScheduleHistoryEvent`, schedule audit views, history export |
204+
| `ScheduleUpdated` | `changed_fields`, `spec`, `action`, `overlap_policy`, `next_fire_at`, `command_context` | `WorkflowScheduleHistoryEvent`, schedule audit views, history export |
205+
| `ScheduleTriggered` | `workflow_instance_id`, `workflow_run_id`, `schedule_id`, `schedule_ulid`, `cron_expression`, `timezone`, `overlap_policy`, `outcome`, `effective_overlap_policy`, `trigger_number`, `occurrence_time`, `command_context` | `WorkflowScheduleHistoryEvent`, workflow run history, schedule audit views |
206+
| `ScheduleDeleted` | `reason`, `deleted_at`, `command_context` | `WorkflowScheduleHistoryEvent`, schedule audit views, history export |
207+
| `ScheduleTriggerSkipped` | `reason`, `skipped_trigger_count`, `last_skipped_at`, `command_context` | `WorkflowScheduleHistoryEvent`, schedule audit views, history export |
168208

169209
The key list is a wire-format list, not a promise that every event row
170210
contains every key. Some keys are optional because older rows predate a
@@ -234,11 +274,14 @@ and the Python replay site (`repos/sdk-python/src/durable_workflow/workflow.py`)
234274
in the same change.
235275

236276
**Broader history-event taxonomy.** The same freeze-at-stable rule
237-
applies to every `HistoryEventType` case. The full per-event schema
238-
enumeration is tracked as follow-up work before 2.0.0 stable; until
239-
then, treat `Workflow\V2\Support\DefaultWorkflowTaskBridge` as the
240-
authoritative emission site and `Workflow\V2\Models\WorkflowHistoryEvent`
241-
rows as the authoritative persisted shape.
277+
applies to every `HistoryEventType` case, including schedule audit
278+
events stored in `workflow_schedule_history_events`. Representative PHP
279+
emit-site guards currently cover the replay-critical subset above; until
280+
every producer is source-guarded, treat each documented table row as the
281+
minimum stable wire-format contract and treat
282+
`Workflow\V2\Models\WorkflowHistoryEvent` /
283+
`Workflow\V2\Models\WorkflowScheduleHistoryEvent` rows as the
284+
authoritative persisted shape.
242285

243286
## Signal And Update Payload Decode Failures
244287

tests/Unit/V2/HistoryEventWireFormatDocumentationTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Tests\Unit\V2;
66

77
use PHPUnit\Framework\TestCase;
8+
use Workflow\V2\Enums\HistoryEventType;
89

910
/**
1011
* DB-free guards for the replay-critical history-event schema documented in
@@ -295,6 +296,24 @@ public function testReplayCriticalEventsAreDocumentedWithFrozenKeysAndConsumers(
295296
}
296297
}
297298

299+
public function testEveryHistoryEventTypeHasADocumentedWireFormatRow(): void
300+
{
301+
$document = $this->fileContents('docs/api-stability.md');
302+
303+
foreach (HistoryEventType::cases() as $case) {
304+
$row = $this->documentationRow($document, $case->value);
305+
306+
$this->assertMatchesRegularExpression(
307+
'/^\| `[^`]+` \| [^|]*`[a-z_]+`[^|]* \| [^|]*[A-Za-z][^|]* \|$/',
308+
$row,
309+
sprintf(
310+
'%s must document frozen payload keys and at least one replay/projection consumer.',
311+
$case->value,
312+
),
313+
);
314+
}
315+
}
316+
298317
public function testRepresentativePhpEmitSitesStillUseDocumentedKeySets(): void
299318
{
300319
foreach (self::REPRESENTATIVE_EMIT_SITES as $eventType => $site) {

0 commit comments

Comments
 (0)