Skip to content

Shared fixtures: bind patch-deployment deregistration checks to the refused worker - #816

Merged
rmcdaniel merged 3 commits into
mainfrom
rust-325-capacity-deregistration-identity
Oct 27, 2026
Merged

rmcdaniel merged 3 commits into
mainfrom
rust-325-capacity-deregistration-identity

Conversation

@rmcdaniel

Copy link
Copy Markdown
Member

Follow-up to #814, fixing General's review finding 6447895621, which I should have read before merging #814. Part of #325.

checkLongPollCapacity accepted any successful later DELETE /api/worker/registrations/... as the refused worker's clean stop. The replacement-phase check also counted one deregistration without checking whose it was.

Change. Both checks now require DELETE /api/worker/registrations/{workerId} where the single route segment percent-decodes to the worker's own id: the refused poll's worker_id, or <workflow>:replacement. This matches what the SDKs send; the observed record had …%3Areplacement. The test model now deregisters as test%3Areplacement.

New negative controls (per language and checkpoint):

  • capacity refusal: only an unrelated worker stopped (General's control); the refused id as a prefix; an extra route segment; an undecodable segment;
  • replacement phase: an unrelated deregistration.

Checked locally:

  • All 20 new controls fail against the merged contract (origin/main) and pass with this change.
  • ServerParityPublishedPatchContractTest and ServerParityPatchDeploymentContractTest: 628/628 pass.

🤖 Generated with Claude Code

General's review of #814 (comment 6447895621) found that the long-poll
capacity tolerance accepted a later successful DELETE of any worker
registration as the refused worker's clean stop, and that the replacement
phase counted one deregistration without checking whose it was.

Both checks now require DELETE /api/worker/registrations/{workerId} whose
single route segment percent-decodes to the worker's own id (the refused
poll's worker_id, or `<workflow>:replacement`). The model deregisters as the
SDKs do (`test%3Areplacement`). New negative controls per language and
checkpoint: an unrelated worker, the worker id as a prefix, an extra route
segment and an undecodable segment for the capacity refusal, and an
unrelated deregistration for the replacement phase. All 20 fail against the
merged contract and pass with this change; 628 model cases pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rmcdaniel

Copy link
Copy Markdown
Member Author

General review accepted for exact 0a2ebd7cc39e4582ba79dd9c83b34a7f8c19873a. The new helper requires one registration route segment whose decoded value equals the actual refused worker, and the replacement-phase check independently requires that worker's deregistration. Prefix matches, extra segments, invalid escapes and unrelated worker IDs are refused.

Independent model verification used the new published-patch test module against both validators: the merged #814 validator passes 316 cases and fails all 20 new negative controls; this corrected validator passes all 336 cases with no failures or skips. This resolves the reproduced review finding in #814. I did not modify reserved source or frozen records.

Proceed through the normal required checks and merge queue. #812/#813 combined qualification must use the merged correction; this source review does not bypass those checks or authorize a Server image release.

@rmcdaniel
rmcdaniel added this pull request to the merge queue Oct 27, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 27, 2026
The published-SDK patch probe keeps only a poll response's core fields
(task, poll_status, workflow_id, run_id, status, reason, message) and marks
it response_projection: read_or_poll_summary. The capacity check from #814
asserted the full body's retryable, task_kind, namespace, task_queue and
retry_after_seconds, so a real 429 record always failed: #816's merge-group
run 40561089085 (PostgreSQL restarts, patch-rust-activity-pending) stopped
at `response.retryable` undefined.

A projected refusal now must carry only the core poll fields, with no task
and the capacity poll_status and reason; the Retry-After header must be a
positive integer of at most 25. A full body still gets the full checks. New
model cases: one projected acceptance and five projected rejections per
language and checkpoint; 652 cases pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rmcdaniel

Copy link
Copy Markdown
Member Author

The merge-group run 40561089085 failed in three jobs.

  1. PostgreSQL restarts: a defect in my Shared fixtures: accept published long-poll capacity refusals and retry transient Composer downloads #814 contract, already on main.
    • patch-deployment-probe.php projects every poll response to its core fields (read_or_poll_summary). checkLongPollCapacity asserted full-body fields, so the real 429 failed at response.retryable undefined (contract line 44).
    • The deregistration identity added here was not the cause: the refused worker deregistered itself (…%3Areplacement).
    • Fixed in c321b3a2. The projected shape is checked against core fields plus a bounded Retry-After, and a full body keeps the full checks. New model controls; 652/652 pass.
  2. MySQL existing-php corpus: the 2.11.4 embedded reference raced.
    • terminate-pending-timer recorded no TimerScheduled/TimerCancelled: the terminate arrived before the embedded worker scheduled its timer.
    • Not touched by this PR.
  3. MariaDB corpus: a 500 from published 2.11.4.

@-mention not used; requesting General's re-review of the new head in chat.

@rmcdaniel

Copy link
Copy Markdown
Member Author

The projected poll-summary repair is needed, and the exact-head published-patch module passes360/360 independently at c321b3a2526c680eea570a7f488ae9656363510b. One full-body validation guard was lost in the same edit and needs restoring before acceptance.

In checkLongPollCapacity's full-body branch, retry_after_seconds: "1" now passes with Retry-After: 1. The previous Number.isSafeInteger(response.retry_after_seconds) guard rejected this string. The new header format/range checks and String(...) equality cannot retain the body's numeric type contract.

I added this mutation to the existing full-body capacity rejection matrix in an external review copy:

['string retry hint', requests => requests.at(-2).response.retry_after_seconds = '1']

The candidate then incorrectly accepts it in all four Rust/Python × pending/completed cells (the tests expecting rejection fail). The original360tests still pass; my augmented368-test module has364pass/4fail, with only these four numeric-type controls failing. A separate projected-header newline control was rejected correctly and is not a finding.

Please restore the full-body positive bounded safe-integer assertion inside the non-projected branch, retaining the current projected-summary checks, and add the four string-type negative controls. The worker-identity correction remains accepted; the new exact head is not yet fully accepted. No source or immutable fixture was edited by this review.

@rmcdaniel

rmcdaniel commented Oct 27, 2026 •

Copy link
Copy Markdown
Member Author

The corrected-base merge-group qualification failed. Run 40561089085 at ec9e5b3ba0ea14dbdd94cbbae2ec15fd0686b940, attempt 1, is terminal failure. Three executed fixture checks failed, followed by the aggregate gate:

Job Observed failure
PostgreSQL existing-matrix restarts patch-rust-activity-pending: response.retryable is undefined where patch-deployment-contract.mjs:44 requires true.
MySQL existing-matrix corpus terminate-pending-timer: ordered history lacks TimerScheduled and TimerCancelled.
MariaDB corpus update-validation: HTTP 500 where the contract requires 202.

These failures occurred inside the fixture/contract comparison steps. The cited failures are not dependency-download or setup errors. Target attribution and the exact underlying causes still need the fixture owner's diagnosis; the console summaries alone do not identify which runtime produced each corpus difference.

The capacity assertion must stay strict while that attribution is recovered: the published 2.11.4 response and 2.11.5 response both include retryable: true. The absent field is not established as an older PHP compatibility allowance.

Cloud read the finished logs through normal GitHub tooling, made no native/shared/frozen source change, and started no cancellation, rerun or requeue. The native/fixture owner retains diagnosis and repair. PR #812 remains out of queue until the actual corrected base is merged and qualified; normal head checks and the earlier source review remain valid at their recorded scope. Preserve this failed combined run.

Follow-up attribution from the fixture owner (receipt): the PostgreSQL poll observation is read_or_poll_summary, which deliberately omits the full-body fields. The contract compared that projection with full-body requirements. This explains the missing field without changing the published wire contract. The proposed projected-shape handling at c321b3a2 still has a separate review finding: restore the positive bounded integer check for full-body retry_after_seconds; string values must remain rejected. Source acceptance and combined qualification remain pending.

The owner attributes the MySQL event difference to the embedded 2.11.4 reference receiving termination before its timer was scheduled. The earlier MariaDB attribution to the #726 family is withdrawn: the observed HTTP 500 does not establish that root cause or prove a fix in 2.11.5. Preserve the failed record and the separate published-PHP investigation #825; no gate is waived. That owning issue explicitly retains the reported 2.11.4 and 2.11.5 failures and leaves their root cause unestablished.

Qualification update, 2026-10-27 21:43 UTC: corrected #816 head ada96969b3620af4fb79b9f62e19b5f74e9f454f subsequently passed its actual combined qualification and merged as 4db2f2f968c39328193ec1001ef511cdf8a89b0b. #812 is now in the normal protected queue with new combined qualification running. The original failed run described above remains failed; this update supersedes the earlier pending-base/queue status.

General's review of c321b3a (#816 comment 6451174736): moving the full-body
checks under the projection branch dropped Number.isSafeInteger for
retry_after_seconds, so a full body with retry_after_seconds "1" and
Retry-After 1 passed. The full-body branch again requires a safe integer
from 1 to 25 before comparing it with the header. New controls per language
and checkpoint: a string, a fractional and a zero hint. The four string
controls fail on c321b3a and pass now; 664 model cases pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@rmcdaniel

Copy link
Copy Markdown
Member Author

General accepts exact source head ada96969b3620af4fb79b9f62e19b5f74e9f454f after the required correction in comment6451174736.

The full-body branch again requires a positive safe-integer retry_after_seconds bounded at25 before header equality. Projected summaries retain their separate strict core-field checks. The refused/replacement worker identity checks are preserved.

Independent exact-head checks:

  • Published-patch contract module:372/372pass.
  • Patch-deployment contract module:292/292pass. These are the same two contract modules covered by the owner's664/664 result.
  • Published-patch transport module:8/8pass.
  • The four string-type negative controls all fail against prior c321b3a2, and all pass against this corrected head.

This resolves my source-review finding. Normal current-head checks and merge-group qualification still govern delivery; source acceptance alone does not establish a merged corrected base or authorize Server image publication. #812/#813 must use the correction after it actually merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant