@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
@@ -25,6 +25,8 @@ import { describe, it, expect } from 'vitest';
25
25
  import { driver } from '../lib/driver.js';
26
26
  import { type SamlVariant } from '../lib/saml-idp.js';
27
27
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
28
+ import { req } from '../lib/requirement-ids.js';
29
+ import { softSkip } from '../lib/soft-skip.js';
28
30
 
29
31
  const SAML_PROFILE = 'openwop-auth-saml';
30
32
 
@@ -46,10 +48,10 @@ async function readProfiles(): Promise<string[] | null> {
46
48
  describe('auth-saml-profile: advertisement shape (RFC 0050)', () => {
47
49
  it('auth.profiles, when present, is an array of non-empty strings', async () => {
48
50
  const profiles = await readProfiles();
49
- if (profiles === null) return; // host advertises no auth profiles
51
+ if (profiles === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `profiles === null` returned early (host advertises no auth profiles)'); // host advertises no auth profiles
50
52
  expect(
51
53
  Array.isArray(profiles),
52
- driver.describe('auth-profiles.md §Discovery', 'capabilities.auth.profiles MUST be an array'),
54
+ req('openwop.it.auth-saml-profile.auth-profiles-when-present-is-an-array-of-non-empty-strings', 'auth-profiles.md §Discovery', 'capabilities.auth.profiles MUST be an array'),
53
55
  ).toBe(true);
54
56
  for (const p of profiles) {
55
57
  expect(typeof p === 'string' && p.length > 0).toBe(true);
@@ -58,10 +60,10 @@ describe('auth-saml-profile: advertisement shape (RFC 0050)', () => {
58
60
 
59
61
  it('claims openwop-auth-saml as a well-formed profile id when advertised', async () => {
60
62
  const profiles = await readProfiles();
61
- if (profiles === null || !profiles.includes(SAML_PROFILE)) return; // profile not claimed
63
+ if (profiles === null || !profiles.includes(SAML_PROFILE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `profiles === null || !profiles.includes(SAML_PROFILE)` returned early (profile not claimed)'); // profile not claimed
62
64
  expect(
63
65
  profiles.includes(SAML_PROFILE),
64
- driver.describe('RFC 0050 §A', 'openwop-auth-saml MUST appear verbatim in capabilities.auth.profiles when claimed'),
66
+ req('openwop.it.auth-saml-profile.claims-openwop-auth-saml-as-a-well-formed-profile-id-when-advertised', 'RFC 0050 §A', 'openwop-auth-saml MUST appear verbatim in capabilities.auth.profiles when claimed'),
65
67
  ).toBe(true);
66
68
  });
67
69
  });
@@ -83,13 +85,13 @@ describe('auth-saml-profile: assertion validation (RFC 0050 §A — opt-in)', ()
83
85
 
84
86
  it('ACCEPTS a valid signed, in-window, non-wrapped assertion (synthetic IdP required)', async () => {
85
87
  const profiles = await readProfiles();
86
- if (profiles === null || !profiles.includes(SAML_PROFILE)) return; // capability-gated
87
- if (!gated()) return; // opt-in: synthetic-IdP harness not provided
88
+ if (profiles === null || !profiles.includes(SAML_PROFILE)) return softSkip('blocked', 'precondition not met — `profiles === null || !profiles.includes(SAML_PROFILE)` returned early (capability-gated) (seam, prior step, or fixture unavailable)'); // capability-gated
89
+ if (!gated()) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!gated()` returned early (opt-in: synthetic-IdP harness not provided)'); // opt-in: synthetic-IdP harness not provided
88
90
  const res = await driver.post('/v1/host/sample/auth/saml/validate', { idpUrl, variant: 'valid' });
89
- if (res.status === 404) return; // seam unwired
91
+ if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam unwired) (seam, prior step, or fixture unavailable)'); // seam unwired
90
92
  expect(
91
93
  res.status,
92
- driver.describe('RFC 0050 §A', 'a valid signed, in-window, non-wrapped SAML assertion MUST be accepted (2xx)'),
94
+ req('openwop.it.auth-saml-profile.accepts-a-valid-signed-in-window-non-wrapped-assertion-synthetic-idp-required', 'RFC 0050 §A', 'a valid signed, in-window, non-wrapped SAML assertion MUST be accepted (2xx)'),
93
95
  ).toBeLessThan(400);
94
96
  });
95
97
 
@@ -108,11 +110,11 @@ describe('auth-saml-profile: assertion validation (RFC 0050 §A — opt-in)', ()
108
110
  for (const [variant, requirement] of negatives) {
109
111
  it(`REJECTS the ${variant} assertion over the seam (synthetic IdP required)`, async () => {
110
112
  const profiles = await readProfiles();
111
- if (profiles === null || !profiles.includes(SAML_PROFILE)) return; // capability-gated
112
- if (!gated()) return; // opt-in: synthetic-IdP harness not provided
113
+ if (profiles === null || !profiles.includes(SAML_PROFILE)) return softSkip('blocked', 'precondition not met — `profiles === null || !profiles.includes(SAML_PROFILE)` returned early (capability-gated) (seam, prior step, or fixture unavailable)'); // capability-gated
114
+ if (!gated()) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!gated()` returned early (opt-in: synthetic-IdP harness not provided)'); // opt-in: synthetic-IdP harness not provided
113
115
  const res = await driver.post('/v1/host/sample/auth/saml/validate', { idpUrl, variant });
114
- if (res.status === 404) return; // seam unwired
115
- expect(res.status, driver.describe('RFC 0050 §A', requirement)).toBeGreaterThanOrEqual(400);
116
+ if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam unwired) (seam, prior step, or fixture unavailable)'); // seam unwired
117
+ expect(res.status, req('openwop.it.auth-saml-profile.rejects-the-assertion-over-the-seam-synthetic-idp-required', 'RFC 0050 §A', requirement)).toBeGreaterThanOrEqual(400);
116
118
  });
117
119
  }
118
120
  });
@@ -19,6 +19,7 @@ import { describe, it, expect } from 'vitest';
19
19
  import { softSkip } from '../lib/soft-skip.js';
20
20
  import { driver } from '../lib/driver.js';
21
21
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
22
+ import { req } from '../lib/requirement-ids.js';
22
23
 
23
24
  const SCIM_PROFILE = 'openwop-auth-scim';
24
25
 
@@ -43,7 +44,7 @@ describe('auth-scim-profile: advertisement shape (RFC 0050)', () => {
43
44
  if (profiles === null || !profiles.includes(SCIM_PROFILE)) return softSkip('inapplicable', 'profile not claimed');
44
45
  expect(
45
46
  profiles.includes(SCIM_PROFILE),
46
- driver.describe('RFC 0050 §B', 'openwop-auth-scim MUST appear verbatim in capabilities.auth.profiles when claimed'),
47
+ req('openwop.it.auth-scim-profile.claims-openwop-auth-scim-as-a-well-formed-profile-id-when-advertised', 'RFC 0050 §B', 'openwop-auth-scim MUST appear verbatim in capabilities.auth.profiles when claimed'),
47
48
  ).toBe(true);
48
49
  });
49
50
  });
@@ -61,7 +62,7 @@ describe('auth-scim-profile: provisioning roundtrip (RFC 0050 §B — opt-in)',
61
62
  if (res.status === 404) return softSkip('blocked', 'seam unwired');
62
63
  expect(
63
64
  res.status,
64
- driver.describe('RFC 0050 §B', 'a SCIM user provisioning MUST succeed (2xx) and upsert an RFC 0048 principal'),
65
+ req('openwop.it.auth-scim-profile.provisions-a-scim-user-principal-scim-endpoint-required', 'RFC 0050 §B', 'a SCIM user provisioning MUST succeed (2xx) and upsert an RFC 0048 principal'),
65
66
  ).toBeLessThan(400);
66
67
  });
67
68
  });
@@ -42,6 +42,7 @@ import { describe, it, expect } from 'vitest';
42
42
  import { softSkip } from '../lib/soft-skip.js';
43
43
  import { driver } from '../lib/driver.js';
44
44
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
45
+ import { req } from '../lib/requirement-ids.js';
45
46
 
46
47
  const SAML_PROFILE = 'openwop-auth-saml';
47
48
  const SCIM_PROFILE = 'openwop-auth-scim';
@@ -81,7 +82,7 @@ describe('auth-subject-link-key-class: advertisement shape (RFC 0163 §A)', () =
81
82
  // profiles) MUST advertise a subjectLinkKey.
82
83
  expect(
83
84
  typeof auth.subjectLinkKey === 'string' && auth.subjectLinkKey.length > 0,
84
- driver.describe(
85
+ req('openwop.it.auth-subject-link-key-class.subjectlinking-true-requires-a-subjectlinkkey-drawn-from-the-closed-safe-class-e',
85
86
  'auth-profiles.md §Subject linking',
86
87
  'RFC 0163 §A.1: a host advertising capabilities.auth.subjectLinking:true MUST also advertise capabilities.auth.subjectLinkKey',
87
88
  ),
@@ -93,7 +94,7 @@ describe('auth-subject-link-key-class: advertisement shape (RFC 0163 §A)', () =
93
94
  // host cannot name one.
94
95
  expect(
95
96
  (SUBJECT_LINK_KEY_CLASSES as readonly string[]).includes(auth.subjectLinkKey ?? ''),
96
- driver.describe(
97
+ req('openwop.it.auth-subject-link-key-class.subjectlinking-true-requires-a-subjectlinkkey-drawn-from-the-closed-safe-class-e',
97
98
  'auth-profiles.md §Subject linking',
98
99
  `RFC 0163 §A.3: subjectLinkKey MUST be one of ${SUBJECT_LINK_KEY_CLASSES.join(', ')} — a closed enum of allowed CLASSES (not attributes); a mutable/PII key is inexpressible`,
99
100
  ),
@@ -108,7 +109,7 @@ describe('auth-subject-link-key-class: advertisement shape (RFC 0163 §A)', () =
108
109
  const profiles = auth.profiles ?? [];
109
110
  expect(
110
111
  profiles.includes(SAML_PROFILE) && profiles.includes(SCIM_PROFILE),
111
- driver.describe(
112
+ req('openwop.it.auth-subject-link-key-class.subjectlinkkey-is-only-claimed-alongside-both-saml-and-scim-profiles-rides-the-r',
112
113
  'auth-profiles.md §Subject linking',
113
114
  'RFC 0163: subjectLinkKey is meaningful only when both openwop-auth-saml and openwop-auth-scim are advertised (it names the class the two lanes are joined on)',
114
115
  ),
@@ -146,13 +147,13 @@ describe('auth-subject-link-key-class: same-IdP trust root (RFC 0163 §B — beh
146
147
  userName: 'r.smith',
147
148
  });
148
149
  if (provSame.status === 404) return softSkip('blocked', 'SCIM provisioning seam unwired');
149
- expect(provSame.status, driver.describe('auth-profiles.md §Subject linking', 'SCIM provisioning MUST succeed')).toBeLessThan(400);
150
+ expect(provSame.status, req('openwop.it.auth-subject-link-key-class.a-same-idp-link-forms-control-but-a-cross-idp-collision-must-not-link', 'auth-profiles.md §Subject linking', 'SCIM provisioning MUST succeed')).toBeLessThan(400);
150
151
 
151
152
  const sameIdp = await driver.post('/v1/host/sample/auth/saml/validate', { idpUrl: idpAUrl, variant: 'valid', nameId: sameId });
152
153
  if (sameIdp.status === 404) return softSkip('blocked', 'SAML validate seam unwired');
153
154
  expect(
154
155
  (sameIdp.json as { authenticated?: boolean } | undefined)?.authenticated,
155
- driver.describe('auth-profiles.md §Subject linking', 'RFC 0163 §B positive control: a SAML assertion from the SAME IdP trust root as the SCIM lane links and authenticates'),
156
+ req('openwop.it.auth-subject-link-key-class.a-same-idp-link-forms-control-but-a-cross-idp-collision-must-not-link', 'auth-profiles.md §Subject linking', 'RFC 0163 §B positive control: a SAML assertion from the SAME IdP trust root as the SCIM lane links and authenticates'),
156
157
  ).toBe(true);
157
158
 
158
159
  // THE §B.1 MUST — cross-IdP identifier collision. Provision a SCIM user fed
@@ -168,13 +169,13 @@ describe('auth-subject-link-key-class: same-IdP trust root (RFC 0163 §B — beh
168
169
  externalId: collideId,
169
170
  userName: 'a.other',
170
171
  });
171
- expect(provCross.status, driver.describe('auth-profiles.md §Subject linking', 'SCIM provisioning MUST succeed')).toBeLessThan(400);
172
+ expect(provCross.status, req('openwop.it.auth-subject-link-key-class.a-same-idp-link-forms-control-but-a-cross-idp-collision-must-not-link', 'auth-profiles.md §Subject linking', 'SCIM provisioning MUST succeed')).toBeLessThan(400);
172
173
 
173
174
  const crossIdp = await driver.post('/v1/host/sample/auth/saml/validate', { idpUrl: idpBUrl, variant: 'valid', nameId: collideId });
174
175
  if (crossIdp.status === 404) return softSkip('blocked', 'SAML validate seam unwired');
175
176
  expect(
176
177
  (crossIdp.json as { authenticated?: boolean } | undefined)?.authenticated === true,
177
- driver.describe(
178
+ req('openwop.it.auth-subject-link-key-class.a-same-idp-link-forms-control-but-a-cross-idp-collision-must-not-link',
178
179
  'auth-profiles.md §Subject linking',
179
180
  'RFC 0163 §B.1: a SAML assertion from IdP-B colliding on an identifier the SCIM lane provisioned from IdP-A MUST NOT form a cross-lane link (no cross-IdP identifier collision joins two principals)',
180
181
  ),
@@ -27,6 +27,7 @@ import { describe, it, expect } from 'vitest';
27
27
  import { softSkip, seamAbsent } from '../lib/soft-skip.js';
28
28
  import { driver } from '../lib/driver.js';
29
29
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
30
+ import { req } from '../lib/requirement-ids.js';
30
31
 
31
32
  const SAML_PROFILE = 'openwop-auth-saml';
32
33
  const SCIM_PROFILE = 'openwop-auth-scim';
@@ -65,7 +66,7 @@ describe('auth-subject-link: advertisement shape (RFC 0159 §B + RFC 0164 §A.3)
65
66
  const profiles = auth.profiles ?? [];
66
67
  expect(
67
68
  profiles.includes(SAML_PROFILE) && profiles.includes(SCIM_PROFILE),
68
- driver.describe(
69
+ req('openwop.it.auth-subject-link.subjectlinking-true-is-only-claimed-alongside-both-saml-and-scim-profiles',
69
70
  'auth-profiles.md §Subject linking',
70
71
  'a host MUST NOT set capabilities.auth.subjectLinking:true unless BOTH openwop-auth-saml and openwop-auth-scim are advertised',
71
72
  ),
@@ -83,14 +84,14 @@ describe('auth-subject-link: advertisement shape (RFC 0159 §B + RFC 0164 §A.3)
83
84
  // absence alongside both profiles is a conformance FAILURE.
84
85
  expect(
85
86
  auth.subjectLinking,
86
- driver.describe(
87
+ req('openwop.it.auth-subject-link.advertising-both-profiles-requires-subjectlinking-true-a-subjectlinkkey-rfc-0164',
87
88
  'auth-profiles.md §Subject linking (RFC 0164)',
88
89
  'RFC 0164 §A.3: a host advertising BOTH openwop-auth-saml and openwop-auth-scim MUST advertise capabilities.auth.subjectLinking:true — the leaver contract follows the profile pair; omitting the flag is the vulnerable pre-0164 shape',
89
90
  ),
90
91
  ).toBe(true);
91
92
  expect(
92
93
  typeof auth.subjectLinkKey === 'string' && SUBJECT_LINK_KEY_CLASSES.includes(auth.subjectLinkKey),
93
- driver.describe(
94
+ req('openwop.it.auth-subject-link.advertising-both-profiles-requires-subjectlinking-true-a-subjectlinkkey-rfc-0164',
94
95
  'auth-profiles.md §Subject linking (RFC 0164)',
95
96
  `RFC 0164 §A.3 / RFC 0163 §A.1: a host advertising both profiles MUST advertise subjectLinkKey ∈ {${SUBJECT_LINK_KEY_CLASSES.join(', ')}}`,
96
97
  ),
@@ -116,7 +117,7 @@ describe('auth-subject-link: cross-lane deactivation (RFC 0159 §A.3 — opt-in)
116
117
  userName: 'r.smith',
117
118
  });
118
119
  if (provision.status === 404) return softSkip('blocked', 'seam unwired');
119
- expect(provision.status, driver.describe('auth-profiles.md §Subject linking', 'SCIM provisioning MUST succeed')).toBeLessThan(400);
120
+ expect(provision.status, req('openwop.it.auth-subject-link.a-scim-deactivation-fail-closes-the-linked-saml-identity', 'auth-profiles.md §Subject linking', 'SCIM provisioning MUST succeed')).toBeLessThan(400);
120
121
 
121
122
  // 2. A valid SAML assertion whose persistent NameID equals the externalId
122
123
  // links to the same subject and authenticates.
@@ -124,19 +125,19 @@ describe('auth-subject-link: cross-lane deactivation (RFC 0159 §A.3 — opt-in)
124
125
  if (before.status === 404) return softSkip('blocked', 'seam unwired');
125
126
  expect(
126
127
  (before.json as { authenticated?: boolean } | undefined)?.authenticated,
127
- driver.describe('auth-profiles.md §Subject linking', 'a valid linked SAML assertion authenticates before deactivation'),
128
+ req('openwop.it.auth-subject-link.a-scim-deactivation-fail-closes-the-linked-saml-identity', 'auth-profiles.md §Subject linking', 'a valid linked SAML assertion authenticates before deactivation'),
128
129
  ).toBe(true);
129
130
 
130
131
  // 3. SCIM-deactivate the provisioned user.
131
132
  const deactivate = await driver.post('/v1/host/sample/auth/scim/provision', { scimUrl, op: 'deactivate-user', externalId });
132
- expect(deactivate.status, driver.describe('auth-profiles.md §Subject linking', 'SCIM deactivation MUST succeed')).toBeLessThan(400);
133
+ expect(deactivate.status, req('openwop.it.auth-subject-link.a-scim-deactivation-fail-closes-the-linked-saml-identity', 'auth-profiles.md §Subject linking', 'SCIM deactivation MUST succeed')).toBeLessThan(400);
133
134
 
134
135
  // 4. THE CONTRACT: a subsequent SAML assertion for the linked subject MUST
135
136
  // NOT yield an authorized decision (fail-closed across the link).
136
137
  const after = await driver.post('/v1/host/sample/auth/saml/validate', { idpUrl, variant: 'valid', nameId: externalId });
137
138
  expect(
138
139
  (after.json as { authenticated?: boolean } | undefined)?.authenticated === true,
139
- driver.describe(
140
+ req('openwop.it.auth-subject-link.a-scim-deactivation-fail-closes-the-linked-saml-identity',
140
141
  'auth-profiles.md §Subject linking',
141
142
  'RFC 0159 §A.3: after a SCIM deactivation the LINKED SAML identity MUST be denied (a provisioned leaver cannot still SSO in)',
142
143
  ),
@@ -169,9 +170,9 @@ describe('auth-subject-link: link-key hygiene (RFC 0159 §A.2 — opt-in)', () =
169
170
  // Conforming: the host rejected a mutable-key link outright.
170
171
  expect(
171
172
  link.status,
172
- driver.describe('auth-profiles.md §Subject linking', 'RFC 0159 §A.2: a link on a mutable/PII key (email) MUST be rejected'),
173
+ req('openwop.it.auth-subject-link.a-mutable-pii-link-key-email-never-produces-a-cross-lane-pass', 'auth-profiles.md §Subject linking', 'RFC 0159 §A.2: a link on a mutable/PII key (email) MUST be rejected'),
173
174
  ).toBeGreaterThanOrEqual(400);
174
- return;
175
+ return softSkip('blocked', 'precondition not met — `link.status >= 400` returned early (seam, prior step, or fixture unavailable)');
175
176
  }
176
177
 
177
178
  // If the host accepted the request, it MUST NOT have formed a cross-lane
@@ -183,7 +184,7 @@ describe('auth-subject-link: link-key hygiene (RFC 0159 §A.2 — opt-in)', () =
183
184
  const other = await driver.post('/v1/host/sample/auth/saml/validate', { idpUrl, variant: 'valid', nameId: 'idp-op-DIFFERENT' });
184
185
  expect(
185
186
  (other.json as { linkedDenied?: boolean } | undefined)?.linkedDenied === true,
186
- driver.describe(
187
+ req('openwop.it.auth-subject-link.a-mutable-pii-link-key-email-never-produces-a-cross-lane-pass',
187
188
  'auth-profiles.md §Subject linking',
188
189
  'RFC 0159 §A.2: a mutable-key (email) operation MUST NOT drive a cross-lane deny on any opaque subject',
189
190
  ),
@@ -8,6 +8,7 @@
8
8
 
9
9
  import { describe, it, expect } from 'vitest';
10
10
  import { driver } from '../lib/driver.js';
11
+ import { req } from '../lib/requirement-ids.js';
11
12
 
12
13
  const KNOWN_AUTHED_PATH = '/v1/runs';
13
14
 
@@ -19,17 +20,17 @@ describe('auth: missing credential', () => {
19
20
  { authenticated: false },
20
21
  );
21
22
 
22
- expect(res.status, driver.describe(
23
+ expect(res.status, req('openwop.it.auth.returns-401-with-canonical-error-envelope-per-auth-md-3',
23
24
  'auth.md §3',
24
25
  'request without Authorization header MUST return 401',
25
26
  )).toBe(401);
26
27
 
27
28
  const body = res.json as { error?: unknown; message?: unknown } | undefined;
28
- expect(typeof body?.error, driver.describe(
29
+ expect(typeof body?.error, req('openwop.it.auth.returns-401-with-canonical-error-envelope-per-auth-md-3',
29
30
  'auth.md §3 + rest-endpoints.md error envelope',
30
31
  'response body MUST include `error` (machine code) string',
31
32
  )).toBe('string');
32
- expect(typeof body?.message, driver.describe(
33
+ expect(typeof body?.message, req('openwop.it.auth.returns-401-with-canonical-error-envelope-per-auth-md-3',
33
34
  'auth.md §3 + rest-endpoints.md error envelope',
34
35
  'response body MUST include `message` (human description) string',
35
36
  )).toBe('string');
@@ -47,7 +48,7 @@ describe('auth: invalid credential', () => {
47
48
  },
48
49
  );
49
50
 
50
- expect(res.status, driver.describe(
51
+ expect(res.status, req('openwop.it.auth.returns-401-not-200-not-403-per-auth-md-3',
51
52
  'auth.md §3',
52
53
  'request with invalid Authorization MUST return 401, not 403',
53
54
  )).toBe(401);
@@ -26,6 +26,7 @@ import { describe, it, expect } from 'vitest';
26
26
  import { softSkip } from '../lib/soft-skip.js';
27
27
  import { driver } from '../lib/driver.js';
28
28
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
29
+ import { req } from '../lib/requirement-ids.js';
29
30
 
30
31
  interface DiscoveryAuthorization {
31
32
  supported?: boolean;
@@ -50,7 +51,7 @@ describe('authorization-fail-closed: advertisement shape (RFC 0049 §C)', () =>
50
51
  if (!authz?.supported || authz.failClosed === undefined) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!authz?.supported || authz.failClosed === undefined` returned early');
51
52
  expect(
52
53
  authz.failClosed,
53
- driver.describe('RFC 0049 §C', 'capabilities.authorization.failClosed MUST be `true`'),
54
+ req('openwop.it.authorization-fail-closed.failclosed-is-exactly-true-when-authorization-is-supported', 'RFC 0049 §C', 'capabilities.authorization.failClosed MUST be `true`'),
54
55
  ).toBe(true);
55
56
  });
56
57
  });
@@ -73,7 +74,7 @@ describe('authorization-fail-closed: absent/unseeded role MUST deny (RFC 0049 §
73
74
  const decision = res.json as { allowed?: boolean } | undefined;
74
75
  expect(
75
76
  decision?.allowed,
76
- driver.describe(
77
+ req('openwop.it.authorization-fail-closed.a-decision-for-an-unseeded-role-principal-resolves-allowed-false',
77
78
  'SECURITY/invariants.yaml authorization-fail-closed',
78
79
  'an absent/unseeded role MUST deny (allowed=false); the host MUST NOT default-allow',
79
80
  ),
@@ -21,6 +21,8 @@
21
21
  import { describe, it, expect } from 'vitest';
22
22
  import { driver } from '../lib/driver.js';
23
23
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
24
+ import { req } from '../lib/requirement-ids.js';
25
+ import { softSkip } from '../lib/soft-skip.js';
24
26
 
25
27
  interface DiscoveryRole {
26
28
  role?: string;
@@ -48,10 +50,10 @@ async function readAuthorization(): Promise<DiscoveryAuthorization | null> {
48
50
  describe('authorization-roles-shape: advertisement shape (RFC 0049 §A)', () => {
49
51
  it('capabilities.authorization is either absent or well-formed', async () => {
50
52
  const authz = await readAuthorization();
51
- if (authz === null) return; // host doesn't advertise authorization at all
53
+ if (authz === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `authz === null` returned early (host doesn\'t advertise authorization at all)'); // host doesn't advertise authorization at all
52
54
  expect(
53
55
  typeof authz.supported,
54
- driver.describe(
56
+ req('openwop.it.authorization-roles-shape.capabilities-authorization-is-either-absent-or-well-formed',
55
57
  'capabilities.schema.json §authorization',
56
58
  'capabilities.authorization.supported MUST be a boolean when authorization is advertised',
57
59
  ),
@@ -60,24 +62,24 @@ describe('authorization-roles-shape: advertisement shape (RFC 0049 §A)', () =>
60
62
 
61
63
  it('failClosed, when present, is exactly true (RFC 0049 §C)', async () => {
62
64
  const authz = await readAuthorization();
63
- if (!authz?.supported || authz.failClosed === undefined) return;
65
+ if (!authz?.supported || authz.failClosed === undefined) return softSkip('blocked', 'precondition not met — `!authz?.supported || authz.failClosed === undefined` returned early (seam, prior step, or fixture unavailable)');
64
66
  expect(
65
67
  authz.failClosed,
66
- driver.describe('RFC 0049 §C', 'capabilities.authorization.failClosed MUST be `true` (fail-closed)'),
68
+ req('openwop.it.authorization-roles-shape.failclosed-when-present-is-exactly-true-rfc-0049-c', 'RFC 0049 §C', 'capabilities.authorization.failClosed MUST be `true` (fail-closed)'),
67
69
  ).toBe(true);
68
70
  });
69
71
 
70
72
  it('every advertised role has a non-empty role name + a scopes array', async () => {
71
73
  const authz = await readAuthorization();
72
- if (!authz?.supported || authz.roles === undefined) return;
74
+ if (!authz?.supported || authz.roles === undefined) return softSkip('blocked', 'precondition not met — `!authz?.supported || authz.roles === undefined` returned early (seam, prior step, or fixture unavailable)');
73
75
  for (const entry of authz.roles) {
74
76
  expect(
75
77
  typeof entry.role === 'string' && entry.role.length > 0,
76
- driver.describe('RFC 0049 §A', 'each capabilities.authorization.roles[] entry MUST declare a non-empty role'),
78
+ req('openwop.it.authorization-roles-shape.every-advertised-role-has-a-non-empty-role-name-a-scopes-array', 'RFC 0049 §A', 'each capabilities.authorization.roles[] entry MUST declare a non-empty role'),
77
79
  ).toBe(true);
78
80
  expect(
79
81
  Array.isArray(entry.scopes),
80
- driver.describe('RFC 0049 §A', 'each role MUST declare a scopes array'),
82
+ req('openwop.it.authorization-roles-shape.every-advertised-role-has-a-non-empty-role-name-a-scopes-array', 'RFC 0049 §A', 'each role MUST declare a scopes array'),
81
83
  ).toBe(true);
82
84
  }
83
85
  });
@@ -10,6 +10,8 @@
10
10
  import { describe, it, expect } from 'vitest';
11
11
  import { driver } from '../lib/driver.js';
12
12
  import { discoveryFamilies } from '../lib/discovery-capabilities.js';
13
+ import { req } from '../lib/requirement-ids.js';
14
+ import { softSkip } from '../lib/soft-skip.js';
13
15
 
14
16
  interface DiscoveryDoc {
15
17
  capabilities?: Record<string, unknown>;
@@ -30,10 +32,10 @@ async function call(tenantId: string, op: string, args: Record<string, unknown>)
30
32
  describe('blob-cross-tenant-isolation: advertisement shape (RFC 0019)', () => {
31
33
  it('capabilities.blobStorage is either absent or a well-formed object', async () => {
32
34
  const cap = await readCap();
33
- if (cap === null) return;
35
+ if (cap === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap === null` returned early');
34
36
  expect(
35
37
  typeof cap.supported,
36
- driver.describe(
38
+ req('openwop.it.blob-cross-tenant-isolation.capabilities-blobstorage-is-either-absent-or-a-well-formed-object',
37
39
  'capabilities.schema.json §blobStorage',
38
40
  'capabilities.blobStorage.supported MUST be a boolean when present',
39
41
  ),
@@ -44,7 +46,7 @@ describe('blob-cross-tenant-isolation: advertisement shape (RFC 0019)', () => {
44
46
  describe('blob-cross-tenant-isolation: behavioral (RFC 0019 §B point 1)', () => {
45
47
  it('put under tenant A → get under tenant B with same key returns found:false', 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 key = `xtenant-blob-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
49
51
 
50
52
  const putRes = await call('tenant-a', 'put', {
@@ -53,15 +55,15 @@ describe('blob-cross-tenant-isolation: behavioral (RFC 0019 §B point 1)', () =>
53
55
  contentBase64: Buffer.from('from-A').toString('base64'),
54
56
  contentType: 'text/plain',
55
57
  });
56
- if (putRes.status === 404) return;
57
- expect(putRes.status, 'put MUST succeed').toBe(200);
58
+ if (putRes.status === 404) return softSkip('blocked', 'precondition not met — `putRes.status === 404` returned early (seam, prior step, or fixture unavailable)');
59
+ expect(putRes.status, req('openwop.it.blob-cross-tenant-isolation.put-under-tenant-a-get-under-tenant-b-with-same-key-returns-found-false', 'RFC 0019 §B point 1', 'put MUST succeed')).toBe(200);
58
60
 
59
61
  const getRes = await call('tenant-b', 'get', { bucket: 'default', key });
60
62
  expect(getRes.status).toBe(200);
61
63
  const body = getRes.json as { found?: boolean };
62
64
  expect(
63
65
  body.found,
64
- driver.describe('RFC 0019 §B point 1', 'tenant B MUST NOT retrieve tenant A blob at same key'),
66
+ req('openwop.it.blob-cross-tenant-isolation.put-under-tenant-a-get-under-tenant-b-with-same-key-returns-found-false', 'RFC 0019 §B point 1', 'tenant B MUST NOT retrieve tenant A blob at same key'),
65
67
  ).toBe(false);
66
68
  });
67
69
  });
@@ -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('blob-presign-expiry: advertisement shape (RFC 0019)', () => {
33
35
  it('capabilities.blobStorage 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.blob-presign-expiry.capabilities-blobstorage-is-either-absent-or-a-well-formed-object',
39
41
  'capabilities.schema.json §blobStorage',
40
42
  'capabilities.blobStorage.supported MUST be a boolean when present',
41
43
  ),
@@ -44,17 +46,17 @@ describe('blob-presign-expiry: advertisement shape (RFC 0019)', () => {
44
46
 
45
47
  it('presignSupported 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 = ["presignSupported"];
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.blob-presign-expiry.presignsupported-is-a-boolean-when-set',
58
60
  'RFC 0019 §A',
59
61
  'blobStorage.presignSupported MUST be boolean when present',
60
62
  ),
@@ -69,7 +71,7 @@ async function call(op: string, args: Record<string, unknown>) {
69
71
  describe('blob-presign-expiry: behavioral (RFC 0019 §B point 1)', () => {
70
72
  it('presigned URL MUST resolve to the blob inside its TTL window and return 403 after expiry', async () => {
71
73
  const probe = await call('get', { key: '__probe__' });
72
- if (probe.status === 404) return; // seam not exposed
74
+ 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
73
75
  const key = `pre-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
74
76
  const contentBase64 = Buffer.from('presigned-payload').toString('base64');
75
77
  await call('put', { key, contentBase64, contentType: 'text/plain' });
@@ -78,14 +80,14 @@ describe('blob-presign-expiry: behavioral (RFC 0019 §B point 1)', () => {
78
80
  const presign = await call('presign', { key, expiresInSeconds: 2 });
79
81
  expect(presign.status).toBe(200);
80
82
  const body = presign.json as { url?: string; expiresAtMs?: number };
81
- expect(typeof body.url, 'presign MUST return a URL').toBe('string');
83
+ expect(typeof body.url, req('openwop.it.blob-presign-expiry.presigned-url-must-resolve-to-the-blob-inside-its-ttl-window-and-return-403-afte', 'RFC 0019 §B point 1', 'presign MUST return a URL')).toBe('string');
82
84
 
83
85
  // Fetch within the window — MUST return 200 + the bytes
84
86
  const within = await driver.get(body.url!);
85
- if (within.status === 404) return; // host doesn't expose the resolver route — soft-skip the expiry side too
87
+ if (within.status === 404) return softSkip('blocked', 'precondition not met — `within.status === 404` returned early (host doesn\'t expose the resolver route — soft-skip the expiry side too) (seam, prior step, or fixture unavailable)'); // host doesn't expose the resolver route — soft-skip the expiry side too
86
88
  expect(
87
89
  within.status,
88
- driver.describe('RFC 0019 §B point 1', 'presigned URL MUST resolve to 200 within its TTL window'),
90
+ req('openwop.it.blob-presign-expiry.presigned-url-must-resolve-to-the-blob-inside-its-ttl-window-and-return-403-afte', 'RFC 0019 §B point 1', 'presigned URL MUST resolve to 200 within its TTL window'),
89
91
  ).toBe(200);
90
92
 
91
93
  // Wait past expiry (TTL=2s + 1s buffer)
@@ -94,7 +96,7 @@ describe('blob-presign-expiry: behavioral (RFC 0019 §B point 1)', () => {
94
96
  const after = await driver.get(body.url!);
95
97
  expect(
96
98
  after.status,
97
- driver.describe('RFC 0019 §B point 1', 'presigned URL MUST return 403 after TTL expiry'),
99
+ req('openwop.it.blob-presign-expiry.presigned-url-must-resolve-to-the-blob-inside-its-ttl-window-and-return-403-afte', 'RFC 0019 §B point 1', 'presigned URL MUST return 403 after TTL expiry'),
98
100
  ).toBe(403);
99
101
  });
100
102
  });
@@ -29,11 +29,12 @@
29
29
  */
30
30
 
31
31
  import { describe, it, expect } from 'vitest';
32
- import { driver } from '../lib/driver.js';
33
32
  import { behaviorGate } from '../lib/behavior-gate.js';
34
33
  import { readBudgetCap, driveBudgetRun, BUDGET_CAP_KINDS, BUDGET_CONTENT_FORBIDDEN } from '../lib/budgetPolicy.js';
35
34
  import { queryTestEvents, isEventLogSeamAvailable, resetTestSeam } from '../lib/event-log-query.js';
36
35
  import type { TestEvent } from '../lib/event-log-query.js';
36
+ import { req } from '../lib/requirement-ids.js';
37
+ import { softSkip } from '../lib/soft-skip.js';
37
38
 
38
39
  function seq(events: TestEvent[], type: string): number {
39
40
  const e = events.find((x) => x.type === type);
@@ -45,7 +46,7 @@ function expectContentFree(events: TestEvent[]): void {
45
46
  for (const f of BUDGET_CONTENT_FORBIDDEN) {
46
47
  expect(
47
48
  !(f in e.payload),
48
- driver.describe('RFC 0084 §F (SR-1) / budget-no-pricing-leak', `budget.* MUST be content-free (no ${f})`),
49
+ req('openwop.it.budget-enforcement.runs-the-reserved-consumed-threshold-exhausted-cap-breached-run-failed-chain-ref', 'RFC 0084 §F (SR-1) / budget-no-pricing-leak', `budget.* MUST be content-free (no ${f})`),
49
50
  ).toBe(true);
50
51
  }
51
52
  }
@@ -55,11 +56,11 @@ describe('budget-enforcement (RFC 0084 §C/§D)', () => {
55
56
  it('runs the reserved→consumed→threshold→exhausted→cap.breached→run.failed chain, refuses denied models, and honors advisory mode', async () => {
56
57
  const cap = await readBudgetCap();
57
58
  if (!behaviorGate('openwop-budget-enforcement', cap?.supported === true)) return;
58
- if (!(await isEventLogSeamAvailable())) return; // event-log seam absent — soft-skip
59
+ if (!(await isEventLogSeamAvailable())) return softSkip('blocked', 'precondition not met — `!(await isEventLogSeamAvailable())` returned early (event-log seam absent — soft-skip) (seam, prior step, or fixture unavailable)'); // event-log seam absent — soft-skip
59
60
 
60
61
  // ---- Leg 1: hard cost exhaust (§C/§D) -------------------------------
61
62
  const hard = await driveBudgetRun({ scenario: 'hard-cost-exhaust' });
62
- if (hard === null) return; // budget seam absent — soft-skip the whole behavior
63
+ if (hard === null) return softSkip('blocked', 'precondition not met — `hard === null` returned early (budget seam absent — soft-skip the whole behavior) (seam, prior step, or fixture unavailable)'); // budget seam absent — soft-skip the whole behavior
63
64
  if (hard.runId) {
64
65
  const q = await queryTestEvents(hard.runId);
65
66
  if (q.ok) {
@@ -72,39 +73,39 @@ describe('budget-enforcement (RFC 0084 §C/§D)', () => {
72
73
 
73
74
  expect(
74
75
  reserved >= 0 && exhausted >= 0,
75
- driver.describe('budget-policy.md §C', 'a hard budget run MUST emit budget.reserved + budget.exhausted'),
76
+ req('openwop.it.budget-enforcement.runs-the-reserved-consumed-threshold-exhausted-cap-breached-run-failed-chain-ref', 'budget-policy.md §C', 'a hard budget run MUST emit budget.reserved + budget.exhausted'),
76
77
  ).toBe(true);
77
78
  // §C ordering: reserved < threshold.crossed < exhausted < run.failed.
78
79
  if (threshold >= 0) {
79
80
  expect(
80
81
  reserved < threshold && threshold < exhausted,
81
- driver.describe('RFC 0084 §C', 'ordering MUST be reserved < threshold.crossed < exhausted'),
82
+ req('openwop.it.budget-enforcement.runs-the-reserved-consumed-threshold-exhausted-cap-breached-run-failed-chain-ref', 'RFC 0084 §C', 'ordering MUST be reserved < threshold.crossed < exhausted'),
82
83
  ).toBe(true);
83
84
  const tc = ev.find((e) => e.type === 'budget.threshold.crossed');
84
85
  expect(
85
86
  typeof tc?.payload.percent === 'number',
86
- driver.describe('run-event-payloads.schema.json#budgetThresholdCrossed', 'threshold.crossed MUST carry a numeric percent'),
87
+ req('openwop.it.budget-enforcement.runs-the-reserved-consumed-threshold-exhausted-cap-breached-run-failed-chain-ref', 'run-event-payloads.schema.json#budgetThresholdCrossed', 'threshold.crossed MUST carry a numeric percent'),
87
88
  ).toBe(true);
88
89
  }
89
90
  // §D hard-stop: exhausted → cap.breached{budget-*} → run.failed{budget_exhausted}.
90
91
  expect(
91
92
  capBreached !== undefined,
92
- driver.describe('RFC 0084 §D', 'exhaustion MUST emit cap.breached with a budget-* kind'),
93
+ req('openwop.it.budget-enforcement.runs-the-reserved-consumed-threshold-exhausted-cap-breached-run-failed-chain-ref', 'RFC 0084 §D', 'exhaustion MUST emit cap.breached with a budget-* kind'),
93
94
  ).toBe(true);
94
95
  if (capBreached) {
95
96
  expect(
96
97
  BUDGET_CAP_KINDS.includes(capBreached.payload.kind as string),
97
- driver.describe('RFC 0084 §D', 'cap.breached.kind MUST be in the closed budget vocabulary'),
98
+ req('openwop.it.budget-enforcement.runs-the-reserved-consumed-threshold-exhausted-cap-breached-run-failed-chain-ref', 'RFC 0084 §D', 'cap.breached.kind MUST be in the closed budget vocabulary'),
98
99
  ).toBe(true);
99
100
  expect(
100
101
  exhausted <= capBreached.sequence && capBreached.sequence <= failed,
101
- driver.describe('RFC 0084 §D', 'ordering MUST be exhausted ≤ cap.breached ≤ run.failed'),
102
+ req('openwop.it.budget-enforcement.runs-the-reserved-consumed-threshold-exhausted-cap-breached-run-failed-chain-ref', 'RFC 0084 §D', 'ordering MUST be exhausted ≤ cap.breached ≤ run.failed'),
102
103
  ).toBe(true);
103
104
  }
104
105
  const failedEvt = ev.find((e) => e.type === 'run.failed');
105
106
  expect(
106
107
  failedEvt?.payload.error === 'budget_exhausted',
107
- driver.describe('RFC 0084 §D', 'a hard-budget overrun MUST fail the run with error budget_exhausted'),
108
+ req('openwop.it.budget-enforcement.runs-the-reserved-consumed-threshold-exhausted-cap-breached-run-failed-chain-ref', 'RFC 0084 §D', 'a hard-budget overrun MUST fail the run with error budget_exhausted'),
108
109
  ).toBe(true);
109
110
  expectContentFree(ev);
110
111
  }
@@ -115,11 +116,11 @@ describe('budget-enforcement (RFC 0084 §C/§D)', () => {
115
116
  if (denied !== null) {
116
117
  expect(
117
118
  denied.error === 'budget_model_denied',
118
- driver.describe('RFC 0084 §D', 'a model violating the budget allow/deny list MUST be refused with budget_model_denied'),
119
+ req('openwop.it.budget-enforcement.runs-the-reserved-consumed-threshold-exhausted-cap-breached-run-failed-chain-ref', 'RFC 0084 §D', 'a model violating the budget allow/deny list MUST be refused with budget_model_denied'),
119
120
  ).toBe(true);
120
121
  expect(
121
122
  denied.modelCalled !== true,
122
- driver.describe('RFC 0084 §D', 'a denied model MUST be refused BEFORE the provider call (fail-closed)'),
123
+ req('openwop.it.budget-enforcement.runs-the-reserved-consumed-threshold-exhausted-cap-breached-run-failed-chain-ref', 'RFC 0084 §D', 'a denied model MUST be refused BEFORE the provider call (fail-closed)'),
123
124
  ).toBe(true);
124
125
  }
125
126
 
@@ -139,7 +140,7 @@ describe('budget-enforcement (RFC 0084 §C/§D)', () => {
139
140
  if (hasBudgetEvents) {
140
141
  expect(
141
142
  !stopped,
142
- driver.describe('RFC 0084 §D', 'advisory enforcement MUST emit budget.* events without stopping the run'),
143
+ req('openwop.it.budget-enforcement.runs-the-reserved-consumed-threshold-exhausted-cap-breached-run-failed-chain-ref', 'RFC 0084 §D', 'advisory enforcement MUST emit budget.* events without stopping the run'),
143
144
  ).toBe(true);
144
145
  }
145
146
  expectContentFree(ev);