@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
@@ -53,6 +53,8 @@ import Ajv2020 from 'ajv/dist/2020.js';
53
53
  import addFormats from 'ajv-formats';
54
54
  import { SCHEMAS_DIR, FIXTURES_DIR } from '../lib/paths.js';
55
55
  import { driver } from '../lib/driver.js';
56
+ import { req } from '../lib/requirement-ids.js';
57
+ import { softSkip } from '../lib/soft-skip.js';
56
58
 
57
59
  const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
58
60
  const SEAM = '/v1/host/sample/purpose-propagation/forward';
@@ -79,30 +81,30 @@ describe('purpose-propagation: label schema (always-on, server-free)', () => {
79
81
 
80
82
  it('a labelled TriggerEvent validates; a non-array label fails', () => {
81
83
  const ev = JSON.parse(readFileSync(FIXTURE, 'utf8'));
82
- expect(validate(ev), `a labelled TriggerEvent MUST validate (RFC 0128 §1). Errors: ${JSON.stringify(validate.errors)}`).toBe(true);
84
+ expect(validate(ev), req('openwop.it.purpose-propagation.a-labelled-triggerevent-validates-a-non-array-label-fails', 'RFC 0128', `a labelled TriggerEvent MUST validate (RFC 0128 §1). Errors: ${JSON.stringify(validate.errors)}`)).toBe(true);
83
85
 
84
86
  ev.permittedPurposes = 'analytics';
85
- expect(validate(ev), 'RFC 0128 §1 — permittedPurposes MUST be string[]').toBe(false);
87
+ expect(validate(ev), req('openwop.it.purpose-propagation.a-labelled-triggerevent-validates-a-non-array-label-fails', 'RFC 0128', 'RFC 0128 §1 — permittedPurposes MUST be string[]')).toBe(false);
86
88
  });
87
89
 
88
90
  it('absent and [] are BOTH wire-valid — distinct states (unlabelled vs no-onward-use)', () => {
89
91
  const ev = JSON.parse(readFileSync(FIXTURE, 'utf8'));
90
92
  delete ev.permittedPurposes;
91
- expect(validate(ev), 'RFC 0128 §1 — an unlabelled event (absent label) MUST validate').toBe(true);
93
+ expect(validate(ev), req('openwop.it.purpose-propagation.absent-and-are-both-wire-valid-distinct-states-unlabelled-vs-no-onward-use', 'RFC 0128', 'RFC 0128 §1 — an unlabelled event (absent label) MUST validate')).toBe(true);
92
94
  ev.permittedPurposes = [];
93
- expect(validate(ev), 'RFC 0128 §1 — a []-labelled event MUST validate (behavioral meaning: no onward use)').toBe(true);
95
+ expect(validate(ev), req('openwop.it.purpose-propagation.absent-and-are-both-wire-valid-distinct-states-unlabelled-vs-no-onward-use', 'RFC 0128', 'RFC 0128 §1 — a []-labelled event MUST validate (behavioral meaning: no onward use)')).toBe(true);
94
96
  });
95
97
 
96
98
  it('the capabilities.purposePropagation family requires `supported` and closes its shape', () => {
97
99
  const caps = JSON.parse(readFileSync(join(SCHEMAS_DIR, 'capabilities.schema.json'), 'utf8'));
98
100
  const fam = caps.properties?.purposePropagation;
99
- expect(fam, 'capabilities.schema.json MUST define the purposePropagation family (RFC 0128 §2)').toBeDefined();
100
- expect(fam.required, 'purposePropagation MUST require `supported`').toContain('supported');
101
- expect(fam.additionalProperties, 'purposePropagation MUST close its shape').toBe(false);
101
+ expect(fam, req('openwop.it.purpose-propagation.the-capabilities-purposepropagation-family-requires-supported-and-closes-its-sha', 'RFC 0128', 'capabilities.schema.json MUST define the purposePropagation family (RFC 0128 §2)')).toBeDefined();
102
+ expect(fam.required, req('openwop.it.purpose-propagation.the-capabilities-purposepropagation-family-requires-supported-and-closes-its-sha', 'RFC 0128', 'purposePropagation MUST require `supported`')).toContain('supported');
103
+ expect(fam.additionalProperties, req('openwop.it.purpose-propagation.the-capabilities-purposepropagation-family-requires-supported-and-closes-its-sha', 'RFC 0128', 'purposePropagation MUST close its shape')).toBe(false);
102
104
  const ajvFam = new Ajv2020({ allErrors: true, strict: false });
103
105
  const vf = ajvFam.compile(fam);
104
- expect(vf({ supported: true, propagatesOnward: true }), 'the canonical advert MUST validate').toBe(true);
105
- expect(vf({ propagatesOnward: true }), 'an advert without `supported` MUST fail').toBe(false);
106
+ expect(vf({ supported: true, propagatesOnward: true }), req('openwop.it.purpose-propagation.the-capabilities-purposepropagation-family-requires-supported-and-closes-its-sha', 'RFC 0128', 'the canonical advert MUST validate')).toBe(true);
107
+ expect(vf({ propagatesOnward: true }), req('openwop.it.purpose-propagation.the-capabilities-purposepropagation-family-requires-supported-and-closes-its-sha', 'RFC 0128', 'an advert without `supported` MUST fail')).toBe(false);
106
108
  });
107
109
  });
108
110
 
@@ -127,17 +129,17 @@ describe.skipIf(HTTP_SKIP)('purpose-propagation: two-hop onward behavior (seam-g
127
129
  it('a forwarded label survives ⊆ the received set (re-emit; MAY narrow, MUST NOT widen)', async () => {
128
130
  const input = ['analytics', 'marketing-email'];
129
131
  const res = await seamPost({ mode: 'forward', records: [{ id: 'r1', permittedPurposes: input, data: { k: 1 } }] });
130
- if (res === null) return;
132
+ if (res === null) return softSkip('blocked', 'precondition not met — `res === null` returned early (seam, prior step, or fixture unavailable)');
131
133
 
132
134
  const onward = res.onward ?? [];
133
135
  expect(
134
136
  onward.length > 0,
135
- driver.describe('RFC 0128 §3', 'an advertising host MUST re-emit a received label on the onward hop — silent loss fails (the promise is propagation)'),
137
+ req('openwop.it.purpose-propagation.a-forwarded-label-survives-the-received-set-re-emit-may-narrow-must-not-widen', 'RFC 0128 §3', 'an advertising host MUST re-emit a received label on the onward hop — silent loss fails (the promise is propagation)'),
136
138
  ).toBe(true);
137
139
  for (const o of onward) {
138
140
  expect(
139
141
  Array.isArray(o.permittedPurposes) && subsetOf(o.permittedPurposes, input),
140
- driver.describe('RFC 0128 §3', `the onward label MUST be a subset of what the host received — got ${JSON.stringify(o.permittedPurposes)} vs input ${JSON.stringify(input)} (widening is the testable violation)`),
142
+ req('openwop.it.purpose-propagation.a-forwarded-label-survives-the-received-set-re-emit-may-narrow-must-not-widen', 'RFC 0128 §3', `the onward label MUST be a subset of what the host received — got ${JSON.stringify(o.permittedPurposes)} vs input ${JSON.stringify(input)} (widening is the testable violation)`),
141
143
  ).toBe(true);
142
144
  }
143
145
  });
@@ -150,13 +152,13 @@ describe.skipIf(HTTP_SKIP)('purpose-propagation: two-hop onward behavior (seam-g
150
152
  { id: 'b', permittedPurposes: ['analytics'], data: { k: 2 } },
151
153
  ],
152
154
  });
153
- if (res === null) return;
155
+ if (res === null) return softSkip('blocked', 'precondition not met — `res === null` returned early (seam, prior step, or fixture unavailable)');
154
156
  const onward = res.onward ?? [];
155
- expect(onward.length > 0, driver.describe('RFC 0128 §3', 'a merge of forwardable labelled inputs MUST produce an onward emission')).toBe(true);
157
+ expect(onward.length > 0, req('openwop.it.purpose-propagation.a-derived-merged-output-arrives-the-intersection-of-contributing-labelled-inputs', 'RFC 0128 §3', 'a merge of forwardable labelled inputs MUST produce an onward emission')).toBe(true);
156
158
  for (const o of onward) {
157
159
  expect(
158
160
  subsetOf(o.permittedPurposes, ['analytics']),
159
- driver.describe('RFC 0128 §3', `a derived output MUST NOT carry a purpose absent from any contributing labelled input — transformation does not launder a grant; got ${JSON.stringify(o.permittedPurposes)}, allowed ⊆ ["analytics"]`),
161
+ req('openwop.it.purpose-propagation.a-derived-merged-output-arrives-the-intersection-of-contributing-labelled-inputs', 'RFC 0128 §3', `a derived output MUST NOT carry a purpose absent from any contributing labelled input — transformation does not launder a grant; got ${JSON.stringify(o.permittedPurposes)}, allowed ⊆ ["analytics"]`),
160
162
  ).toBe(true);
161
163
  }
162
164
  });
@@ -169,11 +171,11 @@ describe.skipIf(HTTP_SKIP)('purpose-propagation: two-hop onward behavior (seam-g
169
171
  { id: 'b', data: { k: 2 } },
170
172
  ],
171
173
  });
172
- if (res === null) return;
174
+ if (res === null) return softSkip('blocked', 'precondition not met — `res === null` returned early (seam, prior step, or fixture unavailable)');
173
175
  for (const o of res.onward ?? []) {
174
176
  expect(
175
177
  subsetOf(o.permittedPurposes, ['analytics']),
176
- driver.describe('RFC 0128 §3', 'an unlabelled input asserts no constraint — the derived label is still bounded by the labelled input(s)'),
178
+ req('openwop.it.purpose-propagation.an-unlabelled-input-adds-no-constraint-to-a-merge', 'RFC 0128 §3', 'an unlabelled input asserts no constraint — the derived label is still bounded by the labelled input(s)'),
177
179
  ).toBe(true);
178
180
  }
179
181
  });
@@ -186,20 +188,20 @@ describe.skipIf(HTTP_SKIP)('purpose-propagation: two-hop onward behavior (seam-g
186
188
  { id: 'control', data: { k: 1 } },
187
189
  ],
188
190
  });
189
- if (res === null) return;
191
+ if (res === null) return softSkip('blocked', 'precondition not met — `res === null` returned early (seam, prior step, or fixture unavailable)');
190
192
 
191
193
  const onwardIds = (res.onward ?? []).map((o) => o.recordId);
192
194
  expect(
193
195
  !onwardIds.includes('blocked'),
194
- driver.describe('RFC 0128 §3', 'permittedPurposes: [] means no onward use — a conformant host MUST NOT forward []-labelled data to a further sink at all'),
196
+ req('openwop.it.purpose-propagation.labelled-data-is-fail-closed-dropped-from-onward-emission-positive-control-unlab', 'RFC 0128 §3', 'permittedPurposes: [] means no onward use — a conformant host MUST NOT forward []-labelled data to a further sink at all'),
195
197
  ).toBe(true);
196
198
  expect(
197
199
  (res.dropped ?? []).includes('blocked'),
198
- driver.describe('RFC 0128 §3', 'the []-labelled record MUST be reported dropped (fail-closed, observable)'),
200
+ req('openwop.it.purpose-propagation.labelled-data-is-fail-closed-dropped-from-onward-emission-positive-control-unlab', 'RFC 0128 §3', 'the []-labelled record MUST be reported dropped (fail-closed, observable)'),
199
201
  ).toBe(true);
200
202
  expect(
201
203
  onwardIds.includes('control'),
202
- driver.describe('RFC 0128 §3', 'the unlabelled twin (positive control) MUST forward — proving the non-arrival of the []-labelled record is the rule firing, not a dead seam'),
204
+ req('openwop.it.purpose-propagation.labelled-data-is-fail-closed-dropped-from-onward-emission-positive-control-unlab', 'RFC 0128 §3', 'the unlabelled twin (positive control) MUST forward — proving the non-arrival of the []-labelled record is the rule firing, not a dead seam'),
203
205
  ).toBe(true);
204
206
  });
205
207
  });
@@ -16,6 +16,8 @@
16
16
  import { describe, it, expect } from 'vitest';
17
17
  import { driver } from '../lib/driver.js';
18
18
  import { discoveryFamilies } from '../lib/discovery-capabilities.js';
19
+ import { req } from '../lib/requirement-ids.js';
20
+ import { softSkip } from '../lib/soft-skip.js';
19
21
 
20
22
  interface DiscoveryDoc {
21
23
  capabilities?: Record<string, unknown>;
@@ -32,10 +34,10 @@ async function readCap(): Promise<Record<string, unknown> | null> {
32
34
  describe('queue-ack-nack-dlq: advertisement shape (RFC 0017)', () => {
33
35
  it('capabilities.queueBus is either absent or a well-formed object', async () => {
34
36
  const cap = await readCap();
35
- if (cap === null) return; // host doesn't advertise — skip
37
+ if (cap === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap === null` returned early (host doesn\'t advertise — skip)'); // host doesn't advertise — skip
36
38
  expect(
37
39
  typeof cap.supported,
38
- driver.describe(
40
+ req('openwop.it.queue-ack-nack-dlq.capabilities-queuebus-is-either-absent-or-a-well-formed-object',
39
41
  'capabilities.schema.json §queueBus',
40
42
  'capabilities.queueBus.supported MUST be a boolean when present',
41
43
  ),
@@ -44,17 +46,17 @@ describe('queue-ack-nack-dlq: advertisement shape (RFC 0017)', () => {
44
46
 
45
47
  it('deadLetterSupported is a boolean when set', async () => {
46
48
  const cap = await readCap();
47
- if (!cap || cap.supported !== true) return;
49
+ if (!cap || cap.supported !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!cap || cap.supported !== true` returned early');
48
50
  const subParts = ["deadLetterSupported"];
49
51
  let sub: unknown = cap;
50
52
  for (const p of subParts) {
51
53
  if (sub && typeof sub === 'object') sub = (sub as Record<string, unknown>)[p];
52
54
  else { sub = undefined; break; }
53
55
  }
54
- if (sub === undefined) return; // optional sub-field
56
+ if (sub === undefined) return softSkip('blocked', 'precondition not met — `sub === undefined` returned early (optional sub-field) (seam, prior step, or fixture unavailable)'); // optional sub-field
55
57
  expect(
56
58
  typeof sub,
57
- driver.describe(
59
+ req('openwop.it.queue-ack-nack-dlq.deadlettersupported-is-a-boolean-when-set',
58
60
  'RFC 0017 §A',
59
61
  'queueBus.deadLetterSupported MUST be boolean when present',
60
62
  ),
@@ -69,7 +71,7 @@ async function call(op: string, args: Record<string, unknown>) {
69
71
  describe('queue-ack-nack-dlq: behavioral (RFC 0017 §B point 2 — nack + DLQ)', () => {
70
72
  it('nack(requeue=true) → message is redelivered on next consume with deliveryCount incremented', async () => {
71
73
  const probe = await call('consume', { subject: '__probe__' });
72
- if (probe.status === 404) return;
74
+ if (probe.status === 404) return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (seam, prior step, or fixture unavailable)');
73
75
  const subject = `q-nack-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
74
76
  await call('publish', { subject, payload: { v: 'redeliver-me' } });
75
77
 
@@ -83,18 +85,18 @@ describe('queue-ack-nack-dlq: behavioral (RFC 0017 §B point 2 — nack + DLQ)',
83
85
  const secondBody = second.json as { found?: boolean; payload?: unknown; deliveryCount?: number };
84
86
  expect(
85
87
  secondBody.found,
86
- driver.describe('RFC 0017 §B point 2', 'nack(requeue=true) MUST make the message available to next consume'),
88
+ req('openwop.it.queue-ack-nack-dlq.nack-requeue-true-message-is-redelivered-on-next-consume-with-deliverycount-incr', 'RFC 0017 §B point 2', 'nack(requeue=true) MUST make the message available to next consume'),
87
89
  ).toBe(true);
88
90
  expect(secondBody.payload).toEqual(firstBody.payload);
89
91
  expect(
90
92
  secondBody.deliveryCount,
91
- driver.describe('RFC 0017 §B point 2', 'redelivered message MUST have incremented deliveryCount'),
93
+ req('openwop.it.queue-ack-nack-dlq.nack-requeue-true-message-is-redelivered-on-next-consume-with-deliverycount-incr', 'RFC 0017 §B point 2', 'redelivered message MUST have incremented deliveryCount'),
92
94
  ).toBe(2);
93
95
  });
94
96
 
95
97
  it('deadLetter → message appears on the <subject>.dlq subject; original subject is empty', async () => {
96
98
  const probe = await call('consume', { subject: '__probe__' });
97
- if (probe.status === 404) return;
99
+ if (probe.status === 404) return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (seam, prior step, or fixture unavailable)');
98
100
  const subject = `q-dlq-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
99
101
  await call('publish', { subject, payload: { v: 'poison' } });
100
102
 
@@ -114,7 +116,7 @@ describe('queue-ack-nack-dlq: behavioral (RFC 0017 §B point 2 — nack + DLQ)',
114
116
  const dlqBody = dlqMsg.json as { found?: boolean; payload?: { original?: unknown; deadLetterReason?: string } };
115
117
  expect(
116
118
  dlqBody.found,
117
- driver.describe('RFC 0017 §B point 2', 'deadLetter MUST route the message to the <subject>.dlq subject'),
119
+ req('openwop.it.queue-ack-nack-dlq.deadletter-message-appears-on-the-subject-dlq-subject-original-subject-is-empty', 'RFC 0017 §B point 2', 'deadLetter MUST route the message to the <subject>.dlq subject'),
118
120
  ).toBe(true);
119
121
  expect(dlqBody.payload?.deadLetterReason).toBe('unparseable_payload');
120
122
  expect(dlqBody.payload?.original).toEqual({ v: 'poison' });
@@ -12,6 +12,8 @@
12
12
  import { describe, it, expect } from 'vitest';
13
13
  import { driver } from '../lib/driver.js';
14
14
  import { discoveryFamilies } from '../lib/discovery-capabilities.js';
15
+ import { req } from '../lib/requirement-ids.js';
16
+ import { softSkip } from '../lib/soft-skip.js';
15
17
 
16
18
  interface DiscoveryDoc {
17
19
  capabilities?: Record<string, unknown>;
@@ -32,10 +34,10 @@ async function call(tenantId: string, op: string, args: Record<string, unknown>)
32
34
  describe('queue-cross-tenant-isolation: advertisement shape (RFC 0017)', () => {
33
35
  it('capabilities.queueBus is either absent or a well-formed object', async () => {
34
36
  const cap = await readCap();
35
- if (cap === null) return;
37
+ if (cap === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap === null` returned early');
36
38
  expect(
37
39
  typeof cap.supported,
38
- driver.describe(
40
+ req('openwop.it.queue-cross-tenant-isolation.capabilities-queuebus-is-either-absent-or-a-well-formed-object',
39
41
  'capabilities.schema.json §queueBus',
40
42
  'capabilities.queueBus.supported MUST be a boolean when present',
41
43
  ),
@@ -46,19 +48,19 @@ describe('queue-cross-tenant-isolation: advertisement shape (RFC 0017)', () => {
46
48
  describe('queue-cross-tenant-isolation: behavioral (RFC 0017 §C)', () => {
47
49
  it('publish under tenant A → consume under tenant B returns not-found', async () => {
48
50
  const cap = await readCap();
49
- if (!cap || cap.supported !== true) return;
51
+ if (!cap || cap.supported !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!cap || cap.supported !== true` returned early');
50
52
  const topic = `xtenant.${Date.now()}.${Math.random().toString(36).slice(2, 6)}`;
51
53
 
52
54
  const pubRes = await call('tenant-a', 'publish', { topic, payload: { hello: 'A' } });
53
- if (pubRes.status === 404) return;
54
- expect(pubRes.status, 'publish MUST succeed').toBe(200);
55
+ if (pubRes.status === 404) return softSkip('blocked', 'precondition not met — `pubRes.status === 404` returned early (seam, prior step, or fixture unavailable)');
56
+ expect(pubRes.status, req('openwop.it.queue-cross-tenant-isolation.publish-under-tenant-a-consume-under-tenant-b-returns-not-found', 'SECURITY/invariants.yaml queue-cross-tenant-isolation', 'publish MUST succeed')).toBe(200);
55
57
 
56
58
  const consRes = await call('tenant-b', 'consume', { topic, timeoutMs: 100 });
57
59
  expect(consRes.status).toBe(200);
58
60
  const body = consRes.json as { found?: boolean };
59
61
  expect(
60
62
  body.found,
61
- driver.describe(
63
+ req('openwop.it.queue-cross-tenant-isolation.publish-under-tenant-a-consume-under-tenant-b-returns-not-found',
62
64
  'SECURITY/invariants.yaml queue-cross-tenant-isolation',
63
65
  'tenant B MUST NOT consume tenant A messages on the same topic',
64
66
  ),
@@ -16,6 +16,8 @@
16
16
  import { describe, it, expect } from 'vitest';
17
17
  import { driver } from '../lib/driver.js';
18
18
  import { discoveryFamilies } from '../lib/discovery-capabilities.js';
19
+ import { req } from '../lib/requirement-ids.js';
20
+ import { softSkip } from '../lib/soft-skip.js';
19
21
 
20
22
  interface DiscoveryDoc {
21
23
  capabilities?: Record<string, unknown>;
@@ -32,10 +34,10 @@ async function readCap(): Promise<Record<string, unknown> | null> {
32
34
  describe('queue-publish-consume-roundtrip: advertisement shape (RFC 0017)', () => {
33
35
  it('capabilities.queueBus is either absent or a well-formed object', async () => {
34
36
  const cap = await readCap();
35
- if (cap === null) return; // host doesn't advertise — skip
37
+ if (cap === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap === null` returned early (host doesn\'t advertise — skip)'); // host doesn't advertise — skip
36
38
  expect(
37
39
  typeof cap.supported,
38
- driver.describe(
40
+ req('openwop.it.queue-publish-consume-roundtrip.capabilities-queuebus-is-either-absent-or-a-well-formed-object',
39
41
  'capabilities.schema.json §queueBus',
40
42
  'capabilities.queueBus.supported MUST be a boolean when present',
41
43
  ),
@@ -50,7 +52,7 @@ async function call(op: string, args: Record<string, unknown>) {
50
52
  describe('queue-publish-consume-roundtrip: behavioral (RFC 0017 §B point 2)', () => {
51
53
  it('publish → consume returns the same payload + subject', async () => {
52
54
  const probe = await call('consume', { subject: '__probe__' });
53
- if (probe.status === 404) return; // seam not exposed
55
+ if (probe.status === 404) return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (seam not exposed) (seam, prior step, or fixture unavailable)'); // seam not exposed
54
56
  const subject = `q-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
55
57
  const payload = { event: 'order.created', orderId: 42 };
56
58
  const pub = await call('publish', { subject, payload });
@@ -59,18 +61,18 @@ describe('queue-publish-consume-roundtrip: behavioral (RFC 0017 §B point 2)', (
59
61
  const got = await call('consume', { subject });
60
62
  expect(got.status).toBe(200);
61
63
  const body = got.json as { found?: boolean; subject?: string; payload?: unknown; deliveryToken?: string };
62
- expect(body.found, 'consume MUST find the just-published message').toBe(true);
64
+ expect(body.found, req('openwop.it.queue-publish-consume-roundtrip.publish-consume-returns-the-same-payload-subject', 'RFC 0017 §B point 2', 'consume MUST find the just-published message')).toBe(true);
63
65
  expect(body.subject).toBe(subject);
64
66
  expect(
65
67
  body.payload,
66
- driver.describe('RFC 0017 §B point 2', 'consume MUST return the exact published payload'),
68
+ req('openwop.it.queue-publish-consume-roundtrip.publish-consume-returns-the-same-payload-subject', 'RFC 0017 §B point 2', 'consume MUST return the exact published payload'),
67
69
  ).toEqual(payload);
68
- expect(typeof body.deliveryToken, 'consume MUST return a deliveryToken for ack/nack').toBe('string');
70
+ expect(typeof body.deliveryToken, req('openwop.it.queue-publish-consume-roundtrip.publish-consume-returns-the-same-payload-subject', 'RFC 0017 §B point 2', 'consume MUST return a deliveryToken for ack/nack')).toBe('string');
69
71
  });
70
72
 
71
73
  it('ack removes the message; subsequent consume on empty queue returns found:false', async () => {
72
74
  const probe = await call('consume', { subject: '__probe__' });
73
- if (probe.status === 404) return;
75
+ if (probe.status === 404) return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (seam, prior step, or fixture unavailable)');
74
76
  const subject = `q-ack-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
75
77
  await call('publish', { subject, payload: { v: 1 } });
76
78
  const got = await call('consume', { subject });
@@ -83,7 +85,7 @@ describe('queue-publish-consume-roundtrip: behavioral (RFC 0017 §B point 2)', (
83
85
  const emptyBody = empty.json as { found?: boolean };
84
86
  expect(
85
87
  emptyBody.found,
86
- driver.describe('RFC 0017 §B point 2', 'consume after ack MUST surface as found:false'),
88
+ req('openwop.it.queue-publish-consume-roundtrip.ack-removes-the-message-subsequent-consume-on-empty-queue-returns-found-false', 'RFC 0017 §B point 2', 'consume after ack MUST surface as found:false'),
87
89
  ).toBe(false);
88
90
  });
89
91
  });
@@ -23,6 +23,7 @@
23
23
  import { describe, it, expect } from 'vitest';
24
24
  import { softSkip } from '../lib/soft-skip.js';
25
25
  import { driver } from '../lib/driver.js';
26
+ import { req } from '../lib/requirement-ids.js';
26
27
 
27
28
  const ALLOWED_SCOPES = new Set(['tenant', 'route', 'global', 'key']);
28
29
  const FORCE = process.env.OPENWOP_FORCE_RATE_LIMIT === 'true';
@@ -48,7 +49,7 @@ describe('rate-limit-envelope: 429 conforms to canonical shape', () => {
48
49
  }
49
50
 
50
51
  const retryAfter = last.headers.get('retry-after');
51
- expect(retryAfter, driver.describe(
52
+ expect(retryAfter, req('openwop.it.rate-limit-envelope.when-a-429-is-observed-the-response-body-satisfies-the-v1-1-contract',
52
53
  'rest-endpoints.md §"429 Too Many Requests envelope"',
53
54
  '429 response MUST set Retry-After header',
54
55
  )).not.toBeNull();
@@ -60,7 +61,7 @@ describe('rate-limit-envelope: 429 conforms to canonical shape', () => {
60
61
  [k: string]: unknown;
61
62
  };
62
63
 
63
- expect(body.error, driver.describe(
64
+ expect(body.error, req('openwop.it.rate-limit-envelope.when-a-429-is-observed-the-response-body-satisfies-the-v1-1-contract',
64
65
  'rest-endpoints.md §"429 Too Many Requests envelope"',
65
66
  "429 body MUST carry error === 'rate_limited'",
66
67
  )).toBe('rate_limited');
@@ -70,13 +71,13 @@ describe('rate-limit-envelope: 429 conforms to canonical shape', () => {
70
71
  const topKeys = Object.keys(body).filter(
71
72
  (k) => !['error', 'message', 'details'].includes(k),
72
73
  );
73
- expect(topKeys, driver.describe(
74
+ expect(topKeys, req('openwop.it.rate-limit-envelope.when-a-429-is-observed-the-response-body-satisfies-the-v1-1-contract',
74
75
  'error-envelope.schema.json additionalProperties: false',
75
76
  '429 envelope MUST NOT carry top-level keys outside {error, message, details}',
76
77
  )).toEqual([]);
77
78
 
78
79
  if (body.details?.scope !== undefined) {
79
- expect(ALLOWED_SCOPES.has(body.details.scope), driver.describe(
80
+ expect(ALLOWED_SCOPES.has(body.details.scope), req('openwop.it.rate-limit-envelope.when-a-429-is-observed-the-response-body-satisfies-the-v1-1-contract',
80
81
  'rest-endpoints.md §"429 Too Many Requests envelope"',
81
82
  "details.scope MUST be one of {'tenant','route','global','key'} when present",
82
83
  )).toBe(true);
@@ -89,7 +90,7 @@ describe('rate-limit-envelope: 429 conforms to canonical shape', () => {
89
90
  retryAfterSec > 0
90
91
  ) {
91
92
  const diff = Math.abs(body.details.retryAfterMs / 1000 - retryAfterSec);
92
- expect(diff, driver.describe(
93
+ expect(diff, req('openwop.it.rate-limit-envelope.when-a-429-is-observed-the-response-body-satisfies-the-v1-1-contract',
93
94
  'rest-endpoints.md §"429 Too Many Requests envelope"',
94
95
  'details.retryAfterMs MUST be consistent with Retry-After header (±1s)',
95
96
  )).toBeLessThanOrEqual(1.5);
@@ -55,6 +55,8 @@ import {
55
55
  getCanary,
56
56
  } from '../lib/canaries.js';
57
57
  import { isFixtureAdvertised } from '../lib/fixtures.js';
58
+ import { req } from '../lib/requirement-ids.js';
59
+ import { softSkip } from '../lib/soft-skip.js';
58
60
 
59
61
  const NOOP_WORKFLOW_ID = 'conformance-noop';
60
62
  const SKIP_NO_NOOP = !isFixtureAdvertised(NOOP_WORKFLOW_ID);
@@ -71,7 +73,7 @@ describe('redaction: /.well-known/openwop secrets+aiProviders shape contract', (
71
73
 
72
74
  if (secrets === undefined) {
73
75
  // Optional v1 field — hosts MAY omit. Spec-allowed; nothing to assert.
74
- return;
76
+ return softSkip('blocked', 'precondition not met — `secrets === undefined` returned early (Optional v1 field — hosts MAY omit. Spec-allowed; nothing to assert.) (seam, prior step, or fixture unavailable)');
75
77
  }
76
78
 
77
79
  // Per capabilities.schema.json: `secrets.supported` is REQUIRED
@@ -81,7 +83,7 @@ describe('redaction: /.well-known/openwop secrets+aiProviders shape contract', (
81
83
  scopes?: unknown;
82
84
  resolution?: unknown;
83
85
  };
84
- expect(typeof s.supported, driver.describe(
86
+ expect(typeof s.supported, req('openwop.it.redaction.secrets-is-well-formed-regardless-of-supported-value',
85
87
  'capabilities.md §"Secrets"',
86
88
  'secrets.supported MUST be a boolean',
87
89
  )).toBe('boolean');
@@ -90,12 +92,12 @@ describe('redaction: /.well-known/openwop secrets+aiProviders shape contract', (
90
92
  // (a host claiming secrets must declare at least one scope) AND
91
93
  // resolution MUST be 'host-managed' (only allowed value in v1.x).
92
94
  if (s.supported === true) {
93
- expect(Array.isArray(s.scopes), driver.describe(
95
+ expect(Array.isArray(s.scopes), req('openwop.it.redaction.secrets-is-well-formed-regardless-of-supported-value',
94
96
  'capabilities.md §"Secrets"',
95
97
  'when secrets.supported is true, scopes MUST be a string[]',
96
98
  )).toBe(true);
97
99
  const scopes = s.scopes as string[];
98
- expect(scopes.length, driver.describe(
100
+ expect(scopes.length, req('openwop.it.redaction.secrets-is-well-formed-regardless-of-supported-value',
99
101
  'capabilities.md §"Secrets"',
100
102
  'when secrets.supported is true, scopes MUST be non-empty',
101
103
  )).toBeGreaterThanOrEqual(1);
@@ -105,7 +107,7 @@ describe('redaction: /.well-known/openwop secrets+aiProviders shape contract', (
105
107
  // sub-tenant scope — additive; hosts that advertise it (e.g. MyndHyve) are conformant.
106
108
  expect(['tenant', 'user', 'run', 'workspace']).toContain(scope);
107
109
  }
108
- expect(s.resolution, driver.describe(
110
+ expect(s.resolution, req('openwop.it.redaction.secrets-is-well-formed-regardless-of-supported-value',
109
111
  'capabilities.md §"Secrets"',
110
112
  'resolution MUST be "host-managed" in v1.x',
111
113
  )).toBe('host-managed');
@@ -119,7 +121,7 @@ describe('redaction: /.well-known/openwop secrets+aiProviders shape contract', (
119
121
  | { supported?: unknown; byok?: unknown }
120
122
  | undefined;
121
123
 
122
- if (ap === undefined) return; // Optional v1 field — hosts MAY omit.
124
+ if (ap === undefined) return softSkip('blocked', 'precondition not met — `ap === undefined` returned early (Optional v1 field — hosts MAY omit.) (seam, prior step, or fixture unavailable)'); // Optional v1 field — hosts MAY omit.
123
125
 
124
126
  if (ap.supported !== undefined) {
125
127
  expect(Array.isArray(ap.supported)).toBe(true);
@@ -130,7 +132,7 @@ describe('redaction: /.well-known/openwop secrets+aiProviders shape contract', (
130
132
  expect(Array.isArray(ap.byok)).toBe(true);
131
133
  const byok = ap.byok as string[];
132
134
  for (const id of byok) {
133
- expect(supportedSet.has(id), driver.describe(
135
+ expect(supportedSet.has(id), req('openwop.it.redaction.aiproviders-is-well-formed-when-present-byok-supported',
134
136
  'capabilities.md §"aiProviders"',
135
137
  `byok entry "${id}" MUST appear in aiProviders.supported`,
136
138
  )).toBe(true);
@@ -177,7 +179,7 @@ describe('redaction: 401 response MUST NOT echo invalid Bearer token (NFR-7)', (
177
179
  },
178
180
  );
179
181
  const responseText = captureToText(res.json) + '\n' + res.text;
180
- expect(responseText).not.toContain(CANARY_MARKER);
182
+ expect(responseText, req('openwop.it.redaction.the-marker-substring-alone-never-appears-in-a-401-body-universal', 'capabilities.md', 'the marker substring alone never appears in a 401 body (universal)')).not.toContain(CANARY_MARKER);
181
183
  });
182
184
  });
183
185
 
@@ -193,8 +195,8 @@ describe.skipIf(SKIP_NO_NOOP)('redaction: credentialRef value MUST NOT appear in
193
195
  if (supported !== true) {
194
196
  // Spec-allowed — this scenario only applies to hosts that opt
195
197
  // into BYOK. Pass trivially.
196
- expect(supported).not.toBe(true);
197
- return;
198
+ expect(supported, req('openwop.it.redaction.skips-when-host-does-not-advertise-secrets-supported', 'capabilities.md', 'skips when host does NOT advertise secrets.supported')).not.toBe(true);
199
+ return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `supported !== true` returned early');
198
200
  }
199
201
 
200
202
  // Real assertion path: plant a canary as credentialRef on a noop
@@ -217,7 +219,7 @@ describe.skipIf(SKIP_NO_NOOP)('redaction: credentialRef value MUST NOT appear in
217
219
  // full scenario; if the key is missing or invalid, the suite's
218
220
  // earlier auth scenarios already catch that. Bail with a non-
219
221
  // assertion — this scenario is opt-in.
220
- return;
222
+ return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `create.status !== 201` returned early (Auth-required hosts may 401 here without an API key. The conformance suite is expected to provide OPENWOP_API_KEY for th…');
221
223
  }
222
224
  const runId = (create.json as { runId: string }).runId;
223
225
 
@@ -47,6 +47,8 @@ import { driver } from '../lib/driver.js';
47
47
  import { CANARIES, CANARY_MARKER, getCanary } from '../lib/canaries.js';
48
48
  import { pollUntilTerminal } from '../lib/polling.js';
49
49
  import { isFixtureAdvertised } from '../lib/fixtures.js';
50
+ import { req } from '../lib/requirement-ids.js';
51
+ import { softSkip } from '../lib/soft-skip.js';
50
52
 
51
53
  const NOOP_WORKFLOW_ID = 'conformance-noop';
52
54
  const SKIP_NO_NOOP = !isFixtureAdvertised(NOOP_WORKFLOW_ID);
@@ -66,7 +68,7 @@ describe.skipIf(SKIP_NO_NOOP)('redaction-adversarial: workflow input canaries',
66
68
  userSuppliedField: canary,
67
69
  },
68
70
  });
69
- expect(create.status, driver.describe('rest-endpoints.md', 'noop run create returns 201')).toBe(201);
71
+ expect(create.status, req('openwop.it.redactionAdversarial.openai-shaped-canary-in-arbitrary-input-field-must-not-appear-verbatim-in-any-ev', 'rest-endpoints.md', 'noop run create returns 201')).toBe(201);
70
72
  const runId = (create.json as { runId: string }).runId;
71
73
 
72
74
  await pollUntilTerminal(runId);
@@ -76,15 +78,15 @@ describe.skipIf(SKIP_NO_NOOP)('redaction-adversarial: workflow input canaries',
76
78
  );
77
79
  if (events.status !== 200) {
78
80
  // Host doesn't expose polling; SSE is the alternative. Skip-equivalent.
79
- return;
81
+ return softSkip('blocked', 'precondition not met — `events.status !== 200` returned early (Host doesn\'t expose polling; SSE is the alternative. Skip-equivalent.) (seam, prior step, or fixture unavailable)');
80
82
  }
81
83
 
82
84
  const eventsText = events.text;
83
- expect(eventsText.includes(canary), driver.describe(
85
+ expect(eventsText.includes(canary), req('openwop.it.redactionAdversarial.openai-shaped-canary-in-arbitrary-input-field-must-not-appear-verbatim-in-any-ev',
84
86
  'capabilities.md §"Secrets" + NFR-7',
85
87
  'canary value submitted as workflow input MUST NOT appear verbatim in event poll response',
86
88
  )).toBe(false);
87
- expect(eventsText.includes(CANARY_MARKER), driver.describe(
89
+ expect(eventsText.includes(CANARY_MARKER), req('openwop.it.redactionAdversarial.openai-shaped-canary-in-arbitrary-input-field-must-not-appear-verbatim-in-any-ev',
88
90
  'NFR-7',
89
91
  'canary marker substring MUST NOT appear in event poll response',
90
92
  )).toBe(false);
@@ -100,7 +102,7 @@ describe('redaction-adversarial: error-envelope canaries', () => {
100
102
  });
101
103
  expect(
102
104
  [400, 404].includes(res.status),
103
- driver.describe('rest-endpoints.md', 'unknown workflowId returns 400 or 404'),
105
+ req('openwop.it.redactionAdversarial.jwt-shaped-canary-in-workflowid-of-a-404-failing-request-must-not-echo-verbatim', 'rest-endpoints.md', 'unknown workflowId returns 400 or 404'),
104
106
  ).toBe(true);
105
107
 
106
108
  const responseText = res.text;
@@ -110,7 +112,7 @@ describe('redaction-adversarial: error-envelope canaries', () => {
110
112
  // SECURITY/invariants.yaml `secret-leakage-error-envelope`, the
111
113
  // host SHOULD sanitize entropy-shaped substrings even when echoing
112
114
  // the workflowId.
113
- expect(responseText.includes(canary), driver.describe(
115
+ expect(responseText.includes(canary), req('openwop.it.redactionAdversarial.jwt-shaped-canary-in-workflowid-of-a-404-failing-request-must-not-echo-verbatim',
114
116
  'SECURITY/invariants.yaml secret-leakage-error-envelope',
115
117
  'JWT-shaped canary submitted in workflowId SHOULD NOT echo verbatim in 4xx error envelope',
116
118
  )).toBe(false);
@@ -137,9 +139,9 @@ describe.skipIf(SKIP_NO_NOOP)('redaction-adversarial: bearer-shaped strings roun
137
139
  const events = await driver.get(
138
140
  `/v1/runs/${encodeURIComponent(runId)}/events/poll`,
139
141
  );
140
- if (events.status !== 200) return;
142
+ if (events.status !== 200) return softSkip('blocked', 'precondition not met — `events.status !== 200` returned early (seam, prior step, or fixture unavailable)');
141
143
 
142
- expect(events.text.includes(canary), driver.describe(
144
+ expect(events.text.includes(canary), req('openwop.it.redactionAdversarial.anthropic-shaped-canary-embedded-in-non-credential-input-must-not-appear-verbati',
143
145
  'capabilities.md §"Secrets" + NFR-7',
144
146
  'Anthropic-shaped canary embedded in arbitrary input MUST NOT appear verbatim in events',
145
147
  )).toBe(false);
@@ -153,7 +155,7 @@ describe('redaction-adversarial: every canary fixture carries the marker substri
153
155
  // If this fails the canary harness has a bug — every other
154
156
  // adversarial scenario depends on this property.
155
157
  for (const c of CANARIES) {
156
- expect(c.value.includes(CANARY_MARKER), driver.describe(
158
+ expect(c.value.includes(CANARY_MARKER), req('openwop.it.redactionAdversarial.every-canary-in-canaries-has-the-marker-substring',
157
159
  'lib/canaries.ts',
158
160
  `every canary fixture MUST contain CANARY_MARKER (offender label: ${c.label})`,
159
161
  )).toBe(true);
@@ -28,6 +28,7 @@
28
28
  import { describe, it, expect } from 'vitest';
29
29
  import { softSkip } from '../lib/soft-skip.js';
30
30
  import { createHash, createPublicKey, verify as cryptoVerify } from 'node:crypto';
31
+ import { req } from '../lib/requirement-ids.js';
31
32
 
32
33
  const REGISTRY_BASE = 'https://packs.openwop.dev';
33
34
  const ENABLED = process.env.OPENWOP_TEST_PUBLIC_REGISTRY === 'true';
@@ -59,7 +60,7 @@ describe('registry-public: packs.openwop.dev discovery document', () => {
59
60
  }
60
61
 
61
62
  const res = await get('/.well-known/openwop-registry');
62
- expect(res.status).toBe(200);
63
+ expect(res.status, req('openwop.it.registry-public.get-well-known-openwop-registry-returns-a-valid-discovery-payload', 'spec/v1/registry-operations.md', 'GET /.well-known/openwop-registry returns a valid discovery payload')).toBe(200);
63
64
 
64
65
  const body = res.json as {
65
66
  registryVersion?: string;
@@ -105,8 +106,8 @@ describe('registry-public: packs.openwop.dev index', () => {
105
106
  expect(body.packs?.length ?? 0).toBeGreaterThan(0);
106
107
 
107
108
  for (const p of body.packs ?? []) {
108
- expect(p.name, `pack name must match reverse-DNS pattern: ${p.name}`).toMatch(PACK_NAME_RE);
109
- expect(p.latestVersion, `pack version must be semver: ${p.latestVersion}`).toMatch(SEMVER_RE);
109
+ expect(p.name, req('openwop.it.registry-public.get-v1-index-json-returns-a-non-empty-pack-list-with-valid-name-version-shapes', 'spec/v1/registry-operations.md', `pack name must match reverse-DNS pattern: ${p.name}`)).toMatch(PACK_NAME_RE);
110
+ expect(p.latestVersion, req('openwop.it.registry-public.get-v1-index-json-returns-a-non-empty-pack-list-with-valid-name-version-shapes', 'spec/v1/registry-operations.md', `pack version must be semver: ${p.latestVersion}`)).toMatch(SEMVER_RE);
110
111
  }
111
112
  });
112
113
  });
@@ -123,7 +124,7 @@ describe('registry-public: spec-canonical pack manifests resolve', () => {
123
124
  if (!ENABLED) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!ENABLED` returned early');
124
125
 
125
126
  const res = await get(`/v1/packs/${name}/-/${version}.json`);
126
- expect(res.status).toBe(200);
127
+ expect(res.status, req('openwop.it.registry-public.get-v1-packs-json-returns-a-valid-manifest', 'spec/v1/registry-operations.md', 'GET /v1/packs/ /-/ .json returns a valid manifest')).toBe(200);
127
128
 
128
129
  const manifest = res.json as { name?: string; version?: string };
129
130
  expect(manifest.name).toBe(name);
@@ -178,26 +179,26 @@ describe('registry-public: tarball + signature + Ed25519 verify roundtrip', () =
178
179
 
179
180
  // 2. Tarball.
180
181
  const tarball = await getBinary(`/v1/packs/${PACK_NAME}/-/${PACK_VERSION}.tgz`);
181
- expect(tarball.status, 'tarball MUST be retrievable').toBe(200);
182
- expect(tarball.bytes.byteLength, 'tarball MUST be non-empty').toBeGreaterThan(0);
182
+ expect(tarball.status, req('openwop.it.registry-public.tarball-sig-public-key-all-retrievable-sri-matches-ed25519-verifies-for', 'spec/v1/registry-operations.md', 'tarball MUST be retrievable')).toBe(200);
183
+ expect(tarball.bytes.byteLength, req('openwop.it.registry-public.tarball-sig-public-key-all-retrievable-sri-matches-ed25519-verifies-for', 'spec/v1/registry-operations.md', 'tarball MUST be non-empty')).toBeGreaterThan(0);
183
184
 
184
185
  // 3. Detached signature (64-byte raw Ed25519).
185
186
  const sig = await getBinary(`/v1/packs/${PACK_NAME}/-/${PACK_VERSION}.sig`);
186
- expect(sig.status, 'signature MUST be retrievable').toBe(200);
187
- expect(sig.bytes.byteLength, 'Ed25519 detached signature MUST be 64 bytes').toBe(64);
187
+ expect(sig.status, req('openwop.it.registry-public.tarball-sig-public-key-all-retrievable-sri-matches-ed25519-verifies-for', 'spec/v1/registry-operations.md', 'signature MUST be retrievable')).toBe(200);
188
+ expect(sig.bytes.byteLength, req('openwop.it.registry-public.tarball-sig-public-key-all-retrievable-sri-matches-ed25519-verifies-for', 'spec/v1/registry-operations.md', 'Ed25519 detached signature MUST be 64 bytes')).toBe(64);
188
189
 
189
190
  // 4. Public key — fetch from the publisher-declared URL when present,
190
191
  // else fall back to the canonical `/keys/<keyId>.pub` shape.
191
192
  const keyUrl = manifest.signing!.publicKeyUrl ?? `/keys/${manifest.signing!.keyId}.pub`;
192
193
  const keyRes = await getText(keyUrl);
193
- expect(keyRes.status, `public key MUST be retrievable at ${keyUrl}`).toBe(200);
194
+ expect(keyRes.status, req('openwop.it.registry-public.tarball-sig-public-key-all-retrievable-sri-matches-ed25519-verifies-for', 'spec/v1/registry-operations.md', `public key MUST be retrievable at ${keyUrl}`)).toBe(200);
194
195
  const publicKey = createPublicKey(keyRes.body);
195
196
 
196
197
  // 5. SRI integrity check: `sha256-<base64>=` MUST match a fresh
197
198
  // sha256 of the tarball bytes per registry-operations.md.
198
199
  expect(manifest.integrity).toMatch(/^sha256-[A-Za-z0-9+/]+=*$/);
199
200
  const expectedSri = `sha256-${createHash('sha256').update(tarball.bytes).digest('base64')}`;
200
- expect(expectedSri, 'SRI integrity in manifest MUST match a fresh sha256 of the tarball').toBe(
201
+ expect(expectedSri, req('openwop.it.registry-public.tarball-sig-public-key-all-retrievable-sri-matches-ed25519-verifies-for', 'spec/v1/registry-operations.md', 'SRI integrity in manifest MUST match a fresh sha256 of the tarball')).toBe(
201
202
  manifest.integrity,
202
203
  );
203
204
 
@@ -217,7 +218,7 @@ describe('registry-public: tarball + signature + Ed25519 verify roundtrip', () =
217
218
  if (method !== 'ed25519') return softSkip('blocked', 'precondition not met — `method !== \'ed25519\'` returned early (seam, prior step, or fixture unavailable)');
218
219
 
219
220
  const verified = cryptoVerify(null, tarball.bytes, publicKey, sig.bytes);
220
- expect(verified, `Ed25519 signature over ${PACK_NAME}@${PACK_VERSION}.tgz MUST verify against ${manifest.signing!.keyId}`)
221
+ expect(verified, req('openwop.it.registry-public.tarball-sig-public-key-all-retrievable-sri-matches-ed25519-verifies-for', 'spec/v1/registry-operations.md', `Ed25519 signature over ${PACK_NAME}@${PACK_VERSION}.tgz MUST verify against ${manifest.signing!.keyId}`))
221
222
  .toBe(true);
222
223
  });
223
224
  });