@openwop/openwop-conformance 2.0.0-rc.41 → 2.0.0-rc.43
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/spec-artifacts.lock.json +2 -2
- package/package.json +2 -2
- package/schemas/CORPUS-STAMP.json +9 -9
- package/src/lib/fixtures.ts +31 -0
- package/src/setup.ts +32 -33
|
@@ -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.43",
|
|
4
|
+
"stampSha256": "2737f8a4be23de309638fc248b80422ac094007293d0ffddf17776071114e5c5"
|
|
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.43",
|
|
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.43"
|
|
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.43",
|
|
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": "b8320186a24fb2749bcbed7e9d5c34677ea0fc5c4b83dbbcf31b263dd7a1c714",
|
|
13
|
+
"api/v2/asyncapi.yaml": "2ee29b26799ea1d180fb7b5726d968bec2f05c707dc863917011e8fd815372bf",
|
|
14
|
+
"api/v2/openapi.yaml": "895f43f227f50056104feeb680ddb5c950d61f39744076cac673b9d9c95b9ce3",
|
|
15
15
|
"api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
|
|
16
16
|
"schemas/README.md": "0c0b737ffcf8f30e7d2809cec8a498232de710f41443212922ad8337cdde0b51",
|
|
17
17
|
"schemas/a2a-task-state.schema.json": "c9365918f993f943b4b619d42551eb066a1ed33a08d895d51b395432a5b1f1bc",
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
"schemas/v2/envelopes/schema.request.schema.json": "be2c5f7aa881cdd252ccff47ac02193659e1393633a1541f4297383205d384a2",
|
|
142
142
|
"schemas/v2/envelopes/schema.response.schema.json": "d049a081267c06c28879c331b147f3ed821b14ccfd93005c67aff64db0a8c68e",
|
|
143
143
|
"schemas/v2/envelopes/ui.a2ui-surface.schema.json": "0a06fbbdc8ba797a6818c79336a98e211ac5ba5747945b4e4098072cd3974d79",
|
|
144
|
-
"schemas/v2/error-envelope.schema.json": "
|
|
144
|
+
"schemas/v2/error-envelope.schema.json": "c08241b74e41f4e3524c25f1c611195ed0e79b49a8ecf63e304add67070bdce5",
|
|
145
145
|
"schemas/v2/eval-summary.schema.json": "bcb165148531d33b7dc01de83da3a049d19ebbef6d12300b17a234c7b03d77d9",
|
|
146
146
|
"schemas/v2/export-bundle.schema.json": "bd28905a7729437ab7f2ad8bb33e13a371b2bf5605a7eb2962a0d246de312d69",
|
|
147
147
|
"schemas/v2/form-content-pack-manifest.schema.json": "307884e57946703b6f86ec283034bf56beb68bea4a000743ebbc9eebeed31b07",
|
|
@@ -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": "efae7332938c2977bf10e780061d16f33b029390c1023fe5418d6421c115d35b",
|
|
203
203
|
"spec/v1/deprecations.json": "1d5acb69a9b8ccb57275a95605f74aef1d920685f8407c9d382a46b59dc803bb",
|
|
204
204
|
"spec/v1/deprecations.schema.json": "18c87e78bedc210431f795ae44c5b5d202f2f3317850d5cf86867d4f1fa1cdfb",
|
|
205
205
|
"spec/v1/event-codemap.json": "3da60d884157793a360da532a9fcbbfb5285636db325a74cec94b34622186d97",
|
|
@@ -213,7 +213,7 @@
|
|
|
213
213
|
"spec/v1/spec-gaps.json": "6cc9962c6b969f632e07a78b52a4f61447ff579e2990cbae989866f584a86042",
|
|
214
214
|
"spec/v2/declaration.json": "52b8c24a2dd463d44086129bf792ad108746f5102778cbbf0ff469fa534eca48",
|
|
215
215
|
"spec/v2/declaration.schema.json": "b0881a76228dfbd1b85a2ec7b7259a60197f768344535cdb475db6ff719b0e54",
|
|
216
|
-
"spec/v2/errors.json": "
|
|
216
|
+
"spec/v2/errors.json": "c2dd3fa0401efa0dc78da3b21e32b58a89ed5114cebdde96d3eb999a702118f0",
|
|
217
217
|
"spec/v2/event-codemap.json": "37869a03cdfd8758b57e6f350641a6ebbd383231cebda51f5473ad85f94ab74c",
|
|
218
218
|
"spec/v2/event-codemap.schema.json": "ce2d0ca085d7668d7ef745d2560b385ec6881d24a6d4b47c49931986b2ba642d",
|
|
219
219
|
"spec/v2/ext/provider-idempotency/registry.json": "5b5fecad604fb4ab38da39f5abe697a4e57b05406c398f8afeb377331b2bd965",
|
|
@@ -230,7 +230,7 @@
|
|
|
230
230
|
"spec/v2/path-manifest.json": "034152e09b1458c66810d4050e20a273b2b9b8fe2d92b66e8b819477de58a1be",
|
|
231
231
|
"spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
|
|
232
232
|
"spec/v2/profiles.json": "1f0ee40491131cf561ef19a0394c47ba476b4567514fe16befb6cada4a10a5a4",
|
|
233
|
-
"spec/v2/release.json": "
|
|
233
|
+
"spec/v2/release.json": "2013f8d8387352a99c425038cbc98688e8b2f9de1b410b60f25478e09d05f8c0"
|
|
234
234
|
},
|
|
235
|
-
"corpusCommit": "
|
|
235
|
+
"corpusCommit": "c671cc08657877c3e26316ad8e9849670555f07a"
|
|
236
236
|
}
|
package/src/lib/fixtures.ts
CHANGED
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
* @see RFCS/0003-fixture-gating.md
|
|
42
42
|
*/
|
|
43
43
|
|
|
44
|
+
import { softSkip } from './soft-skip.js';
|
|
44
45
|
import type { DiscoveryPayload } from './profiles.js';
|
|
45
46
|
|
|
46
47
|
let _advertisedFixtures: ReadonlySet<string> | null = null;
|
|
@@ -76,7 +77,29 @@ function loadOptedOutPredicate(): (id: string) => boolean {
|
|
|
76
77
|
* filtered out of the cache before storage so downstream lookups can
|
|
77
78
|
* stay a single sync set-membership test.
|
|
78
79
|
*/
|
|
80
|
+
let _discoveryUnreadable: string | null = null;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Suite init could not READ the discovery document. This is not "the host
|
|
84
|
+
* advertises no fixtures" and MUST NOT be recorded as such: with 311
|
|
85
|
+
* fixture-gated sites, an empty cache turns an entire lane into `inapplicable`
|
|
86
|
+
* rows that read exactly like a host that advertises nothing. Measured
|
|
87
|
+
* 2026-09-05: all four workers hit the 5 s init abort against a host that
|
|
88
|
+
* answers `/.well-known/openwop` in 120–200 ms (a Cloud Run cold start), and
|
|
89
|
+
* every fixture-gated scenario skipped as "not advertised". The cache stays
|
|
90
|
+
* null; every gate that consults it records `blocked` with this reason.
|
|
91
|
+
*/
|
|
92
|
+
export function setDiscoveryUnreadable(reason: string): void {
|
|
93
|
+
_advertisedFixtures = null;
|
|
94
|
+
_discoveryUnreadable = reason;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function discoveryUnreadableReason(): string | null {
|
|
98
|
+
return _discoveryUnreadable;
|
|
99
|
+
}
|
|
100
|
+
|
|
79
101
|
export function setAdvertisedFixtures(c: DiscoveryPayload | null | undefined): void {
|
|
102
|
+
_discoveryUnreadable = null;
|
|
80
103
|
if (c == null || !Array.isArray(c.fixtures)) {
|
|
81
104
|
_advertisedFixtures = new Set();
|
|
82
105
|
return;
|
|
@@ -103,6 +126,13 @@ export function setAdvertisedFixtures(c: DiscoveryPayload | null | undefined): v
|
|
|
103
126
|
* );
|
|
104
127
|
*/
|
|
105
128
|
export function isFixtureAdvertised(id: string): boolean {
|
|
129
|
+
if (_advertisedFixtures === null && _discoveryUnreadable !== null) {
|
|
130
|
+
// Evidence went unread, on the suite's side. `blocked` outranks the
|
|
131
|
+
// `inapplicable` the caller is about to record (soft-skip.ts RANK), so the
|
|
132
|
+
// file resolves to the honest word instead of the vacuous one.
|
|
133
|
+
softSkip('blocked', `suite init could not read /.well-known/openwop (${_discoveryUnreadable}) — fixture ${id} is UNKNOWN, not unadvertised`);
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
106
136
|
return _advertisedFixtures?.has(id) ?? false;
|
|
107
137
|
}
|
|
108
138
|
|
|
@@ -127,4 +157,5 @@ export function isFixtureCacheReady(): boolean {
|
|
|
127
157
|
/** Test-only: reset the module-level cache. */
|
|
128
158
|
export function __resetForTests(): void {
|
|
129
159
|
_advertisedFixtures = null;
|
|
160
|
+
_discoveryUnreadable = null;
|
|
130
161
|
}
|
package/src/setup.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* `it` here; vitest treats setupFiles differently from scenario files.
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
import { setAdvertisedFixtures } from './lib/fixtures.js';
|
|
28
|
+
import { setAdvertisedFixtures, setDiscoveryUnreadable } from './lib/fixtures.js';
|
|
29
29
|
import { setMultiAgentCapabilities } from './lib/multi-agent-capabilities.js';
|
|
30
30
|
import { OtelCollector, setCollector } from './lib/otel-collector.js';
|
|
31
31
|
import { McpFakeServer, setMcpFakeServer } from './lib/mcp-fake-server.js';
|
|
@@ -43,13 +43,15 @@ import type { DiscoveryPayload } from './lib/profiles.js';
|
|
|
43
43
|
import { targetMajor } from './lib/seams.js';
|
|
44
44
|
import { softSkip } from './lib/soft-skip.js';
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
// 20 s, not 5: a Cloud Run cold start routinely exceeds 5 s, and a discovery
|
|
47
|
+
// fetch that aborted at init used to turn every fixture-gated scenario into a
|
|
48
|
+
// vacuous `inapplicable`. Measured 2026-09-05 on a host answering in 200 ms.
|
|
49
|
+
const SUITE_INIT_TIMEOUT_MS = 20_000;
|
|
50
|
+
const SUITE_INIT_ATTEMPTS = 2;
|
|
47
51
|
|
|
48
52
|
async function loadHostFixtures(): Promise<void> {
|
|
49
53
|
const baseUrl = process.env.OPENWOP_BASE_URL?.trim();
|
|
50
54
|
if (!baseUrl) {
|
|
51
|
-
// Offline / fixture-stub-only run. No host to ask; treat as "host
|
|
52
|
-
// advertises no fixtures" so all fixture-dependent scenarios skip.
|
|
53
55
|
setAdvertisedFixtures(null);
|
|
54
56
|
setMultiAgentCapabilities(null);
|
|
55
57
|
return;
|
|
@@ -57,39 +59,36 @@ async function loadHostFixtures(): Promise<void> {
|
|
|
57
59
|
|
|
58
60
|
const normalizedBase = baseUrl.replace(/\/$/, '');
|
|
59
61
|
const url = `${normalizedBase}/.well-known/openwop`;
|
|
62
|
+
// The representation the header selects is the one whose fixtures[] this
|
|
63
|
+
// lane is held to (versioning.md §5): read it under the lane's major.
|
|
64
|
+
const headers: Record<string, string> = { Accept: 'application/json' };
|
|
65
|
+
if (targetMajor() === 2) headers['OpenWOP-Version'] = '2.0';
|
|
60
66
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
);
|
|
75
|
-
setAdvertisedFixtures(null);
|
|
76
|
-
setMultiAgentCapabilities(null);
|
|
67
|
+
let lastFailure = 'unknown';
|
|
68
|
+
for (let attempt = 1; attempt <= SUITE_INIT_ATTEMPTS; attempt += 1) {
|
|
69
|
+
const controller = new AbortController();
|
|
70
|
+
const timer = setTimeout(() => controller.abort(), SUITE_INIT_TIMEOUT_MS);
|
|
71
|
+
try {
|
|
72
|
+
const res = await fetch(url, { method: 'GET', headers, signal: controller.signal });
|
|
73
|
+
if (!res.ok) {
|
|
74
|
+
lastFailure = `HTTP ${res.status} on attempt ${attempt}`;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const body = (await res.json()) as DiscoveryPayload;
|
|
78
|
+
setAdvertisedFixtures(body);
|
|
79
|
+
setMultiAgentCapabilities(body);
|
|
77
80
|
return;
|
|
81
|
+
} catch (err) {
|
|
82
|
+
lastFailure = `${(err as Error).message ?? 'unknown'} on attempt ${attempt}`;
|
|
83
|
+
} finally {
|
|
84
|
+
clearTimeout(timer);
|
|
78
85
|
}
|
|
79
|
-
const body = (await res.json()) as DiscoveryPayload;
|
|
80
|
-
setAdvertisedFixtures(body);
|
|
81
|
-
setMultiAgentCapabilities(body);
|
|
82
|
-
} catch (err) {
|
|
83
|
-
// eslint-disable-next-line no-console
|
|
84
|
-
console.warn(
|
|
85
|
-
`[openwop-conformance setup] discovery fetch failed (${(err as Error).message ?? 'unknown'}); ` +
|
|
86
|
-
`treating host as advertising no fixtures. Fixture-dependent scenarios will skip.`,
|
|
87
|
-
);
|
|
88
|
-
setAdvertisedFixtures(null);
|
|
89
|
-
setMultiAgentCapabilities(null);
|
|
90
|
-
} finally {
|
|
91
|
-
clearTimeout(timer);
|
|
92
86
|
}
|
|
87
|
+
// Not "the host advertises no fixtures". The document went UNREAD, and every
|
|
88
|
+
// fixture gate will say so as `blocked` rather than `inapplicable`.
|
|
89
|
+
console.warn(`[openwop-conformance setup] discovery unreadable after ${SUITE_INIT_ATTEMPTS} attempt(s) (${lastFailure}); fixture-gated scenarios will record blocked, not inapplicable.`);
|
|
90
|
+
setDiscoveryUnreadable(lastFailure);
|
|
91
|
+
setMultiAgentCapabilities(null);
|
|
93
92
|
}
|
|
94
93
|
|
|
95
94
|
/**
|