@openwop/openwop-conformance 1.99.0 → 1.102.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "1.99.0",
3
+ "version": "1.102.0",
4
4
  "description": "Production-ready black-box conformance suite for OpenWOP v1.0 compliant servers.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "_comment": "Provenance of this vendored schemas/ copy. See conformance/README.md \u00a7\"Resolving the contract\". Compare against the stamp in your installed @openwop/openwop-conformance to detect a stale hand-copied contract.",
3
- "suiteVersion": "1.99.0",
4
- "corpusCommit": "e30c3c5e8b14d76c90b608666deaf10411df0414"
3
+ "suiteVersion": "1.102.0",
4
+ "corpusCommit": "6fa46a846fa43fd14b8499fb38d8230a9bbb6eb3"
5
5
  }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Host-callback declaration — which scenarios need the HOST to reach the suite.
3
+ *
4
+ * ## The rule
5
+ *
6
+ * A scenario that requires the host to **originate a connection back to the
7
+ * harness** MUST declare it:
8
+ *
9
+ * ```ts
10
+ * export const REQUIRES_HOST_CALLBACK = 'the host exports OTLP to the suite collector';
11
+ * ```
12
+ *
13
+ * ## Why a declaration and not a detector
14
+ *
15
+ * The suite advertises harness-hosted endpoints to the host — a fake A2A peer,
16
+ * an MCP server, an OIDC issuer, an OTLP collector — and in-process that
17
+ * advertisement is free loopback. **It stops being free the moment the host is
18
+ * not the harness process.** From a container, VM, or remote origin,
19
+ * `127.0.0.1` is *that* environment, so the call never lands. The scenario does
20
+ * not fail because the host is non-conformant; it fails because there is no
21
+ * route.
22
+ *
23
+ * That is a networking property, not a measurement, and it holds for any
24
+ * consumer running the suite against anything that is not the harness process.
25
+ *
26
+ * **A detector cannot decide this reliably, and trying taught us why.** A first
27
+ * pass that grepped for URL-shaped identifiers flagged `form-content-packs`
28
+ * (where `webhookUrl` is a field name the scenario *forbids*) and
29
+ * `interrupt-external-event-correlation` (where `callbackUrl` flows host →
30
+ * suite, the opposite direction). It also missed the OTLP collector entirely,
31
+ * because nothing in those scenarios names a URL at all. **Two false positives
32
+ * and a false negative on the first attempt** — a list built that way would be
33
+ * wrong in both directions while looking authoritative.
34
+ *
35
+ * So the author declares, because the author knows which way the connection
36
+ * goes. The gate below enforces the declaration where the signal is
37
+ * unambiguous.
38
+ *
39
+ * ## What the gate is, and is not
40
+ *
41
+ * `host-callback-declaration.test.ts` requires the declaration on every scenario
42
+ * importing a module from {@link HARNESS_DOUBLE_MODULES}. That signal is
43
+ * unambiguous: those modules exist to stand up a server the host must reach.
44
+ *
45
+ * **It is a floor, not an oracle.** A scenario that constructs a
46
+ * harness-reachable URL some other way is callback-shaped and the gate will not
47
+ * notice. Saying so here is the point — a gate whose limits are unstated reads
48
+ * as completeness it does not have, which is the failure this whole program
49
+ * exists to close.
50
+ *
51
+ * ## What the declaration buys
52
+ *
53
+ * A consumer running the suite off-process can enumerate, **before running**,
54
+ * which scenarios cannot be witnessed in their environment:
55
+ *
56
+ * ```sh
57
+ * grep -l REQUIRES_HOST_CALLBACK node_modules/@openwop/openwop-conformance/src/scenarios/*.ts
58
+ * ```
59
+ *
60
+ * That converts a silent unwitnessable set into a list they can plan around —
61
+ * and RFC 0148 §A resolves an unwitnessed requirement to `blocked` rather than
62
+ * to a pass, which a consumer can only honour if they know which ones they are.
63
+ */
64
+
65
+ /**
66
+ * Modules that stand up a harness-hosted server the host must reach.
67
+ *
68
+ * Derived from the library rather than remembered: these are the modules
69
+ * exposing an `endpoint()` a scenario hands to the host. The first informal
70
+ * account of this class named three (compat provider, OIDC issuer, webhook
71
+ * subscriber) and **missed the OTLP collector, which has the most consumers of
72
+ * any of them** — which is why this list lives next to the gate that reads it
73
+ * instead of in prose someone has to keep true.
74
+ */
75
+ export const HARNESS_DOUBLE_MODULES: readonly string[] = [
76
+ 'a2a-fake-peer',
77
+ 'mcp-fake-server',
78
+ 'oidc-issuer',
79
+ 'otel-collector',
80
+ ];
81
+
82
+ /**
83
+ * The declaration a callback-shaped scenario exports.
84
+ *
85
+ * The value is the REASON — which connection the host must originate — not a
86
+ * bare `true`. A boolean records that somebody ticked a box; a sentence records
87
+ * what a consumer needs to route, and is checkable against the scenario body by
88
+ * anyone reading the diff.
89
+ */
90
+ export type HostCallbackDeclaration = string;
@@ -48,6 +48,14 @@ import { SCHEMAS_DIR } from '../lib/paths.js';
48
48
  import { behaviorGate } from '../lib/behavior-gate.js';
49
49
  import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
50
50
 
51
+ /**
52
+ * Callback-shaped: the host issues A2A JSON-RPC calls to the suite's fake peer.
53
+ *
54
+ * Unwitnessable when the host is in a separate network namespace — see
55
+ * `../lib/host-callback.ts`. Not host non-conformance; no route.
56
+ */
57
+ export const REQUIRES_HOST_CALLBACK = "the host issues A2A JSON-RPC calls to the suite's fake peer";
58
+
51
59
  const ROUNDTRIP_FIXTURE = 'conformance-a2a-task-roundtrip';
52
60
  const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
53
61
 
@@ -29,6 +29,14 @@ import { behaviorGate } from '../lib/behavior-gate.js';
29
29
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
30
30
  import { getA2AFakePeer } from '../lib/a2a-fake-peer.js';
31
31
 
32
+ /**
33
+ * Callback-shaped: the host issues A2A calls to the suite's fake peer, which records the negotiated version header.
34
+ *
35
+ * Unwitnessable when the host is in a separate network namespace — see
36
+ * `../lib/host-callback.ts`. Not host non-conformance; no route.
37
+ */
38
+ export const REQUIRES_HOST_CALLBACK = "the host issues A2A calls to the suite's fake peer, which records the negotiated version header";
39
+
32
40
  const PROFILE = 'a2a.versionNegotiation';
33
41
 
34
42
  interface A2ACaps {
@@ -35,6 +35,14 @@ import { isFixtureAdvertised } from '../lib/fixtures.js';
35
35
  import { createSyntheticOIDCIssuer } from '../lib/oidc-issuer.js';
36
36
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
37
37
 
38
+ /**
39
+ * Callback-shaped: the host fetches the token endpoint on the suite's synthetic OIDC issuer.
40
+ *
41
+ * Unwitnessable when the host is in a separate network namespace — see
42
+ * `../lib/host-callback.ts`. Not host non-conformance; no route.
43
+ */
44
+ export const REQUIRES_HOST_CALLBACK = "the host fetches the token endpoint on the suite's synthetic OIDC issuer";
45
+
38
46
  interface OAuth2Caps {
39
47
  supported?: boolean;
40
48
  issuer?: string;
@@ -49,6 +49,14 @@ import {
49
49
  } from '../lib/oidc-issuer.js';
50
50
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
51
51
 
52
+ /**
53
+ * Callback-shaped: the host fetches JWKS and discovery from the suite's synthetic OIDC issuer.
54
+ *
55
+ * Unwitnessable when the host is in a separate network namespace — see
56
+ * `../lib/host-callback.ts`. Not host non-conformance; no route.
57
+ */
58
+ export const REQUIRES_HOST_CALLBACK = "the host fetches JWKS and discovery from the suite's synthetic OIDC issuer";
59
+
52
60
  interface OIDCCaps {
53
61
  supported?: boolean;
54
62
  issuers?: string[];
@@ -37,6 +37,14 @@ import { pollUntilTerminal } from '../lib/polling.js';
37
37
  import { isFixtureAdvertised } from '../lib/fixtures.js';
38
38
  import { getCollector, waitForRunSpans } from '../lib/otel-collector.js';
39
39
 
40
+ /**
41
+ * Callback-shaped: the host exports OTLP metrics to the suite's collector.
42
+ *
43
+ * Unwitnessable when the host is in a separate network namespace — see
44
+ * `../lib/host-callback.ts`. Not host non-conformance; no route.
45
+ */
46
+ export const REQUIRES_HOST_CALLBACK = "the host exports OTLP metrics to the suite's collector";
47
+
40
48
  const NOOP_WORKFLOW_ID = 'conformance-noop';
41
49
  const COST_EMIT_WORKFLOW_ID = 'openwop-smoke-cost-emit';
42
50
  const SKIP_NO_NOOP = !isFixtureAdvertised(NOOP_WORKFLOW_ID);
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Every callback-shaped scenario declares itself.
3
+ *
4
+ * See `../lib/host-callback.ts` for the rule and why it is a declaration rather
5
+ * than a detector. In short: a scenario needing the HOST to originate a
6
+ * connection back to the harness cannot be witnessed when the host is in a
7
+ * separate network namespace, and **that is a networking property, not host
8
+ * non-conformance.**
9
+ *
10
+ * This gate enforces the declaration where the signal is unambiguous — a
11
+ * scenario importing a module that stands up a harness-hosted server. It is a
12
+ * floor, not an oracle, and the docblock next door says so.
13
+ *
14
+ * Server-free; reads the suite's own sources.
15
+ */
16
+
17
+ import { describe, it, expect } from 'vitest';
18
+ import { readFileSync, readdirSync } from 'node:fs';
19
+ import { join } from 'node:path';
20
+ import { SCENARIOS_DIR } from '../lib/paths.js';
21
+ import { HARNESS_DOUBLE_MODULES } from '../lib/host-callback.js';
22
+
23
+ const DECLARATION = /export\s+const\s+REQUIRES_HOST_CALLBACK\s*[:=]/;
24
+ /**
25
+ * The authored opt-out, for a scenario that imports a double but drives BOTH
26
+ * ends itself. Importing a harness module is the signal the gate can see; it is
27
+ * not proof a host participates. The opt-out carries a reason for the same
28
+ * reason the declaration does — an unexplained exception is indistinguishable
29
+ * from an author who forgot.
30
+ */
31
+ const OPT_OUT = /export\s+const\s+HOST_CALLBACK_NOT_REQUIRED\s*[:=]/;
32
+
33
+ interface Scenario {
34
+ readonly file: string;
35
+ readonly source: string;
36
+ readonly doubles: readonly string[];
37
+ readonly declared: boolean;
38
+ readonly optedOut: boolean;
39
+ }
40
+
41
+ function scan(dir: string): Scenario[] {
42
+ return readdirSync(dir)
43
+ .filter((f) => f.endsWith('.test.ts'))
44
+ .sort()
45
+ .map((file) => {
46
+ const source = readFileSync(join(dir, file), 'utf8');
47
+ return {
48
+ file,
49
+ source,
50
+ doubles: HARNESS_DOUBLE_MODULES.filter((m) =>
51
+ new RegExp(`from '\\.\\./lib/${m}(\\.js)?'`).test(source),
52
+ ),
53
+ declared: DECLARATION.test(source),
54
+ optedOut: OPT_OUT.test(source),
55
+ };
56
+ });
57
+ }
58
+
59
+ describe.skipIf(SCENARIOS_DIR === null)('host-callback declaration (conformance/README §"Where the suite runs")', () => {
60
+ const all = SCENARIOS_DIR === null ? [] : scan(SCENARIOS_DIR);
61
+
62
+ it('the scan reaches the scenario corpus', () => {
63
+ // Guard: an empty scan makes every leg below vacuously true, which is the
64
+ // shape RFC 0148 §C found in the floor verifier. A gate that passes by
65
+ // having looked at nothing is worse than no gate, because it reports clean.
66
+ expect(all.length, 'the scenario directory MUST be readable and populated').toBeGreaterThan(100);
67
+ expect(
68
+ all.filter((s) => s.doubles.length > 0).length,
69
+ 'the suite MUST contain scenarios that drive harness doubles — if this hits zero the ' +
70
+ 'module list in `host-callback.ts` has drifted from the imports it is meant to track, ' +
71
+ 'and the gate is measuring nothing',
72
+ ).toBeGreaterThan(0);
73
+ });
74
+
75
+ it('every scenario driving a harness double declares the callback', () => {
76
+ const undeclared = all
77
+ .filter((s) => s.doubles.length > 0 && !s.declared && !s.optedOut)
78
+ .map((s) => `${s.file} (imports ${s.doubles.join(', ')})`);
79
+ expect(
80
+ undeclared,
81
+ 'A scenario that hands the host a harness-hosted endpoint MUST export ' +
82
+ '`REQUIRES_HOST_CALLBACK` naming the connection the host has to originate — or ' +
83
+ '`HOST_CALLBACK_NOT_REQUIRED` explaining why it drives both ends itself.\n\n' +
84
+ 'Without it, a consumer running the suite off-process — against a container, VM, or ' +
85
+ 'remote origin — discovers the scenario is unwitnessable by watching it fail, and reads ' +
86
+ 'a routing problem as host non-conformance. RFC 0148 §A resolves an unwitnessed ' +
87
+ 'requirement to `blocked` rather than to a pass, and a consumer can only honour that ' +
88
+ 'for scenarios they can identify in advance.\n ' + undeclared.join('\n '),
89
+ ).toEqual([]);
90
+ });
91
+
92
+ it('the declaration states a reason rather than a bare flag', () => {
93
+ // A boolean records that somebody ticked a box. A sentence records what a
94
+ // consumer must route, and is checkable against the scenario body by anyone
95
+ // reading the diff — the same annotated-vs-bare rule RFC 0149 §D applies to
96
+ // acceptance criteria, one artifact over.
97
+ const bare: string[] = [];
98
+ for (const s of all.filter((x) => x.declared || x.optedOut)) {
99
+ const m = /export\s+const\s+(?:REQUIRES_HOST_CALLBACK|HOST_CALLBACK_NOT_REQUIRED)\s*[:=][^\n]*(?:\n[^\n;]*)?/.exec(s.source);
100
+ const line = m?.[0] ?? '';
101
+ if (/=\s*(true|false)\s*;?\s*$/.test(line) || !/['"`]/.test(line)) bare.push(`${s.file}: ${line.trim()}`);
102
+ }
103
+ expect(
104
+ bare,
105
+ '`REQUIRES_HOST_CALLBACK` MUST be a string naming which connection the host originates — ' +
106
+ 'a bare boolean says a box was ticked, not what a consumer has to route.\n ' +
107
+ bare.join('\n '),
108
+ ).toEqual([]);
109
+ });
110
+
111
+ it('nothing declares a callback it does not make', () => {
112
+ // The reverse direction, and the one that keeps the list honest as the
113
+ // corpus moves. A declaration left behind after the double was removed
114
+ // would tell a consumer to route something nobody needs — a stale claim,
115
+ // and the cheapest kind to leave lying around.
116
+ const orphaned = all
117
+ .filter((s) => s.declared && s.doubles.length === 0)
118
+ .filter((s) => !/endpoint\(\)/.test(s.source))
119
+ .map((s) => s.file);
120
+ expect(
121
+ orphaned,
122
+ 'a scenario declaring `REQUIRES_HOST_CALLBACK` MUST actually drive a harness-hosted ' +
123
+ 'endpoint. A declaration that outlived its double is a routing instruction for a ' +
124
+ 'connection nobody makes.\n ' + orphaned.join('\n '),
125
+ ).toEqual([]);
126
+ });
127
+ });
@@ -66,6 +66,14 @@ import { getMcpFakeServer } from '../lib/mcp-fake-server.js';
66
66
  import { isFixtureAdvertised } from '../lib/fixtures.js';
67
67
  import { pollUntilTerminal } from '../lib/polling.js';
68
68
 
69
+ /**
70
+ * Callback-shaped: the host issues MCP JSON-RPC calls to the suite's fake server.
71
+ *
72
+ * Unwitnessable when the host is in a separate network namespace — see
73
+ * `../lib/host-callback.ts`. Not host non-conformance; no route.
74
+ */
75
+ export const REQUIRES_HOST_CALLBACK = "the host issues MCP JSON-RPC calls to the suite's fake server";
76
+
69
77
  const ROUNDTRIP_FIXTURE = 'conformance-mcp-tool-roundtrip';
70
78
 
71
79
  /**
@@ -30,6 +30,14 @@ import { behaviorGate } from '../lib/behavior-gate.js';
30
30
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
31
31
  import { getMcpFakeServer } from '../lib/mcp-fake-server.js';
32
32
 
33
+ /**
34
+ * Callback-shaped: the host issues MCP calls to the suite's fake server, which records the revision header.
35
+ *
36
+ * Unwitnessable when the host is in a separate network namespace — see
37
+ * `../lib/host-callback.ts`. Not host non-conformance; no route.
38
+ */
39
+ export const REQUIRES_HOST_CALLBACK = "the host issues MCP calls to the suite's fake server, which records the revision header";
40
+
33
41
  const PROFILE = 'mcp.versionNegotiation';
34
42
  const DATE_FORM = /^\d{4}-\d{2}-\d{2}$/;
35
43
 
@@ -24,6 +24,14 @@ import { pollUntilTerminal } from '../lib/polling.js';
24
24
  import { isFixtureAdvertised } from '../lib/fixtures.js';
25
25
  import { getCollector } from '../lib/otel-collector.js';
26
26
 
27
+ /**
28
+ * Callback-shaped: the host exports OTLP metrics to the suite's collector.
29
+ *
30
+ * Unwitnessable when the host is in a separate network namespace — see
31
+ * `../lib/host-callback.ts`. Not host non-conformance; no route.
32
+ */
33
+ export const REQUIRES_HOST_CALLBACK = "the host exports OTLP metrics to the suite's collector";
34
+
27
35
  const FIXTURE = 'conformance-noop';
28
36
 
29
37
  interface MetricsCaps {
@@ -38,6 +38,20 @@
38
38
  import { describe, it, expect, afterEach } from 'vitest';
39
39
  import { OtelCollector } from '../lib/otel-collector.js';
40
40
 
41
+ /**
42
+ * NOT callback-shaped, despite importing the collector.
43
+ *
44
+ * This scenario stands up the collector and POSTs synthetic OTLP payloads to
45
+ * it ITSELF — the suite is both ends. No host is involved, so there is no
46
+ * connection for a host to originate and nothing to route in a container.
47
+ *
48
+ * Stated rather than silently exempted: importing a harness double is the
49
+ * signal the gate can see, and an unexplained exception is indistinguishable
50
+ * from an author who forgot.
51
+ */
52
+ export const HOST_CALLBACK_NOT_REQUIRED =
53
+ "the suite posts synthetic OTLP payloads to its own collector; no host participates";
54
+
41
55
  const CANARY = 'sk-canary-DO-NOT-LEAK-0f3a9c';
42
56
  const REDACTED = '[REDACTED:openwop-conformance-canary-secret]';
43
57
 
@@ -31,6 +31,14 @@ import { pollUntilTerminal } from '../lib/polling.js';
31
31
  import { isFixtureAdvertised } from '../lib/fixtures.js';
32
32
  import { getCollector, waitForRunSpans } from '../lib/otel-collector.js';
33
33
 
34
+ /**
35
+ * Callback-shaped: the host exports OTLP/gRPC spans to the suite's collector.
36
+ *
37
+ * Unwitnessable when the host is in a separate network namespace — see
38
+ * `../lib/host-callback.ts`. Not host non-conformance; no route.
39
+ */
40
+ export const REQUIRES_HOST_CALLBACK = "the host exports OTLP/gRPC spans to the suite's collector";
41
+
34
42
  const FIXTURE = 'conformance-noop';
35
43
 
36
44
  async function advertisesGrpcExport(): Promise<boolean> {
@@ -29,6 +29,14 @@ import { pollUntilTerminal } from '../lib/polling.js';
29
29
  import { isFixtureAdvertised } from '../lib/fixtures.js';
30
30
  import { getCollector, waitForRunSpans } from '../lib/otel-collector.js';
31
31
 
32
+ /**
33
+ * Callback-shaped: the host exports OTLP/HTTP spans to the suite's collector.
34
+ *
35
+ * Unwitnessable when the host is in a separate network namespace — see
36
+ * `../lib/host-callback.ts`. Not host non-conformance; no route.
37
+ */
38
+ export const REQUIRES_HOST_CALLBACK = "the host exports OTLP/HTTP spans to the suite's collector";
39
+
32
40
  const FIXTURE = 'conformance-noop';
33
41
 
34
42
  async function isObservabilityAdvertised(): Promise<boolean> {
@@ -40,6 +40,14 @@ import { isFixtureAdvertised } from '../lib/fixtures.js';
40
40
  import { isScenarioOptedOut } from '../lib/env.js';
41
41
  import { getCollector, waitForRunSpans } from '../lib/otel-collector.js';
42
42
 
43
+ /**
44
+ * Callback-shaped: the host exports OTLP spans to the suite's collector.
45
+ *
46
+ * Unwitnessable when the host is in a separate network namespace — see
47
+ * `../lib/host-callback.ts`. Not host non-conformance; no route.
48
+ */
49
+ export const REQUIRES_HOST_CALLBACK = "the host exports OTLP spans to the suite's collector";
50
+
43
51
  const PARENT_FIXTURE = 'conformance-subworkflow-parent';
44
52
  const SCENARIO_ID = 'otel-trace-propagation-subworkflow';
45
53
 
@@ -23,6 +23,14 @@ import { pollUntilTerminal } from '../lib/polling.js';
23
23
  import { isFixtureAdvertised } from '../lib/fixtures.js';
24
24
  import { getCollector, waitForRunSpans } from '../lib/otel-collector.js';
25
25
 
26
+ /**
27
+ * Callback-shaped: the host exports OTLP spans to the suite's collector.
28
+ *
29
+ * Unwitnessable when the host is in a separate network namespace — see
30
+ * `../lib/host-callback.ts`. Not host non-conformance; no route.
31
+ */
32
+ export const REQUIRES_HOST_CALLBACK = "the host exports OTLP spans to the suite's collector";
33
+
26
34
  const FIXTURE = 'conformance-noop';
27
35
 
28
36
  /** Build a syntactically-valid traceparent with a known traceId. */
@@ -122,11 +122,54 @@ describe.skipIf(SKIP_TIMEOUT)('run-execution-bounds: run-duration breach (RFC 00
122
122
  'run-event-payloads.schema.json §capBreached.kind',
123
123
  'cap.breached payload MUST carry kind="run-duration"',
124
124
  )).toBe('run-duration');
125
+ // Three distinct failure modes, asserted separately and WITH THE VALUES.
126
+ //
127
+ // The previous form ANDed all three into one boolean over a message that
128
+ // named none of them, so a failure said only "MUST be strictly greater" —
129
+ // you could not tell whether `observed` was missing, equal, or smaller. A
130
+ // tier-1 host hit this intermittently and had to reason out the mechanism
131
+ // from first principles, because the assertion about observed values did
132
+ // not report the observed values.
125
133
  expect(
126
- typeof payload?.observed === 'number' && typeof payload?.limit === 'number' && payload!.observed > payload!.limit,
134
+ typeof payload?.observed,
127
135
  driver.describe(
128
136
  'run-event-payloads.schema.json §capBreached.observed',
129
- 'observed (elapsedMs) MUST be strictly greater than limit (resolved timeout)',
137
+ `cap.breached MUST carry a numeric \`observed\`; got ${JSON.stringify(payload?.observed)}`,
138
+ ),
139
+ ).toBe('number');
140
+ expect(
141
+ typeof payload?.limit,
142
+ driver.describe(
143
+ 'run-event-payloads.schema.json §capBreached.limit',
144
+ `cap.breached MUST carry a numeric \`limit\`; got ${JSON.stringify(payload?.limit)}`,
145
+ ),
146
+ ).toBe('number');
147
+
148
+ // `capabilities.md` §"Engine-enforced limits": *"Always strictly greater
149
+ // than limit."* This is satisfiable and it constrains the host's comparison:
150
+ // breach when elapsed EXCEEDS the deadline, not when it reaches it. A host
151
+ // testing `elapsed >= limit` emits `observed === limit` exactly when the
152
+ // clock lands on the boundary — which is rare, machine-dependent, and
153
+ // therefore reads as flake rather than as the deterministic defect it is.
154
+ //
155
+ // That asymmetry is why the diagnosis belongs in the message: system load
156
+ // makes elapsed LARGER, so it makes this assertion easier to satisfy, not
157
+ // harder. An `observed === limit` failure is not a loaded box — it is a
158
+ // `>=` comparison in the host.
159
+ const { observed = NaN, limit = NaN } = payload ?? {};
160
+ expect(
161
+ observed > limit,
162
+ driver.describe(
163
+ 'run-event-payloads.schema.json §capBreached.observed',
164
+ `observed (elapsedMs) MUST be strictly greater than limit (resolved timeout). ` +
165
+ `Got observed=${observed}, limit=${limit}` +
166
+ (observed === limit
167
+ ? '. They are EQUAL, which means the host breached at `elapsed >= limit` rather than ' +
168
+ '`elapsed > limit`. The limit is not breached until it has been passed. This is ' +
169
+ 'deterministic in the host and only surfaces when the clock lands exactly on the ' +
170
+ 'boundary, so it presents as an intermittent failure — load makes elapsed larger and ' +
171
+ 'therefore makes this assertion PASS more often, not less.'
172
+ : '.'),
130
173
  ),
131
174
  ).toBe(true);
132
175
  });
@@ -58,6 +58,14 @@ import { isFixtureAdvertised } from '../lib/fixtures.js';
58
58
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
59
59
  import { getCollector, waitForRunSpans } from '../lib/otel-collector.js';
60
60
 
61
+ /**
62
+ * Callback-shaped: the host exports OTLP spans to the suite's collector, which scans them for the BYOK canary.
63
+ *
64
+ * Unwitnessable when the host is in a separate network namespace — see
65
+ * `../lib/host-callback.ts`. Not host non-conformance; no route.
66
+ */
67
+ export const REQUIRES_HOST_CALLBACK = "the host exports OTLP spans to the suite's collector, which scans them for the BYOK canary";
68
+
61
69
  const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
62
70
  const BYOK_WORKFLOW_ID = 'openwop-smoke-byok-roundtrip';
63
71
  const FIXTURE_SKIP = !isFixtureAdvertised(BYOK_WORKFLOW_ID);
@@ -108,8 +108,36 @@ describe('workflow-chain-host-expansion: live host wraps expansion algorithm cor
108
108
  'a host serving the RFC 0013 host-expansion test seam MUST set `hostExpansionSeam: true` (and, being a chain-pack consumer, `supported: true`) in the discovery block',
109
109
  ),
110
110
  ).toBeDefined();
111
- expect(caps?.hostExpansionSeam).toBe(true);
112
111
  expect(caps?.supported).toBe(true);
112
+
113
+ // `hostExpansionSeam === true` is NOT asserted here, deliberately: this leg
114
+ // only runs when `isExpansionAdvertised()` already read it as true, so
115
+ // asserting it again is a tautology that can never fail. It looked like a
116
+ // check and was a restatement — the same shape as a golden-vector gate
117
+ // comparing two stored constants.
118
+ //
119
+ // What this leg's NAME promises is that the seam is *served*, so that is
120
+ // what it now verifies. A host advertising the flag while the route 404s
121
+ // has made its discovery document false, and a consumer that read it
122
+ // planned against a capability that is not there.
123
+ //
124
+ // Found by a tier-1 host running the suite against its release IMAGE: the
125
+ // seam resolved its fixture manifest through `require.resolve` on a
126
+ // devDependency at request time, which succeeds in a source tree and fails
127
+ // under `npm ci --omit=dev`. Advertised, 404ing, **and this leg passed** —
128
+ // the defect surfaced three legs later as a confusing expansion mismatch
129
+ // rather than here, where the name says it belongs.
130
+ const probe = await driver.post(EXPAND_PATH, { packName: SAMPLE_PACK, chainId: CHAIN_1_NODE, parameters: {} });
131
+ expect(
132
+ probe.status,
133
+ driver.describe(
134
+ 'capabilities.md §workflowChainPacks',
135
+ `a host advertising \`hostExpansionSeam: true\` MUST serve ${EXPAND_PATH}. Got ${probe.status} — ` +
136
+ 'the discovery document promises a seam the host does not route. Advertising a capability ' +
137
+ 'that 404s is worse than advertising nothing: a consumer that read the flag made a plan ' +
138
+ 'on a fact that was not true.',
139
+ ),
140
+ ).not.toBe(404);
113
141
  });
114
142
 
115
143
  it('positive — 1-node chain expansion matches the reference library for the bundled pack', async () => {