@openwop/openwop-conformance 2.46.2 → 2.46.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.
- package/CHANGELOG.md +12 -0
- package/README.md +2 -2
- package/dist/spec-artifacts.lock.json +2 -2
- package/package.json +2 -2
- package/requirements.json +60 -4
- package/scenario-majors.json +8 -2
- package/schemas/CORPUS-STAMP.json +14 -14
- package/src/lib/family-advert-witness.ts +86 -1
- package/src/scenarios/v2-auth-subject-link-key.test.ts +42 -0
- package/src/scenarios/v2-multi-agent-advertisement.test.ts +67 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# `@openwop/openwop-conformance` Changelog
|
|
2
2
|
|
|
3
|
+
## [2.46.3] — 2026-10-12 — discovery rules the schema cannot carry
|
|
4
|
+
|
|
5
|
+
Four rules from openwop-app's v2 discovery review (#4676, ADR 0858, ADR 0812). Each is stated in prose or a schema description, and none can go into the schema within 2.x: a conditional `required` or a narrowed pattern is a major change (COMPATIBILITY.md §2.4). The suite carries them, following RFC 0210 §C; the schema changes are booked for 3.0 (TODO.md).
|
|
6
|
+
|
|
7
|
+
- **New `v2-multi-agent-advertisement`** (claims-check, wave 4 of `family-advert-witness.ts`):
|
|
8
|
+
- `multiAgent.level3-cross-host-causation`: at `executionModel.version` >= 3 the host advertises `crossHostCausation` with a non-empty `hostId`. The levels are cumulative, so the description's "`version: 3`" now reads "3 or higher".
|
|
9
|
+
- `multiAgent.level4-replay-determinism`: at >= 4, `replayDeterminism` with `refusalDivergenceEmission: true`.
|
|
10
|
+
- `multiAgent.escalation-kind-canonical`: `confidenceEscalationInterruptKind` is `clarification` or `approval`. Level 2 fires one of the two, and the v2 interrupt `kind` is closed, so an `x-host-` value promises an interrupt the payload schema rejects. The description no longer points at `spec/v1/host-extensions.md` or an RFC 0044 §C kind-mapping document.
|
|
11
|
+
- **New `v2-auth-subject-link-key`:** a host advertising both the `saml` and `scim` lanes advertises `auth.subjectLinkKey` (`identity.md` §3 now says MUST, as RFC 0164 §A.3 did at v1). Forbidding the key with one lane is a new rule and needs an RFC.
|
|
12
|
+
- **Fan-out has no v2 seat.** The `dispatch-config` and `orchestrator-decision` descriptions named `capabilities.dispatch.*` and `POST /v1/workflows`. They now say plainly that a v2 host refuses `fanOutPolicy: "parallel"` and a non-empty `nextWorkerInputs` until a seat is added. That was already the rule, because the seat could not be advertised. `dispatch-config.schema.json` is hand-owned from here. Adding one needs an RFC.
|
|
13
|
+
- **What a host's next bundle shows:** MyndHyve advertises `x-host-myndhyve-low-confidence` and will record `escalation-kind-canonical` `executed-fail`. Its committed bundles stay valid measurements at their suite versions (§2.3). openwop-app (level 5) and the v2 reference host pass or are `inapplicable`.
|
|
14
|
+
|
|
3
15
|
## [2.46.2] — 2026-10-12 — the seams profile is derived from its advert
|
|
4
16
|
|
|
5
17
|
- **`openwop-conformance-seams-v2` derives from `conformance.seamsProfile`, not from any `conformance` block.** The predicate was the bare `conformance` metadata key, which also carries `certificationBundleUrl` and `mockAgent`. A host that published only a bundle URL claimed the profile, so its claim flapped with whether the URL was set (openwop-app, 7 cuts on 2.45.30 with no host change), and four openwop-app bundles (`openwop-workflow-engine-2.35.1`, `-2.43.0`, `-2.44.0`, `-2.45.28-4683d9f89`) certified it with no seam mounted. A predicate `metadata` entry may now name one member, `key.member` (capabilities.md §7); `v2ProfileIds` and `v2-profiles-derived-only` share one `metadataPresent`.
|
package/README.md
CHANGED
|
@@ -137,7 +137,7 @@ Each scenario file records its disposition and assertion count in `<report-dir>/
|
|
|
137
137
|
|
|
138
138
|
## What's Covered
|
|
139
139
|
|
|
140
|
-
The current suite has
|
|
140
|
+
The current suite has 655 scenario files under `src/scenarios/`. By target major ([`scenario-majors.json`](./scenario-majors.json)): 215 files run against a v2 host and 455 against a v1 host; 15 of them run at both.
|
|
141
141
|
|
|
142
142
|
The v2 scenarios (`src/scenarios/v2-*.test.ts`) cover, by area:
|
|
143
143
|
|
|
@@ -166,7 +166,7 @@ Three kinds of scenario share the tree:
|
|
|
166
166
|
- **Host** checks drive a live host over HTTP.
|
|
167
167
|
- **Gated** checks run only when the host advertises the family, profile or fixture they need, and otherwise record `inapplicable` or `blocked` with a reason. `--require-behavior` turns an advertised-but-unwitnessable behaviour into a failure.
|
|
168
168
|
|
|
169
|
-
Current source tree:
|
|
169
|
+
Current source tree: 655 scenario files. [`coverage.md`](./coverage.md) maps each spec document and every OpenAPI operation to its scenarios. [`CHANGELOG.md`](./CHANGELOG.md) records when each scenario landed.
|
|
170
170
|
|
|
171
171
|
## How the 2.x suite is built
|
|
172
172
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openwop/openwop-conformance",
|
|
3
|
-
"version": "2.46.
|
|
3
|
+
"version": "2.46.3",
|
|
4
4
|
"description": "Black-box conformance suite for OpenWOP hosts (v2 current; v1 scenarios retained for the frozen v1 tree).",
|
|
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.46.
|
|
59
|
+
"@openwop/spec-artifacts": "2.46.3"
|
|
60
60
|
}
|
|
61
61
|
}
|
package/requirements.json
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"$comment": "GENERATED by conformance/scripts/generate-requirement-registry.mjs — do not edit. One record per it()/test() in src/scenarios. Ids: openwop.it.<file-stem>.<title-slug>[~n] (src/lib/requirement-ids.ts). A record with id null has an interpolated title; its run-time row is keyed by the rendered title and maps here by file+line only. Renamed ids need a row in requirement-aliases.json.",
|
|
3
3
|
"generatedFrom": "src/scenarios/*.test.ts",
|
|
4
4
|
"counts": {
|
|
5
|
-
"files":
|
|
6
|
-
"tests":
|
|
7
|
-
"withStableId":
|
|
5
|
+
"files": 743,
|
|
6
|
+
"tests": 2794,
|
|
7
|
+
"withStableId": 2794,
|
|
8
8
|
"interpolatedTitles": 0,
|
|
9
|
-
"explicitIds":
|
|
9
|
+
"explicitIds": 2650
|
|
10
10
|
},
|
|
11
11
|
"records": [
|
|
12
12
|
{
|
|
@@ -28670,6 +28670,20 @@
|
|
|
28670
28670
|
}
|
|
28671
28671
|
]
|
|
28672
28672
|
},
|
|
28673
|
+
{
|
|
28674
|
+
"id": "openwop.it.v2-auth-subject-link-key.a-host-advertising-both-the-saml-and-scim-lanes-advertises-subjectlinkkey",
|
|
28675
|
+
"file": "v2-auth-subject-link-key.test.ts",
|
|
28676
|
+
"line": 34,
|
|
28677
|
+
"title": "a host advertising both the saml and scim lanes advertises subjectLinkKey",
|
|
28678
|
+
"explicitId": "openwop.requirement.auth.subject-link-key-advertised",
|
|
28679
|
+
"citations": [
|
|
28680
|
+
{
|
|
28681
|
+
"section": null,
|
|
28682
|
+
"requirement": null,
|
|
28683
|
+
"interpolated": true
|
|
28684
|
+
}
|
|
28685
|
+
]
|
|
28686
|
+
},
|
|
28673
28687
|
{
|
|
28674
28688
|
"id": "openwop.it.v2-authorization-advertisement.the-authorization-record-validates-against-its-capabilities-schema-seat",
|
|
28675
28689
|
"file": "v2-authorization-advertisement.test.ts",
|
|
@@ -33493,6 +33507,48 @@
|
|
|
33493
33507
|
}
|
|
33494
33508
|
]
|
|
33495
33509
|
},
|
|
33510
|
+
{
|
|
33511
|
+
"id": "openwop.it.v2-multi-agent-advertisement.executionmodel-version-3-advertises-crosshostcausation-with-a-hostid",
|
|
33512
|
+
"file": "v2-multi-agent-advertisement.test.ts",
|
|
33513
|
+
"line": 41,
|
|
33514
|
+
"title": "executionModel.version >= 3 advertises crossHostCausation with a hostId",
|
|
33515
|
+
"explicitId": "openwop.requirement.multiAgent.level3-cross-host-causation",
|
|
33516
|
+
"citations": [
|
|
33517
|
+
{
|
|
33518
|
+
"section": null,
|
|
33519
|
+
"requirement": null,
|
|
33520
|
+
"interpolated": true
|
|
33521
|
+
}
|
|
33522
|
+
]
|
|
33523
|
+
},
|
|
33524
|
+
{
|
|
33525
|
+
"id": "openwop.it.v2-multi-agent-advertisement.executionmodel-version-4-advertises-replaydeterminism-with-refusaldivergenceemis",
|
|
33526
|
+
"file": "v2-multi-agent-advertisement.test.ts",
|
|
33527
|
+
"line": 50,
|
|
33528
|
+
"title": "executionModel.version >= 4 advertises replayDeterminism with refusalDivergenceEmission",
|
|
33529
|
+
"explicitId": "openwop.requirement.multiAgent.level4-replay-determinism",
|
|
33530
|
+
"citations": [
|
|
33531
|
+
{
|
|
33532
|
+
"section": null,
|
|
33533
|
+
"requirement": null,
|
|
33534
|
+
"interpolated": true
|
|
33535
|
+
}
|
|
33536
|
+
]
|
|
33537
|
+
},
|
|
33538
|
+
{
|
|
33539
|
+
"id": "openwop.it.v2-multi-agent-advertisement.confidenceescalationinterruptkind-is-clarification-or-approval",
|
|
33540
|
+
"file": "v2-multi-agent-advertisement.test.ts",
|
|
33541
|
+
"line": 59,
|
|
33542
|
+
"title": "confidenceEscalationInterruptKind is clarification or approval",
|
|
33543
|
+
"explicitId": "openwop.requirement.multiAgent.escalation-kind-canonical",
|
|
33544
|
+
"citations": [
|
|
33545
|
+
{
|
|
33546
|
+
"section": null,
|
|
33547
|
+
"requirement": null,
|
|
33548
|
+
"interpolated": true
|
|
33549
|
+
}
|
|
33550
|
+
]
|
|
33551
|
+
},
|
|
33496
33552
|
{
|
|
33497
33553
|
"id": "openwop.it.v2-multi-party-conversation-advertisement.the-multipartyconversation-record-validates-against-its-capabilities-schema-seat",
|
|
33498
33554
|
"file": "v2-multi-party-conversation-advertisement.test.ts",
|
package/scenario-majors.json
CHANGED
|
@@ -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":
|
|
4
|
+
"files": 655,
|
|
5
5
|
"v1": 455,
|
|
6
|
-
"v2":
|
|
6
|
+
"v2": 215
|
|
7
7
|
},
|
|
8
8
|
"majors": {
|
|
9
9
|
"a2a-1-0-agent-card.test.ts": [
|
|
@@ -1329,6 +1329,9 @@
|
|
|
1329
1329
|
"v2-auth-challenge.test.ts": [
|
|
1330
1330
|
2
|
|
1331
1331
|
],
|
|
1332
|
+
"v2-auth-subject-link-key.test.ts": [
|
|
1333
|
+
2
|
|
1334
|
+
],
|
|
1332
1335
|
"v2-authorization-advertisement.test.ts": [
|
|
1333
1336
|
2
|
|
1334
1337
|
],
|
|
@@ -1569,6 +1572,9 @@
|
|
|
1569
1572
|
"v2-mrtr-rounds-ceiling.test.ts": [
|
|
1570
1573
|
2
|
|
1571
1574
|
],
|
|
1575
|
+
"v2-multi-agent-advertisement.test.ts": [
|
|
1576
|
+
2
|
|
1577
|
+
],
|
|
1572
1578
|
"v2-multi-party-conversation-advertisement.test.ts": [
|
|
1573
1579
|
2
|
|
1574
1580
|
],
|
|
@@ -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.46.
|
|
5
|
-
"corpusTag": "v2.46.
|
|
4
|
+
"version": "2.46.3",
|
|
5
|
+
"corpusTag": "v2.46.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
10
|
"api/openapi.yaml": "ac6de5303872664b78ff419c035915593e01599e02835e777a0123de1baeb398",
|
|
11
11
|
"api/redocly.yaml": "b0604c89b2ca6d5076ec25725c539dad44a741a811fe524439ee6daef8baa09f",
|
|
12
|
-
"api/seams-v2.yaml": "
|
|
13
|
-
"api/v2/asyncapi.yaml": "
|
|
14
|
-
"api/v2/openapi.yaml": "
|
|
12
|
+
"api/seams-v2.yaml": "cb175971637256b36144514f7dbda94281892a1de32afb4eb2bb2e515e8029a2",
|
|
13
|
+
"api/v2/asyncapi.yaml": "cb8c4a0ab6b398fd32f35d5ad496e6fbf115f4af1546533440f1827cc660aaba",
|
|
14
|
+
"api/v2/openapi.yaml": "f4fef925c3013db82449498de884da1ae38a19f802968a0adf2d7c49aa927a0b",
|
|
15
15
|
"api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
|
|
16
16
|
"schemas/README.md": "276fc1d51eb08e14e9127b9fd27761d7046ee33c12000f3a64a801322db8bf58",
|
|
17
17
|
"schemas/a2a-task-state.schema.json": "75d5049dea8bd873ff0e7546f1c60c8d36c219bec7084264360be54a8be30ae1",
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
"schemas/v2/artifact.schema.json": "85b9dc284d39b99d2a66fa8cbf47f2dd3d8526d3bd6cc411ad315b372de38975",
|
|
118
118
|
"schemas/v2/audit-verify-result.schema.json": "14af8e9283510f0a40e48a2059c929ee44e0fc9fa3cc66a95bfbe90b17c48297",
|
|
119
119
|
"schemas/v2/budget-policy.schema.json": "1de68095db0beb5c7a4be629de26e79f62ebbe748a88d46f36e768edfd1f0d68",
|
|
120
|
-
"schemas/v2/capabilities.schema.json": "
|
|
120
|
+
"schemas/v2/capabilities.schema.json": "6dc5d67585d6e9a18ab6a0b9b4c9b70b821517b401218fa855539636fb0b5974",
|
|
121
121
|
"schemas/v2/certification-bundle.schema.json": "5cfa7fb5247160333e33a58b60d42bcd4e5269d8391d6a112d301e14828892ed",
|
|
122
122
|
"schemas/v2/channel-presence-payload.schema.json": "fc03df0c20cc12a25f7ee6a47503a4c5931d79fe3d5ff24815e8fd7c1bcb9a87",
|
|
123
123
|
"schemas/v2/channel-written-payload.schema.json": "ccecff3c71a3275ad8db035ac0e04db25e6f8d23cf091ddbcd5a60d1be1cbb6b",
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
"schemas/v2/credential-provenance.schema.json": "f86b09e94b1c524dec7d1c6353965665ae8891e9517faef6e742d639c5578c99",
|
|
134
134
|
"schemas/v2/credential-reference.schema.json": "ea699ce30f58fb2c32d4279158b3f730f70be7b2dc8c3037693a76e66eb3e001",
|
|
135
135
|
"schemas/v2/debug-bundle.schema.json": "0ec1e0bca9c0a359cc6cf3448e5200a1c3d3d5b1e5efaa6605231477daaf3fee",
|
|
136
|
-
"schemas/v2/dispatch-config.schema.json": "
|
|
136
|
+
"schemas/v2/dispatch-config.schema.json": "c2da8c7075e120c6204499a48dbf1a95a54e30ab897331425ac37660a53ad216",
|
|
137
137
|
"schemas/v2/effect-ledger-projection.schema.json": "0d118d385a43921118a76888449c5c3c5618eab1b80914b84a0a1b48d18822c1",
|
|
138
138
|
"schemas/v2/effect-seam-manifest.schema.json": "197ef9944ab027a50d1b9352f317cbbc12c8ad955fdb14650fb90477052ffef0",
|
|
139
139
|
"schemas/v2/envelopes/clarification.request.schema.json": "9e39d7b09f0b3ce7902d6aa19ca0eb52867b634efc78a881469c4d44fb3333e7",
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
"schemas/v2/memory-entry.schema.json": "290576ae84f4fef4cdf975932b87963283b11c50ea3d2654b32cd96aa6537c3d",
|
|
162
162
|
"schemas/v2/memory-list-options.schema.json": "01ceb7370c4eacd227d2c9b7e1951ee16650abe26a32dc9d6a8e46a07a39093f",
|
|
163
163
|
"schemas/v2/node-pack-manifest.schema.json": "3eb2db1d84f0b8dbde917686c72c121788a4ce6e42cf13d497a3b68564546d0e",
|
|
164
|
-
"schemas/v2/orchestrator-decision.schema.json": "
|
|
164
|
+
"schemas/v2/orchestrator-decision.schema.json": "1b8146b1d050dc48e3900928c5a0b89874433b3b7fa9ee59c037557b80bd0910",
|
|
165
165
|
"schemas/v2/org-chart-responsibility-view.schema.json": "8a2565e4cdc6737a3946a70799f93642808b92f9c8d56dcdff3a0782053b89a1",
|
|
166
166
|
"schemas/v2/pack-lockfile.schema.json": "2685417702c105146b780cd99154296a46578fbb10d81c8c713348ee26fea898",
|
|
167
167
|
"schemas/v2/part.schema.json": "08b75eaec9a0b4a8cedca7bab732dd906b52b0749eaa444ec1ac015aa43519a5",
|
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
"schemas/workspace-file.schema.json": "464de85c2a068243084ee9c1d969bc7cd5d8f7948574e58450d6493c38a0e1e4",
|
|
209
209
|
"spec/v1/alias-detectors.json": "22444c22bb88c568339ef512553f9fc38146e22b566d11ab8608f655d688d19c",
|
|
210
210
|
"spec/v1/capability-declaration-classes.json": "e7729aed5c4b4e1dd02abab0530f14cc95f5d4070fe51fb139e7f5cccefa00c6",
|
|
211
|
-
"spec/v1/core-standard-manifest.json": "
|
|
211
|
+
"spec/v1/core-standard-manifest.json": "c65192dc000e6ad0f7b6eb641ddbf59a905fb09a702cb23d31b8cc807a85b81c",
|
|
212
212
|
"spec/v1/deprecations.json": "e723bc11d7272c71789ad1303bb7f21809a8a024abf57594f9d5c35dc199ece9",
|
|
213
213
|
"spec/v1/deprecations.schema.json": "3e393c405d2a41b467d8c5e3c468549078df1ce6a6d2588fc488097b95d9b55b",
|
|
214
214
|
"spec/v1/end-of-support.json": "35f0bf3b70fe78b16e2a2fd81a153fc27e703f6c16376c6fe7cdf1f4f2c4e6d4",
|
|
@@ -229,13 +229,13 @@
|
|
|
229
229
|
"spec/v2/core/conversation.md": "8d6abedf6af408a74577753bfca24719d318e2632e10b45ff5d63bdcf31a8aaa",
|
|
230
230
|
"spec/v2/core/errors.md": "e48838701b86bbfe0ffee77b18a8ffc4e5f04724b4318507d9771da653f8da0f",
|
|
231
231
|
"spec/v2/core/events.md": "370621629c2439747f2e4ddab4780eea4345354f79e5098739d66823873f3105",
|
|
232
|
-
"spec/v2/core/execution.md": "
|
|
232
|
+
"spec/v2/core/execution.md": "2cabb386aea55f70b6e4d10b2b637819589b6cf980325853024ee4b5a3f346d5",
|
|
233
233
|
"spec/v2/core/form-content-packs.md": "02931227899676fd4581a27017be2a6c4953904717be9b81c29d12f87ab3917d",
|
|
234
234
|
"spec/v2/core/headers.md": "f69fe81e4a3ad21879bb85948660c16860eb05bf152928c31aa4898f6d7d1887",
|
|
235
235
|
"spec/v2/core/host-services.md": "168a40935a03867ee82461b5d83d05578be25ee3e5af4eb080587b891231e242",
|
|
236
236
|
"spec/v2/core/i18n.md": "882904b7d59f992ab1e5f7346167161a54cf665b8fee47e64a3862cf9133a580",
|
|
237
237
|
"spec/v2/core/idempotency.md": "adbd317a6ebb57c2759c076297b31f67def9e391576bccd0bbea787b13e85872",
|
|
238
|
-
"spec/v2/core/identity.md": "
|
|
238
|
+
"spec/v2/core/identity.md": "3110138944189c378b0ca959ce1bfe5e808e6fdfc9481afeee128c899db05547",
|
|
239
239
|
"spec/v2/core/interop.md": "6ec8259117bdb387b9323ca22c707b3b8571c00034e461c4ce79f4070c4d6b4a",
|
|
240
240
|
"spec/v2/core/interrupt.md": "10f15dfe30baa0fc748aea0a067f9b271bea316fde6b598d78f77edc296f698a",
|
|
241
241
|
"spec/v2/core/node-pack-runtimes.md": "e1088cb69b8c3c67256c494063328916c2980ab1aa97a7063de25bab82fdb1e5",
|
|
@@ -313,9 +313,9 @@
|
|
|
313
313
|
"spec/v2/path-manifest.json": "7b5679a4dad98c80d855361d88a98c8a4e045af15e310862a05b285b815dc9fc",
|
|
314
314
|
"spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
|
|
315
315
|
"spec/v2/profiles.json": "b382b261c7844798575f7c1b37c934f0da19846016a2337d883d4f5ef6e05479",
|
|
316
|
-
"spec/v2/release.json": "
|
|
316
|
+
"spec/v2/release.json": "86eefef2a95ae211e813e113b435bdefe8319f263ad43d005362a6ded3db4aaa",
|
|
317
317
|
"spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624",
|
|
318
|
-
"spec/v2/surface-baseline.json": "
|
|
318
|
+
"spec/v2/surface-baseline.json": "046cfc5a89c711a4b06244389ba2131d89d933cc9c888c78b68f36779914228c"
|
|
319
319
|
},
|
|
320
|
-
"corpusCommit": "
|
|
320
|
+
"corpusCommit": "4b2a2a43ddd40943ebf4c332592171c3587c6e15"
|
|
321
321
|
}
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
* facet legs; `conversationTurnModelProvenance`, `channelPresence`,
|
|
10
10
|
* `nondeterminismPolicy`, `promptLibrary` and `envelopeContracts` carry no facet
|
|
11
11
|
* a host-binding v2 rule gives meaning to, so they get the record leg only.
|
|
12
|
+
* Wave 4 (suite 2.46.3): the `multiAgent.executionModel` sub-blocks a version
|
|
13
|
+
* level requires and the escalation kind it may name, and `auth.subjectLinkKey`
|
|
14
|
+
* under the two linking lanes. These rules live in prose and schema
|
|
15
|
+
* descriptions, not in the schema itself: a conditional `required` or a narrowed
|
|
16
|
+
* enum is a major change (COMPATIBILITY.md §2.4), so the suite carries them.
|
|
12
17
|
*
|
|
13
18
|
* A claims-check family is witnessed by what the host says about itself, so
|
|
14
19
|
* every leg here reads the discovery record alone. Each leg checks one facet
|
|
@@ -69,7 +74,8 @@ export type AdvertFamily =
|
|
|
69
74
|
| 'scheduling' | 'artifactTypes' | 'aiEnvelope' | 'deadLetter' | 'portability'
|
|
70
75
|
| 'dataResidency' | 'conversationTurnModelProvenance' | 'multiPartyConversation' | 'channelPresence'
|
|
71
76
|
| 'nodePackRuntimes' | 'uiPlugins' | 'purposePropagation' | 'nondeterminismPolicy'
|
|
72
|
-
| 'agentRuntime' | 'promptLibrary' | 'envelopeContracts'
|
|
77
|
+
| 'agentRuntime' | 'promptLibrary' | 'envelopeContracts'
|
|
78
|
+
| 'multiAgent' | 'auth';
|
|
73
79
|
|
|
74
80
|
export function recordSchemaLeg(profile: MajorProfile, discovery: unknown, family: AdvertFamily, doc: string): AdvertOutcome {
|
|
75
81
|
return onFamily(profile, discovery, family, (rec) => observed([bySchema(`/properties/${family}`, rec, doc, `the ${family} record`)]));
|
|
@@ -409,3 +415,82 @@ export const agentRuntimeImpliesManifestRuntimeLeg = (p: MajorProfile, d: unknow
|
|
|
409
415
|
const ok = agents !== null && isRecord(agents['manifestRuntime']);
|
|
410
416
|
return observed([{ ok, doc: 'host-services.md §agentRuntime', message: `a host advertising agentRuntime MUST also advertise agents.manifestRuntime${ok ? '' : agents === null ? ' (agents is absent)' : ' (agents.manifestRuntime is absent)'}` }]);
|
|
411
417
|
});
|
|
418
|
+
|
|
419
|
+
// ---------------------------------------------------------------------------
|
|
420
|
+
// Wave 4 — multiAgent.executionModel (execution.md §multiAgent) and auth (identity.md §3)
|
|
421
|
+
// ---------------------------------------------------------------------------
|
|
422
|
+
|
|
423
|
+
const MA_DOC = 'execution.md §multiAgent';
|
|
424
|
+
|
|
425
|
+
/** The executionModel facet and its integer level, or a skip when either is absent. */
|
|
426
|
+
function executionModel(rec: Record<string, unknown>, atLeast: number): { em: Record<string, unknown> } | AdvertOutcome {
|
|
427
|
+
const em = rec['executionModel'];
|
|
428
|
+
if (!isRecord(em)) return inapplicable('the host does not advertise multiAgent.executionModel');
|
|
429
|
+
const v = em['version'];
|
|
430
|
+
if (typeof v !== 'number' || !Number.isInteger(v)) return observed([{ ok: false, doc: MA_DOC, message: 'multiAgent.executionModel.version MUST be an integer' }]);
|
|
431
|
+
if (v < atLeast) return inapplicable(`multiAgent.executionModel.version is ${v} (< ${atLeast})`);
|
|
432
|
+
return { em };
|
|
433
|
+
}
|
|
434
|
+
const isOutcome = (x: unknown): x is AdvertOutcome => isRecord(x) && typeof x['kind'] === 'string';
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Level 3 (cumulative): "Hosts advertising `version: 3` MUST advertise this
|
|
438
|
+
* sub-block with a stable `hostId`" — the `causationHostId` rule reads that id.
|
|
439
|
+
*/
|
|
440
|
+
export const multiAgentCrossHostCausationLeg = (p: MajorProfile, d: unknown): AdvertOutcome => onFamily(p, d, 'multiAgent', (rec) => {
|
|
441
|
+
const got = executionModel(rec, 3);
|
|
442
|
+
if (isOutcome(got)) return got;
|
|
443
|
+
const chc = got.em['crossHostCausation'];
|
|
444
|
+
const hostId = isRecord(chc) ? chc['hostId'] : undefined;
|
|
445
|
+
return observed([
|
|
446
|
+
{ ok: isRecord(chc), doc: MA_DOC, message: 'a host at executionModel.version >= 3 MUST advertise executionModel.crossHostCausation' },
|
|
447
|
+
{ ok: typeof hostId === 'string' && hostId.length > 0, doc: MA_DOC, message: 'executionModel.crossHostCausation MUST carry a non-empty hostId (the causationHostId value)' },
|
|
448
|
+
]);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Level 4 (cumulative): "Hosts advertising `version: 4` MUST advertise this
|
|
453
|
+
* sub-block" and "MUST set [`refusalDivergenceEmission`] to `true`".
|
|
454
|
+
*/
|
|
455
|
+
export const multiAgentReplayDeterminismLeg = (p: MajorProfile, d: unknown): AdvertOutcome => onFamily(p, d, 'multiAgent', (rec) => {
|
|
456
|
+
const got = executionModel(rec, 4);
|
|
457
|
+
if (isOutcome(got)) return got;
|
|
458
|
+
const rd = got.em['replayDeterminism'];
|
|
459
|
+
return observed([
|
|
460
|
+
{ ok: isRecord(rd), doc: MA_DOC, message: 'a host at executionModel.version >= 4 MUST advertise executionModel.replayDeterminism' },
|
|
461
|
+
{ ok: isRecord(rd) && rd['refusalDivergenceEmission'] === true, doc: MA_DOC, message: 'executionModel.replayDeterminism.refusalDivergenceEmission MUST be true at version >= 4' },
|
|
462
|
+
]);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
export const ESCALATION_KINDS = ['clarification', 'approval'] as const;
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* `confidenceEscalationInterruptKind`: level 2 fires "a `clarification`
|
|
469
|
+
* interrupt (preferred) or an `approval` interrupt", and the host MUST emit the
|
|
470
|
+
* kind it advertises. The schema still admits `x-host-…` (removing it narrows a
|
|
471
|
+
* type); the v2 interrupt `kind` is closed, so such a value promises an interrupt
|
|
472
|
+
* the payload schema rejects. This leg enforces the prose.
|
|
473
|
+
*/
|
|
474
|
+
export const multiAgentEscalationKindLeg = (p: MajorProfile, d: unknown): AdvertOutcome => onFamily(p, d, 'multiAgent', (rec) => {
|
|
475
|
+
const em = rec['executionModel'];
|
|
476
|
+
if (!isRecord(em)) return inapplicable('the host does not advertise multiAgent.executionModel');
|
|
477
|
+
const k = em['confidenceEscalationInterruptKind'];
|
|
478
|
+
if (k === undefined) return inapplicable('the host does not advertise executionModel.confidenceEscalationInterruptKind (optional)');
|
|
479
|
+
const ok = (ESCALATION_KINDS as readonly unknown[]).includes(k);
|
|
480
|
+
return observed([{ ok, doc: MA_DOC, message: `executionModel.confidenceEscalationInterruptKind MUST be clarification or approval at v2${ok ? '' : ` (got ${JSON.stringify(k)})`}` }]);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
export const SUBJECT_LINK_KEY_CLASSES = ['opaque-idp', 'configured-immutable'] as const;
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* identity.md §3: "Advertising both `saml` and `scim` lanes implies the link
|
|
487
|
+
* contract. The `auth.subjectLinkKey` facet … names the key class the host forms
|
|
488
|
+
* links under." A host that forms links names their class.
|
|
489
|
+
*/
|
|
490
|
+
export const authSubjectLinkKeyLeg = (p: MajorProfile, d: unknown): AdvertOutcome => onFamily(p, d, 'auth', (rec) => {
|
|
491
|
+
const lanes = Array.isArray(rec['lanes']) ? (rec['lanes'] as unknown[]).map((l) => (isRecord(l) ? l['lane'] : undefined)) : [];
|
|
492
|
+
if (!(lanes.includes('saml') && lanes.includes('scim'))) return inapplicable('the host does not advertise both the saml and scim lanes');
|
|
493
|
+
const k = rec['subjectLinkKey'];
|
|
494
|
+
const ok = (SUBJECT_LINK_KEY_CLASSES as readonly unknown[]).includes(k);
|
|
495
|
+
return observed([{ ok, doc: 'identity.md §3', message: `a host advertising both the saml and scim lanes MUST advertise auth.subjectLinkKey (opaque-idp or configured-immutable)${ok ? '' : ` (got ${JSON.stringify(k)})`}` }]);
|
|
496
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2 — `auth.subjectLinkKey` under the two linking lanes (`spec/v2/core/identity.md`
|
|
3
|
+
* §3; RFC 0163 §A, RFC 0164 §A.3). The leg lives in
|
|
4
|
+
* `lib/family-advert-witness.ts` (wave 4).
|
|
5
|
+
*
|
|
6
|
+
* Advertising both the `saml` and `scim` lanes implies the link contract, and
|
|
7
|
+
* the host MUST name the key class it forms links under. The schema cannot
|
|
8
|
+
* say so: a conditional `required` is a major change (COMPATIBILITY.md §2.4),
|
|
9
|
+
* and would also re-judge committed bundles. The schema change waits for 3.0.
|
|
10
|
+
* The behavioural link legs are `v2-subject-link-record`.
|
|
11
|
+
*
|
|
12
|
+
* Dispositions: discovery unreadable ⇒ `blocked`; `auth` absent, or the two
|
|
13
|
+
* lanes not both advertised ⇒ `inapplicable`.
|
|
14
|
+
*
|
|
15
|
+
* @see spec/v2/core/identity.md §3
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { describe, it, expect } from 'vitest';
|
|
19
|
+
import { v2Discovery, familyAdvertised } from '../lib/v2.js';
|
|
20
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
21
|
+
import { req } from '../lib/requirement-ids.js';
|
|
22
|
+
import { majorProfile } from '../lib/major-profile.js';
|
|
23
|
+
import { authSubjectLinkKeyLeg } from '../lib/family-advert-witness.js';
|
|
24
|
+
|
|
25
|
+
const PROFILE = majorProfile(2);
|
|
26
|
+
const UNREADABLE = 'v2 discovery unreachable — /.well-known/openwop did not answer 200 JSON under OpenWOP-Version: 2.0';
|
|
27
|
+
const ID_KEY = 'openwop.requirement.auth.subject-link-key-advertised';
|
|
28
|
+
|
|
29
|
+
async function discovery(): Promise<Record<string, unknown> | null> {
|
|
30
|
+
try { return await v2Discovery(); } catch { return null; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe('v2 auth.subjectLinkKey (identity.md §3)', () => {
|
|
34
|
+
it('a host advertising both the saml and scim lanes advertises subjectLinkKey', async () => {
|
|
35
|
+
const doc = await discovery();
|
|
36
|
+
if (!doc) return softSkip('blocked', UNREADABLE);
|
|
37
|
+
if (!(await familyAdvertised('auth'))) return softSkip('inapplicable', 'the host does not advertise auth');
|
|
38
|
+
const out = authSubjectLinkKeyLeg(PROFILE, doc);
|
|
39
|
+
if (out.kind === 'skip') return softSkip(out.disposition, out.reason);
|
|
40
|
+
for (const x of out.findings) expect(x.ok, req(ID_KEY, x.doc, x.message)).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2 — the `multiAgent.executionModel` advertisement (`spec/v2/core/execution.md`
|
|
3
|
+
* §multiAgent; RFCs 0039, 0040, 0041, 0044). The legs live in
|
|
4
|
+
* `lib/family-advert-witness.ts` (wave 4).
|
|
5
|
+
*
|
|
6
|
+
* crossHostCausation at `version` >= 3 the host advertises
|
|
7
|
+
* `crossHostCausation` with a non-empty `hostId`;
|
|
8
|
+
* replayDeterminism at `version` >= 4 the host advertises
|
|
9
|
+
* `replayDeterminism` with `refusalDivergenceEmission: true`;
|
|
10
|
+
* escalationKind `confidenceEscalationInterruptKind`, when present, is
|
|
11
|
+
* `clarification` or `approval`.
|
|
12
|
+
*
|
|
13
|
+
* The schema admits all three violations: a conditional `required` and a
|
|
14
|
+
* narrowed `x-host-…` pattern are major changes (COMPATIBILITY.md §2.4), so the
|
|
15
|
+
* rules are carried here and the schema change waits for 3.0 (TODO.md).
|
|
16
|
+
*
|
|
17
|
+
* Dispositions: discovery unreadable ⇒ `blocked`; `multiAgent`, its
|
|
18
|
+
* `executionModel`, or the level a leg needs absent ⇒ `inapplicable`.
|
|
19
|
+
*
|
|
20
|
+
* @see spec/v2/core/execution.md §multiAgent
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { describe, it, expect } from 'vitest';
|
|
24
|
+
import { v2Discovery, familyAdvertised } from '../lib/v2.js';
|
|
25
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
26
|
+
import { req } from '../lib/requirement-ids.js';
|
|
27
|
+
import { majorProfile } from '../lib/major-profile.js';
|
|
28
|
+
import { multiAgentCrossHostCausationLeg, multiAgentEscalationKindLeg, multiAgentReplayDeterminismLeg } from '../lib/family-advert-witness.js';
|
|
29
|
+
|
|
30
|
+
const PROFILE = majorProfile(2);
|
|
31
|
+
const UNREADABLE = 'v2 discovery unreachable — /.well-known/openwop did not answer 200 JSON under OpenWOP-Version: 2.0';
|
|
32
|
+
const ID_CROSS_HOST = 'openwop.requirement.multiAgent.level3-cross-host-causation';
|
|
33
|
+
const ID_REPLAY = 'openwop.requirement.multiAgent.level4-replay-determinism';
|
|
34
|
+
const ID_KIND = 'openwop.requirement.multiAgent.escalation-kind-canonical';
|
|
35
|
+
|
|
36
|
+
async function discovery(): Promise<Record<string, unknown> | null> {
|
|
37
|
+
try { return await v2Discovery(); } catch { return null; }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe('v2 multiAgent advertisement (execution.md §multiAgent)', () => {
|
|
41
|
+
it('executionModel.version >= 3 advertises crossHostCausation with a hostId', async () => {
|
|
42
|
+
const doc = await discovery();
|
|
43
|
+
if (!doc) return softSkip('blocked', UNREADABLE);
|
|
44
|
+
if (!(await familyAdvertised('multiAgent'))) return softSkip('inapplicable', 'the host does not advertise multiAgent');
|
|
45
|
+
const out = multiAgentCrossHostCausationLeg(PROFILE, doc);
|
|
46
|
+
if (out.kind === 'skip') return softSkip(out.disposition, out.reason);
|
|
47
|
+
for (const x of out.findings) expect(x.ok, req(ID_CROSS_HOST, x.doc, x.message)).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('executionModel.version >= 4 advertises replayDeterminism with refusalDivergenceEmission', async () => {
|
|
51
|
+
const doc = await discovery();
|
|
52
|
+
if (!doc) return softSkip('blocked', UNREADABLE);
|
|
53
|
+
if (!(await familyAdvertised('multiAgent'))) return softSkip('inapplicable', 'the host does not advertise multiAgent');
|
|
54
|
+
const out = multiAgentReplayDeterminismLeg(PROFILE, doc);
|
|
55
|
+
if (out.kind === 'skip') return softSkip(out.disposition, out.reason);
|
|
56
|
+
for (const x of out.findings) expect(x.ok, req(ID_REPLAY, x.doc, x.message)).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('confidenceEscalationInterruptKind is clarification or approval', async () => {
|
|
60
|
+
const doc = await discovery();
|
|
61
|
+
if (!doc) return softSkip('blocked', UNREADABLE);
|
|
62
|
+
if (!(await familyAdvertised('multiAgent'))) return softSkip('inapplicable', 'the host does not advertise multiAgent');
|
|
63
|
+
const out = multiAgentEscalationKindLeg(PROFILE, doc);
|
|
64
|
+
if (out.kind === 'skip') return softSkip(out.disposition, out.reason);
|
|
65
|
+
for (const x of out.findings) expect(x.ok, req(ID_KIND, x.doc, x.message)).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
});
|