@openwop/openwop-conformance 1.163.0 → 2.0.0-rc.1

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 (611) hide show
  1. package/README.md +11 -2
  2. package/dist/cli.js +262 -14
  3. package/dist/lib/certification-bundle-v3.js +142 -0
  4. package/dist/lib/paths.js +25 -3
  5. package/dist/lib/requirement-ids.js +57 -4
  6. package/dist/lib/requirement-ledger.js +1 -0
  7. package/dist/lib/scenario-disposition.js +6 -1
  8. package/dist/lib/spec-coherence.js +13 -32
  9. package/dist/spec-artifacts.lock.json +5 -0
  10. package/package.json +11 -33
  11. package/requirements.json +22892 -11701
  12. package/scenario-majors.json +1493 -0
  13. package/schemas/CORPUS-STAMP.json +137 -5
  14. package/src/cli.ts +236 -19
  15. package/src/global-setup.ts +3 -2
  16. package/src/lib/certification-bundle-v3.ts +175 -0
  17. package/src/lib/corpus-stamp.ts +29 -1
  18. package/src/lib/driver.ts +9 -1
  19. package/src/lib/era2-seed.ts +197 -0
  20. package/src/lib/paths.ts +25 -3
  21. package/src/lib/requirement-ids.ts +54 -4
  22. package/src/lib/requirement-ledger.ts +7 -1
  23. package/src/lib/scenario-disposition.ts +6 -1
  24. package/src/lib/seams.ts +45 -0
  25. package/src/lib/spec-coherence.ts +15 -32
  26. package/src/lib/v2.ts +62 -0
  27. package/src/scenarios/a2a-1-0-agent-card.test.ts +17 -16
  28. package/src/scenarios/a2a-1-0-task-roundtrip.test.ts +11 -10
  29. package/src/scenarios/a2a-card-runtime-consistency.test.ts +18 -17
  30. package/src/scenarios/a2a-peer-authority.test.ts +7 -6
  31. package/src/scenarios/a2a-task-roundtrip.test.ts +30 -29
  32. package/src/scenarios/a2a-version-negotiation.test.ts +16 -14
  33. package/src/scenarios/a2ui-surface-degrades.test.ts +5 -3
  34. package/src/scenarios/a2ui-surface-delta-transport.test.ts +35 -33
  35. package/src/scenarios/a2ui-surface-replay.test.ts +6 -4
  36. package/src/scenarios/a2ui-surface-shape.test.ts +11 -10
  37. package/src/scenarios/a2ui-surface-version-refusal.test.ts +6 -4
  38. package/src/scenarios/a2ui-untrusted-blocks-approval.test.ts +5 -3
  39. package/src/scenarios/agent-capability-degraded-projection.test.ts +12 -11
  40. package/src/scenarios/agent-channel-dispatch.test.ts +18 -16
  41. package/src/scenarios/agent-deployment-lifecycle.test.ts +20 -19
  42. package/src/scenarios/agent-deployment-shape.test.ts +22 -24
  43. package/src/scenarios/agent-eval-run.test.ts +19 -18
  44. package/src/scenarios/agent-eval-suite-shape.test.ts +20 -22
  45. package/src/scenarios/agent-live-allowlist-enforced.test.ts +6 -5
  46. package/src/scenarios/agent-live-invocation-bracket.test.ts +13 -12
  47. package/src/scenarios/agent-live-runtime-shape.test.ts +10 -12
  48. package/src/scenarios/agent-live-structured-output.test.ts +7 -6
  49. package/src/scenarios/agent-loop-iteration-monotonic.test.ts +3 -3
  50. package/src/scenarios/agent-loop-stateful-resume.test.ts +2 -2
  51. package/src/scenarios/agent-loop-version5-shape.test.ts +4 -4
  52. package/src/scenarios/agent-loop-workspace-snapshot.test.ts +3 -3
  53. package/src/scenarios/agent-manifest-role-profile.test.ts +9 -9
  54. package/src/scenarios/agent-manifest-runtime.test.ts +13 -12
  55. package/src/scenarios/agent-org-chart-scoping.test.ts +14 -13
  56. package/src/scenarios/agent-org-chart-shape.test.ts +11 -13
  57. package/src/scenarios/agent-platform-aggregate-evidence.test.ts +5 -4
  58. package/src/scenarios/agent-platform-profile.test.ts +18 -19
  59. package/src/scenarios/agent-requires-capabilities-shape.test.ts +4 -4
  60. package/src/scenarios/agent-roster-attribution.test.ts +18 -17
  61. package/src/scenarios/agent-roster-shape.test.ts +17 -19
  62. package/src/scenarios/agent-verifier-shape.test.ts +11 -11
  63. package/src/scenarios/agentConfidenceEscalation.test.ts +2 -1
  64. package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +6 -5
  65. package/src/scenarios/agentMemoryRedactionContract.test.ts +2 -1
  66. package/src/scenarios/agentMemoryRoundTrip.test.ts +2 -1
  67. package/src/scenarios/agentMemoryTtlExpiry.test.ts +4 -3
  68. package/src/scenarios/agentMessageReducer.test.ts +2 -1
  69. package/src/scenarios/agentMetadata.test.ts +2 -1
  70. package/src/scenarios/agentPackCatalog.test.ts +23 -21
  71. package/src/scenarios/agentPackExport.test.ts +4 -2
  72. package/src/scenarios/agentPackHandoffSchemaValidation.test.ts +5 -4
  73. package/src/scenarios/agentPackInstall.test.ts +4 -2
  74. package/src/scenarios/agentPackProvenance.test.ts +2 -1
  75. package/src/scenarios/agentReasoningEvents.test.ts +3 -2
  76. package/src/scenarios/agentReasoningStreaming.test.ts +11 -10
  77. package/src/scenarios/agents-run-tool-allowlist.test.ts +14 -12
  78. package/src/scenarios/ai-envelope-shape.test.ts +31 -29
  79. package/src/scenarios/aiEnvelope.capBreached.test.ts +26 -24
  80. package/src/scenarios/aiEnvelope.contractRefusal.test.ts +36 -34
  81. package/src/scenarios/aiEnvelope.correlationReplay.test.ts +17 -15
  82. package/src/scenarios/aiEnvelope.redaction.test.ts +22 -20
  83. package/src/scenarios/aiEnvelope.schemaDrift.test.ts +18 -16
  84. package/src/scenarios/aiEnvelope.trustBoundaryPropagation.test.ts +38 -36
  85. package/src/scenarios/aiEnvelope.universalKinds.test.ts +31 -29
  86. package/src/scenarios/aiproviders-input-shape.test.ts +5 -5
  87. package/src/scenarios/aiproviders-realtimevoice-shape.test.ts +14 -16
  88. package/src/scenarios/aiproviders-selfhosted-honesty.test.ts +6 -4
  89. package/src/scenarios/aiproviders-selfhosted-shape.test.ts +15 -17
  90. package/src/scenarios/aiproviders-speechsynth-shape.test.ts +6 -8
  91. package/src/scenarios/aiproviders-subscription-scope.test.ts +7 -8
  92. package/src/scenarios/anonymous-actor-audit-opaque.test.ts +9 -8
  93. package/src/scenarios/anonymous-actor-default-deny.test.ts +10 -9
  94. package/src/scenarios/anonymous-actor-egress-guarded.test.ts +6 -5
  95. package/src/scenarios/anonymous-actor-no-secret-reach.test.ts +7 -6
  96. package/src/scenarios/anonymous-actor-shape.test.ts +17 -19
  97. package/src/scenarios/anonymous-actor-write-gated.test.ts +9 -8
  98. package/src/scenarios/append-ordering.test.ts +7 -5
  99. package/src/scenarios/approval-gate-events.test.ts +4 -3
  100. package/src/scenarios/approval-gate-flow.test.ts +4 -3
  101. package/src/scenarios/approval-payload.test.ts +5 -4
  102. package/src/scenarios/artifact-auth.test.ts +7 -6
  103. package/src/scenarios/artifact-type-pack-install.test.ts +6 -6
  104. package/src/scenarios/artifact-type-pack-manifest-validation.test.ts +9 -8
  105. package/src/scenarios/artifact-type-registration-source.test.ts +20 -20
  106. package/src/scenarios/artifact-type-store-emission.test.ts +15 -18
  107. package/src/scenarios/artifact-type-store-without-render.test.ts +4 -4
  108. package/src/scenarios/audit-log-integrity.test.ts +8 -6
  109. package/src/scenarios/auth-api-key-rotation.test.ts +11 -9
  110. package/src/scenarios/auth-mtls.test.ts +22 -20
  111. package/src/scenarios/auth-oauth2-client-credentials.test.ts +16 -14
  112. package/src/scenarios/auth-oidc-user-bearer.test.ts +26 -24
  113. package/src/scenarios/auth-saml-profile.test.ts +14 -12
  114. package/src/scenarios/auth-scim-profile.test.ts +3 -2
  115. package/src/scenarios/auth-subject-link-key-class.test.ts +8 -7
  116. package/src/scenarios/auth-subject-link.test.ts +11 -10
  117. package/src/scenarios/auth.test.ts +5 -4
  118. package/src/scenarios/authorization-fail-closed.test.ts +3 -2
  119. package/src/scenarios/authorization-roles-shape.test.ts +9 -7
  120. package/src/scenarios/blob-cross-tenant-isolation.test.ts +8 -6
  121. package/src/scenarios/blob-presign-expiry.test.ts +12 -10
  122. package/src/scenarios/blob-roundtrip.test.ts +0 -0
  123. package/src/scenarios/budget-enforcement.test.ts +15 -14
  124. package/src/scenarios/budget-policy-shape.test.ts +18 -19
  125. package/src/scenarios/bulk-cancel.test.ts +9 -8
  126. package/src/scenarios/byok-auth-modes.test.ts +13 -14
  127. package/src/scenarios/byok-roundtrip.test.ts +12 -10
  128. package/src/scenarios/cache-cross-tenant-isolation.test.ts +8 -6
  129. package/src/scenarios/cache-ttl-expiry.test.ts +7 -5
  130. package/src/scenarios/callai-multimodal.test.ts +5 -3
  131. package/src/scenarios/cancellation.test.ts +6 -5
  132. package/src/scenarios/cap-breach.test.ts +15 -14
  133. package/src/scenarios/capability-arm-witness.test.ts +5 -3
  134. package/src/scenarios/certification-bundle-non-vacuous.test.ts +20 -19
  135. package/src/scenarios/certification-bundle-redaction.test.ts +11 -10
  136. package/src/scenarios/certification-bundle-v2.test.ts +11 -10
  137. package/src/scenarios/chain-compensation-expansion.test.ts +14 -13
  138. package/src/scenarios/chain-produced-var-roundtrip.test.ts +13 -14
  139. package/src/scenarios/chain-subchain-cycle-rejected.test.ts +9 -10
  140. package/src/scenarios/chain-subchain-fanout.test.ts +8 -9
  141. package/src/scenarios/chain-subchain-sibling.test.ts +14 -16
  142. package/src/scenarios/channel-presence-behavioral.test.ts +9 -7
  143. package/src/scenarios/channel-presence-shape.test.ts +11 -12
  144. package/src/scenarios/channel-ttl.test.ts +6 -5
  145. package/src/scenarios/chat-card-pack-execution.test.ts +4 -4
  146. package/src/scenarios/chat-card-pack-manifest-validation.test.ts +9 -8
  147. package/src/scenarios/commitment-fired.test.ts +6 -5
  148. package/src/scenarios/compensation-behavior.test.ts +24 -23
  149. package/src/scenarios/compensation-recovery.test.ts +28 -27
  150. package/src/scenarios/configurable-schema.test.ts +8 -6
  151. package/src/scenarios/conformance-advertised-seam-required.test.ts +6 -5
  152. package/src/scenarios/conformance-execution-witness.test.ts +9 -8
  153. package/src/scenarios/connection-pack-apihosts.test.ts +19 -17
  154. package/src/scenarios/connection-pack-manifest-valid.test.ts +12 -11
  155. package/src/scenarios/connection-pack-no-credential-material.test.ts +8 -6
  156. package/src/scenarios/connection-pack-reach-exclusive.test.ts +5 -4
  157. package/src/scenarios/connection-pack-write-reconsent.test.ts +6 -4
  158. package/src/scenarios/connection-provider-resolution.test.ts +18 -16
  159. package/src/scenarios/connector-manifest-validity.test.ts +8 -7
  160. package/src/scenarios/context-budget-transcript-bound.test.ts +17 -15
  161. package/src/scenarios/context-summarization-replay.test.ts +14 -12
  162. package/src/scenarios/contract-provenance.test.ts +18 -18
  163. package/src/scenarios/conversation-turn-model-provenance-shape.test.ts +14 -15
  164. package/src/scenarios/conversationCapabilityNegotiation.test.ts +2 -1
  165. package/src/scenarios/conversationLifecycle.test.ts +2 -1
  166. package/src/scenarios/conversationReplayDeterminism.test.ts +4 -2
  167. package/src/scenarios/conversationVsLegacySuspend.test.ts +2 -1
  168. package/src/scenarios/core-standard-profile.test.ts +8 -9
  169. package/src/scenarios/cost-attribution.test.ts +23 -21
  170. package/src/scenarios/credential-payload-redaction.test.ts +4 -3
  171. package/src/scenarios/credentials-capability-shape.test.ts +5 -4
  172. package/src/scenarios/cross-engine-append-behavior.test.ts +11 -10
  173. package/src/scenarios/cross-engine-append-ordering.test.ts +4 -3
  174. package/src/scenarios/cross-host-ancestry-endpoint.test.ts +11 -10
  175. package/src/scenarios/cross-host-causation-shape.test.ts +7 -6
  176. package/src/scenarios/cross-workspace-isolation.test.ts +4 -3
  177. package/src/scenarios/data-residency-admission.test.ts +15 -14
  178. package/src/scenarios/deadletter-capability-shape.test.ts +6 -4
  179. package/src/scenarios/deadletter-retry-exhaustion.test.ts +4 -3
  180. package/src/scenarios/debug-bundle-truncation.test.ts +9 -7
  181. package/src/scenarios/debugBundle.test.ts +25 -23
  182. package/src/scenarios/discovery-families-view.test.ts +5 -4
  183. package/src/scenarios/discovery.test.ts +30 -29
  184. package/src/scenarios/dispatch-cross-worker-handoff.test.ts +10 -8
  185. package/src/scenarios/dispatch-fanout-parallel.test.ts +24 -22
  186. package/src/scenarios/dispatch-input-mapping.test.ts +12 -10
  187. package/src/scenarios/dispatch-output-mapping.test.ts +9 -7
  188. package/src/scenarios/dispatch-per-item-input.test.ts +22 -20
  189. package/src/scenarios/dispatchLoop.test.ts +3 -2
  190. package/src/scenarios/distillation-index-roundtrip.test.ts +3 -3
  191. package/src/scenarios/distillation-secret-carryforward.test.ts +2 -2
  192. package/src/scenarios/distillation-shape.test.ts +4 -4
  193. package/src/scenarios/distillation-stable-archive.test.ts +6 -6
  194. package/src/scenarios/distillation-token-budget.test.ts +5 -5
  195. package/src/scenarios/durability-poison-exhaustion.test.ts +7 -6
  196. package/src/scenarios/egress-audience-binding.test.ts +11 -10
  197. package/src/scenarios/egress-decision-content-free.test.ts +6 -5
  198. package/src/scenarios/egress-provenance-shape.test.ts +17 -18
  199. package/src/scenarios/envelope-completion-distinguishes-truncation.test.ts +27 -25
  200. package/src/scenarios/envelope-nl-to-format-engaged.test.ts +19 -17
  201. package/src/scenarios/envelope-reasoning-secret-redaction.test.ts +28 -26
  202. package/src/scenarios/envelope-reasoning-shape.test.ts +19 -17
  203. package/src/scenarios/envelope-recovery-applied.test.ts +24 -22
  204. package/src/scenarios/envelope-refusal-shape.test.ts +31 -29
  205. package/src/scenarios/envelope-rendering-hint.test.ts +5 -4
  206. package/src/scenarios/envelope-retry-attempted.test.ts +33 -31
  207. package/src/scenarios/envelope-retry-exhausted.test.ts +22 -20
  208. package/src/scenarios/envelope-tier-one-subset-static.test.ts +9 -7
  209. package/src/scenarios/envelope-truncated.test.ts +19 -17
  210. package/src/scenarios/envelope-truncation-cap-exhaustion.test.ts +19 -17
  211. package/src/scenarios/envelope-variant-discriminator-static.test.ts +4 -3
  212. package/src/scenarios/errors.test.ts +20 -19
  213. package/src/scenarios/eventOrdering.test.ts +16 -14
  214. package/src/scenarios/exec-not-protocol-tier.test.ts +5 -7
  215. package/src/scenarios/experimental-tier-shape.test.ts +17 -15
  216. package/src/scenarios/export-bundle-portability.test.ts +17 -17
  217. package/src/scenarios/failure-path.test.ts +7 -6
  218. package/src/scenarios/feedback-capability-shape.test.ts +4 -3
  219. package/src/scenarios/feedback-correction-redaction.test.ts +2 -1
  220. package/src/scenarios/feedback-cross-tenant-isolation.test.ts +2 -1
  221. package/src/scenarios/feedback-fork-not-copied.test.ts +3 -2
  222. package/src/scenarios/feedback-on-terminal-run.test.ts +3 -2
  223. package/src/scenarios/feedback-record-and-list.test.ts +2 -1
  224. package/src/scenarios/feedback-unsupported-501.test.ts +2 -1
  225. package/src/scenarios/fixtures-gating.test.ts +29 -28
  226. package/src/scenarios/fixtures-valid.test.ts +16 -15
  227. package/src/scenarios/form-content-instantiation.test.ts +15 -14
  228. package/src/scenarios/frontend-plugin-packs.test.ts +29 -27
  229. package/src/scenarios/fs-path-traversal.test.ts +14 -12
  230. package/src/scenarios/goal-standing-continuation.test.ts +21 -21
  231. package/src/scenarios/grpc-transport.test.ts +9 -7
  232. package/src/scenarios/heartbeat-capability-shape.test.ts +3 -3
  233. package/src/scenarios/heartbeat-fires-once-per-tick.test.ts +2 -2
  234. package/src/scenarios/heartbeat-idempotent-no-spam.test.ts +3 -3
  235. package/src/scenarios/heartbeat-runtime-bound.test.ts +2 -2
  236. package/src/scenarios/highConcurrency.test.ts +14 -13
  237. package/src/scenarios/host-callback-declaration.test.ts +8 -7
  238. package/src/scenarios/http-client-ssrf.test.ts +7 -5
  239. package/src/scenarios/i18n-negotiation.test.ts +14 -12
  240. package/src/scenarios/idempotency-concurrent-claim.test.ts +14 -13
  241. package/src/scenarios/idempotency-key-determinism.test.ts +20 -18
  242. package/src/scenarios/idempotency.test.ts +8 -7
  243. package/src/scenarios/idempotencyRetry.test.ts +13 -11
  244. package/src/scenarios/identity-owner-shape.test.ts +6 -5
  245. package/src/scenarios/identity-passthrough.test.ts +4 -3
  246. package/src/scenarios/interrupt-approval.test.ts +6 -5
  247. package/src/scenarios/interrupt-approver-routing.test.ts +15 -15
  248. package/src/scenarios/interrupt-auth-required-resume.test.ts +5 -3
  249. package/src/scenarios/interrupt-clarification.test.ts +4 -3
  250. package/src/scenarios/interrupt-external-event-correlation.test.ts +6 -5
  251. package/src/scenarios/interrupt-parent-child-cascade.test.ts +4 -3
  252. package/src/scenarios/interrupt-quorum-resolution.test.ts +5 -4
  253. package/src/scenarios/interrupt-token-matrix.test.ts +8 -6
  254. package/src/scenarios/interruptRace.test.ts +12 -10
  255. package/src/scenarios/kv-atomic-increment.test.ts +11 -9
  256. package/src/scenarios/kv-cas.test.ts +14 -12
  257. package/src/scenarios/kv-cross-tenant-isolation.test.ts +12 -10
  258. package/src/scenarios/kv-ttl-expiry.test.ts +7 -5
  259. package/src/scenarios/localized-content-delivery.test.ts +24 -26
  260. package/src/scenarios/maliciousManifest.test.ts +10 -8
  261. package/src/scenarios/mcp-2026-07-28-discover.test.ts +15 -14
  262. package/src/scenarios/mcp-cache-tenant-scope.test.ts +6 -5
  263. package/src/scenarios/mcp-current-auth-boundary.test.ts +7 -6
  264. package/src/scenarios/mcp-discoverability.test.ts +9 -7
  265. package/src/scenarios/mcp-extension-opacity.test.ts +7 -6
  266. package/src/scenarios/mcp-mrtr-roundtrip.test.ts +18 -17
  267. package/src/scenarios/mcp-server-elicitation-bridge.test.ts +13 -12
  268. package/src/scenarios/mcp-server-prompt-roundtrip.test.ts +11 -10
  269. package/src/scenarios/mcp-server-resource-roundtrip.test.ts +11 -10
  270. package/src/scenarios/mcp-server-sampling-bridge.test.ts +12 -11
  271. package/src/scenarios/mcp-server-tool-roundtrip.test.ts +14 -13
  272. package/src/scenarios/mcp-server-untrusted-args.test.ts +14 -13
  273. package/src/scenarios/mcp-stateless-request.test.ts +11 -10
  274. package/src/scenarios/mcp-tool-roundtrip.test.ts +4 -3
  275. package/src/scenarios/mcp-toolcall-redaction.test.ts +4 -3
  276. package/src/scenarios/mcp-version-negotiation.test.ts +15 -14
  277. package/src/scenarios/media-url-inline-cap.test.ts +21 -19
  278. package/src/scenarios/memory-attribution-emits-on-write.test.ts +5 -5
  279. package/src/scenarios/memory-attribution-no-content.test.ts +4 -4
  280. package/src/scenarios/memory-attribution-replay-stable.test.ts +4 -3
  281. package/src/scenarios/memory-attribution-shape.test.ts +4 -3
  282. package/src/scenarios/memory-attribution-tenant-scoped.test.ts +3 -3
  283. package/src/scenarios/memory-capability-model-shape.test.ts +14 -15
  284. package/src/scenarios/memory-compaction-event-emitted.test.ts +7 -6
  285. package/src/scenarios/memory-compaction-provenance-tag.test.ts +3 -2
  286. package/src/scenarios/memory-compaction-sr1-carry-forward.test.ts +7 -6
  287. package/src/scenarios/memory-consolidation-idempotent.test.ts +5 -4
  288. package/src/scenarios/memory-consolidation-shape.test.ts +9 -11
  289. package/src/scenarios/memory-degraded-projection.test.ts +10 -9
  290. package/src/scenarios/memory-injection-budget.test.ts +15 -14
  291. package/src/scenarios/metric-emission.test.ts +4 -3
  292. package/src/scenarios/model-capability-insufficient.test.ts +19 -17
  293. package/src/scenarios/model-capability-substituted.test.ts +20 -18
  294. package/src/scenarios/multi-agent-confidence-escalation.test.ts +12 -11
  295. package/src/scenarios/multi-agent-handoff-state-machine.test.ts +10 -9
  296. package/src/scenarios/multi-agent-memory-lifecycle.test.ts +11 -10
  297. package/src/scenarios/multi-node-ordering.test.ts +4 -3
  298. package/src/scenarios/multi-party-conversation-behavioral.test.ts +6 -6
  299. package/src/scenarios/multi-party-conversation-shape.test.ts +21 -23
  300. package/src/scenarios/multi-region-idempotency-behavior.test.ts +16 -15
  301. package/src/scenarios/multi-region-idempotency.test.ts +6 -5
  302. package/src/scenarios/node-failed-payload-shape.test.ts +5 -4
  303. package/src/scenarios/node-module-required-capabilities-shape.test.ts +10 -8
  304. package/src/scenarios/oauth-authorization-code-roundtrip.test.ts +5 -4
  305. package/src/scenarios/oauth-capability-shape.test.ts +5 -4
  306. package/src/scenarios/oauth-connector-redaction.test.ts +4 -3
  307. package/src/scenarios/openapi-resolved-paths.test.ts +6 -5
  308. package/src/scenarios/orchestratorConservativePath.test.ts +2 -1
  309. package/src/scenarios/orchestratorDispatch.test.ts +2 -1
  310. package/src/scenarios/orchestratorTermination.test.ts +2 -1
  311. package/src/scenarios/org-position-no-authority-escalation.test.ts +2 -2
  312. package/src/scenarios/otel-collector-canary-inspection.test.ts +8 -7
  313. package/src/scenarios/otel-emission-grpc.test.ts +3 -2
  314. package/src/scenarios/otel-emission.test.ts +6 -5
  315. package/src/scenarios/otel-trace-propagation-subworkflow.test.ts +7 -6
  316. package/src/scenarios/otel-trace-propagation.test.ts +2 -1
  317. package/src/scenarios/owner-subject-echo.test.ts +13 -12
  318. package/src/scenarios/owner-subject-shape.test.ts +11 -10
  319. package/src/scenarios/pack-manifest-extension-opacity.test.ts +8 -8
  320. package/src/scenarios/pack-registry-isolation.test.ts +4 -3
  321. package/src/scenarios/pack-registry-publish.test.ts +65 -63
  322. package/src/scenarios/pack-registry.test.ts +38 -36
  323. package/src/scenarios/pause-resume.test.ts +18 -16
  324. package/src/scenarios/policies.test.ts +20 -18
  325. package/src/scenarios/production-backpressure.test.ts +18 -16
  326. package/src/scenarios/production-retention-expiry.test.ts +8 -6
  327. package/src/scenarios/profile-discovery-core-alias.test.ts +13 -11
  328. package/src/scenarios/profileDerivation.test.ts +34 -33
  329. package/src/scenarios/prompt-all-four-kinds-events.test.ts +11 -9
  330. package/src/scenarios/prompt-composed-secret-redaction.test.ts +11 -9
  331. package/src/scenarios/prompt-composed-trust-marker.test.ts +10 -8
  332. package/src/scenarios/prompt-end-to-end-events.test.ts +17 -15
  333. package/src/scenarios/prompt-list-and-fetch.test.ts +16 -14
  334. package/src/scenarios/prompt-mutable-lifecycle.test.ts +13 -11
  335. package/src/scenarios/prompt-mutation-workspace-membership-enforced.test.ts +6 -4
  336. package/src/scenarios/prompt-pack-install.test.ts +14 -12
  337. package/src/scenarios/prompt-prefix-cache.test.ts +19 -17
  338. package/src/scenarios/prompt-read-workspace-membership-enforced.test.ts +11 -9
  339. package/src/scenarios/prompt-render-deterministic.test.ts +19 -17
  340. package/src/scenarios/prompt-resolution-chain-agent-intrinsic.test.ts +7 -5
  341. package/src/scenarios/prompt-resolution-chain-event.test.ts +12 -10
  342. package/src/scenarios/prompt-resolution-chain-fallback-cascade.test.ts +10 -8
  343. package/src/scenarios/prompt-resolution-chain-node-wins.test.ts +8 -6
  344. package/src/scenarios/prompt-template-shape.test.ts +30 -28
  345. package/src/scenarios/proposal-reviewable-learning.test.ts +19 -19
  346. package/src/scenarios/protocol-versions-array.test.ts +10 -9
  347. package/src/scenarios/provider-usage.test.ts +17 -15
  348. package/src/scenarios/providerPolicyEnforcement.test.ts +16 -14
  349. package/src/scenarios/purpose-propagation.test.ts +23 -21
  350. package/src/scenarios/queue-ack-nack-dlq.test.ts +12 -10
  351. package/src/scenarios/queue-cross-tenant-isolation.test.ts +8 -6
  352. package/src/scenarios/queue-publish-consume-roundtrip.test.ts +10 -8
  353. package/src/scenarios/rate-limit-envelope.test.ts +6 -5
  354. package/src/scenarios/redaction.test.ts +13 -11
  355. package/src/scenarios/redactionAdversarial.test.ts +11 -9
  356. package/src/scenarios/registry-public.test.ts +12 -11
  357. package/src/scenarios/replay-divergence-at-refusal.test.ts +16 -15
  358. package/src/scenarios/replay-fanout-suppression.test.ts +16 -15
  359. package/src/scenarios/replay-fork-arbitrary.test.ts +24 -23
  360. package/src/scenarios/replay-fork.test.ts +20 -19
  361. package/src/scenarios/replay-llm-cache-key-portable.test.ts +11 -9
  362. package/src/scenarios/replay-llm-cache-key.test.ts +22 -21
  363. package/src/scenarios/replay-observable-sequence-determinism.test.ts +4 -3
  364. package/src/scenarios/replay-retention-expiry.test.ts +11 -9
  365. package/src/scenarios/replay-side-effect-suppression.test.ts +21 -19
  366. package/src/scenarios/replayDeterminism.test.ts +13 -12
  367. package/src/scenarios/requirement-ledger.test.ts +15 -14
  368. package/src/scenarios/restart-during-run.test.ts +3 -2
  369. package/src/scenarios/route-coverage.test.ts +20 -19
  370. package/src/scenarios/run-diff.test.ts +22 -20
  371. package/src/scenarios/run-execution-bounds-shape.test.ts +14 -12
  372. package/src/scenarios/run-transport-economy.test.ts +13 -11
  373. package/src/scenarios/runner-ledger.test.ts +17 -16
  374. package/src/scenarios/runs-lifecycle.test.ts +7 -6
  375. package/src/scenarios/runtime-capabilities.test.ts +12 -10
  376. package/src/scenarios/runtime-requires-install-gate.test.ts +8 -8
  377. package/src/scenarios/runtime-requires-shape.test.ts +7 -6
  378. package/src/scenarios/safefetch-behavior.test.ts +22 -21
  379. package/src/scenarios/safefetch-live-audit.test.ts +14 -13
  380. package/src/scenarios/sandbox-memory-cap.test.ts +2 -1
  381. package/src/scenarios/sandbox-mvp-behavior.test.ts +24 -23
  382. package/src/scenarios/sandbox-no-host-fs-escape.test.ts +3 -2
  383. package/src/scenarios/sandbox-timeout-cap.test.ts +2 -1
  384. package/src/scenarios/sandbox-wasm-isolation.test.ts +14 -15
  385. package/src/scenarios/sandbox-wasm-timeout.test.ts +5 -6
  386. package/src/scenarios/scheduling-capability-shape.test.ts +8 -6
  387. package/src/scenarios/scheduling-cron-fires-once.test.ts +8 -6
  388. package/src/scenarios/search-bm25-roundtrip.test.ts +8 -6
  389. package/src/scenarios/secret-leakage-otel-attribute.test.ts +22 -20
  390. package/src/scenarios/self-hosted-runner.test.ts +26 -24
  391. package/src/scenarios/semantic-digest-vectors.test.ts +8 -7
  392. package/src/scenarios/speech-synthesis-roundtrip.test.ts +9 -7
  393. package/src/scenarios/speech-synthesis-unadvertised.test.ts +5 -3
  394. package/src/scenarios/sql-injection-rejection.test.ts +8 -6
  395. package/src/scenarios/sql-transaction-atomicity.test.ts +10 -8
  396. package/src/scenarios/staleClaim.test.ts +5 -4
  397. package/src/scenarios/stream-modes-buffer.test.ts +8 -7
  398. package/src/scenarios/stream-modes-mixed.test.ts +10 -9
  399. package/src/scenarios/stream-modes.test.ts +12 -11
  400. package/src/scenarios/stream-subscribe-from-beginning.test.ts +11 -9
  401. package/src/scenarios/stream-text-fixture.test.ts +14 -13
  402. package/src/scenarios/streamReconnect.test.ts +13 -11
  403. package/src/scenarios/strict-behavior-gate.test.ts +9 -8
  404. package/src/scenarios/subrun-approval-fail-closed.test.ts +2 -2
  405. package/src/scenarios/subrun-approval-gate.test.ts +3 -3
  406. package/src/scenarios/subrun-attestation-shape.test.ts +2 -2
  407. package/src/scenarios/subrun-checksum-stable.test.ts +4 -4
  408. package/src/scenarios/subworkflow-input-mapping.test.ts +16 -14
  409. package/src/scenarios/subworkflow.test.ts +10 -9
  410. package/src/scenarios/table-cross-tenant-isolation.test.ts +8 -6
  411. package/src/scenarios/table-cursor-pagination.test.ts +8 -6
  412. package/src/scenarios/table-schema-enforcement.test.ts +7 -5
  413. package/src/scenarios/test-seam-unauthenticated.test.ts +2 -2
  414. package/src/scenarios/tool-catalog-compact-projection.test.ts +10 -8
  415. package/src/scenarios/tool-catalog-projection.test.ts +11 -9
  416. package/src/scenarios/tool-descriptor-shape.test.ts +14 -15
  417. package/src/scenarios/tool-hooks-authorization-fail-closed.test.ts +3 -3
  418. package/src/scenarios/tool-hooks-content-free.test.ts +4 -4
  419. package/src/scenarios/tool-hooks-failure-honesty.test.ts +4 -4
  420. package/src/scenarios/tool-hooks-rate-limit.test.ts +2 -2
  421. package/src/scenarios/tool-hooks-secret-redaction.test.ts +2 -2
  422. package/src/scenarios/tool-hooks-shape.test.ts +3 -3
  423. package/src/scenarios/tool-session-lifecycle.test.ts +14 -13
  424. package/src/scenarios/trigger-bridge-delivery.test.ts +14 -14
  425. package/src/scenarios/trigger-bridge-shape.test.ts +19 -20
  426. package/src/scenarios/trigger-ingestion.test.ts +19 -17
  427. package/src/scenarios/trigger-stream-cdc-sources.test.ts +16 -14
  428. package/src/scenarios/v2-approver-enforced.test.ts +97 -0
  429. package/src/scenarios/v2-assurance-downgrade-audited.test.ts +75 -0
  430. package/src/scenarios/v2-bundle-v3-signed.test.ts +96 -0
  431. package/src/scenarios/v2-capabilities-root-closed.test.ts +63 -0
  432. package/src/scenarios/v2-capability-record-shape.test.ts +104 -0
  433. package/src/scenarios/v2-chain-pin-exact.test.ts +118 -0
  434. package/src/scenarios/v2-coherence-not-in-bundle.test.ts +98 -0
  435. package/src/scenarios/v2-compensation-read-projection.test.ts +85 -0
  436. package/src/scenarios/v2-configurable-closed.test.ts +67 -0
  437. package/src/scenarios/v2-dual-stack-negotiation.test.ts +118 -0
  438. package/src/scenarios/v2-effect-identity-business-key.test.ts +132 -0
  439. package/src/scenarios/v2-effect-seam-manifest.test.ts +163 -0
  440. package/src/scenarios/v2-enum-growth-rule.test.ts +69 -0
  441. package/src/scenarios/v2-era-key.test.ts +86 -0
  442. package/src/scenarios/v2-error-registry.test.ts +89 -0
  443. package/src/scenarios/v2-event-type-closed.test.ts +97 -0
  444. package/src/scenarios/v2-fork-a-v1-run.test.ts +109 -0
  445. package/src/scenarios/v2-form-when-reuses-edge-conditions.test.ts +70 -0
  446. package/src/scenarios/v2-header-scheme.test.ts +67 -0
  447. package/src/scenarios/v2-id-grammar.test.ts +77 -0
  448. package/src/scenarios/v2-idempotency-key-grammar.test.ts +65 -0
  449. package/src/scenarios/v2-interrupt-token-scheme.test.ts +55 -0
  450. package/src/scenarios/v2-lane-issuer-advertised.test.ts +62 -0
  451. package/src/scenarios/v2-legacy-profiles-absent.test.ts +78 -0
  452. package/src/scenarios/v2-manifest-ceiling-refused.test.ts +130 -0
  453. package/src/scenarios/v2-manifest-hatch-carried.test.ts +120 -0
  454. package/src/scenarios/v2-min-client-version.test.ts +48 -0
  455. package/src/scenarios/v2-minimum-version-refused.test.ts +141 -0
  456. package/src/scenarios/v2-mrtr-rounds-ceiling.test.ts +95 -0
  457. package/src/scenarios/v2-negotiation-authenticated.test.ts +125 -0
  458. package/src/scenarios/v2-negotiation-decided-emitted.test.ts +149 -0
  459. package/src/scenarios/v2-no-transport-advertisement.test.ts +57 -0
  460. package/src/scenarios/v2-owner-subject-required.test.ts +72 -0
  461. package/src/scenarios/v2-pack-isolation.test.ts +147 -0
  462. package/src/scenarios/v2-payload-registry-closed.test.ts +110 -0
  463. package/src/scenarios/v2-peer-dependency-declared.test.ts +132 -0
  464. package/src/scenarios/v2-pinned-run-disposition.test.ts +124 -0
  465. package/src/scenarios/v2-poll-cursor-v2.test.ts +114 -0
  466. package/src/scenarios/v2-preferred-version-default.test.ts +83 -0
  467. package/src/scenarios/v2-profiles-derived-only.test.ts +70 -0
  468. package/src/scenarios/v2-provider-conflict.test.ts +115 -0
  469. package/src/scenarios/v2-refresh-sla.test.ts +78 -0
  470. package/src/scenarios/v2-relaxation-recorded.test.ts +134 -0
  471. package/src/scenarios/v2-revocation-honored.test.ts +66 -0
  472. package/src/scenarios/v2-subject-link-record.test.ts +102 -0
  473. package/src/scenarios/v2-unmapped-type-refused.test.ts +95 -0
  474. package/src/scenarios/v2-v1-events-translated.test.ts +105 -0
  475. package/src/scenarios/v2-v1-signed-webhook-accepted.test.ts +109 -0
  476. package/src/scenarios/v2-webhook-durable-delivery.test.ts +238 -0
  477. package/src/scenarios/v2-well-known-one-resource.test.ts +129 -0
  478. package/src/scenarios/vector-knn-roundtrip.test.ts +9 -7
  479. package/src/scenarios/verifier-gating.test.ts +8 -6
  480. package/src/scenarios/version-fold.test.ts +9 -8
  481. package/src/scenarios/version-negotiation.test.ts +12 -11
  482. package/src/scenarios/voice-bargein-no-partial-leak.test.ts +8 -6
  483. package/src/scenarios/voice-event-payloads-shape.test.ts +10 -11
  484. package/src/scenarios/voice-interim-not-durable.test.ts +7 -5
  485. package/src/scenarios/voice-streamref-tenant-bound.test.ts +5 -3
  486. package/src/scenarios/voice-synthesis-streaming.test.ts +11 -9
  487. package/src/scenarios/voice-transcription-streaming.test.ts +8 -6
  488. package/src/scenarios/voice-transcription-unadvertised.test.ts +5 -3
  489. package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +3 -2
  490. package/src/scenarios/wasm-pack-invoke-completed.test.ts +3 -2
  491. package/src/scenarios/wasm-pack-invoke-suspended.test.ts +3 -2
  492. package/src/scenarios/wasm-pack-load.test.ts +4 -3
  493. package/src/scenarios/wasm-pack-memory-cap.test.ts +5 -4
  494. package/src/scenarios/wasm-pack-replay-determinism.test.ts +2 -1
  495. package/src/scenarios/webhook-negative.test.ts +11 -9
  496. package/src/scenarios/webhook-receiver-adversarial.test.ts +8 -7
  497. package/src/scenarios/webhook-sig-algorithm.test.ts +5 -3
  498. package/src/scenarios/webhook-signed-delivery.test.ts +16 -15
  499. package/src/scenarios/webhook-tenant-isolation.test.ts +16 -14
  500. package/src/scenarios/workflow-chain-deferred-parameters.test.ts +28 -30
  501. package/src/scenarios/workflow-chain-expansion.test.ts +21 -20
  502. package/src/scenarios/workflow-chain-host-expansion.test.ts +21 -20
  503. package/src/scenarios/workflow-chain-pack-manifest-validation.test.ts +14 -12
  504. package/src/scenarios/workflow-chain-pack-signature-verification.test.ts +5 -4
  505. package/src/scenarios/workflow-chain-unresolvable-typeid.test.ts +9 -8
  506. package/src/scenarios/workflow-primary-output-annotation.test.ts +7 -6
  507. package/src/scenarios/workflow-variable-format.test.ts +24 -24
  508. package/src/scenarios/workload-identity-behavior.test.ts +15 -13
  509. package/src/scenarios/workload-identity-chain-bounds.test.ts +18 -17
  510. package/src/scenarios/workspace-behavior.test.ts +19 -18
  511. package/src/scenarios/workspace-capability-shape.test.ts +3 -2
  512. package/src/scenarios/workspace-cross-tenant-isolation-blackbox.test.ts +6 -5
  513. package/src/scenarios/workspace-cross-tenant-isolation.test.ts +7 -6
  514. package/src/scenarios/x-openwop-form-pack-manifest.test.ts +8 -7
  515. package/src/setup.ts +44 -6
  516. package/vitest.config.ts +5 -1
  517. package/api/.redocly.lint-ignore.yaml +0 -22
  518. package/api/asyncapi.yaml +0 -834
  519. package/api/grpc/openwop.proto +0 -251
  520. package/api/openapi.yaml +0 -2693
  521. package/api/redocly.yaml +0 -23
  522. package/schemas/README.md +0 -144
  523. package/schemas/a2a-task-state.schema.json +0 -78
  524. package/schemas/a2ui-surface-delta-frame.schema.json +0 -48
  525. package/schemas/agent-deployment-transition.schema.json +0 -49
  526. package/schemas/agent-deployment.schema.json +0 -54
  527. package/schemas/agent-eval-suite.schema.json +0 -140
  528. package/schemas/agent-inventory-response.schema.json +0 -115
  529. package/schemas/agent-manifest.schema.json +0 -156
  530. package/schemas/agent-org-chart.schema.json +0 -82
  531. package/schemas/agent-ref.schema.json +0 -63
  532. package/schemas/agent-roster-entry.schema.json +0 -81
  533. package/schemas/agent-roster-response.schema.json +0 -21
  534. package/schemas/ai-envelope.schema.json +0 -134
  535. package/schemas/annotation-create.schema.json +0 -37
  536. package/schemas/annotation.schema.json +0 -56
  537. package/schemas/artifact-type-pack-manifest.schema.json +0 -244
  538. package/schemas/audit-verify-result.schema.json +0 -90
  539. package/schemas/budget-policy.schema.json +0 -18
  540. package/schemas/capabilities.schema.json +0 -4160
  541. package/schemas/certification-bundle-v2.schema.json +0 -118
  542. package/schemas/channel-presence-payload.schema.json +0 -41
  543. package/schemas/channel-written-payload.schema.json +0 -55
  544. package/schemas/chat-card-pack-manifest.schema.json +0 -253
  545. package/schemas/compact-tool-descriptor.schema.json +0 -51
  546. package/schemas/compensation-policy.schema.json +0 -64
  547. package/schemas/conformance-certification-bundle.schema.json +0 -86
  548. package/schemas/connection-pack-manifest.schema.json +0 -353
  549. package/schemas/conversation-event.schema.json +0 -168
  550. package/schemas/conversation-turn.schema.json +0 -117
  551. package/schemas/core-conformance-mock-agent-config.schema.json +0 -152
  552. package/schemas/credential-provenance.schema.json +0 -18
  553. package/schemas/credential-reference.schema.json +0 -21
  554. package/schemas/debug-bundle.schema.json +0 -196
  555. package/schemas/dispatch-config.schema.json +0 -112
  556. package/schemas/envelopes/clarification.request.schema.json +0 -52
  557. package/schemas/envelopes/error.schema.json +0 -30
  558. package/schemas/envelopes/media.audio.schema.json +0 -38
  559. package/schemas/envelopes/media.file.schema.json +0 -37
  560. package/schemas/envelopes/media.image.schema.json +0 -33
  561. package/schemas/envelopes/schema.request.schema.json +0 -26
  562. package/schemas/envelopes/schema.response.schema.json +0 -22
  563. package/schemas/envelopes/ui.a2ui-surface.schema.json +0 -154
  564. package/schemas/error-envelope.schema.json +0 -25
  565. package/schemas/eval-summary.schema.json +0 -92
  566. package/schemas/export-bundle.schema.json +0 -66
  567. package/schemas/form-content-pack-manifest.schema.json +0 -243
  568. package/schemas/frontend-plugin-manifest.schema.json +0 -109
  569. package/schemas/goal.schema.json +0 -104
  570. package/schemas/heartbeat-evaluated.schema.json +0 -14
  571. package/schemas/heartbeat-state-changed.schema.json +0 -14
  572. package/schemas/localized-content-language-settings.schema.json +0 -26
  573. package/schemas/localized-content-page-response.schema.json +0 -60
  574. package/schemas/localized-content-page.schema.json +0 -62
  575. package/schemas/localized-content-section.schema.json +0 -51
  576. package/schemas/memory-entry.schema.json +0 -36
  577. package/schemas/memory-list-options.schema.json +0 -37
  578. package/schemas/node-pack-manifest.schema.json +0 -654
  579. package/schemas/orchestrator-decision.schema.json +0 -90
  580. package/schemas/org-chart-responsibility-view.schema.json +0 -26
  581. package/schemas/pack-lockfile.schema.json +0 -108
  582. package/schemas/prompt-kind.schema.json +0 -8
  583. package/schemas/prompt-pack-manifest.schema.json +0 -117
  584. package/schemas/prompt-ref.schema.json +0 -40
  585. package/schemas/prompt-template.schema.json +0 -149
  586. package/schemas/proposal.schema.json +0 -84
  587. package/schemas/registry-version-manifest.schema.json +0 -208
  588. package/schemas/residency.schema.json +0 -16
  589. package/schemas/run-ancestry-response.schema.json +0 -54
  590. package/schemas/run-diff-response.schema.json +0 -64
  591. package/schemas/run-event-payloads.schema.json +0 -4452
  592. package/schemas/run-event.schema.json +0 -211
  593. package/schemas/run-options.schema.json +0 -80
  594. package/schemas/run-orchestrator-decided-event.schema.json +0 -20
  595. package/schemas/run-snapshot.schema.json +0 -148
  596. package/schemas/security-advisory.schema.json +0 -109
  597. package/schemas/self-hosted-runner-dispatch-frame.schema.json +0 -79
  598. package/schemas/self-hosted-runner-registration.schema.json +0 -53
  599. package/schemas/self-hosted-runner-result-frame.schema.json +0 -38
  600. package/schemas/subject.schema.json +0 -56
  601. package/schemas/suspend-request.schema.json +0 -207
  602. package/schemas/tool-descriptor.schema.json +0 -63
  603. package/schemas/trigger-event.schema.json +0 -222
  604. package/schemas/trigger-subscription-registration.schema.json +0 -67
  605. package/schemas/trigger-subscription.schema.json +0 -26
  606. package/schemas/ui-plugin-message.schema.json +0 -90
  607. package/schemas/workflow-chain-pack-manifest.schema.json +0 -615
  608. package/schemas/workflow-definition.schema.json +0 -850
  609. package/schemas/workload-identity.schema.json +0 -79
  610. package/schemas/workspace-file-create.schema.json +0 -20
  611. package/schemas/workspace-file.schema.json +0 -39
@@ -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);
@@ -35,8 +35,7 @@ import { join } from 'node:path';
35
35
  import Ajv2020 from 'ajv/dist/2020.js';
36
36
  import addFormats from 'ajv-formats';
37
37
  import { SCHEMAS_DIR } from '../lib/paths.js';
38
-
39
- const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
38
+ import { req } from '../lib/requirement-ids.js';
40
39
 
41
40
  function loadSchema(name: string): Record<string, unknown> {
42
41
  return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
@@ -52,17 +51,17 @@ describe('budget-policy-shape: BudgetPolicy (RFC 0084 §A, server-free)', () =>
52
51
  it('a conforming budget policy validates', () => {
53
52
  expect(
54
53
  validate({ maxTokens: 200000, maxCostUsd: 1.0, maxToolCalls: 50, maxRetries: 10, modelAllow: ['claude-*'], modelDeny: ['gpt-4-32k'], thresholdPercent: 80, onExhaustion: 'fail' }),
55
- why('budget-policy.md §A', 'a conforming BudgetPolicy MUST validate'),
54
+ req('openwop.it.budget-policy-shape.a-conforming-budget-policy-validates', 'budget-policy.md §A', 'a conforming BudgetPolicy MUST validate'),
56
55
  ).toBe(true);
57
56
  });
58
57
 
59
58
  it('the orthogonality guard: a wall-time field is rejected (it is RFC 0058 runTimeoutMs)', () => {
60
- expect(validate({ maxCostUsd: 1.0, maxWallTimeMs: 60000 }), why('budget-policy.md §A/§E', 'wall-time is NOT a budget dimension')).toBe(false);
59
+ expect(validate({ maxCostUsd: 1.0, maxWallTimeMs: 60000 }), req('openwop.it.budget-policy-shape.the-orthogonality-guard-a-wall-time-field-is-rejected-it-is-rfc-0058-runtimeoutm', 'budget-policy.md §A/§E', 'wall-time is NOT a budget dimension')).toBe(false);
61
60
  });
62
61
 
63
62
  it('rejects an out-of-range thresholdPercent and an out-of-enum onExhaustion', () => {
64
- expect(validate({ thresholdPercent: 120 }), why('budget-policy.md §A', 'thresholdPercent MUST be 0..100')).toBe(false);
65
- expect(validate({ onExhaustion: 'explode' }), why('budget-policy.md §A', 'onExhaustion is a closed enum')).toBe(false);
63
+ expect(validate({ thresholdPercent: 120 }), req('openwop.it.budget-policy-shape.rejects-an-out-of-range-thresholdpercent-and-an-out-of-enum-onexhaustion', 'budget-policy.md §A', 'thresholdPercent MUST be 0..100')).toBe(false);
64
+ expect(validate({ onExhaustion: 'explode' }), req('openwop.it.budget-policy-shape.rejects-an-out-of-range-thresholdpercent-and-an-out-of-enum-onexhaustion', 'budget-policy.md §A', 'onExhaustion is a closed enum')).toBe(false);
66
65
  });
67
66
  });
68
67
 
@@ -76,21 +75,21 @@ describe('budget-policy-shape: budget.* events + cap.breached kinds (RFC 0084 §
76
75
  } as Record<string, unknown>);
77
76
 
78
77
  it('the four budget.* payloads validate conforming content-free records', () => {
79
- expect(compile('budgetReserved')({ effectiveBudget: { maxCostUsd: 1.0 }, scope: 'run' }), why('budget-policy.md §C', 'budget.reserved MUST validate')).toBe(true);
80
- expect(compile('budgetConsumed')({ dimension: 'cost', consumed: 0.7, limit: 1.0, remaining: 0.3 }), why('budget-policy.md §C', 'budget.consumed MUST validate')).toBe(true);
81
- expect(compile('budgetThresholdCrossed')({ dimension: 'cost', consumed: 0.8, limit: 1.0, percent: 80 }), why('budget-policy.md §C', 'budget.threshold.crossed MUST validate')).toBe(true);
82
- expect(compile('budgetExhausted')({ dimension: 'cost', consumed: 1.02, limit: 1.0 }), why('budget-policy.md §C', 'budget.exhausted MUST validate')).toBe(true);
78
+ expect(compile('budgetReserved')({ effectiveBudget: { maxCostUsd: 1.0 }, scope: 'run' }), req('openwop.it.budget-policy-shape.the-four-budget-payloads-validate-conforming-content-free-records', 'budget-policy.md §C', 'budget.reserved MUST validate')).toBe(true);
79
+ expect(compile('budgetConsumed')({ dimension: 'cost', consumed: 0.7, limit: 1.0, remaining: 0.3 }), req('openwop.it.budget-policy-shape.the-four-budget-payloads-validate-conforming-content-free-records', 'budget-policy.md §C', 'budget.consumed MUST validate')).toBe(true);
80
+ expect(compile('budgetThresholdCrossed')({ dimension: 'cost', consumed: 0.8, limit: 1.0, percent: 80 }), req('openwop.it.budget-policy-shape.the-four-budget-payloads-validate-conforming-content-free-records', 'budget-policy.md §C', 'budget.threshold.crossed MUST validate')).toBe(true);
81
+ expect(compile('budgetExhausted')({ dimension: 'cost', consumed: 1.02, limit: 1.0 }), req('openwop.it.budget-policy-shape.the-four-budget-payloads-validate-conforming-content-free-records', 'budget-policy.md §C', 'budget.exhausted MUST validate')).toBe(true);
83
82
  });
84
83
 
85
84
  it('rejects an out-of-enum dimension and a missing required field', () => {
86
- expect(compile('budgetConsumed')({ dimension: 'vibes', consumed: 1, limit: 2 }), why('budget-policy.md §C', 'dimension is a closed enum')).toBe(false);
87
- expect(compile('budgetExhausted')({ dimension: 'cost', consumed: 1.0 }), why('budget-policy.md §C', 'limit is REQUIRED')).toBe(false);
85
+ expect(compile('budgetConsumed')({ dimension: 'vibes', consumed: 1, limit: 2 }), req('openwop.it.budget-policy-shape.rejects-an-out-of-enum-dimension-and-a-missing-required-field', 'budget-policy.md §C', 'dimension is a closed enum')).toBe(false);
86
+ expect(compile('budgetExhausted')({ dimension: 'cost', consumed: 1.0 }), req('openwop.it.budget-policy-shape.rejects-an-out-of-enum-dimension-and-a-missing-required-field', 'budget-policy.md §C', 'limit is REQUIRED')).toBe(false);
88
87
  });
89
88
 
90
89
  it('the cap.breached kind enum carries the four budget-* values', () => {
91
90
  const kinds = ((payloads.$defs as Record<string, { properties?: Record<string, { enum?: string[] }> }>).capBreached.properties?.kind?.enum) ?? [];
92
91
  for (const k of ['budget-tokens', 'budget-cost', 'budget-tool-calls', 'budget-retries']) {
93
- expect(kinds.includes(k), why('budget-policy.md §D', `cap.breached.kind MUST include ${k}`)).toBe(true);
92
+ expect(kinds.includes(k), req('openwop.it.budget-policy-shape.the-cap-breached-kind-enum-carries-the-four-budget-values', 'budget-policy.md §D', `cap.breached.kind MUST include ${k}`)).toBe(true);
94
93
  }
95
94
  });
96
95
 
@@ -98,7 +97,7 @@ describe('budget-policy-shape: budget.* events + cap.breached kinds (RFC 0084 §
98
97
  const runEvent = loadSchema('run-event.schema.json');
99
98
  const enumVals = ((runEvent.$defs as Record<string, { enum?: string[] }>).RunEventType?.enum) ?? [];
100
99
  for (const name of ['budget.reserved', 'budget.consumed', 'budget.threshold.crossed', 'budget.exhausted']) {
101
- expect(enumVals.includes(name), why('run-event.schema.json', `${name} MUST be in the RunEventType enum`)).toBe(true);
100
+ expect(enumVals.includes(name), req('openwop.it.budget-policy-shape.all-four-budget-event-names-appear-in-the-runeventtype-enum', 'run-event.schema.json', `${name} MUST be in the RunEventType enum`)).toBe(true);
102
101
  }
103
102
  });
104
103
 
@@ -106,7 +105,7 @@ describe('budget-policy-shape: budget.* events + cap.breached kinds (RFC 0084 §
106
105
  const defs = payloads.$defs as Record<string, { properties?: Record<string, unknown> }>;
107
106
  for (const def of ['budgetReserved', 'budgetConsumed', 'budgetThresholdCrossed', 'budgetExhausted']) {
108
107
  for (const p of Object.keys(defs[def].properties ?? {})) {
109
- expect(PRICING_PROP_NAMES.includes(p.toLowerCase()), why('budget-no-pricing-leak', `${def} MUST NOT declare a pricing-bearing property (${p})`)).toBe(false);
108
+ expect(PRICING_PROP_NAMES.includes(p.toLowerCase()), req('openwop.it.budget-policy-shape.the-budget-payloads-declare-no-pricing-credential-property-budget-no-pricing-lea', 'budget-no-pricing-leak', `${def} MUST NOT declare a pricing-bearing property (${p})`)).toBe(false);
110
109
  }
111
110
  }
112
111
  });
@@ -114,10 +113,10 @@ describe('budget-policy-shape: budget.* events + cap.breached kinds (RFC 0084 §
114
113
  it('the budget.* payloads are additionalProperties:false — a rate-card field on an INSTANCE is rejected', () => {
115
114
  // The aggregate cost total (the user's own budget) is permitted; the host's per-unit rate card is not.
116
115
  // additionalProperties:false makes the rejection structural, not just a declared-property check.
117
- expect(compile('budgetConsumed')({ dimension: 'cost', consumed: 0.8, limit: 1.0 }), why('budget-policy.md §F', 'an aggregate cost total (the user budget) MUST validate')).toBe(true);
116
+ expect(compile('budgetConsumed')({ dimension: 'cost', consumed: 0.8, limit: 1.0 }), req('openwop.it.budget-policy-shape.the-budget-payloads-are-additionalproperties-false-a-rate-card-field-on-an-insta', 'budget-policy.md §F', 'an aggregate cost total (the user budget) MUST validate')).toBe(true);
118
117
  expect(
119
118
  compile('budgetConsumed')({ dimension: 'cost', consumed: 0.8, limit: 1.0, ratePerToken: 0.000003 }),
120
- why('budget-no-pricing-leak', 'a rate-card / per-token-price field MUST be rejected (additionalProperties:false)'),
119
+ req('openwop.it.budget-policy-shape.the-budget-payloads-are-additionalproperties-false-a-rate-card-field-on-an-insta', 'budget-no-pricing-leak', 'a rate-card / per-token-price field MUST be rejected (additionalProperties:false)'),
121
120
  ).toBe(false);
122
121
  });
123
122
  });
@@ -127,10 +126,10 @@ describe('budget-policy-shape: capability advertisement (RFC 0084 §E, server-fr
127
126
  const caps = loadSchema('capabilities.schema.json');
128
127
  const props = caps.properties as Record<string, { properties?: Record<string, unknown> }>;
129
128
  for (const flag of ['supported', 'dimensions', 'enforce', 'scopes']) {
130
- expect(props.budget?.properties?.[flag], why('budget-policy.md §E', `capabilities.budget.${flag} MUST be declared`)).toBeDefined();
129
+ expect(props.budget?.properties?.[flag], req('openwop.it.budget-policy-shape.capabilities-budget-limits-maxbudget-tokens-costusd-are-declared', 'budget-policy.md §E', `capabilities.budget.${flag} MUST be declared`)).toBeDefined();
131
130
  }
132
131
  for (const ceiling of ['maxBudgetTokens', 'maxBudgetCostUsd']) {
133
- expect(props.limits?.properties?.[ceiling], why('budget-policy.md §E', `limits.${ceiling} MUST be declared`)).toBeDefined();
132
+ expect(props.limits?.properties?.[ceiling], req('openwop.it.budget-policy-shape.capabilities-budget-limits-maxbudget-tokens-costusd-are-declared', 'budget-policy.md §E', `limits.${ceiling} MUST be declared`)).toBeDefined();
134
133
  }
135
134
  });
136
135
  });
@@ -20,6 +20,7 @@
20
20
  import { describe, it, expect } from 'vitest';
21
21
  import { driver } from '../lib/driver.js';
22
22
  import { isFixtureAdvertised } from '../lib/fixtures.js';
23
+ import { req } from '../lib/requirement-ids.js';
23
24
 
24
25
  const CANCELLABLE = 'conformance-cancellable';
25
26
  const NOOP = 'conformance-noop';
@@ -48,22 +49,22 @@ describe.skipIf(SKIP)('bulk-cancel: POST /v1/runs:bulk-cancel', () => {
48
49
  runIds: [inflightRunId, 'run-does-not-exist-xxxxxxxx'],
49
50
  reason: 'conformance bulk-cancel test',
50
51
  });
51
- expect(res.status, driver.describe(
52
+ expect(res.status, req('openwop.it.bulk-cancel.mixed-outcome-request-returns-per-id-results-in-order',
52
53
  'rest-endpoints.md §"POST /v1/runs:bulk-cancel"',
53
54
  'top-level operation MUST return 200 when the request reached the host (per-id outcomes carry partial failure)',
54
55
  )).toBe(200);
55
56
 
56
57
  const body = res.json as { results: BulkResult[] };
57
58
  expect(Array.isArray(body.results)).toBe(true);
58
- expect(body.results.length, 'results MUST have one entry per request runId').toBe(2);
59
+ expect(body.results.length, req('openwop.it.bulk-cancel.mixed-outcome-request-returns-per-id-results-in-order', 'rest-endpoints.md §"POST /v1/runs:bulk-cancel"', 'results MUST have one entry per request runId')).toBe(2);
59
60
 
60
- expect(body.results[0]!.runId, 'results order MUST mirror the request order').toBe(inflightRunId);
61
+ expect(body.results[0]!.runId, req('openwop.it.bulk-cancel.mixed-outcome-request-returns-per-id-results-in-order', 'rest-endpoints.md §"POST /v1/runs:bulk-cancel"', 'results order MUST mirror the request order')).toBe(inflightRunId);
61
62
  expect(body.results[0]!.ok).toBe(true);
62
63
  expect(['cancelling', 'cancelled']).toContain(body.results[0]!.status);
63
64
 
64
65
  expect(body.results[1]!.runId).toBe('run-does-not-exist-xxxxxxxx');
65
- expect(body.results[1]!.ok, 'unknown runId entry MUST have ok=false').toBe(false);
66
- expect(body.results[1]!.error?.code, driver.describe(
66
+ expect(body.results[1]!.ok, req('openwop.it.bulk-cancel.mixed-outcome-request-returns-per-id-results-in-order', 'rest-endpoints.md §"POST /v1/runs:bulk-cancel"', 'unknown runId entry MUST have ok=false')).toBe(false);
67
+ expect(body.results[1]!.error?.code, req('openwop.it.bulk-cancel.mixed-outcome-request-returns-per-id-results-in-order',
67
68
  'rest-endpoints.md §"POST /v1/runs:bulk-cancel"',
68
69
  'unknown runId outcomes carry `error.code === "not_found"`',
69
70
  )).toBe('not_found');
@@ -71,7 +72,7 @@ describe.skipIf(SKIP)('bulk-cancel: POST /v1/runs:bulk-cancel', () => {
71
72
 
72
73
  it('empty runIds array returns 400 validation_error', async () => {
73
74
  const res = await driver.post('/v1/runs:bulk-cancel', { runIds: [] });
74
- expect(res.status).toBe(400);
75
+ expect(res.status, req('openwop.it.bulk-cancel.empty-runids-array-returns-400-validation-error', 'rest-endpoints.md §Open', 'empty runIds array returns 400 validation_error')).toBe(400);
75
76
  const body = res.json as { error?: string };
76
77
  expect(body.error).toBe('validation_error');
77
78
  });
@@ -83,7 +84,7 @@ describe.skipIf(SKIP)('bulk-cancel: POST /v1/runs:bulk-cancel', () => {
83
84
  expect(res.status).toBe(400);
84
85
  const body = res.json as { error?: string; details?: { maxRunIds?: number } };
85
86
  expect(body.error).toBe('validation_error');
86
- expect(typeof body.details?.maxRunIds, driver.describe(
87
+ expect(typeof body.details?.maxRunIds, req('openwop.it.bulk-cancel.oversized-runids-array-returns-400-with-details-maxrunids',
87
88
  'rest-endpoints.md §"POST /v1/runs:bulk-cancel"',
88
89
  'over-cap request MUST carry details.maxRunIds disclosing the configured ceiling',
89
90
  )).toBe('number');
@@ -103,7 +104,7 @@ describe.skipIf(SKIP)('bulk-cancel: POST /v1/runs:bulk-cancel', () => {
103
104
  const second = await driver.post('/v1/runs:bulk-cancel', { runIds: [runId] });
104
105
  expect(second.status).toBe(200);
105
106
  const body = second.json as { results: BulkResult[] };
106
- expect(body.results[0]!.ok, driver.describe(
107
+ expect(body.results[0]!.ok, req('openwop.it.bulk-cancel.re-bulk-cancel-after-first-cancel-is-idempotent',
107
108
  'rest-endpoints.md §"POST /v1/runs:bulk-cancel" §Idempotency',
108
109
  're-cancelling an already-cancelling/cancelled run MUST be ok: true (idempotent)',
109
110
  )).toBe(true);