@openwop/spec-artifacts 2.36.1 → 2.37.1

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 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.36.1",
5
- "corpusTag": "v2.36.0",
4
+ "version": "2.37.1",
5
+ "corpusTag": "v2.37.1",
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
- "api/openapi.yaml": "d19c3d4af7fd0e2305dd5fee3d5b984c6d0e9b1824d72d8dffc714a92b248269",
10
+ "api/openapi.yaml": "6366b5514be71098e9ea41fc81ff52bc39db1a547b353c0b344cc2a2bf5370aa",
11
11
  "api/redocly.yaml": "b0604c89b2ca6d5076ec25725c539dad44a741a811fe524439ee6daef8baa09f",
12
- "api/seams-v2.yaml": "591e917af3c0e79fbe831690b35383bcc6976dd2e0ef0660c5e3bce5541dc9c2",
13
- "api/v2/asyncapi.yaml": "813a73c8c1e25c55cb60d92aecf2fc1361ab84894c4e15a378c49d11dd0f1984",
14
- "api/v2/openapi.yaml": "e5156f2a6d241cec8bb4f5dd872400b4bc021dff61e572ab4b11c52105d18f22",
12
+ "api/seams-v2.yaml": "02711723089859b95144d788c0caabf215305570c7d48be1da5d2a5e21eff4e1",
13
+ "api/v2/asyncapi.yaml": "d492342046d1ec3a2dc43a28c4cf6d821d6319c30a8552cbc8fcd13806f7b2df",
14
+ "api/v2/openapi.yaml": "ba67b61f0e716444308ef7bc00416b8eee5829c28b67c5070358d35c3ca64d36",
15
15
  "api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
16
16
  "schemas/README.md": "0c0b737ffcf8f30e7d2809cec8a498232de710f41443212922ad8337cdde0b51",
17
17
  "schemas/a2a-task-state.schema.json": "75d5049dea8bd873ff0e7546f1c60c8d36c219bec7084264360be54a8be30ae1",
@@ -116,8 +116,8 @@
116
116
  "schemas/v2/artifact.schema.json": "85b9dc284d39b99d2a66fa8cbf47f2dd3d8526d3bd6cc411ad315b372de38975",
117
117
  "schemas/v2/audit-verify-result.schema.json": "a748dfadb5155adfde8a91045e76b0db6695005fe559035d71670fdd55edb3ba",
118
118
  "schemas/v2/budget-policy.schema.json": "c7449daeb6e1d95e7a047b8c2814d3d54748697c27046972834874867d9b5f4c",
119
- "schemas/v2/capabilities.schema.json": "0b895f892c4f29317782a81dd2c6ca6f90de972d99248d65d5eb0c3ab96e1b5d",
120
- "schemas/v2/certification-bundle.schema.json": "9caf701cf991644aeb3a95beedd4acdef0b3fb351c41b0eaaa804e658af75642",
119
+ "schemas/v2/capabilities.schema.json": "79ddbbda4686fe66ec5b0aaf9bafb2385e2f8ccbb1ee39087d75cef9fd1f1628",
120
+ "schemas/v2/certification-bundle.schema.json": "cb8ddf29a008b1c2e731fd61f855bf072790b700e458c702c40434b6fd47ba73",
121
121
  "schemas/v2/channel-presence-payload.schema.json": "4eeb224910e6f7eee3ab5e7c3899559c25c83a26d0022e0ad195c4672611977a",
122
122
  "schemas/v2/channel-written-payload.schema.json": "ccecff3c71a3275ad8db035ac0e04db25e6f8d23cf091ddbcd5a60d1be1cbb6b",
123
123
  "schemas/v2/chat-card-pack-manifest.schema.json": "f7cf09b30d3e1d2251620d84ea9f65541438d015ab44aec10d1dcfe68d8bd496",
@@ -204,38 +204,38 @@
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": "99e982c47f276fd0acd7791ed80bedf815c13ec2b9ca74a4e97b53e30d77f7d7",
207
+ "spec/v1/core-standard-manifest.json": "c92119aa65b12b7f670cc59e7768177d20f8f47173e7fbd15c3b0a6ef340810a",
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": "3002e1a77b9011de98e6e1178fbb934059794b0bcecd09519ea127331ae99073",
213
+ "spec/v1/gaps.json": "c20fe027514c8778b34c8fadde8bc7d1db5c4034638d76d297cc89a5bcae7ba2",
214
214
  "spec/v1/gaps.schema.json": "8fd83259f556553c9df0f53e7a82ca8c2a4771d471197f69b4e2ae0ceeacfd66",
215
215
  "spec/v1/migrations.json": "8839497d6a4830b53fa7867014a8336f0d725ef2346352e54fb580b7947897af",
216
216
  "spec/v1/migrations.schema.json": "886779aa6c22e646db097f5df210adb018a4dd14a7b815465a18c8a7056c8f72",
217
- "spec/v1/operation-path-manifest.json": "5f5f4e3842669371730ebbd1aace3fb794192615f0018dc144f484ba5db82ac3",
218
- "spec/v1/spec-gaps.json": "eb3bfcb9c7d05c9a6845d4a40ec9493cfaa5a671932daf1af561310136fdabe8",
217
+ "spec/v1/operation-path-manifest.json": "968c2fceb27cf76acbb14f7d250798f913bbbd3e9789dd3fb2a32c1ef536e771",
218
+ "spec/v1/spec-gaps.json": "ea9612af55bea9dae3e82223d4f714243f8f590b0193f1188257b13491a38afc",
219
219
  "spec/v2/README.md": "ccfed7d978ce7293a6d8f3c0f6045835dfb7e488f2b67a68f6b04d67c4b0cfc9",
220
220
  "spec/v2/core/capabilities.md": "e22381fed3a93376ad478fa7f07571fdad9653aefd7c05f189a2ef45a49f6c63",
221
- "spec/v2/core/conformance.md": "4998af6d64dd6073a396715b5d91476304041dc1493a82885c6c91f098e28e81",
221
+ "spec/v2/core/conformance.md": "975bf95049247530d723e81051b6e64bd2a1f9d150183dec391cae48e35af7cc",
222
222
  "spec/v2/core/connection-packs.md": "7257339a4fc125011bf89961588c949cb058a0c0f7d9808ad08a5288deb3f8fc",
223
223
  "spec/v2/core/conversation.md": "c9a937e410fdb4e092cec2f05a7cab8b5232745090cfb20041c03c70ccbb5cdc",
224
- "spec/v2/core/errors.md": "0210177647748efc5fe9affe29a9029152c4f983248ca88c6375601d7e636591",
225
- "spec/v2/core/events.md": "ee0f7d2cff5bfa29c38b77f73269d7d755b8036cb1a5c7b08f02de92c54f3490",
224
+ "spec/v2/core/errors.md": "619cd74381c41acf33d012eb1afd834e148ac292663a1a6fa6744dbaff9661d9",
225
+ "spec/v2/core/events.md": "3df1866a89fb765c78adf79b8d51f96c59584dee9aa88d59179b88a681d827da",
226
226
  "spec/v2/core/form-content-packs.md": "55ea021b5f676f5851f420b3597262fbf6642108bddeb726922f8c5dd96c968b",
227
- "spec/v2/core/headers.md": "c0e954d08e777e43816fbbabca61f303b78db09596b878d818b7a5022a1b6e7c",
227
+ "spec/v2/core/headers.md": "38c03524e3aeea30862432d203faffb8b0b05ec65136d223e96ab06941c3717b",
228
228
  "spec/v2/core/host-services.md": "c3ff93201d642edf835e9ae84f06a17edd207f9d351f8bfd70572be05bbfd894",
229
- "spec/v2/core/idempotency.md": "a8492ec66569c00f6f559b75b80d93adbbc0e964a24e8dfedfe4057e47d69a9f",
229
+ "spec/v2/core/idempotency.md": "b61d4a200526485937897dd7ebca092e68424a80624e0be7a0b7db8cdbd54403",
230
230
  "spec/v2/core/identity.md": "d15667552019f64316be9b65c6e5696c10766581405f05da3907fb47f36e6459",
231
- "spec/v2/core/interop.md": "5b7d2742eef309b1590b8d6690e5435b00c2ce6b395284684e3591cb9ed47bab",
232
- "spec/v2/core/interrupt.md": "e0199472da7d0412721ab487a07c82fe78cbc9b60612bfcfc3cb876dc1a8c96f",
231
+ "spec/v2/core/interop.md": "ec8239d6d430e0d6df4db51d23bcbbbf9bc989bdb7e487e8312a104485ab6d16",
232
+ "spec/v2/core/interrupt.md": "1dc94f7c791dd5a547b1abafde6a8a76acd3d2993d288057f302f57911dc83bf",
233
233
  "spec/v2/core/node-pack-runtimes.md": "c435ad506f618831b6c0a9fd9150d83a5dc6caa1e3d755b949af2ff8949f1140",
234
234
  "spec/v2/core/oauth.md": "08e558a742259e75cfb2123f4c364d238dc4c57eea1a3c25d8f420ab4dff8244",
235
235
  "spec/v2/core/overview.md": "8f9d4a68b6185664e40d226cb82d9a9270a6c6630e3db59bddff300be781947d",
236
236
  "spec/v2/core/packs.md": "3107db14f68f36f5c0d8a13a6f0b3ca9ebef7ff890b8133894bfd28a63462d9b",
237
237
  "spec/v2/core/persistence.md": "d32736bd74e51df7c552565b4105a8ec801ece6d2292c8676421368ee9bffec1",
238
- "spec/v2/core/replay.md": "1166ea8dc73f825b3fd1ef083085eea72cc2a2663b5939a61d1c69d8466991b3",
238
+ "spec/v2/core/replay.md": "c0412290c805231f5b975acc0c094465c627cd600abec34efa034cbf0a4520cf",
239
239
  "spec/v2/core/runs.md": "c7dcc5116648f42cadecdcd4c949b194272908cbc4b1fbc7321956d5b3a713af",
240
240
  "spec/v2/core/security-defaults.md": "b54c055bea0a3c5417751a7423e390ef59b5f9acd6911f1b4b754e1a4578e7e2",
241
241
  "spec/v2/core/tool-catalog.md": "35f1a3fd509db0fc0490ddf400686fe94dc4b20c98064d5ebe776da516153e60",
@@ -246,7 +246,7 @@
246
246
  "spec/v2/corrections.schema.json": "4ac595b9a6d7f66d53de03fe0edbe0dc58de46426932387bfdc0ceb3cf949a3e",
247
247
  "spec/v2/declaration.json": "84bd247804e625b0a5d66f2011726626e8a9b21e55c479cc9ba239e6b5c358a6",
248
248
  "spec/v2/declaration.schema.json": "881c08b455aa12c41a0a0ec43a960ab78b75ff1302188f7eaaa43074ed4cff7a",
249
- "spec/v2/errors.json": "769e77369aa9bae7ba9c3728fc8ce04795d20ca4baa2a22056b70d0f5a2c4801",
249
+ "spec/v2/errors.json": "287a8bb487be96a2938b2101dd737ee87336440b3777a37dd44954726ed891b1",
250
250
  "spec/v2/event-codemap.json": "37869a03cdfd8758b57e6f350641a6ebbd383231cebda51f5473ad85f94ab74c",
251
251
  "spec/v2/event-codemap.schema.json": "b173f2a9bcc0bf9b62a474ce3b91e1431d597e64fd1560fc58662cd8606eaf9c",
252
252
  "spec/v2/ext/README.md": "ff91a503e0795e1b898e37d9e445add2d5975529723f7b32b2599b60a24c1ae3",
@@ -273,7 +273,7 @@
273
273
  "spec/v2/facets/auth.schema.json": "668f183fc15a6fb058c53635d3457db5a821f551a90141d08676a7a28917b0a9",
274
274
  "spec/v2/facets/envelopeStrictness.schema.json": "28be0a27f880bd45ec1222a16155f5e9167bbca57bf0322970fe1eb2654562aa",
275
275
  "spec/v2/facets/heartbeat.schema.json": "81d7fa1895a8b0241bd07cf00a7e7905bef0fd14eb3427325261c62eee5746a6",
276
- "spec/v2/facets/interrupt.schema.json": "3cc228b9bee48922b22bd6b0bee1a3244dc83d377a616cfb523435368da11ebb",
276
+ "spec/v2/facets/interrupt.schema.json": "2110ef32c3f880bf706475ef4cf8ddf97ff06871b8e6003a5edbcf4439f1fb70",
277
277
  "spec/v2/facets/mcp.schema.json": "7060657dfcff170f079beed160e3db7c1d3cc4410cd3cf64863baf6459f781cd",
278
278
  "spec/v2/facets/oauth.schema.json": "3a0cbe249d3bd5a5982349ae7ba2534776cc958e4a57e0fbeabf2bdab6e99e69",
279
279
  "spec/v2/facets/packs.schema.json": "d544a280419c0052ce0f3c3b378f7aa04085cadc31df52a3c8f43da600b131b1",
@@ -285,16 +285,16 @@
285
285
  "spec/v2/facets/webhooks.schema.json": "2fac635ee5fbc10a14a1a46cf512a11bac660bfbabe0aea79f6c7b657ecb9e4e",
286
286
  "spec/v2/facets/workflowChainPacks.schema.json": "ea625ce818fbeb746478f00f31f96ee9613d538e4409c258c1bfb517acdeaba9",
287
287
  "spec/v2/id-field-bindings.json": "9f6fd280250447e26a6704811c352bb8f2ff6c4bd242befe3da0ba38962370e9",
288
- "spec/v2/interop-map.json": "245bae77503ceba332436ff48054c4de63a414dc02ada4bc3f083740615c6986",
289
- "spec/v2/interop-map.schema.json": "4997f4b877495e6884c525e95b85bf0011aa1b498506a49510d8bb890594ca0d",
288
+ "spec/v2/interop-map.json": "a6bddce7f774f26ed178d2dd2e7268251acc9b1c9119e53dac116aa9c4fb2da0",
289
+ "spec/v2/interop-map.schema.json": "0017c882df34ee215e2e0729de051490e09d43aac3d2c949285698ed51832f12",
290
290
  "spec/v2/migrations.json": "651954938a1e154c0b6f1e3dd6e105889d30de3bb65789f21de7f77a12869e9e",
291
291
  "spec/v2/migrations.schema.json": "b91ade6320ad1b1185c9495b9424f3aa63438ed8e412c45171c227d82c0afd86",
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": "39087f64480a257ee2996f153c8530e6250480bfe7d8b9d12c17156b85faa32a",
295
+ "spec/v2/release.json": "9422440ed08e9d8ff3e8190c3e91e862025fa8b5b49d468a536806f5a841ac13",
296
296
  "spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624",
297
- "spec/v2/surface-baseline.json": "efdb4e5e0eb16689e20dea2be6f326971bb5707990c31896ca4cf24c6a5bbfed"
297
+ "spec/v2/surface-baseline.json": "13cfe5c31c3727a2e36980aa3e5a42f8ce1474b35beb99c5b71f487db006e71a"
298
298
  },
299
- "corpusCommit": "21f518e4e2af1d10280fc121789bc97837a3b009"
299
+ "corpusCommit": "0d370019eaaa841491be58d196e6d7a6934c71f0"
300
300
  }
package/api/openapi.yaml CHANGED
@@ -2056,6 +2056,16 @@ paths:
2056
2056
  name: webhookId
2057
2057
  required: true
2058
2058
  schema: { type: string, minLength: 1 }
2059
+ - in: query
2060
+ name: tenantId
2061
+ required: true
2062
+ schema: { type: string, minLength: 1 }
2063
+ description: >
2064
+ Required — the route is not path-nested under workspaces
2065
+ (`spec/v1/webhooks.md` §Unregister: "`tenantId` query parameter is
2066
+ required"). The prose has stated this since v1; the contract did
2067
+ not declare it until 2.37.1, so a client generated from this file
2068
+ omitted it.
2059
2069
  responses:
2060
2070
  '204':
2061
2071
  description: Unregistered.
@@ -2063,6 +2073,80 @@ paths:
2063
2073
  '403': { $ref: '#/components/responses/Forbidden' }
2064
2074
  '404': { $ref: '#/components/responses/NotFound' }
2065
2075
 
2076
+ # RFC 0201 §E.18 — zero-downtime secret rotation. Gated on
2077
+ # `capabilities.webhooks.secretRotation`; the v2 twin is
2078
+ # `rotateWebhookSecret` in api/v2/openapi.yaml.
2079
+ /v1/webhooks/{webhookId}/rotate-secret:
2080
+ post:
2081
+ tags: [webhooks]
2082
+ summary: Rotate a Standard Webhooks subscription's secret with an overlap.
2083
+ description: >
2084
+ Per `spec/v1/webhooks.md` §"Standard Webhooks companion scheme" →
2085
+ **Rotation** (RFC 0201 §E.18). Gated on
2086
+ `capabilities.webhooks.secretRotation: { overlapSeconds }`: a host that does
2087
+ not advertise the facet MUST answer `404`. Only for a subscription whose
2088
+ registration listed `standard-webhooks-1`; any other subscription gets
2089
+ `400 validation_error`, because its single `v1` signature cannot overlap.
2090
+ `403` when the caller is not a member of the tenant; `404` when the
2091
+ subscription is unknown. Until `previousSecretExpiresAt`,
2092
+ `webhook-signature` MUST carry one entry under each secret and
2093
+ `X-openwop-Signature` stays on the previous secret; afterwards only the new
2094
+ secret signs. A second rotation inside an overlap retires the oldest secret
2095
+ immediately. Rotation does not re-verify the endpoint, and the response
2096
+ carries no secret.
2097
+ operationId: rotateWebhookSecret
2098
+ security:
2099
+ - ApiKeyAuth: ['webhooks:manage']
2100
+ - OAuth2: ['webhooks:manage']
2101
+ - OpenIdConnect: ['webhooks:manage']
2102
+ parameters:
2103
+ - in: path
2104
+ name: webhookId
2105
+ required: true
2106
+ schema: { type: string, minLength: 1 }
2107
+ - in: query
2108
+ name: tenantId
2109
+ required: true
2110
+ schema: { type: string, minLength: 1 }
2111
+ description: >
2112
+ Required, exactly as on `unregisterWebhook` — the route is not
2113
+ path-nested under workspaces (`spec/v1/webhooks.md` §Unregister).
2114
+ - $ref: '#/components/parameters/IdempotencyKey'
2115
+ requestBody:
2116
+ required: true
2117
+ content:
2118
+ application/json:
2119
+ schema:
2120
+ type: object
2121
+ required: [secret]
2122
+ additionalProperties: false
2123
+ properties:
2124
+ secret:
2125
+ type: string
2126
+ pattern: '^whsec_[A-Za-z0-9+/]+={0,2}$'
2127
+ description: >
2128
+ The new secret, `whsec_<base64>` decoding to 24–64 bytes
2129
+ (RFC 0201 §B.6). Never echoed in any response.
2130
+ responses:
2131
+ '200':
2132
+ description: Rotated. No secret is returned.
2133
+ content:
2134
+ application/json:
2135
+ schema:
2136
+ type: object
2137
+ required: [rotatedAt, previousSecretExpiresAt]
2138
+ additionalProperties: false
2139
+ properties:
2140
+ rotatedAt: { type: string, format: date-time }
2141
+ previousSecretExpiresAt:
2142
+ type: string
2143
+ format: date-time
2144
+ description: '`rotatedAt + overlapSeconds`.'
2145
+ '400': { $ref: '#/components/responses/ValidationError' }
2146
+ '401': { $ref: '#/components/responses/Unauthenticated' }
2147
+ '403': { $ref: '#/components/responses/Forbidden' }
2148
+ '404': { $ref: '#/components/responses/NotFound' }
2149
+
2066
2150
  # ── Audit-log integrity verification (gated on profile) ────────────────
2067
2151
  /v1/audit/verify:
2068
2152
  get:
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.36.0
4
+ version: 2.37.1
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
@@ -1,7 +1,7 @@
1
1
  asyncapi: 3.1.0
2
2
  info:
3
3
  title: OpenWOP v2 event streams
4
- version: 2.36.0
4
+ version: 2.37.1
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
@@ -63,16 +63,11 @@ operations:
63
63
  components:
64
64
  securitySchemes:
65
65
  ApiKeyAuth:
66
- type: httpApiKey
67
- in: header
68
- name: Authorization
69
- description: 'Bearer-style API key. Format implementation-defined; reference
70
-
71
- impl uses `hk_`/`hk_test_` prefixes. Required scopes:
72
-
73
- `runs:read` to subscribe. See `auth.md`.
74
-
75
- '
66
+ type: http
67
+ scheme: bearer
68
+ bearerFormat: API key
69
+ description: 'Bearer API key, the same scheme as api/v2/openapi.yaml `ApiKeyAuth` (RFC 0200 §F). Required scope: `runs:read`
70
+ to subscribe.'
76
71
  parameters:
77
72
  RunId:
78
73
  description: The run to subscribe to. Format opaque; clients MUST treat as a string.
@@ -95,7 +90,7 @@ components:
95
90
  EvalCompleted:
96
91
  name: eval.completed
97
92
  title: Eval run completed (RFC 0081)
98
- summary: An eval run finished. Content-free aggregate scalars; the full scorecard is the run output, read via GET /v1/runs/{runId}/eval-summary.
93
+ summary: An eval run finished. Content-free aggregate scalars; the full scorecard is the run output, read via GET /runs/{runId}/eval-summary.
99
94
  contentType: application/json
100
95
  payload:
101
96
  $ref: '#/components/schemas/EvalCompletedPayload'
@@ -115,14 +110,14 @@ components:
115
110
  payload:
116
111
  $ref: '#/components/schemas/DeploymentRolledBackPayload'
117
112
  DeploymentCanaryAdjusted:
118
- name: deployment.canary.adjusted
113
+ name: deployment.canary-adjusted
119
114
  title: Deployment canary adjusted (RFC 0082)
120
115
  summary: An active version's canary traffic share changed. Content-free; recorded-fact; audit-logged.
121
116
  contentType: application/json
122
117
  payload:
123
118
  $ref: '#/components/schemas/DeploymentCanaryAdjustedPayload'
124
119
  DeploymentStateChanged:
125
- name: deployment.state.changed
120
+ name: deployment.state-changed
126
121
  title: Deployment state changed (RFC 0082)
127
122
  summary: A non-promotion lifecycle transition (pause / resume / deprecate). Content-free; recorded-fact; audit-logged.
128
123
  contentType: application/json
@@ -233,7 +228,7 @@ components:
233
228
  payload:
234
229
  $ref: '#/components/schemas/RunEventDoc'
235
230
  CompensationManualInterventionRequired:
236
- name: compensation.manual_intervention_required
231
+ name: compensation.manual-intervention-required
237
232
  title: Compensation needs an operator (RFC 0151 §C/§E)
238
233
  summary: The host cannot proceed unaided. Recorded explicitly because an unwind that stops without saying so is indistinguishable
239
234
  from one that finished.
@@ -316,7 +311,7 @@ components:
316
311
  payload:
317
312
  $ref: '#/components/schemas/RunEventDoc'
318
313
  DispatchFanOut:
319
- name: core.dispatch.fanOut
314
+ name: dispatch.fanned-out
320
315
  title: core.dispatch began a parallel fan-out wave (RFC 0118)
321
316
  summary: Emitted when a fanOutPolicy=parallel wave begins; payload $defs.dispatchFanOut carries childCount/maxConcurrency/joinMode.
322
317
  Parallel path only.
@@ -324,7 +319,7 @@ components:
324
319
  payload:
325
320
  $ref: '#/components/schemas/RunEventDoc'
326
321
  DispatchJoin:
327
- name: core.dispatch.join
322
+ name: dispatch.joined
328
323
  title: core.dispatch parallel join satisfied (RFC 0118)
329
324
  summary: Emitted when a fanOutPolicy=parallel join is satisfied/failed; payload $defs.dispatchJoin carries joinOutcome
330
325
  + the replay-deterministic mergeOrder.
@@ -439,7 +434,7 @@ components:
439
434
  payload:
440
435
  $ref: '#/components/schemas/RunEventDoc'
441
436
  VoiceSpeechStart:
442
- name: voice.speech_start
437
+ name: voice.speech-start
443
438
  title: Inbound user speech onset detected
444
439
  contentType: application/json
445
440
  payload:
@@ -451,25 +446,25 @@ components:
451
446
  payload:
452
447
  $ref: '#/components/schemas/RunEventDoc'
453
448
  VoiceEndpointCandidate:
454
- name: voice.endpoint_candidate
449
+ name: voice.endpoint-candidate
455
450
  title: Likely end-of-turn boundary (semantic turn detection)
456
451
  contentType: application/json
457
452
  payload:
458
453
  $ref: '#/components/schemas/RunEventDoc'
459
454
  VoiceTurnCommit:
460
- name: voice.turn_commit
455
+ name: voice.turn-commit
461
456
  title: User yielded the floor (callTranscriber Promise resolves here)
462
457
  contentType: application/json
463
458
  payload:
464
459
  $ref: '#/components/schemas/RunEventDoc'
465
460
  VoiceSynthesisChunk:
466
- name: voice.synthesis_chunk
461
+ name: voice.synthesis-chunk
467
462
  title: Clause-boundary streaming-synthesis chunk (metadata only)
468
463
  contentType: application/json
469
464
  payload:
470
465
  $ref: '#/components/schemas/RunEventDoc'
471
466
  VoiceBargeIn:
472
- name: voice.barge_in
467
+ name: voice.barge-in
473
468
  title: User speech overlapped active assistant playback
474
469
  contentType: application/json
475
470
  payload:
@@ -1,7 +1,7 @@
1
1
  openapi: 3.1.0
2
2
  info:
3
3
  title: OpenWOP v2 API
4
- version: 2.36.0
4
+ version: 2.37.1
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
@@ -85,8 +85,9 @@ paths:
85
85
  ETag:
86
86
  schema:
87
87
  type: string
88
- description: Optional probe handle for mid-session capability change detection. Deprecated toward v2 (RFC 0165
89
- §C.2).
88
+ description: 'capabilities.md §1 (RFC 0165 §C.2). Standard strong validator for the discovery document: a v2
89
+ host MUST send it and MUST honor `If-None-Match` with `304`; a host that changes semantics without changing
90
+ the bytes is non-conformant.'
90
91
  OpenWOP-Version:
91
92
  $ref: '#/components/headers/OpenWOPVersion'
92
93
  content:
@@ -2749,6 +2750,81 @@ paths:
2749
2750
  $ref: '#/components/responses/Forbidden'
2750
2751
  '404':
2751
2752
  $ref: '#/components/responses/NotFound'
2753
+ /webhooks/{webhookId}/rotate-secret:
2754
+ post:
2755
+ tags:
2756
+ - webhooks
2757
+ operationId: rotateWebhookSecret
2758
+ summary: Rotate a Standard Webhooks subscription's secret with an overlap (RFC 0201 §E)
2759
+ description: 'Gated on `webhooks.secretRotation`: a host that does not advertise it MUST answer `404 not_found`. Only
2760
+ for a subscription that opted into `standard-webhooks-1`; any other subscription gets `400 validation_error` (its
2761
+ single `v1` signature cannot overlap). Tenant checks are exactly those of `unregisterWebhook`: `403 id_tenant_mismatch`
2762
+ when the id''s tenant segment is not the caller''s, checked before the lookup (`identity.md` §5); `404` when unknown.
2763
+ For `overlapSeconds` after `rotatedAt`, `webhook-signature` carries one entry under the new secret and one under the
2764
+ previous one, and `OpenWOP-Signature` stays on the previous secret; at `previousSecretExpiresAt` only the new secret
2765
+ signs. A second rotation inside an overlap retires the oldest secret immediately. Rotation does not re-verify the
2766
+ endpoint. The response carries no secret.'
2767
+ parameters:
2768
+ - name: webhookId
2769
+ in: path
2770
+ required: true
2771
+ schema:
2772
+ $ref: ../../schemas/v2/ids.schema.json#/$defs/subscriptionId
2773
+ description: 'Tenant-bound `<tenantId>/<opaque>` (`identity.md` §5, RFC 0187 §A.1), one path segment: `~`-projected
2774
+ (RFC 0184) or percent-encoded.'
2775
+ - $ref: '#/components/parameters/IdempotencyKey'
2776
+ - $ref: '#/components/parameters/OpenWOPVersion'
2777
+ requestBody:
2778
+ required: true
2779
+ content:
2780
+ application/json:
2781
+ schema:
2782
+ type: object
2783
+ required:
2784
+ - secret
2785
+ additionalProperties: false
2786
+ properties:
2787
+ secret:
2788
+ type: string
2789
+ pattern: ^whsec_[A-Za-z0-9+/]+={0,2}$
2790
+ description: The new secret, `whsec_<base64>` decoding to 24–64 bytes (RFC 0201 §B.6).
2791
+ responses:
2792
+ '200':
2793
+ description: Rotated. No secret is returned.
2794
+ content:
2795
+ application/json:
2796
+ schema:
2797
+ type: object
2798
+ required:
2799
+ - rotatedAt
2800
+ - previousSecretExpiresAt
2801
+ additionalProperties: false
2802
+ properties:
2803
+ rotatedAt:
2804
+ type: string
2805
+ format: date-time
2806
+ previousSecretExpiresAt:
2807
+ type: string
2808
+ format: date-time
2809
+ description: '`rotatedAt + overlapSeconds`.'
2810
+ headers:
2811
+ OpenWOP-Version:
2812
+ $ref: '#/components/headers/OpenWOPVersion'
2813
+ '400':
2814
+ $ref: '#/components/responses/ValidationError'
2815
+ '401':
2816
+ $ref: '#/components/responses/Unauthenticated'
2817
+ '403':
2818
+ $ref: '#/components/responses/Forbidden'
2819
+ '404':
2820
+ $ref: '#/components/responses/NotFound'
2821
+ security:
2822
+ - ApiKeyAuth:
2823
+ - webhooks:manage
2824
+ - OAuth2:
2825
+ - webhooks:manage
2826
+ - OpenIdConnect:
2827
+ - webhooks:manage
2752
2828
  /audit/verify:
2753
2829
  get:
2754
2830
  tags:
@@ -3509,81 +3585,6 @@ paths:
3509
3585
  - runs:read
3510
3586
  - OpenIdConnect:
3511
3587
  - runs:read
3512
- /webhooks/{webhookId}/rotate-secret:
3513
- post:
3514
- tags:
3515
- - webhooks
3516
- operationId: rotateWebhookSecret
3517
- summary: Rotate a Standard Webhooks subscription's secret with an overlap (RFC 0201 §E)
3518
- description: 'Gated on `webhooks.secretRotation`: a host that does not advertise it MUST answer `404 not_found`. Only
3519
- for a subscription that opted into `standard-webhooks-1`; any other subscription gets `400 validation_error` (its
3520
- single `v1` signature cannot overlap). Tenant checks are exactly those of `unregisterWebhook`: `403 id_tenant_mismatch`
3521
- when the id''s tenant segment is not the caller''s, checked before the lookup (`identity.md` §5); `404` when unknown.
3522
- For `overlapSeconds` after `rotatedAt`, `webhook-signature` carries one entry under the new secret and one under the
3523
- previous one, and `OpenWOP-Signature` stays on the previous secret; at `previousSecretExpiresAt` only the new secret
3524
- signs. A second rotation inside an overlap retires the oldest secret immediately. Rotation does not re-verify the
3525
- endpoint. The response carries no secret.'
3526
- parameters:
3527
- - name: webhookId
3528
- in: path
3529
- required: true
3530
- schema:
3531
- $ref: ../../schemas/v2/ids.schema.json#/$defs/subscriptionId
3532
- description: 'Tenant-bound `<tenantId>/<opaque>` (`identity.md` §5, RFC 0187 §A.1), one path segment: `~`-projected
3533
- (RFC 0184) or percent-encoded.'
3534
- - $ref: '#/components/parameters/IdempotencyKey'
3535
- - $ref: '#/components/parameters/OpenWOPVersion'
3536
- requestBody:
3537
- required: true
3538
- content:
3539
- application/json:
3540
- schema:
3541
- type: object
3542
- required:
3543
- - secret
3544
- additionalProperties: false
3545
- properties:
3546
- secret:
3547
- type: string
3548
- pattern: ^whsec_[A-Za-z0-9+/]+={0,2}$
3549
- description: The new secret, `whsec_<base64>` decoding to 24–64 bytes (RFC 0201 §B.6).
3550
- responses:
3551
- '200':
3552
- description: Rotated. No secret is returned.
3553
- content:
3554
- application/json:
3555
- schema:
3556
- type: object
3557
- required:
3558
- - rotatedAt
3559
- - previousSecretExpiresAt
3560
- additionalProperties: false
3561
- properties:
3562
- rotatedAt:
3563
- type: string
3564
- format: date-time
3565
- previousSecretExpiresAt:
3566
- type: string
3567
- format: date-time
3568
- description: '`rotatedAt + overlapSeconds`.'
3569
- headers:
3570
- OpenWOP-Version:
3571
- $ref: '#/components/headers/OpenWOPVersion'
3572
- '400':
3573
- $ref: '#/components/responses/ValidationError'
3574
- '401':
3575
- $ref: '#/components/responses/Unauthenticated'
3576
- '403':
3577
- $ref: '#/components/responses/Forbidden'
3578
- '404':
3579
- $ref: '#/components/responses/NotFound'
3580
- security:
3581
- - ApiKeyAuth:
3582
- - webhooks:manage
3583
- - OAuth2:
3584
- - webhooks:manage
3585
- - OpenIdConnect:
3586
- - webhooks:manage
3587
3588
  components:
3588
3589
  securitySchemes:
3589
3590
  ApiKeyAuth:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/spec-artifacts",
3
- "version": "2.36.1",
3
+ "version": "2.37.1",
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": {
@@ -8130,7 +8130,7 @@
8130
8130
  },
8131
8131
  "callbackDelivery": {
8132
8132
  "type": "boolean",
8133
- "description": "RFC 0196 §A.1 — true only if the host delivers to createRun.callbackUrl (interrupt.md §Callback delivery). A host advertising it MUST hold callbackUrl to the webhooks.md §SSRF guard at create time and at delivery."
8133
+ "description": "RFC 0196 §A.1 — true only if the host delivers to createRun.callbackUrl (interrupt.md §Callback delivery). A host advertising it MUST hold callbackUrl to the webhooks.md §Egress guard at create time and at delivery."
8134
8134
  }
8135
8135
  },
8136
8136
  "allOf": [
@@ -259,7 +259,7 @@
259
259
  "properties": {
260
260
  "id": {
261
261
  "type": "string",
262
- "pattern": "^openwop\\.(?!it\\.(?:a2a-parts-schemas|a2ui-v09-profile|artifact-schema-compile-bounded|artifact-type-legacy-ids|bundle-maturity-gate|capability-example-root-layout|certification-floor-enforcement|chain-subchain-unsupported-refused|compensation-profile|core-manifest-and-extension-registry|discovery-canonical-family-no-shadow|edge-condition-truthy-falsy|effect-identity-composition|effect-identity-cross-scope|error-envelope-canonical-shape|event-codemap-complete|form-content-packs|locale-key-grammar|multi-region-effect-vocabulary|node-pack-mcp-server-record|normative-example-extraction|openapi-asyncapi-sdk-parity|openapi-security-declared|pack-manifest-extensions|protocol-version-grammar|registry-declarative-kinds|removal-dates-minor|rfc-0147-self-audit|rfc-lifecycle-coherence|semantic-digest-v2|spec-corpus-validity|spec-section-citations|tool-descriptor-annotations|tool-result-trust-monotone|v2-alias-covers-reword|v2-bundle-signature-attributable|v2-corpus-tag-pinned|v2-declaration-parity|v2-envelope-catalog|v2-eos-clock|v2-error-registry-prose-parity|v2-event-naming-rule|v2-events-channel-parity|v2-externally-gated-never-stable|v2-facet-advertisement|v2-front-door-counts|v2-id-kinds-bound|v2-interop-map-coherent|v2-lane-exp-only-schema|v2-normative-home-gate|v2-one-signing-scheme|v2-path-parity|v2-release-identity|v2-req-only-assertions|v2-retention-floors|v2-retirement-gate|v2-seams-not-in-canonical|v2-security-defaults-table|v2-spec-artifacts-digest|v2-surface-monotone-gate|v2-threat-model-template|v2-umbrella-alias-covered|v2-umbrella-child-parts|v2-umbrella-codemod-at-active|v2-umbrella-codemod-legs|v2-umbrella-migration-deprecation|v2-witness-required|versioned-composition-profiles|workflow-chain-internal-flag|workload-identity-profile)\\.)(requirement|it|scenario|floor|profile|family)\\.[a-z0-9]",
262
+ "pattern": "^openwop\\.(?!it\\.(?:a2a-parts-schemas|a2ui-v09-profile|artifact-schema-compile-bounded|artifact-type-legacy-ids|bundle-maturity-gate|capability-example-root-layout|certification-floor-enforcement|chain-subchain-unsupported-refused|compensation-profile|core-manifest-and-extension-registry|discovery-canonical-family-no-shadow|edge-condition-truthy-falsy|effect-identity-composition|effect-identity-cross-scope|error-envelope-canonical-shape|event-codemap-complete|form-content-packs|locale-key-grammar|multi-region-effect-vocabulary|node-pack-mcp-server-record|normative-example-extraction|openapi-asyncapi-sdk-parity|openapi-security-declared|pack-manifest-extensions|protocol-version-grammar|registry-declarative-kinds|removal-dates-minor|rfc-0147-self-audit|rfc-lifecycle-coherence|semantic-digest-v2|spec-corpus-validity|spec-section-citations|tool-descriptor-annotations|tool-result-trust-monotone|v1-webhook-rotation-contract|v1-webhook-unregister-contract|v2-alias-covers-reword|v2-bundle-signature-attributable|v2-corpus-tag-pinned|v2-declaration-parity|v2-envelope-catalog|v2-eos-clock|v2-error-registry-prose-parity|v2-event-naming-rule|v2-events-channel-parity|v2-externally-gated-never-stable|v2-facet-advertisement|v2-front-door-counts|v2-id-kinds-bound|v2-interop-map-coherent|v2-lane-exp-only-schema|v2-normative-home-gate|v2-one-signing-scheme|v2-path-parity|v2-release-identity|v2-req-only-assertions|v2-retention-floors|v2-retirement-gate|v2-seams-not-in-canonical|v2-security-defaults-table|v2-spec-artifacts-digest|v2-surface-monotone-gate|v2-threat-model-template|v2-umbrella-alias-covered|v2-umbrella-child-parts|v2-umbrella-codemod-at-active|v2-umbrella-codemod-legs|v2-umbrella-migration-deprecation|v2-witness-required|versioned-composition-profiles|workflow-chain-internal-flag|workload-identity-profile)\\.)(requirement|it|scenario|floor|profile|family)\\.[a-z0-9]",
263
263
  "description": "RFC 0168 §A.1 — `openwop.<area>.<slug>`. The suite mints six areas: `requirement` (an explicit `req()` id), `it` (a per-test id from the scenario file and title), `scenario` and `floor` (per-file rows), `profile` (a claimed-profile row) and `family` (a capability gate). The lookahead refuses every id minted from a corpus-coherence file: those run in the spec repo’s CI and MUST NOT appear in a host bundle (conformance.md §\"Two products, two ledgers\"; RFC 0168 §D.1). `scripts/check-req-only.mjs` fails when the list drifts from `conformance/src/coherence/`."
264
264
  },
265
265
  "scenario": {
@@ -4,7 +4,7 @@
4
4
  "provenance": {
5
5
  "suite": {
6
6
  "package": "@openwop/openwop-conformance",
7
- "version": "2.36.1"
7
+ "version": "2.37.1"
8
8
  },
9
9
  "note": "Derived from the corpus at generation time. Regenerate with --write; verify with --check."
10
10
  },
@@ -86,6 +86,7 @@
86
86
  "resolveInterruptByRun",
87
87
  "resolveInterruptByToken",
88
88
  "resumeRun",
89
+ "rotateWebhookSecret",
89
90
  "streamRunEvents",
90
91
  "transitionAgentDeployment",
91
92
  "unregisterWebhook",
@@ -419,5 +420,5 @@
419
420
  "$id": "https://openwop.dev/spec/v1/workspace-file.schema.json"
420
421
  }
421
422
  ],
422
- "digest": "aea6b0b258dfdd9c3e064a6c1bba4e4982ace58a082035ee5c3ea23290e19e25"
423
+ "digest": "b8e59ac3685c06d6a7599f0b1e03b52d9aff3d9fb86c8b08b0188fce8026d1c0"
423
424
  }
package/spec/v1/gaps.json CHANGED
@@ -10059,6 +10059,40 @@
10059
10059
  }
10060
10060
  ]
10061
10061
  },
10062
+ {
10063
+ "id": "openwop.gap.0175.5",
10064
+ "rfc": "0175",
10065
+ "local": "G5",
10066
+ "section": "§D.1",
10067
+ "surface": "The committed reference-host bundle records `openwop.requirement.0175.negotiation-authenticated.mcp` `executed-pass` having measured the harness: the leg's own single-era MCP server is unreachable behind the shared public front, and §23 has no lever that makes a server offer only a lower revision. The bundle is owed a re-cut on the corrected suite.",
10068
+ "witness": "seam-gated",
10069
+ "requirementId": null,
10070
+ "disposition": "externally-gated",
10071
+ "target": "v2-reference-recut",
10072
+ "sources": [
10073
+ {
10074
+ "file": "RFCS/registers/0175-v2-transports-and-embedded-protocols.gaps.md",
10075
+ "token": "G5"
10076
+ }
10077
+ ]
10078
+ },
10079
+ {
10080
+ "id": "openwop.gap.0175.6",
10081
+ "rfc": "0175",
10082
+ "local": "G6",
10083
+ "section": "§D.1",
10084
+ "surface": "The unauthenticated clause is reachable only when a negotiated version can land below `preferredVersion` and at or above the floor; the only host that witnesses these ids advertises `preferredVersion` equal to its floor on both protocols, so no committed bundle has ever exercised it.",
10085
+ "witness": "seam-gated",
10086
+ "requirementId": null,
10087
+ "disposition": "externally-gated",
10088
+ "target": "host-floor-below-preferred",
10089
+ "sources": [
10090
+ {
10091
+ "file": "RFCS/registers/0175-v2-transports-and-embedded-protocols.gaps.md",
10092
+ "token": "G6"
10093
+ }
10094
+ ]
10095
+ },
10062
10096
  {
10063
10097
  "id": "openwop.gap.0176.1",
10064
10098
  "rfc": "0176",
@@ -13361,7 +13395,7 @@
13361
13395
  "requirementId": null,
13362
13396
  "disposition": "open",
13363
13397
  "target": null,
13364
- "note": "`workspace-cross-tenant-isolation` (WCT-1) + WSR-1 + CRUD/ETag/snapshot conformance land at the behavior milestone, not at this DRAFT. Shape-only conformance (`workspace-capability-shape.test.ts`) ships now.",
13398
+ "note": "WCT-1's half has landed: `workspace-cross-tenant-isolation` is registered in `SECURITY/invariants.yaml` (protocol tier, critical) and two scenarios back it — the seam one and the black-box one that drives the normative §C endpoints with two operator credentials. Still open: WSR-1, which has neither an `invariants.yaml` entry nor a scenario of its own, plus CRUD/ETag/snapshot conformance; both land at the behavior milestone, not at this DRAFT. Shape-only conformance (`workspace-capability-shape.test.ts`) ships now.",
13365
13399
  "sources": [
13366
13400
  {
13367
13401
  "file": "spec/v1/spec-gaps.json",
@@ -14431,12 +14465,11 @@
14431
14465
  "rfc": "0117",
14432
14466
  "local": "r5",
14433
14467
  "section": "spec/v1/frontend-plugin-packs.md",
14434
- "surface": "`Active → Accepted`",
14468
+ "surface": "**Closed (2026-09-23).** RFC 0117 reached `Accepted` on 2026-07-06 (steward single-witness waiver + the tier-1 reference host openwop-app advertising `uiPlugins {supported:true, isolation:\"cross-origin-iframe\"}` live), and openwop#1479 flipped `spec/v1/frontend-plugin-packs.md` to `Status: Stable` on 2026-09-22 because that predicate had fired. Original: `Active → Accepted`.",
14435
14469
  "witness": "witnessable-gated",
14436
14470
  "requirementId": null,
14437
- "disposition": "open",
14471
+ "disposition": "closed",
14438
14472
  "target": null,
14439
- "note": "Pending a host advertising `capabilities.uiPlugins.supported` + passing the gated scenarios non-vacuously (dual-witness per the graduation rule).",
14440
14473
  "sources": [
14441
14474
  {
14442
14475
  "file": "spec/v1/spec-gaps.json",
@@ -443,6 +443,14 @@
443
443
  "tag": "webhooks",
444
444
  "class": "canonical"
445
445
  },
446
+ {
447
+ "operationId": "rotateWebhookSecret",
448
+ "method": "POST",
449
+ "pathKey": "/v1/webhooks/{webhookId}/rotate-secret",
450
+ "resolvedPath": "/v1/webhooks/{webhookId}/rotate-secret",
451
+ "tag": "webhooks",
452
+ "class": "canonical"
453
+ },
446
454
  {
447
455
  "operationId": "getWorkflow",
448
456
  "method": "GET",
@@ -453,10 +461,10 @@
453
461
  }
454
462
  ],
455
463
  "counts": {
456
- "total": 56,
457
- "canonical": 47,
464
+ "total": 57,
465
+ "canonical": 48,
458
466
  "hostExtension": 5,
459
467
  "testCatalog": 4
460
468
  },
461
- "digest": "ef8c07388acc57ac48c30a7600d5197039cbf32cbf8117a286664dd2388b4a60"
469
+ "digest": "056436682c867c985d8b04c42df64d675718c46dab943a290bb275d2b07ea388"
462
470
  }
@@ -4,8 +4,8 @@
4
4
  "tables": 43,
5
5
  "rows": 196,
6
6
  "byDisposition": {
7
- "open": 107,
8
- "closed": 69,
7
+ "open": 106,
8
+ "closed": 70,
9
9
  "carried": 20
10
10
  },
11
11
  "byWitness": {
@@ -981,7 +981,7 @@
981
981
  "local": "r6",
982
982
  "section": null,
983
983
  "surface": "**SECURITY invariant + conformance behavior tests**",
984
- "resolutionPath": "`workspace-cross-tenant-isolation` (WCT-1) + WSR-1 + CRUD/ETag/snapshot conformance land at the behavior milestone, not at this DRAFT. Shape-only conformance (`workspace-capability-shape.test.ts`) ships now.",
984
+ "resolutionPath": "WCT-1's half has landed: `workspace-cross-tenant-isolation` is registered in `SECURITY/invariants.yaml` (protocol tier, critical) and two scenarios back it — the seam one and the black-box one that drives the normative §C endpoints with two operator credentials. Still open: WSR-1, which has neither an `invariants.yaml` entry nor a scenario of its own, plus CRUD/ETag/snapshot conformance; both land at the behavior milestone, not at this DRAFT. Shape-only conformance (`workspace-capability-shape.test.ts`) ships now.",
985
985
  "owningRfc": "0059",
986
986
  "disposition": "open",
987
987
  "dispositionEvidence": null,
@@ -2180,11 +2180,11 @@
2180
2180
  "doc": "spec/v1/frontend-plugin-packs.md",
2181
2181
  "local": "r5",
2182
2182
  "section": null,
2183
- "surface": "`Active → Accepted`",
2184
- "resolutionPath": "Pending a host advertising `capabilities.uiPlugins.supported` + passing the gated scenarios non-vacuously (dual-witness per the graduation rule).",
2183
+ "surface": "**Closed (2026-09-23).** RFC 0117 reached `Accepted` on 2026-07-06 (steward single-witness waiver + the tier-1 reference host openwop-app advertising `uiPlugins {supported:true, isolation:\"cross-origin-iframe\"}` live), and openwop#1479 flipped `spec/v1/frontend-plugin-packs.md` to `Status: Stable` on 2026-09-22 because that predicate had fired. Original: `Active → Accepted`.",
2184
+ "resolutionPath": null,
2185
2185
  "owningRfc": "0117",
2186
- "disposition": "open",
2187
- "dispositionEvidence": null,
2186
+ "disposition": "closed",
2187
+ "dispositionEvidence": "RFC 0117 `Status` is `Accepted` (RFCS/0117-frontend-plugin-packs.md, Updated 2026-07-06); the spec banner is `Stable` (openwop#1479)",
2188
2188
  "target": null,
2189
2189
  "witness": "witnessable-gated",
2190
2190
  "rowKind": "table-row",
@@ -42,7 +42,7 @@ A MUST whose only witness is `seam-gated` MUST either mint a normative observati
42
42
 
43
43
  ## The seams profile
44
44
 
45
- Test seams are the profile `openwop-conformance-seams-v2` (`spec/v2/profiles.json`), described by `api/seams-v2.yaml` with schemas under `schemas/v2/seams/`, in the path space `/conformance/seams/…`. The seam schemas `$ref` the canonical error and event schemas with no tolerance path. A host that mounts the seams MUST advertise the profile in `profiles[]`; a `testSeams` capability flag does not exist and MUST NOT be advertised. `api/v2/openapi.yaml` and `spec/v2/path-manifest.json` MUST contain no seam or sample-host operation; an SDK generated from the canonical document has no seam method. The profile is versioned with the suite (`seams-v2` for 2.x).
45
+ Test seams are the profile `openwop-conformance-seams-v2` (`spec/v2/profiles.json`), described by `api/seams-v2.yaml` with schemas under `schemas/v2/seams/`, in the path space `/conformance/seams/…`. The seam schemas `$ref` the canonical error and event schemas with no tolerance path. A host that mounts the seams MUST advertise the profile as `conformance.seamsProfile: "openwop-conformance-seams-v2"` at the discovery root (RFC 0168 §C.1, erratum 2026-09-05; the closed root has no `profiles[]`, capabilities.md §3); a `testSeams` capability flag does not exist and MUST NOT be advertised. `api/v2/openapi.yaml` and `spec/v2/path-manifest.json` MUST contain no seam or sample-host operation; an SDK generated from the canonical document has no seam method. The profile is versioned with the suite (`seams-v2` for 2.x).
46
46
 
47
47
  ## Two products, two ledgers
48
48
 
@@ -24,7 +24,7 @@ Retry timing lives in the `Retry-After` header only. `details.retryAfter`, `deta
24
24
 
25
25
  ## One code per state
26
26
 
27
- An interrupt has one code per state: a token or run-scoped resolve against an interrupt that is already resolved, or whose run is cancelled or completed, MUST return `409 interrupt_already_resolved`; a signed token past its `expiresAt` MUST return `410 interrupt_expired`; a token whose `alg` or `kid` the host does not accept MUST return `401 interrupt_token_invalid` (see interrupt.md, identity.md). The idempotency mismatch code is `idempotency_key_mismatch` only (idempotency.md).
27
+ An interrupt has one code per state: a token or run-scoped resolve against an interrupt that is already resolved, or whose run is cancelled or completed, MUST return `409 interrupt_already_resolved`; a signed token past its `expiresAt` MUST return `410 interrupt_expired`; a token whose `alg` or `kid` the host does not accept MUST return `401 interrupt_token_invalid` (see interrupt.md, identity.md). The idempotency mismatch code is `idempotency_key_mismatch` only (idempotency.md). `interrupt_cancelled` is registered and names no state of the core resolve surfaces; a host MUST NOT emit it from `resolveInterruptByRun`, `inspectInterruptByToken` or `resolveInterruptByToken` (RFC 0213 §C).
28
28
 
29
29
  ## Codes by HTTP status
30
30
 
@@ -84,7 +84,7 @@ Vendor events appear in `debug` only. In a mixed mode the host emits the union o
84
84
 
85
85
  Each frame carries `id:`, `event:` and `data:`: `id:` is the `sequence`, `event:` is the v2 `type` (single mode), `data:` is the `RunEventDoc`. Three frame names are not types and are absent from the `type` enum: `state.snapshot` (`values`; `data:` a `RunSnapshot`), `batch` (`bufferMs`; `data:` an array of `RunEventDoc`) and `ai.message.chunk` (`messages`; `data:` the `outputChunk` payload, persisted type `output.chunk`). A host MUST set `Content-Type: text/event-stream`, MUST emit a keep-alive comment at least every 30 seconds, and MUST close the connection after the run's terminal event (`run.completed`, `run.failed`, `run.cancelled`).
86
86
 
87
- `Last-Event-ID` resumes every mode: the host MUST look up the event with that sequence, MUST begin at the next sequence, and MUST NOT re-emit the resumption point. In `values` mode resumption MUST emit a `state.snapshot` first. With `bufferMs` (0..5000) the host accumulates events into one `event: batch` frame whose `data:` is an array of `RunEventDoc`; it MUST flush on a terminal event, on `node.suspended`, and on close; the batch's `id:` SHOULD be its highest `sequence` and `Last-Event-ID` MUST honor that id. A consumer MUST tolerate both a one-element batch and an unbatched frame. A host MUST NOT limit subscribers per run except for resource protection, and then MUST answer `429 rate_limited` with `Retry-After` rather than drop silently.
87
+ `Last-Event-ID: N` resumes every mode as an exclusive cursor with the semantics of poll's `afterSequence`: the host MUST stream the events with `sequence > N` in log order and MUST NOT re-emit `N`. When `N` is at or beyond the last persisted sequence there is no backlog: on a live run the host MUST hold the stream open for later events, and on a terminal run it MUST close the stream without a frame. A host MUST NOT refuse a well-formed non-negative integer `Last-Event-ID` because no event carries that sequence; any other value SHOULD be refused with `400 validation_error`. The header is evaluated only after the caller is authorized to read the run: for a run the caller cannot read, the response MUST be the one the host gives without the header (RFC 0213 §A). In `values` mode resumption MUST emit a `state.snapshot` first. With `bufferMs` (0..5000) the host accumulates events into one `event: batch` frame whose `data:` is an array of `RunEventDoc`; it MUST flush on a terminal event, on `node.suspended`, and on close; the batch's `id:` SHOULD be its highest `sequence` and `Last-Event-ID` MUST honor that id. A consumer MUST tolerate both a one-element batch and an unbatched frame. A host MUST NOT limit subscribers per run except for resource protection, and then MUST answer `429 rate_limited` with `Retry-After` rather than drop silently.
88
88
 
89
89
  ### Host events
90
90
 
@@ -25,7 +25,7 @@ RFC 0171 §C.1: every non-standard header is `OpenWOP-<Name>` and every header i
25
25
  | `Cache-Control` | 2 | |
26
26
  | `Content-Encoding` | 1 | RFC 0115. Present only when the host negotiated compression |
27
27
  | `Content-Language` | 1 | The BCP-47 locale actually used (equals the response `locale`). |
28
- | `ETag` | 3 | Optional probe handle for mid-session capability change detection. Deprecated toward v2 (RFC 0165 §C.2). |
28
+ | `ETag` | 3 | Standard HTTP validator (RFC 9110 §8.8.3). The obligation is per operation: MUST on the discovery document (capabilities.md §1), SHOULD on the run snapshot (runs.md §Snapshot), a content hash on a prompt template (getPromptTemplate); see each operation. |
29
29
  | `Location` | 1 | Canonical URI of the new template. |
30
30
  | `OpenWOP-Idempotent-Replay` | 1 | Set when the response was served from the idempotency cache. |
31
31
  | `OpenWOP-Version` | 54 | RFC 0172 §A.4 — the contract that produced this response; MUST equal the one used. |
@@ -19,7 +19,7 @@ The header keeps its standard name (RFC 0171 §C.1) and applies to every mutatin
19
19
  | Retryable outcomes | `429` and `5xx` MUST NOT be replayed from cache; a same-key retry MUST re-execute, and a later final outcome replaces the record. |
20
20
  | Not cached | `400 idempotency_key_invalid`, `400 validation_error`, `401` and `403` MUST NOT be cached. |
21
21
  | Digest mismatch | A different request digest under the same record key MUST fail with `409 idempotency_key_mismatch` and MUST NOT return the cached body. This is the only mismatch code. |
22
- | Concurrency | Of two concurrent same-key requests a host MUST process exactly one to completion and MUST NOT process both. Retry timing travels in `Retry-After` only. |
22
+ | Concurrency | Of two concurrent same-key requests a host MUST process exactly one to completion and MUST NOT process both. The other MAY wait, bounded by the host's request timeout, and receive the winner's response only if it is a final outcome (one this record caches), marked `OpenWOP-Idempotent-Replay: true`. Otherwise the host MUST answer `409 idempotency_in_flight` with no retry timing in `details`, and SHOULD set `Retry-After`. The code's registry `retriable: false` means not retryable without waiting (RFC 0213 §B). |
23
23
  | Replay marker | A response served from cache MUST carry `OpenWOP-Idempotent-Replay: true`. |
24
24
  | Retention | A record MUST be retained for at least 24 hours. |
25
25
  | Keyspace | Host-minted identifiers MUST NOT share the caller idempotency store. Logs and spans MUST NOT expose keys. |
@@ -57,6 +57,8 @@ The event is content-free: `peer` MUST be a digest of the peer origin, never the
57
57
 
58
58
  **A2A multi-turn (A2A §3.4.3).** A message carrying `taskId` without `contextId` MUST be answered with the task's `contextId`. A message whose `contextId` is not its task's MUST be refused with its binding's invalid-parameters error and MUST NOT change the run. A message to a retained terminal task MUST be refused `UnsupportedOperationError`; `TaskNotFoundError` is for unknown, purged and unreadable tasks.
59
59
 
60
+ **A2A error details (RFC 0211).** On a 1.0 `JSONRPC` interface, an A2A error's `error.data` MUST be an array of objects each carrying `@type`, including exactly one `type.googleapis.com/google.rpc.ErrorInfo` whose `reason` is the map row's `reason` and whose `domain` is `a2a-protocol.org` (a tightening of A2A §9.5's SHOULD). An `HTTP+JSON` interface MUST answer A2A §11.6's `google.rpc.Status`. A response on an interface URL the card lists, including a refusal before dispatch, MUST NOT be the OpenWOP error envelope. `TaskNotFoundError` details MUST NOT differ between an unknown task and an unreadable one, apart from an echo of the requested id. On `VersionNotSupportedError` a host SHOULD set `metadata.supportedVersions` to a comma-joined list; a client falls back to the card's `supportedInterfaces[].protocolVersion`. A client MUST identify an error by code or by the ErrorInfo `reason`, MUST accept `data` as an array, and SHOULD accept a `data` object carrying `reason` through 2.x.
61
+
60
62
  ## MCP tasks and cancellation (RFC 0198)
61
63
 
62
64
  A host MAY serve the MCP Tasks extension `io.modelcontextprotocol/tasks` (revision `2026-07-28`) on its server mount. It advertises it in its `server/discover` `capabilities.extensions` and by listing `extensions` in `mcp.features[]`, and nowhere else. A host that advertises it MUST implement the extension as published and the map's `mcp.tasks` rows, and:
@@ -48,7 +48,7 @@ A host MUST expose the run-scoped surface and SHOULD expose the signed-token sur
48
48
  | `400` | `validation_error` | `resumeValue` fails `resumeSchema` or the approval action is not in `actions` |
49
49
  | `401` | `interrupt_token_invalid` | MAC, `alg` or `kid` not accepted |
50
50
  | `404` | `not_found` | No such run or node |
51
- | `409` | `interrupt_already_resolved` | Already resolved; or a token invalidated by resolution, cancellation or completion |
51
+ | `409` | `interrupt_already_resolved` | Already resolved, or the run is cancelled or completed (both surfaces); or a token invalidated by resolution, cancellation or completion |
52
52
  | `410` | `interrupt_expired` | Token past `expiresAt` (token surface only) |
53
53
 
54
54
  ## Tokens
@@ -9,7 +9,7 @@
9
9
 
10
10
  ## The surface
11
11
 
12
- A host that advertises `replay` (capabilities.md) serves `forkRun` (`api/v2/openapi.yaml`, `POST /runs/{runId}:fork`) and `getEffectSeamManifest` (`GET /host/effect-seams`). The `replay` facet (`spec/v2/facets/replay.schema.json`) is `{ modes[], retention?, effectSeamsManifest }`; `modes` enumerates `fork | branch | rerun`; `effectSeamsManifest` is the constant `/host/effect-seams`. There is no `sideEffectSuppression` field: suppression is the only conforming replay behavior (RFC 0173 §B, row `C6.2`) and `none` is not a value.
12
+ A host that advertises `replay` (capabilities.md) serves `forkRun` (`api/v2/openapi.yaml`, `POST /runs/{runId}:fork`) and `getEffectSeamManifest` (`GET /host/effect-seams`). The `replay` facet (`spec/v2/facets/replay.schema.json`) is `{ modes[], retention?, effectSeamsManifest }`; `modes` enumerates `replay | branch` (the `forkRun` `mode` values); `effectSeamsManifest` is the constant `/host/effect-seams`. There is no `sideEffectSuppression` field: suppression is the only conforming replay behavior (RFC 0173 §B, row `C6.2`) and `none` is not a value.
13
13
 
14
14
  The request body, `fromSeq` defaults and `201` response are runs.md §Fork. Events with `sequence < fromSeq` are fixed history; events `>= fromSeq` are re-executed.
15
15
 
@@ -708,11 +708,11 @@
708
708
  {
709
709
  "code": "interrupt_cancelled",
710
710
  "httpStatus": 410,
711
- "statusSource": "v1 prose",
711
+ "statusSource": "registry (v1 prose said 422, run-scoped; RFC 0213 §C)",
712
712
  "retriable": false,
713
713
  "details": null,
714
714
  "since": "1.0",
715
- "source": "spec/v1/interrupt.md (the run was cancelled while suspended)"
715
+ "source": "spec/v1/interrupt.md:434 (422, run-scoped). The v2 core resolve surfaces do not emit it — RFC 0171 C4.6 one code per state; RFC 0213 §C."
716
716
  },
717
717
  {
718
718
  "code": "interrupt_expired",
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "callbackDelivery": {
26
26
  "type": "boolean",
27
- "description": "RFC 0196 \u00a7A.1 \u2014 true only if the host delivers to createRun.callbackUrl (interrupt.md \u00a7Callback delivery). A host advertising it MUST hold callbackUrl to the webhooks.md \u00a7SSRF guard at create time and at delivery."
27
+ "description": "RFC 0196 \u00a7A.1 \u2014 true only if the host delivers to createRun.callbackUrl (interrupt.md \u00a7Callback delivery). A host advertising it MUST hold callbackUrl to the webhooks.md \u00a7Egress guard at create time and at delivery."
28
28
  }
29
29
  },
30
30
  "required": [
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./interop-map.schema.json",
3
- "$comment": "RFC 0208 (RFC 0198 adds mcp.tasks). Normative: spec/v2/core/interop.md §\"The operation mappings\" incorporates every row. Hand-maintained; checked by scripts/check-interop-map.mjs against api/v2/openapi.yaml operationIds, spec/v2/errors.json codes, the RunSnapshot.status enum and the spec/v2/facets/ schemas. Upstream names are verbatim; `v2Operation` is an operationId in api/v2/openapi.yaml or null when no v2 operation serves the row. Ships in @openwop/spec-artifacts.",
3
+ "$comment": "RFC 0208 (RFC 0198 adds mcp.tasks; RFC 0211 adds a2a.errors[].reason). Normative: spec/v2/core/interop.md §\"The operation mappings\" incorporates every row. Hand-maintained; checked by scripts/check-interop-map.mjs against api/v2/openapi.yaml operationIds, spec/v2/errors.json codes, the RunSnapshot.status enum and the spec/v2/facets/ schemas. Upstream names are verbatim; `v2Operation` is an operationId in api/v2/openapi.yaml or null when no v2 operation serves the row. Ships in @openwop/spec-artifacts.",
4
4
  "version": 1,
5
- "updated": "2026-09-22",
5
+ "updated": "2026-09-23",
6
6
  "pins": {
7
7
  "a2a": {
8
8
  "release": "1.0.1",
@@ -99,7 +99,7 @@
99
99
  "a2a.streaming",
100
100
  "a2a.durableTasks"
101
101
  ],
102
- "rule": "First event is the current Task; read-only re-attachment that MUST NOT re-execute or re-accept a message; a terminal task is UnsupportedOperationError."
102
+ "rule": "First event is the current Task; read-only re-attachment that MUST NOT re-execute or re-accept a message; a terminal task is UnsupportedOperationError. The HTTP+JSON verb is an upstream conflict: the A2A v1.0.1 proto binds `GET /tasks/{id=*}:subscribe` while the specification prose (§5.3, §11.3.2) and the a2a-js and a2a-python REST clients send POST; a host that lists an HTTP+JSON interface SHOULD accept both."
103
103
  },
104
104
  {
105
105
  "upstream": "CreateTaskPushNotificationConfig",
@@ -270,7 +270,8 @@
270
270
  "grpc": "NOT_FOUND",
271
271
  "http": 404,
272
272
  "serverWhen": "unknown, purged, or not readable by the caller (identically, including a tenant mismatch)",
273
- "clientProjection": "not_found"
273
+ "clientProjection": "not_found",
274
+ "reason": "TASK_NOT_FOUND"
274
275
  },
275
276
  {
276
277
  "upstream": "TaskNotCancelableError",
@@ -278,7 +279,8 @@
278
279
  "grpc": "FAILED_PRECONDITION",
279
280
  "http": 400,
280
281
  "serverWhen": "CancelTask on a terminal run",
281
- "clientProjection": "run_terminal"
282
+ "clientProjection": "run_terminal",
283
+ "reason": "TASK_NOT_CANCELABLE"
282
284
  },
283
285
  {
284
286
  "upstream": "PushNotificationNotSupportedError",
@@ -286,7 +288,8 @@
286
288
  "grpc": "FAILED_PRECONDITION",
287
289
  "http": 400,
288
290
  "serverWhen": "push config without a2a.pushNotifications",
289
- "clientProjection": "capability_required"
291
+ "clientProjection": "capability_required",
292
+ "reason": "PUSH_NOTIFICATION_NOT_SUPPORTED"
290
293
  },
291
294
  {
292
295
  "upstream": "UnsupportedOperationError",
@@ -294,7 +297,8 @@
294
297
  "grpc": "FAILED_PRECONDITION",
295
298
  "http": 400,
296
299
  "serverWhen": "an unimplemented operation or option; a message to a retained terminal task; SubscribeToTask on a terminal task",
297
- "clientProjection": "capability_required"
300
+ "clientProjection": "capability_required",
301
+ "reason": "UNSUPPORTED_OPERATION"
298
302
  },
299
303
  {
300
304
  "upstream": "ContentTypeNotSupportedError",
@@ -302,7 +306,8 @@
302
306
  "grpc": "INVALID_ARGUMENT",
303
307
  "http": 400,
304
308
  "serverWhen": "a part mediaType or acceptedOutputModes entry the skill cannot handle, at request time",
305
- "clientProjection": "validation_error"
309
+ "clientProjection": "validation_error",
310
+ "reason": "CONTENT_TYPE_NOT_SUPPORTED"
306
311
  },
307
312
  {
308
313
  "upstream": "InvalidAgentResponseError",
@@ -311,6 +316,7 @@
311
316
  "http": 500,
312
317
  "serverWhen": null,
313
318
  "clientProjection": "validation_error",
319
+ "reason": "INVALID_AGENT_RESPONSE",
314
320
  "note": "the malformed body MUST NOT enter run state"
315
321
  },
316
322
  {
@@ -319,7 +325,8 @@
319
325
  "grpc": "FAILED_PRECONDITION",
320
326
  "http": 400,
321
327
  "serverWhen": "GetExtendedAgentCard with capabilities.extendedAgentCard not true",
322
- "clientProjection": "not_found"
328
+ "clientProjection": "not_found",
329
+ "reason": "EXTENDED_AGENT_CARD_NOT_CONFIGURED"
323
330
  },
324
331
  {
325
332
  "upstream": "ExtensionSupportRequiredError",
@@ -327,7 +334,8 @@
327
334
  "grpc": "FAILED_PRECONDITION",
328
335
  "http": 400,
329
336
  "serverWhen": "only for a required:true extension the client did not declare",
330
- "clientProjection": "validation_error"
337
+ "clientProjection": "validation_error",
338
+ "reason": "EXTENSION_SUPPORT_REQUIRED"
331
339
  },
332
340
  {
333
341
  "upstream": "VersionNotSupportedError",
@@ -335,7 +343,8 @@
335
343
  "grpc": "FAILED_PRECONDITION",
336
344
  "http": 400,
337
345
  "serverWhen": "an A2A-Version the interface does not serve",
338
- "clientProjection": "interop_version_unsupported"
346
+ "clientProjection": "interop_version_unsupported",
347
+ "reason": "VERSION_NOT_SUPPORTED"
339
348
  },
340
349
  {
341
350
  "upstream": "(invalid parameters)",
@@ -343,7 +352,8 @@
343
352
  "grpc": "INVALID_ARGUMENT",
344
353
  "http": 400,
345
354
  "serverWhen": "a message whose contextId is not its task's (A2A §3.4.3); no run state changes",
346
- "clientProjection": "validation_error"
355
+ "clientProjection": "validation_error",
356
+ "note": "No A2A reason; the error SHOULD carry a google.rpc.BadRequest (RFC 0211 §A)."
347
357
  }
348
358
  ],
349
359
  "fields": [
@@ -424,6 +424,11 @@
424
424
  "type": "string",
425
425
  "description": "A code in spec/v2/errors.json (gate-checked)."
426
426
  },
427
+ "reason": {
428
+ "type": "string",
429
+ "pattern": "^[A-Z][A-Z0-9_]*$",
430
+ "description": "RFC 0211 §A. The google.rpc.ErrorInfo reason: the upstream error name in UPPER_SNAKE_CASE without the `Error` suffix (A2A §10.6). Absent on a parenthesized non-A2A row, which has no A2A name. Gate-checked against `upstream`."
431
+ },
427
432
  "note": {
428
433
  "type": "string"
429
434
  }
@@ -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.36.0",
4
- "corpusTag": "v2.36.0",
5
- "updated": "2026-09-23"
3
+ "version": "2.37.1",
4
+ "corpusTag": "v2.37.1",
5
+ "updated": "2026-09-24"
6
6
  }
@@ -4,7 +4,7 @@
4
4
  "schemas/v2/**/*.json",
5
5
  "spec/v2/path-manifest.json"
6
6
  ],
7
- "release": "2.36.0",
7
+ "release": "2.37.1",
8
8
  "kinds": {
9
9
  "union": [
10
10
  "channel",