@openwop/openwop-conformance 2.0.0-rc.61 → 2.0.0-rc.63
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/dist/lib/seams.js +31 -1
- package/dist/spec-artifacts.lock.json +2 -2
- package/package.json +2 -2
- package/schemas/CORPUS-STAMP.json +9 -9
- package/src/lib/era2-seed.ts +12 -1
- package/src/lib/seams.ts +31 -1
- package/src/scenarios/v2-assurance-downgrade-audited.test.ts +1 -1
- package/src/scenarios/v2-chain-pin-exact.test.ts +1 -1
- package/src/scenarios/v2-effect-identity-business-key.test.ts +1 -1
- package/src/scenarios/v2-effect-seam-no-refire.test.ts +1 -1
- package/src/scenarios/v2-manifest-ceiling-refused.test.ts +1 -1
- package/src/scenarios/v2-manifest-hatch-carried.test.ts +1 -1
- package/src/scenarios/v2-minimum-version-refused.test.ts +1 -1
- package/src/scenarios/v2-mrtr-rounds-ceiling.test.ts +1 -1
- package/src/scenarios/v2-negotiation-authenticated.test.ts +1 -1
- package/src/scenarios/v2-negotiation-decided-emitted.test.ts +1 -1
- package/src/scenarios/v2-pack-isolation.test.ts +1 -1
- package/src/scenarios/v2-peer-dependency-declared.test.ts +1 -1
- package/src/scenarios/v2-revocation-honored.test.ts +1 -1
- package/src/scenarios/v2-subject-link-record.test.ts +1 -1
- package/src/scenarios/v2-v1-signed-webhook-accepted.test.ts +1 -1
package/dist/lib/seams.js
CHANGED
|
@@ -31,7 +31,37 @@ export function seamPath(v1Path) {
|
|
|
31
31
|
export function isSeamPath(path) {
|
|
32
32
|
return V1_SEAM_PREFIXES.some(([re]) => re.test(path)) || path.startsWith(`${SEAMS_PREFIX}/`);
|
|
33
33
|
}
|
|
34
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Whether a (v2) discovery document advertises the seams profile.
|
|
36
|
+
*
|
|
37
|
+
* **The disposition rule, and it is load-bearing (rc.62).** A scenario that
|
|
38
|
+
* finds this FALSE records `inapplicable`, never `blocked`. The host has not
|
|
39
|
+
* claimed the instrument, so the obligation is out of scope for it — the same
|
|
40
|
+
* sense as `softSkip('inapplicable', 'host does not advertise X')` in
|
|
41
|
+
* `soft-skip.ts`. `blocked` is reserved for the DIFFERENT fact one line later
|
|
42
|
+
* in several of these files: the profile IS advertised and the seam answers
|
|
43
|
+
* 404 — an obligation the host took on and the suite could not measure.
|
|
44
|
+
*
|
|
45
|
+
* Why it matters that these are not the same. `blocked` is bundle-wide fatal
|
|
46
|
+
* (`verifyBundleV3` `blocked-certified`, RFC 0168 §E.1). Until rc.62 fifteen
|
|
47
|
+
* scenarios recorded `blocked` on an ABSENT advert while the seams profile's
|
|
48
|
+
* predicate was empty — and an empty predicate is vacuously satisfied, so
|
|
49
|
+
* `claimedProfilesForV2` claimed the profile for every v2 host. Together that
|
|
50
|
+
* denied certification of EVERY profile to any host that had simply not
|
|
51
|
+
* mounted the conformance seams: 19 of MyndHyve's 45 blocked rows and 9 of
|
|
52
|
+
* openwop-workflow-engine's 29, on hosts that never advertised the profile
|
|
53
|
+
* they were being held to. Mounting test-only surface is not a precondition of
|
|
54
|
+
* certifying `openwop-discovery-core`, and a barrier of that shape falls
|
|
55
|
+
* hardest on the independent implementers the v1 end-of-support clock needs.
|
|
56
|
+
*
|
|
57
|
+
* The predicate (`spec/v2/declaration.json`) now requires `conformance` at the
|
|
58
|
+
* discovery root, which is NECESSARY but not sufficient; this function is the
|
|
59
|
+
* sufficient test, applied per scenario. A host with a `conformance` block
|
|
60
|
+
* naming some other profile claims seams-v2, records every floor row
|
|
61
|
+
* `inapplicable`, witnesses nothing, and so certifies nothing — without a
|
|
62
|
+
* single blocked row. Claimed-and-unwitnessed and not-claimed are both honest;
|
|
63
|
+
* blocked-because-unclaimed was not.
|
|
64
|
+
*/
|
|
35
65
|
export function seamsProfileAdvertised(doc) {
|
|
36
66
|
const conf = doc?.['conformance'];
|
|
37
67
|
return typeof conf === 'object' && conf !== null && conf['seamsProfile'] === SEAMS_PROFILE_ID;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "@openwop/spec-artifacts",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
4
|
-
"stampSha256": "
|
|
3
|
+
"version": "2.0.0-rc.63",
|
|
4
|
+
"stampSha256": "fc832118a094bf1ed10fa233af7b1f2eed7dd6cc9eb3df2ef13610c0957dd9a9"
|
|
5
5
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openwop/openwop-conformance",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
3
|
+
"version": "2.0.0-rc.63",
|
|
4
4
|
"description": "Production-ready black-box conformance suite for OpenWOP v1.0 compliant servers.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -56,6 +56,6 @@
|
|
|
56
56
|
"@openwop/spec-artifacts": "file:../spec-artifacts"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@openwop/spec-artifacts": "2.0.0-rc.
|
|
59
|
+
"@openwop/spec-artifacts": "2.0.0-rc.63"
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
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.0.0-rc.
|
|
4
|
+
"version": "2.0.0-rc.63",
|
|
5
5
|
"corpusTag": null,
|
|
6
6
|
"files": {
|
|
7
7
|
"api/.redocly.lint-ignore.yaml": "bf5a8350b88a72fa43f59605ed8d903ed24b6cfccda5e45509c9f6ed9ee4e712",
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
"api/grpc/openwop.proto": "c3e72bb17cba514ee98feb6434e6c9b6ea6795bfd086489ec69fd882dd1ad977",
|
|
10
10
|
"api/openapi.yaml": "39081c59fb696159806b0f2f9a42e7e9ff830d622fcf2ad4159b21357580a955",
|
|
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": "3346a3abd83920d38c37ba3a76e9853f5eecd9daac5d449c3a81ee89af291f2b",
|
|
13
|
+
"api/v2/asyncapi.yaml": "573603322c45df47b803e26fc6eedc12c1d4c71a69f650e64da15728c9f4865e",
|
|
14
|
+
"api/v2/openapi.yaml": "40a5c055d5e6f0395754c5fe550f3f1b9aa4ce0f6b272fe0cbef7da48499e93c",
|
|
15
15
|
"api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
|
|
16
16
|
"schemas/README.md": "0c0b737ffcf8f30e7d2809cec8a498232de710f41443212922ad8337cdde0b51",
|
|
17
17
|
"schemas/a2a-task-state.schema.json": "c9365918f993f943b4b619d42551eb066a1ed33a08d895d51b395432a5b1f1bc",
|
|
@@ -199,7 +199,7 @@
|
|
|
199
199
|
"schemas/workspace-file.schema.json": "464de85c2a068243084ee9c1d969bc7cd5d8f7948574e58450d6493c38a0e1e4",
|
|
200
200
|
"spec/v1/alias-detectors.json": "fee4594ef49953953ffcd0b3813300067d16b3e65ebff2aac722034ac9b3f545",
|
|
201
201
|
"spec/v1/capability-declaration-classes.json": "e7729aed5c4b4e1dd02abab0530f14cc95f5d4070fe51fb139e7f5cccefa00c6",
|
|
202
|
-
"spec/v1/core-standard-manifest.json": "
|
|
202
|
+
"spec/v1/core-standard-manifest.json": "061b0986b66396273bb08ee7177746b51a66227cc6a535e3f57fa60c932c003d",
|
|
203
203
|
"spec/v1/deprecations.json": "1d5acb69a9b8ccb57275a95605f74aef1d920685f8407c9d382a46b59dc803bb",
|
|
204
204
|
"spec/v1/deprecations.schema.json": "18c87e78bedc210431f795ae44c5b5d202f2f3317850d5cf86867d4f1fa1cdfb",
|
|
205
205
|
"spec/v1/event-codemap.json": "3da60d884157793a360da532a9fcbbfb5285636db325a74cec94b34622186d97",
|
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
"spec/v1/migrations.schema.json": "886779aa6c22e646db097f5df210adb018a4dd14a7b815465a18c8a7056c8f72",
|
|
212
212
|
"spec/v1/operation-path-manifest.json": "5f5f4e3842669371730ebbd1aace3fb794192615f0018dc144f484ba5db82ac3",
|
|
213
213
|
"spec/v1/spec-gaps.json": "6cc9962c6b969f632e07a78b52a4f61447ff579e2990cbae989866f584a86042",
|
|
214
|
-
"spec/v2/declaration.json": "
|
|
214
|
+
"spec/v2/declaration.json": "a624cfa6a46d0b10e0dac40fa49abbe05190aff87a8e852ce125d19379067b00",
|
|
215
215
|
"spec/v2/declaration.schema.json": "b0881a76228dfbd1b85a2ec7b7259a60197f768344535cdb475db6ff719b0e54",
|
|
216
216
|
"spec/v2/errors.json": "f179414a92f30b5dadf26e9e57699649a67324bd7186d5287647d17605d137d3",
|
|
217
217
|
"spec/v2/event-codemap.json": "37869a03cdfd8758b57e6f350641a6ebbd383231cebda51f5473ad85f94ab74c",
|
|
@@ -229,9 +229,9 @@
|
|
|
229
229
|
"spec/v2/id-field-bindings.json": "d9d1f8e266660b581f11020b9511a65c677e026b1a71ada870682ad77e18555d",
|
|
230
230
|
"spec/v2/path-manifest.json": "034152e09b1458c66810d4050e20a273b2b9b8fe2d92b66e8b819477de58a1be",
|
|
231
231
|
"spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
|
|
232
|
-
"spec/v2/profiles.json": "
|
|
233
|
-
"spec/v2/release.json": "
|
|
232
|
+
"spec/v2/profiles.json": "0636f19fceae625390003a347e70ef4797d84766b5c24ce8a02cea52aadebca4",
|
|
233
|
+
"spec/v2/release.json": "efa92244efb4782514548a5c467a968454ca43aacd6339452cba1cc5bd265436",
|
|
234
234
|
"spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624"
|
|
235
235
|
},
|
|
236
|
-
"corpusCommit": "
|
|
236
|
+
"corpusCommit": "443c27405e4bc132f8d436da5f08903291b824e0"
|
|
237
237
|
}
|
package/src/lib/era2-seed.ts
CHANGED
|
@@ -69,7 +69,18 @@ async function http(fn: () => Promise<OpenWOPResponse>): Promise<OpenWOPResponse
|
|
|
69
69
|
export function era2Gate(doc: Record<string, unknown> | null): Seeded | null {
|
|
70
70
|
if (!doc) return { ok: false, kind: 'blocked', reason: 'discovery unreachable' };
|
|
71
71
|
if (!seamsProfileAdvertised(doc)) {
|
|
72
|
-
|
|
72
|
+
// rc.63: `inapplicable`, not `blocked` — the host never claimed the
|
|
73
|
+
// instrument, so the obligation is out of scope for it (`lib/seams.ts`).
|
|
74
|
+
// rc.62 fixed the fifteen scenarios that call `seamsProfileAdvertised`
|
|
75
|
+
// directly and MISSED this one, which mints the same disposition one call
|
|
76
|
+
// deeper for six scenarios that import only `era2Gate`: era-2-append-
|
|
77
|
+
// vocabulary, fork-a-v1-run, pinned-run-disposition, stream-sse-projection,
|
|
78
|
+
// unmapped-type-refused, v1-events-translated. On MyndHyve's bundle that
|
|
79
|
+
// left 16 rows blocked on advert-absence after rc.62 — the same defect
|
|
80
|
+
// wearing a different import. `blocked` above (discovery unreachable) and
|
|
81
|
+
// below (the seam does not answer) is untouched: those are real failed
|
|
82
|
+
// measurements.
|
|
83
|
+
return { ok: false, kind: 'inapplicable', reason: `seams profile not advertised (conformance.seamsProfile !== openwop-conformance-seams-v2) — an era-2 log can only be seeded through ${SEED_PATH}` };
|
|
73
84
|
}
|
|
74
85
|
return null;
|
|
75
86
|
}
|
package/src/lib/seams.ts
CHANGED
|
@@ -33,7 +33,37 @@ export function isSeamPath(path: string): boolean {
|
|
|
33
33
|
return V1_SEAM_PREFIXES.some(([re]) => re.test(path)) || path.startsWith(`${SEAMS_PREFIX}/`);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Whether a (v2) discovery document advertises the seams profile.
|
|
38
|
+
*
|
|
39
|
+
* **The disposition rule, and it is load-bearing (rc.62).** A scenario that
|
|
40
|
+
* finds this FALSE records `inapplicable`, never `blocked`. The host has not
|
|
41
|
+
* claimed the instrument, so the obligation is out of scope for it — the same
|
|
42
|
+
* sense as `softSkip('inapplicable', 'host does not advertise X')` in
|
|
43
|
+
* `soft-skip.ts`. `blocked` is reserved for the DIFFERENT fact one line later
|
|
44
|
+
* in several of these files: the profile IS advertised and the seam answers
|
|
45
|
+
* 404 — an obligation the host took on and the suite could not measure.
|
|
46
|
+
*
|
|
47
|
+
* Why it matters that these are not the same. `blocked` is bundle-wide fatal
|
|
48
|
+
* (`verifyBundleV3` `blocked-certified`, RFC 0168 §E.1). Until rc.62 fifteen
|
|
49
|
+
* scenarios recorded `blocked` on an ABSENT advert while the seams profile's
|
|
50
|
+
* predicate was empty — and an empty predicate is vacuously satisfied, so
|
|
51
|
+
* `claimedProfilesForV2` claimed the profile for every v2 host. Together that
|
|
52
|
+
* denied certification of EVERY profile to any host that had simply not
|
|
53
|
+
* mounted the conformance seams: 19 of MyndHyve's 45 blocked rows and 9 of
|
|
54
|
+
* openwop-workflow-engine's 29, on hosts that never advertised the profile
|
|
55
|
+
* they were being held to. Mounting test-only surface is not a precondition of
|
|
56
|
+
* certifying `openwop-discovery-core`, and a barrier of that shape falls
|
|
57
|
+
* hardest on the independent implementers the v1 end-of-support clock needs.
|
|
58
|
+
*
|
|
59
|
+
* The predicate (`spec/v2/declaration.json`) now requires `conformance` at the
|
|
60
|
+
* discovery root, which is NECESSARY but not sufficient; this function is the
|
|
61
|
+
* sufficient test, applied per scenario. A host with a `conformance` block
|
|
62
|
+
* naming some other profile claims seams-v2, records every floor row
|
|
63
|
+
* `inapplicable`, witnesses nothing, and so certifies nothing — without a
|
|
64
|
+
* single blocked row. Claimed-and-unwitnessed and not-claimed are both honest;
|
|
65
|
+
* blocked-because-unclaimed was not.
|
|
66
|
+
*/
|
|
37
67
|
export function seamsProfileAdvertised(doc: Readonly<Record<string, unknown>> | null | undefined): boolean {
|
|
38
68
|
const conf = doc?.['conformance'];
|
|
39
69
|
return typeof conf === 'object' && conf !== null && (conf as Record<string, unknown>)['seamsProfile'] === SEAMS_PROFILE_ID;
|
|
@@ -39,7 +39,7 @@ async function http(fn: () => Promise<OpenWOPResponse>): Promise<OpenWOPResponse
|
|
|
39
39
|
async function gate(): Promise<{ floor: string } | { kind: 'blocked' | 'inapplicable'; reason: string }> {
|
|
40
40
|
const doc = await discovery();
|
|
41
41
|
if (!doc) return { kind: 'blocked', reason: 'v2 discovery unreachable — /.well-known/openwop did not answer 200 with a JSON body under OpenWOP-Version: 2.0' };
|
|
42
|
-
if (!seamsProfileAdvertised(doc)) return { kind: '
|
|
42
|
+
if (!seamsProfileAdvertised(doc)) return { kind: 'inapplicable', reason: 'seams profile not advertised (conformance.seamsProfile !== openwop-conformance-seams-v2) — the assurance floor is seam-gated' };
|
|
43
43
|
const auth = await familyAdvertised('auth');
|
|
44
44
|
const lanes = Array.isArray(auth?.['lanes']) ? (auth['lanes'] as Array<Record<string, unknown>>) : [];
|
|
45
45
|
const workload = lanes.find((l) => l['lane'] === 'workload');
|
|
@@ -93,7 +93,7 @@ async function preflight(): Promise<{ kind: SoftSkipKind; reason: string } | nul
|
|
|
93
93
|
}
|
|
94
94
|
if (!doc) return { kind: 'blocked', reason: 'discovery unreachable — /.well-known/openwop (OpenWOP-Version: 2.0) did not answer 200 JSON' };
|
|
95
95
|
if (!(await gateFamily('workflowChainPacks'))) return { kind: 'inapplicable', reason: 'v2 discovery does not advertise the workflowChainPacks family (RFC 0169 §A.2)' };
|
|
96
|
-
if (!seamsProfileAdvertised(doc)) return { kind: '
|
|
96
|
+
if (!seamsProfileAdvertised(doc)) return { kind: 'inapplicable', reason: 'host does not advertise conformance.seamsProfile: openwop-conformance-seams-v2 — the packs-test publish seam is the only register path the suite can drive (RFC 0168 §C.1)' };
|
|
97
97
|
if (!v2Validator('workflow-chain-pack-manifest')(chainPack('core.openwop.pin-probe', 'core.ai.callPrompt@1.0.0')).ok) return { kind: 'blocked', reason: SCHEMA_GAP };
|
|
98
98
|
return null;
|
|
99
99
|
}
|
|
@@ -97,7 +97,7 @@ describe('RFC 0173 §B — effect-identity-business-key (gated on idempotency)',
|
|
|
97
97
|
const doc = await discovery();
|
|
98
98
|
if (!doc) return softSkip('blocked', 'discovery unreachable');
|
|
99
99
|
if (!(await gateFamily('idempotency'))) return softSkip('inapplicable', 'idempotency family not advertised — no Layer-2 obligation (gate recorded under openwop.family.idempotency)');
|
|
100
|
-
if (!seamsProfileAdvertised(doc)) return softSkip('
|
|
100
|
+
if (!seamsProfileAdvertised(doc)) return softSkip('inapplicable', 'the retry leg is driven through the suite fixture provider (RFC 0173 §D.2 G4) under the seams profile — seams profile not advertised');
|
|
101
101
|
// The seam is catalogued (api/seams-v2.yaml `forceEffectTransportRetry`). An
|
|
102
102
|
// unreachable providerUrl forces the transport retry; the witness is the
|
|
103
103
|
// host's own Layer-2 ledger (RFC 0173 §C.2), where every attempt of one
|
|
@@ -45,7 +45,7 @@ describe('RFC 0173 §C.2 — effect-seam-no-refire (gated on replay, seam-driven
|
|
|
45
45
|
const replay = await gateFamily('replay');
|
|
46
46
|
if (!replay) return softSkip('inapplicable', 'replay family not advertised (gate recorded under openwop.family.replay)');
|
|
47
47
|
if (!seamsProfileAdvertised(doc)) {
|
|
48
|
-
return softSkip('
|
|
48
|
+
return softSkip('inapplicable', `the no-re-fire leg is seam-gated (RFC 0173 falsifiability §B replay row is witnessable-gated only through a seam that fires a named manifest row inside a run) — seams profile not advertised (conformance.seamsProfile)`);
|
|
49
49
|
}
|
|
50
50
|
// The seam is catalogued (api/seams-v2.yaml `fireEffectSeam`). The witness is
|
|
51
51
|
// the host's own Layer-2 ledger, not an externally reachable receiver: fire a
|
|
@@ -97,7 +97,7 @@ async function preflight(): Promise<{ kind: SoftSkipKind; reason: string } | nul
|
|
|
97
97
|
}
|
|
98
98
|
if (!doc) return { kind: 'blocked', reason: 'discovery unreachable — /.well-known/openwop (OpenWOP-Version: 2.0) did not answer 200 JSON' };
|
|
99
99
|
if (!(await gateFamily('packs'))) return { kind: 'inapplicable', reason: 'v2 discovery does not advertise the packs family (RFC 0169 §A.2)' };
|
|
100
|
-
if (!seamsProfileAdvertised(doc)) return { kind: '
|
|
100
|
+
if (!seamsProfileAdvertised(doc)) return { kind: 'inapplicable', reason: 'host does not advertise conformance.seamsProfile: openwop-conformance-seams-v2 — the packs-test publish seam is the only install path the suite can drive (RFC 0168 §C.1)' };
|
|
101
101
|
return null;
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -77,7 +77,7 @@ async function preflight(): Promise<{ kind: SoftSkipKind; reason: string } | nul
|
|
|
77
77
|
}
|
|
78
78
|
if (!doc) return { kind: 'blocked', reason: 'discovery unreachable — /.well-known/openwop (OpenWOP-Version: 2.0) did not answer 200 JSON' };
|
|
79
79
|
if (!(await gateFamily('packs'))) return { kind: 'inapplicable', reason: 'v2 discovery does not advertise the packs family (RFC 0169 §A.2)' };
|
|
80
|
-
if (!seamsProfileAdvertised(doc)) return { kind: '
|
|
80
|
+
if (!seamsProfileAdvertised(doc)) return { kind: 'inapplicable', reason: 'host does not advertise conformance.seamsProfile: openwop-conformance-seams-v2 — the packs-test publish seam is the only install path the suite can drive (RFC 0168 §C.1)' };
|
|
81
81
|
return null;
|
|
82
82
|
}
|
|
83
83
|
|
|
@@ -88,7 +88,7 @@ describe('RFC 0175 §D.2 — minimum-version-refused (gated on a2a/mcp + seams)'
|
|
|
88
88
|
if (!doc) return softSkip('blocked', 'discovery unreachable');
|
|
89
89
|
const facet = await familyAdvertised('a2a');
|
|
90
90
|
if (!facet) return softSkip('inapplicable', 'a2a facet not advertised — no floor to enforce');
|
|
91
|
-
if (!seamsProfileAdvertised(doc)) return softSkip('
|
|
91
|
+
if (!seamsProfileAdvertised(doc)) return softSkip('inapplicable', 'the below-floor exchange is driven through the seams profile — conformance.seamsProfile is not openwop-conformance-seams-v2');
|
|
92
92
|
const floor = String(facet['minimumVersion']);
|
|
93
93
|
if (!a2aBelow(A2A_LOWEST, floor)) {
|
|
94
94
|
return softSkip('blocked', `the advertised a2a.minimumVersion (${floor}) is at or below the lowest version the suite peer can offer (${A2A_LOWEST}) — nothing below the floor can be offered; a peer speaking a lower A2A revision is needed`);
|
|
@@ -63,7 +63,7 @@ describe('RFC 0175 §E.1 — mrtr-rounds-ceiling (gated on mcp + mrtr)', () => {
|
|
|
63
63
|
if (!facet) return softSkip('inapplicable', 'mcp facet not advertised — no MRTR ceiling');
|
|
64
64
|
const maxRounds = maxRoundsOf(facet);
|
|
65
65
|
if (!Number.isInteger(maxRounds)) return softSkip('inapplicable', 'mcp.mrtr.maxRounds not advertised — no ceiling to exceed');
|
|
66
|
-
if (!seamsProfileAdvertised(doc)) return softSkip('
|
|
66
|
+
if (!seamsProfileAdvertised(doc)) return softSkip('inapplicable', 'the rounds are driven through the seams profile (§23 invoke seam) — conformance.seamsProfile is not openwop-conformance-seams-v2');
|
|
67
67
|
const server = getMcpFakeServer();
|
|
68
68
|
if (server === null) return softSkip('blocked', 'the suite MCP fake server (OPENWOP_MCP_FAKE_SERVER=true) is not started in this run');
|
|
69
69
|
server.reset();
|
|
@@ -88,7 +88,7 @@ describe('RFC 0175 §D.1 — negotiation-authenticated (gated on a2a/mcp + seams
|
|
|
88
88
|
if (!doc) return softSkip('blocked', 'discovery unreachable');
|
|
89
89
|
const facet = await familyAdvertised('a2a');
|
|
90
90
|
if (!facet) return softSkip('inapplicable', 'a2a facet not advertised — no negotiation to authenticate');
|
|
91
|
-
if (!seamsProfileAdvertised(doc)) return softSkip('
|
|
91
|
+
if (!seamsProfileAdvertised(doc)) return softSkip('inapplicable', 'the unauthenticated exchange is driven through the seams profile — conformance.seamsProfile is not openwop-conformance-seams-v2');
|
|
92
92
|
const preferred = String(facet['preferredVersion']);
|
|
93
93
|
if (preferred === A2A_LOWER) return softSkip('blocked', `a2a.preferredVersion is ${preferred}, the lowest version the suite peer can offer — no lower version can be offered`);
|
|
94
94
|
const peer = new A2AFakePeer({ protocolVersions: ['0.3'] });
|
|
@@ -88,7 +88,7 @@ async function leg(protocol: 'a2a' | 'mcp', id: string): Promise<void> {
|
|
|
88
88
|
if (!doc) return softSkip('blocked', 'discovery unreachable');
|
|
89
89
|
const facet = await familyAdvertised(protocol);
|
|
90
90
|
if (!facet) return softSkip('inapplicable', `${protocol} facet not advertised — no negotiation to audit`);
|
|
91
|
-
if (!seamsProfileAdvertised(doc)) return softSkip('
|
|
91
|
+
if (!seamsProfileAdvertised(doc)) return softSkip('inapplicable', `the exchange is driven through the seams profile (RFC 0175 §D.3) — conformance.seamsProfile is not openwop-conformance-seams-v2`);
|
|
92
92
|
const peer = protocol === 'a2a' ? getA2AFakePeer() : getMcpFakeServer();
|
|
93
93
|
if (peer === null) return softSkip('blocked', `the suite ${protocol === 'a2a' ? 'A2A fake peer (OPENWOP_A2A_FAKE_PEER=true)' : 'MCP fake server (OPENWOP_MCP_FAKE_SERVER=true)'} is not started in this run`);
|
|
94
94
|
peer.reset();
|
|
@@ -54,7 +54,7 @@ async function gated(): Promise<Record<string, unknown> | null> {
|
|
|
54
54
|
/** Drive one misbehaving typeId through the sandbox seam; null (reason recorded) when the seam is absent. */
|
|
55
55
|
async function invoke(typeId: string, extra: Record<string, unknown> = {}): Promise<InvokeResult | null> {
|
|
56
56
|
const doc = await discovery();
|
|
57
|
-
if (!doc || !seamsProfileAdvertised(doc)) { softSkip('
|
|
57
|
+
if (!doc || !seamsProfileAdvertised(doc)) { softSkip('inapplicable', `the ${typeId} leg is seam-driven — seams profile (conformance.seamsProfile = openwop-conformance-seams-v2) not advertised`); return null; }
|
|
58
58
|
const res = await driver.post(INVOKE, { typeId, ...extra });
|
|
59
59
|
if (res.status === 404 || res.status === 403 || res.status === 405) { seamAbsent(`host advertises packs + sandbox but ${INVOKE} answered ${res.status} — the ${typeId} leg is unobservable (host-sample-test-seams.md §8)`); return null; }
|
|
60
60
|
expect(res.status, req('openwop.requirement.0173.pack-isolation.seam', 'host-sample-test-seams.md §8', `${INVOKE} MUST answer 200 { result } | 200 { error } for ${typeId}`)).toBe(200);
|
|
@@ -96,7 +96,7 @@ async function preflight(): Promise<{ kind: SoftSkipKind; reason: string } | nul
|
|
|
96
96
|
}
|
|
97
97
|
if (!doc) return { kind: 'blocked', reason: 'discovery unreachable — /.well-known/openwop (OpenWOP-Version: 2.0) did not answer 200 JSON' };
|
|
98
98
|
if (!(await gateFamily('packs'))) return { kind: 'inapplicable', reason: 'v2 discovery does not advertise the packs family (RFC 0169 §A.2)' };
|
|
99
|
-
if (!seamsProfileAdvertised(doc)) return { kind: '
|
|
99
|
+
if (!seamsProfileAdvertised(doc)) return { kind: 'inapplicable', reason: 'host does not advertise conformance.seamsProfile: openwop-conformance-seams-v2 — the packs-test publish seam is the only install path the suite can drive (RFC 0168 §C.1)' };
|
|
100
100
|
return null;
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -38,7 +38,7 @@ describe('v2 revocation-honored (RFC 0170 §B.3 — seam-gated)', () => {
|
|
|
38
38
|
it('a revoked next-request credential is refused on the next request with credential_revoked', async () => {
|
|
39
39
|
const doc = await discovery();
|
|
40
40
|
if (!doc) return softSkip('blocked', 'v2 discovery unreachable — /.well-known/openwop did not answer 200 with a JSON body under OpenWOP-Version: 2.0');
|
|
41
|
-
if (!seamsProfileAdvertised(doc)) return softSkip('
|
|
41
|
+
if (!seamsProfileAdvertised(doc)) return softSkip('inapplicable', 'seams profile not advertised (conformance.seamsProfile !== openwop-conformance-seams-v2) — revocation is seam-gated and cannot be observed from the canonical API');
|
|
42
42
|
const auth = await familyAdvertised('auth');
|
|
43
43
|
const lanes = Array.isArray(auth?.['lanes']) ? (auth['lanes'] as Array<Record<string, unknown>>) : [];
|
|
44
44
|
const nextRequest = lanes.filter((l) => l['revocation'] === 'next-request').map((l) => String(l['lane']));
|
|
@@ -56,7 +56,7 @@ async function readLink(externalId: string): Promise<Record<string, unknown> | n
|
|
|
56
56
|
async function form(): Promise<Formed> {
|
|
57
57
|
const doc = await discovery();
|
|
58
58
|
if (!doc) return { kind: 'blocked', reason: 'v2 discovery unreachable — /.well-known/openwop did not answer 200 with a JSON body under OpenWOP-Version: 2.0' };
|
|
59
|
-
if (!seamsProfileAdvertised(doc)) return { kind: '
|
|
59
|
+
if (!seamsProfileAdvertised(doc)) return { kind: 'inapplicable', reason: 'seams profile not advertised (conformance.seamsProfile !== openwop-conformance-seams-v2) — the link record is seam-gated' };
|
|
60
60
|
const auth = await familyAdvertised('auth');
|
|
61
61
|
const lanes = new Set((Array.isArray(auth?.['lanes']) ? (auth['lanes'] as Array<Record<string, unknown>>) : []).map((l) => String(l['lane'])));
|
|
62
62
|
if (!(lanes.has('saml') && lanes.has('scim'))) return { kind: 'inapplicable', reason: 'the host does not advertise both the saml and scim lanes — advertising both is what implies the linking contract (row C2.5)' };
|
|
@@ -90,7 +90,7 @@ describe('RFC 0176 §D.2 — v1-signed-webhook-accepted (gated on webhooks + sea
|
|
|
90
90
|
const doc = await discovery();
|
|
91
91
|
if (!doc) return softSkip('blocked', 'discovery unreachable');
|
|
92
92
|
if (!(await gateFamily('webhooks'))) return softSkip('inapplicable', 'webhooks family not advertised (gate recorded under openwop.family.webhooks)');
|
|
93
|
-
if (!seamsProfileAdvertised(doc)) return softSkip('
|
|
93
|
+
if (!seamsProfileAdvertised(doc)) return softSkip('inapplicable', `seams profile not advertised (conformance.seamsProfile !== openwop-conformance-seams-v2) — the host's inbound receiver is reachable only through ${RECEIVE}`);
|
|
94
94
|
const secret = `conformance-secret-${Date.now().toString(36)}`;
|
|
95
95
|
const body = JSON.stringify({ runId: 'run-conformance-v1-signed', workspaceId: 'ws-conformance', event: { type: 'run.completed', sequence: 3, payload: { durationMs: 1 } } });
|
|
96
96
|
const good = await deliver(secret, v1Delivery(secret, body));
|