@openwop/openwop-conformance 2.45.2 → 2.45.3

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +2 -2
  3. package/dist/cli.js +1 -1
  4. package/dist/lib/certification-bundle-verify.js +22 -1
  5. package/dist/lib/profiles.js +17 -2
  6. package/dist/lib/requirement-registry.js +15 -0
  7. package/dist/lib/scenario-disposition.js +50 -9
  8. package/dist/spec-artifacts.lock.json +2 -2
  9. package/fixtures/conformance-fs-probe.json +25 -0
  10. package/fixtures/conformance-queue-consume.json +27 -0
  11. package/fixtures/conformance-queue-publish.json +29 -0
  12. package/fixtures/conformance-safefetch-probe.json +23 -0
  13. package/fixtures/conformance-secret-resolve-then-fail.json +33 -0
  14. package/fixtures/conformance-storage-probe.json +27 -0
  15. package/fixtures/conformance-tool-scope-probe.json +27 -0
  16. package/fixtures/openwop-secrets-run-witness.json +29 -0
  17. package/fixtures.md +102 -1
  18. package/package.json +2 -2
  19. package/requirement-aliases.json +72 -71
  20. package/requirements.json +1011 -43
  21. package/scenario-majors.json +39 -3
  22. package/schemas/CORPUS-STAMP.json +22 -22
  23. package/src/cli.ts +1 -1
  24. package/src/lib/certification-bundle-verify.ts +23 -2
  25. package/src/lib/driver.ts +61 -2
  26. package/src/lib/memoryAttribution.ts +41 -6
  27. package/src/lib/polling.ts +2 -18
  28. package/src/lib/profiles.ts +26 -2
  29. package/src/lib/requirement-registry.ts +12 -0
  30. package/src/lib/run-secrets-witness.ts +240 -0
  31. package/src/lib/scenario-disposition.ts +45 -7
  32. package/src/lib/secret-scan.ts +141 -0
  33. package/src/lib/timeout-scale.ts +25 -0
  34. package/src/lib/triggerBridge.ts +69 -1
  35. package/src/scenarios/byok-roundtrip.test.ts +20 -4
  36. package/src/scenarios/runner-ledger.test.ts +6 -1
  37. package/src/scenarios/secrets-run-witness.test.ts +154 -0
  38. package/src/scenarios/trigger-bridge-delivery.test.ts +183 -126
  39. package/src/scenarios/trigger-refused-event-keeps-subscription.test.ts +141 -0
  40. package/src/scenarios/v2-eval-mode-unadvertised-refused.test.ts +55 -0
  41. package/src/scenarios/v2-fs-sandbox-escape-refused.test.ts +161 -0
  42. package/src/scenarios/v2-memory-cross-tenant-isolation.test.ts +113 -0
  43. package/src/scenarios/v2-queue-cross-tenant-isolation.test.ts +118 -0
  44. package/src/scenarios/v2-safefetch-ssrf-refused.test.ts +198 -0
  45. package/src/scenarios/v2-secret-canary-absent.test.ts +211 -0
  46. package/src/scenarios/v2-secrets-run-witness.test.ts +167 -0
  47. package/src/scenarios/v2-storage-cross-tenant-isolation.test.ts +213 -0
  48. package/src/scenarios/v2-tool-authorization-fail-closed.test.ts +197 -0
  49. package/src/scenarios/v2-workspace-scope-from-identity.test.ts +146 -0
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "$comment": "GENERATED by conformance/scripts/generate-scenario-majors.mjs (RFC 0168 §D.3). Do not edit; add a file to BOTH_MAJORS in the generator to target both majors.",
3
3
  "counts": {
4
- "files": 575,
5
- "v1": 452,
6
- "v2": 138
4
+ "files": 587,
5
+ "v1": 454,
6
+ "v2": 148
7
7
  },
8
8
  "majors": {
9
9
  "a2a-1-0-agent-card.test.ts": [
@@ -1149,6 +1149,9 @@
1149
1149
  "secret-leakage-otel-attribute.test.ts": [
1150
1150
  1
1151
1151
  ],
1152
+ "secrets-run-witness.test.ts": [
1153
+ 1
1154
+ ],
1152
1155
  "self-hosted-runner.test.ts": [
1153
1156
  1
1154
1157
  ],
@@ -1263,6 +1266,9 @@
1263
1266
  "trigger-ingestion.test.ts": [
1264
1267
  1
1265
1268
  ],
1269
+ "trigger-refused-event-keeps-subscription.test.ts": [
1270
+ 1
1271
+ ],
1266
1272
  "trigger-stream-cdc-sources.test.ts": [
1267
1273
  1
1268
1274
  ],
@@ -1386,6 +1392,9 @@
1386
1392
  "v2-error-registry.test.ts": [
1387
1393
  2
1388
1394
  ],
1395
+ "v2-eval-mode-unadvertised-refused.test.ts": [
1396
+ 2
1397
+ ],
1389
1398
  "v2-event-type-closed.test.ts": [
1390
1399
  2
1391
1400
  ],
@@ -1401,6 +1410,9 @@
1401
1410
  "v2-form-when-reuses-edge-conditions.test.ts": [
1402
1411
  2
1403
1412
  ],
1413
+ "v2-fs-sandbox-escape-refused.test.ts": [
1414
+ 2
1415
+ ],
1404
1416
  "v2-header-scheme.test.ts": [
1405
1417
  2
1406
1418
  ],
@@ -1452,6 +1464,9 @@
1452
1464
  "v2-mcp-tasks.test.ts": [
1453
1465
  2
1454
1466
  ],
1467
+ "v2-memory-cross-tenant-isolation.test.ts": [
1468
+ 2
1469
+ ],
1455
1470
  "v2-min-client-version.test.ts": [
1456
1471
  2
1457
1472
  ],
@@ -1515,6 +1530,9 @@
1515
1530
  "v2-provider-conflict.test.ts": [
1516
1531
  2
1517
1532
  ],
1533
+ "v2-queue-cross-tenant-isolation.test.ts": [
1534
+ 2
1535
+ ],
1518
1536
  "v2-refresh-sla.test.ts": [
1519
1537
  2
1520
1538
  ],
@@ -1566,12 +1584,24 @@
1566
1584
  "v2-run-snapshot-etag.test.ts": [
1567
1585
  2
1568
1586
  ],
1587
+ "v2-safefetch-ssrf-refused.test.ts": [
1588
+ 2
1589
+ ],
1590
+ "v2-secret-canary-absent.test.ts": [
1591
+ 2
1592
+ ],
1593
+ "v2-secrets-run-witness.test.ts": [
1594
+ 2
1595
+ ],
1569
1596
  "v2-sse-last-event-id-cursor.test.ts": [
1570
1597
  2
1571
1598
  ],
1572
1599
  "v2-sse-last-event-id.test.ts": [
1573
1600
  2
1574
1601
  ],
1602
+ "v2-storage-cross-tenant-isolation.test.ts": [
1603
+ 2
1604
+ ],
1575
1605
  "v2-stream-mode-refusal.test.ts": [
1576
1606
  2
1577
1607
  ],
@@ -1584,6 +1614,9 @@
1584
1614
  "v2-terminal-event-once.test.ts": [
1585
1615
  2
1586
1616
  ],
1617
+ "v2-tool-authorization-fail-closed.test.ts": [
1618
+ 2
1619
+ ],
1587
1620
  "v2-tool-catalog-annotations.test.ts": [
1588
1621
  2
1589
1622
  ],
@@ -1635,6 +1668,9 @@
1635
1668
  "v2-well-known-one-resource.test.ts": [
1636
1669
  2
1637
1670
  ],
1671
+ "v2-workspace-scope-from-identity.test.ts": [
1672
+ 2
1673
+ ],
1638
1674
  "vector-knn-roundtrip.test.ts": [
1639
1675
  1
1640
1676
  ],
@@ -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.2",
5
- "corpusTag": "v2.45.2",
4
+ "version": "2.45.3",
5
+ "corpusTag": "v2.45.3",
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": "69464bc0e1e71ef3b543a73d4fa4f67202343d2603c80af8033f6c3619ef1b30",
10
+ "api/openapi.yaml": "ad37a5a5c732ad46100fc03c3f4243fdd4f2976e330f02a8791b6b5b10a8a0c1",
11
11
  "api/redocly.yaml": "b0604c89b2ca6d5076ec25725c539dad44a741a811fe524439ee6daef8baa09f",
12
- "api/seams-v2.yaml": "4eb20e64dbccf5e7ed1f3ffba7882f576bb1629f8523eaffae54d05809e1bc66",
13
- "api/v2/asyncapi.yaml": "910690735bc6bca78f51801854fa3e3ed9eb9a56f65441d594401fb19e684ba3",
14
- "api/v2/openapi.yaml": "b4fb04a206521719a1b13733914550d4d671e57c0c276122bedb88635fd8901c",
12
+ "api/seams-v2.yaml": "1f22ab7223019080332312b0fc9f7eb4c97bca2eebd57cab886acd1d6e3081d0",
13
+ "api/v2/asyncapi.yaml": "5b74151b1867c3c234203aa1f2dfb1ceeea216e013395e8696da02d4a8630412",
14
+ "api/v2/openapi.yaml": "05a07d9cd1e7f80da1e7044aace1073bf4ab584186cdd67979ba2664f90a80d0",
15
15
  "api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
16
16
  "schemas/README.md": "0c0b737ffcf8f30e7d2809cec8a498232de710f41443212922ad8337cdde0b51",
17
17
  "schemas/a2a-task-state.schema.json": "75d5049dea8bd873ff0e7546f1c60c8d36c219bec7084264360be54a8be30ae1",
@@ -31,7 +31,7 @@
31
31
  "schemas/artifact-type-pack-manifest.schema.json": "60342fcb15eb7f3ab46271f0641409a30d3a4a8f318b1020c53070886b191252",
32
32
  "schemas/audit-verify-result.schema.json": "0d3a3088ac1c32f9df66d346c33d7adbd8037940dba040e14ea847565a1a14bd",
33
33
  "schemas/budget-policy.schema.json": "3d4fe30ae129fe136ab9ece395f2a2a42a76360786b1b9a1d5f1cbc753eb5b39",
34
- "schemas/capabilities.schema.json": "56b367074bce0f02ff87e0b84ccdf91856793d3d285e28f261656edba6352cc0",
34
+ "schemas/capabilities.schema.json": "d1d491248930e9b50ac6aca93a33cb1155eb997c4fbb2af0a940e8e889c76807",
35
35
  "schemas/certification-bundle-v2.schema.json": "2f85530dd04824a15810c94e96869362dee1e78396eca21fdd18e5501416bb7d",
36
36
  "schemas/channel-presence-payload.schema.json": "f9d0ef4219814c2b765baf3e68cbe5eea2cb42379f516a93a0459dcd5f7db47e",
37
37
  "schemas/channel-written-payload.schema.json": "54723a56bc5a35232f64544ca686ac1ac959a5fb05b32b2401facca6b3ef51a3",
@@ -95,7 +95,7 @@
95
95
  "schemas/suspend-request.schema.json": "7a90b622d5c619e3ffb5da696d272d6505e2780ab92ab631ac1b900f5daea908",
96
96
  "schemas/tool-descriptor.schema.json": "7f6adbae63003c1f1311f6a436f7f286542be62aa19a27b46730f2c2810857f7",
97
97
  "schemas/trigger-event.schema.json": "59d63b79dca3593f45d281776cbfe43ae4d85ee25519b82ad59f162a9efb592c",
98
- "schemas/trigger-subscription-registration.schema.json": "929d628bad08e1def696e47e9a148aba00fa801e28d49bbd0dc2360b08673435",
98
+ "schemas/trigger-subscription-registration.schema.json": "2ca680ca6ffdb3cfc5de7bb8b8db8c3ec8eab4b88b7c9f7fe5edaecc7cf61ad0",
99
99
  "schemas/trigger-subscription.schema.json": "4f7b067f20032073b140c4bb15dc9a9914c4f6421a252ff1ff261cbdd8edf270",
100
100
  "schemas/ui-plugin-message.schema.json": "346638babf1f8ea7b161cde75c0038b518cd2cc5bf0631772e20a9b74c96b5dd",
101
101
  "schemas/v2/a2a-task-state.schema.json": "1810363e2d9bddb5997923bdac4044cc17c6f46ec5a40515d2ff21d9c82c1ad9",
@@ -116,7 +116,7 @@
116
116
  "schemas/v2/artifact.schema.json": "85b9dc284d39b99d2a66fa8cbf47f2dd3d8526d3bd6cc411ad315b372de38975",
117
117
  "schemas/v2/audit-verify-result.schema.json": "14af8e9283510f0a40e48a2059c929ee44e0fc9fa3cc66a95bfbe90b17c48297",
118
118
  "schemas/v2/budget-policy.schema.json": "1de68095db0beb5c7a4be629de26e79f62ebbe748a88d46f36e768edfd1f0d68",
119
- "schemas/v2/capabilities.schema.json": "d0b72f589b4e29ef69bd26e38a309aca1db2965c8441cae56c1dcc12c8d7152f",
119
+ "schemas/v2/capabilities.schema.json": "3792aa9d292229cf9b82c7306600a5dfdb63b3274523e0ed5dd4f952a04cb833",
120
120
  "schemas/v2/certification-bundle.schema.json": "82ce31a1ed6c7e21b0ba88ff62a7b8b768fc3b176b1d06fce023203830b94725",
121
121
  "schemas/v2/channel-presence-payload.schema.json": "4eeb224910e6f7eee3ab5e7c3899559c25c83a26d0022e0ad195c4672611977a",
122
122
  "schemas/v2/channel-written-payload.schema.json": "ccecff3c71a3275ad8db035ac0e04db25e6f8d23cf091ddbcd5a60d1be1cbb6b",
@@ -130,7 +130,7 @@
130
130
  "schemas/v2/conversation-turn.schema.json": "75bb6a4a03072a958feb5b34e2a1f40471e9e3996a81fa430b65717f0773c9a4",
131
131
  "schemas/v2/credential-provenance.schema.json": "f86b09e94b1c524dec7d1c6353965665ae8891e9517faef6e742d639c5578c99",
132
132
  "schemas/v2/credential-reference.schema.json": "ea699ce30f58fb2c32d4279158b3f730f70be7b2dc8c3037693a76e66eb3e001",
133
- "schemas/v2/debug-bundle.schema.json": "f2b72cd658a2072e24031fcd916ba3eb8afe5618c540ea0e7e5cc79ef29d5cab",
133
+ "schemas/v2/debug-bundle.schema.json": "0ec1e0bca9c0a359cc6cf3448e5200a1c3d3d5b1e5efaa6605231477daaf3fee",
134
134
  "schemas/v2/dispatch-config.schema.json": "5718c1ed7d95672e618bcb1f1da8e38180e144b12151307afc3aba1314dfcef0",
135
135
  "schemas/v2/effect-ledger-projection.schema.json": "0d118d385a43921118a76888449c5c3c5618eab1b80914b84a0a1b48d18822c1",
136
136
  "schemas/v2/effect-seam-manifest.schema.json": "197ef9944ab027a50d1b9352f317cbbc12c8ad955fdb14650fb90477052ffef0",
@@ -186,7 +186,7 @@
186
186
  "schemas/v2/suspend-request.schema.json": "368d9932efb08f85b4520cac44b3c553876209f55bada93b5fcac93cc890b411",
187
187
  "schemas/v2/tool-descriptor.schema.json": "8c98fb5e93bc649dff859d3d1dde8075db929efd6c49c496252d0a88cccc73e1",
188
188
  "schemas/v2/trigger-event.schema.json": "710ecca8b3ecdad3a532a96768177f062192b24a589a4f798c7eb24bab64b30a",
189
- "schemas/v2/trigger-subscription-registration.schema.json": "04bc314a07c7e8b565577cb8df5821d968ce2af915900dfdccd4a24e928d20db",
189
+ "schemas/v2/trigger-subscription-registration.schema.json": "5f7959f3e13f49399783e47f73101d6af95e5abccea1af2f0014ccbe3e51006c",
190
190
  "schemas/v2/trigger-subscription.schema.json": "1f5c286f68577d531ef9445ab2493f8d69ac9ee42313d28d758a0f00dd0fab36",
191
191
  "schemas/v2/ui-plugin-message.schema.json": "879d65867c01656b3962d64283e66364dcb8f626ad26c5fcf2d2d55a79ae0ce9",
192
192
  "schemas/v2/webhook-dead-letter-page.schema.json": "543f5ccc26d8f777684a037172ab7b9fdf70735e5394eeb42e5fa6569503f528",
@@ -204,13 +204,13 @@
204
204
  "schemas/workspace-file.schema.json": "464de85c2a068243084ee9c1d969bc7cd5d8f7948574e58450d6493c38a0e1e4",
205
205
  "spec/v1/alias-detectors.json": "0b2f28808ffe0b9d98a187775038c3406bb70919747a788a6e4f866e293ae6d3",
206
206
  "spec/v1/capability-declaration-classes.json": "e7729aed5c4b4e1dd02abab0530f14cc95f5d4070fe51fb139e7f5cccefa00c6",
207
- "spec/v1/core-standard-manifest.json": "c948680f15a882c4bd97fa41235879d4103d3d3887ed294028ba9dfbb1d5d1a9",
207
+ "spec/v1/core-standard-manifest.json": "746278bbf9cf46840ba3bd5a85683251ddbc379466585f54e582754ce684d637",
208
208
  "spec/v1/deprecations.json": "a3293072ea951857395d05f0437264e88965f1e9019ff61c55bacfa9c1aaa17c",
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": "ac1fb9286c963aa19824292526a6f9726d9d2df5fe06695cc6afdfb33554edd2",
213
+ "spec/v1/gaps.json": "2705bd6f7be31392ba09da318e99d55905dd08aa80e76e1226cf8f1a85132d18",
214
214
  "spec/v1/gaps.schema.json": "8fd83259f556553c9df0f53e7a82ca8c2a4771d471197f69b4e2ae0ceeacfd66",
215
215
  "spec/v1/migrations.json": "4c50760be70cb644d02427bcaf5749606700edf4513f423903cf3b1d12f5c300",
216
216
  "spec/v1/migrations.schema.json": "886779aa6c22e646db097f5df210adb018a4dd14a7b815465a18c8a7056c8f72",
@@ -227,7 +227,7 @@
227
227
  "spec/v2/core/execution.md": "8a0c9db1bbfb97c008ab9328b7333c1cc3318196e78bfbefb49e781bdf4f9d44",
228
228
  "spec/v2/core/form-content-packs.md": "02931227899676fd4581a27017be2a6c4953904717be9b81c29d12f87ab3917d",
229
229
  "spec/v2/core/headers.md": "21d6c8220c3ac75b10e31c4cf6179abb539e2558af5006e40228d8c383ed5c92",
230
- "spec/v2/core/host-services.md": "18389b55286875922c26e13fbcd6c787fca2568f68b7e6f65176f1938404a218",
230
+ "spec/v2/core/host-services.md": "73f26ea32891044131d63f9861d2cbddccb5d08bf76e29e47afef9bb5b646791",
231
231
  "spec/v2/core/i18n.md": "882904b7d59f992ab1e5f7346167161a54cf665b8fee47e64a3862cf9133a580",
232
232
  "spec/v2/core/idempotency.md": "adbd317a6ebb57c2759c076297b31f67def9e391576bccd0bbea787b13e85872",
233
233
  "spec/v2/core/identity.md": "f61dc646f56fc1c07016c3acec9e8eb8c51051c55ecbedc986de793669fb6e6b",
@@ -240,16 +240,16 @@
240
240
  "spec/v2/core/persistence.md": "249a1de353f421cdf92b9006a13a10afdb002f4029eb6a13520cc95252edb2ec",
241
241
  "spec/v2/core/portability.md": "ab13a6ca43544b9a6a91edfb377ec680c768d14be841c84fb79125b231426891",
242
242
  "spec/v2/core/replay.md": "4a8b53325e18aa53400ece81168c76b4402b932e5e304a391754f293b126eabf",
243
- "spec/v2/core/runs.md": "18261bf0fff85101ee9547dd2cd9f06055f2095609dfd23e0f8fdbd1f1d2d0d0",
243
+ "spec/v2/core/runs.md": "991757ded9b3645bafad2344f4ccb75c7d79993bd80269f9dd3489193841d71b",
244
244
  "spec/v2/core/security-defaults.md": "887d8c4bc592094e3b931c23f1c2175c18ebbd47528b074016f9d2091bc513b2",
245
245
  "spec/v2/core/storage.md": "dc94b985847c1341c7a064f04a6010f0fdbe6c3ab3e555d4362878c81d764c18",
246
246
  "spec/v2/core/tool-catalog.md": "d16a9004418719c48301804aefa4d7ab6f41112a4ed83727f552aa1b92b418b0",
247
247
  "spec/v2/core/versioning.md": "b7acb0d8b77425cb53ea700e47ba790bf7506de600c398690e6ec5bb9c7a220e",
248
- "spec/v2/core/webhooks.md": "7b2606b2ebbeafd84ca052a5c4d2f392cade0b482fbb063acb0aeb38675f6ede",
248
+ "spec/v2/core/webhooks.md": "b0c08a3511b9319da7b0f7d83cddb15758acfc92e4c58c3604ac0f6b1ab13242",
249
249
  "spec/v2/core/workflow-chain-packs.md": "fdce7a8477e269950a2bac05ee6eddbd66eb0b670b464d00054f7196ec05d29c",
250
- "spec/v2/corrections.json": "59f647f4d22477f6c2b06dcf913b682dde5d51dd5e607a5d0cf648ab60864d6e",
250
+ "spec/v2/corrections.json": "3a8d93e33865b692d26a3b6bfd3819766e206b2e0c2a69669baa4bf833d05a49",
251
251
  "spec/v2/corrections.schema.json": "46bbffb954d13bd9995aebc912ceeb2544f7064331abf25fd6ab85c81a5cec94",
252
- "spec/v2/declaration.json": "24866c15908b3b70d96339dd852187ba908ddf65f90fad1d566ba6df5881aa48",
252
+ "spec/v2/declaration.json": "967f6db929fd7c4000d41a12d8b6d9f77bad90348de0dbc792b02f33e7026385",
253
253
  "spec/v2/declaration.schema.json": "ef951633e4070899f64828f5cf1b5f4870270a0360f81dbb49b5b4d6cd407550",
254
254
  "spec/v2/errors.json": "83f048b0a06952b4af6f125fb66c4212acab51056b411390f1010acd6ab9cdc0",
255
255
  "spec/v2/errors.schema.json": "47404ad53e6147cf9017b813483712466573e419fb33e6c4b6c12d3e7deba1bc",
@@ -260,7 +260,7 @@
260
260
  "spec/v2/ext/brand/README.md": "82989c018d7965bab029bcce7a67b1d76ffc433d08971a4cac91befcccc09438",
261
261
  "spec/v2/ext/canvas/README.md": "00ca0856e3a0146f1eeaec87209d450e2900ed2748dc7520cc60250a0d3ffbeb",
262
262
  "spec/v2/ext/chat/README.md": "f59b6706162abbfff96f8bccd4c0e1aeed721047cf62e9af58c875727a38d716",
263
- "spec/v2/ext/coordination/README.md": "c7fe0884f52405fdd02c5f9ec309ac6b585685ee06ec5f338fff7acf03c07148",
263
+ "spec/v2/ext/coordination/README.md": "7185af14aa0a2b554095ddbf10b0443b535fd28311a1836c6aa733b203030a9a",
264
264
  "spec/v2/ext/dataIntegration/README.md": "286dec8aa29e4d3225cbd8b3a361f5a438c3288e759966b9415ecbb99c576e70",
265
265
  "spec/v2/ext/entities/README.md": "956d4a9a2aedb8cb9c79ce8145580480920474cc6943f0e33ede32d7b20414dc",
266
266
  "spec/v2/ext/grpc-transport/README.md": "6ba378e3c6d1f2e3269b392cfdf0c40565ee9bfa259d1baaa9ee1d350e38e792",
@@ -303,9 +303,9 @@
303
303
  "spec/v2/path-manifest.json": "27c2307b84376b2f22bea4e2213fc7e33ef3bf8fd1d18039f7955edb00dc9c9a",
304
304
  "spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
305
305
  "spec/v2/profiles.json": "180beb9ef2d161766fa23b2987e7e829851e3d316273147cf6a4a3f324befd6b",
306
- "spec/v2/release.json": "f042a3f11bc9eaffcd054a6f4b94e78931d6289191d2c8dde2f169a63478e3f1",
306
+ "spec/v2/release.json": "0befa21e6634c82cd3d6be9e487f79b7bf77fd3143ce3e0aed301230753b21bc",
307
307
  "spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624",
308
- "spec/v2/surface-baseline.json": "18c9f38e3b596465128ceeee10cbdd17395672da5fe321db03ab4eee67bd50a4"
308
+ "spec/v2/surface-baseline.json": "fd44c977c6646c0bcbd85f9f5c4dce620bba29ee56b10cd077e3f71437e0276a"
309
309
  },
310
- "corpusCommit": "5bc14dd1ef9a0c6d8ab4bf220fbef47a74792212"
310
+ "corpusCommit": "f84011f0ce7ba068e53c1aa2b92d17fa97f5df5f"
311
311
  }
package/src/cli.ts CHANGED
@@ -685,7 +685,7 @@ async function runCertify(args: ParsedArgs, baseUrl: string, apiKey: string): Pr
685
685
  if (target.major === 2) return verdictFor(profile)?.witnessedPasses ?? 0;
686
686
  const floor = PROFILE_FLOOR_SCENARIOS[profile];
687
687
  if (!floor) return 0;
688
- const onFloor = (scenario: string): boolean => floor.required.includes(scenario) || (floor.requiredAnyPrefix ?? []).some((pre) => scenario.startsWith(pre));
688
+ const onFloor = (scenario: string): boolean => floor.required.includes(scenario) || (floor.requiredAnyPrefix ?? []).some((pre) => scenario.startsWith(pre)) || (floor.requiredAnyOf ?? []).some((g) => g.includes(scenario));
689
689
  return derived.requirements.filter((r) => r.disposition === 'executed-pass' && onFloor(r.scenarioId)).length;
690
690
  };
691
691
  // `certified` IS the verdict (RFC 0148 §A; RFC 0168 §E.1 adds the bundle-wide
@@ -31,7 +31,7 @@ import {
31
31
  type DiscoveryPayload,
32
32
  } from './profiles.js';
33
33
  import { CERTIFIABLE, DISPOSITIONS, type Disposition } from './requirement-ledger.js';
34
- import { floorFilesFor, requirementIdForPrefix, requirementIdForScenario } from './requirement-registry.js';
34
+ import { floorFilesFor, requirementIdForAnyOf, requirementIdForPrefix, requirementIdForScenario } from './requirement-registry.js';
35
35
  import { UNCLASSIFIED_RETURN_DETAIL } from './soft-skip.js';
36
36
 
37
37
  /**
@@ -149,7 +149,7 @@ export function findLiteral(value: unknown, literal: string): string[] {
149
149
  * it is the prefix requirement id, and the rows that satisfy it are the
150
150
  * matching scenario rows.
151
151
  */
152
- function requiredFor(profile: string, document: Readonly<Record<string, unknown>>): { files: string[]; prefixes: string[]; discoveryOnly: boolean } | null {
152
+ function requiredFor(profile: string, document: Readonly<Record<string, unknown>>): { files: string[]; prefixes: string[]; anyOf: Array<readonly string[]>; discoveryOnly: boolean } | null {
153
153
  const floor = PROFILE_FLOOR_SCENARIOS[profile];
154
154
  if (floor === undefined) return null;
155
155
  // Discovery-conditional floors (G7) are evaluated against the bundle's own
@@ -159,6 +159,7 @@ function requiredFor(profile: string, document: Readonly<Record<string, unknown>
159
159
  return {
160
160
  files: [...files],
161
161
  prefixes: [...(floor.requiredAnyPrefix ?? [])],
162
+ anyOf: [...(floor.requiredAnyOf ?? [])],
162
163
  discoveryOnly: floor.discoveryOnly === true,
163
164
  };
164
165
  }
@@ -272,6 +273,26 @@ export function verifyBundleV2(bundle: BundleV2Like): BundleV2Verdict {
272
273
  if (!considered.some((r) => isWitnessedPass(r))) notCertifiable.push(id);
273
274
  }
274
275
 
276
+ for (const group of req.anyOf) {
277
+ // RFC 0229 §E: one requirement, satisfied by a WITNESSED pass of any
278
+ // member with no member failing. The emitter writes a summary row under
279
+ // the group id; member rows are read when it is absent.
280
+ const id = requirementIdForAnyOf(group);
281
+ required.push(id);
282
+ const summary = byId.get(id)?.[0];
283
+ const members = group.map((f) => byId.get(requirementIdForScenario(scenarioBasename(f)))?.[0]).filter((r): r is BundleV2Requirement => r !== undefined);
284
+ if (summary === undefined && members.length === 0) {
285
+ profileRejections.push({ kind: 'unwitnessed-requirement', profile, requirementId: id, detail: `${id}: no alternative (${group.join(', ')}) recorded a row` });
286
+ continue;
287
+ }
288
+ const considered = summary !== undefined ? [summary] : members;
289
+ if (considered.some((r) => r.disposition === 'executed-pass' && !isWitnessedPass(r))) {
290
+ profileRejections.push({ kind: 'vacuous-pass', profile, requirementId: id, detail: `${id}: executed-pass with no witnessed assertion` });
291
+ continue;
292
+ }
293
+ if (considered.some((r) => r.disposition === 'executed-fail') || !considered.some((r) => isWitnessedPass(r))) notCertifiable.push(id);
294
+ }
295
+
275
296
  const evidenceValid = profileRejections.length === 0;
276
297
  const certified = derivable && evidenceValid && notCertifiable.length === 0 && (req.discoveryOnly || required.length > 0);
277
298
  return { profile, derivable, floorUnspecified: false, required, rejections: profileRejections, notCertifiable, evidenceValid, certified };
package/src/lib/driver.ts CHANGED
@@ -11,6 +11,46 @@
11
11
 
12
12
  import { loadEnv } from './env.js';
13
13
  import { seamPath, targetMajor } from './seams.js';
14
+ import { scaledTimeoutMs } from './timeout-scale.js';
15
+
16
+ /**
17
+ * How long one request may go unanswered before the driver gives up (#1829).
18
+ * Below vitest's 30 s `testTimeout`, so a lost response ends as a named
19
+ * {@link TransportError} and not as a bare harness timeout. Driver long-polls
20
+ * are at most 5 s. Scales with `OPENWOP_POLL_TIMEOUT_SCALE`.
21
+ */
22
+ export const REQUEST_TIMEOUT_MS = 20_000;
23
+
24
+ /** Leads every {@link TransportError} message; `resolveItRecord` keys on it. */
25
+ export const TRANSPORT_LOSS_PREFIX = 'transport-loss: ';
26
+
27
+ /**
28
+ * No response arrived: the request timed out or the connection failed. The
29
+ * suite observed nothing about the host, so this is never a verdict on it. A
30
+ * runner whose network dropped for 9 s once recorded `executed-fail` against a
31
+ * host that had answered in 2.6 ms (MyndHyve cut, suite 2.45.2).
32
+ */
33
+ export class TransportError extends Error {
34
+ constructor(
35
+ readonly kind: 'timeout' | 'network',
36
+ readonly method: string,
37
+ /** Origin and path only. A query string can carry a token. */
38
+ readonly target: string,
39
+ reason: string,
40
+ ) {
41
+ super(`${TRANSPORT_LOSS_PREFIX}${method} ${target} got no response (${reason}); the suite could not observe the host`);
42
+ this.name = 'TransportError';
43
+ }
44
+ }
45
+
46
+ function withoutQuery(url: string): string {
47
+ try {
48
+ const u = new URL(url);
49
+ return `${u.origin}${u.pathname}`;
50
+ } catch {
51
+ return url.split('?')[0] ?? url;
52
+ }
53
+ }
14
54
 
15
55
  export interface OpenWOPResponse {
16
56
  readonly status: number;
@@ -25,6 +65,10 @@ export interface OpenWOPRequestInit {
25
65
  /** `false` sends no default credential. A caller-supplied `Authorization`
26
66
  * header is always sent as given, whatever this says. */
27
67
  readonly authenticated?: boolean;
68
+ /** Replaces the driver's own bound entirely; an abort it raises is rethrown as-is. */
69
+ readonly signal?: AbortSignal;
70
+ /** Overrides {@link REQUEST_TIMEOUT_MS} for one request (scaled the same way). */
71
+ readonly timeoutMs?: number;
28
72
  }
29
73
 
30
74
  class OpenWOPDriver {
@@ -83,9 +127,24 @@ class OpenWOPDriver {
83
127
  fetchInit.body = JSON.stringify(init.body);
84
128
  }
85
129
  }
86
- const res = await fetch(url, fetchInit);
130
+ const boundMs = scaledTimeoutMs(init.timeoutMs ?? REQUEST_TIMEOUT_MS);
131
+ fetchInit.signal = init.signal ?? AbortSignal.timeout(boundMs);
132
+ let res: Response;
133
+ let text: string;
134
+ try {
135
+ res = await fetch(url, fetchInit);
136
+ // The body is read under the same bound: a response whose headers arrive
137
+ // and whose body never does is the same lost observation.
138
+ text = await res.text();
139
+ } catch (err) {
140
+ // The caller's own signal aborted: that is the caller's event, not a loss.
141
+ if (init.signal?.aborted === true) throw err;
142
+ const name = err instanceof Error ? err.name : '';
143
+ if (name === 'TimeoutError') throw new TransportError('timeout', method, withoutQuery(url), `no response within ${boundMs} ms`);
144
+ const cause = err instanceof Error ? (err.cause instanceof Error ? err.cause.message : err.message) : String(err);
145
+ throw new TransportError('network', method, withoutQuery(url), cause.slice(0, 160));
146
+ }
87
147
 
88
- const text = await res.text();
89
148
  let json: unknown;
90
149
  try {
91
150
  json = text.length > 0 ? JSON.parse(text) : undefined;
@@ -42,9 +42,19 @@ export async function readMemoryAttributionCap(): Promise<Record<string, unknown
42
42
  return attr && typeof attr === 'object' ? (attr as Record<string, unknown>) : null;
43
43
  }
44
44
 
45
- /** True when the host commits to emitting `memory.written`. */
45
+ /**
46
+ * True when the host commits to emitting `memory.written`.
47
+ *
48
+ * Major 1: the v1 block's `supported: true` (its schema `const`) AND
49
+ * `emitsWriteEvents: true`. Major 2: there is no `supported` field — presence
50
+ * of the `memory` family record IS the claim (RFC 0169 §A.2), and
51
+ * `readMemoryAttributionCap` returns the `attribution` object only when that
52
+ * record is present — so the commitment is `emitsWriteEvents: true` alone.
53
+ * Requiring `supported` at major 2 soft-skipped every v2 host (suite 2.45.3).
54
+ */
46
55
  export function emitsWriteEvents(cap: Record<string, unknown> | null): boolean {
47
- return cap?.['supported'] === true && cap?.['emitsWriteEvents'] === true;
56
+ if (cap?.['emitsWriteEvents'] !== true) return false;
57
+ return targetMajor() === 2 ? true : cap['supported'] === true;
48
58
  }
49
59
 
50
60
  const SEED_FIXTURE = 'conformance-noop';
@@ -71,9 +81,34 @@ interface RunEventLike {
71
81
  payload?: Record<string, unknown>;
72
82
  }
73
83
 
74
- /** Fetches a run's events and returns only the `memory.written` ones. */
84
+ /**
85
+ * Fetches a run's events and returns only the `memory.written` ones.
86
+ *
87
+ * Major 2: `GET /runs/{runId}/events` is the SSE stream (`streamRunEvents`,
88
+ * `text/event-stream` only), so a JSON read of it yields nothing and every leg
89
+ * would soft-skip "run wrote no memory" — a `blocked` row that denies
90
+ * certification for a suite defect. The JSON projection at v2 is the poll
91
+ * (`events.md` §Poll), read from sequence 0 and paged on `afterSequence` until
92
+ * `lastSequence` is reached.
93
+ */
75
94
  export async function memoryWrittenEvents(runId: string): Promise<RunEventLike[]> {
76
- const res = await driver.get(`${runsPath()}/${encodeURIComponent(runId)}/events`);
77
- const events = (res.json as { events?: RunEventLike[] } | undefined)?.events ?? [];
78
- return events.filter((e) => e.type === 'memory.written');
95
+ if (targetMajor() !== 2) {
96
+ const res = await driver.get(`${runsPath()}/${encodeURIComponent(runId)}/events`);
97
+ const events = (res.json as { events?: RunEventLike[] } | undefined)?.events ?? [];
98
+ return events.filter((e) => e.type === 'memory.written');
99
+ }
100
+ const out: RunEventLike[] = [];
101
+ let after = -1;
102
+ for (let page = 0; page < 50; page++) {
103
+ const q = after < 0 ? '' : `&afterSequence=${after}`;
104
+ const res = await driver.get(`/runs/${encodeURIComponent(runId)}/events/poll?timeout=1${q}`);
105
+ const body = res.json as { events?: Array<RunEventLike & { sequence?: number }>; lastSequence?: number } | undefined;
106
+ const events = res.status === 200 && Array.isArray(body?.events) ? body.events : [];
107
+ out.push(...events);
108
+ const seqs = events.map((e) => e.sequence).filter((s): s is number => typeof s === 'number');
109
+ const next = seqs.length ? Math.max(...seqs) : after;
110
+ if (next <= after || typeof body?.lastSequence !== 'number' || next >= body.lastSequence) break;
111
+ after = next;
112
+ }
113
+ return out.filter((e) => e.type === 'memory.written');
79
114
  }
@@ -28,6 +28,7 @@
28
28
  */
29
29
 
30
30
  import { driver } from './driver.js';
31
+ import { scaledTimeoutMs } from './timeout-scale.js';
31
32
  import { targetMajor } from './seams.js';
32
33
 
33
34
  export interface RunSnapshot {
@@ -52,24 +53,7 @@ export interface RunSnapshot {
52
53
  const POLL_INTERVAL_MS = 250;
53
54
  const DEFAULT_TIMEOUT_MS = Number(process.env.OPENWOP_LIFECYCLE_TIMEOUT_MS ?? 10_000);
54
55
 
55
- /**
56
- * Multiplier applied to every poll bound (see the module docstring). Invalid,
57
- * non-positive, or non-finite values fall back to `1` rather than silently
58
- * producing a zero or negative deadline — a mis-set knob must not turn every
59
- * poll into an instant failure that looks like a host defect.
60
- */
61
- function pollTimeoutScale(): number {
62
- const raw = process.env.OPENWOP_POLL_TIMEOUT_SCALE;
63
- if (raw === undefined || raw === '') return 1;
64
- const n = Number(raw);
65
- return Number.isFinite(n) && n > 0 ? n : 1;
66
- }
67
-
68
- /** Apply the scale to a bound, rounding up so a scale of 1 is exactly a no-op. */
69
- export function scaledTimeoutMs(timeoutMs: number): number {
70
- const scale = pollTimeoutScale();
71
- return scale === 1 ? timeoutMs : Math.ceil(timeoutMs * scale);
72
- }
56
+ export { scaledTimeoutMs };
73
57
 
74
58
  /**
75
59
  * Live-model scenarios (RFC 0111's `conformance-context-budget-live`) drive real
@@ -495,6 +495,16 @@ export interface ProfileFloor {
495
495
  readonly required: readonly string[];
496
496
  /** Prefix groups where ≥1 matching passed scenario satisfies the group. */
497
497
  readonly requiredAnyPrefix?: readonly string[];
498
+ /**
499
+ * RFC 0229 §E — ANY-OF groups: each inner list names alternative scenario
500
+ * files, and the group is satisfied when at least one of them records a
501
+ * WITNESSED pass and none of them records a failure. A group is one
502
+ * requirement (`openwop.floor.anyof.<stem>+<stem>`, a summary row the runner
503
+ * derives from the members, like `requiredAnyPrefix`). An `inapplicable` or
504
+ * `skipped` member never satisfies it: `openwop-secrets` is certified by the
505
+ * canary round-trip OR the run-witness, not by the absence of both.
506
+ */
507
+ readonly requiredAnyOf?: ReadonlyArray<readonly string[]>;
498
508
  /**
499
509
  * The profile is discovery-payload-only: its predicate IS the whole claim and
500
510
  * it has no runtime floor. This flag exists so an EMPTY floor is a decision on
@@ -529,6 +539,11 @@ export interface ProfileFloor {
529
539
  readonly conditional?: ReadonlyArray<{ readonly path: string; readonly includes: string; readonly required: readonly string[] }>;
530
540
  }
531
541
 
542
+ /** Every scenario FILE a floor names: `required`, every `conditional` branch, and every `requiredAnyOf` member. */
543
+ export function floorMemberFiles(floor: ProfileFloor): string[] {
544
+ return [...floor.required, ...(floor.conditional ?? []).flatMap((c) => c.required), ...(floor.requiredAnyOf ?? []).flat()];
545
+ }
546
+
532
547
  export const PROFILE_FLOOR_SCENARIOS: Readonly<Record<string, ProfileFloor>> = {
533
548
  'openwop-core-standard': {
534
549
  required: [
@@ -607,7 +622,11 @@ export const PROFILE_FLOOR_SCENARIOS: Readonly<Record<string, ProfileFloor>> = {
607
622
  // `profiles.md` §`openwop-secrets`: credential resolution per `run-options.md`
608
623
  // §"Credential references" — the BYOK canary round-trip (`fixtures.md`
609
624
  // §conformance-secrets-roundtrip, SR-1) is the profile's proof.
610
- 'openwop-secrets': { required: ['byok-roundtrip.test.ts'] },
625
+ // RFC 0229 §E (2026-09-30): a SECOND floor path, as an any-of group. The
626
+ // canary fixture's node is an oracle over whatever its name reaches, so a
627
+ // production host rightly withholds it; the run-witness reads only a value
628
+ // the caller supplied with the same run and outputs only `matched`.
629
+ 'openwop-secrets': { required: [], requiredAnyOf: [['byok-roundtrip.test.ts', 'secrets-run-witness.test.ts']] },
611
630
 
612
631
  // `profiles.md` §`openwop-provider-policy`: the four-mode taxonomy shape and
613
632
  // its enforcement on the wire.
@@ -756,9 +775,14 @@ export function verifyBundleProfile(bundle: CertificationBundleLike, profile: st
756
775
  }
757
776
  const missingFloor = requiredFiles.filter((r) => !passed.has(scenarioBasename(r)));
758
777
  const prefixOk = (floor.requiredAnyPrefix ?? []).every((p) => [...passed].some((s) => s.startsWith(p)));
778
+ // RFC 0229 §E any-of groups: a v1 bundle lists only what passed, so a group
779
+ // holds when one member is in `results.passed`; an unmet group is reported
780
+ // as missing, spelled as its alternatives.
781
+ const groups = floor.requiredAnyOf ?? [];
782
+ for (const g of groups) if (!g.some((f) => passed.has(scenarioBasename(f)))) missingFloor.push(g.join(' | '));
759
783
  // A conditional floor none of whose branches matched requires nothing — that
760
784
  // is unprovable for a non-discovery-only profile, not proven.
761
- const evaluable = floor.discoveryOnly === true || requiredFiles.length > 0 || (floor.requiredAnyPrefix ?? []).length > 0;
785
+ const evaluable = floor.discoveryOnly === true || requiredFiles.length > 0 || (floor.requiredAnyPrefix ?? []).length > 0 || groups.length > 0;
762
786
  const floorProven = evaluable && missingFloor.length === 0 && prefixOk;
763
787
  return {
764
788
  profile,
@@ -36,6 +36,15 @@ export function requirementIdForPrefix(prefix: string): string {
36
36
  return `openwop.floor.any.${prefix}`;
37
37
  }
38
38
 
39
+ /**
40
+ * Any-of groups (RFC 0229 §E) become one requirement:
41
+ * `['byok-roundtrip.test.ts', 'secrets-run-witness.test.ts']` →
42
+ * `openwop.floor.anyof.byok-roundtrip+secrets-run-witness`.
43
+ */
44
+ export function requirementIdForAnyOf(files: readonly string[]): string {
45
+ return `openwop.floor.anyof.${files.map((f) => f.replace(/\.test\.ts$/, '')).join('+')}`;
46
+ }
47
+
39
48
  /**
40
49
  * The requirement IDs a profile's certification rests on.
41
50
  *
@@ -122,6 +131,7 @@ export function requirementsFor(profile: string, document?: Readonly<Record<stri
122
131
  return [
123
132
  ...files.map(requirementIdForScenario),
124
133
  ...(floor.requiredAnyPrefix ?? []).map(requirementIdForPrefix),
134
+ ...(floor.requiredAnyOf ?? []).map(requirementIdForAnyOf),
125
135
  ];
126
136
  }
127
137
 
@@ -134,6 +144,8 @@ export function allRequirements(): readonly string[] {
134
144
  for (const f of floor.required) ids.add(requirementIdForScenario(f));
135
145
  for (const c of floor.conditional ?? []) for (const f of c.required) ids.add(requirementIdForScenario(f));
136
146
  for (const p of floor.requiredAnyPrefix ?? []) ids.add(requirementIdForPrefix(p));
147
+ // an any-of group is one requirement, and each member file records under its own floor id
148
+ for (const g of floor.requiredAnyOf ?? []) { ids.add(requirementIdForAnyOf(g)); for (const f of g) ids.add(requirementIdForScenario(f)); }
137
149
  void profile;
138
150
  }
139
151
  return [...ids].sort();