R1.2 Webhook task hardening - #9
Merged
Merged
Conversation
Closes #6
This was referenced Sep 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements R1.2 (webhook task hardening) from the community-base unification plan. Closes #6.
What changed
202with{"lease_seconds": N}; Relay keeps the taskrunningunder a lease and fails it when the lease expires without a completion. New client callbacksPOST /api/tasks/{id}/completeandPOST /api/tasks/{id}/fail(client-scoped, idempotent, conflict-aware). Lease cap 3600s (RELAY_WEBHOOK_MAX_LEASE_SECONDS).429and5xxretry with exponential backoff (5s base) up to per-taskmax_attempts, now defaulting to 5. Other4xx(including408,425) fail immediately. Response bodies stored on the task are truncated to 2 KB (was 4 KB).RELAY_WEBHOOK_MAX_INFLIGHT_PER_CLIENT) and a 60-per-minute creation rate (RELAY_WEBHOOK_RATE_LIMIT_PER_MINUTE), both rejected with429. Schedule-created tasks are exempt (operator-configured, cron-bounded).X-Relay-Attemptheader added (1-based);X-Relay-Task-Id,X-Relay-Correlation-Id,X-Relay-Timestamp,X-Relay-Signatureunchanged./jobs/dead-letters/(staff-gated): failed tasks with attempts, response, error, and a retry button that requeues with attempts reset.docs/api.md.docs/api.mddocuments the 202 lease protocol, the retry table, per-client limits and the reference receiver;docs/context.mdopen decision 1 is resolved; README updated.Verification
uv run pytest tests -k webhook-> pass:Full suite -> pass (
562 passed in 265.63s);uv run ruff check .-> pass;manage.py checkandmakemigrations --check --dry-run-> clean.Live receiver scenarios (local HTTPS receiver with a self-signed cert and test database; no credentials, no shared infrastructure touched):
timeout_seconds: 60,max_attempts: 1->failedafter 60.1s, errorwebhook request failed: The read operation timed out.202 {"lease_seconds": 300}and completing after 90s ->runningunder lease, thenPOST /api/tasks/{id}/complete->200, tasksucceeded, result stored.Sandbox-native runs of the same scenarios are not included: they need this branch deployed to the sandbox and a registered sleeping receiver on a client origin. The deployment happens through the normal push-to-
mainrelease; the checks should be repeated there afterwards.