@openwop/openwop-conformance 2.45.15 → 2.45.16

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 (47) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +2 -2
  3. package/dist/spec-artifacts.lock.json +2 -2
  4. package/package.json +2 -2
  5. package/requirements.json +1281 -4
  6. package/scenario-majors.json +101 -2
  7. package/schemas/CORPUS-STAMP.json +13 -13
  8. package/src/lib/artifact-schema-url-witness.ts +83 -0
  9. package/src/lib/dispatch-witness.ts +212 -0
  10. package/src/lib/family-advert-witness.ts +204 -5
  11. package/src/lib/fixture-run-observer.ts +97 -0
  12. package/src/lib/provider-usage-witness.ts +63 -0
  13. package/src/lib/run-graph-double.ts +144 -0
  14. package/src/lib/subworkflow-witness.ts +192 -0
  15. package/src/scenarios/v2-agent-runtime-advertisement.test.ts +51 -0
  16. package/src/scenarios/v2-ai-envelope-advertisement.test.ts +43 -0
  17. package/src/scenarios/v2-artifact-created-static.test.ts +92 -0
  18. package/src/scenarios/v2-artifact-type-schema-url.test.ts +44 -0
  19. package/src/scenarios/v2-artifact-types-advertisement.test.ts +64 -0
  20. package/src/scenarios/v2-authorization-advertisement.test.ts +66 -0
  21. package/src/scenarios/v2-channel-presence-advertisement.test.ts +41 -0
  22. package/src/scenarios/v2-conversation-turn-model-provenance-advertisement.test.ts +41 -0
  23. package/src/scenarios/v2-conversation-turn-model-provenance.test.ts +110 -0
  24. package/src/scenarios/v2-credentials-advertisement.test.ts +66 -0
  25. package/src/scenarios/v2-data-residency-admission.test.ts +98 -0
  26. package/src/scenarios/v2-data-residency-advertisement.test.ts +52 -0
  27. package/src/scenarios/v2-dead-letter-advertisement.test.ts +54 -0
  28. package/src/scenarios/v2-dispatch-input-mapping.test.ts +70 -0
  29. package/src/scenarios/v2-dispatch-loop.test.ts +80 -0
  30. package/src/scenarios/v2-envelope-contracts-advertisement.test.ts +40 -0
  31. package/src/scenarios/v2-export-bundle-static.test.ts +108 -0
  32. package/src/scenarios/v2-multi-party-conversation-advertisement.test.ts +52 -0
  33. package/src/scenarios/v2-node-pack-runtimes-advertisement.test.ts +52 -0
  34. package/src/scenarios/v2-nondeterminism-policy-advertisement.test.ts +41 -0
  35. package/src/scenarios/v2-portability-advertisement.test.ts +63 -0
  36. package/src/scenarios/v2-prompt-library-advertisement.test.ts +40 -0
  37. package/src/scenarios/v2-provider-usage-advertisement.test.ts +61 -0
  38. package/src/scenarios/v2-provider-usage-emission.test.ts +50 -0
  39. package/src/scenarios/v2-provider-usage-payload-static.test.ts +92 -0
  40. package/src/scenarios/v2-purpose-propagation-advertisement.test.ts +52 -0
  41. package/src/scenarios/v2-scheduling-advertisement.test.ts +55 -0
  42. package/src/scenarios/v2-self-hosted-runner-advertisement.test.ts +52 -0
  43. package/src/scenarios/v2-self-hosted-runner-frames-static.test.ts +121 -0
  44. package/src/scenarios/v2-sub-workflow-advertisement.test.ts +53 -0
  45. package/src/scenarios/v2-subworkflow-input-mapping.test.ts +74 -0
  46. package/src/scenarios/v2-subworkflow-linkage.test.ts +76 -0
  47. package/src/scenarios/v2-ui-plugins-advertisement.test.ts +85 -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": 615,
4
+ "files": 648,
5
5
  "v1": 455,
6
- "v2": 175
6
+ "v2": 208
7
7
  },
8
8
  "majors": {
9
9
  "a2a-1-0-agent-card.test.ts": [
@@ -1299,6 +1299,12 @@
1299
1299
  "v2-agent-org-chart-served-shape.test.ts": [
1300
1300
  2
1301
1301
  ],
1302
+ "v2-agent-runtime-advertisement.test.ts": [
1303
+ 2
1304
+ ],
1305
+ "v2-ai-envelope-advertisement.test.ts": [
1306
+ 2
1307
+ ],
1302
1308
  "v2-ai-envelope-shape-static.test.ts": [
1303
1309
  2
1304
1310
  ],
@@ -1314,12 +1320,24 @@
1314
1320
  "v2-artifact-a2a-shape.test.ts": [
1315
1321
  2
1316
1322
  ],
1323
+ "v2-artifact-created-static.test.ts": [
1324
+ 2
1325
+ ],
1326
+ "v2-artifact-type-schema-url.test.ts": [
1327
+ 2
1328
+ ],
1329
+ "v2-artifact-types-advertisement.test.ts": [
1330
+ 2
1331
+ ],
1317
1332
  "v2-assurance-downgrade-audited.test.ts": [
1318
1333
  2
1319
1334
  ],
1320
1335
  "v2-auth-challenge.test.ts": [
1321
1336
  2
1322
1337
  ],
1338
+ "v2-authorization-advertisement.test.ts": [
1339
+ 2
1340
+ ],
1323
1341
  "v2-bound-id-kinds.test.ts": [
1324
1342
  2
1325
1343
  ],
@@ -1350,6 +1368,9 @@
1350
1368
  "v2-chain-pin-exact.test.ts": [
1351
1369
  2
1352
1370
  ],
1371
+ "v2-channel-presence-advertisement.test.ts": [
1372
+ 2
1373
+ ],
1353
1374
  "v2-client-version-header.test.ts": [
1354
1375
  2
1355
1376
  ],
@@ -1365,6 +1386,12 @@
1365
1386
  "v2-content-locale-keys.test.ts": [
1366
1387
  2
1367
1388
  ],
1389
+ "v2-conversation-turn-model-provenance-advertisement.test.ts": [
1390
+ 2
1391
+ ],
1392
+ "v2-conversation-turn-model-provenance.test.ts": [
1393
+ 2
1394
+ ],
1368
1395
  "v2-conversation-turn-parts.test.ts": [
1369
1396
  2
1370
1397
  ],
@@ -1377,6 +1404,24 @@
1377
1404
  "v2-credential-interrupt.test.ts": [
1378
1405
  2
1379
1406
  ],
1407
+ "v2-credentials-advertisement.test.ts": [
1408
+ 2
1409
+ ],
1410
+ "v2-data-residency-admission.test.ts": [
1411
+ 2
1412
+ ],
1413
+ "v2-data-residency-advertisement.test.ts": [
1414
+ 2
1415
+ ],
1416
+ "v2-dead-letter-advertisement.test.ts": [
1417
+ 2
1418
+ ],
1419
+ "v2-dispatch-input-mapping.test.ts": [
1420
+ 2
1421
+ ],
1422
+ "v2-dispatch-loop.test.ts": [
1423
+ 2
1424
+ ],
1380
1425
  "v2-dual-stack-negotiation.test.ts": [
1381
1426
  2
1382
1427
  ],
@@ -1395,6 +1440,9 @@
1395
1440
  "v2-enum-growth-rule.test.ts": [
1396
1441
  2
1397
1442
  ],
1443
+ "v2-envelope-contracts-advertisement.test.ts": [
1444
+ 2
1445
+ ],
1398
1446
  "v2-envelope-reasoning-shape.test.ts": [
1399
1447
  2
1400
1448
  ],
@@ -1425,6 +1473,9 @@
1425
1473
  "v2-event-type-closed.test.ts": [
1426
1474
  2
1427
1475
  ],
1476
+ "v2-export-bundle-static.test.ts": [
1477
+ 2
1478
+ ],
1428
1479
  "v2-ext-family-claims.test.ts": [
1429
1480
  2
1430
1481
  ],
@@ -1521,6 +1572,9 @@
1521
1572
  "v2-mrtr-rounds-ceiling.test.ts": [
1522
1573
  2
1523
1574
  ],
1575
+ "v2-multi-party-conversation-advertisement.test.ts": [
1576
+ 2
1577
+ ],
1524
1578
  "v2-negotiation-authenticated.test.ts": [
1525
1579
  2
1526
1580
  ],
@@ -1530,6 +1584,12 @@
1530
1584
  "v2-no-transport-advertisement.test.ts": [
1531
1585
  2
1532
1586
  ],
1587
+ "v2-node-pack-runtimes-advertisement.test.ts": [
1588
+ 2
1589
+ ],
1590
+ "v2-nondeterminism-policy-advertisement.test.ts": [
1591
+ 2
1592
+ ],
1533
1593
  "v2-oauth-client-pkce-state-iss.test.ts": [
1534
1594
  2
1535
1595
  ],
@@ -1563,6 +1623,9 @@
1563
1623
  "v2-poll-cursor-v2.test.ts": [
1564
1624
  2
1565
1625
  ],
1626
+ "v2-portability-advertisement.test.ts": [
1627
+ 2
1628
+ ],
1566
1629
  "v2-preferred-version-default.test.ts": [
1567
1630
  2
1568
1631
  ],
@@ -1578,6 +1641,9 @@
1578
1641
  "v2-prompt-end-to-end-events.test.ts": [
1579
1642
  2
1580
1643
  ],
1644
+ "v2-prompt-library-advertisement.test.ts": [
1645
+ 2
1646
+ ],
1581
1647
  "v2-prompt-list-and-fetch.test.ts": [
1582
1648
  2
1583
1649
  ],
@@ -1602,6 +1668,18 @@
1602
1668
  "v2-provider-conflict.test.ts": [
1603
1669
  2
1604
1670
  ],
1671
+ "v2-provider-usage-advertisement.test.ts": [
1672
+ 2
1673
+ ],
1674
+ "v2-provider-usage-emission.test.ts": [
1675
+ 2
1676
+ ],
1677
+ "v2-provider-usage-payload-static.test.ts": [
1678
+ 2
1679
+ ],
1680
+ "v2-purpose-propagation-advertisement.test.ts": [
1681
+ 2
1682
+ ],
1605
1683
  "v2-queue-cross-tenant-isolation.test.ts": [
1606
1684
  2
1607
1685
  ],
@@ -1662,12 +1740,21 @@
1662
1740
  "v2-safefetch-ssrf-refused.test.ts": [
1663
1741
  2
1664
1742
  ],
1743
+ "v2-scheduling-advertisement.test.ts": [
1744
+ 2
1745
+ ],
1665
1746
  "v2-secret-canary-absent.test.ts": [
1666
1747
  2
1667
1748
  ],
1668
1749
  "v2-secrets-run-witness.test.ts": [
1669
1750
  2
1670
1751
  ],
1752
+ "v2-self-hosted-runner-advertisement.test.ts": [
1753
+ 2
1754
+ ],
1755
+ "v2-self-hosted-runner-frames-static.test.ts": [
1756
+ 2
1757
+ ],
1671
1758
  "v2-sse-last-event-id-cursor.test.ts": [
1672
1759
  2
1673
1760
  ],
@@ -1683,9 +1770,18 @@
1683
1770
  "v2-stream-sse-projection.test.ts": [
1684
1771
  2
1685
1772
  ],
1773
+ "v2-sub-workflow-advertisement.test.ts": [
1774
+ 2
1775
+ ],
1686
1776
  "v2-subject-link-record.test.ts": [
1687
1777
  2
1688
1778
  ],
1779
+ "v2-subworkflow-input-mapping.test.ts": [
1780
+ 2
1781
+ ],
1782
+ "v2-subworkflow-linkage.test.ts": [
1783
+ 2
1784
+ ],
1689
1785
  "v2-table-schema-enforcement.test.ts": [
1690
1786
  2
1691
1787
  ],
@@ -1704,6 +1800,9 @@
1704
1800
  "v2-trigger-dead-letter-read.test.ts": [
1705
1801
  2
1706
1802
  ],
1803
+ "v2-ui-plugins-advertisement.test.ts": [
1804
+ 2
1805
+ ],
1707
1806
  "v2-unadvertised-operation-not-found.test.ts": [
1708
1807
  2
1709
1808
  ],
@@ -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.15",
5
- "corpusTag": "v2.45.15",
4
+ "version": "2.45.16",
5
+ "corpusTag": "v2.45.16",
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": "b8a98faeb78af296f41b6c4bb926d8c01d45b5b81974d785924d6d4efa939831",
13
- "api/v2/asyncapi.yaml": "667763e99e9bbaf49ed0468e2f10fca37a94327fb1d6a6168d011ab4e892066e",
14
- "api/v2/openapi.yaml": "be44d7aa5c911d59c2a993206bb9b491070ad8551d03d49be04371cac7e6b6af",
12
+ "api/seams-v2.yaml": "429b70d7514eca241ec0353d061ddba2263d34f4e4d7614ecad3e11b2d880076",
13
+ "api/v2/asyncapi.yaml": "8a8a8f72e0ec39809c4cc85d71834d8263d51dabacc74d1673f3468a66156911",
14
+ "api/v2/openapi.yaml": "970fdc562da65537205354df32da66c9e1956496fc9b6cd482b1af13e1550fb8",
15
15
  "api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
16
16
  "schemas/README.md": "bd1c00fae7b6fda506a75aff841bbdc438d4ce092642a72041203bee999b88de",
17
17
  "schemas/a2a-task-state.schema.json": "75d5049dea8bd873ff0e7546f1c60c8d36c219bec7084264360be54a8be30ae1",
@@ -118,7 +118,7 @@
118
118
  "schemas/v2/audit-verify-result.schema.json": "14af8e9283510f0a40e48a2059c929ee44e0fc9fa3cc66a95bfbe90b17c48297",
119
119
  "schemas/v2/budget-policy.schema.json": "1de68095db0beb5c7a4be629de26e79f62ebbe748a88d46f36e768edfd1f0d68",
120
120
  "schemas/v2/capabilities.schema.json": "c7a01b2f569139f99ba5e1a5329a42556fbd82bc743f8fd96963304395fc9ef0",
121
- "schemas/v2/certification-bundle.schema.json": "82ce31a1ed6c7e21b0ba88ff62a7b8b768fc3b176b1d06fce023203830b94725",
121
+ "schemas/v2/certification-bundle.schema.json": "012c0176fbe05425507a8096412344388c0e2e1884c5fe1bc189ef1af0532997",
122
122
  "schemas/v2/channel-presence-payload.schema.json": "4eeb224910e6f7eee3ab5e7c3899559c25c83a26d0022e0ad195c4672611977a",
123
123
  "schemas/v2/channel-written-payload.schema.json": "ccecff3c71a3275ad8db035ac0e04db25e6f8d23cf091ddbcd5a60d1be1cbb6b",
124
124
  "schemas/v2/chat-card-pack-manifest.schema.json": "92a57db2fd4b2463f14ef155fef8e3f18afc78960b646459dcf32efc79ff6bf5",
@@ -128,7 +128,7 @@
128
128
  "schemas/v2/configurable.schema.json": "33a6884047e2490f723733bf8b74d3b9164e00374cfea6dc3eac5a70dc368861",
129
129
  "schemas/v2/connection-pack-manifest.schema.json": "8d019de6bd0c5b14ea3b227580734ec792521493b396a850f7f9217aa7a6a547",
130
130
  "schemas/v2/connection-provider-registry.schema.json": "5b404da328a95c45808929e233ee766ebd635cf0df6f5d15d4c19354dfd7e75d",
131
- "schemas/v2/conversation-event.schema.json": "9ad4c57285818edbf38b17816de0c4162f777c835f59e68702d0367a81cbccf6",
131
+ "schemas/v2/conversation-event.schema.json": "51dce69b33e3eb1913b9b2a0fcc0bee3dcf86761faf0c56dd004f4c580c95d39",
132
132
  "schemas/v2/conversation-turn.schema.json": "75bb6a4a03072a958feb5b34e2a1f40471e9e3996a81fa430b65717f0773c9a4",
133
133
  "schemas/v2/credential-provenance.schema.json": "f86b09e94b1c524dec7d1c6353965665ae8891e9517faef6e742d639c5578c99",
134
134
  "schemas/v2/credential-reference.schema.json": "ea699ce30f58fb2c32d4279158b3f730f70be7b2dc8c3037693a76e66eb3e001",
@@ -160,7 +160,7 @@
160
160
  "schemas/v2/memory-entry.schema.json": "290576ae84f4fef4cdf975932b87963283b11c50ea3d2654b32cd96aa6537c3d",
161
161
  "schemas/v2/memory-list-options.schema.json": "f66703bccb68efb28391c273abb66aa117ee5b5ce475c477846ed700c336afcd",
162
162
  "schemas/v2/node-pack-manifest.schema.json": "6e25106c647d58ff6b11c198a83de7f1f988e47c8e3bf9f28ac5f84eef900f5d",
163
- "schemas/v2/orchestrator-decision.schema.json": "2da17538e84cfbe70151eb118f1ccf670cff58b8697148e394074d32ac4af6b4",
163
+ "schemas/v2/orchestrator-decision.schema.json": "4d4b4c5fe73c22a1f42242b403650206ad58fd981f8fd45795894cebe3a514ae",
164
164
  "schemas/v2/org-chart-responsibility-view.schema.json": "8a2565e4cdc6737a3946a70799f93642808b92f9c8d56dcdff3a0782053b89a1",
165
165
  "schemas/v2/pack-lockfile.schema.json": "2685417702c105146b780cd99154296a46578fbb10d81c8c713348ee26fea898",
166
166
  "schemas/v2/part.schema.json": "08b75eaec9a0b4a8cedca7bab732dd906b52b0749eaa444ec1ac015aa43519a5",
@@ -173,7 +173,7 @@
173
173
  "schemas/v2/residency.schema.json": "3fd23c9732beb140504e81560fc426eb6764d547e56da5fb92c5a5c7bf04db3a",
174
174
  "schemas/v2/run-ancestry-response.schema.json": "334c17f2062dea62726ca1d07b8be3f169cdc422af843c1def5fb317039814c9",
175
175
  "schemas/v2/run-diff-response.schema.json": "8bdb2045c274580038bd74781f7d062f5049839797c164a89de4ed89edc53a74",
176
- "schemas/v2/run-event-payloads.schema.json": "fc1af4245dec98f7be86d20ad339e3409d0b2aaaa301dace083f734999c470b7",
176
+ "schemas/v2/run-event-payloads.schema.json": "942d6f65a765f6b574922a54d1e531ab66bc8d5c673a006ec68f8d75f3f0aec9",
177
177
  "schemas/v2/run-event.schema.json": "2f0d7416bd376c56affe97b6b2b49981532e8e9ff45c0a162cf806c78b3e7fb7",
178
178
  "schemas/v2/run-list-response.schema.json": "c2dbc34768dcf64528914a213aa3cb55952e8e3e90a64b04a9423b2a19749d38",
179
179
  "schemas/v2/run-options.schema.json": "f0e9f494d80086c2ed2cf9fe87df54e5b4e66601699c4a1089f4edbc7ba58867",
@@ -207,7 +207,7 @@
207
207
  "schemas/workspace-file.schema.json": "464de85c2a068243084ee9c1d969bc7cd5d8f7948574e58450d6493c38a0e1e4",
208
208
  "spec/v1/alias-detectors.json": "0b2f28808ffe0b9d98a187775038c3406bb70919747a788a6e4f866e293ae6d3",
209
209
  "spec/v1/capability-declaration-classes.json": "e7729aed5c4b4e1dd02abab0530f14cc95f5d4070fe51fb139e7f5cccefa00c6",
210
- "spec/v1/core-standard-manifest.json": "430abcd05b35ca4fbb14f21f1d7a76a7408d80a795986b6c6a4aa525870e7624",
210
+ "spec/v1/core-standard-manifest.json": "e034d628d415045195718b3065abf8ac2df8ad2179804ac63ff04ad7d0e198bb",
211
211
  "spec/v1/deprecations.json": "a3293072ea951857395d05f0437264e88965f1e9019ff61c55bacfa9c1aaa17c",
212
212
  "spec/v1/deprecations.schema.json": "3e393c405d2a41b467d8c5e3c468549078df1ce6a6d2588fc488097b95d9b55b",
213
213
  "spec/v1/end-of-support.json": "35f0bf3b70fe78b16e2a2fd81a153fc27e703f6c16376c6fe7cdf1f4f2c4e6d4",
@@ -309,9 +309,9 @@
309
309
  "spec/v2/path-manifest.json": "b84c7561f50c7df0cb61b202bd6595acad3ee7edca7a45f711949bea3c69bd27",
310
310
  "spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
311
311
  "spec/v2/profiles.json": "180beb9ef2d161766fa23b2987e7e829851e3d316273147cf6a4a3f324befd6b",
312
- "spec/v2/release.json": "75a6a33e253e1015ff582bcd3dbc2ed23cd8bea95e12d34006ee3239c91b71bb",
312
+ "spec/v2/release.json": "68d8fd2c8ef4cca09f08ba43adece748ea97a2eb9a5218b899be543569dbe26a",
313
313
  "spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624",
314
- "spec/v2/surface-baseline.json": "49311a9dd16aaa597d1c6724aa24456edd5f372d9b7d0bd6f5f1647f68db6db8"
314
+ "spec/v2/surface-baseline.json": "94199023cd15ef90174eccbb457aa805ac4a60df858fe8dc11e96d599edc56ca"
315
315
  },
316
- "corpusCommit": "eef96d5601f777c4bf94d9c2a69b06df86948172"
316
+ "corpusCommit": "11150aa3a6506f9d679564bff000755897628922"
317
317
  }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The artifact canonical schema-URL witness (`artifact-type-packs.md`
3
+ * §Schema distribution), unaided.
4
+ *
5
+ * "Its canonical URL and `$id` is
6
+ * `{HostBase}/schemas/artifacts/{artifactTypeId}.schema.json`, served as
7
+ * `application/schema+json`. A host advertising `artifactTypes` SHOULD serve
8
+ * each installed type's schema there, and MUST for a host-registered type whose
9
+ * `schemaVersion` it advertises."
10
+ *
11
+ * The MUST set is read from discovery alone: every `artifactTypes.types[id]`
12
+ * with `registrationSource: "host"` and an integer `schemaVersion`
13
+ * (`capabilities.schema.json`: the facet "discloses which §Schema distribution
14
+ * regime applies"). For each, `GET {HostBase}/schemas/artifacts/{id}.schema.json`:
15
+ * served answers `200`;
16
+ * media with `Content-Type: application/schema+json`;
17
+ * schema a JSON object whose `$id` is an absolute URL ending
18
+ * `/schemas/artifacts/{id}.schema.json`.
19
+ * `{HostBase}` is the base the suite drives the host at (the v2 text does not
20
+ * define it further), so the `$id`'s origin is not compared: a host fronted by
21
+ * a public origin legitimately mints its `$id` there.
22
+ *
23
+ * {@link driveSchemaUrls} observes and asserts nothing; {@link judgeSchemaUrl}
24
+ * is pure, proven in `artifact-schema-url-witness.test.ts`.
25
+ */
26
+
27
+ import { driver } from './driver.js';
28
+ import type { MajorProfile } from './major-profile.js';
29
+ import { finding, isRecord, skip, type Finding, type Skip } from './fixture-run-observer.js';
30
+
31
+ const DOC = 'artifact-type-packs.md §Schema distribution';
32
+
33
+ export interface SchemaUrlObservation {
34
+ readonly artifactTypeId: string;
35
+ readonly path: string;
36
+ readonly status: number;
37
+ readonly contentType: string | null;
38
+ readonly json: unknown;
39
+ }
40
+
41
+ /** The host-registered types whose `schemaVersion` the host advertises, sorted. */
42
+ export function hostRegisteredTypes(profile: MajorProfile, doc: unknown): string[] | Skip {
43
+ const fam = profile.family(doc, 'artifactTypes');
44
+ if (fam === null) return skip('inapplicable', 'the host does not advertise artifactTypes');
45
+ const types = isRecord(fam['types']) ? fam['types'] : {};
46
+ const ids = Object.entries(types)
47
+ .filter(([, t]) => isRecord(t) && t['registrationSource'] === 'host' && Number.isInteger(t['schemaVersion']))
48
+ .map(([id]) => id)
49
+ .sort();
50
+ if (ids.length === 0) return skip('inapplicable', 'the host advertises no artifactTypes.types entry with registrationSource "host" and a schemaVersion — serving the canonical URL is only a SHOULD for the rest');
51
+ return ids;
52
+ }
53
+
54
+ export const schemaPath = (artifactTypeId: string): string => `/schemas/artifacts/${encodeURIComponent(artifactTypeId)}.schema.json`;
55
+
56
+ /** Fetch each MUST-served schema. Asserts nothing. */
57
+ export async function driveSchemaUrls(profile: MajorProfile, doc: unknown): Promise<Skip | SchemaUrlObservation[]> {
58
+ const ids = hostRegisteredTypes(profile, doc);
59
+ if (!Array.isArray(ids)) return ids;
60
+ const out: SchemaUrlObservation[] = [];
61
+ for (const artifactTypeId of ids) {
62
+ const path = schemaPath(artifactTypeId);
63
+ const r = await driver.get(path, { headers: { Accept: 'application/schema+json, application/json' } });
64
+ out.push({ artifactTypeId, path, status: r.status, contentType: r.headers.get('content-type'), json: r.json });
65
+ }
66
+ return out;
67
+ }
68
+
69
+ /** One type's served schema. Pure. */
70
+ export function judgeSchemaUrl(o: SchemaUrlObservation): Finding[] {
71
+ const media = (o.contentType ?? '').split(';')[0]!.trim().toLowerCase();
72
+ const id = isRecord(o.json) ? o.json['$id'] : undefined;
73
+ let idOk = false;
74
+ if (typeof id === 'string') {
75
+ try { idOk = /^https?:$/.test(new URL(id).protocol) && id.endsWith(`/schemas/artifacts/${o.artifactTypeId}.schema.json`); } catch { idOk = false; }
76
+ }
77
+ return [
78
+ finding(o.status === 200, DOC, `the host MUST serve host-registered ${o.artifactTypeId}'s schema at ${o.path} (got ${o.status})`),
79
+ finding(media === 'application/schema+json', DOC, `${o.path} MUST be served as application/schema+json (got ${JSON.stringify(o.contentType)})`),
80
+ finding(isRecord(o.json), DOC, `${o.path} MUST be a JSON Schema object`),
81
+ finding(idOk, DOC, `the schema's $id MUST be its canonical URL, {HostBase}/schemas/artifacts/${o.artifactTypeId}.schema.json (got ${JSON.stringify(id)})`),
82
+ ];
83
+ }
@@ -0,0 +1,212 @@
1
+ /**
2
+ * The `core.dispatch` witnesses, fixture ports through `POST /runs` and
3
+ * `events/poll`. No seam.
4
+ *
5
+ * Input mapping (`spec/v2/facets/agents.schema.json` `dispatchMapping`;
6
+ * `schemas/v2/dispatch-config.schema.json` `inputMapping`), on
7
+ * `conformance-dispatch-input-mapping[-no-default]` →
8
+ * `conformance-dispatch-input-mapping-child`. A host advertising
9
+ * `agents.dispatchMapping: true` honors `inputMapping`, "building child inputs
10
+ * from parent variables before dispatch": the child receives
11
+ * `inputs[childKey] = parentVariables[parentKey]`.
12
+ * projection the child's `inputs.childGreeting` is the parent's
13
+ * `parentName` ("Alice");
14
+ * unset an unset `parentName` projects to undefined — the child's
15
+ * `inputs.childGreeting` is absent, never `null` or a placeholder.
16
+ *
17
+ * Supervisor loop (`execution.md` §`multiAgent`, level 1 and level 5), on
18
+ * `conformance-dispatch-loop`:
19
+ * terminates the run completes; each turn records a decision as
20
+ * `runOrchestrator.decided` (`orchestrator.decided` at major 2),
21
+ * and the last is `terminate` — `terminate` completes the run,
22
+ * so no decision follows it;
23
+ * decisions every decision payload is well-formed (the decision checked
24
+ * branch by branch — see {@link decisionBranchValidator}), and
25
+ * its `agentId` is the same for the run's lifetime;
26
+ * iteration at `executionModel.version >= 5`, every decision carries
27
+ * `iteration`, 1-based, incremented by exactly 1 per turn.
28
+ *
29
+ * The drivers observe and assert nothing; the judges are pure, proven in
30
+ * `dispatch-witness.test.ts`.
31
+ */
32
+
33
+ import { isFixtureAdvertised } from './fixtures.js';
34
+ import type { MajorProfile } from './major-profile.js';
35
+ import {
36
+ completionsOf, finding, isRecord, observed, observeRun, readRun, runCompleted, skip,
37
+ type Ev, type Finding, type ObservedRun, type ObserveOpts, type Outcome, type RunRead, type Skip, type Validate,
38
+ } from './fixture-run-observer.js';
39
+
40
+ export const DISPATCH_PARENT_FIXTURE = 'conformance-dispatch-input-mapping';
41
+ export const DISPATCH_NO_DEFAULT_FIXTURE = 'conformance-dispatch-input-mapping-no-default';
42
+ export const DISPATCH_CHILD_FIXTURE = 'conformance-dispatch-input-mapping-child';
43
+ export const DISPATCH_NODE = 'dispatch';
44
+ export const LOOP_FIXTURE = 'conformance-dispatch-loop';
45
+
46
+ const DOC_MAP = 'agents.schema.json dispatchMapping; dispatch-config.schema.json inputMapping';
47
+ const DOC_LOOP = 'execution.md §multiAgent';
48
+
49
+ export interface DispatchObservation {
50
+ readonly parent: ObservedRun;
51
+ /** How the child was found: the parent's `node.dispatched`, or the dispatch node's `node.completed` outputs. */
52
+ readonly childRunId: string | undefined;
53
+ readonly child: RunRead | undefined;
54
+ }
55
+
56
+ /** `agents.dispatchMapping: true`, the parent fixture and the child fixture. */
57
+ export function dispatchMappingGate(profile: MajorProfile, doc: unknown, fixture: string): Skip | null {
58
+ const agents = profile.family(doc, 'agents');
59
+ if (agents === null) return skip('inapplicable', 'the host does not advertise agents');
60
+ if (agents['dispatchMapping'] !== true) return skip('inapplicable', 'the host does not advertise agents.dispatchMapping: true, so inputMapping does not bind it (it must refuse the field at registration instead)');
61
+ for (const id of [fixture, DISPATCH_CHILD_FIXTURE]) if (!isFixtureAdvertised(id)) return skip('inapplicable', `fixture ${id} is not advertised — the fixture is the opt-in`);
62
+ return null;
63
+ }
64
+
65
+ /** Find the dispatched child: `node.dispatched` naming the child workflow, else the dispatch node's outputs. */
66
+ export function locateChild(profile: MajorProfile, events: readonly Ev[], childWorkflowId: string): string | undefined {
67
+ const dispatchedType = profile.eventType('node.dispatched') ?? 'node.dispatched';
68
+ const d = events.find((e) => e.type === dispatchedType && e.payload['childWorkflowId'] === childWorkflowId && typeof e.payload['childRunId'] === 'string');
69
+ if (d !== undefined) return d.payload['childRunId'] as string;
70
+ for (const c of completionsOf(events, DISPATCH_NODE)) {
71
+ const out = c.payload['outputs'];
72
+ if (isRecord(out) && typeof out['childRunId'] === 'string' && out['childRunId'].length > 0) return out['childRunId'];
73
+ }
74
+ return undefined;
75
+ }
76
+
77
+ /** Run a dispatch input-mapping fixture and follow it to the child. Asserts nothing. */
78
+ export async function driveDispatchMapping(profile: MajorProfile, doc: unknown, fixture: string, opts: ObserveOpts = {}): Promise<Skip | DispatchObservation> {
79
+ const g = dispatchMappingGate(profile, doc, fixture);
80
+ if (g) return g;
81
+ const parent = await observeRun(profile, fixture, opts);
82
+ const childRunId = locateChild(profile, parent.events, DISPATCH_CHILD_FIXTURE);
83
+ return { parent, childRunId, child: childRunId === undefined ? undefined : await readRun(profile, childRunId) };
84
+ }
85
+
86
+ function childInputs(o: DispatchObservation): Skip | { inputs: Record<string, unknown>; reached: Finding[] } {
87
+ if (o.parent.createStatus !== 201 && o.parent.createStatus !== 202) return { inputs: {}, reached: runCompleted(o.parent) };
88
+ if (o.childRunId === undefined) return skip('inapplicable', `no v2 rule requires a host to expose the dispatched child, and this one does not: the parent log names no ${DISPATCH_CHILD_FIXTURE} run (no node.dispatched, no dispatch-node outputs.childRunId), so its inputs cannot be read`);
89
+ const c = o.child;
90
+ return {
91
+ inputs: c !== undefined && isRecord(c.snapshot['inputs']) ? c.snapshot['inputs'] : {},
92
+ reached: [
93
+ ...runCompleted(o.parent),
94
+ finding(c?.status === 200, 'runs.md getRun', `the dispatched child run MUST be readable (GET answered ${String(c?.status)})`),
95
+ finding(c?.snapshot['status'] === 'completed', `fixtures.md ${DISPATCH_CHILD_FIXTURE}`, `the child run MUST end completed (got ${JSON.stringify(c?.snapshot['status'])})`),
96
+ ],
97
+ };
98
+ }
99
+
100
+ /** Leg: the child's inputs carry the parent's projected variable. Pure. */
101
+ export function judgeProjection(o: DispatchObservation): Outcome {
102
+ const c = childInputs(o);
103
+ if ('kind' in c) return c;
104
+ return observed([
105
+ ...c.reached,
106
+ finding(c.inputs['childGreeting'] === 'Alice', DOC_MAP, `the child MUST receive inputs.childGreeting = parentVariables.parentName ("Alice"; got ${JSON.stringify(c.inputs['childGreeting'])})`),
107
+ ]);
108
+ }
109
+
110
+ /** Leg: an unset parent variable projects to undefined — absent, never null. Pure. */
111
+ export function judgeUnsetProjection(o: DispatchObservation): Outcome {
112
+ const c = childInputs(o);
113
+ if ('kind' in c) return c;
114
+ return observed([
115
+ ...c.reached,
116
+ finding(c.inputs['childGreeting'] !== null, `${DOC_MAP} (mirrors execution.md §subWorkflow inputMapping: never null)`, 'an unset parent variable MUST NOT project as null'),
117
+ finding(!('childGreeting' in c.inputs), DOC_MAP, `inputs[childKey] = parentVariables[parentKey] is undefined for an unset parent variable: inputs.childGreeting MUST be absent (got ${JSON.stringify(c.inputs['childGreeting'])})`),
118
+ ]);
119
+ }
120
+
121
+ export interface LoopObservation { readonly run: ObservedRun; readonly decidedType: string; readonly version: number }
122
+
123
+ /** `multiAgent.executionModel.version >= 1`, the fixture, and this major's event name. */
124
+ export function loopGate(profile: MajorProfile, doc: unknown): Skip | { decidedType: string; version: number } {
125
+ const ma = profile.family(doc, 'multiAgent');
126
+ const em = isRecord(ma?.['executionModel']) ? ma['executionModel'] : undefined;
127
+ const version = em?.['version'];
128
+ if (typeof version !== 'number' || version < 1) return skip('inapplicable', 'the host does not advertise multiAgent.executionModel (version >= 1), so the supervisor loop does not bind it');
129
+ if (!isFixtureAdvertised(LOOP_FIXTURE)) return skip('inapplicable', `fixture ${LOOP_FIXTURE} is not advertised — the fixture is the opt-in`);
130
+ const decidedType = profile.eventType('runOrchestrator.decided');
131
+ if (decidedType === undefined) return skip('blocked', 'the event codemap is not on disk in this layout — the suite will not guess an event name');
132
+ return { decidedType, version };
133
+ }
134
+
135
+ /** Run the loop fixture. Asserts nothing. */
136
+ export async function driveLoop(profile: MajorProfile, doc: unknown, opts: ObserveOpts = {}): Promise<Skip | LoopObservation> {
137
+ const g = loopGate(profile, doc);
138
+ if ('kind' in g) return g;
139
+ return { run: await observeRun(profile, LOOP_FIXTURE, opts), ...g };
140
+ }
141
+
142
+ const decisions = (o: LoopObservation): Ev[] => o.run.events.filter((e) => e.type === o.decidedType);
143
+ const kindOf = (e: Ev): unknown => (isRecord(e.payload['decision']) ? e.payload['decision']['kind'] : undefined);
144
+
145
+ /** Leg: decisions are recorded, the last is `terminate`, and it completes the run. Pure. */
146
+ export function judgeLoopTerminates(o: LoopObservation): Outcome {
147
+ const ds = decisions(o);
148
+ const firstTerminate = ds.findIndex((e) => kindOf(e) === 'terminate');
149
+ return observed([
150
+ ...runCompleted(o.run),
151
+ finding(ds.length > 0, `${DOC_LOOP} (level 1)`, `each turn MUST record its decision as ${o.decidedType} (got none)`),
152
+ finding(ds.length > 0 && kindOf(ds[ds.length - 1]!) === 'terminate', `${DOC_LOOP} (level 1: terminate completes the run)`, `the completed loop's last decision MUST be terminate (got ${JSON.stringify(ds.map(kindOf))})`),
153
+ finding(firstTerminate === -1 || firstTerminate === ds.length - 1, `${DOC_LOOP} (level 1: terminate completes the run)`, `no decision may follow a terminate (got ${JSON.stringify(ds.map(kindOf))})`),
154
+ ]);
155
+ }
156
+
157
+ /**
158
+ * A validator for one `OrchestratorDecision`, by its `oneOf` branches.
159
+ *
160
+ * `schemas/v2/orchestrator-decision.schema.json` closes its ROOT with
161
+ * `additionalProperties: false` and declares no root `properties`, so the
162
+ * schema as a whole rejects every decision, `kind` included (the v1 root is
163
+ * open). Validating against it would convict every host. Until the corpus
164
+ * fixes it, a decision is valid when exactly one branch accepts it.
165
+ */
166
+ export function decisionBranchValidator(branch: (index: number) => Validate, branches = 3): Validate {
167
+ const vs = Array.from({ length: branches }, (_, i) => branch(i));
168
+ return (doc) => {
169
+ const results = vs.map((v) => v(doc));
170
+ const ok = results.filter((r) => r.ok).length === 1;
171
+ return { ok, errors: ok ? '' : results.map((r, i) => `branch ${i}: ${r.errors || 'accepted'}`).join(' | ') };
172
+ };
173
+ }
174
+
175
+ const PAYLOAD_KEYS = new Set(['agentId', 'decision', 'iteration', 'causationHostId']);
176
+ const AGENT_ID = /^[A-Za-z0-9._~:-]{1,128}$/;
177
+
178
+ /**
179
+ * Leg: every decision payload is well-formed (the `runOrchestratorDecided`
180
+ * def's closed key set and `agentId`; the decision by its branches), and the
181
+ * deciding agent never changes. Pure.
182
+ */
183
+ export function judgeDecisions(o: LoopObservation, validateDecision: Validate): Outcome {
184
+ const ds = decisions(o);
185
+ if (ds.length === 0) return skip('inapplicable', `the loop recorded no ${o.decidedType}, so there is nothing to judge here — the terminates leg convicts that`);
186
+ const agents = new Set(ds.map((e) => e.payload['agentId']));
187
+ const DEF = 'schemas run-event-payloads runOrchestratorDecided';
188
+ return observed([
189
+ ...ds.flatMap((e) => {
190
+ const extra = Object.keys(e.payload).filter((k) => !PAYLOAD_KEYS.has(k));
191
+ const agentId = e.payload['agentId'];
192
+ const v = validateDecision(e.payload['decision']);
193
+ return [
194
+ finding(extra.length === 0, DEF, `a ${o.decidedType} payload carries only agentId, decision, iteration, causationHostId (seq ${e.sequence}; extra ${JSON.stringify(extra)})`),
195
+ finding(typeof agentId === 'string' && AGENT_ID.test(agentId), `${DEF}.agentId`, `every ${o.decidedType} MUST carry agentId (seq ${e.sequence}; got ${JSON.stringify(agentId)})`),
196
+ finding(v.ok, 'schemas orchestrator-decision', `every decision MUST be one OrchestratorDecision (seq ${e.sequence}): ${v.errors}`),
197
+ ];
198
+ }),
199
+ finding(agents.size === 1, `${DEF}.agentId`, `orchestrator identity is set at the first decision and does not change (got ${JSON.stringify([...agents])})`),
200
+ ]);
201
+ }
202
+
203
+ /** Leg: at version >= 5, `iteration` is 1-based and +1 per turn. Pure. */
204
+ export function judgeIteration(o: LoopObservation): Outcome {
205
+ if (o.version < 5) return skip('inapplicable', `the host advertises multiAgent.executionModel.version ${o.version}; iteration binds from level 5`);
206
+ const ds = decisions(o);
207
+ if (ds.length === 0) return skip('inapplicable', `the loop recorded no ${o.decidedType}, so there is nothing to judge here — the terminates leg convicts that`);
208
+ const its = ds.map((e) => e.payload['iteration']);
209
+ return observed([
210
+ finding(its.every((n, i) => n === i + 1), `${DOC_LOOP} (level 5)`, `every ${o.decidedType} MUST carry iteration, 1-based and incremented by exactly 1 per turn (got ${JSON.stringify(its)})`),
211
+ ]);
212
+ }