@openwop/openwop-conformance 2.4.0 → 2.4.2

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,35 @@
1
1
  # `@openwop/openwop-conformance` Changelog
2
2
 
3
+ ## [2.4.2] — 2026-09-18 — an id no host could ever witness
4
+
5
+ **Why a patch.** One assertion moves into its own `it`; no scenario file is
6
+ added or removed and no host obligation changes.
7
+
8
+ ### Fixed
9
+
10
+ - **`openwop.requirement.0173.pack-isolation.seam`** was minted inside
11
+ `v2-pack-isolation`'s shared `invoke()` helper.
12
+ `generate-requirement-registry.mjs` harvests `req(…)` only within an `it`, so
13
+ the id never reached `requirements.json` and no bundle could carry a row for
14
+ it — while `check-cut-gates.mjs` scans every `req(` in the source and went on
15
+ demanding one, failing RFC 0167 §G.2 on a row no host could supply. The seam
16
+ contract now has its own `it`, which is the copy the registry can see. The
17
+ helper keeps its `req(…)` call — RFC 0168 §A.1 makes `req(…)` the only
18
+ assertion-message form in `src/scenarios`, so the fix is the extra `it`, not
19
+ a quieter assertion.
20
+
21
+ ## [2.4.1] — 2026-09-18 — the leg that reads the host, not the schema
22
+
23
+ **Why a patch.** A leg is added to an existing scenario file; no file is added or removed.
24
+
25
+ ### Fixed
26
+
27
+ - **`v2-chain-pin-exact`** gains a third leg: an EXTERNAL `subChainRef` carrying
28
+ `version: "^1.0.0"`. The existing legs are satisfied by schema validation
29
+ alone (sabotage-proved: delete the reference host's pin rule and both stay
30
+ green); `SubChainRef.ref` types the external `version` as a semver range, so
31
+ this leg is the only one that reads the host's rule.
32
+
3
33
  ## [2.4.0] — 2026-09-18 — every bound kind, not just runId
4
34
 
5
35
  **Why a minor.** One scenario file is added (`PROTOCOL-STATUS.md` §suite version).
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "package": "@openwop/spec-artifacts",
3
- "version": "2.4.0",
4
- "stampSha256": "5b68f54de94dc0d6693867080f33f9004a4d4455648f899cd7ae8f7a6276ef38"
3
+ "version": "2.4.2",
4
+ "stampSha256": "cb840f92cef6dbff3beed39b860bde984adf1080e830dab5088487f2bf17d767"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "2.4.0",
3
+ "version": "2.4.2",
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.4.0"
59
+ "@openwop/spec-artifacts": "2.4.2"
60
60
  }
61
61
  }
package/requirements.json CHANGED
@@ -3,10 +3,10 @@
3
3
  "generatedFrom": "src/scenarios/*.test.ts",
4
4
  "counts": {
5
5
  "files": 571,
6
- "tests": 2172,
7
- "withStableId": 2172,
6
+ "tests": 2174,
7
+ "withStableId": 2174,
8
8
  "interpolatedTitles": 0,
9
- "explicitIds": 2125
9
+ "explicitIds": 2127
10
10
  },
11
11
  "records": [
12
12
  {
@@ -26310,7 +26310,7 @@
26310
26310
  {
26311
26311
  "id": "openwop.it.v2-chain-pin-exact.a-chain-reference-carrying-a-range-core-ai-callprompt-1-is-refused-at-register",
26312
26312
  "file": "v2-chain-pin-exact.test.ts",
26313
- "line": 102,
26313
+ "line": 116,
26314
26314
  "title": "a chain reference carrying a range (core.ai.callPrompt@^1) is refused at register",
26315
26315
  "explicitId": "openwop.requirement.0177.chain-pin-exact.range-refused",
26316
26316
  "citations": [
@@ -26325,10 +26325,28 @@
26325
26325
  }
26326
26326
  ]
26327
26327
  },
26328
+ {
26329
+ "id": "openwop.it.v2-chain-pin-exact.an-external-sub-chain-reference-carrying-a-range-is-refused-the-one-pin-the-sche",
26330
+ "file": "v2-chain-pin-exact.test.ts",
26331
+ "line": 125,
26332
+ "title": "an EXTERNAL sub-chain reference carrying a range is refused — the one pin the schema cannot catch",
26333
+ "explicitId": "openwop.requirement.0177.chain-pin-exact.external-range-refused",
26334
+ "citations": [
26335
+ {
26336
+ "section": "workflow-chain-packs.md §\"Exact pins\" (RFC 0177 §E.1)",
26337
+ "requirement": null,
26338
+ "interpolated": true
26339
+ },
26340
+ {
26341
+ "section": "workflow-chain-packs.md §\"Exact pins\" (RFC 0177 §E.1)",
26342
+ "requirement": "the refusal MUST carry an error code in the canonical envelope"
26343
+ }
26344
+ ]
26345
+ },
26328
26346
  {
26329
26347
  "id": "openwop.it.v2-chain-pin-exact.a-chain-reference-pinning-an-exact-version-core-ai-callprompt-1-0-0-registers",
26330
26348
  "file": "v2-chain-pin-exact.test.ts",
26331
- "line": 111,
26349
+ "line": 140,
26332
26350
  "title": "a chain reference pinning an exact version (core.ai.callPrompt@1.0.0) registers",
26333
26351
  "explicitId": "openwop.requirement.0177.chain-pin-exact.exact-accepted",
26334
26352
  "citations": [
@@ -27663,7 +27681,7 @@
27663
27681
  {
27664
27682
  "id": "openwop.it.v2-pack-isolation.sandbox-isolationmodel-names-a-real-mechanism-never-node-vm",
27665
27683
  "file": "v2-pack-isolation.test.ts",
27666
- "line": 70,
27684
+ "line": 78,
27667
27685
  "title": "sandbox.isolationModel names a real mechanism, never node:vm",
27668
27686
  "explicitId": "openwop.requirement.0173.pack-isolation",
27669
27687
  "citations": [
@@ -27678,10 +27696,24 @@
27678
27696
  }
27679
27697
  ]
27680
27698
  },
27699
+ {
27700
+ "id": "openwop.it.v2-pack-isolation.the-sandbox-invoke-seam-answers-the-seam-contract",
27701
+ "file": "v2-pack-isolation.test.ts",
27702
+ "line": 92,
27703
+ "title": "the sandbox-invoke seam answers the seam contract",
27704
+ "explicitId": "openwop.requirement.0173.pack-isolation.seam",
27705
+ "citations": [
27706
+ {
27707
+ "section": "host-sample-test-seams.md §8",
27708
+ "requirement": null,
27709
+ "interpolated": true
27710
+ }
27711
+ ]
27712
+ },
27681
27713
  {
27682
27714
  "id": "openwop.it.v2-pack-isolation.node-pack-sandbox-fs-gated-a-host-filesystem-read-is-refused",
27683
27715
  "file": "v2-pack-isolation.test.ts",
27684
- "line": 84,
27716
+ "line": 104,
27685
27717
  "title": "node-pack-sandbox-fs-gated: a host filesystem read is refused",
27686
27718
  "explicitId": null,
27687
27719
  "citations": []
@@ -27689,7 +27721,7 @@
27689
27721
  {
27690
27722
  "id": "openwop.it.v2-pack-isolation.node-pack-sandbox-fs-gated-a-host-filesystem-write-is-refused",
27691
27723
  "file": "v2-pack-isolation.test.ts",
27692
- "line": 91,
27724
+ "line": 111,
27693
27725
  "title": "node-pack-sandbox-fs-gated: a host filesystem write is refused",
27694
27726
  "explicitId": null,
27695
27727
  "citations": []
@@ -27697,7 +27729,7 @@
27697
27729
  {
27698
27730
  "id": "openwop.it.v2-pack-isolation.node-pack-sandbox-no-env-the-host-environment-does-not-leak",
27699
27731
  "file": "v2-pack-isolation.test.ts",
27700
- "line": 98,
27732
+ "line": 118,
27701
27733
  "title": "node-pack-sandbox-no-env: the host environment does not leak",
27702
27734
  "explicitId": null,
27703
27735
  "citations": []
@@ -27705,7 +27737,7 @@
27705
27737
  {
27706
27738
  "id": "openwop.it.v2-pack-isolation.node-pack-sandbox-network-gated-ungated-network-egress-is-refused",
27707
27739
  "file": "v2-pack-isolation.test.ts",
27708
- "line": 105,
27740
+ "line": 125,
27709
27741
  "title": "node-pack-sandbox-network-gated: ungated network egress is refused",
27710
27742
  "explicitId": null,
27711
27743
  "citations": []
@@ -27713,7 +27745,7 @@
27713
27745
  {
27714
27746
  "id": "openwop.it.v2-pack-isolation.node-pack-sandbox-no-process-host-process-access-is-refused",
27715
27747
  "file": "v2-pack-isolation.test.ts",
27716
- "line": 112,
27748
+ "line": 132,
27717
27749
  "title": "node-pack-sandbox-no-process: host process access is refused",
27718
27750
  "explicitId": null,
27719
27751
  "citations": []
@@ -27721,7 +27753,7 @@
27721
27753
  {
27722
27754
  "id": "openwop.it.v2-pack-isolation.node-pack-sandbox-timeout-exceeding-wallclocklimitms-fails-with-sandbox-timeout",
27723
27755
  "file": "v2-pack-isolation.test.ts",
27724
- "line": 119,
27756
+ "line": 139,
27725
27757
  "title": "node-pack-sandbox-timeout: exceeding wallClockLimitMs fails with sandbox_timeout",
27726
27758
  "explicitId": "openwop.requirement.0173.pack-isolation.timeout",
27727
27759
  "citations": [
@@ -27734,7 +27766,7 @@
27734
27766
  {
27735
27767
  "id": "openwop.it.v2-pack-isolation.node-pack-sandbox-memory-cap-exceeding-memorylimitbytes-fails-with-sandbox-memor",
27736
27768
  "file": "v2-pack-isolation.test.ts",
27737
- "line": 126,
27769
+ "line": 146,
27738
27770
  "title": "node-pack-sandbox-memory-cap: exceeding memoryLimitBytes fails with sandbox_memory_exceeded",
27739
27771
  "explicitId": "openwop.requirement.0173.pack-isolation.memory-cap",
27740
27772
  "citations": [
@@ -27747,7 +27779,7 @@
27747
27779
  {
27748
27780
  "id": "openwop.it.v2-pack-isolation.node-pack-sandbox-isolated-context-a-pack-cannot-mutate-state-another-invocation",
27749
27781
  "file": "v2-pack-isolation.test.ts",
27750
- "line": 133,
27782
+ "line": 153,
27751
27783
  "title": "node-pack-sandbox-isolated-context: a pack cannot mutate state another invocation sees",
27752
27784
  "explicitId": "openwop.requirement.0173.pack-isolation.isolated-context",
27753
27785
  "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.4.0",
5
- "corpusTag": "v2.4.0",
4
+ "version": "2.4.2",
5
+ "corpusTag": "v2.4.2",
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": "d7297c933f4987c085f1427b1cb245c0e7f88d0ab91a1d1534c7959bb659940e",
13
- "api/v2/asyncapi.yaml": "5730b64974958076b2da1e77990011228465c088334a8a5c6005e136cf86e20d",
14
- "api/v2/openapi.yaml": "cc94e454854f28d7aae5f0cab264a21dfdc8ce43259dfd30ea4e2340ede756fc",
12
+ "api/seams-v2.yaml": "2a94a4195377913107f884aacd48605e71804f151333de8a8a0abc7b2799c5e2",
13
+ "api/v2/asyncapi.yaml": "930bd4ea705b35ec37605f139a0be7b08b39d8d9c7628c9756c4fdbe47f4655f",
14
+ "api/v2/openapi.yaml": "33298f7981473111ce767695367e44fa3eaa3cbdda9a52badc2b198efcd8259d",
15
15
  "api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
16
16
  "schemas/README.md": "0c0b737ffcf8f30e7d2809cec8a498232de710f41443212922ad8337cdde0b51",
17
17
  "schemas/a2a-task-state.schema.json": "c9365918f993f943b4b619d42551eb066a1ed33a08d895d51b395432a5b1f1bc",
@@ -200,7 +200,7 @@
200
200
  "schemas/workspace-file.schema.json": "464de85c2a068243084ee9c1d969bc7cd5d8f7948574e58450d6493c38a0e1e4",
201
201
  "spec/v1/alias-detectors.json": "e90916db12d2297e2e560b2d54d2c4702d6cb3bef7179a575844687a7ec17262",
202
202
  "spec/v1/capability-declaration-classes.json": "e7729aed5c4b4e1dd02abab0530f14cc95f5d4070fe51fb139e7f5cccefa00c6",
203
- "spec/v1/core-standard-manifest.json": "41af65d02636d651a99f0f48caa5dd5ed45be576f745166bd1510908674997e0",
203
+ "spec/v1/core-standard-manifest.json": "1575d6d8b801b3d84c65c63d55a4f0563a3350a754e403ee06ffb853b996b639",
204
204
  "spec/v1/deprecations.json": "c81c8f71460936c8d489d8270477c7661cf4a5d7b7bad86c98899750b5656027",
205
205
  "spec/v1/deprecations.schema.json": "18c87e78bedc210431f795ae44c5b5d202f2f3317850d5cf86867d4f1fa1cdfb",
206
206
  "spec/v1/event-codemap.json": "3da60d884157793a360da532a9fcbbfb5285636db325a74cec94b34622186d97",
@@ -219,7 +219,7 @@
219
219
  "spec/v2/core/errors.md": "ad85fc830139e587f20deabf88297b91b1fb6ce31e80d5442624a650246ab16e",
220
220
  "spec/v2/core/events.md": "f1bac16419740f3b722febb6f5caaabed8838e092cba3d080fe9184024049dcf",
221
221
  "spec/v2/core/form-content-packs.md": "6ffaae85d4550be48dd53fb6e2959af8aceb35bd87c5343b722885084f197713",
222
- "spec/v2/core/headers.md": "6f350e4a6893e82fcab40aec6cb515b989e8b56a20a74f6d10d24a0d3fdf5cf0",
222
+ "spec/v2/core/headers.md": "d34646a59632a79e9907439d0521c90e613ed968578af02129bad7002063de99",
223
223
  "spec/v2/core/idempotency.md": "b86c352d964aebad532727f314e26ed2177d49de382076fa64d3162e2f56118d",
224
224
  "spec/v2/core/identity.md": "9d2b823eb899463807452fe5721dcf09881837c161be024175957db27870b243",
225
225
  "spec/v2/core/interop.md": "d4c404e469e1d9d9be88bc2c939d777e7d7c1d0419b1251c18770f039838a401",
@@ -271,8 +271,8 @@
271
271
  "spec/v2/path-manifest.json": "a6f4e652e866889e8bde815874062f62929aee95dc8e58e7152c56adee262163",
272
272
  "spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
273
273
  "spec/v2/profiles.json": "0636f19fceae625390003a347e70ef4797d84766b5c24ce8a02cea52aadebca4",
274
- "spec/v2/release.json": "975e8d6100d4c0697384dd093eb586eed3f41481a26b954e052fa321525a0273",
274
+ "spec/v2/release.json": "4fdf29fe944e63528cdf5dc108e22e5006b2d7fcb1349c33caee85a1cacdd81e",
275
275
  "spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624"
276
276
  },
277
- "corpusCommit": "5f410d86a87e25473f8e1d63ce08e9b0947c7e43"
277
+ "corpusCommit": "164711f69c396daffb6a300a94e89242640b4087"
278
278
  }
@@ -76,6 +76,20 @@ function chainPack(name: string, reference: string): Record<string, unknown> {
76
76
  }],
77
77
  };
78
78
  }
79
+ /**
80
+ * The same fixture with an EXTERNAL sub-chain reference. `SubChainRef.ref`
81
+ * oneOf[1] types `version` as a semver RANGE, so — unlike the node reference
82
+ * above — the schema cannot refuse a range here. A host either carries the
83
+ * §Exact pins rule or it does not, and this is the only shape that tells the
84
+ * two apart.
85
+ */
86
+ function externalRefPack(name: string, version: string): Record<string, unknown> {
87
+ const pack = chainPack(name, 'core.ai.callPrompt@1.0.0');
88
+ const chain = (pack['chains'] as Array<Record<string, unknown>>)[0] as Record<string, unknown>;
89
+ chain['subChains'] = [{ ref: { packName: 'core.openwop.v2-pin-external', chainId: 'c.remote', version } }];
90
+ return pack;
91
+ }
92
+
79
93
  async function publish(manifest: Record<string, unknown>) {
80
94
  const name = manifest['name'] as string;
81
95
  const version = manifest['version'] as string;
@@ -108,6 +122,21 @@ describe('v2-chain-pin-exact (RFC 0177 §E.1)', () => {
108
122
  expect(typeof readErrorCode(res.json), req('openwop.requirement.0177.chain-pin-exact.range-refused', SECTION, 'the refusal MUST carry an error code in the canonical envelope')).toBe('string');
109
123
  });
110
124
 
125
+ it('an EXTERNAL sub-chain reference carrying a range is refused — the one pin the schema cannot catch', async () => {
126
+ const skip = await preflight();
127
+ if (skip) return softSkip(skip.kind, skip.reason);
128
+ // Measured 2026-09-18 on both production hosts: the external branch is
129
+ // typed, reachable and UNEXERCISED — every shipped pack uses the sibling
130
+ // string form — so nothing on either host would have noticed a range here.
131
+ // The two legs above are satisfied by schema validation alone (sabotage on
132
+ // the reference host: delete its pin rule and they stay green), which is
133
+ // why this leg exists: it is the only one that reads the HOST's rule.
134
+ const res = await publish(externalRefPack(freshName('chain-ext-range'), '^1.0.0'));
135
+ if (res.status === 404) return softSkip('blocked', 'packs-test publish seam answered 404 — seams profile advertised but the seam is not mounted');
136
+ expect(res.status >= 400 && res.status < 500, req('openwop.requirement.0177.chain-pin-exact.external-range-refused', SECTION, `an external reference carrying a range MUST be refused at register — the schema types this field as a semver range, so only the host's rule can refuse it (got ${res.status})`)).toBe(true);
137
+ expect(typeof readErrorCode(res.json), req('openwop.requirement.0177.chain-pin-exact.external-range-refused', SECTION, 'the refusal MUST carry an error code in the canonical envelope')).toBe('string');
138
+ });
139
+
111
140
  it('a chain reference pinning an exact version (core.ai.callPrompt@1.0.0) registers', async () => {
112
141
  const skip = await preflight();
113
142
  if (skip) return softSkip(skip.kind, skip.reason);
@@ -57,6 +57,14 @@ async function invoke(typeId: string, extra: Record<string, unknown> = {}): Prom
57
57
  if (!doc || !seamsProfileAdvertised(doc)) { softSkip('inapplicable', `the ${typeId} leg is seam-driven — seams profile (conformance.seamsProfile = openwop-conformance-seams-v2) not advertised`); return null; }
58
58
  const res = await driver.post(INVOKE, { typeId, ...extra });
59
59
  if (res.status === 404 || res.status === 403 || res.status === 405) { seamAbsent(`host advertises packs + sandbox but ${INVOKE} answered ${res.status} — the ${typeId} leg is unobservable (host-sample-test-seams.md §8)`); return null; }
60
+ // The same id is ALSO asserted by its own `it` above, and that is the copy the
61
+ // registry can see: `generate-requirement-registry.mjs` harvests `req(…)` only
62
+ // within an `it`, so this helper-level call alone left the id out of
63
+ // requirements.json — and no bundle could carry a row for it — while
64
+ // `check-cut-gates.mjs`, which scans every `req(` in the source, went on
65
+ // demanding one. §G.2 failed on a row no host could supply. RFC 0168 §A.1
66
+ // requires `req(…)` here rather than a bare describe, so the fix is the extra
67
+ // `it`, not a quieter assertion.
60
68
  expect(res.status, req('openwop.requirement.0173.pack-isolation.seam', 'host-sample-test-seams.md §8', `${INVOKE} MUST answer 200 { result } | 200 { error } for ${typeId}`)).toBe(200);
61
69
  return (res.json ?? {}) as InvokeResult;
62
70
  }
@@ -81,6 +89,18 @@ describe('RFC 0173 §B — pack-isolation (gated on packs + sandbox)', () => {
81
89
  ).toContain(model);
82
90
  });
83
91
 
92
+ it('the sandbox-invoke seam answers the seam contract', async () => {
93
+ if (!(await gated())) return softSkip('inapplicable', 'gate not met (reason recorded above)');
94
+ const doc = await discovery();
95
+ if (!doc || !seamsProfileAdvertised(doc)) return softSkip('inapplicable', 'seam-driven — seams profile (conformance.seamsProfile = openwop-conformance-seams-v2) not advertised');
96
+ const res = await driver.post(INVOKE, { typeId: 'misbehave.fs-escape-read' });
97
+ if (res.status === 404 || res.status === 403 || res.status === 405) return seamAbsent(`host advertises packs + sandbox but ${INVOKE} answered ${res.status}`);
98
+ expect(
99
+ res.status,
100
+ req('openwop.requirement.0173.pack-isolation.seam', 'host-sample-test-seams.md §8', `${INVOKE} MUST answer 200 { result } | 200 { error } — the eight behavioural legs are unobservable otherwise`),
101
+ ).toBe(200);
102
+ });
103
+
84
104
  it('node-pack-sandbox-fs-gated: a host filesystem read is refused', async () => {
85
105
  if (!(await gated())) return softSkip('inapplicable', 'gate not met (reason recorded above)');
86
106
  const r = await invoke('misbehave.fs-escape-read');