Repository navigation
Native: return the full not-recorded receipt for refused workflow-task reports (#994) - #999
Conversation
#994) A workflow-task completion, failure, waiting acknowledgement or cancellation-scope checkpoint refused task_not_leased or run_closed has written nothing. Its 409 now carries recorded:false, the task row's exact task_id and run_id, and the request's normalized workflow_task_attempt, beside the refusal's existing fields and task_status. This follows Security's contract in issue comment 6613395001. The guard and open-run refusals in fail_workflow_task and complete_or_checkpoint are mapped, as is Core's refused checkpoint for a run that closed after the guard. The identity fields are authoritative and overwrite anything the refusal carried. Attempt and owner mismatches, expired leases, stale registrations, heartbeats and history reads are unchanged and stay fatal to workers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ive only (#994) Published PHP 2.11.8's ownership guard refuses a late completion or failure, and a closed-run report, without recorded or run_id. It reports both only from its rarer post-guard engine outcome, which a deterministic late request never reaches. Native now adds the not-recorded receipt to those refusals (server#994). The probe records the implementation from cluster info (a failed read throws). The contract requires PHP's published bodies exactly when it is absent, and for native the same bodies plus exactly recorded false and the task's run_id. Renewal and attempt-mismatch refusals are unchanged for both. Any other implementation is refused. Unit controls: PHP with native fields, native without them (3.0.4's bodies), a wrong run, a claimed record, receipt fields on renewal or attempt mismatch, and unknown or unnamed implementations all fail. Against main's contract the native case and the 3.0.4 control fail; without the implementation check, both unknown-implementation controls fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The conformance half is added as
The native corpus's |
|
Security exact-head refresh readback for
The merge-only refresh introduces no overlap or source drift. Source and conformance remain cleared at this exact head. Final merge clearance is held only for the full hosted matrix, including native parity/corpus, to finish on |
|
|
Hosted checks are terminal at exact head
Requesting Security's final clearance. Server will mark this ready and queue exactly this SHA. |
|
Security final exact-head clearance: Independent readback confirms the source-cleared head is unchanged, CLEAN/MERGEABLE, with 57 terminal successes, one expected conditional skip, and zero failed or pending checks. The terminal matrix includes the native corpus and native tests on SQLite, MySQL 8, the existing PHP MySQL matrix, MariaDB 10.11, PostgreSQL 17 and the existing PHP PostgreSQL matrix; codec/native builds, feature suite, corrected markers, SDK directions and the aggregate Shared Server fixtures result all pass. This exact head closes the #994 receipt contract: native Merge only this SHA and return the merge commit, parents and tree readback. Server 3.0.5 and sdk-rust #170 remain downstream gates until the merged artifact is qualified. |



Fixes #994
This follows Security's acceptance contract in issue comment 6613395001.
What changes
When a workflow-task report is refused
task_not_leasedorrun_closed, nothing was written for it. The 409 now carries the full not-recorded receipt:recorded: falsetask_id: the task row's IDworkflow_task_attempt: the request's attempt, normalizedrun_id: the task row's runtask_statusis filled in when the refusal didn't already carry it. Every existing field stays. The receipt carries no payload or command data. The receipt identity is authoritative: it overwrites any conflictingtask_id, attempt,run_idorrecordedthe refusal carried.This is a native addition, not parity with PHP. Published PHP 2.11.8's lease guard (
guardWorkflowTaskOwnership) sends norecordedorrun_id. Only PHP's engine-outcome race path (WorkerController.php 2155–2164) carries them.Covered surfaces
All in
rust/src/runtime/store.rs, throughStore::not_recorded→workflow_task_guard::not_recorded/not_recorded_answer:Store::fail_workflow_task:workflow_task_guarderrortask_not_leased,run_closed(failure reports and waiting acknowledgements)Store::fail_workflow_task:active_runerrorrun_closed, now in the worker envelopeStore::complete_or_checkpoint:workflow_task_guarderrortask_not_leased,run_closed(completion and the shared checkpoint wrapper; includes the waiting/ack duplicate, which falls throughduplicate_receiptto the guard)Store::complete_or_checkpoint:active_runerrorrun_closedStore::complete_or_checkpoint:checkpoint_prefixanswer or errorrun_closedcheckpoint, for a run that closed after the guard (scope_checkpoint.rs)run_closedcovers every terminal status the guard recognizes: completed, failed, timed out (statusfailed, closed reasontimed_out), cancelled and terminated.Unchanged (still fatal to workers)
workflow_task_attempt_mismatchlease_owner_mismatchlease_expiredstale_worker_registrationconflicting_completioncancellation_deadline_expiredWorkflow-task heartbeat and history-read refusal bodies are untouched.
Tests
rust/tests/execution/not_recorded_receipts.rspins the public HTTP body exactly: the worker envelope,server_capabilitiespresent,run_closed_atformat-checked. Each case also proves nothing was recorded: every table's row count, the run history and thedw_task_completionsreceipts are unchanged.a_report_on_every_closed_run_status_is_run_closed_with_the_full_receipt: completed, failed, timed_out, cancelled and terminated × complete, fail and checkpoint.a_report_for_a_finished_task_on_an_open_run_is_task_not_leased_with_the_full_receipt: a completed task and the waiting/ack duplicate × complete, fail, waiting ack and checkpoint.a_report_on_a_closed_run_whose_task_is_still_leased_carries_the_full_receipt: the bareactive_runrun_closed, on complete and fail.a_checkpoint_whose_run_closed_after_the_guard_carries_the_full_receipt: a cooperative claim still leased, then its run closed (completed, failed, cancelled, terminated), through Core's checkpoint.fatal_report_refusals_gain_no_receipt: attempt mismatch, owner mismatch and expired lease on all three routes keep their exact published bodies.There are also new unit tests in
workflow_task_guard.rs: fatal reasons pass through unchanged, conflicting identity is overwritten, and other checkpoint answers are unchanged.late_workflow_claims.rsnow expects the receipt on the post-close fail and complete refusals only. Its heartbeat and attempt-mismatch expectations are unchanged.Results
Run on lab3 in
rust:1.99.0-slim-bookworm(--cpus 3 -m 5g,CARGO_BUILD_JOBS=2):cargo test --locked --lib: 413 passed.cargo test --locked --test execution: 694 of 696 passed. The 2 failures were load-timing (namespaces::a_named_namespace_child_runs…,workflow_failures::workflow_failure_attributes_only…). Both pass on rerun in isolation, twice. They don't involve these refusals. An earlier full run had 4 different intermittent failures; all of them passed on rerun.Counterfactuals
origin/mainsources with these tests: 4 receipt tests andlate_workflow_claimsfail on the missingrecorded/run_id. On main the barerun_closedalso lacks the worker envelope.fatal_report_refusals_gain_no_receiptpasses on main, as intended.checkpoint_prefixmapping: the checkpoint test fails. The body has norecorded,run_idor attempt.entry().or_insertidentity instead of overwrite: the authoritative-identity unit test fails.Not in this PR
The cross-SDK conformance fixture and the implementation-aware
late-workflow-completioncontract are the Server agent's commit on this branch. Until that lands, the shared parity contract still expects 2.11.8's bodies for the three affected late reports.There is no CHANGELOG in this repo.
🤖 Generated with Claude Code