@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
@@ -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>;
@@ -54,32 +53,32 @@ describe('tool-descriptor-shape: ToolDescriptor (RFC 0078 §C, server-free)', ()
54
53
  egress: 'none', approval: 'never', replayPolicy: 'idempotent',
55
54
  safetyTier: 'read', costHint: 'low', latencyHint: 'low',
56
55
  }),
57
- why('tool-catalog.md §C', 'a conforming ToolDescriptor MUST validate'),
56
+ req('openwop.it.tool-descriptor-shape.a-conforming-descriptor-validates', 'tool-catalog.md §C', 'a conforming ToolDescriptor MUST validate'),
58
57
  ).toBe(true);
59
58
  });
60
59
 
61
60
  it('a descriptor missing the REQUIRED safetyTier is rejected', () => {
62
61
  expect(
63
62
  validate({ toolId: 'x', source: 'mcp' }),
64
- why('tool-catalog.md §C', 'safetyTier is REQUIRED'),
63
+ req('openwop.it.tool-descriptor-shape.a-descriptor-missing-the-required-safetytier-is-rejected', 'tool-catalog.md §C', 'safetyTier is REQUIRED'),
65
64
  ).toBe(false);
66
65
  });
67
66
 
68
67
  it('enforces exec ⇒ host-extension (RFC 0069; §C-1/§F-4)', () => {
69
68
  expect(
70
69
  validate({ toolId: 'x-host-acme-shell', source: 'host-extension', safetyTier: 'exec', approval: 'always', egress: 'host-owned' }),
71
- why('tool-catalog.md §C-1', 'an exec tool sourced from host-extension MUST validate'),
70
+ req('openwop.it.tool-descriptor-shape.enforces-exec-host-extension-rfc-0069-c-1-f-4', 'tool-catalog.md §C-1', 'an exec tool sourced from host-extension MUST validate'),
72
71
  ).toBe(true);
73
72
  expect(
74
73
  validate({ toolId: 'openwop:run-shell', source: 'node-pack', safetyTier: 'exec' }),
75
- why('tool-catalog.md §C-1 / RFC 0069', 'an exec tool MUST NOT be protocol-tier (node-pack)'),
74
+ req('openwop.it.tool-descriptor-shape.enforces-exec-host-extension-rfc-0069-c-1-f-4', 'tool-catalog.md §C-1 / RFC 0069', 'an exec tool MUST NOT be protocol-tier (node-pack)'),
76
75
  ).toBe(false);
77
76
  });
78
77
 
79
78
  it('rejects an unknown property (additionalProperties:false)', () => {
80
79
  expect(
81
80
  validate({ toolId: 'x', source: 'mcp', safetyTier: 'read', danger: true }),
82
- why('tool-catalog.md §C', 'ToolDescriptor MUST be additionalProperties:false'),
81
+ req('openwop.it.tool-descriptor-shape.rejects-an-unknown-property-additionalproperties-false', 'tool-catalog.md §C', 'ToolDescriptor MUST be additionalProperties:false'),
83
82
  ).toBe(false);
84
83
  });
85
84
  });
@@ -90,12 +89,12 @@ describe('tool-descriptor-shape: capability advertisement (RFC 0078 §A, server-
90
89
  const toolCatalog = (caps.properties as Record<string, { properties?: Record<string, unknown> }>).toolCatalog;
91
90
  expect(
92
91
  toolCatalog,
93
- why('capabilities.md §toolCatalog', 'capabilities.toolCatalog MUST be declared'),
92
+ req('openwop.it.tool-descriptor-shape.capabilities-toolcatalog-is-declared-with-its-sub-flags', 'capabilities.md §toolCatalog', 'capabilities.toolCatalog MUST be declared'),
94
93
  ).toBeDefined();
95
94
  for (const flag of ['supported', 'sources', 'sessionLifecycle']) {
96
95
  expect(
97
96
  toolCatalog?.properties?.[flag],
98
- why('tool-catalog.md §A', `capabilities.toolCatalog.${flag} MUST be declared`),
97
+ req('openwop.it.tool-descriptor-shape.capabilities-toolcatalog-is-declared-with-its-sub-flags', 'tool-catalog.md §A', `capabilities.toolCatalog.${flag} MUST be declared`),
99
98
  ).toBeDefined();
100
99
  }
101
100
  });
@@ -112,22 +111,22 @@ describe('tool-descriptor-shape: session lifecycle events (RFC 0078 §D, server-
112
111
 
113
112
  it('tool.session.opened validates a content-free record', () => {
114
113
  const v = compile('toolSessionOpened');
115
- expect(v({ sessionId: 's1', toolId: 'mcp:fs.read' }), why('tool-catalog.md §D', 'opened MUST validate')).toBe(true);
116
- expect(v({ toolId: 'mcp:fs.read' }), why('tool-catalog.md §D', 'opened requires sessionId')).toBe(false);
114
+ expect(v({ sessionId: 's1', toolId: 'mcp:fs.read' }), req('openwop.it.tool-descriptor-shape.tool-session-opened-validates-a-content-free-record', 'tool-catalog.md §D', 'opened MUST validate')).toBe(true);
115
+ expect(v({ toolId: 'mcp:fs.read' }), req('openwop.it.tool-descriptor-shape.tool-session-opened-validates-a-content-free-record', 'tool-catalog.md §D', 'opened requires sessionId')).toBe(false);
117
116
  });
118
117
 
119
118
  it('tool.session.closed validates + enforces the closed outcome enum', () => {
120
119
  const v = compile('toolSessionClosed');
121
- expect(v({ sessionId: 's1', toolId: 'mcp:fs.read', outcome: 'completed' }), why('tool-catalog.md §D', 'closed MUST validate')).toBe(true);
122
- expect(v({ sessionId: 's1', toolId: 'mcp:fs.read' }), why('tool-catalog.md §D', 'closed requires outcome')).toBe(false);
123
- expect(v({ sessionId: 's1', toolId: 'mcp:fs.read', outcome: 'exploded' }), why('tool-catalog.md §D', 'outcome is a closed enum')).toBe(false);
120
+ expect(v({ sessionId: 's1', toolId: 'mcp:fs.read', outcome: 'completed' }), req('openwop.it.tool-descriptor-shape.tool-session-closed-validates-enforces-the-closed-outcome-enum', 'tool-catalog.md §D', 'closed MUST validate')).toBe(true);
121
+ expect(v({ sessionId: 's1', toolId: 'mcp:fs.read' }), req('openwop.it.tool-descriptor-shape.tool-session-closed-validates-enforces-the-closed-outcome-enum', 'tool-catalog.md §D', 'closed requires outcome')).toBe(false);
122
+ expect(v({ sessionId: 's1', toolId: 'mcp:fs.read', outcome: 'exploded' }), req('openwop.it.tool-descriptor-shape.tool-session-closed-validates-enforces-the-closed-outcome-enum', 'tool-catalog.md §D', 'outcome is a closed enum')).toBe(false);
124
123
  });
125
124
 
126
125
  it('both session event names appear in the RunEventType enum', () => {
127
126
  const runEvent = loadSchema('run-event.schema.json');
128
127
  const enumVals = ((runEvent.$defs as Record<string, { enum?: string[] }>).RunEventType?.enum) ?? [];
129
128
  for (const name of ['tool.session.opened', 'tool.session.closed']) {
130
- expect(enumVals.includes(name), why('run-event.schema.json', `${name} MUST be in the RunEventType enum`)).toBe(true);
129
+ expect(enumVals.includes(name), req('openwop.it.tool-descriptor-shape.both-session-event-names-appear-in-the-runeventtype-enum', 'run-event.schema.json', `${name} MUST be in the RunEventType enum`)).toBe(true);
131
130
  }
132
131
  });
133
132
  });
@@ -13,8 +13,8 @@
13
13
 
14
14
  import { describe, it, expect } from 'vitest';
15
15
  import { softSkip } from '../lib/soft-skip.js';
16
- import { driver } from '../lib/driver.js';
17
16
  import { readToolHooksCap, invokeToolHook } from '../lib/toolHooks.js';
17
+ import { req } from '../lib/requirement-ids.js';
18
18
 
19
19
  describe('tool-hooks-authorization-fail-closed (RFC 0064 §C)', () => {
20
20
  it('a principal lacking a tool scope is denied and the tool is not invoked', async () => {
@@ -30,11 +30,11 @@ describe('tool-hooks-authorization-fail-closed (RFC 0064 §C)', () => {
30
30
  if (res === null) return softSkip('blocked', 'seam absent — soft-skip');
31
31
  expect(
32
32
  (res.toolReturned ?? {}).status,
33
- driver.describe('RFC 0064 §C', 'a missing/unevaluable tool scope MUST fail closed → status:"forbidden"'),
33
+ req('openwop.it.tool-hooks-authorization-fail-closed.a-principal-lacking-a-tool-scope-is-denied-and-the-tool-is-not-invoked', 'RFC 0064 §C', 'a missing/unevaluable tool scope MUST fail closed → status:"forbidden"'),
34
34
  ).toBe('forbidden');
35
35
  expect(
36
36
  (res.toolReturned ?? {}).durationMs,
37
- driver.describe('RFC 0064 §C', 'a forbidden call never starts, so durationMs MUST be absent'),
37
+ req('openwop.it.tool-hooks-authorization-fail-closed.a-principal-lacking-a-tool-scope-is-denied-and-the-tool-is-not-invoked', 'RFC 0064 §C', 'a forbidden call never starts, so durationMs MUST be absent'),
38
38
  ).toBeUndefined();
39
39
  });
40
40
  });
@@ -12,8 +12,8 @@
12
12
 
13
13
  import { describe, it, expect } from 'vitest';
14
14
  import { softSkip } from '../lib/soft-skip.js';
15
- import { driver } from '../lib/driver.js';
16
15
  import { readToolHooksCap, invokeToolHook } from '../lib/toolHooks.js';
16
+ import { req } from '../lib/requirement-ids.js';
17
17
 
18
18
  describe('tool-hooks-content-free (RFC 0064 §B)', () => {
19
19
  it('toolCalled carries argsHash; toolReturned carries status + durationMs', async () => {
@@ -25,16 +25,16 @@ describe('tool-hooks-content-free (RFC 0064 §B)', () => {
25
25
  const returned = res.toolReturned ?? {};
26
26
  expect(
27
27
  typeof called.argsHash === 'string' && (called.argsHash as string).length > 0,
28
- driver.describe('RFC 0064 §B', 'agent.toolCalled MUST carry a non-empty argsHash when prePostEvents'),
28
+ req('openwop.it.tool-hooks-content-free.toolcalled-carries-argshash-toolreturned-carries-status-durationms', 'RFC 0064 §B', 'agent.toolCalled MUST carry a non-empty argsHash when prePostEvents'),
29
29
  ).toBe(true);
30
30
  expect(
31
31
  ['ok', 'error', 'forbidden', 'rate_limited'].includes(returned.status as string),
32
- driver.describe('RFC 0064 §B', 'agent.toolReturned MUST carry a tool-hooks status'),
32
+ req('openwop.it.tool-hooks-content-free.toolcalled-carries-argshash-toolreturned-carries-status-durationms', 'RFC 0064 §B', 'agent.toolReturned MUST carry a tool-hooks status'),
33
33
  ).toBe(true);
34
34
  if (returned.status === 'ok') {
35
35
  expect(
36
36
  typeof returned.durationMs === 'number' && (returned.durationMs as number) >= 0,
37
- driver.describe('RFC 0064 §B', 'a completed tool call MUST record a non-negative durationMs'),
37
+ req('openwop.it.tool-hooks-content-free.toolcalled-carries-argshash-toolreturned-carries-status-durationms', 'RFC 0064 §B', 'a completed tool call MUST record a non-negative durationMs'),
38
38
  ).toBe(true);
39
39
  }
40
40
  });
@@ -17,8 +17,8 @@
17
17
 
18
18
  import { describe, it, expect } from 'vitest';
19
19
  import { softSkip } from '../lib/soft-skip.js';
20
- import { driver } from '../lib/driver.js';
21
20
  import { readToolHooksCap, invokeToolHook } from '../lib/toolHooks.js';
21
+ import { req } from '../lib/requirement-ids.js';
22
22
 
23
23
  describe('tool-hooks-failure-honesty (RFC 0064 §F)', () => {
24
24
  it('a failing tool yields a populated error, never a bare success', async () => {
@@ -42,20 +42,20 @@ describe('tool-hooks-failure-honesty (RFC 0064 §F)', () => {
42
42
  // §F: the failure carries a populated `_errorObject { code, message }`.
43
43
  expect(
44
44
  err !== undefined && typeof err['code'] === 'string' && (err['code'] as string).length > 0 && typeof err['message'] === 'string',
45
- driver.describe('RFC 0064 §F', 'a failed tool MUST populate error {code, message} on agent.toolReturned'),
45
+ req('openwop.it.tool-hooks-failure-honesty.a-failing-tool-yields-a-populated-error-never-a-bare-success', 'RFC 0064 §F', 'a failed tool MUST populate error {code, message} on agent.toolReturned'),
46
46
  ).toBe(true);
47
47
 
48
48
  // §F: `error` and `outcome` are mutually exclusive.
49
49
  expect(
50
50
  returned['outcome'] === undefined,
51
- driver.describe('RFC 0064 §F', 'error and outcome are mutually exclusive on a failed tool-return'),
51
+ req('openwop.it.tool-hooks-failure-honesty.a-failing-tool-yields-a-populated-error-never-a-bare-success', 'RFC 0064 §F', 'error and outcome are mutually exclusive on a failed tool-return'),
52
52
  ).toBe(true);
53
53
 
54
54
  // §F: a host advertising `prePostEvents` MUST also set `status: 'error'`.
55
55
  if (cap['prePostEvents'] === true) {
56
56
  expect(
57
57
  returned['status'] === 'error',
58
- driver.describe('RFC 0064 §F', 'a host advertising prePostEvents MUST set status:"error" on a tool failure'),
58
+ req('openwop.it.tool-hooks-failure-honesty.a-failing-tool-yields-a-populated-error-never-a-bare-success', 'RFC 0064 §F', 'a host advertising prePostEvents MUST set status:"error" on a tool failure'),
59
59
  ).toBe(true);
60
60
  }
61
61
  });
@@ -11,8 +11,8 @@
11
11
 
12
12
  import { describe, it, expect } from 'vitest';
13
13
  import { softSkip } from '../lib/soft-skip.js';
14
- import { driver } from '../lib/driver.js';
15
14
  import { readToolHooksCap, invokeToolHook } from '../lib/toolHooks.js';
15
+ import { req } from '../lib/requirement-ids.js';
16
16
 
17
17
  describe('tool-hooks-rate-limit (RFC 0064 §D)', () => {
18
18
  it('an exhausted (principal, tool) bucket yields status:"rate_limited"', async () => {
@@ -27,7 +27,7 @@ describe('tool-hooks-rate-limit (RFC 0064 §D)', () => {
27
27
  if (res === null) return softSkip('blocked', 'seam absent — soft-skip');
28
28
  expect(
29
29
  (res.toolReturned ?? {}).status,
30
- driver.describe('RFC 0064 §D', 'an exhausted token bucket MUST yield status:"rate_limited" without invoking the tool'),
30
+ req('openwop.it.tool-hooks-rate-limit.an-exhausted-principal-tool-bucket-yields-status-rate-limited', 'RFC 0064 §D', 'an exhausted token bucket MUST yield status:"rate_limited" without invoking the tool'),
31
31
  ).toBe('rate_limited');
32
32
  });
33
33
  });
@@ -12,8 +12,8 @@
12
12
 
13
13
  import { describe, it, expect } from 'vitest';
14
14
  import { softSkip } from '../lib/soft-skip.js';
15
- import { driver } from '../lib/driver.js';
16
15
  import { readToolHooksCap, invokeToolHook } from '../lib/toolHooks.js';
16
+ import { req } from '../lib/requirement-ids.js';
17
17
 
18
18
  const CANARY = 'sk-canary-rfc0064-do-not-leak-xyz789';
19
19
 
@@ -29,7 +29,7 @@ describe('tool-hooks-secret-redaction (RFC 0064 §E)', () => {
29
29
  if (res === null) return softSkip('blocked', 'seam absent — soft-skip');
30
30
  expect(
31
31
  JSON.stringify(res).includes(CANARY),
32
- driver.describe('RFC 0064 §B', 'a resolved secret MUST be redacted before hashing; the raw value MUST NOT appear in argsHash or any emitted field (SR-1)'),
32
+ req('openwop.it.tool-hooks-secret-redaction.a-secret-shaped-tool-arg-never-appears-in-the-emitted-events', 'RFC 0064 §B', 'a resolved secret MUST be redacted before hashing; the raw value MUST NOT appear in argsHash or any emitted field (SR-1)'),
33
33
  ).toBe(false);
34
34
  });
35
35
  });
@@ -12,8 +12,8 @@
12
12
 
13
13
  import { describe, it, expect } from 'vitest';
14
14
  import { softSkip } from '../lib/soft-skip.js';
15
- import { driver } from '../lib/driver.js';
16
15
  import { readToolHooksCap } from '../lib/toolHooks.js';
16
+ import { req } from '../lib/requirement-ids.js';
17
17
 
18
18
  describe('tool-hooks-shape: advertisement (RFC 0064 §A)', () => {
19
19
  it('capabilities.toolHooks is absent or a well-formed object', async () => {
@@ -21,13 +21,13 @@ describe('tool-hooks-shape: advertisement (RFC 0064 §A)', () => {
21
21
  if (cap === null) return softSkip('inapplicable', 'not advertised — valid');
22
22
  expect(
23
23
  typeof cap.supported,
24
- driver.describe('capabilities.schema.json §toolHooks', 'toolHooks.supported MUST be a boolean when the block is present'),
24
+ req('openwop.it.tool-hooks-shape.capabilities-toolhooks-is-absent-or-a-well-formed-object', 'capabilities.schema.json §toolHooks', 'toolHooks.supported MUST be a boolean when the block is present'),
25
25
  ).toBe('boolean');
26
26
  for (const k of ['prePostEvents', 'perToolAuthorization', 'perToolRateLimit'] as const) {
27
27
  if (cap[k] !== undefined) {
28
28
  expect(
29
29
  typeof cap[k],
30
- driver.describe('capabilities.schema.json §toolHooks', `toolHooks.${k} MUST be a boolean when present`),
30
+ req('openwop.it.tool-hooks-shape.capabilities-toolhooks-is-absent-or-a-well-formed-object', 'capabilities.schema.json §toolHooks', `toolHooks.${k} MUST be a boolean when present`),
31
31
  ).toBe('boolean');
32
32
  }
33
33
  }
@@ -19,10 +19,11 @@
19
19
  */
20
20
 
21
21
  import { describe, it, expect } from 'vitest';
22
- import { driver } from '../lib/driver.js';
23
22
  import { behaviorGate } from '../lib/behavior-gate.js';
24
23
  import { readToolCatalogCap, driveToolSession, TOOL_CONTENT_FORBIDDEN } from '../lib/toolCatalog.js';
25
24
  import { queryTestEvents, isEventLogSeamAvailable, resetTestSeam } from '../lib/event-log-query.js';
25
+ import { req } from '../lib/requirement-ids.js';
26
+ import { softSkip } from '../lib/soft-skip.js';
26
27
 
27
28
  const SESSION_OUTCOMES = ['completed', 'failed', 'aborted', 'expired'];
28
29
  /** RFC 0064 tool-call event family bracketed by a tool session. */
@@ -35,21 +36,21 @@ describe('tool-session-lifecycle (RFC 0078 §D)', () => {
35
36
  const lifecycle = cap?.sessionLifecycle === true || (typeof cap?.sessionLifecycle === 'object' && cap?.sessionLifecycle !== null);
36
37
  if (!behaviorGate('openwop-tool-session-lifecycle', lifecycle)) return;
37
38
 
38
- if (!(await isEventLogSeamAvailable())) return; // event-log seam absent — soft-skip
39
+ 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
39
40
  const res = await driveToolSession({});
40
- if (res === null || !res.runId) return; // session seam absent — soft-skip
41
+ if (res === null || !res.runId) return softSkip('blocked', 'precondition not met — `res === null || !res.runId` returned early (session seam absent — soft-skip) (seam, prior step, or fixture unavailable)'); // session seam absent — soft-skip
41
42
 
42
43
  const q = await queryTestEvents(res.runId);
43
- if (!q.ok) return;
44
+ if (!q.ok) return softSkip('blocked', 'precondition not met — `!q.ok` returned early (seam, prior step, or fixture unavailable)');
44
45
  const events = q.events.slice().sort((a, b) => a.sequence - b.sequence);
45
46
 
46
47
  const opened = events.filter((e) => e.type === 'tool.session.opened');
47
48
  const closed = events.filter((e) => e.type === 'tool.session.closed');
48
49
  expect(
49
50
  opened.length >= 1 && closed.length >= 1,
50
- driver.describe('tool-catalog.md §D', 'a tool session MUST emit tool.session.opened + tool.session.closed'),
51
+ req('openwop.it.tool-session-lifecycle.brackets-the-call-events-with-tool-session-opened-first-closed-last-one-sessioni', 'tool-catalog.md §D', 'a tool session MUST emit tool.session.opened + tool.session.closed'),
51
52
  ).toBe(true);
52
- if (opened.length === 0 || closed.length === 0) return;
53
+ if (opened.length === 0 || closed.length === 0) return softSkip('blocked', 'precondition not met — `opened.length === 0 || closed.length === 0` returned early (seam, prior step, or fixture unavailable)');
53
54
 
54
55
  const open = opened[0]!;
55
56
  const close = closed[closed.length - 1]!;
@@ -59,16 +60,16 @@ describe('tool-session-lifecycle (RFC 0078 §D)', () => {
59
60
  if (calls.length > 0) {
60
61
  expect(
61
62
  open.sequence < calls[0]!.sequence,
62
- driver.describe('RFC 0078 §D', 'tool.session.opened MUST precede the first call event'),
63
+ req('openwop.it.tool-session-lifecycle.brackets-the-call-events-with-tool-session-opened-first-closed-last-one-sessioni', 'RFC 0078 §D', 'tool.session.opened MUST precede the first call event'),
63
64
  ).toBe(true);
64
65
  expect(
65
66
  close.sequence > calls[calls.length - 1]!.sequence,
66
- driver.describe('RFC 0078 §D', 'tool.session.closed MUST follow the last call event'),
67
+ req('openwop.it.tool-session-lifecycle.brackets-the-call-events-with-tool-session-opened-first-closed-last-one-sessioni', 'RFC 0078 §D', 'tool.session.closed MUST follow the last call event'),
67
68
  ).toBe(true);
68
69
  } else {
69
70
  expect(
70
71
  open.sequence < close.sequence,
71
- driver.describe('RFC 0078 §D', 'tool.session.opened MUST precede tool.session.closed'),
72
+ req('openwop.it.tool-session-lifecycle.brackets-the-call-events-with-tool-session-opened-first-closed-last-one-sessioni', 'RFC 0078 §D', 'tool.session.opened MUST precede tool.session.closed'),
72
73
  ).toBe(true);
73
74
  }
74
75
 
@@ -77,17 +78,17 @@ describe('tool-session-lifecycle (RFC 0078 §D)', () => {
77
78
  const closeSid = close.payload.sessionId;
78
79
  expect(
79
80
  typeof openSid === 'string' && openSid === closeSid,
80
- driver.describe('run-event-payloads.schema.json#toolSession*', 'the bracket MUST share one sessionId'),
81
+ req('openwop.it.tool-session-lifecycle.brackets-the-call-events-with-tool-session-opened-first-closed-last-one-sessioni', 'run-event-payloads.schema.json#toolSession*', 'the bracket MUST share one sessionId'),
81
82
  ).toBe(true);
82
83
  expect(
83
84
  typeof open.payload.toolId === 'string' && typeof close.payload.toolId === 'string',
84
- driver.describe('run-event-payloads.schema.json#toolSession*', 'tool.session.* MUST carry a toolId'),
85
+ req('openwop.it.tool-session-lifecycle.brackets-the-call-events-with-tool-session-opened-first-closed-last-one-sessioni', 'run-event-payloads.schema.json#toolSession*', 'tool.session.* MUST carry a toolId'),
85
86
  ).toBe(true);
86
87
 
87
88
  // Closed outcome enum discipline.
88
89
  expect(
89
90
  typeof close.payload.outcome === 'string' && SESSION_OUTCOMES.includes(close.payload.outcome as string),
90
- driver.describe('run-event-payloads.schema.json#toolSessionClosed', 'outcome MUST be in the closed enum'),
91
+ req('openwop.it.tool-session-lifecycle.brackets-the-call-events-with-tool-session-opened-first-closed-last-one-sessioni', 'run-event-payloads.schema.json#toolSessionClosed', 'outcome MUST be in the closed enum'),
91
92
  ).toBe(true);
92
93
 
93
94
  // Content-free: identifiers + metadata only.
@@ -95,7 +96,7 @@ describe('tool-session-lifecycle (RFC 0078 §D)', () => {
95
96
  for (const forbidden of TOOL_CONTENT_FORBIDDEN) {
96
97
  expect(
97
98
  !(forbidden in evt.payload),
98
- driver.describe('RFC 0078 §F (SR-1)', `tool.session.* MUST be content-free (no ${forbidden})`),
99
+ req('openwop.it.tool-session-lifecycle.brackets-the-call-events-with-tool-session-opened-first-closed-last-one-sessioni', 'RFC 0078 §F (SR-1)', `tool.session.* MUST be content-free (no ${forbidden})`),
99
100
  ).toBe(true);
100
101
  }
101
102
  }
@@ -29,7 +29,6 @@
29
29
 
30
30
  import { describe, it, expect } from 'vitest';
31
31
  import { seamAbsent } from '../lib/soft-skip.js';
32
- import { driver } from '../lib/driver.js';
33
32
  import { behaviorGate } from '../lib/behavior-gate.js';
34
33
  import {
35
34
  isTriggerBridgeProfileAdvertised,
@@ -38,6 +37,7 @@ import {
38
37
  SUBSCRIPTION_STATES,
39
38
  } from '../lib/triggerBridge.js';
40
39
  import { queryTestEvents, requireEvents, isEventLogSeamAvailable, resetTestSeam } from '../lib/event-log-query.js';
40
+ import { req } from '../lib/requirement-ids.js';
41
41
 
42
42
  const CONTENT_FREE_FORBIDDEN = ['body', 'headers', 'payload', 'secret', 'credentials', 'token', 'apiKey'];
43
43
 
@@ -45,7 +45,7 @@ function expectContentFree(payload: Record<string, unknown>, where: string): voi
45
45
  for (const f of CONTENT_FREE_FORBIDDEN) {
46
46
  expect(
47
47
  !(f in payload),
48
- driver.describe('RFC 0083 §C (SR-1)', `${where} MUST be content-free (no ${f})`),
48
+ req('openwop.it.trigger-bridge-delivery.de-dups-by-dedupkey-retries-to-dead-letter-and-links-delivery-run-causation', 'RFC 0083 §C (SR-1)', `${where} MUST be content-free (no ${f})`),
49
49
  ).toBe(true);
50
50
  }
51
51
  }
@@ -72,12 +72,12 @@ describe('trigger-bridge-delivery (RFC 0083 §C)', () => {
72
72
  );
73
73
  expect(
74
74
  deliveredForKey.length === 1,
75
- driver.describe('trigger-bridge.md §C-1', 'a repeated dedupKey MUST be effectively-once — EXACTLY one delivered attempt (not zero, not two)'),
75
+ req('openwop.it.trigger-bridge-delivery.de-dups-by-dedupkey-retries-to-dead-letter-and-links-delivery-run-causation', 'trigger-bridge.md §C-1', 'a repeated dedupKey MUST be effectively-once — EXACTLY one delivered attempt (not zero, not two)'),
76
76
  ).toBe(true);
77
77
  for (const e of dedupEvents) {
78
78
  expect(
79
79
  typeof e.payload.outcome === 'string' && DELIVERY_OUTCOMES.includes(e.payload.outcome as string),
80
- driver.describe('run-event-payloads.schema.json#triggerDeliveryAttempted', 'outcome MUST be delivered|retrying|dead-lettered'),
80
+ req('openwop.it.trigger-bridge-delivery.de-dups-by-dedupkey-retries-to-dead-letter-and-links-delivery-run-causation', 'run-event-payloads.schema.json#triggerDeliveryAttempted', 'outcome MUST be delivered|retrying|dead-lettered'),
81
81
  ).toBe(true);
82
82
  expectContentFree(e.payload, 'trigger.delivery.attempted');
83
83
  }
@@ -86,7 +86,7 @@ describe('trigger-bridge-delivery (RFC 0083 §C)', () => {
86
86
  const exhaust = await driveDelivery({ scenario: 'exhaust', source: 'webhook' });
87
87
  expect(
88
88
  exhaust !== null,
89
- driver.describe('trigger-bridge.md §C-2', 'the exhaust scenario MUST be wired when the delivery seam is'),
89
+ req('openwop.it.trigger-bridge-delivery.de-dups-by-dedupkey-retries-to-dead-letter-and-links-delivery-run-causation', 'trigger-bridge.md §C-2', 'the exhaust scenario MUST be wired when the delivery seam is'),
90
90
  ).toBe(true);
91
91
  const exKey = exhaust!.runId ?? '__exhaust__';
92
92
  const exhaustEvents = requireEvents(
@@ -95,12 +95,12 @@ describe('trigger-bridge-delivery (RFC 0083 §C)', () => {
95
95
  );
96
96
  expect(
97
97
  exhaustEvents.length >= 1,
98
- driver.describe('trigger-bridge.md §C-2', 'an exhausted delivery MUST emit ≥1 trigger.delivery.attempted'),
98
+ req('openwop.it.trigger-bridge-delivery.de-dups-by-dedupkey-retries-to-dead-letter-and-links-delivery-run-causation', 'trigger-bridge.md §C-2', 'an exhausted delivery MUST emit ≥1 trigger.delivery.attempted'),
99
99
  ).toBe(true);
100
100
  const terminal = exhaustEvents.sort((a, b) => a.sequence - b.sequence)[exhaustEvents.length - 1]!;
101
101
  expect(
102
102
  terminal.payload.outcome === 'dead-lettered',
103
- driver.describe('trigger-bridge.md §C-2', 'an exhausted retry policy MUST terminate in a dead-lettered delivery'),
103
+ req('openwop.it.trigger-bridge-delivery.de-dups-by-dedupkey-retries-to-dead-letter-and-links-delivery-run-causation', 'trigger-bridge.md §C-2', 'an exhausted retry policy MUST terminate in a dead-lettered delivery'),
104
104
  ).toBe(true);
105
105
  const stateEvents = requireEvents(
106
106
  await queryTestEvents(exKey, { type: 'trigger.subscription.state.changed' }),
@@ -108,16 +108,16 @@ describe('trigger-bridge-delivery (RFC 0083 §C)', () => {
108
108
  );
109
109
  expect(
110
110
  stateEvents.length >= 1,
111
- driver.describe('trigger-bridge.md §B', 'exhaustion MUST emit ≥1 trigger.subscription.state.changed'),
111
+ req('openwop.it.trigger-bridge-delivery.de-dups-by-dedupkey-retries-to-dead-letter-and-links-delivery-run-causation', 'trigger-bridge.md §B', 'exhaustion MUST emit ≥1 trigger.subscription.state.changed'),
112
112
  ).toBe(true);
113
113
  expect(
114
114
  stateEvents.some((e) => e.payload.toState === 'dead-lettered'),
115
- driver.describe('trigger-bridge.md §B', 'the subscription MUST transition to dead-lettered on exhaustion'),
115
+ req('openwop.it.trigger-bridge-delivery.de-dups-by-dedupkey-retries-to-dead-letter-and-links-delivery-run-causation', 'trigger-bridge.md §B', 'the subscription MUST transition to dead-lettered on exhaustion'),
116
116
  ).toBe(true);
117
117
  for (const e of stateEvents) {
118
118
  expect(
119
119
  typeof e.payload.toState === 'string' && SUBSCRIPTION_STATES.includes(e.payload.toState as string),
120
- driver.describe('trigger-bridge.md §B', 'toState MUST be in the four-state vocabulary'),
120
+ req('openwop.it.trigger-bridge-delivery.de-dups-by-dedupkey-retries-to-dead-letter-and-links-delivery-run-causation', 'trigger-bridge.md §B', 'toState MUST be in the four-state vocabulary'),
121
121
  ).toBe(true);
122
122
  expectContentFree(e.payload, 'trigger.subscription.state.changed');
123
123
  }
@@ -130,7 +130,7 @@ describe('trigger-bridge-delivery (RFC 0083 §C)', () => {
130
130
  const delivered = await driveDelivery({ scenario: 'deliver', source: 'schedule' });
131
131
  expect(
132
132
  delivered !== null && typeof delivered.runId === 'string' && (delivered.runId as string).length > 0,
133
- driver.describe('trigger-bridge.md §C', 'a successful delivery MUST create a run'),
133
+ req('openwop.it.trigger-bridge-delivery.de-dups-by-dedupkey-retries-to-dead-letter-and-links-delivery-run-causation', 'trigger-bridge.md §C', 'a successful delivery MUST create a run'),
134
134
  ).toBe(true);
135
135
  const deliveredRunId = delivered!.runId as string;
136
136
  const attemptEvents = requireEvents(
@@ -140,7 +140,7 @@ describe('trigger-bridge-delivery (RFC 0083 §C)', () => {
140
140
  const deliveredEvent = attemptEvents.find((e) => e.payload.outcome === 'delivered');
141
141
  expect(
142
142
  deliveredEvent !== undefined,
143
- driver.describe('trigger-bridge.md §C-1', 'a successful delivery MUST emit a trigger.delivery.attempted{outcome:delivered}'),
143
+ req('openwop.it.trigger-bridge-delivery.de-dups-by-dedupkey-retries-to-dead-letter-and-links-delivery-run-causation', 'trigger-bridge.md §C-1', 'a successful delivery MUST emit a trigger.delivery.attempted{outcome:delivered}'),
144
144
  ).toBe(true);
145
145
  const runStartedEvents = requireEvents(
146
146
  await queryTestEvents(deliveredRunId, { type: 'run.started' }),
@@ -148,14 +148,14 @@ describe('trigger-bridge-delivery (RFC 0083 §C)', () => {
148
148
  );
149
149
  expect(
150
150
  runStartedEvents.length >= 1,
151
- driver.describe('trigger-bridge.md §C', 'a delivered run MUST emit run.started'),
151
+ req('openwop.it.trigger-bridge-delivery.de-dups-by-dedupkey-retries-to-dead-letter-and-links-delivery-run-causation', 'trigger-bridge.md §C', 'a delivered run MUST emit run.started'),
152
152
  ).toBe(true);
153
153
  const runStarted = runStartedEvents.sort((a, b) => a.sequence - b.sequence)[0]!;
154
154
  expect(
155
155
  typeof runStarted.causationId === 'string' &&
156
156
  (runStarted.causationId as string).length > 0 &&
157
157
  runStarted.causationId === deliveredEvent!.eventId,
158
- driver.describe('trigger-bridge.md §C / RFC 0040', 'run.started.causationId MUST EQUAL the delivery id (the trigger.delivery.attempted{delivered} eventId) — resolvable via /ancestry'),
158
+ req('openwop.it.trigger-bridge-delivery.de-dups-by-dedupkey-retries-to-dead-letter-and-links-delivery-run-causation', 'trigger-bridge.md §C / RFC 0040', 'run.started.causationId MUST EQUAL the delivery id (the trigger.delivery.attempted{delivered} eventId) — resolvable via /ancestry'),
159
159
  ).toBe(true);
160
160
 
161
161
  await resetTestSeam();
@@ -36,8 +36,7 @@ import Ajv2020 from 'ajv/dist/2020.js';
36
36
  import addFormats from 'ajv-formats';
37
37
  import { SCHEMAS_DIR } from '../lib/paths.js';
38
38
  import { deriveProfiles } from '../lib/profiles.js';
39
-
40
- const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
39
+ import { req } from '../lib/requirement-ids.js';
41
40
 
42
41
  function loadSchema(name: string): Record<string, unknown> {
43
42
  return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
@@ -53,26 +52,26 @@ describe('trigger-bridge-shape: TriggerSubscription (RFC 0083 §B, server-free)'
53
52
  it('a conforming subscription validates', () => {
54
53
  expect(
55
54
  validate({ subscriptionId: 'sub-1', source: 'webhook', state: 'active', dedupEnabled: true, retryPolicy: { maxAttempts: 8, backoff: 'exponential' }, webhookId: 'wh-1', secretFingerprint: 'fp-abc' }),
56
- why('trigger-bridge.md §B', 'a conforming TriggerSubscription MUST validate'),
55
+ req('openwop.it.trigger-bridge-shape.a-conforming-subscription-validates', 'trigger-bridge.md §B', 'a conforming TriggerSubscription MUST validate'),
57
56
  ).toBe(true);
58
57
  });
59
58
 
60
59
  it('rejects a missing REQUIRED state, an out-of-enum source, and an unknown property', () => {
61
- expect(validate({ subscriptionId: 's', source: 'webhook' }), why('trigger-bridge.md §B', 'state is REQUIRED')).toBe(false);
62
- expect(validate({ subscriptionId: 's', source: 'carrier-pigeon', state: 'active' }), why('trigger-bridge.md §B', 'source is a closed enum')).toBe(false);
63
- expect(validate({ subscriptionId: 's', source: 'webhook', state: 'active', body: 'inbound' }), why('trigger-bridge.md §B', 'TriggerSubscription MUST be additionalProperties:false')).toBe(false);
60
+ expect(validate({ subscriptionId: 's', source: 'webhook' }), req('openwop.it.trigger-bridge-shape.rejects-a-missing-required-state-an-out-of-enum-source-and-an-unknown-property', 'trigger-bridge.md §B', 'state is REQUIRED')).toBe(false);
61
+ expect(validate({ subscriptionId: 's', source: 'carrier-pigeon', state: 'active' }), req('openwop.it.trigger-bridge-shape.rejects-a-missing-required-state-an-out-of-enum-source-and-an-unknown-property', 'trigger-bridge.md §B', 'source is a closed enum')).toBe(false);
62
+ expect(validate({ subscriptionId: 's', source: 'webhook', state: 'active', body: 'inbound' }), req('openwop.it.trigger-bridge-shape.rejects-a-missing-required-state-an-out-of-enum-source-and-an-unknown-property', 'trigger-bridge.md §B', 'TriggerSubscription MUST be additionalProperties:false')).toBe(false);
64
63
  });
65
64
 
66
65
  it('secretFingerprint MUST be bounded — a full 64-hex (unsalted-hash-smelling) digest is rejected', () => {
67
66
  const truncated = 'a1b2c3d4e5f6a7b8'; // 16 hex — a truncated host-keyed fingerprint
68
67
  const fullDigest = 'a'.repeat(64); // 64 hex — smells like an unsalted SHA256(secret)
69
- expect(validate({ subscriptionId: 's', source: 'webhook', state: 'active', secretFingerprint: truncated }), why('trigger-bridge.md §B', 'a truncated fingerprint MUST validate')).toBe(true);
70
- expect(validate({ subscriptionId: 's', source: 'webhook', state: 'active', secretFingerprint: fullDigest }), why('SR-1', 'a full 64-hex digest MUST be rejected (brute-force oracle)')).toBe(false);
68
+ expect(validate({ subscriptionId: 's', source: 'webhook', state: 'active', secretFingerprint: truncated }), req('openwop.it.trigger-bridge-shape.secretfingerprint-must-be-bounded-a-full-64-hex-unsalted-hash-smelling-digest-is', 'trigger-bridge.md §B', 'a truncated fingerprint MUST validate')).toBe(true);
69
+ expect(validate({ subscriptionId: 's', source: 'webhook', state: 'active', secretFingerprint: fullDigest }), req('openwop.it.trigger-bridge-shape.secretfingerprint-must-be-bounded-a-full-64-hex-unsalted-hash-smelling-digest-is', 'SR-1', 'a full 64-hex digest MUST be rejected (brute-force oracle)')).toBe(false);
71
70
  });
72
71
 
73
72
  it('the state enum is exactly the four §B states', () => {
74
73
  const stateEnum = ((sub.properties as Record<string, { enum?: string[] }>).state?.enum) ?? [];
75
- expect([...stateEnum].sort(), why('trigger-bridge.md §B', 'the four-state vocabulary MUST be stable')).toEqual([...STATES].sort());
74
+ expect([...stateEnum].sort(), req('openwop.it.trigger-bridge-shape.the-state-enum-is-exactly-the-four-b-states', 'trigger-bridge.md §B', 'the four-state vocabulary MUST be stable')).toEqual([...STATES].sort());
76
75
  });
77
76
  });
78
77
 
@@ -87,23 +86,23 @@ describe('trigger-bridge-shape: trigger.* events (RFC 0083 §C, server-free)', (
87
86
 
88
87
  it('trigger.subscription.state.changed validates + reason is a CLOSED enum (no URL-bearing free text)', () => {
89
88
  const v = compile('triggerSubscriptionStateChanged');
90
- expect(v({ subscriptionId: 's', source: 'webhook', fromState: 'active', toState: 'dead-lettered', reason: 'retry-exhausted' }), why('trigger-bridge.md §C', 'state-changed MUST validate')).toBe(true);
91
- expect(v({ subscriptionId: 's', source: 'webhook', fromState: 'active' }), why('trigger-bridge.md §C', 'toState is REQUIRED')).toBe(false);
92
- expect(v({ subscriptionId: 's', source: 'webhook', fromState: 'active', toState: 'failed', reason: 'https://attacker.example/leak?token=sk' }), why('SR-1', 'a free-form / URL-bearing reason MUST be rejected')).toBe(false);
89
+ expect(v({ subscriptionId: 's', source: 'webhook', fromState: 'active', toState: 'dead-lettered', reason: 'retry-exhausted' }), req('openwop.it.trigger-bridge-shape.trigger-subscription-state-changed-validates-reason-is-a-closed-enum-no-url-bear', 'trigger-bridge.md §C', 'state-changed MUST validate')).toBe(true);
90
+ expect(v({ subscriptionId: 's', source: 'webhook', fromState: 'active' }), req('openwop.it.trigger-bridge-shape.trigger-subscription-state-changed-validates-reason-is-a-closed-enum-no-url-bear', 'trigger-bridge.md §C', 'toState is REQUIRED')).toBe(false);
91
+ expect(v({ subscriptionId: 's', source: 'webhook', fromState: 'active', toState: 'failed', reason: 'https://attacker.example/leak?token=sk' }), req('openwop.it.trigger-bridge-shape.trigger-subscription-state-changed-validates-reason-is-a-closed-enum-no-url-bear', 'SR-1', 'a free-form / URL-bearing reason MUST be rejected')).toBe(false);
93
92
  });
94
93
 
95
94
  it('trigger.delivery.attempted validates + enforces the outcome enum', () => {
96
95
  const v = compile('triggerDeliveryAttempted');
97
- expect(v({ subscriptionId: 's', dedupKey: 'evt-9f3', attempt: 1, outcome: 'delivered', runId: 'run_x' }), why('trigger-bridge.md §C', 'delivery-attempted MUST validate')).toBe(true);
98
- expect(v({ subscriptionId: 's', dedupKey: 'evt-9f3', attempt: 1 }), why('trigger-bridge.md §C', 'outcome is REQUIRED')).toBe(false);
99
- expect(v({ subscriptionId: 's', dedupKey: 'evt-9f3', attempt: 1, outcome: 'exploded' }), why('trigger-bridge.md §C', 'outcome is a closed enum')).toBe(false);
96
+ expect(v({ subscriptionId: 's', dedupKey: 'evt-9f3', attempt: 1, outcome: 'delivered', runId: 'run_x' }), req('openwop.it.trigger-bridge-shape.trigger-delivery-attempted-validates-enforces-the-outcome-enum', 'trigger-bridge.md §C', 'delivery-attempted MUST validate')).toBe(true);
97
+ expect(v({ subscriptionId: 's', dedupKey: 'evt-9f3', attempt: 1 }), req('openwop.it.trigger-bridge-shape.trigger-delivery-attempted-validates-enforces-the-outcome-enum', 'trigger-bridge.md §C', 'outcome is REQUIRED')).toBe(false);
98
+ expect(v({ subscriptionId: 's', dedupKey: 'evt-9f3', attempt: 1, outcome: 'exploded' }), req('openwop.it.trigger-bridge-shape.trigger-delivery-attempted-validates-enforces-the-outcome-enum', 'trigger-bridge.md §C', 'outcome is a closed enum')).toBe(false);
100
99
  });
101
100
 
102
101
  it('both trigger event names appear in the RunEventType enum', () => {
103
102
  const runEvent = loadSchema('run-event.schema.json');
104
103
  const enumVals = ((runEvent.$defs as Record<string, { enum?: string[] }>).RunEventType?.enum) ?? [];
105
104
  for (const name of ['trigger.subscription.state.changed', 'trigger.delivery.attempted']) {
106
- expect(enumVals.includes(name), why('run-event.schema.json', `${name} MUST be in the RunEventType enum`)).toBe(true);
105
+ expect(enumVals.includes(name), req('openwop.it.trigger-bridge-shape.both-trigger-event-names-appear-in-the-runeventtype-enum', 'run-event.schema.json', `${name} MUST be in the RunEventType enum`)).toBe(true);
107
106
  }
108
107
  });
109
108
  });
@@ -112,8 +111,8 @@ describe('trigger-bridge-shape: capability + profile derivation (RFC 0083 §A/§
112
111
  it('capabilities.triggerBridge + webhooks.durable are declared', () => {
113
112
  const caps = loadSchema('capabilities.schema.json');
114
113
  const props = caps.properties as Record<string, { properties?: Record<string, unknown> }>;
115
- expect(props.triggerBridge?.properties?.supported, why('trigger-bridge.md §A', 'triggerBridge.supported MUST be declared')).toBeDefined();
116
- expect(props.webhooks?.properties?.durable, why('trigger-bridge.md §A', 'webhooks.durable MUST be declared')).toBeDefined();
114
+ expect(props.triggerBridge?.properties?.supported, req('openwop.it.trigger-bridge-shape.capabilities-triggerbridge-webhooks-durable-are-declared', 'trigger-bridge.md §A', 'triggerBridge.supported MUST be declared')).toBeDefined();
115
+ expect(props.webhooks?.properties?.durable, req('openwop.it.trigger-bridge-shape.capabilities-triggerbridge-webhooks-durable-are-declared', 'trigger-bridge.md §A', 'webhooks.durable MUST be declared')).toBeDefined();
117
116
  });
118
117
 
119
118
  const coreBase = {
@@ -125,11 +124,11 @@ describe('trigger-bridge-shape: capability + profile derivation (RFC 0083 §A/§
125
124
 
126
125
  it('deriveProfiles surfaces openwop-trigger-bridge for bridge + deadLetter + a durable source', () => {
127
126
  const c = { ...coreBase, triggerBridge: { supported: true }, deadLetter: { supported: true }, queueBus: { supported: true } } as Record<string, unknown>;
128
- expect(deriveProfiles(c).includes('openwop-trigger-bridge'), why('profiles.md §openwop-trigger-bridge', 'bridge + sink + durable source MUST derive the profile')).toBe(true);
127
+ expect(deriveProfiles(c).includes('openwop-trigger-bridge'), req('openwop.it.trigger-bridge-shape.deriveprofiles-surfaces-openwop-trigger-bridge-for-bridge-deadletter-a-durable-s', 'profiles.md §openwop-trigger-bridge', 'bridge + sink + durable source MUST derive the profile')).toBe(true);
129
128
  });
130
129
 
131
130
  it('deriveProfiles withholds openwop-trigger-bridge when the dead-letter sink is absent', () => {
132
131
  const c = { ...coreBase, triggerBridge: { supported: true }, webhooks: { durable: true } } as Record<string, unknown>;
133
- expect(deriveProfiles(c).includes('openwop-trigger-bridge'), why('profiles.md §openwop-trigger-bridge', 'no deadLetter sink ⇒ MUST NOT derive the profile')).toBe(false);
132
+ expect(deriveProfiles(c).includes('openwop-trigger-bridge'), req('openwop.it.trigger-bridge-shape.deriveprofiles-withholds-openwop-trigger-bridge-when-the-dead-letter-sink-is-abs', 'profiles.md §openwop-trigger-bridge', 'no deadLetter sink ⇒ MUST NOT derive the profile')).toBe(false);
134
133
  });
135
134
  });