@openwop/spec-artifacts 2.39.4 → 2.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CORPUS-STAMP.json +11 -11
- package/api/seams-v2.yaml +148 -1
- package/api/v2/asyncapi.yaml +1 -1
- package/api/v2/openapi.yaml +1 -1
- package/package.json +1 -1
- package/spec/v1/core-standard-manifest.json +2 -2
- package/spec/v1/gaps.json +7 -7
- package/spec/v2/core/webhooks.md +5 -0
- package/spec/v2/release.json +3 -3
- package/spec/v2/surface-baseline.json +1 -1
package/CORPUS-STAMP.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "Provenance of @openwop/spec-artifacts (RFC 0168 §D.2). files: SHA-256 per file; the conformance suite compares the installed peer against dist/spec-artifacts.lock.json at start.",
|
|
3
3
|
"package": "@openwop/spec-artifacts",
|
|
4
|
-
"version": "2.
|
|
5
|
-
"corpusTag": "v2.
|
|
4
|
+
"version": "2.40.0",
|
|
5
|
+
"corpusTag": "v2.40.0",
|
|
6
6
|
"files": {
|
|
7
7
|
"api/.redocly.lint-ignore.yaml": "bf5a8350b88a72fa43f59605ed8d903ed24b6cfccda5e45509c9f6ed9ee4e712",
|
|
8
8
|
"api/asyncapi.yaml": "d5ecb9ee6114582be3b1f662c84bfac9ae96dae7bacb853e461168f70a8e1c7d",
|
|
9
9
|
"api/grpc/openwop.proto": "c3e72bb17cba514ee98feb6434e6c9b6ea6795bfd086489ec69fd882dd1ad977",
|
|
10
10
|
"api/openapi.yaml": "6366b5514be71098e9ea41fc81ff52bc39db1a547b353c0b344cc2a2bf5370aa",
|
|
11
11
|
"api/redocly.yaml": "b0604c89b2ca6d5076ec25725c539dad44a741a811fe524439ee6daef8baa09f",
|
|
12
|
-
"api/seams-v2.yaml": "
|
|
13
|
-
"api/v2/asyncapi.yaml": "
|
|
14
|
-
"api/v2/openapi.yaml": "
|
|
12
|
+
"api/seams-v2.yaml": "acd23b627e68bee6c886f21f4b8252f0dcb49d9deb5d4abc4c19d390f56b426a",
|
|
13
|
+
"api/v2/asyncapi.yaml": "de935d89b72a1196c0f8f9731f1c3d32cee118e81b4271365e76ffdc5c9fced6",
|
|
14
|
+
"api/v2/openapi.yaml": "0d12490c1739defdb302ec384d9dd81306d1d865c7023fda040baa846ff35c8b",
|
|
15
15
|
"api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
|
|
16
16
|
"schemas/README.md": "0c0b737ffcf8f30e7d2809cec8a498232de710f41443212922ad8337cdde0b51",
|
|
17
17
|
"schemas/a2a-task-state.schema.json": "75d5049dea8bd873ff0e7546f1c60c8d36c219bec7084264360be54a8be30ae1",
|
|
@@ -204,13 +204,13 @@
|
|
|
204
204
|
"schemas/workspace-file.schema.json": "464de85c2a068243084ee9c1d969bc7cd5d8f7948574e58450d6493c38a0e1e4",
|
|
205
205
|
"spec/v1/alias-detectors.json": "2401fcb1c18cdd688c018b3d716ae6bca85c5e356220ee2b793bd9d81872412d",
|
|
206
206
|
"spec/v1/capability-declaration-classes.json": "e7729aed5c4b4e1dd02abab0530f14cc95f5d4070fe51fb139e7f5cccefa00c6",
|
|
207
|
-
"spec/v1/core-standard-manifest.json": "
|
|
207
|
+
"spec/v1/core-standard-manifest.json": "ca5a83168c0e6a4d73d22d32acdd4ca37bfc62297ad730df6698134caa99ebbb",
|
|
208
208
|
"spec/v1/deprecations.json": "2d03f4729810280147ea08c630ea65434f2dc5370567d0aac41c3595f8337d40",
|
|
209
209
|
"spec/v1/deprecations.schema.json": "3e393c405d2a41b467d8c5e3c468549078df1ce6a6d2588fc488097b95d9b55b",
|
|
210
210
|
"spec/v1/event-codemap.json": "3da60d884157793a360da532a9fcbbfb5285636db325a74cec94b34622186d97",
|
|
211
211
|
"spec/v1/event-codemap.schema.json": "d05933b2e88103aff51a2f774df97b9bda065e0fc88dbd9b114cc47a32189174",
|
|
212
212
|
"spec/v1/extensions.json": "79a60754aa16cbdbdb604f8e5c00038af535a1690a4d07bd5ff7c38f0256a7bc",
|
|
213
|
-
"spec/v1/gaps.json": "
|
|
213
|
+
"spec/v1/gaps.json": "468d5b60f5e8b276f895833ac072c3984ab104d0ad97c4d31b757290c6077172",
|
|
214
214
|
"spec/v1/gaps.schema.json": "8fd83259f556553c9df0f53e7a82ca8c2a4771d471197f69b4e2ae0ceeacfd66",
|
|
215
215
|
"spec/v1/migrations.json": "8839497d6a4830b53fa7867014a8336f0d725ef2346352e54fb580b7947897af",
|
|
216
216
|
"spec/v1/migrations.schema.json": "886779aa6c22e646db097f5df210adb018a4dd14a7b815465a18c8a7056c8f72",
|
|
@@ -240,7 +240,7 @@
|
|
|
240
240
|
"spec/v2/core/security-defaults.md": "500471a8db7af9b776ac40b4a9a278c9f3d45e1c1dead880114e7fadc7bee37d",
|
|
241
241
|
"spec/v2/core/tool-catalog.md": "35f1a3fd509db0fc0490ddf400686fe94dc4b20c98064d5ebe776da516153e60",
|
|
242
242
|
"spec/v2/core/versioning.md": "0aded71d090c8358c3ce17763c120cfc1cc42531a5eaa1b8a2a7016ed9594069",
|
|
243
|
-
"spec/v2/core/webhooks.md": "
|
|
243
|
+
"spec/v2/core/webhooks.md": "95880896e37e5b7096693a75203cf0ed70cefa09959230e17b45aa9193078eff",
|
|
244
244
|
"spec/v2/core/workflow-chain-packs.md": "ee45d3fede3bb6f0cc6abcf0d6c929c7cd213a0fbe862b738a998f929c4d1102",
|
|
245
245
|
"spec/v2/corrections.json": "cc74ffde74384b0f261a4f625871d922e8661d654d647a8534e21036595cb280",
|
|
246
246
|
"spec/v2/corrections.schema.json": "4ac595b9a6d7f66d53de03fe0edbe0dc58de46426932387bfdc0ceb3cf949a3e",
|
|
@@ -292,9 +292,9 @@
|
|
|
292
292
|
"spec/v2/path-manifest.json": "ae9b56d03a701061fd48ad0b73cb0373493f6a4547da92763b422295b5e19535",
|
|
293
293
|
"spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
|
|
294
294
|
"spec/v2/profiles.json": "0636f19fceae625390003a347e70ef4797d84766b5c24ce8a02cea52aadebca4",
|
|
295
|
-
"spec/v2/release.json": "
|
|
295
|
+
"spec/v2/release.json": "f17a7e0066d7851adfb246c586f12199d14fee60bf7a0eda203a124116dcff9e",
|
|
296
296
|
"spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624",
|
|
297
|
-
"spec/v2/surface-baseline.json": "
|
|
297
|
+
"spec/v2/surface-baseline.json": "2ba53481583261c59008da86d1db82ed9f657675dfe2f8b019ec7d82f5bdb4b4"
|
|
298
298
|
},
|
|
299
|
-
"corpusCommit": "
|
|
299
|
+
"corpusCommit": "44b89e1e50f56dd2a0834771225b8e04d2bd9e6f"
|
|
300
300
|
}
|
package/api/seams-v2.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
openapi: 3.1.0
|
|
2
2
|
info:
|
|
3
3
|
title: OpenWOP conformance seams profile (openwop-conformance-seams-v2)
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.40.0
|
|
5
5
|
description: 'GENERATED by scripts/derive-v2-api.py. RFC 0168 §C: the seams are a versioned conformance profile with their
|
|
6
6
|
own document and path space (/conformance/seams/…), validated against the canonical v2 schemas with no tolerance path,
|
|
7
7
|
forbidden from the capability namespace (a host advertises the profile, never a testSeams flag). This document carries
|
|
@@ -747,6 +747,55 @@ paths:
|
|
|
747
747
|
$ref: '#/components/responses/ValidationError'
|
|
748
748
|
'401':
|
|
749
749
|
$ref: '#/components/responses/Unauthenticated'
|
|
750
|
+
/conformance/seams/sample/test/idempotency/hold:
|
|
751
|
+
post:
|
|
752
|
+
tags:
|
|
753
|
+
- Seams
|
|
754
|
+
operationId: armIdempotencyHold
|
|
755
|
+
summary: Hold the caller's next same-key create in flight — RFC 0213 §B witness
|
|
756
|
+
description: 'Arms a single-use hold: the caller tenant''s next real `POST /runs` carrying `key` as its Idempotency-Key
|
|
757
|
+
keeps its Layer-1 claim in flight for `holdMs` after claiming, then completes normally. The seam MUST NOT answer a
|
|
758
|
+
create or emit a 409: the refusal a concurrent same-key create receives while the claim is held comes from the host''s
|
|
759
|
+
production in-flight branch (idempotency.md §Concurrency). Keyed by (tenant, key); single-use; an unarmed key is never
|
|
760
|
+
delayed.'
|
|
761
|
+
requestBody:
|
|
762
|
+
required: true
|
|
763
|
+
content:
|
|
764
|
+
application/json:
|
|
765
|
+
schema:
|
|
766
|
+
type: object
|
|
767
|
+
additionalProperties: false
|
|
768
|
+
required:
|
|
769
|
+
- key
|
|
770
|
+
- holdMs
|
|
771
|
+
properties:
|
|
772
|
+
key:
|
|
773
|
+
type: string
|
|
774
|
+
pattern: ^[A-Za-z0-9._~-]{22,128}$
|
|
775
|
+
holdMs:
|
|
776
|
+
type: integer
|
|
777
|
+
minimum: 1
|
|
778
|
+
maximum: 10000
|
|
779
|
+
responses:
|
|
780
|
+
'201':
|
|
781
|
+
description: The hold is armed.
|
|
782
|
+
content:
|
|
783
|
+
application/json:
|
|
784
|
+
schema:
|
|
785
|
+
type: object
|
|
786
|
+
additionalProperties: false
|
|
787
|
+
required:
|
|
788
|
+
- key
|
|
789
|
+
- holdMs
|
|
790
|
+
properties:
|
|
791
|
+
key:
|
|
792
|
+
type: string
|
|
793
|
+
holdMs:
|
|
794
|
+
type: integer
|
|
795
|
+
'400':
|
|
796
|
+
$ref: '#/components/responses/ValidationError'
|
|
797
|
+
'401':
|
|
798
|
+
$ref: '#/components/responses/Unauthenticated'
|
|
750
799
|
/conformance/seams/sample/webhooks/receive:
|
|
751
800
|
post:
|
|
752
801
|
tags:
|
|
@@ -980,6 +1029,104 @@ paths:
|
|
|
980
1029
|
$ref: '#/components/responses/Unauthenticated'
|
|
981
1030
|
'404':
|
|
982
1031
|
$ref: '#/components/responses/NotFound'
|
|
1032
|
+
/conformance/seams/sample/auth/credential/mint:
|
|
1033
|
+
post:
|
|
1034
|
+
tags:
|
|
1035
|
+
- Seams
|
|
1036
|
+
operationId: mintLaneCredential
|
|
1037
|
+
summary: Mint a fresh credential on an advertised lane for the caller's tenant — RFC 0170 §B.3 witness
|
|
1038
|
+
description: 'Issues a new credential on `lane` (an advertised `auth.lanes[].lane` whose `revocation` is `next-request`;
|
|
1039
|
+
absent ⇒ `api-key`) for the AUTHENTICATED caller''s tenant, through the host''s PRODUCTION issuance path, so the credential
|
|
1040
|
+
it returns authenticates on the canonical API exactly as an ordinary one does. The suite uses it only to hold a credential
|
|
1041
|
+
it can then revoke: the revocation, and the refusal on the next request (`401 credential_revoked`), are production
|
|
1042
|
+
behaviour the seam does not simulate. A host that has not wired the seam answers `404`, and the scenario records the
|
|
1043
|
+
requirement unobserved.'
|
|
1044
|
+
requestBody:
|
|
1045
|
+
required: true
|
|
1046
|
+
content:
|
|
1047
|
+
application/json:
|
|
1048
|
+
schema:
|
|
1049
|
+
type: object
|
|
1050
|
+
additionalProperties: false
|
|
1051
|
+
properties:
|
|
1052
|
+
lane:
|
|
1053
|
+
type: string
|
|
1054
|
+
minLength: 1
|
|
1055
|
+
description: An advertised `auth.lanes[].lane`; absent ⇒ `api-key`.
|
|
1056
|
+
responses:
|
|
1057
|
+
'201':
|
|
1058
|
+
description: The minted credential.
|
|
1059
|
+
content:
|
|
1060
|
+
application/json:
|
|
1061
|
+
schema:
|
|
1062
|
+
type: object
|
|
1063
|
+
additionalProperties: false
|
|
1064
|
+
required:
|
|
1065
|
+
- lane
|
|
1066
|
+
- credential
|
|
1067
|
+
properties:
|
|
1068
|
+
lane:
|
|
1069
|
+
type: string
|
|
1070
|
+
credential:
|
|
1071
|
+
type: string
|
|
1072
|
+
minLength: 1
|
|
1073
|
+
description: 'The bearer credential, presented as `Authorization: Bearer <credential>`.'
|
|
1074
|
+
subjectId:
|
|
1075
|
+
type: string
|
|
1076
|
+
description: The Subject the credential authenticates as, when the host names one.
|
|
1077
|
+
'400':
|
|
1078
|
+
$ref: '#/components/responses/ValidationError'
|
|
1079
|
+
'401':
|
|
1080
|
+
$ref: '#/components/responses/Unauthenticated'
|
|
1081
|
+
'404':
|
|
1082
|
+
$ref: '#/components/responses/NotFound'
|
|
1083
|
+
/conformance/seams/sample/auth/credential/revoke:
|
|
1084
|
+
post:
|
|
1085
|
+
tags:
|
|
1086
|
+
- Seams
|
|
1087
|
+
operationId: revokeLaneCredential
|
|
1088
|
+
summary: Revoke a credential the mint seam issued — RFC 0170 §B.3 witness
|
|
1089
|
+
description: Revokes `credential` through the host's PRODUCTION revocation path. On a `next-request` lane the very next
|
|
1090
|
+
request presenting it MUST be refused `401 credential_revoked` (identity.md §2.2); the suite observes that on the
|
|
1091
|
+
canonical API, not through this seam. `404` when no active credential matches.
|
|
1092
|
+
requestBody:
|
|
1093
|
+
required: true
|
|
1094
|
+
content:
|
|
1095
|
+
application/json:
|
|
1096
|
+
schema:
|
|
1097
|
+
type: object
|
|
1098
|
+
additionalProperties: false
|
|
1099
|
+
required:
|
|
1100
|
+
- credential
|
|
1101
|
+
properties:
|
|
1102
|
+
lane:
|
|
1103
|
+
type: string
|
|
1104
|
+
minLength: 1
|
|
1105
|
+
credential:
|
|
1106
|
+
type: string
|
|
1107
|
+
minLength: 1
|
|
1108
|
+
responses:
|
|
1109
|
+
'200':
|
|
1110
|
+
description: The credential is revoked.
|
|
1111
|
+
content:
|
|
1112
|
+
application/json:
|
|
1113
|
+
schema:
|
|
1114
|
+
type: object
|
|
1115
|
+
additionalProperties: false
|
|
1116
|
+
required:
|
|
1117
|
+
- revoked
|
|
1118
|
+
properties:
|
|
1119
|
+
revoked:
|
|
1120
|
+
type: boolean
|
|
1121
|
+
const: true
|
|
1122
|
+
lane:
|
|
1123
|
+
type: string
|
|
1124
|
+
'400':
|
|
1125
|
+
$ref: '#/components/responses/ValidationError'
|
|
1126
|
+
'401':
|
|
1127
|
+
$ref: '#/components/responses/Unauthenticated'
|
|
1128
|
+
'404':
|
|
1129
|
+
$ref: '#/components/responses/NotFound'
|
|
983
1130
|
components:
|
|
984
1131
|
securitySchemes:
|
|
985
1132
|
ApiKeyAuth:
|
package/api/v2/asyncapi.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
asyncapi: 3.1.0
|
|
2
2
|
info:
|
|
3
3
|
title: OpenWOP v2 event streams
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.40.0
|
|
5
5
|
description: GENERATED by scripts/derive-v2-api.py (RFC 0171 §E.1, RFC 0172 §C.2). One run-events channel whose address
|
|
6
6
|
is the OpenAPI path key; the server pathname is empty (bare origin). streamMode is a pattern over the closed set and its
|
|
7
7
|
comma-separated combinations (`values` never combines). hostEvents has a real address (RFC 0171 §E.1) — the documented
|
package/api/v2/openapi.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
openapi: 3.1.0
|
|
2
2
|
info:
|
|
3
3
|
title: OpenWOP v2 API
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.40.0
|
|
5
5
|
summary: REST surface for declaring, executing, suspending, resuming, and observing multi-step workflows.
|
|
6
6
|
description: GENERATED by scripts/derive-v2-api.py from api/openapi.yaml and the RFC 0167 children (v2 charter Phase 3,
|
|
7
7
|
P3-C). Bare origin, unversioned path keys, negotiation by `OpenWOP-Version` + `protocolVersions[]` (RFC 0172 §A). No seam
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openwop/spec-artifacts",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.40.0",
|
|
4
4
|
"description": "The OpenWOP machine-readable contract: api/ (OpenAPI, AsyncAPI, the seams profile), schemas/ (v1 and v2 JSON Schemas), the spec/v1 and spec/v2 registries (errors, event codemap, declaration, deprecations, migrations, gaps) and CORPUS-STAMP.json — published from the openwop/openwop corpus tag; the peer dependency @openwop/openwop-conformance digest-checks at start (RFC 0168 §D.2).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"provenance": {
|
|
5
5
|
"suite": {
|
|
6
6
|
"package": "@openwop/openwop-conformance",
|
|
7
|
-
"version": "2.
|
|
7
|
+
"version": "2.40.0"
|
|
8
8
|
},
|
|
9
9
|
"note": "Derived from the corpus at generation time. Regenerate with --write; verify with --check."
|
|
10
10
|
},
|
|
@@ -420,5 +420,5 @@
|
|
|
420
420
|
"$id": "https://openwop.dev/spec/v1/workspace-file.schema.json"
|
|
421
421
|
}
|
|
422
422
|
],
|
|
423
|
-
"digest": "
|
|
423
|
+
"digest": "0b4806ae6ef9fc2e593216c486384bf915130e5b51ac879528d9362b2107ddb0"
|
|
424
424
|
}
|
package/spec/v1/gaps.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./gaps.schema.json",
|
|
3
3
|
"version": 1,
|
|
4
|
-
"updated": "2026-09-
|
|
4
|
+
"updated": "2026-09-26",
|
|
5
5
|
"witnessClasses": [
|
|
6
6
|
"witnessable-unaided",
|
|
7
7
|
"witnessable-gated",
|
|
@@ -12668,8 +12668,8 @@
|
|
|
12668
12668
|
"surface": "No `SECURITY/threat-model-*.md` covers availability or noisy-neighbour abuse. `webhook-delivery-isolation` has no threat model to cite, and `secret-leakage` does not fit a delay.",
|
|
12669
12669
|
"witness": "unclassified",
|
|
12670
12670
|
"requirementId": null,
|
|
12671
|
-
"disposition": "
|
|
12672
|
-
"target":
|
|
12671
|
+
"disposition": "closed",
|
|
12672
|
+
"target": null,
|
|
12673
12673
|
"sources": [
|
|
12674
12674
|
{
|
|
12675
12675
|
"file": "RFCS/registers/0215-webhook-delivery-isolation.gaps.md",
|
|
@@ -12685,8 +12685,8 @@
|
|
|
12685
12685
|
"surface": "The cross-tenant leg needs tenant A to hold at least the §A.2 floor of attempts open while tenant B's delivery is observed. Its pass condition is a SHOULD, so it reports. Whether a reporting leg belongs in the certified bundle, or only in the run log, is not decided.",
|
|
12686
12686
|
"witness": "unclassified",
|
|
12687
12687
|
"requirementId": null,
|
|
12688
|
-
"disposition": "
|
|
12689
|
-
"target":
|
|
12688
|
+
"disposition": "closed",
|
|
12689
|
+
"target": null,
|
|
12690
12690
|
"sources": [
|
|
12691
12691
|
{
|
|
12692
12692
|
"file": "RFCS/registers/0215-webhook-delivery-isolation.gaps.md",
|
|
@@ -12719,8 +12719,8 @@
|
|
|
12719
12719
|
"surface": "The floor of 8 is chosen to exceed the one measured claim batch (openwop-app, 5), not derived from prior art. Hosted webhook services' published delivery designs were not surveyed.",
|
|
12720
12720
|
"witness": "unclassified",
|
|
12721
12721
|
"requirementId": null,
|
|
12722
|
-
"disposition": "
|
|
12723
|
-
"target":
|
|
12722
|
+
"disposition": "closed",
|
|
12723
|
+
"target": null,
|
|
12724
12724
|
"sources": [
|
|
12725
12725
|
{
|
|
12726
12726
|
"file": "RFCS/registers/0215-webhook-delivery-isolation.gaps.md",
|
package/spec/v2/core/webhooks.md
CHANGED
|
@@ -16,6 +16,8 @@ A host that advertises `webhooks` (capabilities.md) serves `registerWebhook` (`P
|
|
|
16
16
|
| `registerWebhook` | `{ url, events[], secret?, tags?, signatureAlgorithms? }`; `url` MUST be `https://`; `events[]` MUST be non-empty v2 event type names (events.md) | `201 { webhookId }` |
|
|
17
17
|
| `unregisterWebhook` | path `webhookId` | `204`; `404` when unknown; `403` when the caller is outside the subscription's tenant |
|
|
18
18
|
|
|
19
|
+
A `204` from `unregisterWebhook` ends the subscription's deliveries, including retries already scheduled (§Durability; RFC 0215 §B).
|
|
20
|
+
|
|
19
21
|
A subscription MUST receive only events from runs within its tenant scope; cross-tenant delivery is a protocol violation whatever the filter says (invariant `webhook-cross-tenant-isolation`). `tags` narrows delivery to runs whose options carry an overlapping tag.
|
|
20
22
|
|
|
21
23
|
## Delivery
|
|
@@ -66,6 +68,9 @@ Durable delivery is an obligation of the `webhooks` surface (RFC 0173 §B; secur
|
|
|
66
68
|
- retry a failed attempt per its advertised `retryPolicy` (`maxAttempts`, `backoff ∈ none | fixed | exponential`) with backoff between attempts;
|
|
67
69
|
- route a delivery whose retries are exhausted to the dead-letter sink, rather than drop it. A host advertising `webhooks.deadLetter` MUST serve `GET /webhooks/{webhookId}/dead-letters` (RFC 0188), and that record MUST NOT carry the delivered body, the delivery headers, or the subscription secret — a dead-letter read names a delivery, it does not replay one. This sink is the DELIVERY sink; the `deadLetter` family (RFC 0053) is the RUN sink and is a different thing;
|
|
68
70
|
- deliver each matching event at least once; a receiver MAY observe the same event more than once;
|
|
71
|
+
- not make the start of an attempt to one subscription wait for an attempt to a *different* subscription to finish (answered, failed, or timed out): one subscription's slow or dead receiver MUST NOT delay another subscription's deliveries (invariant `webhook-delivery-isolation`, RFC 0215 §A.1). The rule is about when an attempt starts, not how soon after an event it must start, and it names no mechanism: a lane per subscription, a concurrent pool or asynchronous I/O all meet it, and a sequential loop over a batch does not;
|
|
72
|
+
- sustain that while at least **8** subscriptions have attempts outstanding that their receivers have not answered (RFC 0215 §A.2). A host MAY bound concurrent attempts beyond that, and SHOULD NOT let one tenant's unanswered attempts occupy capacity another tenant's deliveries need (§A.3);
|
|
73
|
+
- after `unregisterWebhook` answers `204`, not start any further attempt for that subscription, including attempts already scheduled for retry (invariant `webhook-unregister-stops-delivery`, RFC 0215 §B). An attempt whose request the host had begun sending before the `204` MAY complete. The unregister does not oblige the host to route that subscription's undelivered events to the dead-letter sink: the exhaustion rule above governs deliveries of a live subscription;
|
|
69
74
|
- dead-letter a `payload_unprojectable` delivery (events.md §Era-2) on the first attempt, never retry it.
|
|
70
75
|
|
|
71
76
|
Best-effort delivery is not a conforming mode. A `3xx` response is a delivery failure, retried under the same policy. `webhook-durable-delivery` witnesses it.
|
package/spec/v2/release.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "RFC 0172 §D.1 — the one release identity the v2 artifacts derive from. `version` is the next corpus tag `v<version>` (the publish workflow's coordinated-release tag pattern `v*`; RFC 0172's `openwop/v2.<minor>.<patch>` spelling is amended to this at its flip). api/v2/*.yaml info.version, the suite's 2.x version and @openwop/spec-artifacts read it. Bumped by the release PR that cuts the tag, never by hand elsewhere.",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"corpusTag": "v2.
|
|
5
|
-
"updated": "2026-09-
|
|
3
|
+
"version": "2.40.0",
|
|
4
|
+
"corpusTag": "v2.40.0",
|
|
5
|
+
"updated": "2026-09-26"
|
|
6
6
|
}
|