@openwop/openwop-conformance 2.45.21 → 2.45.22

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # `@openwop/openwop-conformance` Changelog
2
2
 
3
+ ## [2.45.22] — 2026-10-06 — an empty `dataResidency.regions` still binds the reject leg; RFC 0236's registers close
4
+
5
+ - **No scenario change.** RFC 0236 goes `Accepted` on the v2 reference host's certified 2.45.21 cut; its gaps G1 and G2 close, which changes the packed `spec/v1/gaps.json`.
6
+
7
+ - **`v2-data-residency-admission`: an empty `regions` list no longer skips the reject leg.** A host advertising `dataResidency` with no region honours residency nowhere, so it MUST refuse every constraint. Both legs used to skip on `[]`, so a host that accepted and ignored every constraint passed; a double now fails it. The accept leg stays `inapplicable` there.
8
+
3
9
  ## [2.45.21] — 2026-10-05 — `0236.webhook.host-variant` gets its own row
4
10
 
5
11
  - **`v2-host-event-delivery`: no-fan-out is its own `it`.** A bundle records one requirement id per `it` (the last one asserted), so the webhook `it` that asserted host-variant and then no-fan-out recorded only no-fan-out. Found by the v2 reference host's 2.45.20 loopback rehearsal: five of the six RFC 0236 ids appeared. The new `it` re-runs the same gate, so a host without the families records the same disposition.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "package": "@openwop/spec-artifacts",
3
- "version": "2.45.21",
4
- "stampSha256": "ee91d9e41fbcdc73f379cf977fee5ac3e44590a80349c693da2cc5f3a566c68b"
3
+ "version": "2.45.22",
4
+ "stampSha256": "ef94915e768fcf83164913c8fcabb237515e2b7538d2ae93bc7dde8ae7ae79ad"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "2.45.21",
3
+ "version": "2.45.22",
4
4
  "description": "Production-ready black-box conformance suite for OpenWOP v1.0 compliant servers.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -56,6 +56,6 @@
56
56
  "@openwop/spec-artifacts": "file:../spec-artifacts"
57
57
  },
58
58
  "peerDependencies": {
59
- "@openwop/spec-artifacts": "2.45.21"
59
+ "@openwop/spec-artifacts": "2.45.22"
60
60
  }
61
61
  }
package/requirements.json CHANGED
@@ -30103,7 +30103,7 @@
30103
30103
  {
30104
30104
  "id": "openwop.it.v2-data-residency-admission.a-region-outside-dataresidency-regions-answers-422-residency-unavailable-and-cre",
30105
30105
  "file": "v2-data-residency-admission.test.ts",
30106
- "line": 66,
30106
+ "line": 73,
30107
30107
  "title": "a region outside dataResidency.regions answers 422 residency_unavailable and creates no run",
30108
30108
  "explicitId": "openwop.requirement.data-residency.reject-unadvertised-region",
30109
30109
  "citations": [
@@ -30126,7 +30126,7 @@
30126
30126
  {
30127
30127
  "id": "openwop.it.v2-data-residency-admission.a-region-inside-dataresidency-regions-is-accepted",
30128
30128
  "file": "v2-data-residency-admission.test.ts",
30129
- "line": 82,
30129
+ "line": 89,
30130
30130
  "title": "a region inside dataResidency.regions is accepted",
30131
30131
  "explicitId": "openwop.requirement.data-residency.accept-advertised-region",
30132
30132
  "citations": [
@@ -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.45.21",
5
- "corpusTag": "v2.45.21",
4
+ "version": "2.45.22",
5
+ "corpusTag": "v2.45.22",
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": "ac6de5303872664b78ff419c035915593e01599e02835e777a0123de1baeb398",
11
11
  "api/redocly.yaml": "b0604c89b2ca6d5076ec25725c539dad44a741a811fe524439ee6daef8baa09f",
12
- "api/seams-v2.yaml": "b589c75cf872617be98ffe553cd4e738b03469d26ee1e8a139d07cb185e61be1",
13
- "api/v2/asyncapi.yaml": "a0af325b50f06b2c493d5e7e6cab81cc7af3928065d9c12560a32029b1983a78",
14
- "api/v2/openapi.yaml": "173a39da5f735c10b9e24c3582d7aeb05c42e4c221761dfc940e894d9f0728fd",
12
+ "api/seams-v2.yaml": "a3a2dd8f2691fe7fa831af534f41789abaea6e5a03ededf88dd4bc96803cdaea",
13
+ "api/v2/asyncapi.yaml": "587bab6ff008b0bb1d3144029bc7ed4c3badc52ac916a0afd49d6c85923bbe1b",
14
+ "api/v2/openapi.yaml": "33a60f1454fb0efeb22c667ecf505447775a0de84772b1003c3226a6e419685b",
15
15
  "api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
16
16
  "schemas/README.md": "a829e3208413062388e52ab6ba714d35889c9c7645db38a290b0c4f4992cd3da",
17
17
  "schemas/a2a-task-state.schema.json": "75d5049dea8bd873ff0e7546f1c60c8d36c219bec7084264360be54a8be30ae1",
@@ -208,14 +208,14 @@
208
208
  "schemas/workspace-file.schema.json": "464de85c2a068243084ee9c1d969bc7cd5d8f7948574e58450d6493c38a0e1e4",
209
209
  "spec/v1/alias-detectors.json": "0b2f28808ffe0b9d98a187775038c3406bb70919747a788a6e4f866e293ae6d3",
210
210
  "spec/v1/capability-declaration-classes.json": "e7729aed5c4b4e1dd02abab0530f14cc95f5d4070fe51fb139e7f5cccefa00c6",
211
- "spec/v1/core-standard-manifest.json": "9aa532383ef7f21117d806a74796680a296c8963f5dae666600f318d560ec25c",
211
+ "spec/v1/core-standard-manifest.json": "b1d45d8fc106392a46ea67fd227bcdf623454ec0499e115487ed4e8dc5647d7f",
212
212
  "spec/v1/deprecations.json": "a3293072ea951857395d05f0437264e88965f1e9019ff61c55bacfa9c1aaa17c",
213
213
  "spec/v1/deprecations.schema.json": "3e393c405d2a41b467d8c5e3c468549078df1ce6a6d2588fc488097b95d9b55b",
214
214
  "spec/v1/end-of-support.json": "35f0bf3b70fe78b16e2a2fd81a153fc27e703f6c16376c6fe7cdf1f4f2c4e6d4",
215
215
  "spec/v1/event-codemap.json": "3da60d884157793a360da532a9fcbbfb5285636db325a74cec94b34622186d97",
216
216
  "spec/v1/event-codemap.schema.json": "d05933b2e88103aff51a2f774df97b9bda065e0fc88dbd9b114cc47a32189174",
217
217
  "spec/v1/extensions.json": "79a60754aa16cbdbdb604f8e5c00038af535a1690a4d07bd5ff7c38f0256a7bc",
218
- "spec/v1/gaps.json": "b745aa2620ae8c9f79e7df7bd7c81559b8929f0bdfae62a7ea15debc29e8f19a",
218
+ "spec/v1/gaps.json": "335186074848531561262525967bd85b379b3137a2233dd4c8b63914222dfa71",
219
219
  "spec/v1/gaps.schema.json": "8fd83259f556553c9df0f53e7a82ca8c2a4771d471197f69b4e2ae0ceeacfd66",
220
220
  "spec/v1/migrations.json": "4c50760be70cb644d02427bcaf5749606700edf4513f423903cf3b1d12f5c300",
221
221
  "spec/v1/migrations.schema.json": "886779aa6c22e646db097f5df210adb018a4dd14a7b815465a18c8a7056c8f72",
@@ -311,9 +311,9 @@
311
311
  "spec/v2/path-manifest.json": "b84c7561f50c7df0cb61b202bd6595acad3ee7edca7a45f711949bea3c69bd27",
312
312
  "spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
313
313
  "spec/v2/profiles.json": "180beb9ef2d161766fa23b2987e7e829851e3d316273147cf6a4a3f324befd6b",
314
- "spec/v2/release.json": "839927946b61bb164d5b55121ccf51c7e109b8f444dcb09a5de0412ebfb4031e",
314
+ "spec/v2/release.json": "72fec975046a00db77ff1666dc76ada8663cbd18366104e451091e3f82c02455",
315
315
  "spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624",
316
- "spec/v2/surface-baseline.json": "0b284b59b87c69acbaf06caff0da67bee0e4221ac8de1f3fd21cc9c5e5922f0f"
316
+ "spec/v2/surface-baseline.json": "12f193f56af68402c747503b465f8d7e18580ef832ac7b95c218ab06286478ea"
317
317
  },
318
- "corpusCommit": "7a395e42ec42b03017af5cadc2ef9006a606cef8"
318
+ "corpusCommit": "b768269329be09d06da2de25b668ce27d5cc51df"
319
319
  }
@@ -17,9 +17,14 @@
17
17
  * `inapplicable` rather than a pass. Physical confinement is an operator SHOULD
18
18
  * and cannot be observed on the wire.
19
19
  *
20
- * Dispositions: discovery unreadable ⇒ `blocked`; `dataResidency` absent or no
21
- * advertised region ⇒ `inapplicable`; a 429 ⇒ `blocked` (the run budget, not
22
- * the wire).
20
+ * An empty `regions` list still binds the reject leg: the host honours
21
+ * residency in no region, so every constraint MUST be refused. Until 2.45.22
22
+ * both legs skipped on an empty list, so a host advertising no region that
23
+ * silently accepted and ignored every constraint passed.
24
+ *
25
+ * Dispositions: discovery unreadable ⇒ `blocked`; `dataResidency` absent or a
26
+ * malformed `regions` ⇒ `inapplicable`; an empty `regions` ⇒ `inapplicable` for
27
+ * the accept leg only; a 429 ⇒ `blocked` (the run budget, not the wire).
23
28
  *
24
29
  * @see spec/v2/core/runs.md §Refusals
25
30
  * @see spec/v2/core/runs.md §`dataResidency`
@@ -49,9 +54,11 @@ async function advertisedRegions(): Promise<string[] | { disposition: 'blocked'
49
54
  if (!doc) return { disposition: 'blocked', reason: UNREADABLE };
50
55
  const rec = await familyAdvertised('dataResidency');
51
56
  if (!rec) return { disposition: 'inapplicable', reason: 'the host does not advertise dataResidency' };
52
- const regions = Array.isArray(rec['regions']) ? rec['regions'].filter((r): r is string => typeof r === 'string' && r.length > 0) : [];
53
- if (regions.length === 0) return { disposition: 'inapplicable', reason: 'dataResidency advertises no region (regions is empty or malformed; the record leg owns that)' };
54
- return regions;
57
+ // An empty list is a claim, not an absence: the host honours residency in no
58
+ // region, so it MUST refuse every residency constraint. Only a malformed list
59
+ // is left to the record leg.
60
+ if (!Array.isArray(rec['regions'])) return { disposition: 'inapplicable', reason: 'dataResidency.regions is not an array (the record leg owns that)' };
61
+ return rec['regions'].filter((r): r is string => typeof r === 'string' && r.length > 0);
55
62
  }
56
63
 
57
64
  /** Cancel a run the host created, so a failing row leaves nothing behind. */
@@ -82,7 +89,8 @@ describe('v2 dataResidency admission (runs.md §dataResidency)', () => {
82
89
  it('a region inside dataResidency.regions is accepted', async () => {
83
90
  const regions = await advertisedRegions();
84
91
  if (!Array.isArray(regions)) return softSkip(regions.disposition, regions.reason);
85
- const region = regions[0] as string;
92
+ const region = regions[0];
93
+ if (region === undefined) return softSkip('inapplicable', 'dataResidency.regions is empty: there is no advertised region to accept (the reject leg still binds)');
86
94
 
87
95
  const res = await http(() => driver.post('/runs', { workflowId: NOOP, residency: { region } }));
88
96
  if (res === null) return softSkip('blocked', 'POST /runs unreachable (fetch failed)');