@openwop/openwop-conformance 1.162.0 → 2.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (553) hide show
  1. package/README.md +11 -2
  2. package/dist/cli.js +54 -1
  3. package/dist/lib/paths.js +25 -3
  4. package/dist/lib/requirement-ids.js +34 -4
  5. package/dist/lib/scenario-disposition.js +1 -0
  6. package/dist/lib/spec-coherence.js +13 -32
  7. package/dist/spec-artifacts.lock.json +5 -0
  8. package/package.json +11 -33
  9. package/requirements.json +20303 -11809
  10. package/scenario-majors.json +1343 -0
  11. package/schemas/CORPUS-STAMP.json +138 -6
  12. package/src/cli.ts +47 -1
  13. package/src/global-setup.ts +3 -2
  14. package/src/lib/corpus-stamp.ts +29 -1
  15. package/src/lib/paths.ts +25 -3
  16. package/src/lib/requirement-ids.ts +33 -4
  17. package/src/lib/scenario-disposition.ts +1 -0
  18. package/src/lib/spec-coherence.ts +15 -32
  19. package/src/scenarios/a2a-1-0-agent-card.test.ts +17 -16
  20. package/src/scenarios/a2a-1-0-task-roundtrip.test.ts +11 -10
  21. package/src/scenarios/a2a-card-runtime-consistency.test.ts +18 -17
  22. package/src/scenarios/a2a-peer-authority.test.ts +7 -6
  23. package/src/scenarios/a2a-task-roundtrip.test.ts +30 -29
  24. package/src/scenarios/a2a-version-negotiation.test.ts +16 -14
  25. package/src/scenarios/a2ui-surface-degrades.test.ts +5 -3
  26. package/src/scenarios/a2ui-surface-delta-transport.test.ts +35 -33
  27. package/src/scenarios/a2ui-surface-replay.test.ts +6 -4
  28. package/src/scenarios/a2ui-surface-shape.test.ts +11 -10
  29. package/src/scenarios/a2ui-surface-version-refusal.test.ts +6 -4
  30. package/src/scenarios/a2ui-untrusted-blocks-approval.test.ts +5 -3
  31. package/src/scenarios/agent-capability-degraded-projection.test.ts +12 -11
  32. package/src/scenarios/agent-channel-dispatch.test.ts +18 -16
  33. package/src/scenarios/agent-deployment-lifecycle.test.ts +20 -19
  34. package/src/scenarios/agent-deployment-shape.test.ts +22 -24
  35. package/src/scenarios/agent-eval-run.test.ts +19 -18
  36. package/src/scenarios/agent-eval-suite-shape.test.ts +20 -22
  37. package/src/scenarios/agent-live-allowlist-enforced.test.ts +6 -5
  38. package/src/scenarios/agent-live-invocation-bracket.test.ts +13 -12
  39. package/src/scenarios/agent-live-runtime-shape.test.ts +10 -12
  40. package/src/scenarios/agent-live-structured-output.test.ts +7 -6
  41. package/src/scenarios/agent-loop-iteration-monotonic.test.ts +3 -3
  42. package/src/scenarios/agent-loop-stateful-resume.test.ts +2 -2
  43. package/src/scenarios/agent-loop-version5-shape.test.ts +4 -4
  44. package/src/scenarios/agent-loop-workspace-snapshot.test.ts +3 -3
  45. package/src/scenarios/agent-manifest-role-profile.test.ts +9 -9
  46. package/src/scenarios/agent-manifest-runtime.test.ts +13 -12
  47. package/src/scenarios/agent-org-chart-scoping.test.ts +14 -13
  48. package/src/scenarios/agent-org-chart-shape.test.ts +11 -13
  49. package/src/scenarios/agent-platform-aggregate-evidence.test.ts +5 -4
  50. package/src/scenarios/agent-platform-profile.test.ts +18 -19
  51. package/src/scenarios/agent-requires-capabilities-shape.test.ts +4 -4
  52. package/src/scenarios/agent-roster-attribution.test.ts +18 -17
  53. package/src/scenarios/agent-roster-shape.test.ts +17 -19
  54. package/src/scenarios/agent-verifier-shape.test.ts +11 -11
  55. package/src/scenarios/agentConfidenceEscalation.test.ts +2 -1
  56. package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +6 -5
  57. package/src/scenarios/agentMemoryRedactionContract.test.ts +2 -1
  58. package/src/scenarios/agentMemoryRoundTrip.test.ts +2 -1
  59. package/src/scenarios/agentMemoryTtlExpiry.test.ts +4 -3
  60. package/src/scenarios/agentMessageReducer.test.ts +2 -1
  61. package/src/scenarios/agentMetadata.test.ts +2 -1
  62. package/src/scenarios/agentPackCatalog.test.ts +23 -21
  63. package/src/scenarios/agentPackExport.test.ts +4 -2
  64. package/src/scenarios/agentPackHandoffSchemaValidation.test.ts +5 -4
  65. package/src/scenarios/agentPackInstall.test.ts +4 -2
  66. package/src/scenarios/agentPackProvenance.test.ts +2 -1
  67. package/src/scenarios/agentReasoningEvents.test.ts +3 -2
  68. package/src/scenarios/agentReasoningStreaming.test.ts +11 -10
  69. package/src/scenarios/agents-run-tool-allowlist.test.ts +14 -12
  70. package/src/scenarios/ai-envelope-shape.test.ts +31 -29
  71. package/src/scenarios/aiEnvelope.capBreached.test.ts +26 -24
  72. package/src/scenarios/aiEnvelope.contractRefusal.test.ts +36 -34
  73. package/src/scenarios/aiEnvelope.correlationReplay.test.ts +17 -15
  74. package/src/scenarios/aiEnvelope.redaction.test.ts +22 -20
  75. package/src/scenarios/aiEnvelope.schemaDrift.test.ts +18 -16
  76. package/src/scenarios/aiEnvelope.trustBoundaryPropagation.test.ts +38 -36
  77. package/src/scenarios/aiEnvelope.universalKinds.test.ts +31 -29
  78. package/src/scenarios/aiproviders-input-shape.test.ts +5 -5
  79. package/src/scenarios/aiproviders-realtimevoice-shape.test.ts +14 -16
  80. package/src/scenarios/aiproviders-selfhosted-honesty.test.ts +6 -4
  81. package/src/scenarios/aiproviders-selfhosted-shape.test.ts +15 -17
  82. package/src/scenarios/aiproviders-speechsynth-shape.test.ts +6 -8
  83. package/src/scenarios/aiproviders-subscription-scope.test.ts +7 -8
  84. package/src/scenarios/anonymous-actor-audit-opaque.test.ts +9 -8
  85. package/src/scenarios/anonymous-actor-default-deny.test.ts +10 -9
  86. package/src/scenarios/anonymous-actor-egress-guarded.test.ts +6 -5
  87. package/src/scenarios/anonymous-actor-no-secret-reach.test.ts +7 -6
  88. package/src/scenarios/anonymous-actor-shape.test.ts +17 -19
  89. package/src/scenarios/anonymous-actor-write-gated.test.ts +9 -8
  90. package/src/scenarios/append-ordering.test.ts +7 -5
  91. package/src/scenarios/approval-gate-events.test.ts +4 -3
  92. package/src/scenarios/approval-gate-flow.test.ts +4 -3
  93. package/src/scenarios/approval-payload.test.ts +5 -4
  94. package/src/scenarios/artifact-auth.test.ts +7 -6
  95. package/src/scenarios/artifact-type-pack-install.test.ts +6 -6
  96. package/src/scenarios/artifact-type-pack-manifest-validation.test.ts +9 -8
  97. package/src/scenarios/artifact-type-registration-source.test.ts +20 -20
  98. package/src/scenarios/artifact-type-store-emission.test.ts +15 -18
  99. package/src/scenarios/artifact-type-store-without-render.test.ts +4 -4
  100. package/src/scenarios/audit-log-integrity.test.ts +8 -6
  101. package/src/scenarios/auth-api-key-rotation.test.ts +11 -9
  102. package/src/scenarios/auth-mtls.test.ts +22 -20
  103. package/src/scenarios/auth-oauth2-client-credentials.test.ts +16 -14
  104. package/src/scenarios/auth-oidc-user-bearer.test.ts +26 -24
  105. package/src/scenarios/auth-saml-profile.test.ts +14 -12
  106. package/src/scenarios/auth-scim-profile.test.ts +3 -2
  107. package/src/scenarios/auth-subject-link-key-class.test.ts +8 -7
  108. package/src/scenarios/auth-subject-link.test.ts +11 -10
  109. package/src/scenarios/auth.test.ts +5 -4
  110. package/src/scenarios/authorization-fail-closed.test.ts +3 -2
  111. package/src/scenarios/authorization-roles-shape.test.ts +9 -7
  112. package/src/scenarios/blob-cross-tenant-isolation.test.ts +8 -6
  113. package/src/scenarios/blob-presign-expiry.test.ts +12 -10
  114. package/src/scenarios/blob-roundtrip.test.ts +0 -0
  115. package/src/scenarios/budget-enforcement.test.ts +15 -14
  116. package/src/scenarios/budget-policy-shape.test.ts +18 -19
  117. package/src/scenarios/bulk-cancel.test.ts +9 -8
  118. package/src/scenarios/byok-auth-modes.test.ts +13 -14
  119. package/src/scenarios/byok-roundtrip.test.ts +12 -10
  120. package/src/scenarios/cache-cross-tenant-isolation.test.ts +8 -6
  121. package/src/scenarios/cache-ttl-expiry.test.ts +7 -5
  122. package/src/scenarios/callai-multimodal.test.ts +5 -3
  123. package/src/scenarios/cancellation.test.ts +6 -5
  124. package/src/scenarios/cap-breach.test.ts +15 -14
  125. package/src/scenarios/capability-arm-witness.test.ts +5 -3
  126. package/src/scenarios/certification-bundle-non-vacuous.test.ts +20 -19
  127. package/src/scenarios/certification-bundle-redaction.test.ts +11 -10
  128. package/src/scenarios/certification-bundle-v2.test.ts +11 -10
  129. package/src/scenarios/chain-compensation-expansion.test.ts +14 -13
  130. package/src/scenarios/chain-produced-var-roundtrip.test.ts +13 -14
  131. package/src/scenarios/chain-subchain-cycle-rejected.test.ts +9 -10
  132. package/src/scenarios/chain-subchain-fanout.test.ts +8 -9
  133. package/src/scenarios/chain-subchain-sibling.test.ts +14 -16
  134. package/src/scenarios/channel-presence-behavioral.test.ts +9 -7
  135. package/src/scenarios/channel-presence-shape.test.ts +11 -12
  136. package/src/scenarios/channel-ttl.test.ts +6 -5
  137. package/src/scenarios/chat-card-pack-execution.test.ts +4 -4
  138. package/src/scenarios/chat-card-pack-manifest-validation.test.ts +9 -8
  139. package/src/scenarios/commitment-fired.test.ts +6 -5
  140. package/src/scenarios/compensation-behavior.test.ts +24 -23
  141. package/src/scenarios/compensation-recovery.test.ts +28 -27
  142. package/src/scenarios/configurable-schema.test.ts +8 -6
  143. package/src/scenarios/conformance-advertised-seam-required.test.ts +6 -5
  144. package/src/scenarios/conformance-execution-witness.test.ts +9 -8
  145. package/src/scenarios/connection-pack-apihosts.test.ts +19 -17
  146. package/src/scenarios/connection-pack-manifest-valid.test.ts +12 -11
  147. package/src/scenarios/connection-pack-no-credential-material.test.ts +8 -6
  148. package/src/scenarios/connection-pack-reach-exclusive.test.ts +5 -4
  149. package/src/scenarios/connection-pack-write-reconsent.test.ts +6 -4
  150. package/src/scenarios/connection-provider-resolution.test.ts +18 -16
  151. package/src/scenarios/connector-manifest-validity.test.ts +8 -7
  152. package/src/scenarios/context-budget-transcript-bound.test.ts +17 -15
  153. package/src/scenarios/context-summarization-replay.test.ts +14 -12
  154. package/src/scenarios/contract-provenance.test.ts +18 -18
  155. package/src/scenarios/conversation-turn-model-provenance-shape.test.ts +14 -15
  156. package/src/scenarios/conversationCapabilityNegotiation.test.ts +2 -1
  157. package/src/scenarios/conversationLifecycle.test.ts +2 -1
  158. package/src/scenarios/conversationReplayDeterminism.test.ts +4 -2
  159. package/src/scenarios/conversationVsLegacySuspend.test.ts +2 -1
  160. package/src/scenarios/core-standard-profile.test.ts +8 -9
  161. package/src/scenarios/cost-attribution.test.ts +23 -21
  162. package/src/scenarios/credential-payload-redaction.test.ts +4 -3
  163. package/src/scenarios/credentials-capability-shape.test.ts +5 -4
  164. package/src/scenarios/cross-engine-append-behavior.test.ts +11 -10
  165. package/src/scenarios/cross-engine-append-ordering.test.ts +4 -3
  166. package/src/scenarios/cross-host-ancestry-endpoint.test.ts +11 -10
  167. package/src/scenarios/cross-host-causation-shape.test.ts +7 -6
  168. package/src/scenarios/cross-workspace-isolation.test.ts +4 -3
  169. package/src/scenarios/data-residency-admission.test.ts +15 -14
  170. package/src/scenarios/deadletter-capability-shape.test.ts +6 -4
  171. package/src/scenarios/deadletter-retry-exhaustion.test.ts +4 -3
  172. package/src/scenarios/debug-bundle-truncation.test.ts +9 -7
  173. package/src/scenarios/debugBundle.test.ts +25 -23
  174. package/src/scenarios/discovery-families-view.test.ts +5 -4
  175. package/src/scenarios/discovery.test.ts +30 -29
  176. package/src/scenarios/dispatch-cross-worker-handoff.test.ts +10 -8
  177. package/src/scenarios/dispatch-fanout-parallel.test.ts +24 -22
  178. package/src/scenarios/dispatch-input-mapping.test.ts +12 -10
  179. package/src/scenarios/dispatch-output-mapping.test.ts +9 -7
  180. package/src/scenarios/dispatch-per-item-input.test.ts +22 -20
  181. package/src/scenarios/dispatchLoop.test.ts +3 -2
  182. package/src/scenarios/distillation-index-roundtrip.test.ts +3 -3
  183. package/src/scenarios/distillation-secret-carryforward.test.ts +2 -2
  184. package/src/scenarios/distillation-shape.test.ts +4 -4
  185. package/src/scenarios/distillation-stable-archive.test.ts +6 -6
  186. package/src/scenarios/distillation-token-budget.test.ts +5 -5
  187. package/src/scenarios/durability-poison-exhaustion.test.ts +7 -6
  188. package/src/scenarios/egress-audience-binding.test.ts +11 -10
  189. package/src/scenarios/egress-decision-content-free.test.ts +6 -5
  190. package/src/scenarios/egress-provenance-shape.test.ts +17 -18
  191. package/src/scenarios/envelope-completion-distinguishes-truncation.test.ts +27 -25
  192. package/src/scenarios/envelope-nl-to-format-engaged.test.ts +19 -17
  193. package/src/scenarios/envelope-reasoning-secret-redaction.test.ts +28 -26
  194. package/src/scenarios/envelope-reasoning-shape.test.ts +19 -17
  195. package/src/scenarios/envelope-recovery-applied.test.ts +24 -22
  196. package/src/scenarios/envelope-refusal-shape.test.ts +31 -29
  197. package/src/scenarios/envelope-rendering-hint.test.ts +5 -4
  198. package/src/scenarios/envelope-retry-attempted.test.ts +33 -31
  199. package/src/scenarios/envelope-retry-exhausted.test.ts +22 -20
  200. package/src/scenarios/envelope-tier-one-subset-static.test.ts +9 -7
  201. package/src/scenarios/envelope-truncated.test.ts +19 -17
  202. package/src/scenarios/envelope-truncation-cap-exhaustion.test.ts +19 -17
  203. package/src/scenarios/envelope-variant-discriminator-static.test.ts +4 -3
  204. package/src/scenarios/errors.test.ts +20 -19
  205. package/src/scenarios/eventOrdering.test.ts +16 -14
  206. package/src/scenarios/exec-not-protocol-tier.test.ts +5 -7
  207. package/src/scenarios/experimental-tier-shape.test.ts +17 -15
  208. package/src/scenarios/export-bundle-portability.test.ts +17 -17
  209. package/src/scenarios/failure-path.test.ts +7 -6
  210. package/src/scenarios/feedback-capability-shape.test.ts +4 -3
  211. package/src/scenarios/feedback-correction-redaction.test.ts +2 -1
  212. package/src/scenarios/feedback-cross-tenant-isolation.test.ts +2 -1
  213. package/src/scenarios/feedback-fork-not-copied.test.ts +3 -2
  214. package/src/scenarios/feedback-on-terminal-run.test.ts +3 -2
  215. package/src/scenarios/feedback-record-and-list.test.ts +2 -1
  216. package/src/scenarios/feedback-unsupported-501.test.ts +2 -1
  217. package/src/scenarios/fixtures-gating.test.ts +29 -28
  218. package/src/scenarios/fixtures-valid.test.ts +16 -15
  219. package/src/scenarios/form-content-instantiation.test.ts +15 -14
  220. package/src/scenarios/frontend-plugin-packs.test.ts +29 -27
  221. package/src/scenarios/fs-path-traversal.test.ts +14 -12
  222. package/src/scenarios/goal-standing-continuation.test.ts +21 -21
  223. package/src/scenarios/grpc-transport.test.ts +9 -7
  224. package/src/scenarios/heartbeat-capability-shape.test.ts +3 -3
  225. package/src/scenarios/heartbeat-fires-once-per-tick.test.ts +2 -2
  226. package/src/scenarios/heartbeat-idempotent-no-spam.test.ts +3 -3
  227. package/src/scenarios/heartbeat-runtime-bound.test.ts +2 -2
  228. package/src/scenarios/highConcurrency.test.ts +14 -13
  229. package/src/scenarios/host-callback-declaration.test.ts +8 -7
  230. package/src/scenarios/http-client-ssrf.test.ts +7 -5
  231. package/src/scenarios/i18n-negotiation.test.ts +14 -12
  232. package/src/scenarios/idempotency-concurrent-claim.test.ts +14 -13
  233. package/src/scenarios/idempotency-key-determinism.test.ts +20 -18
  234. package/src/scenarios/idempotency.test.ts +8 -7
  235. package/src/scenarios/idempotencyRetry.test.ts +13 -11
  236. package/src/scenarios/identity-owner-shape.test.ts +6 -5
  237. package/src/scenarios/identity-passthrough.test.ts +4 -3
  238. package/src/scenarios/interrupt-approval.test.ts +6 -5
  239. package/src/scenarios/interrupt-approver-routing.test.ts +15 -15
  240. package/src/scenarios/interrupt-auth-required-resume.test.ts +5 -3
  241. package/src/scenarios/interrupt-clarification.test.ts +4 -3
  242. package/src/scenarios/interrupt-external-event-correlation.test.ts +6 -5
  243. package/src/scenarios/interrupt-parent-child-cascade.test.ts +4 -3
  244. package/src/scenarios/interrupt-quorum-resolution.test.ts +5 -4
  245. package/src/scenarios/interrupt-token-matrix.test.ts +8 -6
  246. package/src/scenarios/interruptRace.test.ts +12 -10
  247. package/src/scenarios/kv-atomic-increment.test.ts +11 -9
  248. package/src/scenarios/kv-cas.test.ts +14 -12
  249. package/src/scenarios/kv-cross-tenant-isolation.test.ts +12 -10
  250. package/src/scenarios/kv-ttl-expiry.test.ts +7 -5
  251. package/src/scenarios/localized-content-delivery.test.ts +24 -26
  252. package/src/scenarios/maliciousManifest.test.ts +10 -8
  253. package/src/scenarios/mcp-2026-07-28-discover.test.ts +15 -14
  254. package/src/scenarios/mcp-cache-tenant-scope.test.ts +6 -5
  255. package/src/scenarios/mcp-current-auth-boundary.test.ts +7 -6
  256. package/src/scenarios/mcp-discoverability.test.ts +9 -7
  257. package/src/scenarios/mcp-extension-opacity.test.ts +7 -6
  258. package/src/scenarios/mcp-mrtr-roundtrip.test.ts +18 -17
  259. package/src/scenarios/mcp-server-elicitation-bridge.test.ts +13 -12
  260. package/src/scenarios/mcp-server-prompt-roundtrip.test.ts +11 -10
  261. package/src/scenarios/mcp-server-resource-roundtrip.test.ts +11 -10
  262. package/src/scenarios/mcp-server-sampling-bridge.test.ts +12 -11
  263. package/src/scenarios/mcp-server-tool-roundtrip.test.ts +14 -13
  264. package/src/scenarios/mcp-server-untrusted-args.test.ts +14 -13
  265. package/src/scenarios/mcp-stateless-request.test.ts +11 -10
  266. package/src/scenarios/mcp-tool-roundtrip.test.ts +4 -3
  267. package/src/scenarios/mcp-toolcall-redaction.test.ts +4 -3
  268. package/src/scenarios/mcp-version-negotiation.test.ts +15 -14
  269. package/src/scenarios/media-url-inline-cap.test.ts +21 -19
  270. package/src/scenarios/memory-attribution-emits-on-write.test.ts +5 -5
  271. package/src/scenarios/memory-attribution-no-content.test.ts +4 -4
  272. package/src/scenarios/memory-attribution-replay-stable.test.ts +4 -3
  273. package/src/scenarios/memory-attribution-shape.test.ts +4 -3
  274. package/src/scenarios/memory-attribution-tenant-scoped.test.ts +3 -3
  275. package/src/scenarios/memory-capability-model-shape.test.ts +14 -15
  276. package/src/scenarios/memory-compaction-event-emitted.test.ts +7 -6
  277. package/src/scenarios/memory-compaction-provenance-tag.test.ts +3 -2
  278. package/src/scenarios/memory-compaction-sr1-carry-forward.test.ts +7 -6
  279. package/src/scenarios/memory-consolidation-idempotent.test.ts +5 -4
  280. package/src/scenarios/memory-consolidation-shape.test.ts +9 -11
  281. package/src/scenarios/memory-degraded-projection.test.ts +10 -9
  282. package/src/scenarios/memory-injection-budget.test.ts +15 -14
  283. package/src/scenarios/metric-emission.test.ts +4 -3
  284. package/src/scenarios/model-capability-insufficient.test.ts +19 -17
  285. package/src/scenarios/model-capability-substituted.test.ts +20 -18
  286. package/src/scenarios/multi-agent-confidence-escalation.test.ts +12 -11
  287. package/src/scenarios/multi-agent-handoff-state-machine.test.ts +10 -9
  288. package/src/scenarios/multi-agent-memory-lifecycle.test.ts +11 -10
  289. package/src/scenarios/multi-node-ordering.test.ts +4 -3
  290. package/src/scenarios/multi-party-conversation-behavioral.test.ts +6 -6
  291. package/src/scenarios/multi-party-conversation-shape.test.ts +21 -23
  292. package/src/scenarios/multi-region-idempotency-behavior.test.ts +16 -15
  293. package/src/scenarios/multi-region-idempotency.test.ts +6 -5
  294. package/src/scenarios/node-failed-payload-shape.test.ts +5 -4
  295. package/src/scenarios/node-module-required-capabilities-shape.test.ts +10 -8
  296. package/src/scenarios/oauth-authorization-code-roundtrip.test.ts +5 -4
  297. package/src/scenarios/oauth-capability-shape.test.ts +5 -4
  298. package/src/scenarios/oauth-connector-redaction.test.ts +4 -3
  299. package/src/scenarios/openapi-resolved-paths.test.ts +6 -5
  300. package/src/scenarios/orchestratorConservativePath.test.ts +2 -1
  301. package/src/scenarios/orchestratorDispatch.test.ts +2 -1
  302. package/src/scenarios/orchestratorTermination.test.ts +2 -1
  303. package/src/scenarios/org-position-no-authority-escalation.test.ts +2 -2
  304. package/src/scenarios/otel-collector-canary-inspection.test.ts +8 -7
  305. package/src/scenarios/otel-emission-grpc.test.ts +3 -2
  306. package/src/scenarios/otel-emission.test.ts +6 -5
  307. package/src/scenarios/otel-trace-propagation-subworkflow.test.ts +7 -6
  308. package/src/scenarios/otel-trace-propagation.test.ts +2 -1
  309. package/src/scenarios/owner-subject-echo.test.ts +13 -12
  310. package/src/scenarios/owner-subject-shape.test.ts +11 -10
  311. package/src/scenarios/pack-manifest-extension-opacity.test.ts +8 -8
  312. package/src/scenarios/pack-registry-isolation.test.ts +4 -3
  313. package/src/scenarios/pack-registry-publish.test.ts +65 -63
  314. package/src/scenarios/pack-registry.test.ts +38 -36
  315. package/src/scenarios/pause-resume.test.ts +18 -16
  316. package/src/scenarios/policies.test.ts +20 -18
  317. package/src/scenarios/production-backpressure.test.ts +18 -16
  318. package/src/scenarios/production-retention-expiry.test.ts +8 -6
  319. package/src/scenarios/profile-discovery-core-alias.test.ts +13 -11
  320. package/src/scenarios/profileDerivation.test.ts +34 -33
  321. package/src/scenarios/prompt-all-four-kinds-events.test.ts +11 -9
  322. package/src/scenarios/prompt-composed-secret-redaction.test.ts +11 -9
  323. package/src/scenarios/prompt-composed-trust-marker.test.ts +10 -8
  324. package/src/scenarios/prompt-end-to-end-events.test.ts +17 -15
  325. package/src/scenarios/prompt-list-and-fetch.test.ts +16 -14
  326. package/src/scenarios/prompt-mutable-lifecycle.test.ts +13 -11
  327. package/src/scenarios/prompt-mutation-workspace-membership-enforced.test.ts +6 -4
  328. package/src/scenarios/prompt-pack-install.test.ts +14 -12
  329. package/src/scenarios/prompt-prefix-cache.test.ts +19 -17
  330. package/src/scenarios/prompt-read-workspace-membership-enforced.test.ts +11 -9
  331. package/src/scenarios/prompt-render-deterministic.test.ts +19 -17
  332. package/src/scenarios/prompt-resolution-chain-agent-intrinsic.test.ts +7 -5
  333. package/src/scenarios/prompt-resolution-chain-event.test.ts +12 -10
  334. package/src/scenarios/prompt-resolution-chain-fallback-cascade.test.ts +10 -8
  335. package/src/scenarios/prompt-resolution-chain-node-wins.test.ts +8 -6
  336. package/src/scenarios/prompt-template-shape.test.ts +30 -28
  337. package/src/scenarios/proposal-reviewable-learning.test.ts +19 -19
  338. package/src/scenarios/protocol-versions-array.test.ts +10 -9
  339. package/src/scenarios/provider-usage.test.ts +17 -15
  340. package/src/scenarios/providerPolicyEnforcement.test.ts +16 -14
  341. package/src/scenarios/purpose-propagation.test.ts +23 -21
  342. package/src/scenarios/queue-ack-nack-dlq.test.ts +12 -10
  343. package/src/scenarios/queue-cross-tenant-isolation.test.ts +8 -6
  344. package/src/scenarios/queue-publish-consume-roundtrip.test.ts +10 -8
  345. package/src/scenarios/rate-limit-envelope.test.ts +6 -5
  346. package/src/scenarios/redaction.test.ts +13 -11
  347. package/src/scenarios/redactionAdversarial.test.ts +11 -9
  348. package/src/scenarios/registry-public.test.ts +12 -11
  349. package/src/scenarios/replay-divergence-at-refusal.test.ts +16 -15
  350. package/src/scenarios/replay-fanout-suppression.test.ts +16 -15
  351. package/src/scenarios/replay-fork-arbitrary.test.ts +24 -23
  352. package/src/scenarios/replay-fork.test.ts +20 -19
  353. package/src/scenarios/replay-llm-cache-key-portable.test.ts +11 -9
  354. package/src/scenarios/replay-llm-cache-key.test.ts +22 -21
  355. package/src/scenarios/replay-observable-sequence-determinism.test.ts +4 -3
  356. package/src/scenarios/replay-retention-expiry.test.ts +11 -9
  357. package/src/scenarios/replay-side-effect-suppression.test.ts +21 -19
  358. package/src/scenarios/replayDeterminism.test.ts +13 -12
  359. package/src/scenarios/requirement-ledger.test.ts +15 -14
  360. package/src/scenarios/restart-during-run.test.ts +3 -2
  361. package/src/scenarios/route-coverage.test.ts +20 -19
  362. package/src/scenarios/run-diff.test.ts +22 -20
  363. package/src/scenarios/run-execution-bounds-shape.test.ts +14 -12
  364. package/src/scenarios/run-transport-economy.test.ts +13 -11
  365. package/src/scenarios/runner-ledger.test.ts +17 -16
  366. package/src/scenarios/runs-lifecycle.test.ts +7 -6
  367. package/src/scenarios/runtime-capabilities.test.ts +12 -10
  368. package/src/scenarios/runtime-requires-install-gate.test.ts +8 -8
  369. package/src/scenarios/runtime-requires-shape.test.ts +7 -6
  370. package/src/scenarios/safefetch-behavior.test.ts +22 -21
  371. package/src/scenarios/safefetch-live-audit.test.ts +14 -13
  372. package/src/scenarios/sandbox-memory-cap.test.ts +2 -1
  373. package/src/scenarios/sandbox-mvp-behavior.test.ts +24 -23
  374. package/src/scenarios/sandbox-no-host-fs-escape.test.ts +3 -2
  375. package/src/scenarios/sandbox-timeout-cap.test.ts +2 -1
  376. package/src/scenarios/sandbox-wasm-isolation.test.ts +14 -15
  377. package/src/scenarios/sandbox-wasm-timeout.test.ts +5 -6
  378. package/src/scenarios/scheduling-capability-shape.test.ts +8 -6
  379. package/src/scenarios/scheduling-cron-fires-once.test.ts +8 -6
  380. package/src/scenarios/search-bm25-roundtrip.test.ts +8 -6
  381. package/src/scenarios/secret-leakage-otel-attribute.test.ts +22 -20
  382. package/src/scenarios/self-hosted-runner.test.ts +26 -24
  383. package/src/scenarios/semantic-digest-vectors.test.ts +8 -7
  384. package/src/scenarios/speech-synthesis-roundtrip.test.ts +9 -7
  385. package/src/scenarios/speech-synthesis-unadvertised.test.ts +5 -3
  386. package/src/scenarios/sql-injection-rejection.test.ts +8 -6
  387. package/src/scenarios/sql-transaction-atomicity.test.ts +10 -8
  388. package/src/scenarios/staleClaim.test.ts +5 -4
  389. package/src/scenarios/stream-modes-buffer.test.ts +8 -7
  390. package/src/scenarios/stream-modes-mixed.test.ts +10 -9
  391. package/src/scenarios/stream-modes.test.ts +12 -11
  392. package/src/scenarios/stream-subscribe-from-beginning.test.ts +11 -9
  393. package/src/scenarios/stream-text-fixture.test.ts +14 -13
  394. package/src/scenarios/streamReconnect.test.ts +13 -11
  395. package/src/scenarios/strict-behavior-gate.test.ts +9 -8
  396. package/src/scenarios/subrun-approval-fail-closed.test.ts +2 -2
  397. package/src/scenarios/subrun-approval-gate.test.ts +3 -3
  398. package/src/scenarios/subrun-attestation-shape.test.ts +2 -2
  399. package/src/scenarios/subrun-checksum-stable.test.ts +4 -4
  400. package/src/scenarios/subworkflow-input-mapping.test.ts +16 -14
  401. package/src/scenarios/subworkflow.test.ts +10 -9
  402. package/src/scenarios/table-cross-tenant-isolation.test.ts +8 -6
  403. package/src/scenarios/table-cursor-pagination.test.ts +8 -6
  404. package/src/scenarios/table-schema-enforcement.test.ts +7 -5
  405. package/src/scenarios/test-seam-unauthenticated.test.ts +2 -2
  406. package/src/scenarios/tool-catalog-compact-projection.test.ts +10 -8
  407. package/src/scenarios/tool-catalog-projection.test.ts +11 -9
  408. package/src/scenarios/tool-descriptor-shape.test.ts +14 -15
  409. package/src/scenarios/tool-hooks-authorization-fail-closed.test.ts +3 -3
  410. package/src/scenarios/tool-hooks-content-free.test.ts +4 -4
  411. package/src/scenarios/tool-hooks-failure-honesty.test.ts +4 -4
  412. package/src/scenarios/tool-hooks-rate-limit.test.ts +2 -2
  413. package/src/scenarios/tool-hooks-secret-redaction.test.ts +2 -2
  414. package/src/scenarios/tool-hooks-shape.test.ts +3 -3
  415. package/src/scenarios/tool-session-lifecycle.test.ts +14 -13
  416. package/src/scenarios/trigger-bridge-delivery.test.ts +14 -14
  417. package/src/scenarios/trigger-bridge-shape.test.ts +19 -20
  418. package/src/scenarios/trigger-ingestion.test.ts +19 -17
  419. package/src/scenarios/trigger-stream-cdc-sources.test.ts +16 -14
  420. package/src/scenarios/vector-knn-roundtrip.test.ts +9 -7
  421. package/src/scenarios/verifier-gating.test.ts +8 -6
  422. package/src/scenarios/version-fold.test.ts +9 -8
  423. package/src/scenarios/version-negotiation.test.ts +12 -11
  424. package/src/scenarios/voice-bargein-no-partial-leak.test.ts +8 -6
  425. package/src/scenarios/voice-event-payloads-shape.test.ts +10 -11
  426. package/src/scenarios/voice-interim-not-durable.test.ts +7 -5
  427. package/src/scenarios/voice-streamref-tenant-bound.test.ts +5 -3
  428. package/src/scenarios/voice-synthesis-streaming.test.ts +11 -9
  429. package/src/scenarios/voice-transcription-streaming.test.ts +8 -6
  430. package/src/scenarios/voice-transcription-unadvertised.test.ts +5 -3
  431. package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +3 -2
  432. package/src/scenarios/wasm-pack-invoke-completed.test.ts +3 -2
  433. package/src/scenarios/wasm-pack-invoke-suspended.test.ts +3 -2
  434. package/src/scenarios/wasm-pack-load.test.ts +4 -3
  435. package/src/scenarios/wasm-pack-memory-cap.test.ts +5 -4
  436. package/src/scenarios/wasm-pack-replay-determinism.test.ts +2 -1
  437. package/src/scenarios/webhook-negative.test.ts +11 -9
  438. package/src/scenarios/webhook-receiver-adversarial.test.ts +8 -7
  439. package/src/scenarios/webhook-sig-algorithm.test.ts +5 -3
  440. package/src/scenarios/webhook-signed-delivery.test.ts +16 -15
  441. package/src/scenarios/webhook-tenant-isolation.test.ts +16 -14
  442. package/src/scenarios/workflow-chain-deferred-parameters.test.ts +28 -30
  443. package/src/scenarios/workflow-chain-expansion.test.ts +21 -20
  444. package/src/scenarios/workflow-chain-host-expansion.test.ts +21 -20
  445. package/src/scenarios/workflow-chain-pack-manifest-validation.test.ts +14 -12
  446. package/src/scenarios/workflow-chain-pack-signature-verification.test.ts +5 -4
  447. package/src/scenarios/workflow-chain-unresolvable-typeid.test.ts +9 -8
  448. package/src/scenarios/workflow-primary-output-annotation.test.ts +7 -6
  449. package/src/scenarios/workflow-variable-format.test.ts +24 -24
  450. package/src/scenarios/workload-identity-behavior.test.ts +15 -13
  451. package/src/scenarios/workload-identity-chain-bounds.test.ts +18 -17
  452. package/src/scenarios/workspace-behavior.test.ts +19 -18
  453. package/src/scenarios/workspace-capability-shape.test.ts +3 -2
  454. package/src/scenarios/workspace-cross-tenant-isolation-blackbox.test.ts +6 -5
  455. package/src/scenarios/workspace-cross-tenant-isolation.test.ts +7 -6
  456. package/src/scenarios/x-openwop-form-pack-manifest.test.ts +8 -7
  457. package/src/setup.ts +4 -2
  458. package/vitest.config.ts +5 -1
  459. package/api/.redocly.lint-ignore.yaml +0 -22
  460. package/api/asyncapi.yaml +0 -834
  461. package/api/grpc/openwop.proto +0 -251
  462. package/api/openapi.yaml +0 -2693
  463. package/api/redocly.yaml +0 -23
  464. package/schemas/README.md +0 -144
  465. package/schemas/a2a-task-state.schema.json +0 -78
  466. package/schemas/a2ui-surface-delta-frame.schema.json +0 -48
  467. package/schemas/agent-deployment-transition.schema.json +0 -49
  468. package/schemas/agent-deployment.schema.json +0 -54
  469. package/schemas/agent-eval-suite.schema.json +0 -140
  470. package/schemas/agent-inventory-response.schema.json +0 -115
  471. package/schemas/agent-manifest.schema.json +0 -156
  472. package/schemas/agent-org-chart.schema.json +0 -82
  473. package/schemas/agent-ref.schema.json +0 -63
  474. package/schemas/agent-roster-entry.schema.json +0 -81
  475. package/schemas/agent-roster-response.schema.json +0 -21
  476. package/schemas/ai-envelope.schema.json +0 -134
  477. package/schemas/annotation-create.schema.json +0 -37
  478. package/schemas/annotation.schema.json +0 -56
  479. package/schemas/artifact-type-pack-manifest.schema.json +0 -244
  480. package/schemas/audit-verify-result.schema.json +0 -90
  481. package/schemas/budget-policy.schema.json +0 -18
  482. package/schemas/capabilities.schema.json +0 -4124
  483. package/schemas/certification-bundle-v2.schema.json +0 -118
  484. package/schemas/channel-presence-payload.schema.json +0 -41
  485. package/schemas/channel-written-payload.schema.json +0 -55
  486. package/schemas/chat-card-pack-manifest.schema.json +0 -253
  487. package/schemas/compact-tool-descriptor.schema.json +0 -51
  488. package/schemas/compensation-policy.schema.json +0 -64
  489. package/schemas/conformance-certification-bundle.schema.json +0 -86
  490. package/schemas/connection-pack-manifest.schema.json +0 -353
  491. package/schemas/conversation-event.schema.json +0 -168
  492. package/schemas/conversation-turn.schema.json +0 -117
  493. package/schemas/core-conformance-mock-agent-config.schema.json +0 -152
  494. package/schemas/credential-provenance.schema.json +0 -18
  495. package/schemas/credential-reference.schema.json +0 -21
  496. package/schemas/debug-bundle.schema.json +0 -196
  497. package/schemas/dispatch-config.schema.json +0 -112
  498. package/schemas/envelopes/clarification.request.schema.json +0 -52
  499. package/schemas/envelopes/error.schema.json +0 -30
  500. package/schemas/envelopes/media.audio.schema.json +0 -38
  501. package/schemas/envelopes/media.file.schema.json +0 -37
  502. package/schemas/envelopes/media.image.schema.json +0 -33
  503. package/schemas/envelopes/schema.request.schema.json +0 -26
  504. package/schemas/envelopes/schema.response.schema.json +0 -22
  505. package/schemas/envelopes/ui.a2ui-surface.schema.json +0 -154
  506. package/schemas/error-envelope.schema.json +0 -25
  507. package/schemas/eval-summary.schema.json +0 -92
  508. package/schemas/export-bundle.schema.json +0 -66
  509. package/schemas/form-content-pack-manifest.schema.json +0 -243
  510. package/schemas/frontend-plugin-manifest.schema.json +0 -109
  511. package/schemas/goal.schema.json +0 -104
  512. package/schemas/heartbeat-evaluated.schema.json +0 -14
  513. package/schemas/heartbeat-state-changed.schema.json +0 -14
  514. package/schemas/localized-content-language-settings.schema.json +0 -26
  515. package/schemas/localized-content-page-response.schema.json +0 -60
  516. package/schemas/localized-content-page.schema.json +0 -62
  517. package/schemas/localized-content-section.schema.json +0 -51
  518. package/schemas/memory-entry.schema.json +0 -36
  519. package/schemas/memory-list-options.schema.json +0 -37
  520. package/schemas/node-pack-manifest.schema.json +0 -654
  521. package/schemas/orchestrator-decision.schema.json +0 -90
  522. package/schemas/org-chart-responsibility-view.schema.json +0 -26
  523. package/schemas/pack-lockfile.schema.json +0 -108
  524. package/schemas/prompt-kind.schema.json +0 -8
  525. package/schemas/prompt-pack-manifest.schema.json +0 -117
  526. package/schemas/prompt-ref.schema.json +0 -40
  527. package/schemas/prompt-template.schema.json +0 -149
  528. package/schemas/proposal.schema.json +0 -84
  529. package/schemas/registry-version-manifest.schema.json +0 -208
  530. package/schemas/residency.schema.json +0 -16
  531. package/schemas/run-ancestry-response.schema.json +0 -54
  532. package/schemas/run-diff-response.schema.json +0 -64
  533. package/schemas/run-event-payloads.schema.json +0 -4452
  534. package/schemas/run-event.schema.json +0 -211
  535. package/schemas/run-options.schema.json +0 -80
  536. package/schemas/run-orchestrator-decided-event.schema.json +0 -20
  537. package/schemas/run-snapshot.schema.json +0 -148
  538. package/schemas/security-advisory.schema.json +0 -109
  539. package/schemas/self-hosted-runner-dispatch-frame.schema.json +0 -79
  540. package/schemas/self-hosted-runner-registration.schema.json +0 -53
  541. package/schemas/self-hosted-runner-result-frame.schema.json +0 -38
  542. package/schemas/subject.schema.json +0 -56
  543. package/schemas/suspend-request.schema.json +0 -207
  544. package/schemas/tool-descriptor.schema.json +0 -63
  545. package/schemas/trigger-event.schema.json +0 -222
  546. package/schemas/trigger-subscription-registration.schema.json +0 -67
  547. package/schemas/trigger-subscription.schema.json +0 -26
  548. package/schemas/ui-plugin-message.schema.json +0 -90
  549. package/schemas/workflow-chain-pack-manifest.schema.json +0 -615
  550. package/schemas/workflow-definition.schema.json +0 -850
  551. package/schemas/workload-identity.schema.json +0 -79
  552. package/schemas/workspace-file-create.schema.json +0 -20
  553. package/schemas/workspace-file.schema.json +0 -39
@@ -53,6 +53,7 @@
53
53
  import { describe, it, expect } from 'vitest';
54
54
  import { driver } from '../lib/driver.js';
55
55
  import { softSkip } from '../lib/soft-skip.js';
56
+ import { req } from '../lib/requirement-ids.js';
56
57
 
57
58
  const SEAM = '/v1/host/sample/test/idempotency/concurrent-claim';
58
59
 
@@ -64,12 +65,12 @@ type ClaimResult = {
64
65
  };
65
66
 
66
67
  /** Is the §25 seam mounted? 404/403/501 mean not mounted — anything else is a real answer. */
67
- async function seamPresent(): Promise<ClaimResult | null> {
68
+ async function seamPresent(requirementId: string): Promise<ClaimResult | null> {
68
69
  const res = await driver.post(SEAM, { executors: 2 });
69
70
  if (res.status === 404 || res.status === 403 || res.status === 501) return null;
70
71
  expect(
71
72
  res.status,
72
- driver.describe(
73
+ req(requirementId,
73
74
  'host-sample-test-seams.md §25',
74
75
  'a mounted concurrent-claim seam MUST answer 200; a non-200 that is not 404/403/501 is a broken seam, not an absent one',
75
76
  ),
@@ -79,28 +80,28 @@ async function seamPresent(): Promise<ClaimResult | null> {
79
80
 
80
81
  describe('idempotency-concurrent-claim: two executors of one run, one effect (RFC 0150 §B, G17)', () => {
81
82
  it('every executor mints the SAME logicalInvocationId — without this, one delivery proves nothing', async () => {
82
- const out = await seamPresent();
83
+ const out = await seamPresent('openwop.it.idempotency-concurrent-claim.every-executor-mints-the-same-logicalinvocationid-without-this-one-delivery-prov');
83
84
  if (out === null) return softSkip('blocked', `${SEAM} not mounted (host-sample-test-seams.md §25)`);
84
85
 
85
86
  const minted = out.mintedIds;
86
87
  expect(
87
88
  Array.isArray(minted),
88
- driver.describe('host-sample-test-seams.md §25', 'the seam MUST report mintedIds, one per executor'),
89
+ req('openwop.it.idempotency-concurrent-claim.every-executor-mints-the-same-logicalinvocationid-without-this-one-delivery-prov', 'host-sample-test-seams.md §25', 'the seam MUST report mintedIds, one per executor'),
89
90
  ).toBe(true);
90
91
  const ids = minted as unknown[];
91
92
  expect(
92
93
  ids.length,
93
- driver.describe('host-sample-test-seams.md §25', 'mintedIds length MUST equal the executors that reached the chokepoint'),
94
+ req('openwop.it.idempotency-concurrent-claim.every-executor-mints-the-same-logicalinvocationid-without-this-one-delivery-prov', 'host-sample-test-seams.md §25', 'mintedIds length MUST equal the executors that reached the chokepoint'),
94
95
  ).toBe(out.attempted);
95
96
  expect(
96
97
  ids.length >= 2,
97
- driver.describe('host-sample-test-seams.md §25', 'fewer than two executors is not a race and cannot witness the rule'),
98
+ req('openwop.it.idempotency-concurrent-claim.every-executor-mints-the-same-logicalinvocationid-without-this-one-delivery-prov', 'host-sample-test-seams.md §25', 'fewer than two executors is not a race and cannot witness the rule'),
98
99
  ).toBe(true);
99
100
 
100
101
  const unique = new Set(ids.map((x) => JSON.stringify(x)));
101
102
  expect(
102
103
  unique.size,
103
- driver.describe(
104
+ req('openwop.it.idempotency-concurrent-claim.every-executor-mints-the-same-logicalinvocationid-without-this-one-delivery-prov',
104
105
  'idempotency.md §"Idempotency key composition" (Across a recovery boundary)',
105
106
  'all executors MUST mint one identity — differing ids mean they never collided, so a single delivery is a vacuous pass rather than a working claim',
106
107
  ),
@@ -108,16 +109,16 @@ describe('idempotency-concurrent-claim: two executors of one run, one effect (RF
108
109
  });
109
110
 
110
111
  it('exactly one effect escapes, however many executors attempt it', async () => {
111
- const out = await seamPresent();
112
+ const out = await seamPresent('openwop.it.idempotency-concurrent-claim.exactly-one-effect-escapes-however-many-executors-attempt-it');
112
113
  if (out === null) return softSkip('blocked', `${SEAM} not mounted (host-sample-test-seams.md §25)`);
113
114
 
114
115
  expect(
115
116
  typeof out.attempted === 'number' && (out.attempted as number) >= 2,
116
- driver.describe('host-sample-test-seams.md §25', 'the seam MUST report how many executors attempted the effect'),
117
+ req('openwop.it.idempotency-concurrent-claim.exactly-one-effect-escapes-however-many-executors-attempt-it', 'host-sample-test-seams.md §25', 'the seam MUST report how many executors attempted the effect'),
117
118
  ).toBe(true);
118
119
  expect(
119
120
  out.delivered,
120
- driver.describe(
121
+ req('openwop.it.idempotency-concurrent-claim.exactly-one-effect-escapes-however-many-executors-attempt-it',
121
122
  'idempotency.md §"Concurrent duplicates (Layer 2)"',
122
123
  'the engine MUST ensure at most one concurrent executor performs the external effect — the guarding persist MUST be an atomic claim, not a read-then-write',
123
124
  ),
@@ -125,7 +126,7 @@ describe('idempotency-concurrent-claim: two executors of one run, one effect (RF
125
126
  });
126
127
 
127
128
  it('the race is real at higher concurrency too — a claim that only holds at 2 is not a claim', async () => {
128
- const probe = await seamPresent();
129
+ const probe = await seamPresent('openwop.it.idempotency-concurrent-claim.the-race-is-real-at-higher-concurrency-too-a-claim-that-only-holds-at-2-is-not-a');
129
130
  if (probe === null) return softSkip('blocked', `${SEAM} not mounted (host-sample-test-seams.md §25)`);
130
131
 
131
132
  const res = await driver.post(SEAM, { executors: 5 });
@@ -138,7 +139,7 @@ describe('idempotency-concurrent-claim: two executors of one run, one effect (RF
138
139
  const out = res.json as ClaimResult;
139
140
  expect(
140
141
  out.delivered,
141
- driver.describe(
142
+ req('openwop.it.idempotency-concurrent-claim.the-race-is-real-at-higher-concurrency-too-a-claim-that-only-holds-at-2-is-not-a',
142
143
  'idempotency.md §"Concurrent duplicates (Layer 2)"',
143
144
  'exactly one effect regardless of how many executors race — at most one wins the compare-and-set, the rest observe the hit',
144
145
  ),
@@ -147,7 +148,7 @@ describe('idempotency-concurrent-claim: two executors of one run, one effect (RF
147
148
  if (ids.length >= 2) {
148
149
  expect(
149
150
  new Set(ids.map((x) => JSON.stringify(x))).size,
150
- driver.describe('idempotency.md §"Idempotency key composition"', 'one identity across all executors at any concurrency'),
151
+ req('openwop.it.idempotency-concurrent-claim.the-race-is-real-at-higher-concurrency-too-a-claim-that-only-holds-at-2-is-not-a', 'idempotency.md §"Idempotency key composition"', 'one identity across all executors at any concurrency'),
151
152
  ).toBe(1);
152
153
  }
153
154
  });
@@ -44,6 +44,8 @@ import { createHash } from 'node:crypto';
44
44
  import { existsSync } from 'node:fs';
45
45
  import { dirname, resolve } from 'node:path';
46
46
  import { fileURLToPath } from 'node:url';
47
+ import { req } from '../lib/requirement-ids.js';
48
+ import { softSkip } from '../lib/soft-skip.js';
47
49
 
48
50
  const __dirname = dirname(fileURLToPath(import.meta.url));
49
51
  const PACK_PATH = resolve(__dirname, '../../../packs/core.openwop.http/index.mjs');
@@ -114,14 +116,14 @@ describe('category: core.openwop.http.idempotency-key — determinism contract',
114
116
  // — the scenario soft-skips rather than failing.
115
117
  if (!packAvailable) {
116
118
  console.warn(`[idempotency-key-determinism] packs/core.openwop.http/index.mjs not present; skipping`);
117
- expect(packAvailable).toBe(false);
118
- return;
119
+ expect(packAvailable, req('openwop.it.idempotency-key-determinism.skips-cleanly-when-packs-is-not-bundled', 'idempotency.md §"Idempotency-Key', 'skips cleanly when packs/ is not bundled')).toBe(false);
120
+ return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!packAvailable` returned early ([idempotency-key-determinism] packs/core.openwop.http/index.mjs not present; skipping)');
119
121
  }
120
122
  expect(packAvailable).toBe(true);
121
123
  });
122
124
 
123
125
  it('default mode (composite) — identical (runId, nodeId, payload) produces identical keys', async () => {
124
- if (!packAvailable) return;
126
+ if (!packAvailable) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!packAvailable` returned early');
125
127
  const ctx: IdempotencyKeyCtx = {
126
128
  runId: 'run-1',
127
129
  nodeId: 'node-1',
@@ -129,45 +131,45 @@ describe('category: core.openwop.http.idempotency-key — determinism contract',
129
131
  };
130
132
  const a = await idempotencyKey(ctx);
131
133
  const b = await idempotencyKey(ctx);
132
- expect(a.outputs.key, 'idempotency.md §Idempotency-Key: same logical request MUST produce same key').toBe(b.outputs.key);
134
+ expect(a.outputs.key, req('openwop.it.idempotency-key-determinism.default-mode-composite-identical-runid-nodeid-payload-produces-identical-keys', 'idempotency.md §"Idempotency-Key', 'idempotency.md §Idempotency-Key: same logical request MUST produce same key')).toBe(b.outputs.key);
133
135
  expect(a.outputs.key).toMatch(KEY_PATTERN);
134
136
  });
135
137
 
136
138
  it('payload sensitivity — different payload produces different key', async () => {
137
- if (!packAvailable) return;
139
+ if (!packAvailable) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!packAvailable` returned early');
138
140
  const baseCtx = { runId: 'run-1', nodeId: 'node-1' };
139
141
  const a = await idempotencyKey({ ...baseCtx, inputs: { payload: { hello: 'world' } } });
140
142
  const b = await idempotencyKey({ ...baseCtx, inputs: { payload: { hello: 'CHANGED' } } });
141
- expect(a.outputs.key).not.toBe(b.outputs.key);
143
+ expect(a.outputs.key, req('openwop.it.idempotency-key-determinism.payload-sensitivity-different-payload-produces-different-key', 'idempotency.md §"Idempotency-Key', 'payload sensitivity — different payload produces different key')).not.toBe(b.outputs.key);
142
144
  });
143
145
 
144
146
  it('run isolation — different runId produces different key', async () => {
145
- if (!packAvailable) return;
147
+ if (!packAvailable) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!packAvailable` returned early');
146
148
  const payload = { hello: 'world' };
147
149
  const a = await idempotencyKey({ runId: 'run-1', nodeId: 'node-1', inputs: { payload } });
148
150
  const b = await idempotencyKey({ runId: 'run-2', nodeId: 'node-1', inputs: { payload } });
149
- expect(a.outputs.key).not.toBe(b.outputs.key);
151
+ expect(a.outputs.key, req('openwop.it.idempotency-key-determinism.run-isolation-different-runid-produces-different-key', 'idempotency.md §"Idempotency-Key', 'run isolation — different runId produces different key')).not.toBe(b.outputs.key);
150
152
  });
151
153
 
152
154
  it('node isolation — different nodeId produces different key', async () => {
153
- if (!packAvailable) return;
155
+ if (!packAvailable) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!packAvailable` returned early');
154
156
  const payload = { hello: 'world' };
155
157
  const a = await idempotencyKey({ runId: 'run-1', nodeId: 'node-A', inputs: { payload } });
156
158
  const b = await idempotencyKey({ runId: 'run-1', nodeId: 'node-B', inputs: { payload } });
157
- expect(a.outputs.key).not.toBe(b.outputs.key);
159
+ expect(a.outputs.key, req('openwop.it.idempotency-key-determinism.node-isolation-different-nodeid-produces-different-key', 'idempotency.md §"Idempotency-Key', 'node isolation — different nodeId produces different key')).not.toBe(b.outputs.key);
158
160
  });
159
161
 
160
162
  it('hash mode is deterministic in the payload alone (run/node ignored)', async () => {
161
- if (!packAvailable) return;
163
+ if (!packAvailable) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!packAvailable` returned early');
162
164
  const payload = { request: 'payload-here', id: 42 };
163
165
  const a = await idempotencyKey({ config: { mode: 'hash' }, runId: 'run-1', nodeId: 'node-1', inputs: { payload } });
164
166
  const b = await idempotencyKey({ config: { mode: 'hash' }, runId: 'run-2', nodeId: 'node-2', inputs: { payload } });
165
- expect(a.outputs.key, 'hash mode MUST ignore run/node — useful for global remote-dedup caches').toBe(b.outputs.key);
167
+ expect(a.outputs.key, req('openwop.it.idempotency-key-determinism.hash-mode-is-deterministic-in-the-payload-alone-run-node-ignored', 'idempotency.md §"Idempotency-Key', 'hash mode MUST ignore run/node — useful for global remote-dedup caches')).toBe(b.outputs.key);
166
168
  expect(a.outputs.key).toMatch(KEY_PATTERN);
167
169
  });
168
170
 
169
171
  it('output shape — every emitted key matches openwop-<sha256-prefix-16>', async () => {
170
- if (!packAvailable) return;
172
+ if (!packAvailable) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!packAvailable` returned early');
171
173
  const samples: IdempotencyKeyCtx[] = [
172
174
  { runId: 'r', nodeId: 'n', inputs: { payload: null } },
173
175
  { runId: 'r', nodeId: 'n', inputs: { payload: 'string-payload' } },
@@ -177,12 +179,12 @@ describe('category: core.openwop.http.idempotency-key — determinism contract',
177
179
  ];
178
180
  for (const ctx of samples) {
179
181
  const result = await idempotencyKey(ctx);
180
- expect(result.outputs.key, `key shape for ctx=${JSON.stringify(ctx)}`).toMatch(KEY_PATTERN);
182
+ expect(result.outputs.key, req('openwop.it.idempotency-key-determinism.output-shape-every-emitted-key-matches-openwop-sha256-prefix-16', 'idempotency.md §"Idempotency-Key', `key shape for ctx=${JSON.stringify(ctx)}`)).toMatch(KEY_PATTERN);
181
183
  }
182
184
  });
183
185
 
184
186
  it('uuid mode rejects with CONFIG_INVALID (safety-fix per 1.1.2)', async () => {
185
- if (!packAvailable) return;
187
+ if (!packAvailable) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!packAvailable` returned early');
186
188
  // The removed mode names the safety-fix in its error message so
187
189
  // pinned-version callers diagnosing a regression find the fix.
188
190
  let caught: unknown;
@@ -191,13 +193,13 @@ describe('category: core.openwop.http.idempotency-key — determinism contract',
191
193
  } catch (err) {
192
194
  caught = err;
193
195
  }
194
- expect(caught, 'mode: uuid MUST be rejected — the 1.1.0/1.1.1 non-deterministic default was removed').toBeInstanceOf(Error);
196
+ expect(caught, req('openwop.it.idempotency-key-determinism.uuid-mode-rejects-with-config-invalid-safety-fix-per-1-1-2', 'idempotency.md §"Idempotency-Key', 'mode: uuid MUST be rejected — the 1.1.0/1.1.1 non-deterministic default was removed')).toBeInstanceOf(Error);
195
197
  expect((caught as Error & { code?: string }).code).toBe('CONFIG_INVALID');
196
198
  expect((caught as Error).message).toMatch(/uuid.*removed|safety-fix/i);
197
199
  });
198
200
 
199
201
  it('cross-impl invariant — pack output equals canonical SHA-256 formula', async () => {
200
- if (!packAvailable) return;
202
+ if (!packAvailable) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!packAvailable` returned early');
201
203
  // Five fixed vectors. A third-party core.openwop.http reimplementation
202
204
  // can run the same scenario against its runtime — agreement on these
203
205
  // vectors proves wire-level interop on the Idempotency-Key shape.
@@ -217,7 +219,7 @@ describe('category: core.openwop.http.idempotency-key — determinism contract',
217
219
  const expected = canonicalCompositeKey(v.runId, v.nodeId, v.payload);
218
220
  expect(
219
221
  packed.outputs.key,
220
- `vector ${JSON.stringify(v)} — pack output MUST match canonical sha256(runId\\0nodeId\\0JSON(payload))`,
222
+ req('openwop.it.idempotency-key-determinism.cross-impl-invariant-pack-output-equals-canonical-sha-256-formula', 'idempotency.md §"Idempotency-Key', `vector ${JSON.stringify(v)} — pack output MUST match canonical sha256(runId\\0nodeId\\0JSON(payload))`),
221
223
  ).toBe(expected);
222
224
  }
223
225
 
@@ -18,6 +18,7 @@ import { describe, it, expect } from 'vitest';
18
18
  import { driver } from '../lib/driver.js';
19
19
  import { isFixtureAdvertised } from '../lib/fixtures.js';
20
20
  import { readErrorCode } from '../lib/error-envelope.js';
21
+ import { req } from '../lib/requirement-ids.js';
21
22
 
22
23
  const WORKFLOW_ID = 'conformance-idempotent';
23
24
  const SKIP_NO_FIXTURE = !isFixtureAdvertised(WORKFLOW_ID);
@@ -34,7 +35,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('idempotency: same key + same body replays per
34
35
  const first = await driver.post('/v1/runs', body, {
35
36
  headers: { 'Idempotency-Key': key },
36
37
  });
37
- expect(first.status, driver.describe(
38
+ expect(first.status, req('openwop.it.idempotency.returns-same-runid-twice-and-sets-openwop-idempotent-replay-on-the-replay',
38
39
  'rest-endpoints.md',
39
40
  'first POST /v1/runs MUST return 201',
40
41
  )).toBe(201);
@@ -45,20 +46,20 @@ describe.skipIf(SKIP_NO_FIXTURE)('idempotency: same key + same body replays per
45
46
  });
46
47
  expect(
47
48
  [200, 201].includes(replay.status),
48
- driver.describe(
49
+ req('openwop.it.idempotency.returns-same-runid-twice-and-sets-openwop-idempotent-replay-on-the-replay',
49
50
  'idempotency.md §Layer 1',
50
51
  'replay request with same key + same body MUST return success status (200/201)',
51
52
  ),
52
53
  ).toBe(true);
53
54
 
54
55
  const replayRunId = (replay.json as { runId: string }).runId;
55
- expect(replayRunId, driver.describe(
56
+ expect(replayRunId, req('openwop.it.idempotency.returns-same-runid-twice-and-sets-openwop-idempotent-replay-on-the-replay',
56
57
  'idempotency.md §Layer 1',
57
58
  'replay MUST return the SAME runId (no new run created)',
58
59
  )).toBe(firstRunId);
59
60
 
60
61
  const replayHeader = replay.headers.get('openwop-idempotent-replay');
61
- expect(replayHeader, driver.describe(
62
+ expect(replayHeader, req('openwop.it.idempotency.returns-same-runid-twice-and-sets-openwop-idempotent-replay-on-the-replay',
62
63
  'rest-endpoints.md POST /v1/runs response headers',
63
64
  'openwop-Idempotent-Replay header MUST be set on cache-served responses',
64
65
  )).toBeTruthy();
@@ -82,7 +83,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('idempotency: same key + different body conflic
82
83
  { headers: { 'Idempotency-Key': key } },
83
84
  );
84
85
 
85
- expect(conflict.status, driver.describe(
86
+ expect(conflict.status, req('openwop.it.idempotency.returns-409-when-the-body-changes-under-the-same-key',
86
87
  'idempotency.md §Layer 1',
87
88
  'same Idempotency-Key with a different body MUST return 409',
88
89
  )).toBe(409);
@@ -107,7 +108,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('idempotency: same key + different body conflic
107
108
  const LEGACY = ['idempotency_key_conflict', 'idempotency_key_replay_mismatch'];
108
109
  expect(
109
110
  code === 'idempotency_key_mismatch' || LEGACY.includes(code ?? ''),
110
- driver.describe(
111
+ req('openwop.it.idempotency.returns-409-when-the-body-changes-under-the-same-key',
111
112
  'idempotency.md §"Record shape, digest, and lease"',
112
113
  `a different request digest under the same scoped key MUST fail with the canonical ` +
113
114
  `\`idempotency_key_mismatch\` (legacy \`${LEGACY.join('` / `')}\` tolerated through the ` +
@@ -120,7 +121,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('idempotency: same key + different body conflic
120
121
  const conflictRunId = (conflict.json as { runId?: unknown } | undefined)?.runId;
121
122
  expect(
122
123
  conflictRunId,
123
- driver.describe(
124
+ req('openwop.it.idempotency.returns-409-when-the-body-changes-under-the-same-key',
124
125
  'idempotency.md §"Record shape, digest, and lease"',
125
126
  'a digest mismatch MUST NOT return the cached body (no runId on the 409)',
126
127
  ),
@@ -26,6 +26,8 @@
26
26
  import { describe, it, expect } from 'vitest';
27
27
  import { driver } from '../lib/driver.js';
28
28
  import { isFixtureAdvertised } from '../lib/fixtures.js';
29
+ import { req } from '../lib/requirement-ids.js';
30
+ import { softSkip } from '../lib/soft-skip.js';
29
31
 
30
32
  const WORKFLOW_ID = 'conformance-idempotent';
31
33
  const SKIP_NO_FIXTURE = !isFixtureAdvertised(WORKFLOW_ID);
@@ -40,7 +42,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('idempotency-retry: openwop-Idempotent-Replay h
40
42
  const body = { workflowId: WORKFLOW_ID, inputs: { nonce: 'replay-test' } };
41
43
 
42
44
  const first = await driver.post('/v1/runs', body, { headers: { 'Idempotency-Key': key } });
43
- expect(first.status, driver.describe(
45
+ expect(first.status, req('openwop.it.idempotencyRetry.first-request-with-new-key-returns-false-or-absent-should-per-current-spec-repla',
44
46
  'rest-endpoints.md',
45
47
  'first POST /v1/runs returns 201',
46
48
  )).toBe(201);
@@ -50,7 +52,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('idempotency-retry: openwop-Idempotent-Replay h
50
52
  // set to "false". Pre-RFC spec only requires it on the replay.
51
53
  // Permissive assertion: if present, MUST be "false" or "true".
52
54
  if (firstReplay !== null) {
53
- expect(['false', 'true'].includes(firstReplay), driver.describe(
55
+ expect(['false', 'true'].includes(firstReplay), req('openwop.it.idempotencyRetry.first-request-with-new-key-returns-false-or-absent-should-per-current-spec-repla',
54
56
  'idempotency.md §Server responsibilities',
55
57
  'openwop-Idempotent-Replay value MUST be "true" or "false"',
56
58
  )).toBe(true);
@@ -59,18 +61,18 @@ describe.skipIf(SKIP_NO_FIXTURE)('idempotency-retry: openwop-Idempotent-Replay h
59
61
  const replay = await driver.post('/v1/runs', body, { headers: { 'Idempotency-Key': key } });
60
62
  expect(
61
63
  [200, 201].includes(replay.status),
62
- driver.describe('idempotency.md §Layer 1', 'replay returns 200 or 201'),
64
+ req('openwop.it.idempotencyRetry.first-request-with-new-key-returns-false-or-absent-should-per-current-spec-repla', 'idempotency.md §Layer 1', 'replay returns 200 or 201'),
63
65
  ).toBe(true);
64
66
 
65
67
  const replayHeader = replay.headers.get('openwop-idempotent-replay');
66
68
  // Per idempotency.md §Server responsibilities #2: SHOULD be set.
67
69
  // RFC 0002 §1 promotes to MUST. Today's strictness: present on replay.
68
- expect(replayHeader, driver.describe(
70
+ expect(replayHeader, req('openwop.it.idempotencyRetry.first-request-with-new-key-returns-false-or-absent-should-per-current-spec-repla',
69
71
  'idempotency.md §Server responsibilities #2',
70
72
  'openwop-Idempotent-Replay SHOULD be set on idempotent replay responses',
71
73
  )).not.toBeNull();
72
74
  if (replayHeader !== null) {
73
- expect(replayHeader, driver.describe(
75
+ expect(replayHeader, req('openwop.it.idempotencyRetry.first-request-with-new-key-returns-false-or-absent-should-per-current-spec-repla',
74
76
  'idempotency.md §Server responsibilities #2',
75
77
  'openwop-Idempotent-Replay on replay MUST be "true"',
76
78
  )).toBe('true');
@@ -93,7 +95,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('idempotency-retry: 5-retry budget per scale-pr
93
95
  for (const res of responses) {
94
96
  expect(
95
97
  [200, 201].includes(res.status),
96
- driver.describe(
98
+ req('openwop.it.idempotencyRetry.5-retries-100ms-apart-with-same-key-all-return-the-same-runid',
97
99
  'scale-profiles.md §Retry semantics',
98
100
  'host MUST handle ≥5 retries 100ms apart without losing the cached response',
99
101
  ),
@@ -101,7 +103,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('idempotency-retry: 5-retry budget per scale-pr
101
103
  }
102
104
 
103
105
  const runIds = new Set(responses.map((r) => (r.json as { runId?: string })?.runId));
104
- expect(runIds.size, driver.describe(
106
+ expect(runIds.size, req('openwop.it.idempotencyRetry.5-retries-100ms-apart-with-same-key-all-return-the-same-runid',
105
107
  'idempotency.md §Layer 1',
106
108
  '5 retries with same key MUST collapse to exactly one runId',
107
109
  )).toBe(1);
@@ -114,18 +116,18 @@ describe('idempotency-retry: limits.idempotencyAckTimeoutSec contract per idempo
114
116
  expect(res.status).toBe(200);
115
117
 
116
118
  const limits = (res.json as { limits?: Record<string, unknown> })?.limits;
117
- if (!limits) return; // limits required per capabilities.md §3 — covered elsewhere
119
+ if (!limits) return softSkip('blocked', 'precondition not met — `!limits` returned early (limits required per capabilities.md §3 — covered elsewhere) (seam, prior step, or fixture unavailable)'); // limits required per capabilities.md §3 — covered elsewhere
118
120
  const ack = limits.idempotencyAckTimeoutSec;
119
121
  if (ack === undefined) {
120
122
  // Per idempotency.md, the field is optional; absence implies the
121
123
  // 5-second floor. Nothing to assert.
122
- return;
124
+ return softSkip('blocked', 'precondition not met — `ack === undefined` returned early (Per idempotency.md, the field is optional; absence implies the 5-second floor. Nothing to assert.) (seam, prior step, or fixture unavailable)');
123
125
  }
124
- expect(typeof ack === 'number' && Number.isInteger(ack), driver.describe(
126
+ expect(typeof ack === 'number' && Number.isInteger(ack), req('openwop.it.idempotencyRetry.host-advertising-idempotencyacktimeoutsec-sets-integer-5',
125
127
  'idempotency.md',
126
128
  'limits.idempotencyAckTimeoutSec MUST be an integer when advertised',
127
129
  )).toBe(true);
128
- expect(ack as number, driver.describe(
130
+ expect(ack as number, req('openwop.it.idempotencyRetry.host-advertising-idempotencyacktimeoutsec-sets-integer-5',
129
131
  'idempotency.md',
130
132
  'limits.idempotencyAckTimeoutSec MUST be ≥ 5',
131
133
  )).toBeGreaterThanOrEqual(5);
@@ -22,6 +22,7 @@ import { readFileSync } from 'node:fs';
22
22
  import { join } from 'node:path';
23
23
  import Ajv2020 from 'ajv/dist/2020.js';
24
24
  import { SCHEMAS_DIR } from '../lib/paths.js';
25
+ import { req } from '../lib/requirement-ids.js';
25
26
 
26
27
  interface SnapshotSchema {
27
28
  $schema: string;
@@ -37,7 +38,7 @@ describe('category: identity owner-triple shape (RFC 0048 §C)', () => {
37
38
  it('run-snapshot.schema.json defines an optional owner triple', () => {
38
39
  expect(
39
40
  snapshot.properties.owner,
40
- 'RFC 0048 §C: RunSnapshot MUST define an optional `owner` object',
41
+ req('openwop.it.identity-owner-shape.run-snapshot-schema-json-defines-an-optional-owner-triple', 'RFC 0048 §C', 'RFC 0048 §C: RunSnapshot MUST define an optional `owner` object'),
41
42
  ).toBeDefined();
42
43
  });
43
44
 
@@ -48,21 +49,21 @@ describe('category: identity owner-triple shape (RFC 0048 §C)', () => {
48
49
  const validate = ajv.compile(ownerSchema);
49
50
 
50
51
  it('positive: tenant-only owner validates', () => {
51
- expect(validate({ tenant: 'acme' }), JSON.stringify(validate.errors)).toBe(true);
52
+ expect(validate({ tenant: 'acme' }), req('openwop.it.identity-owner-shape.positive-tenant-only-owner-validates', 'RFC 0048 §C', JSON.stringify(validate.errors))).toBe(true);
52
53
  });
53
54
 
54
55
  it('positive: full triple validates', () => {
55
56
  expect(
56
57
  validate({ tenant: 'acme', workspace: 'ws-eng', principal: 'user_42' }),
57
- JSON.stringify(validate.errors),
58
+ req('openwop.it.identity-owner-shape.positive-full-triple-validates', 'RFC 0048 §C', JSON.stringify(validate.errors)),
58
59
  ).toBe(true);
59
60
  });
60
61
 
61
62
  it('negative: owner missing tenant is rejected (tenant is required)', () => {
62
- expect(validate({ workspace: 'ws-eng' })).toBe(false);
63
+ expect(validate({ workspace: 'ws-eng' }), req('openwop.it.identity-owner-shape.negative-owner-missing-tenant-is-rejected-tenant-is-required', 'RFC 0048 §C', 'negative: owner missing tenant is rejected (tenant is required)')).toBe(false);
63
64
  });
64
65
 
65
66
  it('negative: unknown owner property is rejected (additionalProperties:false)', () => {
66
- expect(validate({ tenant: 'acme', role: 'admin' })).toBe(false);
67
+ expect(validate({ tenant: 'acme', role: 'admin' }), req('openwop.it.identity-owner-shape.negative-unknown-owner-property-is-rejected-additionalproperties-false', 'RFC 0048 §C', 'negative: unknown owner property is rejected (additionalProperties:false)')).toBe(false);
67
68
  });
68
69
  });
@@ -13,6 +13,7 @@ import { describe, it, expect } from 'vitest';
13
13
  import { driver } from '../lib/driver.js';
14
14
  import { pollUntilTerminal } from '../lib/polling.js';
15
15
  import { isFixtureAdvertised } from '../lib/fixtures.js';
16
+ import { req } from '../lib/requirement-ids.js';
16
17
 
17
18
  const WORKFLOW_ID = 'conformance-identity';
18
19
  const SKIP_NO_FIXTURE = !isFixtureAdvertised(WORKFLOW_ID);
@@ -33,7 +34,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('identity: conformance-identity fixture echoes
33
34
  workflowId: WORKFLOW_ID,
34
35
  inputs: { payload },
35
36
  });
36
- expect(create.status, driver.describe(
37
+ expect(create.status, req('openwop.it.identity-passthrough.arbitrary-nested-json-payload-round-trips-through-inputs-variables',
37
38
  'rest-endpoints.md',
38
39
  'POST /v1/runs MUST return 201',
39
40
  )).toBe(201);
@@ -41,12 +42,12 @@ describe.skipIf(SKIP_NO_FIXTURE)('identity: conformance-identity fixture echoes
41
42
 
42
43
  const terminal = await pollUntilTerminal(runId);
43
44
 
44
- expect(terminal.status, driver.describe(
45
+ expect(terminal.status, req('openwop.it.identity-passthrough.arbitrary-nested-json-payload-round-trips-through-inputs-variables',
45
46
  'fixtures.md conformance-identity',
46
47
  'identity fixture MUST reach terminal `completed`',
47
48
  )).toBe('completed');
48
49
 
49
- expect(terminal.variables?.payload, driver.describe(
50
+ expect(terminal.variables?.payload, req('openwop.it.identity-passthrough.arbitrary-nested-json-payload-round-trips-through-inputs-variables',
50
51
  'fixtures.md conformance-identity §Expected behavior',
51
52
  'RunSnapshot.variables.payload MUST deep-equal the input payload',
52
53
  )).toEqual(payload);
@@ -11,6 +11,7 @@ import { describe, it, expect } from 'vitest';
11
11
  import { driver } from '../lib/driver.js';
12
12
  import { pollUntilStatus, pollUntilTerminal } from '../lib/polling.js';
13
13
  import { isFixtureAdvertised } from '../lib/fixtures.js';
14
+ import { req } from '../lib/requirement-ids.js';
14
15
 
15
16
  const WORKFLOW_ID = 'conformance-approval';
16
17
  const NODE_ID = 'gate';
@@ -23,7 +24,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('interrupt: approval accept resumes to `complet
23
24
  const runId = (create.json as { runId: string }).runId;
24
25
 
25
26
  const suspended = await pollUntilStatus(runId, 'waiting-approval', { timeoutMs: 10_000 });
26
- expect(suspended.currentNodeId, driver.describe(
27
+ expect(suspended.currentNodeId, req('openwop.it.interrupt-approval.run-suspends-at-gate-accept-resolution-drives-terminal-completed',
27
28
  'fixtures.md conformance-approval',
28
29
  'suspended run MUST report currentNodeId === "gate"',
29
30
  )).toBe(NODE_ID);
@@ -32,13 +33,13 @@ describe.skipIf(SKIP_NO_FIXTURE)('interrupt: approval accept resumes to `complet
32
33
  `/v1/runs/${encodeURIComponent(runId)}/interrupts/${encodeURIComponent(NODE_ID)}`,
33
34
  { resumeValue: { action: 'accept' } },
34
35
  );
35
- expect(resolve.status, driver.describe(
36
+ expect(resolve.status, req('openwop.it.interrupt-approval.run-suspends-at-gate-accept-resolution-drives-terminal-completed',
36
37
  'rest-endpoints.md POST /v1/runs/{runId}/interrupts/{nodeId}',
37
38
  'valid approval resolve MUST return 200',
38
39
  )).toBe(200);
39
40
 
40
41
  const terminal = await pollUntilTerminal(runId, { timeoutMs: 10_000 });
41
- expect(terminal.status, driver.describe(
42
+ expect(terminal.status, req('openwop.it.interrupt-approval.run-suspends-at-gate-accept-resolution-drives-terminal-completed',
42
43
  'fixtures.md conformance-approval §Terminal status',
43
44
  'fixture after accept MUST reach terminal `completed`',
44
45
  )).toBe('completed');
@@ -59,7 +60,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('interrupt: invalid resolve payload rejected pe
59
60
  );
60
61
  expect(
61
62
  [400, 422].includes(resolve.status),
62
- driver.describe(
63
+ req('openwop.it.interrupt-approval.400-or-422-when-action-is-not-in-accept-reject',
63
64
  'interrupt.md + resumeSchema validation',
64
65
  'resolve payload that violates resumeSchema MUST return 400 or 422',
65
66
  ),
@@ -85,7 +86,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('interrupt: resolving an unknown interrupt retu
85
86
  `/v1/runs/${encodeURIComponent(runId)}/interrupts/no-such-node`,
86
87
  { resumeValue: { action: 'accept' } },
87
88
  );
88
- expect(resolve.status, driver.describe(
89
+ expect(resolve.status, req('openwop.it.interrupt-approval.400-404-when-nodeid-does-not-match-an-active-interrupt',
89
90
  'rest-endpoints.md POST /v1/runs/{runId}/interrupts/{nodeId}',
90
91
  'resolving an unknown nodeId MUST return 404',
91
92
  )).toBe(404);
@@ -35,8 +35,8 @@ import addFormats from 'ajv-formats';
35
35
  import { SCHEMAS_DIR } from '../lib/paths.js';
36
36
  import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
37
37
  import { behaviorGate } from '../lib/behavior-gate.js';
38
-
39
- const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
38
+ import { softSkip } from '../lib/soft-skip.js';
39
+ import { req } from '../lib/requirement-ids.js';
40
40
  function loadSchema(name: string): Record<string, unknown> {
41
41
  return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
42
42
  }
@@ -81,12 +81,12 @@ describe('interrupt-approver-routing: capability advertisement shape (capabiliti
81
81
  const interrupt = (caps.properties as Record<string, { properties?: Record<string, { required?: string[]; properties?: Record<string, unknown> }> }>).interrupt;
82
82
 
83
83
  it('capabilities schema declares interrupt.approverRouting', () => {
84
- expect(interrupt, why('capabilities.schema.json §interrupt', 'the interrupt block MUST be declared')).toBeDefined();
84
+ expect(interrupt, req('openwop.it.interrupt-approver-routing.capabilities-schema-declares-interrupt-approverrouting', 'capabilities.schema.json §interrupt', 'the interrupt block MUST be declared')).toBeDefined();
85
85
  const ar = interrupt?.properties?.approverRouting;
86
- expect(ar, why('RFC 0104', 'interrupt.approverRouting MUST be declared')).toBeDefined();
87
- expect(ar?.required, why('RFC 0104', 'approverRouting.supported MUST be required')).toEqual(expect.arrayContaining(['supported']));
86
+ expect(ar, req('openwop.it.interrupt-approver-routing.capabilities-schema-declares-interrupt-approverrouting', 'RFC 0104', 'interrupt.approverRouting MUST be declared')).toBeDefined();
87
+ expect(ar?.required, req('openwop.it.interrupt-approver-routing.capabilities-schema-declares-interrupt-approverrouting', 'RFC 0104', 'approverRouting.supported MUST be required')).toEqual(expect.arrayContaining(['supported']));
88
88
  for (const f of ['supported', 'refKinds', 'audience']) {
89
- expect(ar?.properties?.[f], why('RFC 0104', `approverRouting.${f} MUST be declared`)).toBeDefined();
89
+ expect(ar?.properties?.[f], req('openwop.it.interrupt-approver-routing.capabilities-schema-declares-interrupt-approverrouting', 'RFC 0104', `approverRouting.${f} MUST be declared`)).toBeDefined();
90
90
  }
91
91
  });
92
92
  });
@@ -104,7 +104,7 @@ describe('interrupt-approver-routing: ApprovalData additive optionality (interru
104
104
  };
105
105
 
106
106
  it('an approval payload WITHOUT the routing fields still validates (additive, optional)', () => {
107
- expect(validate(baseApproval), why('RFC 0104 Compatibility', `the fields are optional — Errors: ${JSON.stringify(validate.errors)}`)).toBe(true);
107
+ expect(validate(baseApproval), req('openwop.it.interrupt-approver-routing.an-approval-payload-without-the-routing-fields-still-validates-additive-optional', 'RFC 0104 Compatibility', `the fields are optional — Errors: ${JSON.stringify(validate.errors)}`)).toBe(true);
108
108
  });
109
109
  it('an approval payload WITH group/role refs + audience validates', () => {
110
110
  const withRouting = {
@@ -116,11 +116,11 @@ describe('interrupt-approver-routing: ApprovalData additive optionality (interru
116
116
  audience: { groups: ['grp:finance-approvers'], roles: ['role:controller'], subjects: ['user:cfo'] },
117
117
  },
118
118
  };
119
- expect(validate(withRouting), why('RFC 0104 §Proposal', `routing fields MUST validate — Errors: ${JSON.stringify(validate.errors)}`)).toBe(true);
119
+ expect(validate(withRouting), req('openwop.it.interrupt-approver-routing.an-approval-payload-with-group-role-refs-audience-validates', 'RFC 0104 §Proposal', `routing fields MUST validate — Errors: ${JSON.stringify(validate.errors)}`)).toBe(true);
120
120
  });
121
121
  it('an audience with an unknown key is rejected (audience object is closed)', () => {
122
122
  const badAudience = { ...baseApproval, data: { ...baseApproval.data, audience: { teams: ['grp:x'] } } };
123
- expect(validate(badAudience), why('RFC 0104', 'audience MUST be additionalProperties:false')).toBe(false);
123
+ expect(validate(badAudience), req('openwop.it.interrupt-approver-routing.an-audience-with-an-unknown-key-is-rejected-audience-object-is-closed', 'RFC 0104', 'audience MUST be additionalProperties:false')).toBe(false);
124
124
  });
125
125
  });
126
126
 
@@ -128,17 +128,17 @@ describe('interrupt-approver-routing: audience default/override rule (interrupt.
128
128
  it('omitted audience ⇒ notify the resolved eligibility union', () => {
129
129
  expect(
130
130
  notifyTargets({ approversList: ['user:a'], approverGroupRefs: ['grp:fin'], approverRoleRefs: ['role:ctrl'] }),
131
- why('RFC 0104', 'omitted audience MUST default to the eligibility union'),
131
+ req('openwop.it.interrupt-approver-routing.omitted-audience-notify-the-resolved-eligibility-union', 'RFC 0104', 'omitted audience MUST default to the eligibility union'),
132
132
  ).toEqual(['user:a', 'grp:fin', 'role:ctrl']);
133
133
  });
134
134
  it('present audience ⇒ overrides the eligibility union', () => {
135
135
  expect(
136
136
  notifyTargets({ approverGroupRefs: ['grp:fin'], audience: { subjects: ['user:cfo'], groups: ['grp:audit'] } }),
137
- why('RFC 0104', 'present audience MUST override the default'),
137
+ req('openwop.it.interrupt-approver-routing.present-audience-overrides-the-eligibility-union', 'RFC 0104', 'present audience MUST override the default'),
138
138
  ).toEqual(['user:cfo', 'grp:audit']);
139
139
  });
140
140
  it('no eligibility refs and no audience ⇒ empty target set', () => {
141
- expect(notifyTargets({}), why('RFC 0104', 'nothing to route when no refs')).toEqual([]);
141
+ expect(notifyTargets({}), req('openwop.it.interrupt-approver-routing.no-eligibility-refs-and-no-audience-empty-target-set', 'RFC 0104', 'nothing to route when no refs')).toEqual([]);
142
142
  });
143
143
  });
144
144
 
@@ -148,7 +148,7 @@ describe('interrupt-approver-routing: audience default/override rule (interrupt.
148
148
 
149
149
  describe('interrupt-approver-routing: advertised shape is honest (capability-gated)', () => {
150
150
  it('an advertising host advertises a coherent approverRouting block', async () => {
151
- if (!process.env.OPENWOP_BASE_URL) return; // no live host → nothing to read
151
+ if (!process.env.OPENWOP_BASE_URL) return softSkip('blocked', 'precondition not met — `!process.env.OPENWOP_BASE_URL` returned early (no live host → nothing to read) (seam, prior step, or fixture unavailable)'); // no live host → nothing to read
152
152
  const interrupt = await readCapabilityFamily<InterruptCap>('interrupt');
153
153
  const ar = interrupt?.approverRouting;
154
154
  // Soft-skip: host does not advertise the capability (fields ignored; still conformant).
@@ -157,10 +157,10 @@ describe('interrupt-approver-routing: advertised shape is honest (capability-gat
157
157
  // Opt-in established (supported === true): the advertised shape MUST be honest.
158
158
  const allowed = new Set(['group', 'role']);
159
159
  for (const k of ar?.refKinds ?? []) {
160
- expect(allowed.has(k), why('RFC 0104 capabilities.md', `refKinds MUST be a subset of {group, role} — saw ${k}`)).toBe(true);
160
+ expect(allowed.has(k), req('openwop.it.interrupt-approver-routing.an-advertising-host-advertises-a-coherent-approverrouting-block', 'RFC 0104 capabilities.md', `refKinds MUST be a subset of {group, role} — saw ${k}`)).toBe(true);
161
161
  }
162
162
  if (ar?.audience !== undefined) {
163
- expect(typeof ar.audience, why('RFC 0104 capabilities.md', 'approverRouting.audience MUST be boolean')).toBe('boolean');
163
+ expect(typeof ar.audience, req('openwop.it.interrupt-approver-routing.an-advertising-host-advertises-a-coherent-approverrouting-block', 'RFC 0104 capabilities.md', 'approverRouting.audience MUST be boolean')).toBe('boolean');
164
164
  }
165
165
  });
166
166
  });