@openwop/spec-artifacts 2.1.7 → 2.2.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 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.1.7",
5
- "corpusTag": "v2.1.7",
4
+ "version": "2.2.0",
5
+ "corpusTag": "v2.2.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": "39081c59fb696159806b0f2f9a42e7e9ff830d622fcf2ad4159b21357580a955",
11
11
  "api/redocly.yaml": "b0604c89b2ca6d5076ec25725c539dad44a741a811fe524439ee6daef8baa09f",
12
- "api/seams-v2.yaml": "a848487c183fff6a6f9117932245c2c7765b5b7da8cad1d0a6d02e669ef1b40c",
13
- "api/v2/asyncapi.yaml": "66440a6d3f136c5d12c9ebe4dad80e993f7d8fcf5c6203a5694f06ce45f977df",
14
- "api/v2/openapi.yaml": "0ca4bdcc6572f427369e1d55b7d3e981668cc424f4bc84f314d6d25170964d51",
12
+ "api/seams-v2.yaml": "5d074016ed82dda66ec00016a06aef595f6688e47d95690d1eb395e3bf217c56",
13
+ "api/v2/asyncapi.yaml": "11107def567fc6188b29c267a2f5488fd92c59fb3433fd4124fdf63f9aca4981",
14
+ "api/v2/openapi.yaml": "303752410cc4b0be42206a678c08d7aeb14333ab9e354e2e021356d468e8c3e7",
15
15
  "api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
16
16
  "schemas/README.md": "0c0b737ffcf8f30e7d2809cec8a498232de710f41443212922ad8337cdde0b51",
17
17
  "schemas/a2a-task-state.schema.json": "c9365918f993f943b4b619d42551eb066a1ed33a08d895d51b395432a5b1f1bc",
@@ -82,7 +82,7 @@
82
82
  "schemas/residency.schema.json": "b45fdc91ae88091251914dbd6787a1e2ce687580b91d31a759217b7a1f0010c0",
83
83
  "schemas/run-ancestry-response.schema.json": "963a5763b03216aa72fc2854560c778e53994f8cb06ea9a4053e541273c87f5c",
84
84
  "schemas/run-diff-response.schema.json": "d9d196730b5d5d8a04a591d21a8fc6b44be7fa134f11827ff7e4f1a86d47e83d",
85
- "schemas/run-event-payloads.schema.json": "37cdbdb5038e148056c104a49b8d28a9668479e06d401309d354a8b0a70921a3",
85
+ "schemas/run-event-payloads.schema.json": "2f2a5192a6262758bbffb358d2044ebe4ad234b388ddcd47b818602897efb93f",
86
86
  "schemas/run-event.schema.json": "c70c06932b9831295042836213e617c8e946f20ddd49cf4e889b74d89d2175a8",
87
87
  "schemas/run-options.schema.json": "f8241ece6ddc6527de9160d811d42c231b4f77b935f2e107b3c05608545c59ab",
88
88
  "schemas/run-orchestrator-decided-event.schema.json": "17a43a3c44680a24d411141357a867b3caed6107d56de88fe2e062eaf93fa2c1",
@@ -149,7 +149,7 @@
149
149
  "schemas/v2/goal.schema.json": "146c4182e36d3538778f947e668a92cdb69ec42ee0820104b00063b035e342d7",
150
150
  "schemas/v2/heartbeat-evaluated.schema.json": "4086b1ba1d0547c08b038dfb263ac89416a3e406430db1250156fb2f9b74f8d6",
151
151
  "schemas/v2/heartbeat-state-changed.schema.json": "eb68d0082aa996650e3a6b8ccf32457f959cebc7e6728cc7c331b9d171a4bef6",
152
- "schemas/v2/ids.schema.json": "05c98e3f8a72fa79be123765bd9c2ea7c6620188d69df8524160399225316cd6",
152
+ "schemas/v2/ids.schema.json": "6481cab16c9b89fab168627502dcbf2ee7489b5a636a4080ea8cfd2670d7b17e",
153
153
  "schemas/v2/localized-content-language-settings.schema.json": "92240f19a2b4b2f05ab41a797f703485a28813848035478e72f395edd04c1d35",
154
154
  "schemas/v2/localized-content-page-response.schema.json": "ef48329749758afb21e4d3cbc3f017abb911f75726819457f35b9f1390332c75",
155
155
  "schemas/v2/localized-content-page.schema.json": "e0f97660121955d6705d935f34e748adb739ac70f8536a009cd8d6f303735151",
@@ -169,7 +169,7 @@
169
169
  "schemas/v2/residency.schema.json": "3fd23c9732beb140504e81560fc426eb6764d547e56da5fb92c5a5c7bf04db3a",
170
170
  "schemas/v2/run-ancestry-response.schema.json": "334c17f2062dea62726ca1d07b8be3f169cdc422af843c1def5fb317039814c9",
171
171
  "schemas/v2/run-diff-response.schema.json": "8bdb2045c274580038bd74781f7d062f5049839797c164a89de4ed89edc53a74",
172
- "schemas/v2/run-event-payloads.schema.json": "1d3ccffc4e506d3109c17990e01976ba935e61672562684b9635743d56af3f57",
172
+ "schemas/v2/run-event-payloads.schema.json": "38dd47929f0245c865e4850aab453e073bb717bc81590d68a91bd7b490536d63",
173
173
  "schemas/v2/run-event.schema.json": "1a9946374af9323b71635d00fed22abe02c300fa2cf3ae3e9c30f3e553e408ec",
174
174
  "schemas/v2/run-list-response.schema.json": "c2dbc34768dcf64528914a213aa3cb55952e8e3e90a64b04a9423b2a19749d38",
175
175
  "schemas/v2/run-options.schema.json": "f0e9f494d80086c2ed2cf9fe87df54e5b4e66601699c4a1089f4edbc7ba58867",
@@ -200,11 +200,11 @@
200
200
  "schemas/workspace-file.schema.json": "464de85c2a068243084ee9c1d969bc7cd5d8f7948574e58450d6493c38a0e1e4",
201
201
  "spec/v1/alias-detectors.json": "fee4594ef49953953ffcd0b3813300067d16b3e65ebff2aac722034ac9b3f545",
202
202
  "spec/v1/capability-declaration-classes.json": "e7729aed5c4b4e1dd02abab0530f14cc95f5d4070fe51fb139e7f5cccefa00c6",
203
- "spec/v1/core-standard-manifest.json": "de593dd613393b6d93d90c8e76032466aaa68379a53eaf274fcd921264e51ead",
203
+ "spec/v1/core-standard-manifest.json": "9220156eee1d465b371af344f19fe7ad151e492da3be066f43032d7bd451b0a0",
204
204
  "spec/v1/deprecations.json": "a2022c2d6eb501bf406b5d75fe2d431a3fe0158e8509125d2453985eb1bf511a",
205
205
  "spec/v1/deprecations.schema.json": "18c87e78bedc210431f795ae44c5b5d202f2f3317850d5cf86867d4f1fa1cdfb",
206
206
  "spec/v1/event-codemap.json": "3da60d884157793a360da532a9fcbbfb5285636db325a74cec94b34622186d97",
207
- "spec/v1/event-codemap.schema.json": "d52caf29071e3784d4c20259cdbd5a7b533986548a440bb632a80b82cadd7ea5",
207
+ "spec/v1/event-codemap.schema.json": "d05933b2e88103aff51a2f774df97b9bda065e0fc88dbd9b114cc47a32189174",
208
208
  "spec/v1/extensions.json": "79a60754aa16cbdbdb604f8e5c00038af535a1690a4d07bd5ff7c38f0256a7bc",
209
209
  "spec/v1/gaps.json": "96ca051cb20f1a669b355d67a89421c56fef28aa5e6710c9519f592f26af33a3",
210
210
  "spec/v1/gaps.schema.json": "8fd83259f556553c9df0f53e7a82ca8c2a4771d471197f69b4e2ae0ceeacfd66",
@@ -212,16 +212,16 @@
212
212
  "spec/v1/migrations.schema.json": "886779aa6c22e646db097f5df210adb018a4dd14a7b815465a18c8a7056c8f72",
213
213
  "spec/v1/operation-path-manifest.json": "5f5f4e3842669371730ebbd1aace3fb794192615f0018dc144f484ba5db82ac3",
214
214
  "spec/v1/spec-gaps.json": "6cc9962c6b969f632e07a78b52a4f61447ff579e2990cbae989866f584a86042",
215
- "spec/v2/README.md": "e3d8f6a13338014c349e57bf2b50a398aa1e0aa162c5adda0273e26dd6f48acd",
215
+ "spec/v2/README.md": "e2a3e769eec448a50b41d910805d31ccc173bbebf79b2418a7734d70d2ad828e",
216
216
  "spec/v2/core/capabilities.md": "c5107a4964e487c20bbb5254cfe1f706e0d9ba7e34da9070bc43b50b385e7948",
217
217
  "spec/v2/core/conformance.md": "622084e3c74c4549087f4d861b1bb98cec201a0c5aec23788d27f53a355fde3b",
218
218
  "spec/v2/core/connection-packs.md": "1d759681e2962f5103228c52c45f24607ed4bb9ae73e30605f690c1d5a59e9ee",
219
219
  "spec/v2/core/errors.md": "74fdb5862f432a831cf0c2eadbe0c8cca6b2660ec578180ae33db54773a1df28",
220
- "spec/v2/core/events.md": "defe0969d6ec7c980f415cc0d78b9d065782ac7e5638b12a44aa4895a35d6126",
220
+ "spec/v2/core/events.md": "3b7d1ad34f9e438c9825d60c0114b9a21c82a468f15ccbd7567022641a4537ad",
221
221
  "spec/v2/core/form-content-packs.md": "6ffaae85d4550be48dd53fb6e2959af8aceb35bd87c5343b722885084f197713",
222
- "spec/v2/core/headers.md": "a795c8cd3e5f004b05dad9603d14b92b639a24ccd23b183716cb01097a106119",
222
+ "spec/v2/core/headers.md": "f8dd0d285e73906059cd815f18ecc068a0f1339a3755cf6319f0ee5d8da607d2",
223
223
  "spec/v2/core/idempotency.md": "b86c352d964aebad532727f314e26ed2177d49de382076fa64d3162e2f56118d",
224
- "spec/v2/core/identity.md": "dd5299f9cdf54640870e1fffae11edb820cf6396ea298c4ebd14c649c4bd4a87",
224
+ "spec/v2/core/identity.md": "9d2b823eb899463807452fe5721dcf09881837c161be024175957db27870b243",
225
225
  "spec/v2/core/interop.md": "d4c404e469e1d9d9be88bc2c939d777e7d7c1d0419b1251c18770f039838a401",
226
226
  "spec/v2/core/interrupt.md": "d872e0652a938594eeb2a19e86cf64e64e29b234f5d9c4fbe2970ab5fb191b9c",
227
227
  "spec/v2/core/overview.md": "69377c90bbb87b6bfe4b8be4f1b65810094b0759c8d4f93ca6df92cc530ea023",
@@ -233,11 +233,11 @@
233
233
  "spec/v2/core/versioning.md": "d731782eeabb953919c7c7057340b7f9c2916b6186790e6b04c7d8ea60767189",
234
234
  "spec/v2/core/webhooks.md": "d360da78336afa837bddf515e289e445808bca02e1e4b493f81fa51ce4a545c4",
235
235
  "spec/v2/core/workflow-chain-packs.md": "ef2b07d2d99334eaec0cc370314e2ad2f84ea58deb699e5d92e6e52d6056613f",
236
- "spec/v2/declaration.json": "63dd351a87439241bc744ce62fdc498b7621292c11a4ad42c3f41429d72366b9",
237
- "spec/v2/declaration.schema.json": "eac5f8080bd572f147bd57d0c4ac8ad73b14536d9236222490ae29a33b21e184",
236
+ "spec/v2/declaration.json": "a09bac61c1b825e4a77b5abc0f86f1f8c20c381e0650eb08c2f6d0ef0aaec1dc",
237
+ "spec/v2/declaration.schema.json": "c86e3083fb9789c1958ecb4576cc6f71aa90e6f339521139238543ed08211dd4",
238
238
  "spec/v2/errors.json": "f179414a92f30b5dadf26e9e57699649a67324bd7186d5287647d17605d137d3",
239
239
  "spec/v2/event-codemap.json": "37869a03cdfd8758b57e6f350641a6ebbd383231cebda51f5473ad85f94ab74c",
240
- "spec/v2/event-codemap.schema.json": "ce2d0ca085d7668d7ef745d2560b385ec6881d24a6d4b47c49931986b2ba642d",
240
+ "spec/v2/event-codemap.schema.json": "b173f2a9bcc0bf9b62a474ce3b91e1431d597e64fd1560fc58662cd8606eaf9c",
241
241
  "spec/v2/ext/a2uiSurface/README.md": "176cb0542e1c236ffcd6d682536ba0081242684444e2dec790abd480a44a571e",
242
242
  "spec/v2/ext/brand/README.md": "1a80102a9cd766dcb6098c999e552af88f5b83c88951acde18d17cc0b29aa09d",
243
243
  "spec/v2/ext/canvas/README.md": "a2d5f28523753e8e85cc9042db39faf9207a2eebce1a8553be22e341f7ea58d9",
@@ -266,12 +266,12 @@
266
266
  "spec/v2/facets/runList.schema.json": "6223e1baaebbb9f4faa71bfe968f8d22b4aa88046d0e43e8d5f56409e7f86e90",
267
267
  "spec/v2/facets/sandbox.schema.json": "e75f54486d387b2d089791aa93b5093cd1450f1920753a0f97fc23acdfe978ed",
268
268
  "spec/v2/facets/webhooks.schema.json": "0b766033ef4b4c6c2ac6f9979a19843ba024d3a1d1576972dcb55aaba52f1b9a",
269
- "spec/v2/id-field-bindings.json": "d9d1f8e266660b581f11020b9511a65c677e026b1a71ada870682ad77e18555d",
269
+ "spec/v2/id-field-bindings.json": "eecadfd11581ba2e075ccb320d85fc033b816edfdd51edb5c5727a181e55dcb4",
270
270
  "spec/v2/path-manifest.json": "a6f4e652e866889e8bde815874062f62929aee95dc8e58e7152c56adee262163",
271
- "spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
271
+ "spec/v2/peer-dependency-aliases.json": "10fcab53c65305e9b6bae4a40d30176091dca68739ac8a78291510c77b50ca6b",
272
272
  "spec/v2/profiles.json": "0636f19fceae625390003a347e70ef4797d84766b5c24ce8a02cea52aadebca4",
273
- "spec/v2/release.json": "2bacef4931ec682b7bf003746eb1f69e3b945bbb93b98d76ddf86128e9922be5",
273
+ "spec/v2/release.json": "ce7a6bb9f4337e3063147edcd609c28c12cc26599ee4ffcfe32e816d85ddc399",
274
274
  "spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624"
275
275
  },
276
- "corpusCommit": "4c50dd0dc8b411090dfeeb3a5bbf9466474807c9"
276
+ "corpusCommit": "e2db4047ee81b8f31ffee84238ebdd551d65b34c"
277
277
  }
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.1.7
4
+ version: 2.2.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
@@ -1,7 +1,7 @@
1
1
  asyncapi: 3.1.0
2
2
  info:
3
3
  title: OpenWOP v2 event streams
4
- version: 2.1.7
4
+ version: 2.2.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
@@ -1,7 +1,7 @@
1
1
  openapi: 3.1.0
2
2
  info:
3
3
  title: OpenWOP v2 API
4
- version: 2.1.7
4
+ version: 2.2.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.1.7",
3
+ "version": "2.2.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": {
@@ -6,7 +6,7 @@
6
6
  "type": "object",
7
7
  "$defs": {
8
8
  "_typeIndex": {
9
- "description": "Lookup map: RunEventType \u2192 $defs key. Informative \u2014 consumers can derive this themselves but having it explicit aids tooling.",
9
+ "description": "Lookup map: RunEventType \u2192 $defs key. Informative \u2014 consumers can derive this themselves but having it explicit aids tooling. THE MAP IS THE `properties` OBJECT: this node is a JSON Schema, so its own keys are `description`, `type`, `additionalProperties` and `properties`. Asking for its top-level keys to test whether a type is registered returns those four and reads as a miss.",
10
10
  "type": "object",
11
11
  "additionalProperties": false,
12
12
  "properties": {
@@ -3,6 +3,7 @@
3
3
  "$id": "https://openwop.dev/spec/v2/ids.schema.json",
4
4
  "title": "OpenWOP v2 identifier grammars (RFC 0170 \u00a7D.1)",
5
5
  "description": "One $defs entry per id kind. Every id field in every v2 schema, OpenAPI parameter and response body $refs its kind. `minted` records who mints the id: host (opaque, checkable: `^[A-Za-z0-9._~-]{16,128}$` after any tenant segment), author (a workflow/pack author chooses it; the v1 grammar stands), registry (the pack registry mints it). Tenant-bound kinds (runId, interruptId, subscriptionId, deliveryId, effectId) carry the tenant segment: a host MUST reject an id whose tenant segment is not the caller's.",
6
+ "$comment": "PATH PROJECTION (RFC 0184 \u00a7A.1). A tenant-bound id is TWO segments joined by `/`, and a URL path parameter is ONE segment, so the id must be projected. ENCODE: UTF-8 the id, then per byte emit it verbatim if it is in `[A-Za-z0-9._-]`, else `~` + two UPPERCASE hex digits (`/`\u2192`~2F`, `:`\u2192`~3A`, `~`\u2192`~7E`). DECODE: replace every `~HH`; a `~` not followed by two hex digits is `400 validation_error`. The escape marker is `~` and NOT `%` because `~` is RFC 3986 \u00a72.3 UNRESERVED: an intermediary has no license to rewrite it, so `~2F` reaches the origin byte-for-byte. `%2F` does not \u2014 handling it correctly requires an intermediary to distinguish a percent-encoded RESERVED octet (which \u00a76.2.2.2 says it MUST NOT decode) from an unreserved one (which it SHOULD), and deployed front doors demonstrably do not: a reference host lost every bound id at its origin to exactly this. The codec is TOTAL over bytes, so it cannot be invalidated by a later widening of any id grammar. It is injective because it escapes its own marker \u2014 but ONLY over its own output, which is why \u00a7A.2 forbids MINTING a tenant-bound id containing `~`: an unprojected legacy segment carrying a literal `~` is the one ambiguous input, and a host can resolve those from its OWN store because they are its own ids.APPLY EXACTLY ONCE: the codec is NOT idempotent \u2014 `a~3Ab` encodes to `a~7E3Ab`, which is required for injectivity but means a second application silently corrupts, and nothing marks an encoded id as already encoded. Both reporting hosts found they compose projections on one read path already (one of them caught itself writing two `payload:` keys where the second silently won), so the invariant is stated on the VALUE, not the call site: project where an id LEAVES the host, and never re-encode what was read back from its own output. SCOPE: only the TENANT-BOUND kinds. `nodeId` is author-chosen, carries no tenant segment and already admits `:` by design \u2014 projecting it would change ids that are legal today, which is the cost base64url was rejected for. `Idempotency-Key` (`idempotency.md` \u00a7Layer 1, `^[A-Za-z0-9._~-]{22,128}$`) is caller-supplied and tilde-LEGAL; it is not an id kind, \u00a75 does not bind it, and it is NOT projected \u2014 stated because \"escape `~` in identifiers\" is the sentence a later reader would apply to it. LENGTH: the `{16,128}` bounds govern the ID, never the projected segment. A bound id is already up to 257 characters before any byte escapes, so a validator that checks the projected segment against an id pattern is checking the wrong string.",
6
7
  "type": "object",
7
8
  "additionalProperties": false,
8
9
  "$defs": {
@@ -15,7 +16,7 @@
15
16
  "type": "string",
16
17
  "pattern": "^(anon:)?[A-Za-z0-9._~-]{1,128}$",
17
18
  "x-openwop-minted": "host",
18
- "description": "A tenant identifier. The optional `anon:` prefix is the ONLY colon admitted, and it exists because RFC 0132 anonymous tenants are `anon:<hash>` while RFC 0170 \u00a7A.5 makes the anonymous surface first-class \u2014 without it a conforming host can serve an anonymous run with neither a valid `owner.tenant` nor a valid tenant-bound `runId`. `:` is a legal path character (RFC 3986 pchar), so this stays safe inside the `<tenantId>/<opaque>` run-id form. The alternative \u2014 renaming anonymous tenants at the cut \u2014 was rejected: it is a write to historical identity, which is the one thing the v2 migration does not do."
19
+ "description": "A tenant identifier. The optional `anon:` prefix is the ONLY colon admitted. Its ORIGINAL rationale here cited RFC 0132, and that citation was WRONG: RFC 0132 puts `anon:` on the **principal** and \u00a7C.1 requires the host to resolve a real `tenant` (the surface owner) \u2014 `{tenant: \"acme\", principal: \"anon:sess-3f9c\"}`. The prefix is kept because a REPORTING HOST was observed minting `tenantId: \"anon:<sid>\"` (`conformance/src/scenarios/test-seam-unauthenticated.test.ts`), and that grammar shipped in v2.0.0; removing it is a major-version change, not an erratum. Whether it belongs on a tenant at all is deferred to v3 (RFC 0184 \u00a7A.3). `:` is a legal path character (RFC 3986 pchar), but the claim that it \"stays safe inside the `<tenantId>/<opaque>` run-id form\" was FALSE ON ARRIVAL: this pattern admitted `anon:`, and all five tenant-bound kinds rejected it, so every runId an anon-tenant host minted was schema-invalid. RFC 0184 \u00a7A.3 widened those five to match. On the wire the question is moot \u2014 the \u00a7A.1 projection escapes `:` to `~3A` like any other non-passthrough byte. The alternative \u2014 renaming anonymous tenants at the cut \u2014 was rejected: it is a write to historical identity, which is the one thing the v2 migration does not do."
19
20
  },
20
21
  "workspaceId": {
21
22
  "type": "string",
@@ -30,32 +31,32 @@
30
31
  },
31
32
  "runId": {
32
33
  "type": "string",
33
- "pattern": "^[A-Za-z0-9._~-]{1,128}/[A-Za-z0-9._~-]{16,128}$",
34
+ "pattern": "^(anon:)?[A-Za-z0-9._~-]{1,128}/[A-Za-z0-9._~-]{16,128}$",
34
35
  "description": "Tenant-bound: `<tenantId>/<opaque>`.",
35
36
  "x-openwop-minted": "host",
36
37
  "x-openwop-tenant-bound": true
37
38
  },
38
39
  "interruptId": {
39
40
  "type": "string",
40
- "pattern": "^[A-Za-z0-9._~-]{1,128}/[A-Za-z0-9._~-]{16,128}$",
41
+ "pattern": "^(anon:)?[A-Za-z0-9._~-]{1,128}/[A-Za-z0-9._~-]{16,128}$",
41
42
  "x-openwop-minted": "host",
42
43
  "x-openwop-tenant-bound": true
43
44
  },
44
45
  "subscriptionId": {
45
46
  "type": "string",
46
- "pattern": "^[A-Za-z0-9._~-]{1,128}/[A-Za-z0-9._~-]{16,128}$",
47
+ "pattern": "^(anon:)?[A-Za-z0-9._~-]{1,128}/[A-Za-z0-9._~-]{16,128}$",
47
48
  "x-openwop-minted": "host",
48
49
  "x-openwop-tenant-bound": true
49
50
  },
50
51
  "deliveryId": {
51
52
  "type": "string",
52
- "pattern": "^[A-Za-z0-9._~-]{1,128}/[A-Za-z0-9._~-]{16,128}$",
53
+ "pattern": "^(anon:)?[A-Za-z0-9._~-]{1,128}/[A-Za-z0-9._~-]{16,128}$",
53
54
  "x-openwop-minted": "host",
54
55
  "x-openwop-tenant-bound": true
55
56
  },
56
57
  "effectId": {
57
58
  "type": "string",
58
- "pattern": "^[A-Za-z0-9._~-]{1,128}/[A-Za-z0-9._~-]{16,128}$",
59
+ "pattern": "^(anon:)?[A-Za-z0-9._~-]{1,128}/[A-Za-z0-9._~-]{16,128}$",
59
60
  "x-openwop-minted": "host",
60
61
  "x-openwop-tenant-bound": true
61
62
  },
@@ -6,7 +6,7 @@
6
6
  "type": "object",
7
7
  "$defs": {
8
8
  "_typeIndex": {
9
- "description": "Lookup map: RunEventType \u2192 $defs key. Informative \u2014 consumers can derive this themselves but having it explicit aids tooling.",
9
+ "description": "Lookup map: RunEventType \u2192 $defs key. Informative \u2014 consumers can derive this themselves but having it explicit aids tooling. THE MAP IS THE `properties` OBJECT: this node is a JSON Schema, so its own keys are `description`, `type`, `additionalProperties` and `properties`. Asking for its top-level keys to test whether a type is registered returns those four and reads as a miss. These are v1 vocabulary names (`agent.toolCalled`, `run.resuming`). The NORMATIVE v2 map is the sibling `_typeIndex` at the document root, keyed by v2 names (`agent.tool-called`, `run.resume-started`) \u2014 finding \"a _typeIndex\" is not enough, there are two.",
10
10
  "type": "object",
11
11
  "additionalProperties": false,
12
12
  "properties": {
@@ -1409,7 +1409,47 @@
1409
1409
  },
1410
1410
  "decision": {
1411
1411
  "type": "string",
1412
- "description": "The decision recorded for approval-kind interrupts (granted | rejected | overridden), carried from the v1 approval.* payloads."
1412
+ "enum": ["granted", "rejected", "overridden"],
1413
+ "description": "RFC 0183 \u00a7A.3 \u2014 the GOVERNANCE outcome for approval-kind interrupts, carried from the v1 approval.* payloads. Enum-constrained as of RFC 0183: it was an unconstrained string, so an off-vocabulary value such as a resume action validated. This is a different axis from `action`: an override and a refine are orthogonal facts about one resolution, not alternatives."
1414
+ },
1415
+ "action": {
1416
+ "type": "string",
1417
+ "enum": ["accept", "reject", "refine", "edit-accept", "timeout"],
1418
+ "description": "RFC 0183 \u00a7A.1 \u2014 the ApprovalResume action applied at resolution (interrupt.md \u00a7ApprovalResume). OPTIONAL: this def serves all eight interrupt kinds and only approval-kind resolutions have an action. A host recording `refine` MUST carry `refineFeedback`; one recording `edit-accept` MUST carry `editedArtifactData`."
1419
+ },
1420
+ "refineFeedback": {
1421
+ "type": "object",
1422
+ "description": "RFC 0183 \u00a7A.2 \u2014 structured feedback for `action: 'refine'`, modelled from interrupt.md \u00a7RefineFeedback. v1 left this an open object; v2 closes it, so the shape is stated here rather than implied by prose.",
1423
+ "required": ["scope"],
1424
+ "additionalProperties": false,
1425
+ "properties": {
1426
+ "scope": {
1427
+ "type": "string",
1428
+ "enum": ["whole", "section", "items"],
1429
+ "description": "Target of the feedback."
1430
+ },
1431
+ "sectionPath": {
1432
+ "type": "string",
1433
+ "description": "JSON path into artifactData when `scope === 'section'`."
1434
+ },
1435
+ "itemIds": {
1436
+ "type": "array",
1437
+ "items": { "type": "string" },
1438
+ "description": "Item identifiers when `scope === 'items'`."
1439
+ },
1440
+ "tags": {
1441
+ "type": "array",
1442
+ "items": { "type": "string" },
1443
+ "description": "Structured chip selections \u2014 UI-aware, AI-interpreted."
1444
+ },
1445
+ "text": {
1446
+ "type": "string",
1447
+ "description": "Free-text feedback. At least one of {tags, text} SHOULD be populated."
1448
+ }
1449
+ }
1450
+ },
1451
+ "editedArtifactData": {
1452
+ "description": "RFC 0183 \u00a7A.2 \u2014 user-edited artifact bytes when `action: 'edit-accept'`."
1413
1453
  }
1414
1454
  },
1415
1455
  "additionalProperties": false
@@ -4238,7 +4278,7 @@
4238
4278
  },
4239
4279
  "additionalProperties": false,
4240
4280
  "_typeIndex": {
4241
- "description": "NORMATIVE (RFC 0171 \u00a7A.4): v2 event type \u2192 $defs key, generated from spec/v2/event-codemap.json.",
4281
+ "description": "NORMATIVE (RFC 0171 \u00a7A.4): v2 event type \u2192 $defs key, generated from spec/v2/event-codemap.json. THE MAP IS THE `properties` OBJECT: this node is a JSON Schema, so its own keys are `description`, `type`, `additionalProperties` and `properties`. Asking for its top-level keys to test whether a type is registered returns those four and reads as a miss. The sibling `$defs._typeIndex` is the v1-vocabulary map and is informative; this one is the normative v2 map. A type absent here is not a persisted event type \u2014 synthesized SSE frame names (`state.snapshot`, `batch`, `ai.message.chunk`) are deliberately absent, per events.md \u00a7SSE.",
4242
4282
  "type": "object",
4243
4283
  "additionalProperties": false,
4244
4284
  "properties": {
@@ -4,7 +4,7 @@
4
4
  "provenance": {
5
5
  "suite": {
6
6
  "package": "@openwop/openwop-conformance",
7
- "version": "2.1.7"
7
+ "version": "2.2.0"
8
8
  },
9
9
  "note": "Derived from the corpus at generation time. Regenerate with --write; verify with --check."
10
10
  },
@@ -419,5 +419,5 @@
419
419
  "$id": "https://openwop.dev/spec/v1/workspace-file.schema.json"
420
420
  }
421
421
  ],
422
- "digest": "27ce6ceae93a2afda176db9d414d057a831f3ea48e3657f14d9a9708ca4f0767"
422
+ "digest": "ec0aab427ffc60ec0fcfbdeba3ca2cc7f7a3a8dcbe0f5fa24d52325fb8435813"
423
423
  }
@@ -32,8 +32,8 @@
32
32
  "payloadDef": { "type": ["string", "null"], "description": "Key under run-event-payloads.schema.json $defs." },
33
33
  "status": { "type": "string", "enum": ["proposed", "decided"] },
34
34
  "review": { "type": "boolean", "description": "true when the mechanical proposal needs a semantic decision (tense, domain rename, reserved prefix)." },
35
- "notes": { "type": "array", "items": { "type": "string" } },
36
- "note": { "type": "string" }
35
+ "notes": { "type": "array", "items": { "type": "string" }, "description": "GENERATED by scripts/generate-event-codemap.mjs and rewritten on every `--write`: the mechanical reasons the v2 NAME differs from the v1 one (segments folded, case or separator normalised, tense not past). Naming only." },
36
+ "note": { "type": "string", "description": "HAND-WRITTEN and preserved across `--write`: why this NAME may depart from `events.md` §Naming's `domain.verb-ed` convention. NAMING RATIONALE ONLY — never a statement about the event's semantics or payload. \"artifact event\" names the RFC 0171 §A.2 exception category in which the past-tense rule is relaxed and a noun form is permitted; it does NOT mean the event is about artifacts. Read beside `notes`: different provenance, neither complete alone." }
37
37
  }
38
38
  }
39
39
  }
package/spec/v2/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # `spec/v2/` — the OpenWOP v2 tree (the current protocol major)
2
2
 
3
- > **Status: released.** v2 is the current protocol major — `v2.0.0` was tagged 2026-09-05 and this tree is at corpus `v2.1.7` (`release.json`). Everything under `spec/v2/`, `schemas/v2/` and `api/v2/` is normative, is vendored into `@openwop/spec-artifacts`, and is what `@openwop/openwop-conformance` 2.x measures. A new integration targets v2.
3
+ > **Status: released.** v2 is the current protocol major — `v2.0.0` was tagged 2026-09-05 and this tree is at corpus `v2.2.0` (`release.json`). Everything under `spec/v2/`, `schemas/v2/` and `api/v2/` is normative, is vendored into `@openwop/spec-artifacts`, and is what `@openwop/openwop-conformance` 2.x measures. A new integration targets v2.
4
4
  >
5
5
  > **v1 is not retired.** Through the overlap a host advertises both majors and `preferredVersion` MUST remain a `1.x` member (`core/versioning.md` §1.1); v1 clients keep working unchanged on `/v1/…`. v1 end-of-support is the later of two clocks in `core/overview.md`, earliest 2026-12-04, and until then `spec/v1/` stays the maintained parallel track. A `1.x` conformance tarball still excludes this tree (`conformance/scripts/pack-vendor.sh`).
6
6
  >
@@ -85,7 +85,7 @@ Vendor events appear in `debug` only. In a mixed mode the host emits the union o
85
85
 
86
86
  ### SSE frames
87
87
 
88
- Each frame carries `id:`, `event:` and `data:`: `id:` is the `sequence`, `event:` is the v2 `type` (single mode), `data:` is the `RunEventDoc`. Two frame names are not types and are not in the `type` enum: `state.snapshot` (the `values` frame, whose `data:` is a `RunSnapshot`) and `batch` (the `bufferMs` frame, whose `data:` is an array of `RunEventDoc`). 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`).
88
+ 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`).
89
89
 
90
90
  `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.
91
91
 
@@ -1,6 +1,6 @@
1
1
  # Headers
2
2
 
3
- > **Status: Stable · v2.1.7 (2026-09-13) · RFC 0171 §C.1, RFC 0172 §A.3–§A.4.** GENERATED by `scripts/derive-v2-api.py` from `api/v2/openapi.yaml`; do not edit.
3
+ > **Status: Stable · v2.2.0 (2026-09-16) · RFC 0171 §C.1, RFC 0172 §A.3–§A.4.** GENERATED by `scripts/derive-v2-api.py` from `api/v2/openapi.yaml`; do not edit.
4
4
 
5
5
  ## Why this exists
6
6
 
@@ -101,24 +101,22 @@ Every id field in every v2 schema and every `api/v2/openapi.yaml` parameter and
101
101
 
102
102
  | Kinds | Grammar | Minted |
103
103
  | --- | --- | --- |
104
- | `runId`, `interruptId`, `subscriptionId`, `deliveryId`, `effectId` | tenant-bound `<tenantId>/<opaque>`: `^[A-Za-z0-9._~-]{1,128}/[A-Za-z0-9._~-]{16,128}$` | host |
104
+ | `runId`, `interruptId`, `subscriptionId`, `deliveryId`, `effectId` | tenant-bound: `^(anon:)?[A-Za-z0-9._~-]{1,128}/[A-Za-z0-9._~-]{16,128}$` | host |
105
105
  | `eventId` | `^[A-Za-z0-9._~-]{16,128}$` | host |
106
106
  | `tenantId`, `workspaceId` | `^[A-Za-z0-9._~-]{1,128}$` | host |
107
107
  | `subjectId` | `^[^\s/]{1,256}$` (the issuer's grammar) | host |
108
108
  | `traceId`, `spanId` | W3C `^[0-9a-f]{32}$`, `^[0-9a-f]{16}$` | host |
109
109
  | `keyId` | `^[A-Za-z0-9._~-]{1,128}$` (signing keys, resume-token `kid`, bundle signatures) | registry |
110
110
  | `nodeId`, `workflowId`, `agentId`, `chainId`, `pluginId`, `templateId`, `libraryId` | `^[A-Za-z0-9._~:-]{1,128}$` | author |
111
- | `typeId` | `^[a-z][a-z0-9-]*(\.[a-z][a-zA-Z0-9-]*)+$` | author |
111
+ | `typeId` | `^[a-z][a-z0-9_-]*(\.[a-z][a-zA-Z0-9_-]*)+$`, maxLength 256 | author |
112
112
 
113
- This obligation is enforced by `scripts/check-id-kinds-bound.mjs` against `spec/v2/id-field-bindings.json`, which places every `*Id` property in a v2 schema into one of two sets: it **is** one of the kinds above (and MUST `$ref` it), or nothing here governs it (with the reason recorded). A property in neither **fails**, so a new id field cannot be added without someone deciding which it is. The map exists rather than a name-matching rule because only 20 of the 88 `*Id` properties share a name with a kind: `childRunId` sat as `{type: string, minLength: 1}` in the same file where `parentRunId` was correctly bound, and a check keyed on names would have reported green over it. The rule above says *every id field*, not *every field whose name matches*.
113
+ `scripts/check-id-kinds-bound.mjs` enforces this against `spec/v2/id-field-bindings.json`, which sorts every `*Id` property in a v2 schema into two sets: it **is** a kind above (and MUST `$ref` it), or nothing here governs it (reason recorded). Neither **fails**, so no id field lands without someone deciding which it is. A map rather than a name rule because only 20 of 88 `*Id` properties share a name with a kind: `childRunId` sat as `{type: string, minLength: 1}` in the file where `parentRunId` was bound, and a name-keyed check reports green over that. *Every id field*, not every matching name.
114
114
 
115
- The `typeId` grammar admits `_` because `node-pack-manifest.schema.json`'s `name` pattern does and a pack's node type ids are derived from its name — a pack legally named `vendor.acme.my_tools` MUST be able to declare `vendor.acme.my_tools.echo`. A kind that rejects an id a legal name generates is a constraint that cannot express a legitimate value.
115
+ A host MUST reject a tenant-bound id whose tenant segment is not the caller's with `403` `id_tenant_mismatch`. A host-minted opaque segment MUST match `^[A-Za-z0-9._~-]{16,128}$`.
116
116
 
117
- A host MUST reject a tenant-bound id whose tenant segment is not the caller's with `403` `id_tenant_mismatch`. A host-minted opaque segment MUST match `^[A-Za-z0-9._~-]{16,128}$`: no `@`, no whitespace, no `/`.
117
+ **On the wire a tenant-bound id is one path segment, projected** (RFC 0184): every UTF-8 byte outside `[A-Za-z0-9._-]` becomes `~` plus two uppercase hex digits, so `acme/r-9f3c…` travels as `acme~2Fr-9f3c…`. A host MUST emit it in every link and MUST accept it on every tenant-bound parameter; it MUST still accept `tenant%2Fopaque`, and MUST decode either before matching the grammar. A host MUST NOT mint a tenant-bound id containing `~`; ids already minted MUST still resolve. A host MUST project exactly once, where an id leaves it, and MUST NOT re-encode its own output. `~` is the escape, not `%`, because RFC 3986 §2.3 makes it unreserved — no intermediary may rewrite it (`ids.schema.json` records what `%2F` costs).
118
118
 
119
- **On the wire a tenant-bound id is one percent-encoded path segment** (`tenant%2Fopaque`); a host MUST decode before matching the grammar and MUST accept the bound form on every tenant-bound parameter.
120
-
121
- **Through the overlap the bare form is admitted on a major-2 path parameter** — a stated affordance with an expiry, not a legacy branch of the grammar. A parameter carrying only the opaque segment (the spelling a `/v1/` create hands out) MUST resolve under the caller's tenant and never another's, and the response MUST name the resource bound (`versioning.md` §5); the credential supplies the segment the `403` check would read. Once a host advertises no `1.x` member it MUST refuse the bare form `400 validation_error` (not `id_tenant_mismatch`, not `not_found`). Ids in documents and bodies are bound, always. A client MAY bind at its request seam. Handle grammars (`memoryRef`, workspace `path`/`etag`, the plugin version token) and their `resolvability` class are specified where each handle is used; an importer MUST re-mint every `host`-scoped handle (`spec/v2/ext/portability/`).
119
+ **Through the overlap the bare form is admitted on a major-2 path parameter** — an affordance with an expiry, not a branch of the grammar. A parameter carrying only the opaque segment (what a `/v1/` create hands out) MUST resolve under the caller's tenant and never another's, and the response MUST name the resource bound (`versioning.md` §5); the credential supplies the segment the `403` check would read. Once a host advertises no `1.x` member it MUST refuse the bare form `400 validation_error` (not `id_tenant_mismatch`, not `not_found`). Ids in documents and bodies are bound, always. A client MAY bind at its request seam. Handle grammars (`memoryRef`, workspace `path`/`etag`, the plugin version token) and their `resolvability` class are specified where each handle is used; an importer MUST re-mint every `host`-scoped handle (`spec/v2/ext/portability/`).
122
120
 
123
121
  ## 6. Identity error codes (`spec/v2/errors.json`)
124
122
 
@@ -925,7 +925,10 @@
925
925
  ],
926
926
  "floorScenarios": [],
927
927
  "requirementIds": [],
928
- "owningRfc": "0064"
928
+ "owningRfc": "0064",
929
+ "normativeText": [
930
+ "spec/v1/host-capabilities.md"
931
+ ]
929
932
  },
930
933
  {
931
934
  "key": "toolCatalog",
@@ -945,7 +948,10 @@
945
948
  ],
946
949
  "floorScenarios": [],
947
950
  "requirementIds": [],
948
- "owningRfc": "0078"
951
+ "owningRfc": "0078",
952
+ "normativeText": [
953
+ "spec/v1/tool-catalog.md"
954
+ ]
949
955
  },
950
956
  {
951
957
  "key": "httpClient",
@@ -253,6 +253,15 @@
253
253
  },
254
254
  "facetsFrom": {
255
255
  "type": "string"
256
+ },
257
+ "normativeText": {
258
+ "type": "array",
259
+ "minItems": 1,
260
+ "items": {
261
+ "type": "string",
262
+ "minLength": 1
263
+ },
264
+ "description": "Repo-relative path(s) to the document(s) where this family's normative text is actually written. The `section` pointer names the DECLARATION site (core/capabilities.md), which for most families is a stub naming an owning RFC; this names where a reader finds the behaviour. A path under `spec/v1/` means the family is orphaned at v1 end-of-support \u2014 counted and ratcheted by scripts/check-v2-normative-home.mjs."
256
265
  }
257
266
  }
258
267
  }
@@ -102,10 +102,12 @@
102
102
  "type": "array",
103
103
  "items": {
104
104
  "type": "string"
105
- }
105
+ },
106
+ "description": "GENERATED by scripts/generate-event-codemap.mjs and rewritten on every `--write`: the mechanical reasons the v2 NAME differs from the v1 one (segments folded, case or separator normalised, tense not past). Naming only."
106
107
  },
107
108
  "note": {
108
- "type": "string"
109
+ "type": "string",
110
+ "description": "HAND-WRITTEN and preserved across `--write`: why this NAME may depart from `events.md` §Naming's `domain.verb-ed` convention. NAMING RATIONALE ONLY — never a statement about the event's semantics or payload. \"artifact event\" names the RFC 0171 §A.2 exception category in which the past-tense rule is relaxed and a noun form is permitted; it does NOT mean the event is about artifacts. Read beside `notes`: different provenance, neither complete alone."
109
111
  }
110
112
  }
111
113
  }
@@ -76,6 +76,7 @@
76
76
  "hostId": "no openwop id kind governs this surface; the grammar is the owning schema's own",
77
77
  "instanceId": "no openwop id kind governs this surface; the grammar is the owning schema's own",
78
78
  "invocationId": "no openwop id kind governs this surface; the grammar is the owning schema's own",
79
+ "itemIds": "no openwop id kind governs this surface; these are artifact-local item identifiers inside RefineFeedback (interrupt.md \u00a7RefineFeedback), opaque to the protocol",
79
80
  "leaseId": "no openwop id kind governs this surface; the grammar is the owning schema's own",
80
81
  "memoryId": "no openwop id kind governs this surface; the grammar is the owning schema's own",
81
82
  "mergedIds": "memory-entry ids (agentMemoryConsolidated); no openwop id kind governs memory entries",
@@ -26,7 +26,7 @@
26
26
  {
27
27
  "alias": "host.agentRuntime",
28
28
  "family": "agentRuntime",
29
- "publishedUses": 41,
29
+ "publishedUses": 29,
30
30
  "removalTrigger": "v1-end-of-support"
31
31
  },
32
32
  {
@@ -92,7 +92,7 @@
92
92
  {
93
93
  "alias": "host.fs",
94
94
  "family": "fs",
95
- "publishedUses": 2,
95
+ "publishedUses": 1,
96
96
  "removalTrigger": "v1-end-of-support"
97
97
  },
98
98
  {
@@ -128,7 +128,7 @@
128
128
  {
129
129
  "alias": "host.queueBus",
130
130
  "family": "queueBus",
131
- "publishedUses": 2,
131
+ "publishedUses": 1,
132
132
  "removalTrigger": "v1-end-of-support"
133
133
  },
134
134
  {
@@ -149,7 +149,7 @@
149
149
  "facets": [
150
150
  "memoryBackends"
151
151
  ],
152
- "publishedUses": 11,
152
+ "publishedUses": 9,
153
153
  "removalTrigger": "v1-end-of-support"
154
154
  },
155
155
  {
@@ -1,6 +1,6 @@
1
1
  {
2
- "$comment": "RFC 0172 \u00a7D.1 \u2014 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.1.7",
4
- "corpusTag": "v2.1.7",
5
- "updated": "2026-09-13"
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.2.0",
4
+ "corpusTag": "v2.2.0",
5
+ "updated": "2026-09-16"
6
6
  }