@openwop/openwop-conformance 1.162.0 → 2.0.0-rc.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 (553) hide show
  1. package/README.md +11 -2
  2. package/dist/cli.js +54 -1
  3. package/dist/lib/paths.js +25 -3
  4. package/dist/lib/requirement-ids.js +34 -4
  5. package/dist/lib/scenario-disposition.js +1 -0
  6. package/dist/lib/spec-coherence.js +13 -32
  7. package/dist/spec-artifacts.lock.json +5 -0
  8. package/package.json +11 -33
  9. package/requirements.json +20303 -11809
  10. package/scenario-majors.json +1343 -0
  11. package/schemas/CORPUS-STAMP.json +138 -6
  12. package/src/cli.ts +47 -1
  13. package/src/global-setup.ts +3 -2
  14. package/src/lib/corpus-stamp.ts +29 -1
  15. package/src/lib/paths.ts +25 -3
  16. package/src/lib/requirement-ids.ts +33 -4
  17. package/src/lib/scenario-disposition.ts +1 -0
  18. package/src/lib/spec-coherence.ts +15 -32
  19. package/src/scenarios/a2a-1-0-agent-card.test.ts +17 -16
  20. package/src/scenarios/a2a-1-0-task-roundtrip.test.ts +11 -10
  21. package/src/scenarios/a2a-card-runtime-consistency.test.ts +18 -17
  22. package/src/scenarios/a2a-peer-authority.test.ts +7 -6
  23. package/src/scenarios/a2a-task-roundtrip.test.ts +30 -29
  24. package/src/scenarios/a2a-version-negotiation.test.ts +16 -14
  25. package/src/scenarios/a2ui-surface-degrades.test.ts +5 -3
  26. package/src/scenarios/a2ui-surface-delta-transport.test.ts +35 -33
  27. package/src/scenarios/a2ui-surface-replay.test.ts +6 -4
  28. package/src/scenarios/a2ui-surface-shape.test.ts +11 -10
  29. package/src/scenarios/a2ui-surface-version-refusal.test.ts +6 -4
  30. package/src/scenarios/a2ui-untrusted-blocks-approval.test.ts +5 -3
  31. package/src/scenarios/agent-capability-degraded-projection.test.ts +12 -11
  32. package/src/scenarios/agent-channel-dispatch.test.ts +18 -16
  33. package/src/scenarios/agent-deployment-lifecycle.test.ts +20 -19
  34. package/src/scenarios/agent-deployment-shape.test.ts +22 -24
  35. package/src/scenarios/agent-eval-run.test.ts +19 -18
  36. package/src/scenarios/agent-eval-suite-shape.test.ts +20 -22
  37. package/src/scenarios/agent-live-allowlist-enforced.test.ts +6 -5
  38. package/src/scenarios/agent-live-invocation-bracket.test.ts +13 -12
  39. package/src/scenarios/agent-live-runtime-shape.test.ts +10 -12
  40. package/src/scenarios/agent-live-structured-output.test.ts +7 -6
  41. package/src/scenarios/agent-loop-iteration-monotonic.test.ts +3 -3
  42. package/src/scenarios/agent-loop-stateful-resume.test.ts +2 -2
  43. package/src/scenarios/agent-loop-version5-shape.test.ts +4 -4
  44. package/src/scenarios/agent-loop-workspace-snapshot.test.ts +3 -3
  45. package/src/scenarios/agent-manifest-role-profile.test.ts +9 -9
  46. package/src/scenarios/agent-manifest-runtime.test.ts +13 -12
  47. package/src/scenarios/agent-org-chart-scoping.test.ts +14 -13
  48. package/src/scenarios/agent-org-chart-shape.test.ts +11 -13
  49. package/src/scenarios/agent-platform-aggregate-evidence.test.ts +5 -4
  50. package/src/scenarios/agent-platform-profile.test.ts +18 -19
  51. package/src/scenarios/agent-requires-capabilities-shape.test.ts +4 -4
  52. package/src/scenarios/agent-roster-attribution.test.ts +18 -17
  53. package/src/scenarios/agent-roster-shape.test.ts +17 -19
  54. package/src/scenarios/agent-verifier-shape.test.ts +11 -11
  55. package/src/scenarios/agentConfidenceEscalation.test.ts +2 -1
  56. package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +6 -5
  57. package/src/scenarios/agentMemoryRedactionContract.test.ts +2 -1
  58. package/src/scenarios/agentMemoryRoundTrip.test.ts +2 -1
  59. package/src/scenarios/agentMemoryTtlExpiry.test.ts +4 -3
  60. package/src/scenarios/agentMessageReducer.test.ts +2 -1
  61. package/src/scenarios/agentMetadata.test.ts +2 -1
  62. package/src/scenarios/agentPackCatalog.test.ts +23 -21
  63. package/src/scenarios/agentPackExport.test.ts +4 -2
  64. package/src/scenarios/agentPackHandoffSchemaValidation.test.ts +5 -4
  65. package/src/scenarios/agentPackInstall.test.ts +4 -2
  66. package/src/scenarios/agentPackProvenance.test.ts +2 -1
  67. package/src/scenarios/agentReasoningEvents.test.ts +3 -2
  68. package/src/scenarios/agentReasoningStreaming.test.ts +11 -10
  69. package/src/scenarios/agents-run-tool-allowlist.test.ts +14 -12
  70. package/src/scenarios/ai-envelope-shape.test.ts +31 -29
  71. package/src/scenarios/aiEnvelope.capBreached.test.ts +26 -24
  72. package/src/scenarios/aiEnvelope.contractRefusal.test.ts +36 -34
  73. package/src/scenarios/aiEnvelope.correlationReplay.test.ts +17 -15
  74. package/src/scenarios/aiEnvelope.redaction.test.ts +22 -20
  75. package/src/scenarios/aiEnvelope.schemaDrift.test.ts +18 -16
  76. package/src/scenarios/aiEnvelope.trustBoundaryPropagation.test.ts +38 -36
  77. package/src/scenarios/aiEnvelope.universalKinds.test.ts +31 -29
  78. package/src/scenarios/aiproviders-input-shape.test.ts +5 -5
  79. package/src/scenarios/aiproviders-realtimevoice-shape.test.ts +14 -16
  80. package/src/scenarios/aiproviders-selfhosted-honesty.test.ts +6 -4
  81. package/src/scenarios/aiproviders-selfhosted-shape.test.ts +15 -17
  82. package/src/scenarios/aiproviders-speechsynth-shape.test.ts +6 -8
  83. package/src/scenarios/aiproviders-subscription-scope.test.ts +7 -8
  84. package/src/scenarios/anonymous-actor-audit-opaque.test.ts +9 -8
  85. package/src/scenarios/anonymous-actor-default-deny.test.ts +10 -9
  86. package/src/scenarios/anonymous-actor-egress-guarded.test.ts +6 -5
  87. package/src/scenarios/anonymous-actor-no-secret-reach.test.ts +7 -6
  88. package/src/scenarios/anonymous-actor-shape.test.ts +17 -19
  89. package/src/scenarios/anonymous-actor-write-gated.test.ts +9 -8
  90. package/src/scenarios/append-ordering.test.ts +7 -5
  91. package/src/scenarios/approval-gate-events.test.ts +4 -3
  92. package/src/scenarios/approval-gate-flow.test.ts +4 -3
  93. package/src/scenarios/approval-payload.test.ts +5 -4
  94. package/src/scenarios/artifact-auth.test.ts +7 -6
  95. package/src/scenarios/artifact-type-pack-install.test.ts +6 -6
  96. package/src/scenarios/artifact-type-pack-manifest-validation.test.ts +9 -8
  97. package/src/scenarios/artifact-type-registration-source.test.ts +20 -20
  98. package/src/scenarios/artifact-type-store-emission.test.ts +15 -18
  99. package/src/scenarios/artifact-type-store-without-render.test.ts +4 -4
  100. package/src/scenarios/audit-log-integrity.test.ts +8 -6
  101. package/src/scenarios/auth-api-key-rotation.test.ts +11 -9
  102. package/src/scenarios/auth-mtls.test.ts +22 -20
  103. package/src/scenarios/auth-oauth2-client-credentials.test.ts +16 -14
  104. package/src/scenarios/auth-oidc-user-bearer.test.ts +26 -24
  105. package/src/scenarios/auth-saml-profile.test.ts +14 -12
  106. package/src/scenarios/auth-scim-profile.test.ts +3 -2
  107. package/src/scenarios/auth-subject-link-key-class.test.ts +8 -7
  108. package/src/scenarios/auth-subject-link.test.ts +11 -10
  109. package/src/scenarios/auth.test.ts +5 -4
  110. package/src/scenarios/authorization-fail-closed.test.ts +3 -2
  111. package/src/scenarios/authorization-roles-shape.test.ts +9 -7
  112. package/src/scenarios/blob-cross-tenant-isolation.test.ts +8 -6
  113. package/src/scenarios/blob-presign-expiry.test.ts +12 -10
  114. package/src/scenarios/blob-roundtrip.test.ts +0 -0
  115. package/src/scenarios/budget-enforcement.test.ts +15 -14
  116. package/src/scenarios/budget-policy-shape.test.ts +18 -19
  117. package/src/scenarios/bulk-cancel.test.ts +9 -8
  118. package/src/scenarios/byok-auth-modes.test.ts +13 -14
  119. package/src/scenarios/byok-roundtrip.test.ts +12 -10
  120. package/src/scenarios/cache-cross-tenant-isolation.test.ts +8 -6
  121. package/src/scenarios/cache-ttl-expiry.test.ts +7 -5
  122. package/src/scenarios/callai-multimodal.test.ts +5 -3
  123. package/src/scenarios/cancellation.test.ts +6 -5
  124. package/src/scenarios/cap-breach.test.ts +15 -14
  125. package/src/scenarios/capability-arm-witness.test.ts +5 -3
  126. package/src/scenarios/certification-bundle-non-vacuous.test.ts +20 -19
  127. package/src/scenarios/certification-bundle-redaction.test.ts +11 -10
  128. package/src/scenarios/certification-bundle-v2.test.ts +11 -10
  129. package/src/scenarios/chain-compensation-expansion.test.ts +14 -13
  130. package/src/scenarios/chain-produced-var-roundtrip.test.ts +13 -14
  131. package/src/scenarios/chain-subchain-cycle-rejected.test.ts +9 -10
  132. package/src/scenarios/chain-subchain-fanout.test.ts +8 -9
  133. package/src/scenarios/chain-subchain-sibling.test.ts +14 -16
  134. package/src/scenarios/channel-presence-behavioral.test.ts +9 -7
  135. package/src/scenarios/channel-presence-shape.test.ts +11 -12
  136. package/src/scenarios/channel-ttl.test.ts +6 -5
  137. package/src/scenarios/chat-card-pack-execution.test.ts +4 -4
  138. package/src/scenarios/chat-card-pack-manifest-validation.test.ts +9 -8
  139. package/src/scenarios/commitment-fired.test.ts +6 -5
  140. package/src/scenarios/compensation-behavior.test.ts +24 -23
  141. package/src/scenarios/compensation-recovery.test.ts +28 -27
  142. package/src/scenarios/configurable-schema.test.ts +8 -6
  143. package/src/scenarios/conformance-advertised-seam-required.test.ts +6 -5
  144. package/src/scenarios/conformance-execution-witness.test.ts +9 -8
  145. package/src/scenarios/connection-pack-apihosts.test.ts +19 -17
  146. package/src/scenarios/connection-pack-manifest-valid.test.ts +12 -11
  147. package/src/scenarios/connection-pack-no-credential-material.test.ts +8 -6
  148. package/src/scenarios/connection-pack-reach-exclusive.test.ts +5 -4
  149. package/src/scenarios/connection-pack-write-reconsent.test.ts +6 -4
  150. package/src/scenarios/connection-provider-resolution.test.ts +18 -16
  151. package/src/scenarios/connector-manifest-validity.test.ts +8 -7
  152. package/src/scenarios/context-budget-transcript-bound.test.ts +17 -15
  153. package/src/scenarios/context-summarization-replay.test.ts +14 -12
  154. package/src/scenarios/contract-provenance.test.ts +18 -18
  155. package/src/scenarios/conversation-turn-model-provenance-shape.test.ts +14 -15
  156. package/src/scenarios/conversationCapabilityNegotiation.test.ts +2 -1
  157. package/src/scenarios/conversationLifecycle.test.ts +2 -1
  158. package/src/scenarios/conversationReplayDeterminism.test.ts +4 -2
  159. package/src/scenarios/conversationVsLegacySuspend.test.ts +2 -1
  160. package/src/scenarios/core-standard-profile.test.ts +8 -9
  161. package/src/scenarios/cost-attribution.test.ts +23 -21
  162. package/src/scenarios/credential-payload-redaction.test.ts +4 -3
  163. package/src/scenarios/credentials-capability-shape.test.ts +5 -4
  164. package/src/scenarios/cross-engine-append-behavior.test.ts +11 -10
  165. package/src/scenarios/cross-engine-append-ordering.test.ts +4 -3
  166. package/src/scenarios/cross-host-ancestry-endpoint.test.ts +11 -10
  167. package/src/scenarios/cross-host-causation-shape.test.ts +7 -6
  168. package/src/scenarios/cross-workspace-isolation.test.ts +4 -3
  169. package/src/scenarios/data-residency-admission.test.ts +15 -14
  170. package/src/scenarios/deadletter-capability-shape.test.ts +6 -4
  171. package/src/scenarios/deadletter-retry-exhaustion.test.ts +4 -3
  172. package/src/scenarios/debug-bundle-truncation.test.ts +9 -7
  173. package/src/scenarios/debugBundle.test.ts +25 -23
  174. package/src/scenarios/discovery-families-view.test.ts +5 -4
  175. package/src/scenarios/discovery.test.ts +30 -29
  176. package/src/scenarios/dispatch-cross-worker-handoff.test.ts +10 -8
  177. package/src/scenarios/dispatch-fanout-parallel.test.ts +24 -22
  178. package/src/scenarios/dispatch-input-mapping.test.ts +12 -10
  179. package/src/scenarios/dispatch-output-mapping.test.ts +9 -7
  180. package/src/scenarios/dispatch-per-item-input.test.ts +22 -20
  181. package/src/scenarios/dispatchLoop.test.ts +3 -2
  182. package/src/scenarios/distillation-index-roundtrip.test.ts +3 -3
  183. package/src/scenarios/distillation-secret-carryforward.test.ts +2 -2
  184. package/src/scenarios/distillation-shape.test.ts +4 -4
  185. package/src/scenarios/distillation-stable-archive.test.ts +6 -6
  186. package/src/scenarios/distillation-token-budget.test.ts +5 -5
  187. package/src/scenarios/durability-poison-exhaustion.test.ts +7 -6
  188. package/src/scenarios/egress-audience-binding.test.ts +11 -10
  189. package/src/scenarios/egress-decision-content-free.test.ts +6 -5
  190. package/src/scenarios/egress-provenance-shape.test.ts +17 -18
  191. package/src/scenarios/envelope-completion-distinguishes-truncation.test.ts +27 -25
  192. package/src/scenarios/envelope-nl-to-format-engaged.test.ts +19 -17
  193. package/src/scenarios/envelope-reasoning-secret-redaction.test.ts +28 -26
  194. package/src/scenarios/envelope-reasoning-shape.test.ts +19 -17
  195. package/src/scenarios/envelope-recovery-applied.test.ts +24 -22
  196. package/src/scenarios/envelope-refusal-shape.test.ts +31 -29
  197. package/src/scenarios/envelope-rendering-hint.test.ts +5 -4
  198. package/src/scenarios/envelope-retry-attempted.test.ts +33 -31
  199. package/src/scenarios/envelope-retry-exhausted.test.ts +22 -20
  200. package/src/scenarios/envelope-tier-one-subset-static.test.ts +9 -7
  201. package/src/scenarios/envelope-truncated.test.ts +19 -17
  202. package/src/scenarios/envelope-truncation-cap-exhaustion.test.ts +19 -17
  203. package/src/scenarios/envelope-variant-discriminator-static.test.ts +4 -3
  204. package/src/scenarios/errors.test.ts +20 -19
  205. package/src/scenarios/eventOrdering.test.ts +16 -14
  206. package/src/scenarios/exec-not-protocol-tier.test.ts +5 -7
  207. package/src/scenarios/experimental-tier-shape.test.ts +17 -15
  208. package/src/scenarios/export-bundle-portability.test.ts +17 -17
  209. package/src/scenarios/failure-path.test.ts +7 -6
  210. package/src/scenarios/feedback-capability-shape.test.ts +4 -3
  211. package/src/scenarios/feedback-correction-redaction.test.ts +2 -1
  212. package/src/scenarios/feedback-cross-tenant-isolation.test.ts +2 -1
  213. package/src/scenarios/feedback-fork-not-copied.test.ts +3 -2
  214. package/src/scenarios/feedback-on-terminal-run.test.ts +3 -2
  215. package/src/scenarios/feedback-record-and-list.test.ts +2 -1
  216. package/src/scenarios/feedback-unsupported-501.test.ts +2 -1
  217. package/src/scenarios/fixtures-gating.test.ts +29 -28
  218. package/src/scenarios/fixtures-valid.test.ts +16 -15
  219. package/src/scenarios/form-content-instantiation.test.ts +15 -14
  220. package/src/scenarios/frontend-plugin-packs.test.ts +29 -27
  221. package/src/scenarios/fs-path-traversal.test.ts +14 -12
  222. package/src/scenarios/goal-standing-continuation.test.ts +21 -21
  223. package/src/scenarios/grpc-transport.test.ts +9 -7
  224. package/src/scenarios/heartbeat-capability-shape.test.ts +3 -3
  225. package/src/scenarios/heartbeat-fires-once-per-tick.test.ts +2 -2
  226. package/src/scenarios/heartbeat-idempotent-no-spam.test.ts +3 -3
  227. package/src/scenarios/heartbeat-runtime-bound.test.ts +2 -2
  228. package/src/scenarios/highConcurrency.test.ts +14 -13
  229. package/src/scenarios/host-callback-declaration.test.ts +8 -7
  230. package/src/scenarios/http-client-ssrf.test.ts +7 -5
  231. package/src/scenarios/i18n-negotiation.test.ts +14 -12
  232. package/src/scenarios/idempotency-concurrent-claim.test.ts +14 -13
  233. package/src/scenarios/idempotency-key-determinism.test.ts +20 -18
  234. package/src/scenarios/idempotency.test.ts +8 -7
  235. package/src/scenarios/idempotencyRetry.test.ts +13 -11
  236. package/src/scenarios/identity-owner-shape.test.ts +6 -5
  237. package/src/scenarios/identity-passthrough.test.ts +4 -3
  238. package/src/scenarios/interrupt-approval.test.ts +6 -5
  239. package/src/scenarios/interrupt-approver-routing.test.ts +15 -15
  240. package/src/scenarios/interrupt-auth-required-resume.test.ts +5 -3
  241. package/src/scenarios/interrupt-clarification.test.ts +4 -3
  242. package/src/scenarios/interrupt-external-event-correlation.test.ts +6 -5
  243. package/src/scenarios/interrupt-parent-child-cascade.test.ts +4 -3
  244. package/src/scenarios/interrupt-quorum-resolution.test.ts +5 -4
  245. package/src/scenarios/interrupt-token-matrix.test.ts +8 -6
  246. package/src/scenarios/interruptRace.test.ts +12 -10
  247. package/src/scenarios/kv-atomic-increment.test.ts +11 -9
  248. package/src/scenarios/kv-cas.test.ts +14 -12
  249. package/src/scenarios/kv-cross-tenant-isolation.test.ts +12 -10
  250. package/src/scenarios/kv-ttl-expiry.test.ts +7 -5
  251. package/src/scenarios/localized-content-delivery.test.ts +24 -26
  252. package/src/scenarios/maliciousManifest.test.ts +10 -8
  253. package/src/scenarios/mcp-2026-07-28-discover.test.ts +15 -14
  254. package/src/scenarios/mcp-cache-tenant-scope.test.ts +6 -5
  255. package/src/scenarios/mcp-current-auth-boundary.test.ts +7 -6
  256. package/src/scenarios/mcp-discoverability.test.ts +9 -7
  257. package/src/scenarios/mcp-extension-opacity.test.ts +7 -6
  258. package/src/scenarios/mcp-mrtr-roundtrip.test.ts +18 -17
  259. package/src/scenarios/mcp-server-elicitation-bridge.test.ts +13 -12
  260. package/src/scenarios/mcp-server-prompt-roundtrip.test.ts +11 -10
  261. package/src/scenarios/mcp-server-resource-roundtrip.test.ts +11 -10
  262. package/src/scenarios/mcp-server-sampling-bridge.test.ts +12 -11
  263. package/src/scenarios/mcp-server-tool-roundtrip.test.ts +14 -13
  264. package/src/scenarios/mcp-server-untrusted-args.test.ts +14 -13
  265. package/src/scenarios/mcp-stateless-request.test.ts +11 -10
  266. package/src/scenarios/mcp-tool-roundtrip.test.ts +4 -3
  267. package/src/scenarios/mcp-toolcall-redaction.test.ts +4 -3
  268. package/src/scenarios/mcp-version-negotiation.test.ts +15 -14
  269. package/src/scenarios/media-url-inline-cap.test.ts +21 -19
  270. package/src/scenarios/memory-attribution-emits-on-write.test.ts +5 -5
  271. package/src/scenarios/memory-attribution-no-content.test.ts +4 -4
  272. package/src/scenarios/memory-attribution-replay-stable.test.ts +4 -3
  273. package/src/scenarios/memory-attribution-shape.test.ts +4 -3
  274. package/src/scenarios/memory-attribution-tenant-scoped.test.ts +3 -3
  275. package/src/scenarios/memory-capability-model-shape.test.ts +14 -15
  276. package/src/scenarios/memory-compaction-event-emitted.test.ts +7 -6
  277. package/src/scenarios/memory-compaction-provenance-tag.test.ts +3 -2
  278. package/src/scenarios/memory-compaction-sr1-carry-forward.test.ts +7 -6
  279. package/src/scenarios/memory-consolidation-idempotent.test.ts +5 -4
  280. package/src/scenarios/memory-consolidation-shape.test.ts +9 -11
  281. package/src/scenarios/memory-degraded-projection.test.ts +10 -9
  282. package/src/scenarios/memory-injection-budget.test.ts +15 -14
  283. package/src/scenarios/metric-emission.test.ts +4 -3
  284. package/src/scenarios/model-capability-insufficient.test.ts +19 -17
  285. package/src/scenarios/model-capability-substituted.test.ts +20 -18
  286. package/src/scenarios/multi-agent-confidence-escalation.test.ts +12 -11
  287. package/src/scenarios/multi-agent-handoff-state-machine.test.ts +10 -9
  288. package/src/scenarios/multi-agent-memory-lifecycle.test.ts +11 -10
  289. package/src/scenarios/multi-node-ordering.test.ts +4 -3
  290. package/src/scenarios/multi-party-conversation-behavioral.test.ts +6 -6
  291. package/src/scenarios/multi-party-conversation-shape.test.ts +21 -23
  292. package/src/scenarios/multi-region-idempotency-behavior.test.ts +16 -15
  293. package/src/scenarios/multi-region-idempotency.test.ts +6 -5
  294. package/src/scenarios/node-failed-payload-shape.test.ts +5 -4
  295. package/src/scenarios/node-module-required-capabilities-shape.test.ts +10 -8
  296. package/src/scenarios/oauth-authorization-code-roundtrip.test.ts +5 -4
  297. package/src/scenarios/oauth-capability-shape.test.ts +5 -4
  298. package/src/scenarios/oauth-connector-redaction.test.ts +4 -3
  299. package/src/scenarios/openapi-resolved-paths.test.ts +6 -5
  300. package/src/scenarios/orchestratorConservativePath.test.ts +2 -1
  301. package/src/scenarios/orchestratorDispatch.test.ts +2 -1
  302. package/src/scenarios/orchestratorTermination.test.ts +2 -1
  303. package/src/scenarios/org-position-no-authority-escalation.test.ts +2 -2
  304. package/src/scenarios/otel-collector-canary-inspection.test.ts +8 -7
  305. package/src/scenarios/otel-emission-grpc.test.ts +3 -2
  306. package/src/scenarios/otel-emission.test.ts +6 -5
  307. package/src/scenarios/otel-trace-propagation-subworkflow.test.ts +7 -6
  308. package/src/scenarios/otel-trace-propagation.test.ts +2 -1
  309. package/src/scenarios/owner-subject-echo.test.ts +13 -12
  310. package/src/scenarios/owner-subject-shape.test.ts +11 -10
  311. package/src/scenarios/pack-manifest-extension-opacity.test.ts +8 -8
  312. package/src/scenarios/pack-registry-isolation.test.ts +4 -3
  313. package/src/scenarios/pack-registry-publish.test.ts +65 -63
  314. package/src/scenarios/pack-registry.test.ts +38 -36
  315. package/src/scenarios/pause-resume.test.ts +18 -16
  316. package/src/scenarios/policies.test.ts +20 -18
  317. package/src/scenarios/production-backpressure.test.ts +18 -16
  318. package/src/scenarios/production-retention-expiry.test.ts +8 -6
  319. package/src/scenarios/profile-discovery-core-alias.test.ts +13 -11
  320. package/src/scenarios/profileDerivation.test.ts +34 -33
  321. package/src/scenarios/prompt-all-four-kinds-events.test.ts +11 -9
  322. package/src/scenarios/prompt-composed-secret-redaction.test.ts +11 -9
  323. package/src/scenarios/prompt-composed-trust-marker.test.ts +10 -8
  324. package/src/scenarios/prompt-end-to-end-events.test.ts +17 -15
  325. package/src/scenarios/prompt-list-and-fetch.test.ts +16 -14
  326. package/src/scenarios/prompt-mutable-lifecycle.test.ts +13 -11
  327. package/src/scenarios/prompt-mutation-workspace-membership-enforced.test.ts +6 -4
  328. package/src/scenarios/prompt-pack-install.test.ts +14 -12
  329. package/src/scenarios/prompt-prefix-cache.test.ts +19 -17
  330. package/src/scenarios/prompt-read-workspace-membership-enforced.test.ts +11 -9
  331. package/src/scenarios/prompt-render-deterministic.test.ts +19 -17
  332. package/src/scenarios/prompt-resolution-chain-agent-intrinsic.test.ts +7 -5
  333. package/src/scenarios/prompt-resolution-chain-event.test.ts +12 -10
  334. package/src/scenarios/prompt-resolution-chain-fallback-cascade.test.ts +10 -8
  335. package/src/scenarios/prompt-resolution-chain-node-wins.test.ts +8 -6
  336. package/src/scenarios/prompt-template-shape.test.ts +30 -28
  337. package/src/scenarios/proposal-reviewable-learning.test.ts +19 -19
  338. package/src/scenarios/protocol-versions-array.test.ts +10 -9
  339. package/src/scenarios/provider-usage.test.ts +17 -15
  340. package/src/scenarios/providerPolicyEnforcement.test.ts +16 -14
  341. package/src/scenarios/purpose-propagation.test.ts +23 -21
  342. package/src/scenarios/queue-ack-nack-dlq.test.ts +12 -10
  343. package/src/scenarios/queue-cross-tenant-isolation.test.ts +8 -6
  344. package/src/scenarios/queue-publish-consume-roundtrip.test.ts +10 -8
  345. package/src/scenarios/rate-limit-envelope.test.ts +6 -5
  346. package/src/scenarios/redaction.test.ts +13 -11
  347. package/src/scenarios/redactionAdversarial.test.ts +11 -9
  348. package/src/scenarios/registry-public.test.ts +12 -11
  349. package/src/scenarios/replay-divergence-at-refusal.test.ts +16 -15
  350. package/src/scenarios/replay-fanout-suppression.test.ts +16 -15
  351. package/src/scenarios/replay-fork-arbitrary.test.ts +24 -23
  352. package/src/scenarios/replay-fork.test.ts +20 -19
  353. package/src/scenarios/replay-llm-cache-key-portable.test.ts +11 -9
  354. package/src/scenarios/replay-llm-cache-key.test.ts +22 -21
  355. package/src/scenarios/replay-observable-sequence-determinism.test.ts +4 -3
  356. package/src/scenarios/replay-retention-expiry.test.ts +11 -9
  357. package/src/scenarios/replay-side-effect-suppression.test.ts +21 -19
  358. package/src/scenarios/replayDeterminism.test.ts +13 -12
  359. package/src/scenarios/requirement-ledger.test.ts +15 -14
  360. package/src/scenarios/restart-during-run.test.ts +3 -2
  361. package/src/scenarios/route-coverage.test.ts +20 -19
  362. package/src/scenarios/run-diff.test.ts +22 -20
  363. package/src/scenarios/run-execution-bounds-shape.test.ts +14 -12
  364. package/src/scenarios/run-transport-economy.test.ts +13 -11
  365. package/src/scenarios/runner-ledger.test.ts +17 -16
  366. package/src/scenarios/runs-lifecycle.test.ts +7 -6
  367. package/src/scenarios/runtime-capabilities.test.ts +12 -10
  368. package/src/scenarios/runtime-requires-install-gate.test.ts +8 -8
  369. package/src/scenarios/runtime-requires-shape.test.ts +7 -6
  370. package/src/scenarios/safefetch-behavior.test.ts +22 -21
  371. package/src/scenarios/safefetch-live-audit.test.ts +14 -13
  372. package/src/scenarios/sandbox-memory-cap.test.ts +2 -1
  373. package/src/scenarios/sandbox-mvp-behavior.test.ts +24 -23
  374. package/src/scenarios/sandbox-no-host-fs-escape.test.ts +3 -2
  375. package/src/scenarios/sandbox-timeout-cap.test.ts +2 -1
  376. package/src/scenarios/sandbox-wasm-isolation.test.ts +14 -15
  377. package/src/scenarios/sandbox-wasm-timeout.test.ts +5 -6
  378. package/src/scenarios/scheduling-capability-shape.test.ts +8 -6
  379. package/src/scenarios/scheduling-cron-fires-once.test.ts +8 -6
  380. package/src/scenarios/search-bm25-roundtrip.test.ts +8 -6
  381. package/src/scenarios/secret-leakage-otel-attribute.test.ts +22 -20
  382. package/src/scenarios/self-hosted-runner.test.ts +26 -24
  383. package/src/scenarios/semantic-digest-vectors.test.ts +8 -7
  384. package/src/scenarios/speech-synthesis-roundtrip.test.ts +9 -7
  385. package/src/scenarios/speech-synthesis-unadvertised.test.ts +5 -3
  386. package/src/scenarios/sql-injection-rejection.test.ts +8 -6
  387. package/src/scenarios/sql-transaction-atomicity.test.ts +10 -8
  388. package/src/scenarios/staleClaim.test.ts +5 -4
  389. package/src/scenarios/stream-modes-buffer.test.ts +8 -7
  390. package/src/scenarios/stream-modes-mixed.test.ts +10 -9
  391. package/src/scenarios/stream-modes.test.ts +12 -11
  392. package/src/scenarios/stream-subscribe-from-beginning.test.ts +11 -9
  393. package/src/scenarios/stream-text-fixture.test.ts +14 -13
  394. package/src/scenarios/streamReconnect.test.ts +13 -11
  395. package/src/scenarios/strict-behavior-gate.test.ts +9 -8
  396. package/src/scenarios/subrun-approval-fail-closed.test.ts +2 -2
  397. package/src/scenarios/subrun-approval-gate.test.ts +3 -3
  398. package/src/scenarios/subrun-attestation-shape.test.ts +2 -2
  399. package/src/scenarios/subrun-checksum-stable.test.ts +4 -4
  400. package/src/scenarios/subworkflow-input-mapping.test.ts +16 -14
  401. package/src/scenarios/subworkflow.test.ts +10 -9
  402. package/src/scenarios/table-cross-tenant-isolation.test.ts +8 -6
  403. package/src/scenarios/table-cursor-pagination.test.ts +8 -6
  404. package/src/scenarios/table-schema-enforcement.test.ts +7 -5
  405. package/src/scenarios/test-seam-unauthenticated.test.ts +2 -2
  406. package/src/scenarios/tool-catalog-compact-projection.test.ts +10 -8
  407. package/src/scenarios/tool-catalog-projection.test.ts +11 -9
  408. package/src/scenarios/tool-descriptor-shape.test.ts +14 -15
  409. package/src/scenarios/tool-hooks-authorization-fail-closed.test.ts +3 -3
  410. package/src/scenarios/tool-hooks-content-free.test.ts +4 -4
  411. package/src/scenarios/tool-hooks-failure-honesty.test.ts +4 -4
  412. package/src/scenarios/tool-hooks-rate-limit.test.ts +2 -2
  413. package/src/scenarios/tool-hooks-secret-redaction.test.ts +2 -2
  414. package/src/scenarios/tool-hooks-shape.test.ts +3 -3
  415. package/src/scenarios/tool-session-lifecycle.test.ts +14 -13
  416. package/src/scenarios/trigger-bridge-delivery.test.ts +14 -14
  417. package/src/scenarios/trigger-bridge-shape.test.ts +19 -20
  418. package/src/scenarios/trigger-ingestion.test.ts +19 -17
  419. package/src/scenarios/trigger-stream-cdc-sources.test.ts +16 -14
  420. package/src/scenarios/vector-knn-roundtrip.test.ts +9 -7
  421. package/src/scenarios/verifier-gating.test.ts +8 -6
  422. package/src/scenarios/version-fold.test.ts +9 -8
  423. package/src/scenarios/version-negotiation.test.ts +12 -11
  424. package/src/scenarios/voice-bargein-no-partial-leak.test.ts +8 -6
  425. package/src/scenarios/voice-event-payloads-shape.test.ts +10 -11
  426. package/src/scenarios/voice-interim-not-durable.test.ts +7 -5
  427. package/src/scenarios/voice-streamref-tenant-bound.test.ts +5 -3
  428. package/src/scenarios/voice-synthesis-streaming.test.ts +11 -9
  429. package/src/scenarios/voice-transcription-streaming.test.ts +8 -6
  430. package/src/scenarios/voice-transcription-unadvertised.test.ts +5 -3
  431. package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +3 -2
  432. package/src/scenarios/wasm-pack-invoke-completed.test.ts +3 -2
  433. package/src/scenarios/wasm-pack-invoke-suspended.test.ts +3 -2
  434. package/src/scenarios/wasm-pack-load.test.ts +4 -3
  435. package/src/scenarios/wasm-pack-memory-cap.test.ts +5 -4
  436. package/src/scenarios/wasm-pack-replay-determinism.test.ts +2 -1
  437. package/src/scenarios/webhook-negative.test.ts +11 -9
  438. package/src/scenarios/webhook-receiver-adversarial.test.ts +8 -7
  439. package/src/scenarios/webhook-sig-algorithm.test.ts +5 -3
  440. package/src/scenarios/webhook-signed-delivery.test.ts +16 -15
  441. package/src/scenarios/webhook-tenant-isolation.test.ts +16 -14
  442. package/src/scenarios/workflow-chain-deferred-parameters.test.ts +28 -30
  443. package/src/scenarios/workflow-chain-expansion.test.ts +21 -20
  444. package/src/scenarios/workflow-chain-host-expansion.test.ts +21 -20
  445. package/src/scenarios/workflow-chain-pack-manifest-validation.test.ts +14 -12
  446. package/src/scenarios/workflow-chain-pack-signature-verification.test.ts +5 -4
  447. package/src/scenarios/workflow-chain-unresolvable-typeid.test.ts +9 -8
  448. package/src/scenarios/workflow-primary-output-annotation.test.ts +7 -6
  449. package/src/scenarios/workflow-variable-format.test.ts +24 -24
  450. package/src/scenarios/workload-identity-behavior.test.ts +15 -13
  451. package/src/scenarios/workload-identity-chain-bounds.test.ts +18 -17
  452. package/src/scenarios/workspace-behavior.test.ts +19 -18
  453. package/src/scenarios/workspace-capability-shape.test.ts +3 -2
  454. package/src/scenarios/workspace-cross-tenant-isolation-blackbox.test.ts +6 -5
  455. package/src/scenarios/workspace-cross-tenant-isolation.test.ts +7 -6
  456. package/src/scenarios/x-openwop-form-pack-manifest.test.ts +8 -7
  457. package/src/setup.ts +4 -2
  458. package/vitest.config.ts +5 -1
  459. package/api/.redocly.lint-ignore.yaml +0 -22
  460. package/api/asyncapi.yaml +0 -834
  461. package/api/grpc/openwop.proto +0 -251
  462. package/api/openapi.yaml +0 -2693
  463. package/api/redocly.yaml +0 -23
  464. package/schemas/README.md +0 -144
  465. package/schemas/a2a-task-state.schema.json +0 -78
  466. package/schemas/a2ui-surface-delta-frame.schema.json +0 -48
  467. package/schemas/agent-deployment-transition.schema.json +0 -49
  468. package/schemas/agent-deployment.schema.json +0 -54
  469. package/schemas/agent-eval-suite.schema.json +0 -140
  470. package/schemas/agent-inventory-response.schema.json +0 -115
  471. package/schemas/agent-manifest.schema.json +0 -156
  472. package/schemas/agent-org-chart.schema.json +0 -82
  473. package/schemas/agent-ref.schema.json +0 -63
  474. package/schemas/agent-roster-entry.schema.json +0 -81
  475. package/schemas/agent-roster-response.schema.json +0 -21
  476. package/schemas/ai-envelope.schema.json +0 -134
  477. package/schemas/annotation-create.schema.json +0 -37
  478. package/schemas/annotation.schema.json +0 -56
  479. package/schemas/artifact-type-pack-manifest.schema.json +0 -244
  480. package/schemas/audit-verify-result.schema.json +0 -90
  481. package/schemas/budget-policy.schema.json +0 -18
  482. package/schemas/capabilities.schema.json +0 -4124
  483. package/schemas/certification-bundle-v2.schema.json +0 -118
  484. package/schemas/channel-presence-payload.schema.json +0 -41
  485. package/schemas/channel-written-payload.schema.json +0 -55
  486. package/schemas/chat-card-pack-manifest.schema.json +0 -253
  487. package/schemas/compact-tool-descriptor.schema.json +0 -51
  488. package/schemas/compensation-policy.schema.json +0 -64
  489. package/schemas/conformance-certification-bundle.schema.json +0 -86
  490. package/schemas/connection-pack-manifest.schema.json +0 -353
  491. package/schemas/conversation-event.schema.json +0 -168
  492. package/schemas/conversation-turn.schema.json +0 -117
  493. package/schemas/core-conformance-mock-agent-config.schema.json +0 -152
  494. package/schemas/credential-provenance.schema.json +0 -18
  495. package/schemas/credential-reference.schema.json +0 -21
  496. package/schemas/debug-bundle.schema.json +0 -196
  497. package/schemas/dispatch-config.schema.json +0 -112
  498. package/schemas/envelopes/clarification.request.schema.json +0 -52
  499. package/schemas/envelopes/error.schema.json +0 -30
  500. package/schemas/envelopes/media.audio.schema.json +0 -38
  501. package/schemas/envelopes/media.file.schema.json +0 -37
  502. package/schemas/envelopes/media.image.schema.json +0 -33
  503. package/schemas/envelopes/schema.request.schema.json +0 -26
  504. package/schemas/envelopes/schema.response.schema.json +0 -22
  505. package/schemas/envelopes/ui.a2ui-surface.schema.json +0 -154
  506. package/schemas/error-envelope.schema.json +0 -25
  507. package/schemas/eval-summary.schema.json +0 -92
  508. package/schemas/export-bundle.schema.json +0 -66
  509. package/schemas/form-content-pack-manifest.schema.json +0 -243
  510. package/schemas/frontend-plugin-manifest.schema.json +0 -109
  511. package/schemas/goal.schema.json +0 -104
  512. package/schemas/heartbeat-evaluated.schema.json +0 -14
  513. package/schemas/heartbeat-state-changed.schema.json +0 -14
  514. package/schemas/localized-content-language-settings.schema.json +0 -26
  515. package/schemas/localized-content-page-response.schema.json +0 -60
  516. package/schemas/localized-content-page.schema.json +0 -62
  517. package/schemas/localized-content-section.schema.json +0 -51
  518. package/schemas/memory-entry.schema.json +0 -36
  519. package/schemas/memory-list-options.schema.json +0 -37
  520. package/schemas/node-pack-manifest.schema.json +0 -654
  521. package/schemas/orchestrator-decision.schema.json +0 -90
  522. package/schemas/org-chart-responsibility-view.schema.json +0 -26
  523. package/schemas/pack-lockfile.schema.json +0 -108
  524. package/schemas/prompt-kind.schema.json +0 -8
  525. package/schemas/prompt-pack-manifest.schema.json +0 -117
  526. package/schemas/prompt-ref.schema.json +0 -40
  527. package/schemas/prompt-template.schema.json +0 -149
  528. package/schemas/proposal.schema.json +0 -84
  529. package/schemas/registry-version-manifest.schema.json +0 -208
  530. package/schemas/residency.schema.json +0 -16
  531. package/schemas/run-ancestry-response.schema.json +0 -54
  532. package/schemas/run-diff-response.schema.json +0 -64
  533. package/schemas/run-event-payloads.schema.json +0 -4452
  534. package/schemas/run-event.schema.json +0 -211
  535. package/schemas/run-options.schema.json +0 -80
  536. package/schemas/run-orchestrator-decided-event.schema.json +0 -20
  537. package/schemas/run-snapshot.schema.json +0 -148
  538. package/schemas/security-advisory.schema.json +0 -109
  539. package/schemas/self-hosted-runner-dispatch-frame.schema.json +0 -79
  540. package/schemas/self-hosted-runner-registration.schema.json +0 -53
  541. package/schemas/self-hosted-runner-result-frame.schema.json +0 -38
  542. package/schemas/subject.schema.json +0 -56
  543. package/schemas/suspend-request.schema.json +0 -207
  544. package/schemas/tool-descriptor.schema.json +0 -63
  545. package/schemas/trigger-event.schema.json +0 -222
  546. package/schemas/trigger-subscription-registration.schema.json +0 -67
  547. package/schemas/trigger-subscription.schema.json +0 -26
  548. package/schemas/ui-plugin-message.schema.json +0 -90
  549. package/schemas/workflow-chain-pack-manifest.schema.json +0 -615
  550. package/schemas/workflow-definition.schema.json +0 -850
  551. package/schemas/workload-identity.schema.json +0 -79
  552. package/schemas/workspace-file-create.schema.json +0 -20
  553. package/schemas/workspace-file.schema.json +0 -39
@@ -47,6 +47,8 @@ import { describe, it, expect } from 'vitest';
47
47
  import { driver } from '../lib/driver.js';
48
48
  import { recordRequirement } from '../lib/requirement-ledger.js';
49
49
  import { requirementIdForScenario } from '../lib/requirement-registry.js';
50
+ import { req } from '../lib/requirement-ids.js';
51
+ import { softSkip } from '../lib/soft-skip.js';
50
52
 
51
53
  /** RFC 0148 §A requirement id this file's floor row is recorded under. */
52
54
  const REQUIREMENT_ID = requirementIdForScenario('pack-registry.test.ts');
@@ -112,7 +114,7 @@ async function probeRegistry(): Promise<RegistryProbeResult> {
112
114
  describe('pack-registry: read-endpoint shape contracts', () => {
113
115
  it('GET /v1/packs/-/search returns an OpenWOP-shaped response (or registry is absent)', async () => {
114
116
  const probe = await probeRegistry();
115
- if (!probe.registryPresent) return; // Host doesn't ship a registry.
117
+ if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (Host doesn\'t ship a registry.) (seam, prior step, or fixture unavailable)'); // Host doesn't ship a registry.
116
118
 
117
119
  const res = await driver.get('/v1/packs/-/search?q=', { authenticated: false });
118
120
  // Spec doesn't pin empty-q behavior — both 400 (validation) and 200
@@ -120,18 +122,18 @@ describe('pack-registry: read-endpoint shape contracts', () => {
120
122
  // JSON-shaped.
121
123
  expect([200, 400]).toContain(res.status);
122
124
  const body = res.json as Record<string, unknown> | undefined;
123
- expect(body, driver.describe(
125
+ expect(body, req('openwop.it.pack-registry.get-v1-packs-search-returns-an-openwop-shaped-response-or-registry-is-absent',
124
126
  'node-packs.md §"GET /v1/packs/-/search"',
125
127
  'response MUST be JSON',
126
128
  )).toBeDefined();
127
129
 
128
130
  if (res.status === 200) {
129
- expect(Array.isArray(body?.results), driver.describe(
131
+ expect(Array.isArray(body?.results), req('openwop.it.pack-registry.get-v1-packs-search-returns-an-openwop-shaped-response-or-registry-is-absent',
130
132
  'node-packs.md §"GET /v1/packs/-/search"',
131
133
  'search response MUST carry a `results` array',
132
134
  )).toBe(true);
133
135
  } else {
134
- expect(typeof body?.error, driver.describe(
136
+ expect(typeof body?.error, req('openwop.it.pack-registry.get-v1-packs-search-returns-an-openwop-shaped-response-or-registry-is-absent',
135
137
  'rest-endpoints.md §"Error envelope"',
136
138
  '400 response MUST carry a string `error` field',
137
139
  )).toBe('string');
@@ -140,24 +142,24 @@ describe('pack-registry: read-endpoint shape contracts', () => {
140
142
 
141
143
  it('GET /v1/packs/{nonexistent} returns 404 pack_not_found with openwop error envelope', async () => {
142
144
  const probe = await probeRegistry();
143
- if (!probe.registryPresent) return;
145
+ if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (seam, prior step, or fixture unavailable)');
144
146
 
145
147
  const res = await driver.get(`/v1/packs/${encodeURIComponent(NONEXISTENT_PACK)}`, {
146
148
  authenticated: false,
147
149
  });
148
- expect(res.status, driver.describe(
150
+ expect(res.status, req('openwop.it.pack-registry.get-v1-packs-nonexistent-returns-404-pack-not-found-with-openwop-error-envelope',
149
151
  'node-packs.md §"GET /v1/packs/{name}"',
150
152
  'unknown pack name MUST return 404',
151
153
  )).toBe(404);
152
154
 
153
155
  const body = res.json as { error?: unknown } | undefined;
154
- expect(typeof body?.error, driver.describe(
156
+ expect(typeof body?.error, req('openwop.it.pack-registry.get-v1-packs-nonexistent-returns-404-pack-not-found-with-openwop-error-envelope',
155
157
  'rest-endpoints.md §"Error envelope"',
156
158
  '404 response MUST carry a string `error` field',
157
159
  )).toBe('string');
158
160
  // Reference impl emits `pack_not_found`; spec doesn't pin the exact
159
161
  // string — but the prefix `pack_` is the documented family.
160
- expect((body?.error as string).length, driver.describe(
162
+ expect((body?.error as string).length, req('openwop.it.pack-registry.get-v1-packs-nonexistent-returns-404-pack-not-found-with-openwop-error-envelope',
161
163
  'rest-endpoints.md §"Error envelope"',
162
164
  '`error` field MUST be non-empty',
163
165
  )).toBeGreaterThan(0);
@@ -165,19 +167,19 @@ describe('pack-registry: read-endpoint shape contracts', () => {
165
167
 
166
168
  it('GET /v1/packs/{name}/-/{version}.json returns 404 for nonexistent version', async () => {
167
169
  const probe = await probeRegistry();
168
- if (!probe.registryPresent) return;
170
+ if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (seam, prior step, or fixture unavailable)');
169
171
 
170
172
  const res = await driver.get(
171
173
  `/v1/packs/${encodeURIComponent(NONEXISTENT_PACK)}/-/${NONEXISTENT_VERSION}.json`,
172
174
  { authenticated: false },
173
175
  );
174
- expect(res.status, driver.describe(
176
+ expect(res.status, req('openwop.it.pack-registry.get-v1-packs-name-version-json-returns-404-for-nonexistent-version',
175
177
  'node-packs.md §"GET /v1/packs/{name}/-/{version}.json"',
176
178
  'unknown (name, version) MUST return 404',
177
179
  )).toBe(404);
178
180
 
179
181
  const body = res.json as { error?: unknown } | undefined;
180
- expect(typeof body?.error, driver.describe(
182
+ expect(typeof body?.error, req('openwop.it.pack-registry.get-v1-packs-name-version-json-returns-404-for-nonexistent-version',
181
183
  'rest-endpoints.md §"Error envelope"',
182
184
  '404 response MUST carry a string `error` field',
183
185
  )).toBe('string');
@@ -185,7 +187,7 @@ describe('pack-registry: read-endpoint shape contracts', () => {
185
187
 
186
188
  it('GET /v1/packs/{name}/-/{version}.sig returns 404 signature_not_available for nonexistent version', async () => {
187
189
  const probe = await probeRegistry();
188
- if (!probe.registryPresent) return;
190
+ if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (seam, prior step, or fixture unavailable)');
189
191
 
190
192
  const res = await driver.get(
191
193
  `/v1/packs/${encodeURIComponent(NONEXISTENT_PACK)}/-/${NONEXISTENT_VERSION}.sig`,
@@ -196,13 +198,13 @@ describe('pack-registry: read-endpoint shape contracts', () => {
196
198
  // intentionally indistinguishable. A 302 redirect to a storage-
197
199
  // backed signed URL is also spec-allowed for VALID signatures —
198
200
  // for a NONEXISTENT pack, only 404 is correct.
199
- expect(res.status, driver.describe(
201
+ expect(res.status, req('openwop.it.pack-registry.get-v1-packs-name-version-sig-returns-404-signature-not-available-for-nonexisten',
200
202
  'node-packs.md §"GET /v1/packs/{name}/-/{version}.sig"',
201
203
  'nonexistent (name, version) MUST return 404 signature_not_available',
202
204
  )).toBe(404);
203
205
 
204
206
  const body = res.json as { error?: unknown } | undefined;
205
- expect(typeof body?.error, driver.describe(
207
+ expect(typeof body?.error, req('openwop.it.pack-registry.get-v1-packs-name-version-sig-returns-404-signature-not-available-for-nonexisten',
206
208
  'node-packs.md §"GET /v1/packs/{name}/-/{version}.sig"',
207
209
  '404 response MUST carry a string `error` field — `signature_not_available` is the canonical code',
208
210
  )).toBe('string');
@@ -211,19 +213,19 @@ describe('pack-registry: read-endpoint shape contracts', () => {
211
213
 
212
214
  it('GET /v1/packs/{bad-name}/-/{version}.json returns 400 invalid_pack_name', async () => {
213
215
  const probe = await probeRegistry();
214
- if (!probe.registryPresent) return;
216
+ if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (seam, prior step, or fixture unavailable)');
215
217
 
216
218
  // Single-segment name violates the reverse-DNS pattern.
217
219
  const res = await driver.get('/v1/packs/not-reverse-dns/-/1.0.json', {
218
220
  authenticated: false,
219
221
  });
220
- expect(res.status, driver.describe(
222
+ expect(res.status, req('openwop.it.pack-registry.get-v1-packs-bad-name-version-json-returns-400-invalid-pack-name',
221
223
  'node-packs.md §"GET /v1/packs/{name}/-/{version}.json"',
222
224
  'malformed pack name MUST return 400 invalid_pack_name',
223
225
  )).toBe(400);
224
226
 
225
227
  const body = res.json as { error?: unknown } | undefined;
226
- expect(typeof body?.error, driver.describe(
228
+ expect(typeof body?.error, req('openwop.it.pack-registry.get-v1-packs-bad-name-version-json-returns-400-invalid-pack-name',
227
229
  'rest-endpoints.md §"Error envelope"',
228
230
  '400 response MUST carry a string `error` field',
229
231
  )).toBe('string');
@@ -231,14 +233,14 @@ describe('pack-registry: read-endpoint shape contracts', () => {
231
233
 
232
234
  it('GET /v1/packs/{name}/-/{bad-version}.json returns 400 invalid_version', async () => {
233
235
  const probe = await probeRegistry();
234
- if (!probe.registryPresent) return;
236
+ if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (seam, prior step, or fixture unavailable)');
235
237
 
236
238
  // `not-a-version` violates semver.
237
239
  const res = await driver.get(
238
240
  `/v1/packs/${encodeURIComponent(NONEXISTENT_PACK)}/-/not-a-version.json`,
239
241
  { authenticated: false },
240
242
  );
241
- expect(res.status, driver.describe(
243
+ expect(res.status, req('openwop.it.pack-registry.get-v1-packs-name-bad-version-json-returns-400-invalid-version',
242
244
  'node-packs.md §"GET /v1/packs/{name}/-/{version}.json"',
243
245
  'non-semver version MUST return 400 invalid_version',
244
246
  )).toBe(400);
@@ -251,20 +253,20 @@ describe('pack-registry: read-endpoint shape contracts', () => {
251
253
  describe('pack-registry: catalog response shape (when populated)', () => {
252
254
  it('GET /v1/packs/{name} catalog records validate against the documented shape', async () => {
253
255
  const probe = await probeRegistry();
254
- if (!probe.registryPresent) return;
256
+ if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (seam, prior step, or fixture unavailable)');
255
257
 
256
258
  // Probe search for a real entry. If the catalog is empty, skip.
257
259
  const search = await driver.get('/v1/packs/-/search?q=', { authenticated: false });
258
- if (search.status !== 200) return;
260
+ if (search.status !== 200) return softSkip('blocked', 'precondition not met — `search.status !== 200` returned early (seam, prior step, or fixture unavailable)');
259
261
  const results = (search.json as { results?: Array<{ name?: unknown }> } | undefined)?.results;
260
- if (!Array.isArray(results) || results.length === 0) return;
262
+ if (!Array.isArray(results) || results.length === 0) return softSkip('blocked', 'precondition not met — `!Array.isArray(results) || results.length === 0` returned early (seam, prior step, or fixture unavailable)');
261
263
 
262
264
  // Walk up to 3 results and assert their catalog records are well-shaped.
263
265
  const sample = results.slice(0, 3);
264
266
  for (const entry of sample) {
265
267
  const name = entry.name;
266
268
  if (typeof name !== 'string') continue;
267
- expect(name, driver.describe(
269
+ expect(name, req('openwop.it.pack-registry.get-v1-packs-name-catalog-records-validate-against-the-documented-shape',
268
270
  'node-packs.md §"Naming"',
269
271
  `search result name "${name}" MUST match reverse-DNS pattern`,
270
272
  )).toMatch(PACK_NAME_RE);
@@ -272,7 +274,7 @@ describe('pack-registry: catalog response shape (when populated)', () => {
272
274
  const cat = await driver.get(`/v1/packs/${encodeURIComponent(name)}`, {
273
275
  authenticated: false,
274
276
  });
275
- expect(cat.status, driver.describe(
277
+ expect(cat.status, req('openwop.it.pack-registry.get-v1-packs-name-catalog-records-validate-against-the-documented-shape',
276
278
  'node-packs.md §"GET /v1/packs/{name}"',
277
279
  `catalog read for known pack "${name}" MUST return 200`,
278
280
  )).toBe(200);
@@ -282,11 +284,11 @@ describe('pack-registry: catalog response shape (when populated)', () => {
282
284
  versions?: Record<string, unknown>;
283
285
  'dist-tags'?: { latest?: unknown };
284
286
  } | undefined;
285
- expect(body?.name, driver.describe(
287
+ expect(body?.name, req('openwop.it.pack-registry.get-v1-packs-name-catalog-records-validate-against-the-documented-shape',
286
288
  'node-packs.md §"GET /v1/packs/{name}"',
287
289
  'catalog response MUST echo the requested pack name',
288
290
  )).toBe(name);
289
- expect(typeof body?.versions, driver.describe(
291
+ expect(typeof body?.versions, req('openwop.it.pack-registry.get-v1-packs-name-catalog-records-validate-against-the-documented-shape',
290
292
  'node-packs.md §"GET /v1/packs/{name}"',
291
293
  'catalog response MUST carry a `versions` map',
292
294
  )).toBe('object');
@@ -294,7 +296,7 @@ describe('pack-registry: catalog response shape (when populated)', () => {
294
296
  // Every version key MUST be valid semver per the spec.
295
297
  const versionKeys = Object.keys(body?.versions ?? {});
296
298
  for (const v of versionKeys) {
297
- expect(v, driver.describe(
299
+ expect(v, req('openwop.it.pack-registry.get-v1-packs-name-catalog-records-validate-against-the-documented-shape',
298
300
  'node-packs.md §"Versioning"',
299
301
  `version key "${v}" MUST match SemVer 2.0.0`,
300
302
  )).toMatch(SEMVER_RE);
@@ -306,16 +308,16 @@ describe('pack-registry: catalog response shape (when populated)', () => {
306
308
  describe('pack-registry: keychain shape (when present)', () => {
307
309
  it('GET /v1/packs/{name}/-/keychain returns well-formed key entries when present', async () => {
308
310
  const probe = await probeRegistry();
309
- if (!probe.registryPresent) return;
311
+ if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (seam, prior step, or fixture unavailable)');
310
312
 
311
313
  // Probe for any real pack to fetch its keychain. Skip if no packs.
312
314
  const search = await driver.get('/v1/packs/-/search?q=', { authenticated: false });
313
- if (search.status !== 200) return;
315
+ if (search.status !== 200) return softSkip('blocked', 'precondition not met — `search.status !== 200` returned early (seam, prior step, or fixture unavailable)');
314
316
  const results = (search.json as { results?: Array<{ name?: unknown }> } | undefined)?.results;
315
- if (!Array.isArray(results) || results.length === 0) return;
317
+ if (!Array.isArray(results) || results.length === 0) return softSkip('blocked', 'precondition not met — `!Array.isArray(results) || results.length === 0` returned early (seam, prior step, or fixture unavailable)');
316
318
 
317
319
  const first = results[0]?.name;
318
- if (typeof first !== 'string') return;
320
+ if (typeof first !== 'string') return softSkip('blocked', 'precondition not met — `typeof first !== \'string\'` returned early (seam, prior step, or fixture unavailable)');
319
321
 
320
322
  const res = await driver.get(`/v1/packs/${encodeURIComponent(first)}/-/keychain`, {
321
323
  authenticated: false,
@@ -323,25 +325,25 @@ describe('pack-registry: keychain shape (when present)', () => {
323
325
  // 200 with `{keys: []}` and 404 are both spec-allowed (keychain is
324
326
  // optional; not every namespace publishes one).
325
327
  expect([200, 404]).toContain(res.status);
326
- if (res.status === 404) return;
328
+ if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam, prior step, or fixture unavailable)');
327
329
 
328
330
  const body = res.json as { keys?: unknown; namespace?: unknown } | undefined;
329
- expect(Array.isArray(body?.keys), driver.describe(
331
+ expect(Array.isArray(body?.keys), req('openwop.it.pack-registry.get-v1-packs-name-keychain-returns-well-formed-key-entries-when-present',
330
332
  'registry-operations.md §"Signing keychain"',
331
333
  'keychain response MUST carry a `keys` array',
332
334
  )).toBe(true);
333
335
 
334
336
  const keys = body?.keys as Array<Record<string, unknown>>;
335
337
  for (const key of keys) {
336
- expect(typeof key.kid, driver.describe(
338
+ expect(typeof key.kid, req('openwop.it.pack-registry.get-v1-packs-name-keychain-returns-well-formed-key-entries-when-present',
337
339
  'registry-operations.md §"Signing keychain"',
338
340
  'each key MUST have a string `kid`',
339
341
  )).toBe('string');
340
- expect(typeof key.algorithm, driver.describe(
342
+ expect(typeof key.algorithm, req('openwop.it.pack-registry.get-v1-packs-name-keychain-returns-well-formed-key-entries-when-present',
341
343
  'registry-operations.md §"Signing keychain"',
342
344
  'each key MUST have a string `algorithm`',
343
345
  )).toBe('string');
344
- expect(typeof key.publicKey, driver.describe(
346
+ expect(typeof key.publicKey, req('openwop.it.pack-registry.get-v1-packs-name-keychain-returns-well-formed-key-entries-when-present',
345
347
  'registry-operations.md §"Signing keychain"',
346
348
  'each key MUST have a string `publicKey`',
347
349
  )).toBe('string');
@@ -22,6 +22,8 @@ import { driver } from '../lib/driver.js';
22
22
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
23
23
  import { pollUntilStatus, pollUntilTerminal } from '../lib/polling.js';
24
24
  import { isFixtureAdvertised } from '../lib/fixtures.js';
25
+ import { req } from '../lib/requirement-ids.js';
26
+ import { softSkip } from '../lib/soft-skip.js';
25
27
 
26
28
  const FIXTURE =
27
29
  (isFixtureAdvertised('conformance-cancellable') && 'conformance-cancellable') ||
@@ -54,9 +56,9 @@ describe.skipIf(SKIP)('pause/resume: running → paused → running → terminal
54
56
  await driver.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {
55
57
  reason: 'conformance-cleanup',
56
58
  });
57
- return;
59
+ return softSkip('blocked', 'precondition not met — `pause.status === 404` returned early ([pause-resume] host returned 404 for :pause — endpoint not implemented; skipping rest) (seam, prior step, or fixture unavailable)');
58
60
  }
59
- expect(pause.status, driver.describe(
61
+ expect(pause.status, req('openwop.it.pause-resume.pause-transitions-to-paused-resume-returns-the-run-to-running',
60
62
  'rest-endpoints.md POST /v1/runs/{runId}:pause',
61
63
  ':pause MUST return 202 on a pausable run',
62
64
  )).toBe(202);
@@ -66,7 +68,7 @@ describe.skipIf(SKIP)('pause/resume: running → paused → running → terminal
66
68
  const resume = await driver.post(`/v1/runs/${encodeURIComponent(runId)}:resume`, {
67
69
  reason: 'conformance-test',
68
70
  });
69
- expect(resume.status, driver.describe(
71
+ expect(resume.status, req('openwop.it.pause-resume.pause-transitions-to-paused-resume-returns-the-run-to-running',
70
72
  'rest-endpoints.md POST /v1/runs/{runId}:resume',
71
73
  ':resume MUST return 202 on a paused run',
72
74
  )).toBe(202);
@@ -92,9 +94,9 @@ describe.skipIf(SKIP)('pause/resume: :resume on a non-paused run returns 409', (
92
94
  await driver.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {
93
95
  reason: 'conformance-cleanup',
94
96
  });
95
- return;
97
+ return softSkip('blocked', 'precondition not met — `resume.status === 404` returned early (seam, prior step, or fixture unavailable)');
96
98
  }
97
- expect(resume.status, driver.describe(
99
+ expect(resume.status, req('openwop.it.pause-resume.resuming-a-running-not-paused-run-returns-409-with-details-runstatus',
98
100
  'rest-endpoints.md POST /v1/runs/{runId}:resume',
99
101
  ':resume on a non-paused run MUST return 409',
100
102
  )).toBe(409);
@@ -124,7 +126,7 @@ describe.skipIf(SKIP)('pause/resume: pause is idempotent when already paused', (
124
126
  await driver.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {
125
127
  reason: 'conformance-cleanup',
126
128
  });
127
- return;
129
+ return softSkip('blocked', 'precondition not met — `first.status === 404` returned early (seam, prior step, or fixture unavailable)');
128
130
  }
129
131
  expect([200, 202]).toContain(first.status);
130
132
  await pollUntilStatus(runId, 'paused', { timeoutMs: 10_000 });
@@ -135,7 +137,7 @@ describe.skipIf(SKIP)('pause/resume: pause is idempotent when already paused', (
135
137
  const second = await driver.post(`/v1/runs/${encodeURIComponent(runId)}:pause`, {});
136
138
  expect(
137
139
  [200, 202].includes(second.status),
138
- driver.describe(
140
+ req('openwop.it.pause-resume.pause-on-an-already-paused-run-is-a-no-op-200-202-idempotent',
139
141
  'rest-endpoints.md POST /v1/runs/{runId}:pause',
140
142
  ':pause on an already-paused run MUST be idempotent (200/202), not 409',
141
143
  ),
@@ -152,13 +154,13 @@ describe.skipIf(SKIP)('pause/resume: :pause on a terminal run returns 409', () =
152
154
  const create = await driver.post('/v1/runs', {
153
155
  workflowId: 'conformance-noop',
154
156
  });
155
- if (create.status !== 201) return; // conformance-noop not seeded; skip cleanly
157
+ if (create.status !== 201) return softSkip('blocked', 'precondition not met — `create.status !== 201` returned early (conformance-noop not seeded; skip cleanly) (seam, prior step, or fixture unavailable)'); // conformance-noop not seeded; skip cleanly
156
158
  const runId = (create.json as { runId: string }).runId;
157
159
  await pollUntilTerminal(runId, { timeoutMs: 10_000 });
158
160
 
159
161
  const pause = await driver.post(`/v1/runs/${encodeURIComponent(runId)}:pause`, {});
160
- if (pause.status === 404) return;
161
- expect(pause.status, driver.describe(
162
+ if (pause.status === 404) return softSkip('blocked', 'precondition not met — `pause.status === 404` returned early (seam, prior step, or fixture unavailable)');
163
+ expect(pause.status, req('openwop.it.pause-resume.pause-on-a-completed-cancelled-failed-run-must-return-409',
162
164
  'rest-endpoints.md POST /v1/runs/{runId}:pause',
163
165
  ':pause on a terminal run MUST return 409',
164
166
  )).toBe(409);
@@ -185,7 +187,7 @@ describe.skipIf(SKIP)('pause/resume: :pause-during-suspend race', () => {
185
187
  console.warn(
186
188
  '[pause-resume] conformance-approval not advertised; skipping :pause-during-suspend race subtest',
187
189
  );
188
- return;
190
+ return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(\'conformance-approval\')` returned early ([pause-resume] conformance-approval not advertised; skipping :pause-during-suspend race subtest)');
189
191
  }
190
192
  const create = await driver.post('/v1/runs', { workflowId: 'conformance-approval' });
191
193
  expect(create.status).toBe(201);
@@ -200,13 +202,13 @@ describe.skipIf(SKIP)('pause/resume: :pause-during-suspend race', () => {
200
202
  await driver.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {
201
203
  reason: 'conformance-cleanup',
202
204
  });
203
- return;
205
+ return softSkip('blocked', 'precondition not met — `pause.status === 404` returned early (seam, prior step, or fixture unavailable)');
204
206
  }
205
207
 
206
208
  // Either rejection (preferred) or stacked-pause is OK; silent override is not.
207
209
  if (pause.status === 409) {
208
210
  const body = pause.json as { details?: { runStatus?: string } };
209
- expect(body.details?.runStatus, driver.describe(
211
+ expect(body.details?.runStatus, req('openwop.it.pause-resume.pause-must-not-silently-override-an-active-interrupt-suspend',
210
212
  'rest-endpoints.md POST /v1/runs/{runId}:pause',
211
213
  ':pause-during-suspend MUST surface the active waiting-* status in the conflict envelope',
212
214
  )).toMatch(/^waiting-/);
@@ -218,7 +220,7 @@ describe.skipIf(SKIP)('pause/resume: :pause-during-suspend race', () => {
218
220
  const status = (snap.json as { status: string }).status;
219
221
  expect(
220
222
  status === 'paused' || status.startsWith('waiting-'),
221
- ':pause-during-suspend MUST NOT silently discard the active interrupt',
223
+ req('openwop.it.pause-resume.pause-must-not-silently-override-an-active-interrupt-suspend', 'rest-endpoints.md POST /v1/runs/{runId}:pause', ':pause-during-suspend MUST NOT silently discard the active interrupt'),
222
224
  ).toBe(true);
223
225
  }
224
226
 
@@ -241,7 +243,7 @@ describe.skipIf(SKIP)('pause/resume: drainPolicy discrimination per capabilities
241
243
  if (drainPolicies.length === 0) {
242
244
  // eslint-disable-next-line no-console
243
245
  console.warn('[pause-resume] host advertises no drainPolicies; skipping policy-discrimination subtest');
244
- return;
246
+ return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `drainPolicies.length === 0` returned early ([pause-resume] host advertises no drainPolicies; skipping policy-discrimination subtest)');
245
247
  }
246
248
 
247
249
  for (const policy of drainPolicies) {
@@ -258,7 +260,7 @@ describe.skipIf(SKIP)('pause/resume: drainPolicy discrimination per capabilities
258
260
  reason: `conformance-drainpolicy-${policy}`,
259
261
  drainPolicy: policy,
260
262
  });
261
- expect(pause.status, driver.describe(
263
+ expect(pause.status, req('openwop.it.pause-resume.every-drainpolicy-advertised-by-the-host-is-accepted-on-pause',
262
264
  'capabilities.md §`runs.pauseResume.drainPolicies` + rest-endpoints.md POST /v1/runs/{runId}:pause',
263
265
  `host-advertised drainPolicy='${policy}' MUST be accepted on :pause`,
264
266
  )).toBe(202);
@@ -35,6 +35,8 @@
35
35
 
36
36
  import { describe, it, expect } from 'vitest';
37
37
  import { driver } from '../lib/driver.js';
38
+ import { req } from '../lib/requirement-ids.js';
39
+ import { softSkip } from '../lib/soft-skip.js';
38
40
 
39
41
  const CANONICAL_MODES = ['disabled', 'optional', 'required', 'restricted'] as const;
40
42
 
@@ -60,21 +62,21 @@ async function fetchAiProviders(): Promise<AiProvidersShape | undefined> {
60
62
  describe('policies: /.well-known/openwop aiProviders.policies shape contract', () => {
61
63
  it('aiProviders.policies is well-formed when present (or absent — both spec-allowed)', async () => {
62
64
  const ap = await fetchAiProviders();
63
- if (ap === undefined) return; // Optional v1 field — hosts MAY omit aiProviders entirely.
65
+ if (ap === undefined) return softSkip('blocked', 'precondition not met — `ap === undefined` returned early (Optional v1 field — hosts MAY omit aiProviders entirely.) (seam, prior step, or fixture unavailable)'); // Optional v1 field — hosts MAY omit aiProviders entirely.
64
66
 
65
67
  const policies = ap.policies;
66
68
  if (policies === undefined) {
67
69
  // Spec-allowed: omitting `policies` means the host implements no
68
70
  // enforcement. Clients see only `optional` semantics. Nothing
69
71
  // further to assert.
70
- return;
72
+ return softSkip('blocked', 'precondition not met — `policies === undefined` returned early (Spec-allowed: omitting `policies` means the host implements no enforcement. Clients see only `optional` semantics. Nothing further to assert.) (seam, pri…');
71
73
  }
72
74
 
73
- expect(typeof policies, driver.describe(
75
+ expect(typeof policies, req('openwop.it.policies.aiproviders-policies-is-well-formed-when-present-or-absent-both-spec-allowed',
74
76
  'capabilities.md §"`aiProviders.policies`"',
75
77
  'aiProviders.policies MUST be an object when present',
76
78
  )).toBe('object');
77
- expect(policies, driver.describe(
79
+ expect(policies, req('openwop.it.policies.aiproviders-policies-is-well-formed-when-present-or-absent-both-spec-allowed',
78
80
  'capabilities.md §"`aiProviders.policies`"',
79
81
  'aiProviders.policies MUST NOT be null when present',
80
82
  )).not.toBeNull();
@@ -83,20 +85,20 @@ describe('policies: /.well-known/openwop aiProviders.policies shape contract', (
83
85
  it('policies.modes — every advertised mode is one of the four canonical values', async () => {
84
86
  const ap = await fetchAiProviders();
85
87
  const modes = ap?.policies?.modes;
86
- if (modes === undefined) return;
88
+ if (modes === undefined) return softSkip('blocked', 'precondition not met — `modes === undefined` returned early (seam, prior step, or fixture unavailable)');
87
89
 
88
- expect(Array.isArray(modes), driver.describe(
90
+ expect(Array.isArray(modes), req('openwop.it.policies.policies-modes-every-advertised-mode-is-one-of-the-four-canonical-values',
89
91
  'capabilities.md §"`aiProviders.policies`"',
90
92
  'policies.modes MUST be a string[] when present',
91
93
  )).toBe(true);
92
94
 
93
95
  const arr = modes as unknown[];
94
96
  for (const mode of arr) {
95
- expect(typeof mode, driver.describe(
97
+ expect(typeof mode, req('openwop.it.policies.policies-modes-every-advertised-mode-is-one-of-the-four-canonical-values',
96
98
  'capabilities.md §"`aiProviders.policies`"',
97
99
  'policies.modes entries MUST be strings',
98
100
  )).toBe('string');
99
- expect(CANONICAL_MODES, driver.describe(
101
+ expect(CANONICAL_MODES, req('openwop.it.policies.policies-modes-every-advertised-mode-is-one-of-the-four-canonical-values',
100
102
  'capabilities.md §"`aiProviders.policies`"',
101
103
  `mode "${mode}" MUST be one of ${CANONICAL_MODES.join(', ')}`,
102
104
  )).toContain(mode);
@@ -106,11 +108,11 @@ describe('policies: /.well-known/openwop aiProviders.policies shape contract', (
106
108
  it('policies.modes — no duplicate entries (uniqueItems contract)', async () => {
107
109
  const ap = await fetchAiProviders();
108
110
  const modes = ap?.policies?.modes;
109
- if (!Array.isArray(modes)) return;
111
+ if (!Array.isArray(modes)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!Array.isArray(modes)` returned early');
110
112
 
111
113
  const arr = modes as string[];
112
114
  const set = new Set(arr);
113
- expect(set.size, driver.describe(
115
+ expect(set.size, req('openwop.it.policies.policies-modes-no-duplicate-entries-uniqueitems-contract',
114
116
  'capabilities.schema.json — policies.modes uniqueItems: true',
115
117
  'policies.modes MUST NOT contain duplicate entries',
116
118
  )).toBe(arr.length);
@@ -119,27 +121,27 @@ describe('policies: /.well-known/openwop aiProviders.policies shape contract', (
119
121
  it('policies.scopes — string[] of non-empty entries when present', async () => {
120
122
  const ap = await fetchAiProviders();
121
123
  const scopes = ap?.policies?.scopes;
122
- if (scopes === undefined) return;
124
+ if (scopes === undefined) return softSkip('blocked', 'precondition not met — `scopes === undefined` returned early (seam, prior step, or fixture unavailable)');
123
125
 
124
- expect(Array.isArray(scopes), driver.describe(
126
+ expect(Array.isArray(scopes), req('openwop.it.policies.policies-scopes-string-of-non-empty-entries-when-present',
125
127
  'capabilities.md §"`aiProviders.policies`"',
126
128
  'policies.scopes MUST be a string[] when present',
127
129
  )).toBe(true);
128
130
 
129
131
  const arr = scopes as unknown[];
130
132
  for (const scope of arr) {
131
- expect(typeof scope, driver.describe(
133
+ expect(typeof scope, req('openwop.it.policies.policies-scopes-string-of-non-empty-entries-when-present',
132
134
  'capabilities.md §"`aiProviders.policies`"',
133
135
  'policies.scopes entries MUST be strings',
134
136
  )).toBe('string');
135
- expect((scope as string).length, driver.describe(
137
+ expect((scope as string).length, req('openwop.it.policies.policies-scopes-string-of-non-empty-entries-when-present',
136
138
  'capabilities.md §"`aiProviders.policies`"',
137
139
  'policies.scopes entries MUST be non-empty',
138
140
  )).toBeGreaterThan(0);
139
141
  }
140
142
 
141
143
  const set = new Set(arr as string[]);
142
- expect(set.size, driver.describe(
144
+ expect(set.size, req('openwop.it.policies.policies-scopes-string-of-non-empty-entries-when-present',
143
145
  'capabilities.schema.json — policies.scopes uniqueItems: true',
144
146
  'policies.scopes MUST NOT contain duplicate entries',
145
147
  )).toBe(arr.length);
@@ -148,13 +150,13 @@ describe('policies: /.well-known/openwop aiProviders.policies shape contract', (
148
150
  it('policies.errorCode — non-empty string when present (defaults to provider_policy_denied)', async () => {
149
151
  const ap = await fetchAiProviders();
150
152
  const errorCode = ap?.policies?.errorCode;
151
- if (errorCode === undefined) return;
153
+ if (errorCode === undefined) return softSkip('blocked', 'precondition not met — `errorCode === undefined` returned early (seam, prior step, or fixture unavailable)');
152
154
 
153
- expect(typeof errorCode, driver.describe(
155
+ expect(typeof errorCode, req('openwop.it.policies.policies-errorcode-non-empty-string-when-present-defaults-to-provider-policy-den',
154
156
  'capabilities.md §"`aiProviders.policies`"',
155
157
  'policies.errorCode MUST be a string when present',
156
158
  )).toBe('string');
157
- expect((errorCode as string).length, driver.describe(
159
+ expect((errorCode as string).length, req('openwop.it.policies.policies-errorcode-non-empty-string-when-present-defaults-to-provider-policy-den',
158
160
  'capabilities.md §"`aiProviders.policies`"',
159
161
  'policies.errorCode MUST be non-empty when present',
160
162
  )).toBeGreaterThan(0);