Repository navigation
Refuse service redrive of reused steps its SDKs can't replay yet - #1006
Conversation
…#864) Redrive of a run a service worker started (WorkflowStarted. workflow_definition_fingerprint_source = worker) could reuse memo upserts, search-attribute upserts and signal waits, whose reused_recorded_at service-mode SDKs do not replay yet. Such a run now refuses with service_redrive_history_not_qualified before any successor, history, cursor, row or projection write. Embedded runs and the already-qualified worker-sourced steps (activities, side effects, version markers, fired timers, start time) redrive as before. The fence lifts only through a replay-capability check once sdk-python#235, sdk-php#244 and sdk-rust#172 ship. Counterfactuals: a service run reusing each of a memo upsert, a search-attribute upsert and a signal wait is accepted on main and refused here with every engine table and projection unchanged; a service run reusing a side effect is still accepted. Replay corpus evidence: redrive-service-unqualified-history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Security final clearance for exact head a154b23. I independently verified:
Cleared to mark ready and merge only this exact head. Any source/head change returns to review. This clears the service fence only. Workflow #1005 and the 2.19.0 tag remain held until the release branch is refreshed onto this merge, the fence entry and public notes accurately say worker-sourced runs are refused, and that exact release head is separately reviewed. |
|
Security merge-boundary readback for Workflow #1006:
The reviewed service-mode fence is durably installed without merge-time source drift. Workflow #1005 may now be refreshed onto this merge, but its release head and public notes remain a separate Security gate; no 2.19.0 tag is cleared by this readback. |
|
Security forward-gate clarification after exact SDK reviews:
Therefore keep the 2.19 fence for Python/PHP signal-wait reuse and for any search-attribute history whose complete value+type identity the runtime cannot author and verify. Memo reuse can be qualified separately. Unknown runtime/capability combinations must continue to fail closed. The SDK PR comments carry the exact fixture and source holds. |



Implements the product fence General chose (#864) for Security's 2.19.0 service-mode hold (#864 comment 6614022929). It's a prerequisite for the 2.19.0 release PR #1005.
Problem
A run started by a service worker can be redriven on the same definition when the worker presents a matching fingerprint. Since #1001 and #1002, its successor can reuse
MemoUpserted,SearchAttributesUpsertedand signal-wait events (SignalWaitOpened,SignalReceived,SignalApplied) withreused_recorded_at. No service-mode SDK replays those reused keys yet (sdk-python#235, sdk-php#244, sdk-rust#172). An SDK worker could replay that successor at the redrive time, and documentation can't make an already-writable history safe.Change
FailedRunRedriveEligibility::evaluate()refuses withservice_redrive_history_not_qualifiedwhen:WorkflowStarted.workflow_definition_fingerprint_sourceisworker, andThe refusal runs before the definition decision and before any write. Run detail and describe report the same reason.
Unchanged:
The fence lifts only through a replay-capability/version check once those SDK issues ship installed-qualified. The CHANGELOG entry under Unreleased says so.
Tests
Local runs, PHP 8.4, SQLite + Redis:
DefaultWorkflowControlPlaneRedriveRejectionTest::testAServiceRunRefusesReusedStepsItsSdksAreNotQualifiedToReplayhas cases for a memo upsert, a search-attribute upsert and a signal wait. Aremote.redriveservice run gets that step at sequence 1 and a failed activity at 2. Each case asserts:message_cursor_position, commands, history, tasks, links, memos, search attributes, activities, failures),WorkflowRunSummary,WorkflowTimelineEntry),redrive_blocked_reason.All three are accepted on
mainand refused here.testAServiceRunStillRedrivesAQualifiedReusedSideEffect: a service run reusing a side effect is still accepted, with the reuse keys.Suites: rejection 47/47,
V2FailedRunRedriveTest28/28 (embedded unchanged),V2EmbeddedReplayRegressionCorpusTest19/19,FailedRunRedrivePlanTest11/11.Replay corpus: new
redrive-service-unqualified-historyfixture and scenario.validate-regression-corpus.py --base-ref c5b63c2cpasses with replaycounterfactual_fixture_paths: 1.ecsandphpstanare clean.🤖 Generated with Claude Code