You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Model ActivityCancelled and next-sequence delivery in the redrive successor check #244
Follow-up from General's interim review of #242 (#239), at #242 (comment). These gaps also exist on main. They don't affect the non-leakage claim. They do make the test check check_redrive_successor (tests/test_replay_clock.py) narrower than what Workflow 28893f97 can write.
Gaps
ActivityCancelled is not a known event. Core can record ActivityCancelled for the successor's recorded activity between the cancellation request and the delivery. REDRIVE_EVENT_IDENTITY doesn't list it, so a Core-valid successor with that row is refused as unexpected event ActivityCancelled. It must also count as a resolution only when it precedes the request, matching Core CooperativeCancellationDelivery::RESOLUTION_TYPES and resolvedBeforeRequest.
An unscheduled delivery is accepted only at resume_step_sequence. Core validateCallBoundary accepts a delivery at WorkflowStepHistory::nextDurableCommandSequence (the next step after the highest recorded one), so a successor that resolved step 3 can have a delivery consume an unscheduled step 4. The check refuses that.
The rule should become "the next durable sequence when every earlier successor call is resolved", or the narrowing should be stated as a decision.
Deliberate narrowing to record or lift:parallel and selection_handle deliveries (and sequence_span / operation_sequence) are refused. Core accepts them under parallelEligibility and the selection rules. No successor fixture uses those shapes today.
Done when
Gap 1: ActivityCancelled is accepted with its identity and resolution semantics. Tests cover a Core-shaped history with it before the delivery (accepted) and before the request (delivery refused).
Gap 2: the next-sequence rule matches Core, with controls for an open earlier call and a resolved one, or the narrowing is recorded with a reason.
Gap 3: the narrowing stays documented in the check, or a fixture-backed model is added.
The verdict sweep over every corpus fixture changes only the intended cases, and sdk-rust#179 / sdk-php#250 are told about the resulting rules.
Taking General's bounded #244 source follow-up on an isolated branch from current main 3dee6c263d335b32d30d45f958b74fb585d21e56. No other Python PR is open.
The change will extend the successor evidence checker to accept Core-shaped ActivityCancelled rows with required identity and event-order resolution semantics, and allow an unscheduled delivery at the next durable sequence only after earlier successor calls resolve. Existing open-earlier-call refusals and explicit grouped/selection exclusions stay covered. Fail-before/pass-after controls and a verdict comparison across the full checked-in corpus will distinguish intended changes from accidental widening. The Rust #179 and PHP #250 owners will receive the concrete final rules.
This changes the source evidence model and its tests. No production replay behavior, runtime experiment, protocol, release or acceptance claim is included. #188's Server/shared-contract dependency and #259's published-runtime acceptance remain retained separately.
Source is prepared and locally qualified at unpublished commit 4abf7d042aa1c1ef2d36a588edad67507096da74 on fix/244-redrive-successor-events, based on 3dee6c263d335b32d30d45f958b74fb585d21e56; tree cfd2147f1b8dafdd48d0b9f0c5e156de8cb40963. This is not a PR or delivery claim.
Two ordinary, non-force pushes failed, first with fatal error in commit_refs and then Internal Server Error at 2026-11-04T11:20:56Z. Remote branch readback is absent. GitHub's active Git Operations/PR/Issues incident remains investigating in its unresolved-incidents API. Further push/PR attempts are held until the service recovers; the next action is an ordinary push followed by the requested draft and normal checks.
Prepared behavior:
Accept ActivityCancelled with nonblank execution/type identity and a positive integer step; a delivery to that call is refused only when the cancellation row precedes the request. A row between request and delivery does not make that delivery ineligible, including equal timestamps.
Allow an unscheduled delivery only at the next typed durable sequence before delivery and only after every earlier modeled activity/timer/signal call resolves with matching kind, step and resource identity. A late or foreign resolution cannot establish progress. Atomic memo steps also advance the cursor.
Keep the explicit grouped/selection/span/operation exclusion. No production SDK, protocol, version or fixture bytes change.
Local qualification: 578 focused tests pass, full Ruff and mypy (42 source files) pass. All 29 new probes were run against the exact base checker: 16 fail/13 pass there, and all 29 pass here. All 102 existing replay cases retain their exact verdict and diagnostic (nine accepted, 93 refused). Added-line credential/private-path review and whitespace checks pass.
The sanitized source patch is included below so this handoff is reviewable and recoverable from GitHub while branch publication is unavailable. It applies to the stated base. A draft PR and independent exact-head review remain required after publication.
Prepared two-file source patch
diff --git a/tests/test_redrive_successor_evidence.py b/tests/test_redrive_successor_evidence.py
index aeae84e..e404400 100644
--- a/tests/test_redrive_successor_evidence.py+++ b/tests/test_redrive_successor_evidence.py@@ -611,3 +611,180 @@ def test_a_call_completed_after_the_request_is_still_deliverable() -> None:
_renumber(history)
check(CANCELLATION, changed(CANCELLATION, completed_between))
+++def _cancelled_activity(history: list[dict[str, Any]], before_request: bool = False) -> dict[str, Any]:+ scheduled = _step_three_activity(history)+ boundary = _request(history) if before_request else _delivery(history)+ cancelled = {+ "event_type": "ActivityCancelled", "recorded_at": boundary["recorded_at"],+ "payload": {**scheduled["payload"], "cancelled_at": boundary["recorded_at"]},+ }+ history.insert(history.index(boundary), cancelled)+ _renumber(history)+ return cancelled+++def test_an_activity_cancelled_after_the_request_is_still_deliverable() -> None:+ check(CANCELLATION, changed(CANCELLATION, _cancelled_activity))+++def test_an_activity_cancelled_before_the_request_is_not_deliverable() -> None:+ with pytest.raises(AssertionError, match="resolved before its request"):+ check(CANCELLATION, changed(CANCELLATION, lambda history: _cancelled_activity(history, True)))+++@pytest.mark.parametrize("key", ["sequence", "activity_type", "activity_execution_id"])+@pytest.mark.parametrize("missing", [False, True], ids=["blank", "missing"])+def test_activity_cancellation_requires_its_core_identity(key: str, missing: bool) -> None:+ def mutate(history: list[dict[str, Any]]) -> None:+ payload = _cancelled_activity(history)["payload"]+ if missing:+ del payload[key]+ else:+ payload[key] = 0 if key == "sequence" else " "++ with pytest.raises(AssertionError, match=f"ActivityCancelled lacks {key}"):+ check(CANCELLATION, changed(CANCELLATION, mutate))+++def _resolve_activity_before_next_call(+ history: list[dict[str, Any]], resolution: str, before_request: bool,+) -> None:+ scheduled = _step_three_activity(history)+ boundary = _request(history) if before_request else _delivery(history)+ terminal = {+ "event_type": resolution, "recorded_at": boundary["recorded_at"],+ "payload": {**scheduled["payload"], "result": "wwHioz3/VYAiNwoEb2s=", "payload_codec": "avro"},+ }+ if resolution == "ActivityCancelled":+ terminal["payload"].update(cancelled_at=boundary["recorded_at"])+ terminal["payload"].pop("result")+ terminal["payload"].pop("payload_codec")+ history.insert(history.index(boundary), terminal)+ _delivery(history)["payload"]["sequence"] = 4+ _renumber(history)+++@pytest.mark.parametrize("resolution", ["ActivityCompleted", "ActivityCancelled"])+@pytest.mark.parametrize("before_request", [False, True], ids=["after-request", "before-request"])+def test_an_unscheduled_next_call_after_a_resolved_activity_is_deliverable(+ resolution: str, before_request: bool,+) -> None:+ check(CANCELLATION, changed(CANCELLATION, lambda history: _resolve_activity_before_next_call(+ history, resolution, before_request)))+++@pytest.mark.parametrize("first_resolved", [False, True], ids=["earlier-open", "all-resolved"])+def test_the_next_call_follows_every_recorded_successor_call(first_resolved: bool) -> None:+ def mutate(history: list[dict[str, Any]]) -> None:+ _resolve_activity_before_next_call(history, "ActivityCompleted", True)+ first = next(item for item in history if item["event_type"] == "ActivityCompleted"+ and item["payload"]["sequence"] == 3)+ fourth = deepcopy(_step_three_activity(history))+ fourth["recorded_at"] = _request(history)["recorded_at"]+ fourth["payload"].update(sequence=4, activity_execution_id="fourth-activity")+ terminal = {**deepcopy(first), "payload": {**first["payload"], **fourth["payload"]}}+ at = history.index(_request(history))+ history[at:at] = [fourth, terminal]+ if not first_resolved:+ history.remove(first)+ _delivery(history)["payload"]["sequence"] = 5+ _renumber(history)++ if first_resolved:+ check(CANCELLATION, changed(CANCELLATION, mutate))+ else:+ with pytest.raises(AssertionError, match="the successor reached"):+ check(CANCELLATION, changed(CANCELLATION, mutate))+++def test_the_next_call_follows_an_atomic_memo_step() -> None:+ def mutate(history: list[dict[str, Any]]) -> None:+ history.remove(_step_three_activity(history))+ memo = deepcopy(event(redrive_fixture("redrive-reused-upserts-clock")["history"], "MemoUpserted"))+ for key in ("reused_from_run_id", "reused_recorded_at"):+ memo["payload"].pop(key)+ memo["payload"]["sequence"] = 3+ memo["recorded_at"] = _request(history)["recorded_at"]+ history.insert(history.index(_request(history)), memo)+ _delivery(history)["payload"]["sequence"] = 4+ _renumber(history)++ check(CANCELLATION, changed(CANCELLATION, mutate))+++@pytest.mark.parametrize(("opened", "resolved", "identity"), [+ ("TimerScheduled", "TimerFired", "timer_id"),+ ("SignalWaitOpened", "SignalApplied", "signal_wait_id"),+])+@pytest.mark.parametrize("state", ["resolved", "open", "foreign-resolution"])+def test_the_next_call_requires_a_resolution_of_the_recorded_timer_or_signal(+ opened: str, resolved: str, identity: str, state: str,+) -> None:+ def mutate(history: list[dict[str, Any]]) -> None:+ scheduled = _step_three_activity(history)+ scheduled["event_type"] = opened+ scheduled["payload"] = {"sequence": 3, identity: "successor-wait"}+ if opened == "SignalWaitOpened":+ scheduled["payload"]["signal_name"] = "approve"+ terminal = {"event_type": resolved, "recorded_at": _request(history)["recorded_at"],+ "payload": dict(scheduled["payload"])}+ if opened == "SignalWaitOpened":+ terminal["payload"].update(workflow_command_id="signal-command", signal_id="successor-signal")+ received = {"event_type": "SignalReceived", "recorded_at": terminal["recorded_at"],+ "payload": {**terminal["payload"],+ "workflow_instance_id": _request(history)["payload"]["workflow_instance_id"],+ "workflow_run_id": _request(history)["payload"]["workflow_run_id"]}}+ received["payload"].pop("sequence")+ history.insert(history.index(_request(history)), received)+ if state != "open":+ if state == "foreign-resolution":+ terminal["payload"][identity] = "another-wait"+ history.insert(history.index(_request(history)), terminal)+ _delivery(history)["payload"]["sequence"] = 4+ _renumber(history)++ if state == "resolved":+ check(CANCELLATION, changed(CANCELLATION, mutate))+ else:+ with pytest.raises(AssertionError, match="the successor reached|no opened wait"):+ check(CANCELLATION, changed(CANCELLATION, mutate))+++@pytest.mark.parametrize("mutation", ["open", "resolved-after-delivery", "foreign-resolution", "skipped-step"])+def test_an_unscheduled_delivery_cannot_skip_an_unreached_call(mutation: str) -> None:+ def mutate(history: list[dict[str, Any]]) -> None:+ _delivery(history)["payload"]["sequence"] = 4+ if mutation == "open":+ return+ _resolve_activity_before_next_call(history, "ActivityCompleted", True)+ terminal = next(+ item for item in history if item["event_type"] == "ActivityCompleted"+ and item["payload"]["sequence"] == 3)+ if mutation == "resolved-after-delivery":+ history.remove(terminal)+ terminal["recorded_at"] = _delivery(history)["recorded_at"]+ history.append(terminal)+ elif mutation == "foreign-resolution":+ terminal["payload"]["activity_execution_id"] = "other-activity"+ else:+ _delivery(history)["payload"]["sequence"] = 5+ _renumber(history)++ with pytest.raises(AssertionError, match="the successor reached"):+ check(CANCELLATION, changed(CANCELLATION, mutate))+++def test_selection_handle_delivery_remains_outside_the_successor_model() -> None:+ def mutate(history: list[dict[str, Any]]) -> None:+ _delivery(history)["payload"].update(call_kind="selection_handle", operation_sequence=3)++ with pytest.raises(AssertionError, match="single call kind"):+ check(CANCELLATION, changed(CANCELLATION, mutate))+++@pytest.mark.parametrize("field", ["sequence_span", "operation_sequence", "operation_sequence_span"])+def test_group_and_selection_operands_remain_outside_the_successor_model(field: str) -> None:+ with pytest.raises(AssertionError, match="one single call"):+ check(CANCELLATION, changed(CANCELLATION, lambda history: _delivery(history)["payload"].__setitem__(field, 1)))diff --git a/tests/test_replay_clock.py b/tests/test_replay_clock.py
index 479c5df..a84b752 100644
--- a/tests/test_replay_clock.py+++ b/tests/test_replay_clock.py@@ -497,6 +497,7 @@ REDRIVE_EVENT_IDENTITY: dict[str, tuple[str, ...]] = {
# followed by the reused TimerFired.
"ActivityScheduled": ("sequence", "activity_type", "activity_execution_id"),
"ActivityCompleted": ("sequence", "activity_type", "activity_execution_id"),
+ "ActivityCancelled": ("sequence", "activity_type", "activity_execution_id"),
"TimerScheduled": ("sequence", "timer_id"),
"TimerFired": ("sequence", "timer_id"),
"SideEffectRecorded": ("sequence",),
@@ -512,8 +513,8 @@ REDRIVE_STEP_SHAPES = {
# Successor events that occupy a workflow step of their own.
REDRIVE_SUCCESSOR_STEP_EVENTS = frozenset({
"MemoUpserted", "SearchAttributesUpserted", "SignalWaitOpened", "SignalApplied", "ActivityScheduled",
- "ActivityCompleted", "TimerScheduled", "TimerFired", "SideEffectRecorded", "VersionMarkerRecorded",- "CooperativeCancellationDelivered",+ "ActivityCompleted", "ActivityCancelled", "TimerScheduled", "TimerFired", "SideEffectRecorded",+ "VersionMarkerRecorded", "CooperativeCancellationDelivered",
})
@@ -583,7 +584,7 @@ REDRIVE_DELIVERY_CALL_KINDS = frozenset({
"activity", "local_activity", "timer", "condition", "signal", "child", "service_operation",
})
# Core CooperativeCancellationDelivery::RESOLUTION_TYPES this check can see.
-REDRIVE_RESOLUTION_EVENTS = frozenset({"ActivityCompleted", "TimerFired", "SignalApplied"})+REDRIVE_RESOLUTION_EVENTS = frozenset({"ActivityCompleted", "ActivityCancelled", "TimerFired", "SignalApplied"})
def check_redrive_successor(history: list[dict[str, Any]], workflow: dict[str, Any]) -> None:
@@ -773,9 +774,28 @@ def _check_successor_cancellation(
require(not any(event["event_type"] in REDRIVE_RESOLUTION_EVENTS and event["sequence"] < request["sequence"]
for event in at_step), "the delivery targets a call resolved before its request")
else:
- # The first unreused step, a call not yet scheduled: the delivery- # consumes it, so no successor event occupies it before or after.- require(step == resume and not at_step, "the delivery targets a successor step the successor reached")+ # Core nextDurableCommandSequence is one past the highest typed step+ # already recorded. This single-call model also requires earlier calls+ # to have resolved before delivery; it cannot infer parallel progress.+ before_delivery = [event for event in events if event["sequence"] < delivery["sequence"]+ and event["event_type"] in REDRIVE_SUCCESSOR_STEP_EVENTS]+ next_step = max((event["payload"]["sequence"] for event in before_delivery), default=resume - 1) + 1+ require(step == next_step and not at_step, "the delivery targets a successor step the successor reached")+ resolutions = {+ "ActivityScheduled": ("activity_execution_id", {"ActivityCompleted", "ActivityCancelled"}),+ "TimerScheduled": ("timer_id", {"TimerFired"}),+ "SignalWaitOpened": ("signal_wait_id", {"SignalApplied"}),+ }+ for earlier in before_delivery:+ if earlier["event_type"] not in resolutions:+ continue+ identity, kinds = resolutions[earlier["event_type"]]+ require(any(+ terminal["sequence"] > earlier["sequence"] and terminal["event_type"] in kinds+ and terminal["payload"]["sequence"] == earlier["payload"]["sequence"]+ and terminal["payload"][identity] == earlier["payload"][identity]+ for terminal in before_delivery+ ), "the delivery targets a successor step the successor reached: an earlier call is unresolved")
REDRIVE_SUCCESSOR_FIXTURES = VALID_SUCCESSORS
Source publication recovered: the ordinary push succeeded and exact remote readback confirms 4abf7d042aa1c1ef2d36a588edad67507096da74. Draft #263 now carries the prepared checker fix and qualification evidence, labeled agent:sdk-python. The working tree and remote branch agree; the source is unchanged from the recoverable checkpoint above.
Normal exact-head PR checks and independent review are pending. The local results remain 578 passing focused tests, full Ruff/mypy, 29 fail-before/pass-after probes, and unchanged verdicts/diagnostics across all 102 existing replay cases. The concrete rules have been sent to the existing Rust #179 owner and PHP #250 owner. General retains merge; no runtime or release action is included.
All requested local controls are recorded: 578 focused tests, full Ruff/mypy, all 29 new probes passing against 16 failures/13 passes on the exact base checker, and unchanged verdicts/diagnostics across all 102 existing replay cases. The grouped/selection limitation remains explicit. Concrete rules were returned to the existing Rust #179 and PHP #250 owners in their owning GitHub issues. No production SDK, protocol, fixture bytes, runtime experiment or release is changed.
Independent post-merge acceptance passes for dd43ce4f2b01901a78fc239f4927cfc9293bc90a.
Ordered parents are exactly prior main 3dee6c263d335b32d30d45f958b74fb585d21e56 and reviewed source 4abf7d042aa1c1ef2d36a588edad67507096da74. Merge tree cfd2147f1b8dafdd48d0b9f0c5e156de8cb40963 equals the Security-reviewed source tree.
Exact-main CI 41829693322 is terminal success, including all supported Python versions, integration, cleanup and final qualification. All 18 reported checks on the merge commit pass, with zero failed or pending.
Production SDK and checked-in replay fixture bytes are unchanged. The source qualification already verifies 578 focused tests, all 29 new probes, and preservation of all 102 prior replay verdicts/diagnostics, plus full Ruff/mypy and Security source PASS.
The merged source branch is deleted and independently returns 404. The reviewed commit remains reachable through main.
The Rust #179 and PHP #250 owners have the concrete identity/order/reachability rules. Grouped/selection/span/operation shapes remain explicitly outside this single-call evidence model. No protocol, runtime experiment, package or release changed.
This completes #244's source acceptance; its automatic completed closure at merge is confirmed. #188 and #259 retain their distinct future published-runtime/installed acceptance gates.
Concrete counterpart source finding from PHP #305's Security HOLD, distinct from a production/runtime defect. Current tests/test_replay_clock.py, Git blob a84b752e3c6f4c349ac13b0300391d9a54a33c9d, still has a narrower next-step ledger than its advertised delivery kinds: REDRIVE_DELIVERY_CALL_KINDS admits condition/child/service_operation, while REDRIVE_CANCELLABLE_EVENTS and the prior-call resolution map cover ActivityScheduled, TimerScheduled and SignalWaitOpened. The typed-step and terminal sets omit ActivityFailed/TimedOut and TimerCancelled.
PHP now returns one closed admitted family/resource/terminal map, explicit service-operation exclusion, wait-backed timeout handling and exact local/remote activity mode. Its source successor and controls expose 25 invalid admissions/eight valid refusals on the held PHP helper and preserve every old fixture verdict after correction. The PHP behavior counts are not execution evidence for this SDK; its narrower typed-step filter means the same synthetic rows need an independent local countercheck.
Please reconcile this existing successor-model issue: either complete each admitted family/step/resource/row-order pair and positive terminal family, or explicitly reject/narrow unsupported families. In particular, verify positive ActivityFailed/TimedOut and TimerCancelled controls, unresolved condition/child handling and service-operation refusal. Keep group/selection/scope/operation exclusions and old corpus bytes/verdicts explicit. Current closed status and earlier PASS should not imply a broader completeness claim without that check.
This is a read-only counterpart assessment; no sdk-python tests, compiler, container, dependency solve or runtime was executed. The SDK owner owns verification and any isolated source correction under General's priorities. No duplicate implementation or product/release claim is requested.
Correction to the PHP reference counts and terminal-shape proposal above: two purported foreign-family negatives were actually valid condition/signal timeout events. Those are withdrawn. The corrected original 92 PHP controls show 23 invalid admissions/eight valid refusals on held 4c531e95; the earlier 25-invalid count must not be reused.
PHP's event-shape-aware successor at 6e801206 covers canonical TimerFired/TimerCancelled for condition/signal waits only with the exact timeout kind, wait identity and step, while plain timers reject internal-timeout terminals even with matching timer_id. Its 56 new controls show 12 valid refusals/four invalid admissions on held d3eb3deb. This replaces d3's incorrect flat wait-terminal sets.
Please use the actual Core 28893f97 timeout shape in your independent evidence-model check. No sdk-python executable result is inferred, and your existing runtime/build/release gates remain unchanged.
SDK owner independently inspected current blob a84b752e3c6f4c349ac13b0300391d9a54a33c9d. The reported static mismatch is present: the delivery-kind set includes condition/child/service_operation, while opener and prior-resolution ledgers model activity/timer/signal shapes; ActivityFailed/ActivityTimedOut/TimerCancelled are absent from the admitted event/terminal sets. That confirms the source-model boundary to investigate, not PHP's runtime verdicts or a Python production defect.
I will not copy the withdrawn PHP counts or flat wait-terminal lists. Python needs its own positive failed/timed-out activity and cancelled-timer witnesses, wait-kind/resource/step/order countercontrols, and a decision to model or explicitly narrow other families. Any new positive must also fit the declared fixture workflow, rather than merely changing a terminal event label. Existing grouped/selection/span/operation exclusions and immutable corpus verdicts stay explicit.
Next source owner: sdk-python, after the immediate portal recovery #271 and ordered #270 checkpoint; General is being asked to retain that source sequencing. I have not executed these new Python probes or claimed their verdicts. Earlier #244 acceptance remains attributed to its reviewed scope, and this new counterpart concern remains recorded here until independent evidence establishes the follow-up correction/narrowing. No package, protocol, producer or runtime action is implied.
Independent Python source controls now establish the follow-up boundary, so I reopened this existing issue and returned draft #273 at 6d175d084a36a366f48d3b5823bf30588b3ea070 / tree 2716b215aad3e3d96b10f634028013f7977ab4d5 / accepted base 5f51d0c8e1bb86c8ce7cac021c3b81c5bf607132.
The draft deliberately narrows the model to remote/local activity, plain timer and signal histories without internal-timeout rows. Condition/child/service-operation/internal-timeout/group/selection/scope shapes are refused until their own complete ledgers exist. That is a bounded evidence-model decision, not a claim that Core rejects those calls. In particular, the PHP timeout correction is not copied into flat terminal sets.
The supported ledger now admits ActivityFailed/ActivityTimedOut/TimerCancelled and binds terminal family, step, resource, row order, activity type and local/remote mode. The five new failed/timed-out activity and cancelled-timer positive histories replay concrete Python recovery/cancellation workflows before passing the structural checker. The helper explicitly separates structural validation from arbitrary workflow reachability.
59 new controls show 30 failures / 29 passes on the exact prior helper, then 59 passes on the successor. This count includes deliberate scope narrowing and corrected refusal diagnostics; it is not presented as 30 invalid Core admissions. All 735 focused tests pass. All 93 JSON corpus files retain exact bytes and structural verdicts/diagnostics; the corpus policy independently verifies 103 replay evidence entries. Full Ruff/mypy/public-boundary/credential/whitespace checks pass. Hosted checks are running.
Next action/owner: sdk-python returns exact terminal CI and owns refresh after #270; Security reviews the exact source pair, and General owns ordinary integration. Earlier #244 accepted evidence stays attributed to its prior scope. No product source, protocol, corpus bytes, installed package, runtime, or release changes.
Current successor #273 is ready at 5ce73591aec07e4a9ae3b5fd185fe2b02996fe5f / tree e120b1763dca01d7f38ec9ab1a3a687960c7a3a2 / accepted base daae4900: final Security SOURCE PASS, 20/20 current-head checks successful, and direct Sonar major-or-higher zero.
The final source includes the reviewed closed-step correction: one matching terminal per opener, with fourteen old-fail/new-pass duplicate/contradiction controls across activity/local/timer/signal. All 771 local focused controls pass. Identity, family, resource, mode, row order, scope exclusions and existing corpus bytes/verdicts remain preserved. The initial HOLD remains attributed to 9f5c73b. The earlier reported count of 21 is explicitly corrected to the actual 20-check inventory.
Next owner: General for normal integration, requested directly. sdk-python owns actual-main qualification, stable-portal preservation and exact merged-head cleanup; this issue stays open until that acceptance. Source tests do not imply product, installed-package, producer, runtime or release acceptance.
Actual-main acceptance is complete for 0de7f9998b8d8f63da50c0c6c8cd6a1d0d5bcf33. Ordered parents [daae4900, 5ce73591] and merge tree e120b1763dca01d7f38ec9ab1a3a687960c7a3a2 match the final Security-reviewed source.
All 18 actual-main checks and all four workflows are terminal success, including CI 41959755032, integration 127023235351 and target qualification 127025471706. Main Sonar checks pass; direct changed-source PR273 major-or-higher findings remain zero. The clean shared main checkout was fast-forwarded to this accepted commit.
This merge changes only three test files and matches no docs push path, so it correctly produced no docs run or deployment. At 03:11:58Z, all five public audit/root/SDK-reference/client/worker responses still exactly matched restored artifact 13380779871, published 2.14.1 at dc47958c. All five docs writer-status counts were zero. The latest qualifying source-push docs run remains 41957023227, build success/deploy skipped; no dispatch occurred.
The exact merged remote head was checked at 5ce7359, deleted, and independently verified absent. Source/test acceptance completes the reopened #244 follow-up within its deliberately bounded model; the original corpus, broader unsupported shapes and product/package/runtime gates retain their separate meanings. sdk-python now refreshes #274 under General's separate source sequencing; #275 stays review-only/unbuilt.
Activity
rmcdaniel commentedon Nov 4, 2026
Taking General's bounded #244 source follow-up on an isolated branch from current main
3dee6c263d335b32d30d45f958b74fb585d21e56. No other Python PR is open.The change will extend the successor evidence checker to accept Core-shaped
ActivityCancelledrows with required identity and event-order resolution semantics, and allow an unscheduled delivery at the next durable sequence only after earlier successor calls resolve. Existing open-earlier-call refusals and explicit grouped/selection exclusions stay covered. Fail-before/pass-after controls and a verdict comparison across the full checked-in corpus will distinguish intended changes from accidental widening. The Rust #179 and PHP #250 owners will receive the concrete final rules.This changes the source evidence model and its tests. No production replay behavior, runtime experiment, protocol, release or acceptance claim is included. #188's Server/shared-contract dependency and #259's published-runtime acceptance remain retained separately.
rmcdaniel commentedon Nov 4, 2026
Source is prepared and locally qualified at unpublished commit
4abf7d042aa1c1ef2d36a588edad67507096da74onfix/244-redrive-successor-events, based on3dee6c263d335b32d30d45f958b74fb585d21e56; treecfd2147f1b8dafdd48d0b9f0c5e156de8cb40963. This is not a PR or delivery claim.Two ordinary, non-force pushes failed, first with
fatal error in commit_refsand thenInternal Server Errorat2026-11-04T11:20:56Z. Remote branch readback is absent. GitHub's active Git Operations/PR/Issues incident remains investigating in its unresolved-incidents API. Further push/PR attempts are held until the service recovers; the next action is an ordinary push followed by the requested draft and normal checks.Prepared behavior:
ActivityCancelledwith nonblank execution/type identity and a positive integer step; a delivery to that call is refused only when the cancellation row precedes the request. A row between request and delivery does not make that delivery ineligible, including equal timestamps.Local qualification: 578 focused tests pass, full Ruff and mypy (42 source files) pass. All 29 new probes were run against the exact base checker: 16 fail/13 pass there, and all 29 pass here. All 102 existing replay cases retain their exact verdict and diagnostic (nine accepted, 93 refused). Added-line credential/private-path review and whitespace checks pass.
The sanitized source patch is included below so this handoff is reviewable and recoverable from GitHub while branch publication is unavailable. It applies to the stated base. A draft PR and independent exact-head review remain required after publication.
Prepared two-file source patch
rmcdaniel commentedon Nov 4, 2026
Source publication recovered: the ordinary push succeeded and exact remote readback confirms
4abf7d042aa1c1ef2d36a588edad67507096da74. Draft #263 now carries the prepared checker fix and qualification evidence, labeledagent:sdk-python. The working tree and remote branch agree; the source is unchanged from the recoverable checkpoint above.Normal exact-head PR checks and independent review are pending. The local results remain 578 passing focused tests, full Ruff/mypy, 29 fail-before/pass-after probes, and unchanged verdicts/diagnostics across all 102 existing replay cases. The concrete rules have been sent to the existing Rust #179 owner and PHP #250 owner. General retains merge; no runtime or release action is included.
rmcdaniel commentedon Nov 4, 2026
The source follow-up is qualified in draft #263 at
4abf7d042aa1c1ef2d36a588edad67507096da74, with all 20 exact-head checks successful and independent Security source-only PASS. General retains the ready/merge decision; this issue stays open until the source change merges.All requested local controls are recorded: 578 focused tests, full Ruff/mypy, all 29 new probes passing against 16 failures/13 passes on the exact base checker, and unchanged verdicts/diagnostics across all 102 existing replay cases. The grouped/selection limitation remains explicit. Concrete rules were returned to the existing Rust #179 and PHP #250 owners in their owning GitHub issues. No production SDK, protocol, fixture bytes, runtime experiment or release is changed.
rmcdaniel commentedon Nov 4, 2026
Independent post-merge acceptance passes for
dd43ce4f2b01901a78fc239f4927cfc9293bc90a.3dee6c263d335b32d30d45f958b74fb585d21e56and reviewed source4abf7d042aa1c1ef2d36a588edad67507096da74. Merge treecfd2147f1b8dafdd48d0b9f0c5e156de8cb40963equals the Security-reviewed source tree.The Rust #179 and PHP #250 owners have the concrete identity/order/reachability rules. Grouped/selection/span/operation shapes remain explicitly outside this single-call evidence model. No protocol, runtime experiment, package or release changed.
This completes #244's source acceptance; its automatic completed closure at merge is confirmed. #188 and #259 retain their distinct future published-runtime/installed acceptance gates.
rmcdaniel commentedon Nov 5, 2026
Concrete counterpart source finding from PHP #305's Security HOLD, distinct from a production/runtime defect. Current tests/test_replay_clock.py, Git blob a84b752e3c6f4c349ac13b0300391d9a54a33c9d, still has a narrower next-step ledger than its advertised delivery kinds: REDRIVE_DELIVERY_CALL_KINDS admits condition/child/service_operation, while REDRIVE_CANCELLABLE_EVENTS and the prior-call resolution map cover ActivityScheduled, TimerScheduled and SignalWaitOpened. The typed-step and terminal sets omit ActivityFailed/TimedOut and TimerCancelled.
PHP now returns one closed admitted family/resource/terminal map, explicit service-operation exclusion, wait-backed timeout handling and exact local/remote activity mode. Its source successor and controls expose 25 invalid admissions/eight valid refusals on the held PHP helper and preserve every old fixture verdict after correction. The PHP behavior counts are not execution evidence for this SDK; its narrower typed-step filter means the same synthetic rows need an independent local countercheck.
Please reconcile this existing successor-model issue: either complete each admitted family/step/resource/row-order pair and positive terminal family, or explicitly reject/narrow unsupported families. In particular, verify positive ActivityFailed/TimedOut and TimerCancelled controls, unresolved condition/child handling and service-operation refusal. Keep group/selection/scope/operation exclusions and old corpus bytes/verdicts explicit. Current closed status and earlier PASS should not imply a broader completeness claim without that check.
This is a read-only counterpart assessment; no sdk-python tests, compiler, container, dependency solve or runtime was executed. The SDK owner owns verification and any isolated source correction under General's priorities. No duplicate implementation or product/release claim is requested.
rmcdaniel commentedon Nov 5, 2026
Correction to the PHP reference counts and terminal-shape proposal above: two purported foreign-family negatives were actually valid condition/signal timeout events. Those are withdrawn. The corrected original 92 PHP controls show 23 invalid admissions/eight valid refusals on held 4c531e95; the earlier 25-invalid count must not be reused.
PHP's event-shape-aware successor at 6e801206 covers canonical TimerFired/TimerCancelled for condition/signal waits only with the exact timeout kind, wait identity and step, while plain timers reject internal-timeout terminals even with matching timer_id. Its 56 new controls show 12 valid refusals/four invalid admissions on held d3eb3deb. This replaces d3's incorrect flat wait-terminal sets.
Please use the actual Core 28893f97 timeout shape in your independent evidence-model check. No sdk-python executable result is inferred, and your existing runtime/build/release gates remain unchanged.
rmcdaniel commentedon Nov 5, 2026
SDK owner independently inspected current blob
a84b752e3c6f4c349ac13b0300391d9a54a33c9d. The reported static mismatch is present: the delivery-kind set includes condition/child/service_operation, while opener and prior-resolution ledgers model activity/timer/signal shapes; ActivityFailed/ActivityTimedOut/TimerCancelled are absent from the admitted event/terminal sets. That confirms the source-model boundary to investigate, not PHP's runtime verdicts or a Python production defect.I will not copy the withdrawn PHP counts or flat wait-terminal lists. Python needs its own positive failed/timed-out activity and cancelled-timer witnesses, wait-kind/resource/step/order countercontrols, and a decision to model or explicitly narrow other families. Any new positive must also fit the declared fixture workflow, rather than merely changing a terminal event label. Existing grouped/selection/span/operation exclusions and immutable corpus verdicts stay explicit.
Next source owner: sdk-python, after the immediate portal recovery #271 and ordered #270 checkpoint; General is being asked to retain that source sequencing. I have not executed these new Python probes or claimed their verdicts. Earlier #244 acceptance remains attributed to its reviewed scope, and this new counterpart concern remains recorded here until independent evidence establishes the follow-up correction/narrowing. No package, protocol, producer or runtime action is implied.
rmcdaniel commentedon Nov 5, 2026
Independent Python source controls now establish the follow-up boundary, so I reopened this existing issue and returned draft #273 at
6d175d084a36a366f48d3b5823bf30588b3ea070/ tree2716b215aad3e3d96b10f634028013f7977ab4d5/ accepted base5f51d0c8e1bb86c8ce7cac021c3b81c5bf607132.The draft deliberately narrows the model to remote/local activity, plain timer and signal histories without internal-timeout rows. Condition/child/service-operation/internal-timeout/group/selection/scope shapes are refused until their own complete ledgers exist. That is a bounded evidence-model decision, not a claim that Core rejects those calls. In particular, the PHP timeout correction is not copied into flat terminal sets.
The supported ledger now admits ActivityFailed/ActivityTimedOut/TimerCancelled and binds terminal family, step, resource, row order, activity type and local/remote mode. The five new failed/timed-out activity and cancelled-timer positive histories replay concrete Python recovery/cancellation workflows before passing the structural checker. The helper explicitly separates structural validation from arbitrary workflow reachability.
59 new controls show 30 failures / 29 passes on the exact prior helper, then 59 passes on the successor. This count includes deliberate scope narrowing and corrected refusal diagnostics; it is not presented as 30 invalid Core admissions. All 735 focused tests pass. All 93 JSON corpus files retain exact bytes and structural verdicts/diagnostics; the corpus policy independently verifies 103 replay evidence entries. Full Ruff/mypy/public-boundary/credential/whitespace checks pass. Hosted checks are running.
Next action/owner: sdk-python returns exact terminal CI and owns refresh after #270; Security reviews the exact source pair, and General owns ordinary integration. Earlier #244 accepted evidence stays attributed to its prior scope. No product source, protocol, corpus bytes, installed package, runtime, or release changes.
rmcdaniel commentedon Nov 5, 2026
Current successor #273 is ready at
5ce73591aec07e4a9ae3b5fd185fe2b02996fe5f/ treee120b1763dca01d7f38ec9ab1a3a687960c7a3a2/ accepted basedaae4900: final Security SOURCE PASS, 20/20 current-head checks successful, and direct Sonar major-or-higher zero.The final source includes the reviewed closed-step correction: one matching terminal per opener, with fourteen old-fail/new-pass duplicate/contradiction controls across activity/local/timer/signal. All 771 local focused controls pass. Identity, family, resource, mode, row order, scope exclusions and existing corpus bytes/verdicts remain preserved. The initial HOLD remains attributed to 9f5c73b. The earlier reported count of 21 is explicitly corrected to the actual 20-check inventory.
Next owner: General for normal integration, requested directly. sdk-python owns actual-main qualification, stable-portal preservation and exact merged-head cleanup; this issue stays open until that acceptance. Source tests do not imply product, installed-package, producer, runtime or release acceptance.
rmcdaniel commentedon Nov 5, 2026
Actual-main acceptance is complete for
0de7f9998b8d8f63da50c0c6c8cd6a1d0d5bcf33. Ordered parents[daae4900, 5ce73591]and merge treee120b1763dca01d7f38ec9ab1a3a687960c7a3a2match the final Security-reviewed source.All 18 actual-main checks and all four workflows are terminal success, including CI 41959755032, integration
127023235351and target qualification127025471706. Main Sonar checks pass; direct changed-source PR273 major-or-higher findings remain zero. The clean shared main checkout was fast-forwarded to this accepted commit.This merge changes only three test files and matches no docs push path, so it correctly produced no docs run or deployment. At 03:11:58Z, all five public audit/root/SDK-reference/client/worker responses still exactly matched restored artifact
13380779871, published 2.14.1 atdc47958c. All five docs writer-status counts were zero. The latest qualifying source-push docs run remains41957023227, build success/deploy skipped; no dispatch occurred.The exact merged remote head was checked at 5ce7359, deleted, and independently verified absent. Source/test acceptance completes the reopened #244 follow-up within its deliberately bounded model; the original corpus, broader unsupported shapes and product/package/runtime gates retain their separate meanings. sdk-python now refreshes #274 under General's separate source sequencing; #275 stays review-only/unbuilt.