@openwop/openwop-conformance 1.106.1 → 1.124.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.
Files changed (166) hide show
  1. package/README.md +3 -3
  2. package/coverage.md +6 -0
  3. package/dist/cli.js +89 -45
  4. package/dist/lib/certification-bundle-verify.js +274 -0
  5. package/dist/lib/profiles.js +102 -19
  6. package/dist/lib/requirement-ledger.js +182 -0
  7. package/dist/lib/requirement-registry.js +110 -0
  8. package/dist/lib/scenario-disposition.js +249 -0
  9. package/dist/lib/soft-skip.js +82 -0
  10. package/fixtures/conformance-a2a-task-roundtrip.json +1 -1
  11. package/fixtures.md +1 -1
  12. package/package.json +1 -1
  13. package/schemas/CORPUS-STAMP.json +2 -2
  14. package/schemas/README.md +1 -0
  15. package/schemas/capabilities.schema.json +2 -2
  16. package/schemas/compensation-policy.schema.json +64 -0
  17. package/schemas/run-snapshot.schema.json +5 -0
  18. package/schemas/workflow-chain-pack-manifest.schema.json +125 -0
  19. package/schemas/workflow-definition.schema.json +4 -0
  20. package/src/cli.ts +99 -45
  21. package/src/lib/a2a-fake-peer.ts +290 -110
  22. package/src/lib/certification-bundle-verify.ts +356 -0
  23. package/src/lib/error-envelope.ts +77 -0
  24. package/src/lib/llm-cache-key-recipe.ts +6 -5
  25. package/src/lib/mcp-fake-server.ts +235 -72
  26. package/src/lib/profiles.ts +132 -19
  27. package/src/lib/requirement-ledger.ts +98 -1
  28. package/src/lib/requirement-registry.ts +47 -4
  29. package/src/lib/scenario-disposition.ts +291 -0
  30. package/src/lib/soft-skip.ts +89 -0
  31. package/src/lib/workflow-chain-expansion.ts +189 -0
  32. package/src/scenarios/a2a-1-0-agent-card.test.ts +179 -0
  33. package/src/scenarios/a2a-1-0-task-roundtrip.test.ts +109 -0
  34. package/src/scenarios/a2a-card-runtime-consistency.test.ts +167 -0
  35. package/src/scenarios/a2a-peer-authority.test.ts +99 -0
  36. package/src/scenarios/a2a-task-roundtrip.test.ts +18 -6
  37. package/src/scenarios/agent-loop-iteration-monotonic.test.ts +3 -2
  38. package/src/scenarios/agent-loop-stateful-resume.test.ts +3 -2
  39. package/src/scenarios/agent-loop-version5-shape.test.ts +2 -1
  40. package/src/scenarios/agent-loop-workspace-snapshot.test.ts +4 -3
  41. package/src/scenarios/approval-gate-flow.test.ts +5 -4
  42. package/src/scenarios/artifact-type-store-without-render.test.ts +2 -1
  43. package/src/scenarios/auth-scim-profile.test.ts +5 -4
  44. package/src/scenarios/authorization-fail-closed.test.ts +4 -3
  45. package/src/scenarios/certification-bundle-non-vacuous.test.ts +249 -0
  46. package/src/scenarios/certification-bundle-redaction.test.ts +182 -0
  47. package/src/scenarios/certification-floor-enforcement.test.ts +30 -6
  48. package/src/scenarios/chain-compensation-expansion.test.ts +243 -0
  49. package/src/scenarios/commitment-fired.test.ts +3 -2
  50. package/src/scenarios/compensation-behavior.test.ts +63 -0
  51. package/src/scenarios/compensation-profile.test.ts +144 -1
  52. package/src/scenarios/conformance-advertised-seam-required.test.ts +118 -0
  53. package/src/scenarios/conformance-execution-witness.test.ts +104 -0
  54. package/src/scenarios/conversationCapabilityNegotiation.test.ts +3 -2
  55. package/src/scenarios/core-manifest-and-extension-registry.test.ts +26 -0
  56. package/src/scenarios/credential-payload-redaction.test.ts +4 -3
  57. package/src/scenarios/credentials-capability-shape.test.ts +4 -3
  58. package/src/scenarios/cross-engine-append-behavior.test.ts +5 -4
  59. package/src/scenarios/cross-engine-append-ordering.test.ts +3 -2
  60. package/src/scenarios/cross-host-ancestry-endpoint.test.ts +5 -4
  61. package/src/scenarios/cross-host-causation-shape.test.ts +3 -2
  62. package/src/scenarios/cross-workspace-isolation.test.ts +4 -3
  63. package/src/scenarios/data-residency-admission.test.ts +6 -6
  64. package/src/scenarios/deadletter-retry-exhaustion.test.ts +5 -4
  65. package/src/scenarios/distillation-index-roundtrip.test.ts +3 -2
  66. package/src/scenarios/distillation-secret-carryforward.test.ts +3 -2
  67. package/src/scenarios/distillation-shape.test.ts +2 -1
  68. package/src/scenarios/distillation-stable-archive.test.ts +4 -3
  69. package/src/scenarios/distillation-token-budget.test.ts +4 -3
  70. package/src/scenarios/envelope-recovery-applied.test.ts +3 -2
  71. package/src/scenarios/envelope-refusal-shape.test.ts +4 -3
  72. package/src/scenarios/error-envelope-canonical-shape.test.ts +64 -0
  73. package/src/scenarios/feedback-correction-redaction.test.ts +4 -3
  74. package/src/scenarios/feedback-cross-tenant-isolation.test.ts +4 -3
  75. package/src/scenarios/feedback-fork-not-copied.test.ts +6 -5
  76. package/src/scenarios/feedback-on-terminal-run.test.ts +4 -3
  77. package/src/scenarios/feedback-record-and-list.test.ts +4 -3
  78. package/src/scenarios/feedback-unsupported-501.test.ts +3 -2
  79. package/src/scenarios/fixtures-valid.test.ts +16 -18
  80. package/src/scenarios/fs-path-traversal.test.ts +3 -2
  81. package/src/scenarios/heartbeat-capability-shape.test.ts +2 -1
  82. package/src/scenarios/heartbeat-fires-once-per-tick.test.ts +4 -3
  83. package/src/scenarios/heartbeat-idempotent-no-spam.test.ts +6 -5
  84. package/src/scenarios/heartbeat-runtime-bound.test.ts +4 -3
  85. package/src/scenarios/mcp-2026-07-28-discover.test.ts +207 -0
  86. package/src/scenarios/mcp-cache-tenant-scope.test.ts +66 -0
  87. package/src/scenarios/mcp-current-auth-boundary.test.ts +55 -0
  88. package/src/scenarios/mcp-extension-opacity.test.ts +79 -0
  89. package/src/scenarios/mcp-mrtr-roundtrip.test.ts +140 -0
  90. package/src/scenarios/mcp-stateless-request.test.ts +77 -0
  91. package/src/scenarios/mcp-tool-roundtrip.test.ts +4 -3
  92. package/src/scenarios/mcp-toolcall-redaction.test.ts +2 -1
  93. package/src/scenarios/memory-attribution-emits-on-write.test.ts +6 -5
  94. package/src/scenarios/memory-attribution-no-content.test.ts +4 -3
  95. package/src/scenarios/memory-attribution-replay-stable.test.ts +6 -5
  96. package/src/scenarios/memory-attribution-tenant-scoped.test.ts +4 -3
  97. package/src/scenarios/memory-compaction-event-emitted.test.ts +3 -2
  98. package/src/scenarios/memory-compaction-provenance-tag.test.ts +6 -5
  99. package/src/scenarios/memory-compaction-sr1-carry-forward.test.ts +4 -3
  100. package/src/scenarios/memory-consolidation-idempotent.test.ts +4 -3
  101. package/src/scenarios/memory-injection-budget.test.ts +4 -3
  102. package/src/scenarios/metric-emission.test.ts +4 -3
  103. package/src/scenarios/multi-agent-confidence-escalation.test.ts +5 -4
  104. package/src/scenarios/multi-agent-handoff-state-machine.test.ts +4 -3
  105. package/src/scenarios/multi-agent-memory-lifecycle.test.ts +5 -4
  106. package/src/scenarios/multi-party-conversation-behavioral.test.ts +3 -2
  107. package/src/scenarios/multi-region-idempotency-behavior.test.ts +7 -6
  108. package/src/scenarios/multi-region-idempotency.test.ts +4 -3
  109. package/src/scenarios/normative-example-extraction.test.ts +242 -0
  110. package/src/scenarios/oauth-authorization-code-roundtrip.test.ts +4 -3
  111. package/src/scenarios/oauth-capability-shape.test.ts +4 -3
  112. package/src/scenarios/oauth-connector-redaction.test.ts +4 -3
  113. package/src/scenarios/openapi-asyncapi-sdk-parity.test.ts +309 -0
  114. package/src/scenarios/org-position-no-authority-escalation.test.ts +2 -1
  115. package/src/scenarios/otel-emission-grpc.test.ts +4 -3
  116. package/src/scenarios/otel-emission.test.ts +4 -3
  117. package/src/scenarios/otel-trace-propagation-subworkflow.test.ts +5 -4
  118. package/src/scenarios/otel-trace-propagation.test.ts +4 -3
  119. package/src/scenarios/pack-registry-isolation.test.ts +4 -3
  120. package/src/scenarios/pack-registry-publish.test.ts +65 -3
  121. package/src/scenarios/pack-registry.test.ts +22 -0
  122. package/src/scenarios/profile-discovery-core-alias.test.ts +137 -0
  123. package/src/scenarios/profileDerivation.test.ts +2 -1
  124. package/src/scenarios/provider-usage.test.ts +2 -2
  125. package/src/scenarios/rate-limit-envelope.test.ts +2 -1
  126. package/src/scenarios/registry-public.test.ts +6 -5
  127. package/src/scenarios/replay-divergence-at-refusal.test.ts +9 -8
  128. package/src/scenarios/replay-llm-cache-key-portable.test.ts +19 -13
  129. package/src/scenarios/replay-llm-cache-key.test.ts +120 -47
  130. package/src/scenarios/replay-observable-sequence-determinism.test.ts +5 -4
  131. package/src/scenarios/replay-side-effect-suppression.test.ts +20 -1
  132. package/src/scenarios/requirement-ledger.test.ts +29 -6
  133. package/src/scenarios/runner-ledger.test.ts +279 -0
  134. package/src/scenarios/runtime-requires-install-gate.test.ts +6 -5
  135. package/src/scenarios/sandbox-memory-cap.test.ts +3 -2
  136. package/src/scenarios/sandbox-mvp-behavior.test.ts +11 -10
  137. package/src/scenarios/sandbox-no-host-fs-escape.test.ts +2 -1
  138. package/src/scenarios/sandbox-timeout-cap.test.ts +3 -2
  139. package/src/scenarios/stream-text-fixture.test.ts +5 -4
  140. package/src/scenarios/subrun-approval-fail-closed.test.ts +3 -2
  141. package/src/scenarios/subrun-approval-gate.test.ts +4 -3
  142. package/src/scenarios/subrun-attestation-shape.test.ts +2 -1
  143. package/src/scenarios/subrun-checksum-stable.test.ts +4 -3
  144. package/src/scenarios/table-schema-enforcement.test.ts +2 -2
  145. package/src/scenarios/tool-hooks-authorization-fail-closed.test.ts +3 -2
  146. package/src/scenarios/tool-hooks-content-free.test.ts +3 -2
  147. package/src/scenarios/tool-hooks-rate-limit.test.ts +3 -2
  148. package/src/scenarios/tool-hooks-secret-redaction.test.ts +3 -2
  149. package/src/scenarios/tool-hooks-shape.test.ts +2 -1
  150. package/src/scenarios/trigger-bridge-delivery.test.ts +3 -2
  151. package/src/scenarios/version-fold.test.ts +5 -4
  152. package/src/scenarios/voice-streamref-tenant-bound.test.ts +2 -1
  153. package/src/scenarios/voice-transcription-streaming.test.ts +2 -1
  154. package/src/scenarios/voice-transcription-unadvertised.test.ts +2 -1
  155. package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +5 -4
  156. package/src/scenarios/wasm-pack-invoke-completed.test.ts +3 -2
  157. package/src/scenarios/wasm-pack-invoke-suspended.test.ts +3 -2
  158. package/src/scenarios/wasm-pack-load.test.ts +4 -3
  159. package/src/scenarios/wasm-pack-memory-cap.test.ts +4 -3
  160. package/src/scenarios/wasm-pack-replay-determinism.test.ts +3 -2
  161. package/src/scenarios/webhook-signed-delivery.test.ts +4 -3
  162. package/src/scenarios/workflow-primary-output-annotation.test.ts +13 -23
  163. package/src/scenarios/workload-identity-behavior.test.ts +7 -4
  164. package/src/scenarios/workspace-cross-tenant-isolation-blackbox.test.ts +3 -2
  165. package/src/scenarios/workspace-cross-tenant-isolation.test.ts +3 -2
  166. package/src/setup.ts +95 -1
@@ -32,6 +32,7 @@
32
32
  */
33
33
 
34
34
  import { describe, it, expect } from 'vitest';
35
+ import { softSkip } from '../lib/soft-skip.js';
35
36
  import { driver, type OpenWOPResponse } from '../lib/driver.js';
36
37
  import { subscribe } from '../lib/sse.js';
37
38
  import { pollUntilTerminal } from '../lib/polling.js';
@@ -93,14 +94,14 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-text-fixture: messages-mode fold throug
93
94
  '[stream-text-fixture] host does not advertise the stream-text mock provider ' +
94
95
  '(Capabilities.testing.mockProviders); skipping',
95
96
  );
96
- return;
97
+ return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isStreamTextMockAdvertised())` returned early');
97
98
  }
98
99
 
99
100
  const create = await createMockRun('stream-text');
100
101
  if (create.status === 403 && errCode(create.json) === 'mock_provider_forbidden') {
101
102
  // eslint-disable-next-line no-console
102
103
  console.warn('[stream-text-fixture] API key is production-scoped (403 mock_provider_forbidden); skipping');
103
- return;
104
+ return softSkip('inapplicable', '[stream-text-fixture] API key is production-scoped (403 mock_provider_forbidden); skipping');
104
105
  }
105
106
  expect(
106
107
  create.status,
@@ -192,13 +193,13 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-text-fixture: messages-mode fold throug
192
193
  if (!(await isStreamTextMockAdvertised())) {
193
194
  // eslint-disable-next-line no-console
194
195
  console.warn('[stream-text-fixture] stream-text mock not advertised; skipping negative leg');
195
- return;
196
+ return softSkip('inapplicable', '[stream-text-fixture] stream-text mock not advertised; skipping negative leg');
196
197
  }
197
198
  const create = await createMockRun('does-not-exist');
198
199
  if (create.status === 403 && errCode(create.json) === 'mock_provider_forbidden') {
199
200
  // eslint-disable-next-line no-console
200
201
  console.warn('[stream-text-fixture] API key is production-scoped; skipping negative leg');
201
- return;
202
+ return softSkip('inapplicable', '[stream-text-fixture] API key is production-scoped; skipping negative leg');
202
203
  }
203
204
  expect(
204
205
  create.status,
@@ -13,18 +13,19 @@
13
13
  */
14
14
 
15
15
  import { describe, it, expect } from 'vitest';
16
+ import { softSkip } from '../lib/soft-skip.js';
16
17
  import { driver } from '../lib/driver.js';
17
18
  import { readSubRunAttestationCap, invokeSubRunAttest } from '../lib/subRunAttestation.js';
18
19
 
19
20
  describe('subrun-approval-fail-closed (RFC 0063 §C)', () => {
20
21
  it('no accept/edit-accept (terminated or expired) MUST NOT merge', async () => {
21
- if ((await readSubRunAttestationCap()) !== true) return;
22
+ if ((await readSubRunAttestationCap()) !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `(await readSubRunAttestationCap()) !== true` returned early');
22
23
  // approvalAction omitted models a run that terminated without a response.
23
24
  const res = await invokeSubRunAttest({
24
25
  childOutputs: { artifact: 'unverified' },
25
26
  outputAttestation: { requireApproval: true },
26
27
  });
27
- if (res === null) return; // seam absent — soft-skip
28
+ if (res === null) return softSkip('blocked', 'seam absent — soft-skip');
28
29
  expect(
29
30
  res.merged,
30
31
  driver.describe('RFC 0063 §C', 'an unresolved approval MUST fail closed — outputs MUST NOT be merged'),
@@ -10,23 +10,24 @@
10
10
  */
11
11
 
12
12
  import { describe, it, expect } from 'vitest';
13
+ import { softSkip } from '../lib/soft-skip.js';
13
14
  import { driver } from '../lib/driver.js';
14
15
  import { readSubRunAttestationCap, invokeSubRunAttest } from '../lib/subRunAttestation.js';
15
16
 
16
17
  describe('subrun-approval-gate (RFC 0063 §C)', () => {
17
18
  it('accept merges the child outputs; reject does not', async () => {
18
- if ((await readSubRunAttestationCap()) !== true) return;
19
+ if ((await readSubRunAttestationCap()) !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `(await readSubRunAttestationCap()) !== true` returned early');
19
20
  const base = { childOutputs: { artifact: 'x' }, outputAttestation: { requireApproval: true } };
20
21
 
21
22
  const accepted = await invokeSubRunAttest({ ...base, approvalAction: 'accept' });
22
- if (accepted === null) return; // seam absent — soft-skip
23
+ if (accepted === null) return softSkip('blocked', 'seam absent — soft-skip');
23
24
  expect(
24
25
  accepted.merged,
25
26
  driver.describe('RFC 0063 §C', 'an `accept` approval MUST merge the child outputs'),
26
27
  ).toBe(true);
27
28
 
28
29
  const rejected = await invokeSubRunAttest({ ...base, approvalAction: 'reject' });
29
- if (rejected === null) return;
30
+ if (rejected === null) return softSkip('blocked', 'precondition not met — `rejected === null` returned early (seam, prior step, or fixture unavailable)');
30
31
  expect(
31
32
  rejected.merged,
32
33
  driver.describe('RFC 0063 §C', 'a `reject` approval MUST NOT merge the child outputs'),
@@ -11,6 +11,7 @@
11
11
  */
12
12
 
13
13
  import { describe, it, expect } from 'vitest';
14
+ import { softSkip } from '../lib/soft-skip.js';
14
15
  import { driver } from '../lib/driver.js';
15
16
  import { readSubRunAttestationCap } from '../lib/subRunAttestation.js';
16
17
 
@@ -18,7 +19,7 @@ describe('subrun-attestation-shape: advertisement (RFC 0063 §A)', () => {
18
19
  it('capabilities.agents.subRunAttestation is absent or a boolean', async () => {
19
20
  const cap = await readSubRunAttestationCap();
20
21
  // null = unadvertised (no agents block OR flag omitted) — valid.
21
- if (cap === null) return;
22
+ if (cap === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap === null` returned early');
22
23
  expect(
23
24
  typeof cap,
24
25
  driver.describe(
@@ -11,21 +11,22 @@
11
11
  */
12
12
 
13
13
  import { describe, it, expect } from 'vitest';
14
+ import { softSkip } from '../lib/soft-skip.js';
14
15
  import { driver } from '../lib/driver.js';
15
16
  import { readSubRunAttestationCap, invokeSubRunAttest } from '../lib/subRunAttestation.js';
16
17
 
17
18
  describe('subrun-checksum-stable (RFC 0063 §B)', () => {
18
19
  it('identical child outputs produce an identical sha256 attestation checksum', async () => {
19
- if ((await readSubRunAttestationCap()) !== true) return;
20
+ if ((await readSubRunAttestationCap()) !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `(await readSubRunAttestationCap()) !== true` returned early');
20
21
  const childOutputs = { report: 'done', score: 0.9, tags: ['a', 'b'] };
21
22
  const a = await invokeSubRunAttest({ childOutputs, outputAttestation: { checksum: true } });
22
- if (a === null) return; // seam absent — soft-skip
23
+ if (a === null) return softSkip('blocked', 'seam absent — soft-skip');
23
24
  // Key-reordered but value-identical: JCS canonicalization MUST yield the same hash.
24
25
  const b = await invokeSubRunAttest({
25
26
  childOutputs: { tags: ['a', 'b'], score: 0.9, report: 'done' },
26
27
  outputAttestation: { checksum: true },
27
28
  });
28
- if (b === null) return;
29
+ if (b === null) return softSkip('blocked', 'precondition not met — `b === null` returned early (seam, prior step, or fixture unavailable)');
29
30
  const att = a.attestation ?? {};
30
31
  expect(
31
32
  typeof att.checksum === 'string' && (att.checksum as string).length > 0,
@@ -14,6 +14,7 @@
14
14
  */
15
15
 
16
16
  import { describe, it, expect } from 'vitest';
17
+ import { readErrorCode } from '../lib/error-envelope.js';
17
18
  import { driver } from '../lib/driver.js';
18
19
 
19
20
  interface DiscoveryDoc {
@@ -74,8 +75,7 @@ describe('table-schema-enforcement: behavioral (RFC 0016 §B point 2)', () => {
74
75
  bad.status >= 400 && bad.status < 500,
75
76
  driver.describe('RFC 0016 §B point 2', 'type-divergent insert MUST be rejected with 4xx'),
76
77
  ).toBe(true);
77
- const body = bad.json as { error?: { code?: string } | string };
78
- const code = typeof body.error === 'string' ? body.error : body.error?.code;
78
+ const code = readErrorCode(bad.json);
79
79
  expect(
80
80
  code,
81
81
  driver.describe('RFC 0016 §B point 2', 'rejection MUST carry the table_schema_violation error code'),
@@ -12,13 +12,14 @@
12
12
  */
13
13
 
14
14
  import { describe, it, expect } from 'vitest';
15
+ import { softSkip } from '../lib/soft-skip.js';
15
16
  import { driver } from '../lib/driver.js';
16
17
  import { readToolHooksCap, invokeToolHook } from '../lib/toolHooks.js';
17
18
 
18
19
  describe('tool-hooks-authorization-fail-closed (RFC 0064 §C)', () => {
19
20
  it('a principal lacking a tool scope is denied and the tool is not invoked', async () => {
20
21
  const cap = await readToolHooksCap();
21
- if (cap?.perToolAuthorization !== true) return;
22
+ if (cap?.perToolAuthorization !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap?.perToolAuthorization !== true` returned early');
22
23
  // A principal with no scopes against a tool requiring one MUST be denied.
23
24
  const res = await invokeToolHook({
24
25
  principal: 'conformance-unprivileged',
@@ -26,7 +27,7 @@ describe('tool-hooks-authorization-fail-closed (RFC 0064 §C)', () => {
26
27
  requiredScopes: ['db:write'],
27
28
  args: {},
28
29
  });
29
- if (res === null) return; // seam absent — soft-skip
30
+ if (res === null) return softSkip('blocked', 'seam absent — soft-skip');
30
31
  expect(
31
32
  (res.toolReturned ?? {}).status,
32
33
  driver.describe('RFC 0064 §C', 'a missing/unevaluable tool scope MUST fail closed → status:"forbidden"'),
@@ -11,15 +11,16 @@
11
11
  */
12
12
 
13
13
  import { describe, it, expect } from 'vitest';
14
+ import { softSkip } from '../lib/soft-skip.js';
14
15
  import { driver } from '../lib/driver.js';
15
16
  import { readToolHooksCap, invokeToolHook } from '../lib/toolHooks.js';
16
17
 
17
18
  describe('tool-hooks-content-free (RFC 0064 §B)', () => {
18
19
  it('toolCalled carries argsHash; toolReturned carries status + durationMs', async () => {
19
20
  const cap = await readToolHooksCap();
20
- if (cap?.prePostEvents !== true) return;
21
+ if (cap?.prePostEvents !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap?.prePostEvents !== true` returned early');
21
22
  const res = await invokeToolHook({ principal: 'core.system', toolName: 'web.search', args: { q: 'openwop' } });
22
- if (res === null) return; // seam absent — soft-skip
23
+ if (res === null) return softSkip('blocked', 'seam absent — soft-skip');
23
24
  const called = res.toolCalled ?? {};
24
25
  const returned = res.toolReturned ?? {};
25
26
  expect(
@@ -10,20 +10,21 @@
10
10
  */
11
11
 
12
12
  import { describe, it, expect } from 'vitest';
13
+ import { softSkip } from '../lib/soft-skip.js';
13
14
  import { driver } from '../lib/driver.js';
14
15
  import { readToolHooksCap, invokeToolHook } from '../lib/toolHooks.js';
15
16
 
16
17
  describe('tool-hooks-rate-limit (RFC 0064 §D)', () => {
17
18
  it('an exhausted (principal, tool) bucket yields status:"rate_limited"', async () => {
18
19
  const cap = await readToolHooksCap();
19
- if (cap?.perToolRateLimit !== true) return;
20
+ if (cap?.perToolRateLimit !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap?.perToolRateLimit !== true` returned early');
20
21
  const res = await invokeToolHook({
21
22
  principal: 'core.system',
22
23
  toolName: 'web.search',
23
24
  args: { q: 'x' },
24
25
  simulateRateLimitExhausted: true,
25
26
  });
26
- if (res === null) return; // seam absent — soft-skip
27
+ if (res === null) return softSkip('blocked', 'seam absent — soft-skip');
27
28
  expect(
28
29
  (res.toolReturned ?? {}).status,
29
30
  driver.describe('RFC 0064 §D', 'an exhausted token bucket MUST yield status:"rate_limited" without invoking the tool'),
@@ -11,6 +11,7 @@
11
11
  */
12
12
 
13
13
  import { describe, it, expect } from 'vitest';
14
+ import { softSkip } from '../lib/soft-skip.js';
14
15
  import { driver } from '../lib/driver.js';
15
16
  import { readToolHooksCap, invokeToolHook } from '../lib/toolHooks.js';
16
17
 
@@ -19,13 +20,13 @@ const CANARY = 'sk-canary-rfc0064-do-not-leak-xyz789';
19
20
  describe('tool-hooks-secret-redaction (RFC 0064 §E)', () => {
20
21
  it('a secret-shaped tool arg never appears in the emitted events', async () => {
21
22
  const cap = await readToolHooksCap();
22
- if (cap?.prePostEvents !== true) return;
23
+ if (cap?.prePostEvents !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap?.prePostEvents !== true` returned early');
23
24
  const res = await invokeToolHook({
24
25
  principal: 'core.system',
25
26
  toolName: 'web.search',
26
27
  args: { apiKey: CANARY, q: 'openwop' },
27
28
  });
28
- if (res === null) return; // seam absent — soft-skip
29
+ if (res === null) return softSkip('blocked', 'seam absent — soft-skip');
29
30
  expect(
30
31
  JSON.stringify(res).includes(CANARY),
31
32
  driver.describe('RFC 0064 §B', 'a resolved secret MUST be redacted before hashing; the raw value MUST NOT appear in argsHash or any emitted field (SR-1)'),
@@ -11,13 +11,14 @@
11
11
  */
12
12
 
13
13
  import { describe, it, expect } from 'vitest';
14
+ import { softSkip } from '../lib/soft-skip.js';
14
15
  import { driver } from '../lib/driver.js';
15
16
  import { readToolHooksCap } from '../lib/toolHooks.js';
16
17
 
17
18
  describe('tool-hooks-shape: advertisement (RFC 0064 §A)', () => {
18
19
  it('capabilities.toolHooks is absent or a well-formed object', async () => {
19
20
  const cap = await readToolHooksCap();
20
- if (cap === null) return; // not advertised — valid
21
+ if (cap === null) return softSkip('inapplicable', 'not advertised — valid');
21
22
  expect(
22
23
  typeof cap.supported,
23
24
  driver.describe('capabilities.schema.json §toolHooks', 'toolHooks.supported MUST be a boolean when the block is present'),
@@ -28,6 +28,7 @@
28
28
  */
29
29
 
30
30
  import { describe, it, expect } from 'vitest';
31
+ import { seamAbsent } from '../lib/soft-skip.js';
31
32
  import { driver } from '../lib/driver.js';
32
33
  import { behaviorGate } from '../lib/behavior-gate.js';
33
34
  import {
@@ -52,11 +53,11 @@ function expectContentFree(payload: Record<string, unknown>, where: string): voi
52
53
  describe('trigger-bridge-delivery (RFC 0083 §C)', () => {
53
54
  it('de-dups by dedupKey, retries to dead-letter, and links delivery→run causation', async () => {
54
55
  if (!behaviorGate('openwop-trigger-bridge', await isTriggerBridgeProfileAdvertised())) return;
55
- if (!(await isEventLogSeamAvailable())) return; // event-log seam absent — soft-skip
56
+ if (!(await isEventLogSeamAvailable())) return seamAbsent('host advertises openwop-trigger-bridge but the event-log seam is absent');
56
57
 
57
58
  // ---- Leg 1: dedup → effectively-once (§C-1) ---------------------------
58
59
  const dedup = await driveDelivery({ scenario: 'dedup', dedupKey: 'conformance-dedup-key', source: 'queue' });
59
- if (dedup === null) return; // delivery seam unwired soft-skip the whole behavioral suite
60
+ if (dedup === null) return seamAbsent('host advertises openwop-trigger-bridge but the delivery seam is unwired');
60
61
 
61
62
  // The profile is derived AND the seam is wired — missing evidence is a
62
63
  // FAILURE, not a soft-skip. A repeated dedupKey MUST be effectively-once:
@@ -34,6 +34,7 @@
34
34
  */
35
35
 
36
36
  import { describe, it, expect } from 'vitest';
37
+ import { softSkip } from '../lib/soft-skip.js';
37
38
  import { driver, type OpenWOPResponse } from '../lib/driver.js';
38
39
  import { pollUntilTerminal } from '../lib/polling.js';
39
40
  import { isFixtureAdvertised } from '../lib/fixtures.js';
@@ -84,7 +85,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('version-fold: forced engine versions fold-best
84
85
  '[version-fold] host does not advertise Capabilities.testing.forceEngineVersionRange; ' +
85
86
  'skipping (the X-Force-Engine-Version seam is test-keys-only and optional)',
86
87
  );
87
- return;
88
+ return softSkip('blocked', 'precondition not met — `seam === null` returned early (seam, prior step, or fixture unavailable)');
88
89
  }
89
90
 
90
91
  const candidates = [seam.range.min, ...(seam.current === null ? [] : [seam.current]), seam.range.max];
@@ -104,7 +105,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('version-fold: forced engine versions fold-best
104
105
  if (create.status === 403 && errCode(create.json) === 'force_engine_version_forbidden') {
105
106
  // eslint-disable-next-line no-console
106
107
  console.warn('[version-fold] API key is production-scoped (403 force_engine_version_forbidden); skipping');
107
- return;
108
+ return softSkip('blocked', '[version-fold] API key is production-scoped (403 force_engine_version_forbidden); skipping (create.status === 403 && errCode(create.json) === \'force_engine_version_forbidden\')');
108
109
  }
109
110
  expect(
110
111
  create.status,
@@ -165,7 +166,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('version-fold: forced engine versions fold-best
165
166
  if (seam === null) {
166
167
  // eslint-disable-next-line no-console
167
168
  console.warn('[version-fold] forceEngineVersionRange not advertised; skipping negative leg');
168
- return;
169
+ return softSkip('inapplicable', '[version-fold] forceEngineVersionRange not advertised; skipping negative leg');
169
170
  }
170
171
 
171
172
  const outOfRange = seam.range.max + 1;
@@ -173,7 +174,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('version-fold: forced engine versions fold-best
173
174
  if (create.status === 403 && errCode(create.json) === 'force_engine_version_forbidden') {
174
175
  // eslint-disable-next-line no-console
175
176
  console.warn('[version-fold] API key is production-scoped; skipping negative leg');
176
- return;
177
+ return softSkip('blocked', '[version-fold] API key is production-scoped; skipping negative leg (create.status === 403 && errCode(create.json) === \'force_engine_version_forbidden\')');
177
178
  }
178
179
  expect(
179
180
  create.status,
@@ -18,6 +18,7 @@
18
18
  */
19
19
 
20
20
  import { describe, it, expect } from 'vitest';
21
+ import { readErrorCode } from '../lib/error-envelope.js';
21
22
  import { driver } from '../lib/driver.js';
22
23
  import { behaviorGate } from '../lib/behavior-gate.js';
23
24
  import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
@@ -29,7 +30,7 @@ function realtimeVoiceOf(ai: Record<string, unknown> | undefined): Record<string
29
30
  return rv && typeof rv === 'object' ? (rv as Record<string, unknown>) : undefined;
30
31
  }
31
32
  function errCode(json: unknown): string | undefined {
32
- return (json as { error?: { code?: string } })?.error?.code;
33
+ return readErrorCode(json);
33
34
  }
34
35
 
35
36
  describe('voice-streamref-tenant-bound (RFC 0106 §F INV-4)', () => {
@@ -19,6 +19,7 @@
19
19
  */
20
20
 
21
21
  import { describe, it, expect } from 'vitest';
22
+ import { readErrorCode } from '../lib/error-envelope.js';
22
23
  import { driver } from '../lib/driver.js';
23
24
  import { behaviorGate } from '../lib/behavior-gate.js';
24
25
  import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
@@ -30,7 +31,7 @@ function realtimeVoiceOf(ai: Record<string, unknown> | undefined): Record<string
30
31
  return rv && typeof rv === 'object' ? (rv as Record<string, unknown>) : undefined;
31
32
  }
32
33
  function errCode(json: unknown): string | undefined {
33
- return (json as { error?: { code?: string } })?.error?.code;
34
+ return readErrorCode(json);
34
35
  }
35
36
  function eventsOf(json: unknown): Array<{ type?: string; payload?: Record<string, unknown> }> {
36
37
  const e = (json as { events?: unknown })?.events;
@@ -14,6 +14,7 @@
14
14
  */
15
15
 
16
16
  import { describe, it, expect } from 'vitest';
17
+ import { readErrorCode } from '../lib/error-envelope.js';
17
18
  import { driver } from '../lib/driver.js';
18
19
  import { behaviorGate } from '../lib/behavior-gate.js';
19
20
  import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
@@ -25,7 +26,7 @@ function realtimeVoiceOf(ai: Record<string, unknown> | undefined): Record<string
25
26
  return rv && typeof rv === 'object' ? (rv as Record<string, unknown>) : undefined;
26
27
  }
27
28
  function errCode(json: unknown): string | undefined {
28
- return (json as { error?: { code?: string } })?.error?.code;
29
+ return readErrorCode(json);
29
30
  }
30
31
 
31
32
  describe('voice-transcription-unadvertised (RFC 0106 §B)', () => {
@@ -25,6 +25,7 @@
25
25
  */
26
26
 
27
27
  import { describe, it, expect } from 'vitest';
28
+ import { softSkip } from '../lib/soft-skip.js';
28
29
  import { driver } from '../lib/driver.js';
29
30
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
30
31
 
@@ -37,7 +38,7 @@ describe('wasm-pack-abi-version-rejection: host advertises supported ABI version
37
38
  const wasm =
38
39
  capabilityFamily<{ wasm?: Record<string, unknown> }>(disco.json, 'nodePackRuntimes')?.wasm;
39
40
 
40
- if (!wasm?.supported) return;
41
+ if (!wasm?.supported) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!wasm?.supported` returned early');
41
42
 
42
43
  expect(Array.isArray(wasm.abiVersions), driver.describe(
43
44
  'RFCS/0008-wasm-abi.md §H',
@@ -63,7 +64,7 @@ describe('wasm-pack-abi-version-rejection: positive path via misbehaving pack',
63
64
  const wasm =
64
65
  capabilityFamily<{ wasm?: Record<string, unknown> }>(disco.json, 'nodePackRuntimes')?.wasm;
65
66
 
66
- if (!wasm?.supported) return;
67
+ if (!wasm?.supported) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!wasm?.supported` returned early');
67
68
 
68
69
  if (!Array.isArray(wasm.loadedPacks)) {
69
70
  // eslint-disable-next-line no-console
@@ -71,7 +72,7 @@ describe('wasm-pack-abi-version-rejection: positive path via misbehaving pack',
71
72
  '[wasm-pack-abi-version-rejection] host does not advertise capabilities.nodePackRuntimes.wasm.loadedPacks[]; skipping positive path. ' +
72
73
  'Hosts MAY advertise loadedPacks[] to surface ABI rejection observably (RFC 0008 §H + Track 7).',
73
74
  );
74
- return;
75
+ return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!Array.isArray(wasm.loadedPacks)` returned early');
75
76
  }
76
77
 
77
78
  // Soft-skip when the well-behaved reference pack isn't loaded. If
@@ -84,7 +85,7 @@ describe('wasm-pack-abi-version-rejection: positive path via misbehaving pack',
84
85
  `[wasm-pack-abi-version-rejection] reference pack ${WELL_BEHAVED_PACK_NAME} not in loadedPacks[]; ` +
85
86
  'skipping positive path (build examples/packs/rust-hello first or run against a host that ships it).',
86
87
  );
87
- return;
88
+ return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!wasm.loadedPacks.includes(WELL_BEHAVED_PACK_NAME)` returned early');
88
89
  }
89
90
 
90
91
  // Core assertion: ABI 999 pack MUST NOT be in loadedPacks[]
@@ -13,6 +13,7 @@
13
13
  */
14
14
 
15
15
  import { describe, it, expect } from 'vitest';
16
+ import { softSkip } from '../lib/soft-skip.js';
16
17
  import { driver } from '../lib/driver.js';
17
18
  import { pollUntilTerminal } from '../lib/polling.js';
18
19
  import { isFixtureAdvertised } from '../lib/fixtures.js';
@@ -29,11 +30,11 @@ async function isWasmSupported(): Promise<boolean> {
29
30
 
30
31
  describe('wasm-pack-invoke-completed: round-trip output', () => {
31
32
  it('greet node returns Hello, <name>! and run reaches completed', async () => {
32
- if (!isFixtureAdvertised(FIXTURE)) return;
33
+ if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
33
34
  if (!(await isWasmSupported())) {
34
35
  // eslint-disable-next-line no-console
35
36
  console.warn('[wasm-pack-invoke-completed] WASM not advertised; skipping');
36
- return;
37
+ return softSkip('inapplicable', '[wasm-pack-invoke-completed] WASM not advertised; skipping');
37
38
  }
38
39
 
39
40
  const create = await driver.post('/v1/runs', {
@@ -17,6 +17,7 @@
17
17
  */
18
18
 
19
19
  import { describe, it, expect } from 'vitest';
20
+ import { softSkip } from '../lib/soft-skip.js';
20
21
  import { driver } from '../lib/driver.js';
21
22
  import { pollUntilTerminal } from '../lib/polling.js';
22
23
  import { isFixtureAdvertised } from '../lib/fixtures.js';
@@ -33,8 +34,8 @@ async function isWasmSupported(): Promise<boolean> {
33
34
 
34
35
  describe('wasm-pack-invoke-suspended: suspend → resume round-trip', () => {
35
36
  it('host either suspends the run or explicitly reports wasm_suspend_not_implemented', async () => {
36
- if (!isFixtureAdvertised(FIXTURE)) return;
37
- if (!(await isWasmSupported())) return;
37
+ if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
38
+ if (!(await isWasmSupported())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isWasmSupported())` returned early');
38
39
 
39
40
  // The reference rust-hello pack does NOT itself suspend (it always
40
41
  // returns `completed`), so against that pack this scenario can only
@@ -13,6 +13,7 @@
13
13
  */
14
14
 
15
15
  import { describe, it, expect } from 'vitest';
16
+ import { softSkip } from '../lib/soft-skip.js';
16
17
  import { driver } from '../lib/driver.js';
17
18
  import { isFixtureAdvertised } from '../lib/fixtures.js';
18
19
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
@@ -39,7 +40,7 @@ describe('wasm-pack-load: discovery surfaces WASM runtime support', () => {
39
40
  if (!wasm?.supported) {
40
41
  // eslint-disable-next-line no-console
41
42
  console.warn('[wasm-pack-load] host does not advertise WASM support; skipping');
42
- return;
43
+ return softSkip('inapplicable', '[wasm-pack-load] host does not advertise WASM support; skipping');
43
44
  }
44
45
  expect(Array.isArray(wasm.abiVersions), driver.describe(
45
46
  'RFCS/0008-wasm-abi.md §H',
@@ -55,11 +56,11 @@ describe('wasm-pack-load: discovery surfaces WASM runtime support', () => {
55
56
  describe('wasm-pack-load: loaded pack typeIds are dispatchable', () => {
56
57
  it('host accepts a workflow whose node typeId is provided by a loaded WASM pack', async () => {
57
58
  const wasm = await getWasmCaps();
58
- if (!wasm?.supported) return;
59
+ if (!wasm?.supported) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!wasm?.supported` returned early');
59
60
  if (!isFixtureAdvertised(FIXTURE)) {
60
61
  // eslint-disable-next-line no-console
61
62
  console.warn(`[wasm-pack-load] fixture ${FIXTURE} not advertised; skipping`);
62
- return;
63
+ return softSkip('inapplicable', '[wasm-pack-load] fixture … not advertised; skipping');
63
64
  }
64
65
  // Creating a run against the fixture proves the host knows about the
65
66
  // WASM-provided typeId. A host that loaded the pack accepts the
@@ -23,6 +23,7 @@
23
23
  */
24
24
 
25
25
  import { describe, it, expect } from 'vitest';
26
+ import { softSkip } from '../lib/soft-skip.js';
26
27
  import { driver } from '../lib/driver.js';
27
28
  import { pollUntilTerminal } from '../lib/polling.js';
28
29
  import { isFixtureAdvertised } from '../lib/fixtures.js';
@@ -36,7 +37,7 @@ describe('wasm-pack-memory-cap: host advertises maxMemoryBytes', () => {
36
37
  const wasm =
37
38
  capabilityFamily<{ wasm?: Record<string, unknown> }>(disco.json, 'nodePackRuntimes')?.wasm;
38
39
 
39
- if (!wasm?.supported) return;
40
+ if (!wasm?.supported) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!wasm?.supported` returned early');
40
41
 
41
42
  // The cap is REQUIRED to enforce §K. Hosts MUST surface the configured
42
43
  // ceiling so clients can size their packs accordingly.
@@ -59,12 +60,12 @@ describe('wasm-pack-memory-cap: positive path via misbehaving pack', () => {
59
60
  `[wasm-pack-memory-cap] fixture ${CAP_BREACH_FIXTURE} not advertised; skipping positive path. ` +
60
61
  'Build the misbehaving pack at examples/packs/rust-misbehaving-memory/ and ensure the host serves the fixture.',
61
62
  );
62
- return;
63
+ return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(CAP_BREACH_FIXTURE)` returned early');
63
64
  }
64
65
  const disco = await driver.get('/.well-known/openwop');
65
66
  const wasm =
66
67
  capabilityFamily<{ wasm?: Record<string, unknown> }>(disco.json, 'nodePackRuntimes')?.wasm;
67
- if (!wasm?.supported) return;
68
+ if (!wasm?.supported) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!wasm?.supported` returned early');
68
69
 
69
70
  const create = await driver.post('/v1/runs', {
70
71
  workflowId: CAP_BREACH_FIXTURE,
@@ -11,6 +11,7 @@
11
11
  */
12
12
 
13
13
  import { describe, it, expect } from 'vitest';
14
+ import { softSkip } from '../lib/soft-skip.js';
14
15
  import { driver } from '../lib/driver.js';
15
16
  import { pollUntilTerminal } from '../lib/polling.js';
16
17
  import { isFixtureAdvertised } from '../lib/fixtures.js';
@@ -33,8 +34,8 @@ function extractGreeting(events: Array<{ type: string; data?: unknown }>): strin
33
34
 
34
35
  describe('wasm-pack-replay-determinism: same inputs → same output', () => {
35
36
  it('two independent runs with same inputs produce same WASM output', async () => {
36
- if (!isFixtureAdvertised(FIXTURE)) return;
37
- if (!(await isWasmSupported())) return;
37
+ if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
38
+ if (!(await isWasmSupported())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isWasmSupported())` returned early');
38
39
 
39
40
  const inputs = { name: 'determinism-probe' };
40
41
 
@@ -24,6 +24,7 @@
24
24
  */
25
25
 
26
26
  import { afterEach, describe, expect, it } from 'vitest';
27
+ import { softSkip } from '../lib/soft-skip.js';
27
28
  import { createHmac } from 'node:crypto';
28
29
  import { createServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http';
29
30
  import { driver } from '../lib/driver.js';
@@ -78,12 +79,12 @@ describe('webhook-signed-delivery: end-to-end HMAC v1', () => {
78
79
  if (!(await isWebhookSupported())) {
79
80
  // eslint-disable-next-line no-console
80
81
  console.warn('[webhook-signed-delivery] host does not advertise webhook support; skipping');
81
- return;
82
+ return softSkip('inapplicable', '[webhook-signed-delivery] host does not advertise webhook support; skipping');
82
83
  }
83
84
  if (!isFixtureAdvertised('conformance-noop')) {
84
85
  // eslint-disable-next-line no-console
85
86
  console.warn('[webhook-signed-delivery] conformance-noop not advertised; skipping');
86
- return;
87
+ return softSkip('inapplicable', '[webhook-signed-delivery] conformance-noop not advertised; skipping');
87
88
  }
88
89
 
89
90
  const receiver = await startReceiver();
@@ -114,7 +115,7 @@ describe('webhook-signed-delivery: end-to-end HMAC v1', () => {
114
115
  '[webhook-signed-delivery] host SSRF guard rejected the loopback receiver; ' +
115
116
  'set OPENWOP_WEBHOOK_ALLOW_PRIVATE=true on the host (or equivalent) to run',
116
117
  );
117
- return;
118
+ return softSkip('blocked', 'precondition not met — `body.error === \'webhook_url_rejected\'` returned early (seam, prior step, or fixture unavailable)');
118
119
  }
119
120
  }
120
121