@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
@@ -108,6 +108,7 @@ import { discoveryFamilies } from '../lib/discovery-capabilities.js';
108
108
  import { pollUntilTerminal } from '../lib/polling.js';
109
109
  import { isFixtureAdvertised } from '../lib/fixtures.js';
110
110
  import { discoverOwnedTenant, resolveRegistrationUrl } from '../lib/webhook-receiver.js';
111
+ import { req } from '../lib/requirement-ids.js';
111
112
 
112
113
  interface DeliveredRequest {
113
114
  readonly headers: Record<string, string>;
@@ -116,13 +117,13 @@ interface DeliveredRequest {
116
117
 
117
118
  async function startReceiver(): Promise<{ server: Server; url: string; received: DeliveredRequest[] }> {
118
119
  const received: DeliveredRequest[] = [];
119
- const server = createServer((req: IncomingMessage, res: ServerResponse) => {
120
+ const server = createServer((reqBody: IncomingMessage, res: ServerResponse) => {
120
121
  const chunks: Buffer[] = [];
121
- req.on('data', (c: Buffer) => chunks.push(c));
122
- req.on('end', () => {
122
+ reqBody.on('data', (c: Buffer) => chunks.push(c));
123
+ reqBody.on('end', () => {
123
124
  const body = Buffer.concat(chunks).toString('utf8');
124
125
  const headers: Record<string, string> = {};
125
- for (const [k, v] of Object.entries(req.headers)) {
126
+ for (const [k, v] of Object.entries(reqBody.headers)) {
126
127
  if (typeof v === 'string') headers[k.toLowerCase()] = v;
127
128
  else if (Array.isArray(v)) headers[k.toLowerCase()] = v.join(',');
128
129
  }
@@ -225,7 +226,7 @@ describe('webhook-signed-delivery: end-to-end HMAC v1', () => {
225
226
  }
226
227
  }
227
228
 
228
- expect(reg.status, driver.describe(
229
+ expect(reg.status, req('openwop.it.webhook-signed-delivery.host-posts-run-events-to-subscriber-with-valid-x-openwop-signature',
229
230
  'webhooks.md §"Register"',
230
231
  'POST /v1/webhooks MUST return 201 with webhookId + secret on success',
231
232
  )).toBe(201);
@@ -240,7 +241,7 @@ describe('webhook-signed-delivery: end-to-end HMAC v1', () => {
240
241
  // what the spec shows. The postgres reference host returns both names, which
241
242
  // is why nothing went red here.
242
243
  const sub = reg.json as { webhookId: string; secret: string };
243
- expect(typeof sub.webhookId, driver.describe(
244
+ expect(typeof sub.webhookId, req('openwop.it.webhook-signed-delivery.host-posts-run-events-to-subscriber-with-valid-x-openwop-signature',
244
245
  'api/openapi.yaml registerWebhook 201',
245
246
  'the 201 body MUST carry `webhookId` (required) — `subscriptionId` is not in the contract',
246
247
  )).toBe('string');
@@ -276,7 +277,7 @@ describe('webhook-signed-delivery: end-to-end HMAC v1', () => {
276
277
  // has a subscriber; if nothing arrived HERE, the delivery went somewhere
277
278
  // this process cannot see and every assertion below it would be vacuous.
278
279
  // It fails — it must never soft-skip.
279
- expect(ourDeliveries.length, driver.describe(
280
+ expect(ourDeliveries.length, req('openwop.it.webhook-signed-delivery.host-posts-run-events-to-subscriber-with-valid-x-openwop-signature',
280
281
  'webhooks.md §"Delivery"',
281
282
  registration.tunnelled
282
283
  ? 'host MUST POST at least one event for THIS run to the registered subscriber. ' +
@@ -289,7 +290,7 @@ describe('webhook-signed-delivery: end-to-end HMAC v1', () => {
289
290
  // Validate the FIRST delivery's signature contract. Other deliveries
290
291
  // share the same signing rules; checking one is sufficient.
291
292
  const first = ourDeliveries[0]!;
292
- expect(first.headers['x-openwop-signature-algorithm'], driver.describe(
293
+ expect(first.headers['x-openwop-signature-algorithm'], req('openwop.it.webhook-signed-delivery.host-posts-run-events-to-subscriber-with-valid-x-openwop-signature',
293
294
  'webhooks.md §"Signature algorithm versioning"',
294
295
  'every delivery MUST set X-openwop-Signature-Algorithm: v1',
295
296
  )).toBe('v1');
@@ -301,26 +302,26 @@ describe('webhook-signed-delivery: end-to-end HMAC v1', () => {
301
302
  // `X-openwop-Signature: sha256={hex}`. (Suite↔spec divergence fixed 2026-08-09.)
302
303
  expect(
303
304
  first.headers['x-openwop-webhook-id'],
304
- driver.describe('webhooks.md §"Delivery headers"', 'X-openwop-Webhook-Id MUST carry the subscription id'),
305
+ req('openwop.it.webhook-signed-delivery.host-posts-run-events-to-subscriber-with-valid-x-openwop-signature', 'webhooks.md §"Delivery headers"', 'X-openwop-Webhook-Id MUST carry the subscription id'),
305
306
  ).toBe(sub.webhookId);
306
307
 
307
308
  const timestamp = first.headers['x-openwop-timestamp'];
308
309
  expect(
309
310
  typeof timestamp === 'string' && timestamp.length > 0,
310
- driver.describe('webhooks.md §"Delivery headers"', 'X-openwop-Timestamp MUST be a Unix-seconds integer string'),
311
+ req('openwop.it.webhook-signed-delivery.host-posts-run-events-to-subscriber-with-valid-x-openwop-signature', 'webhooks.md §"Delivery headers"', 'X-openwop-Timestamp MUST be a Unix-seconds integer string'),
311
312
  ).toBe(true);
312
313
 
313
314
  const signature = first.headers['x-openwop-signature'] ?? '';
314
315
  expect(
315
316
  signature.startsWith('sha256='),
316
- driver.describe('webhooks.md §"Delivery headers"', 'X-openwop-Signature MUST carry the `sha256=` prefix'),
317
+ req('openwop.it.webhook-signed-delivery.host-posts-run-events-to-subscriber-with-valid-x-openwop-signature', 'webhooks.md §"Delivery headers"', 'X-openwop-Signature MUST carry the `sha256=` prefix'),
317
318
  ).toBe(true);
318
319
  const expected = createHmac('sha256', sub.secret)
319
320
  .update(`${timestamp}.${first.body}`, 'utf8')
320
321
  .digest('hex');
321
322
  expect(
322
323
  signature.replace('sha256=', ''),
323
- driver.describe('webhooks.md §"Delivery headers"', 'X-openwop-Signature MUST be sha256=HMAC-SHA256(secret, `${X-openwop-Timestamp}.${rawBody}`)'),
324
+ req('openwop.it.webhook-signed-delivery.host-posts-run-events-to-subscriber-with-valid-x-openwop-signature', 'webhooks.md §"Delivery headers"', 'X-openwop-Signature MUST be sha256=HMAC-SHA256(secret, `${X-openwop-Timestamp}.${rawBody}`)'),
324
325
  ).toBe(expected);
325
326
 
326
327
  // RFC 0165 §C.1 — dual emission of the `OpenWOP-*` family. SHOULD-level and
@@ -337,11 +338,11 @@ describe('webhook-signed-delivery: end-to-end HMAC v1', () => {
337
338
  ['openwop-signature', 'x-openwop-signature'],
338
339
  ['openwop-signature-algorithm', 'x-openwop-signature-algorithm'],
339
340
  ] as const) {
340
- expect(first.headers[neu], driver.describe('RFC 0165 §C.1', `${neu} MUST equal ${old} when the OpenWOP-* family is emitted`)).toBe(first.headers[old]);
341
+ expect(first.headers[neu], req('openwop.it.webhook-signed-delivery.host-posts-run-events-to-subscriber-with-valid-x-openwop-signature', 'RFC 0165 §C.1', `${neu} MUST equal ${old} when the OpenWOP-* family is emitted`)).toBe(first.headers[old]);
341
342
  }
342
343
  expect(
343
344
  String(dual).replace('sha256=', ''),
344
- driver.describe('RFC 0165 §C.1', 'OpenWOP-Signature MUST verify over the same bytes as X-openwop-Signature'),
345
+ req('openwop.it.webhook-signed-delivery.host-posts-run-events-to-subscriber-with-valid-x-openwop-signature', 'RFC 0165 §C.1', 'OpenWOP-Signature MUST verify over the same bytes as X-openwop-Signature'),
345
346
  ).toBe(expected);
346
347
  }
347
348
 
@@ -404,7 +405,7 @@ describe('webhook-signed-delivery: end-to-end HMAC v1', () => {
404
405
  }
405
406
  }
406
407
 
407
- expect(reg.status, driver.describe(
408
+ expect(reg.status, req('openwop.it.webhook-signed-delivery.control-with-a-tunnel-wired-the-host-still-refuses-the-loopback-receiver',
408
409
  'webhooks.md §"SSRF protection"',
409
410
  'the server MUST reject loopback subscription URLs at registration time. ' +
410
411
  'It was accepted here, so this run cannot attribute the tunnelled test\'s pass to ' +
@@ -38,6 +38,8 @@ import { describe, it, expect } from 'vitest';
38
38
  import { driver, type OpenWOPResponse } from '../lib/driver.js';
39
39
  import { behaviorGate } from '../lib/behavior-gate.js';
40
40
  import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
41
+ import { req } from '../lib/requirement-ids.js';
42
+ import { softSkip } from '../lib/soft-skip.js';
41
43
 
42
44
  const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
43
45
 
@@ -66,26 +68,26 @@ async function gateOnWebhooks(): Promise<boolean> {
66
68
  describe.skipIf(HTTP_SKIP)('webhook-tenant-isolation: advertisement shape (webhooks.md)', () => {
67
69
  it('the webhooks block is either absent or carries a boolean supported flag', async () => {
68
70
  const cap = await readCapabilityFamily<WebhooksCap>('webhooks');
69
- if (cap === undefined) return; // host doesn't advertise webhooks — conformant
71
+ if (cap === undefined) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap === undefined` returned early (host doesn\'t advertise webhooks — conformant)'); // host doesn't advertise webhooks — conformant
70
72
  expect(
71
73
  typeof cap.supported,
72
- driver.describe('capabilities.schema.json §webhooks', 'webhooks.supported MUST be a boolean when present'),
74
+ req('openwop.it.webhook-tenant-isolation.the-webhooks-block-is-either-absent-or-carries-a-boolean-supported-flag', 'capabilities.schema.json §webhooks', 'webhooks.supported MUST be a boolean when present'),
73
75
  ).toBe('boolean');
74
76
  });
75
77
  });
76
78
 
77
79
  describe.skipIf(HTTP_SKIP)('webhook-tenant-isolation: two-tenant proof via the test seam (RFC 0093 §A.3)', () => {
78
80
  it('a subscription registered under tenant A is invisible to tenant B', async () => {
79
- if (!(await gateOnWebhooks())) return;
81
+ if (!(await gateOnWebhooks())) return softSkip('blocked', 'precondition not met — `!(await gateOnWebhooks())` returned early (seam, prior step, or fixture unavailable)');
80
82
 
81
83
  const reg = await seam('tenant-a', 'register', {
82
84
  url: SAFE_URL,
83
85
  events: ['run.completed'],
84
86
  });
85
- if (reg.status === 404) return; // host doesn't expose the test seam — soft-skip
86
- expect(reg.status, driver.describe('RFC 0093 §A.3', 'seam register under tenant A MUST succeed')).toBe(200);
87
+ if (reg.status === 404) return softSkip('blocked', 'precondition not met — `reg.status === 404` returned early (host doesn\'t expose the test seam — soft-skip) (seam, prior step, or fixture unavailable)'); // host doesn't expose the test seam — soft-skip
88
+ expect(reg.status, req('openwop.it.webhook-tenant-isolation.a-subscription-registered-under-tenant-a-is-invisible-to-tenant-b', 'RFC 0093 §A.3', 'seam register under tenant A MUST succeed')).toBe(200);
87
89
  const webhookId = (reg.json as { webhookId?: string }).webhookId;
88
- expect(typeof webhookId, 'seam register MUST return the new webhookId').toBe('string');
90
+ expect(typeof webhookId, req('openwop.it.webhook-tenant-isolation.a-subscription-registered-under-tenant-a-is-invisible-to-tenant-b', 'RFC 0093 §A.3', 'seam register MUST return the new webhookId')).toBe('string');
89
91
 
90
92
  try {
91
93
  const listB = await seam('tenant-b', 'list', {});
@@ -94,7 +96,7 @@ describe.skipIf(HTTP_SKIP)('webhook-tenant-isolation: two-tenant proof via the t
94
96
  .map((w) => w.webhookId);
95
97
  expect(
96
98
  idsB,
97
- driver.describe(
99
+ req('openwop.it.webhook-tenant-isolation.a-subscription-registered-under-tenant-a-is-invisible-to-tenant-b',
98
100
  'SECURITY/invariants.yaml webhook-cross-tenant-isolation',
99
101
  'tenant B MUST NOT see tenant A subscriptions in its list',
100
102
  ),
@@ -105,7 +107,7 @@ describe.skipIf(HTTP_SKIP)('webhook-tenant-isolation: two-tenant proof via the t
105
107
  .map((w) => w.webhookId);
106
108
  expect(
107
109
  idsA,
108
- driver.describe('RFC 0093 §A.3', 'tenant A MUST see its own subscription (same-tenant control)'),
110
+ req('openwop.it.webhook-tenant-isolation.a-subscription-registered-under-tenant-a-is-invisible-to-tenant-b', 'RFC 0093 §A.3', 'tenant A MUST see its own subscription (same-tenant control)'),
109
111
  ).toContain(webhookId);
110
112
  } finally {
111
113
  await seam('tenant-a', 'unregister', { webhookId });
@@ -115,7 +117,7 @@ describe.skipIf(HTTP_SKIP)('webhook-tenant-isolation: two-tenant proof via the t
115
117
 
116
118
  describe.skipIf(HTTP_SKIP)('webhook-tenant-isolation: black-box registration-surface scoping (webhooks.md)', () => {
117
119
  it('registering under a tenant the caller is not a member of is refused', async () => {
118
- if (!(await gateOnWebhooks())) return;
120
+ if (!(await gateOnWebhooks())) return softSkip('blocked', 'precondition not met — `!(await gateOnWebhooks())` returned early (seam, prior step, or fixture unavailable)');
119
121
 
120
122
  const foreignTenant = `openwop-conformance-foreign-${uniqueSuffix()}`;
121
123
  const reg = await driver.post('/v1/webhooks', {
@@ -135,7 +137,7 @@ describe.skipIf(HTTP_SKIP)('webhook-tenant-isolation: black-box registration-sur
135
137
  }
136
138
  expect(
137
139
  reg.status,
138
- driver.describe(
140
+ req('openwop.it.webhook-tenant-isolation.registering-under-a-tenant-the-caller-is-not-a-member-of-is-refused',
139
141
  'webhooks.md §Endpoints ("the caller MUST be a member of the tenant") + RFC 0093 §A.3',
140
142
  'registration under a tenant the caller is not a member of MUST be refused (403/404/400), never 201',
141
143
  ),
@@ -143,7 +145,7 @@ describe.skipIf(HTTP_SKIP)('webhook-tenant-isolation: black-box registration-sur
143
145
  });
144
146
 
145
147
  it('a held subscription cannot be unregistered through a foreign tenant scope', async () => {
146
- if (!(await gateOnWebhooks())) return;
148
+ if (!(await gateOnWebhooks())) return softSkip('blocked', 'precondition not met — `!(await gateOnWebhooks())` returned early (seam, prior step, or fixture unavailable)');
147
149
 
148
150
  // Register under the caller's own tenant (host defaults the scope from
149
151
  // the API key when tenantId is omitted — the same registration shape the
@@ -159,10 +161,10 @@ describe.skipIf(HTTP_SKIP)('webhook-tenant-isolation: black-box registration-sur
159
161
  console.warn(
160
162
  `[webhook-tenant-isolation] could not register a probe subscription (${reg.status}); skipping foreign-unregister leg`,
161
163
  );
162
- return;
164
+ return softSkip('blocked', 'precondition not met — `reg.status !== 201` returned early ([webhook-tenant-isolation] could not register a probe subscription (…); skipping foreign-unregister leg) (seam, prior step, or fixture unavailable)');
163
165
  }
164
166
  const webhookId = (reg.json as { webhookId?: string }).webhookId;
165
- expect(typeof webhookId, 'registration MUST return webhookId').toBe('string');
167
+ expect(typeof webhookId, req('openwop.it.webhook-tenant-isolation.a-held-subscription-cannot-be-unregistered-through-a-foreign-tenant-scope', 'webhooks.md §Unregister (403 non-member / 404 not-in-scope) + RFC 0093 §A.3', 'registration MUST return webhookId')).toBe('string');
166
168
 
167
169
  try {
168
170
  const foreignTenant = `openwop-conformance-foreign-${uniqueSuffix()}`;
@@ -171,7 +173,7 @@ describe.skipIf(HTTP_SKIP)('webhook-tenant-isolation: black-box registration-sur
171
173
  );
172
174
  expect(
173
175
  del.status !== 204 && del.status < 500,
174
- driver.describe(
176
+ req('openwop.it.webhook-tenant-isolation.a-held-subscription-cannot-be-unregistered-through-a-foreign-tenant-scope',
175
177
  'webhooks.md §Unregister (403 non-member / 404 not-in-scope) + RFC 0093 §A.3',
176
178
  `unregister through a foreign tenant scope MUST NOT succeed (got ${del.status})`,
177
179
  ),
@@ -50,6 +50,8 @@ import {
50
50
  type WorkflowChain,
51
51
  type DeferredExpansionContext,
52
52
  } from '../lib/workflow-chain-expansion.js';
53
+ import { req } from '../lib/requirement-ids.js';
54
+ import { softSkip } from '../lib/soft-skip.js';
53
55
 
54
56
  const CAPS = join(SCHEMAS_DIR, 'capabilities.schema.json');
55
57
  const MANIFEST = join(SCHEMAS_DIR, 'workflow-chain-pack-manifest.schema.json');
@@ -59,10 +61,6 @@ const MANIFEST = join(SCHEMAS_DIR, 'workflow-chain-pack-manifest.schema.json');
59
61
  // repo-layout only: `null` in the published layout and skipped, never thrown.
60
62
  const CHAIN_DOC: string | null = V1_DIR === null ? null : join(V1_DIR, 'workflow-chain-packs.md');
61
63
 
62
- /** Spec-cited assertion message. Unlike `driver.describe`, this does NOT load
63
- * env, so it is safe in always-on server-free legs (no OPENWOP_BASE_URL). */
64
- const cite = (section: string, requirement: string): string => `${section} — ${requirement}`;
65
-
66
64
  const resolvable = () => true;
67
65
  const HOST_FULL = { promptVariableSource: true, secretsSupported: true };
68
66
 
@@ -112,13 +110,13 @@ describe('workflow-chain-deferred: non-sensitive deferral (server-free, RFC 0124
112
110
 
113
111
  expect(
114
112
  hasResidualToken({ nodes: out.nodes, edges: out.edges }),
115
- cite('workflow-chain-packs.md §Deferred-parameter expansion', 'the persisted fragment MUST contain zero {{params.*}} tokens (R3 portability)'),
113
+ req('openwop.it.workflow-chain-deferred-parameters.materializes-variables-with-defaultvalue-type-and-leaves-zero-params-tokens-r3', 'workflow-chain-packs.md §Deferred-parameter expansion', 'the persisted fragment MUST contain zero {{params.*}} tokens (R3 portability)'),
116
114
  ).toBe(false);
117
115
 
118
116
  const topicVar = out.variables.find((v) => v.name === 'topic');
119
- expect(topicVar, 'topic MUST be materialized as a top-level variable').toBeDefined();
120
- expect(topicVar!.defaultValue, 'author input becomes defaultValue').toBe('sales');
121
- expect(topicVar!.type, 'type copied from the parameter schema').toBe('string');
117
+ expect(topicVar, req('openwop.it.workflow-chain-deferred-parameters.materializes-variables-with-defaultvalue-type-and-leaves-zero-params-tokens-r3', 'workflow-chain-packs.md', 'topic MUST be materialized as a top-level variable')).toBeDefined();
118
+ expect(topicVar!.defaultValue, req('openwop.it.workflow-chain-deferred-parameters.materializes-variables-with-defaultvalue-type-and-leaves-zero-params-tokens-r3', 'workflow-chain-packs.md', 'author input becomes defaultValue')).toBe('sales');
119
+ expect(topicVar!.type, req('openwop.it.workflow-chain-deferred-parameters.materializes-variables-with-defaultvalue-type-and-leaves-zero-params-tokens-r3', 'workflow-chain-packs.md', 'type copied from the parameter schema')).toBe('string');
122
120
 
123
121
  // prompt token → {{varName}} + source:"variable"
124
122
  expect((out.nodes[0].config as { systemPrompt: string }).systemPrompt).toBe('Hello {{topic}}');
@@ -133,7 +131,7 @@ describe('workflow-chain-deferred: non-sensitive deferral (server-free, RFC 0124
133
131
  const out = expandChainDeferred(c, deferCtx({ count: 5 }, c.parameters as never));
134
132
  expect(
135
133
  out.nodes[0].inputs!.limit,
136
- cite('workflow-chain-packs.md §Deferred-parameter expansion', 'a whole-value {{params.x}} input rewrites to a variable-sourced PortValue, not a stringified token'),
134
+ req('openwop.it.workflow-chain-deferred-parameters.a-whole-value-input-token-becomes-a-variable-sourced-portvalue-wcp2-raw-typed', 'workflow-chain-packs.md §Deferred-parameter expansion', 'a whole-value {{params.x}} input rewrites to a variable-sourced PortValue, not a stringified token'),
137
135
  ).toEqual({ source: 'variable', variable: 'count' });
138
136
  });
139
137
 
@@ -145,7 +143,7 @@ describe('workflow-chain-deferred: non-sensitive deferral (server-free, RFC 0124
145
143
  const out = expandChainDeferred(c, deferCtx({ topic: 't' }, c.parameters as never));
146
144
  expect(
147
145
  out.configurableSchema.properties.topic,
148
- cite('workflow-chain-packs.md §Override key + variable naming', 'the bare parameter name is the normative override key'),
146
+ req('openwop.it.workflow-chain-deferred-parameters.the-bare-param-name-is-the-override-key-in-the-auto-generated-configurableschema', 'workflow-chain-packs.md §Override key + variable naming', 'the bare parameter name is the normative override key'),
149
147
  ).toEqual({ type: 'string' });
150
148
  });
151
149
  });
@@ -162,14 +160,14 @@ describe('workflow-chain-deferred: §Security sensitive-parameter MUSTs (server-
162
160
 
163
161
  expect(
164
162
  out.promptVariables.find((p) => p.name === 'apiKey')?.source,
165
- cite('workflow-chain-packs.md §Security', 'a sensitive prompt-body param MUST bind source:"secret" (not source:"variable")'),
163
+ req('openwop.it.workflow-chain-deferred-parameters.a-sensitive-param-in-a-prompt-body-materializes-as-source-secret-no-plaintext-de', 'workflow-chain-packs.md §Security', 'a sensitive prompt-body param MUST bind source:"secret" (not source:"variable")'),
166
164
  ).toBe('secret');
167
165
  // The plaintext secret MUST appear NOWHERE — not as a variable defaultValue, not in the fragment.
168
166
  expect(
169
167
  JSON.stringify(out).includes('sk-PLAINTEXT-SECRET'),
170
- cite('workflow-chain-packs.md §Security', 'the sensitive value MUST NOT be materialized into variables[] or the persisted fragment (never bagged, SR-1)'),
168
+ req('openwop.it.workflow-chain-deferred-parameters.a-sensitive-param-in-a-prompt-body-materializes-as-source-secret-no-plaintext-de', 'workflow-chain-packs.md §Security', 'the sensitive value MUST NOT be materialized into variables[] or the persisted fragment (never bagged, SR-1)'),
171
169
  ).toBe(false);
172
- expect(out.variables.find((v) => v.name === 'apiKey'), 'sensitive param is NOT a plaintext top-level variable').toBeUndefined();
170
+ expect(out.variables.find((v) => v.name === 'apiKey'), req('openwop.it.workflow-chain-deferred-parameters.a-sensitive-param-in-a-prompt-body-materializes-as-source-secret-no-plaintext-de', 'workflow-chain-packs.md', 'sensitive param is NOT a plaintext top-level variable')).toBeUndefined();
173
171
  });
174
172
 
175
173
  it('a sensitive param in a whole-value node.input FAILS CLOSED (sensitive_param_not_deferrable, 422)', () => {
@@ -183,10 +181,10 @@ describe('workflow-chain-deferred: §Security sensitive-parameter MUSTs (server-
183
181
  } catch (e) {
184
182
  err = e;
185
183
  }
186
- expect(err, 'a sensitive whole-value input MUST throw, not defer').toBeInstanceOf(SensitiveParamNotDeferrableError);
184
+ expect(err, req('openwop.it.workflow-chain-deferred-parameters.a-sensitive-param-in-a-whole-value-node-input-fails-closed-sensitive-param-not-d', 'workflow-chain-packs.md', 'a sensitive whole-value input MUST throw, not defer')).toBeInstanceOf(SensitiveParamNotDeferrableError);
187
185
  expect(
188
186
  (err as SensitiveParamNotDeferrableError).code,
189
- cite('workflow-chain-packs.md §Security', 'a sensitive param outside a prompt body MUST fail closed with sensitive_param_not_deferrable'),
187
+ req('openwop.it.workflow-chain-deferred-parameters.a-sensitive-param-in-a-whole-value-node-input-fails-closed-sensitive-param-not-d', 'workflow-chain-packs.md §Security', 'a sensitive param outside a prompt body MUST fail closed with sensitive_param_not_deferrable'),
190
188
  ).toBe('sensitive_param_not_deferrable');
191
189
  expect((err as SensitiveParamNotDeferrableError).httpStatus).toBe(422);
192
190
  });
@@ -204,7 +202,7 @@ describe('workflow-chain-deferred: §Security sensitive-parameter MUSTs (server-
204
202
  }
205
203
  expect(
206
204
  err,
207
- cite('workflow-chain-packs.md §Security', 'a host lacking capabilities.secrets MUST NOT plaintext-defer a sensitive param — fail closed'),
205
+ req('openwop.it.workflow-chain-deferred-parameters.a-sensitive-prompt-body-param-on-a-host-without-secrets-support-fails-closed-422', 'workflow-chain-packs.md §Security', 'a host lacking capabilities.secrets MUST NOT plaintext-defer a sensitive param — fail closed'),
208
206
  ).toBeInstanceOf(SensitiveParamNotDeferrableError);
209
207
  });
210
208
 
@@ -219,7 +217,7 @@ describe('workflow-chain-deferred: §Security sensitive-parameter MUSTs (server-
219
217
  );
220
218
  // Fallback resolves the token at expansion time — no {{topic}} slot, value inlined, still zero {{params.*}}.
221
219
  expect((out.nodes[0].config as { systemPrompt: string }).systemPrompt).toBe('Hi sales');
222
- expect(hasResidualToken({ nodes: out.nodes }), 'no residual {{params.*}} even on the fallback path').toBe(false);
220
+ expect(hasResidualToken({ nodes: out.nodes }), req('openwop.it.workflow-chain-deferred-parameters.negative-capability-no-prompts-variable-source-prompt-token-falls-back-to-expans', 'workflow-chain-packs.md', 'no residual {{params.*}} even on the fallback path')).toBe(false);
223
221
  });
224
222
  });
225
223
 
@@ -227,29 +225,29 @@ describe('workflow-chain-deferred: schema + spec surface (always-on, server-free
227
225
  it('capabilities.schema.json §workflowChainPacks.deferredParameters requires supported:boolean', () => {
228
226
  const raw = readFileSync(CAPS, 'utf8');
229
227
  // The deferredParameters block MUST parse as part of the capabilities schema.
230
- expect(() => JSON.parse(raw), 'capabilities.schema.json MUST be valid JSON').not.toThrow();
231
- expect(raw.includes('"deferredParameters"'), 'the deferredParameters capability block MUST exist').toBe(true);
228
+ expect(() => JSON.parse(raw), req('openwop.it.workflow-chain-deferred-parameters.capabilities-schema-json-workflowchainpacks-deferredparameters-requires-supporte', 'workflow-chain-packs.md', 'capabilities.schema.json MUST be valid JSON')).not.toThrow();
229
+ expect(raw.includes('"deferredParameters"'), req('openwop.it.workflow-chain-deferred-parameters.capabilities-schema-json-workflowchainpacks-deferredparameters-requires-supporte', 'workflow-chain-packs.md', 'the deferredParameters capability block MUST exist')).toBe(true);
232
230
  expect(
233
231
  raw.includes('sensitive_param_not_deferrable'),
234
- cite('capabilities.schema.json §deferredParameters', 'the capability description MUST reference the fail-closed sensitive rule'),
232
+ req('openwop.it.workflow-chain-deferred-parameters.capabilities-schema-json-workflowchainpacks-deferredparameters-requires-supporte', 'capabilities.schema.json §deferredParameters', 'the capability description MUST reference the fail-closed sensitive rule'),
235
233
  ).toBe(true);
236
234
  });
237
235
 
238
236
  it('workflow-chain-pack-manifest.schema.json documents x-openwop-sensitive with the source:"secret" MUST', () => {
239
237
  const raw = readFileSync(MANIFEST, 'utf8');
240
- expect(raw.includes('x-openwop-sensitive'), 'the manifest schema MUST recognize x-openwop-sensitive').toBe(true);
238
+ expect(raw.includes('x-openwop-sensitive'), req('openwop.it.workflow-chain-deferred-parameters.workflow-chain-pack-manifest-schema-json-documents-x-openwop-sensitive-with-the', 'workflow-chain-packs.md', 'the manifest schema MUST recognize x-openwop-sensitive')).toBe(true);
241
239
  expect(
242
240
  raw.includes('source:"secret"') || raw.includes('source:\\"secret\\"'),
243
- cite('workflow-chain-pack-manifest.schema.json', 'the x-openwop-sensitive description MUST reflect the amended source:"secret" MUST, not the stale plaintext-variable wording'),
241
+ req('openwop.it.workflow-chain-deferred-parameters.workflow-chain-pack-manifest-schema-json-documents-x-openwop-sensitive-with-the', 'workflow-chain-pack-manifest.schema.json', 'the x-openwop-sensitive description MUST reflect the amended source:"secret" MUST, not the stale plaintext-variable wording'),
244
242
  ).toBe(true);
245
243
  });
246
244
 
247
245
  it.skipIf(CHAIN_DOC === null)('the spec pins the error code + the per-run credentialRef (not plaintext) supply shape', () => {
248
246
  const spec = readFileSync(CHAIN_DOC as string, 'utf8');
249
- expect(spec.includes('sensitive_param_not_deferrable'), 'error code MUST be documented').toBe(true);
247
+ expect(spec.includes('sensitive_param_not_deferrable'), req('openwop.it.workflow-chain-deferred-parameters.the-spec-pins-the-error-code-the-per-run-credentialref-not-plaintext-supply-shap', 'workflow-chain-packs.md', 'error code MUST be documented')).toBe(true);
250
248
  expect(
251
249
  spec.includes('credentialRef'),
252
- cite('workflow-chain-packs.md §Security', 'per-run supply of a sensitive param MUST be a credentialRef, not plaintext'),
250
+ req('openwop.it.workflow-chain-deferred-parameters.the-spec-pins-the-error-code-the-per-run-credentialref-not-plaintext-supply-shap', 'workflow-chain-packs.md §Security', 'per-run supply of a sensitive param MUST be a credentialRef, not plaintext'),
253
251
  ).toBe(true);
254
252
  });
255
253
  });
@@ -265,20 +263,20 @@ describe('workflow-chain-deferred: host behavior (capability-gated, RFC 0124)',
265
263
  override: { topic: 'run-topic' },
266
264
  fork: true,
267
265
  });
268
- if (res.status === 404 || res.status === 403) return; // seam unwired — soft-skip
266
+ if (res.status === 404 || res.status === 403) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 403` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
269
267
 
270
268
  const body = res.json as { resolved?: string; forkResolved?: string; contentTrust?: string } | undefined;
271
269
  expect(
272
270
  body?.resolved,
273
- cite('workflow-chain-packs.md §Deferred-parameter expansion', 'a bare-param configurable override rebinds the resolved value'),
271
+ req('openwop.it.workflow-chain-deferred-parameters.a-deferred-round-trip-bare-param-configurable-override-changes-the-value-fork-re', 'workflow-chain-packs.md §Deferred-parameter expansion', 'a bare-param configurable override rebinds the resolved value'),
274
272
  ).toBe('run-topic');
275
273
  expect(
276
274
  body?.forkResolved,
277
- cite('replay.md §Determinism', ':fork replays the same bound value (RunSnapshot.variables byte-equivalence, R4)'),
275
+ req('openwop.it.workflow-chain-deferred-parameters.a-deferred-round-trip-bare-param-configurable-override-changes-the-value-fork-re', 'replay.md §Determinism', ':fork replays the same bound value (RunSnapshot.variables byte-equivalence, R4)'),
278
276
  ).toBe('run-topic');
279
277
  expect(
280
278
  body?.contentTrust,
281
- cite('workflow-chain-packs.md §Deferred-parameter expansion step 4', 'a deferred-variable prompt binding composes contentTrust:"untrusted" (R1)'),
279
+ req('openwop.it.workflow-chain-deferred-parameters.a-deferred-round-trip-bare-param-configurable-override-changes-the-value-fork-re', 'workflow-chain-packs.md §Deferred-parameter expansion step 4', 'a deferred-variable prompt binding composes contentTrust:"untrusted" (R1)'),
282
280
  ).toBe('untrusted');
283
281
  });
284
282
 
@@ -291,12 +289,12 @@ describe('workflow-chain-deferred: host behavior (capability-gated, RFC 0124)',
291
289
  sensitiveParam: 'apiKey',
292
290
  credentialRef: 'cred-123',
293
291
  });
294
- if (res.status === 404 || res.status === 403) return; // seam unwired — soft-skip
292
+ if (res.status === 404 || res.status === 403) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 403` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
295
293
 
296
294
  const body = res.json as { composed?: string } | undefined;
297
295
  expect(
298
296
  body?.composed?.includes('[REDACTED'),
299
- cite('workflow-chain-packs.md §Security', 'a source:"secret" sensitive var MUST redact to [REDACTED:<credentialRef>] in prompt.composed'),
297
+ req('openwop.it.workflow-chain-deferred-parameters.a-sensitive-param-composes-as-redacted-credentialref-and-the-plaintext-appears-n', 'workflow-chain-packs.md §Security', 'a source:"secret" sensitive var MUST redact to [REDACTED:<credentialRef>] in prompt.composed'),
300
298
  ).toBe(true);
301
299
  });
302
300
  });
@@ -34,6 +34,7 @@
34
34
 
35
35
  import { describe, it, expect } from 'vitest';
36
36
  import { expandChain, type WorkflowChain } from '../lib/workflow-chain-expansion.js';
37
+ import { req } from '../lib/requirement-ids.js';
37
38
 
38
39
  /** Index helper that narrows away the `T | undefined` from `noUncheckedIndexedAccess`
39
40
  * with an actionable error message — replaces `arr[i]!` non-null assertions so a
@@ -95,7 +96,7 @@ describe('category: workflow-chain expansion — placeholder substitution', () =
95
96
  const config = at(fragment.nodes, 0, 'fragment.nodes').config as { systemPrompt: string };
96
97
  expect(
97
98
  config.systemPrompt,
98
- 'Per workflow-chain-packs.md §"Parameter substitution": placeholders MUST be substituted literally at expansion time.',
99
+ req('openwop.it.workflow-chain-expansion.substitutes-params-x-placeholders-literally-in-config-strings', 'workflow-chain-packs.md', 'Per workflow-chain-packs.md §"Parameter substitution": placeholders MUST be substituted literally at expansion time.'),
99
100
  ).toBe('Write a PRD for: AI-powered toaster\nAudience: restaurant chains');
100
101
  });
101
102
 
@@ -130,11 +131,11 @@ describe('category: workflow-chain expansion — placeholder substitution', () =
130
131
  };
131
132
  expect(
132
133
  config.outer.middle.message,
133
- 'Recursive substitution MUST walk into nested objects (per spec §Parameter substitution: "Substitution MUST recurse into nested string values within `config` and `inputs`").',
134
+ req('openwop.it.workflow-chain-expansion.recurses-into-nested-object-structures-in-config', 'workflow-chain-packs.md', 'Recursive substitution MUST walk into nested objects (per spec §Parameter substitution: "Substitution MUST recurse into nested string values within `config` and `inputs`").'),
134
135
  ).toBe('Hello world');
135
136
  expect(
136
137
  config.outer.middle.tags,
137
- 'Recursive substitution MUST also walk into array elements when those elements are strings.',
138
+ req('openwop.it.workflow-chain-expansion.recurses-into-nested-object-structures-in-config', 'workflow-chain-packs.md', 'Recursive substitution MUST also walk into array elements when those elements are strings.'),
138
139
  ).toEqual(['static', 'world-tag']);
139
140
  });
140
141
 
@@ -156,7 +157,7 @@ describe('category: workflow-chain expansion — placeholder substitution', () =
156
157
  { expansionId: 'x', params: {}, isTypeIdResolvable: RESOLVE_ALL },
157
158
  );
158
159
  const config = at(fragment.nodes, 0, 'fragment.nodes').config as { literal: string; empty: string };
159
- expect(config.literal).toBe('no placeholders here');
160
+ expect(config.literal, req('openwop.it.workflow-chain-expansion.leaves-non-placeholder-strings-untouched', 'workflow-chain-packs.md', 'leaves non-placeholder strings untouched')).toBe('no placeholders here');
160
161
  expect(config.empty).toBe('');
161
162
  });
162
163
 
@@ -178,7 +179,7 @@ describe('category: workflow-chain expansion — placeholder substitution', () =
178
179
  { expansionId: 'x', params: { who: 'Ada' }, isTypeIdResolvable: RESOLVE_ALL },
179
180
  );
180
181
  const config = at(fragment.nodes, 0, 'fragment.nodes').config as { a: string; b: string; joined: string };
181
- expect(config.a).toBe('Ada');
182
+ expect(config.a, req('openwop.it.workflow-chain-expansion.substitutes-the-same-placeholder-name-in-multiple-positions', 'workflow-chain-packs.md', 'substitutes the same placeholder name in multiple positions')).toBe('Ada');
182
183
  expect(config.b).toBe('Ada');
183
184
  expect(config.joined).toBe('hi Ada, hi again Ada');
184
185
  });
@@ -231,7 +232,7 @@ describe('category: workflow-chain expansion — whole-value typed resolution',
231
232
  };
232
233
  expect(
233
234
  config.retryPolicy,
234
- 'Per workflow-chain-packs.md §"Parameter substitution": a value that is EXACTLY one `{{params.x}}` token MUST resolve to the raw typed value — an object param MUST NOT be stringified to "[object Object]".',
235
+ req('openwop.it.workflow-chain-expansion.resolves-a-whole-value-params-x-token-to-the-raw-typed-value-object-array-number', 'workflow-chain-packs.md', 'Per workflow-chain-packs.md §"Parameter substitution": a value that is EXACTLY one `{{params.x}}` token MUST resolve to the raw typed value — an object param MUST NOT be stringified to "[object Object]".'),
235
236
  ).toEqual({ attempts: 3, backoff: 'exponential' });
236
237
  expect(config.allowlist).toEqual(['a', 'b']);
237
238
  expect(config.maxTokens).toBe(4096);
@@ -255,7 +256,7 @@ describe('category: workflow-chain expansion — whole-value typed resolution',
255
256
  const config = at(fragment.nodes, 0, 'fragment.nodes').config as { label: string };
256
257
  expect(
257
258
  config.label,
258
- 'A token embedded in surrounding text MUST do literal string substitution (the numeric param coerces to its string form).',
259
+ req('openwop.it.workflow-chain-expansion.does-literal-string-coercion-for-an-embedded-token-in-surrounding-text', 'workflow-chain-packs.md', 'A token embedded in surrounding text MUST do literal string substitution (the numeric param coerces to its string form).'),
259
260
  ).toBe('items: 42');
260
261
  });
261
262
  });
@@ -290,7 +291,7 @@ describe('category: workflow-chain expansion — inputs preservation', () => {
290
291
  };
291
292
  expect(
292
293
  inputs.prompt,
293
- 'Per workflow-chain-packs.md §"Parameter substitution": expansion MUST preserve a present `node.inputs` (PortValue references) verbatim — only `{{params.*}}` tokens inside its string leaves are substituted.',
294
+ req('openwop.it.workflow-chain-expansion.preserves-a-present-node-inputs-portvalue-references-through-expansion', 'workflow-chain-packs.md', 'Per workflow-chain-packs.md §"Parameter substitution": expansion MUST preserve a present `node.inputs` (PortValue references) verbatim — only `{{params.*}}` tokens inside its string leaves are substituted.'),
294
295
  ).toEqual({ sourceNodeId: 'upstream', sourcePort: 'text' });
295
296
  expect(inputs.seed).toBe('xyz');
296
297
  });
@@ -310,7 +311,7 @@ describe('category: workflow-chain expansion — node id collision avoidance', (
310
311
  });
311
312
  expect(
312
313
  at(first.nodes, 0, 'first.nodes').id,
313
- 'Per spec §"Expansion semantics" step 6: each expansion MUST use a unique `expansionId` prefix so multi-expansion DAGs do not collide.',
314
+ req('openwop.it.workflow-chain-expansion.same-chain-expanded-twice-in-one-parent-workflow-produces-non-colliding-node-ids', 'workflow-chain-packs.md', 'Per spec §"Expansion semantics" step 6: each expansion MUST use a unique `expansionId` prefix so multi-expansion DAGs do not collide.'),
314
315
  ).not.toBe(at(second.nodes, 0, 'second.nodes').id);
315
316
  expect(at(first.nodes, 0, 'first.nodes').id).toBe('vendor_acme_generatePRD_a8f3_prd-call');
316
317
  expect(at(second.nodes, 0, 'second.nodes').id).toBe('vendor_acme_generatePRD_b9e4_prd-call');
@@ -324,7 +325,7 @@ describe('category: workflow-chain expansion — node id collision avoidance', (
324
325
  });
325
326
  expect(
326
327
  at(fragment.nodes, 0, 'fragment.nodes').id,
327
- 'chainId `vendor.acme.generatePRD` MUST be slugged (dots → underscores) so the resulting id is safe to use in storage backends that reserve `.` for hierarchical keys.',
328
+ req('openwop.it.workflow-chain-expansion.rewritten-ids-replace-dots-in-chainid-with-underscores-for-storage-key-safety', 'workflow-chain-packs.md', 'chainId `vendor.acme.generatePRD` MUST be slugged (dots → underscores) so the resulting id is safe to use in storage backends that reserve `.` for hierarchical keys.'),
328
329
  ).toMatch(/^vendor_acme_generatePRD_/);
329
330
  expect(at(fragment.nodes, 0, 'fragment.nodes').id).not.toMatch(/\./);
330
331
  });
@@ -337,7 +338,7 @@ describe('category: workflow-chain expansion — node id collision avoidance', (
337
338
  });
338
339
  expect(
339
340
  fragment.idMap.get('prd-call'),
340
- 'The expansion contract MUST surface the original-id → rewritten-id map so the caller can route adjacent parent-workflow edges to the right expanded node.',
341
+ req('openwop.it.workflow-chain-expansion.exposes-idmap-so-the-caller-can-wire-parent-workflow-edges-into-the-expansion', 'workflow-chain-packs.md', 'The expansion contract MUST surface the original-id → rewritten-id map so the caller can route adjacent parent-workflow edges to the right expanded node.'),
341
342
  ).toBe('vendor_acme_generatePRD_a8f3_prd-call');
342
343
  });
343
344
  });
@@ -367,7 +368,7 @@ describe('category: workflow-chain expansion — edge rewriting', () => {
367
368
  expect(fragment.edges).toHaveLength(1);
368
369
  expect(
369
370
  at(fragment.edges, 0, 'fragment.edges').from,
370
- 'Edge `from` ids that match fragment node ids MUST be rewritten with the same prefix as the nodes themselves.',
371
+ req('openwop.it.workflow-chain-expansion.rewrites-from-and-to-ids-that-reference-fragment-nodes', 'workflow-chain-packs.md', 'Edge `from` ids that match fragment node ids MUST be rewritten with the same prefix as the nodes themselves.'),
371
372
  ).toBe('vendor_acme_twoStep_e1_first');
372
373
  expect(at(fragment.edges, 0, 'fragment.edges').to).toBe('vendor_acme_twoStep_e1_second');
373
374
  });
@@ -387,7 +388,7 @@ describe('category: workflow-chain expansion — edge rewriting', () => {
387
388
  });
388
389
  expect(
389
390
  at(fragment.edges, 0, 'fragment.edges').from,
390
- 'Port-name suffix (after the `.`) MUST be preserved verbatim during id rewriting.',
391
+ req('openwop.it.workflow-chain-expansion.preserves-port-name-suffix-nodeid-portname-when-rewriting-edge-refs', 'workflow-chain-packs.md', 'Port-name suffix (after the `.`) MUST be preserved verbatim during id rewriting.'),
391
392
  ).toBe('vendor_acme_twoStep_e2_first.out');
392
393
  expect(at(fragment.edges, 0, 'fragment.edges').to).toBe('vendor_acme_twoStep_e2_second.in');
393
394
  });
@@ -413,7 +414,7 @@ describe('category: workflow-chain expansion — edge rewriting', () => {
413
414
  });
414
415
  expect(
415
416
  at(fragment.edges, 0, 'fragment.edges').from,
416
- 'Edge refs to nodes OUTSIDE the fragment MUST pass through unchanged so the parent host can wire adjacent edges post-splice.',
417
+ req('openwop.it.workflow-chain-expansion.leaves-edge-refs-alone-when-they-don-t-match-a-fragment-node-id', 'workflow-chain-packs.md', 'Edge refs to nodes OUTSIDE the fragment MUST pass through unchanged so the parent host can wire adjacent edges post-splice.'),
417
418
  ).toBe('parent-upstream');
418
419
  expect(at(fragment.edges, 0, 'fragment.edges').to).toBe('vendor_acme_twoStep_e3_first');
419
420
  expect(at(fragment.edges, 1, 'fragment.edges').from).toBe('vendor_acme_twoStep_e3_second');
@@ -449,11 +450,11 @@ describe('category: workflow-chain expansion — edge rewriting', () => {
449
450
  expect(edge.to).toBe('vendor_acme_twoStep_e4_second');
450
451
  expect(
451
452
  edge.condition,
452
- 'Per RFC 0013 amendment (2026-07-03): a present `FragmentEdge.condition` MUST be carried onto the expanded WorkflowEdge (not dropped at expansion).',
453
+ req('openwop.it.workflow-chain-expansion.preserves-edge-condition-and-triggerrule-onto-the-expanded-workflowedge-rfc-0013', 'RFC 0013', 'Per RFC 0013 amendment (2026-07-03): a present `FragmentEdge.condition` MUST be carried onto the expanded WorkflowEdge (not dropped at expansion).'),
453
454
  ).toEqual({ type: 'equals', left: 'status', right: 'ok' });
454
455
  expect(
455
456
  edge.triggerRule,
456
- 'Per RFC 0125 / workflow-chain-packs.md §"Expansion semantics": expansion MUST preserve `FragmentEdge.triggerRule` onto the resulting WorkflowEdge so the scheduler honors the fan-in / error-routing rule.',
457
+ req('openwop.it.workflow-chain-expansion.preserves-edge-condition-and-triggerrule-onto-the-expanded-workflowedge-rfc-0013', 'RFC 0013', 'Per RFC 0125 / workflow-chain-packs.md §"Expansion semantics": expansion MUST preserve `FragmentEdge.triggerRule` onto the resulting WorkflowEdge so the scheduler honors the fan-in / error-routing rule.'),
457
458
  ).toBe('all_complete');
458
459
  });
459
460
 
@@ -465,7 +466,7 @@ describe('category: workflow-chain expansion — edge rewriting', () => {
465
466
  });
466
467
  expect(
467
468
  at(fragment.edges, 0, 'fragment.edges').triggerRule,
468
- 'When a fragment edge declares no `triggerRule`, the expanded edge MUST NOT carry one — omission is identical to the `all_success` default (preserves wire-shape minimality + prior behavior).',
469
+ req('openwop.it.workflow-chain-expansion.omits-triggerrule-on-the-expanded-edge-when-the-fragment-edge-declares-none-defa', 'workflow-chain-packs.md', 'When a fragment edge declares no `triggerRule`, the expanded edge MUST NOT carry one — omission is identical to the `all_success` default (preserves wire-shape minimality + prior behavior).'),
469
470
  ).toBeUndefined();
470
471
  });
471
472
  });
@@ -491,7 +492,7 @@ describe('category: workflow-chain expansion — capability propagation', () =>
491
492
  for (const node of fragment.nodes) {
492
493
  expect(
493
494
  node.capabilities,
494
- 'Per spec §"Expansion semantics" step 8: chain-level `capabilities[]` MUST be copied to every expanded node so existing capability gates (side-effect, streamable) apply uniformly.',
495
+ req('openwop.it.workflow-chain-expansion.copies-chain-capabilities-into-every-expanded-node-s-capabilities', 'workflow-chain-packs.md', 'Per spec §"Expansion semantics" step 8: chain-level `capabilities[]` MUST be copied to every expanded node so existing capability gates (side-effect, streamable) apply uniformly.'),
495
496
  ).toEqual(['side-effectful', 'cacheable']);
496
497
  }
497
498
  });
@@ -504,7 +505,7 @@ describe('category: workflow-chain expansion — capability propagation', () =>
504
505
  });
505
506
  expect(
506
507
  at(fragment.nodes, 0, 'fragment.nodes').capabilities,
507
- 'When the chain declares no `capabilities[]`, expanded nodes MUST NOT carry an empty array (preserves wire-shape minimality).',
508
+ req('openwop.it.workflow-chain-expansion.omits-capabilities-on-expanded-nodes-when-the-chain-declares-none', 'workflow-chain-packs.md', 'When the chain declares no `capabilities[]`, expanded nodes MUST NOT carry an empty array (preserves wire-shape minimality).'),
508
509
  ).toBeUndefined();
509
510
  });
510
511
  });
@@ -519,7 +520,7 @@ describe('category: workflow-chain expansion — runtime-invariance contract', (
519
520
  for (const node of fragment.nodes) {
520
521
  expect(
521
522
  node.typeId,
522
- 'Per workflow-chain-packs.md §"What hosts dispatch": dispatching runtimes see only concrete `core.*`/published-vendor typeIds — the chain reference MUST NOT be preserved at runtime.',
523
+ req('openwop.it.workflow-chain-expansion.expanded-fragment-carries-only-concrete-typeids-no-chain-reference-survives', 'workflow-chain-packs.md', 'Per workflow-chain-packs.md §"What hosts dispatch": dispatching runtimes see only concrete `core.*`/published-vendor typeIds — the chain reference MUST NOT be preserved at runtime.'),
523
524
  ).not.toMatch(/^vendor\.acme\.generatePRD$/);
524
525
  expect(node.typeId).toBe('core.ai.callPrompt');
525
526
  }