@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
@@ -20,6 +20,8 @@ import { describe, it, expect } from 'vitest';
20
20
  import { driver } from '../lib/driver.js';
21
21
  import { pollUntilTerminal } from '../lib/polling.js';
22
22
  import { isFixtureAdvertised } from '../lib/fixtures.js';
23
+ import { req } from '../lib/requirement-ids.js';
24
+ import { softSkip } from '../lib/soft-skip.js';
23
25
 
24
26
  const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
25
27
  const FIXTURE = 'conformance-envelope-truncated';
@@ -56,20 +58,20 @@ async function lastBudget(): Promise<number | null> {
56
58
 
57
59
  describe.skipIf(HTTP_SKIP)('envelope-truncated: runtime behavior (RFC 0032 §B.4 + RFC 0033 §B)', () => {
58
60
  it('when mock returns stopReason: max_tokens on attempt 1, exactly one envelope.truncated event fires with stopReason: max_tokens', async () => {
59
- if (!isFixtureAdvertised(FIXTURE)) return;
61
+ if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
60
62
  const seed = await programMock([
61
63
  { stopReason: 'max_tokens', content: '{"valid":' },
62
64
  { stopReason: 'end_turn', content: '{"valid":true}' },
63
65
  ]);
64
- if (seed.status === 404) return;
66
+ if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
65
67
  expect(seed.status).toBe(200);
66
68
 
67
69
  const result = await startRunAndRead();
68
- if (result === null) return;
70
+ if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
69
71
  const truncated = result.events.filter((e) => e.type === 'envelope.truncated');
70
72
  expect(
71
73
  truncated.length,
72
- driver.describe(
74
+ req('openwop.it.envelope-truncated.when-mock-returns-stopreason-max-tokens-on-attempt-1-exactly-one-envelope-trunca',
73
75
  'RFCS/0032-envelope-reliability-events.md §B.4',
74
76
  'exactly one envelope.truncated event MUST fire when the provider returns finishReason corresponding to truncation',
75
77
  ),
@@ -78,17 +80,17 @@ describe.skipIf(HTTP_SKIP)('envelope-truncated: runtime behavior (RFC 0032 §B.4
78
80
  });
79
81
 
80
82
  it('payload includes nodeId + provider + model + partialPayloadAvailable boolean', async () => {
81
- if (!isFixtureAdvertised(FIXTURE)) return;
83
+ if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
82
84
  const seed = await programMock([
83
85
  { stopReason: 'max_tokens', content: '{"partial' },
84
86
  { stopReason: 'end_turn', content: '{"valid":true}' },
85
87
  ]);
86
- if (seed.status === 404) return;
88
+ if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
87
89
 
88
90
  const result = await startRunAndRead();
89
- if (result === null) return;
91
+ if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
90
92
  const truncated = result.events.find((e) => e.type === 'envelope.truncated');
91
- expect(truncated).toBeDefined();
93
+ expect(truncated, req('openwop.it.envelope-truncated.payload-includes-nodeid-provider-model-partialpayloadavailable-boolean', 'RFC 0032 §B.4', 'payload includes nodeId + provider + model + partialPayloadAvailable boolean')).toBeDefined();
92
94
  const payload = truncated!.payload ?? {};
93
95
  expect(payload.nodeId).toBe(NODE_ID);
94
96
  expect(payload.provider).toBe('mock');
@@ -97,24 +99,24 @@ describe.skipIf(HTTP_SKIP)('envelope-truncated: runtime behavior (RFC 0032 §B.4
97
99
  });
98
100
 
99
101
  it('retry attempt receives a maxTokens value strictly greater than the previous attempt (RFC 0033 §B truncationBudgetMultiplier)', async () => {
100
- if (!isFixtureAdvertised(FIXTURE)) return;
102
+ if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
101
103
  const seed = await programMock([
102
104
  { stopReason: 'max_tokens', content: '{"partial' },
103
105
  { stopReason: 'end_turn', content: '{"valid":true}' },
104
106
  ]);
105
- if (seed.status === 404) return;
107
+ if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
106
108
 
107
109
  const result = await startRunAndRead();
108
- if (result === null) return;
110
+ if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
109
111
  // After the run, the mock's most-recent budget is the SECOND (retry)
110
112
  // attempt's maxTokens. Per RFC 0033 §B, this MUST exceed the fixture's
111
113
  // initial maxTokens (50). The host's default multiplier is 2 — so the
112
114
  // retry should see 100.
113
115
  const budget = await lastBudget();
114
- if (budget === null) return; // host doesn't expose the seam
116
+ if (budget === null) return softSkip('blocked', 'precondition not met — `budget === null` returned early (host doesn\'t expose the seam) (seam, prior step, or fixture unavailable)'); // host doesn't expose the seam
115
117
  expect(
116
118
  budget,
117
- driver.describe(
119
+ req('openwop.it.envelope-truncated.retry-attempt-receives-a-maxtokens-value-strictly-greater-than-the-previous-atte',
118
120
  'RFCS/0033-envelope-completion-contract.md §B',
119
121
  'truncation retry MUST issue with a strictly-increased maxTokens budget (host multiplies by capabilities.envelopes.reliability.completion.truncationBudgetMultiplier)',
120
122
  ),
@@ -122,15 +124,15 @@ describe.skipIf(HTTP_SKIP)('envelope-truncated: runtime behavior (RFC 0032 §B.4
122
124
  });
123
125
 
124
126
  it('run terminates `completed` after the second attempt succeeds', async () => {
125
- if (!isFixtureAdvertised(FIXTURE)) return;
127
+ if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
126
128
  const seed = await programMock([
127
129
  { stopReason: 'max_tokens', content: '{"partial' },
128
130
  { stopReason: 'end_turn', content: '{"valid":true}' },
129
131
  ]);
130
- if (seed.status === 404) return;
132
+ if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
131
133
 
132
134
  const result = await startRunAndRead();
133
- if (result === null) return;
134
- expect((result.terminal as { status?: string }).status).toBe('completed');
135
+ if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
136
+ expect((result.terminal as { status?: string }).status, req('openwop.it.envelope-truncated.run-terminates-completed-after-the-second-attempt-succeeds', 'RFC 0032 §B.4', 'run terminates `completed` after the second attempt succeeds')).toBe('completed');
135
137
  });
136
138
  });
@@ -19,6 +19,8 @@ import { describe, it, expect } from 'vitest';
19
19
  import { driver } from '../lib/driver.js';
20
20
  import { pollUntilTerminal } from '../lib/polling.js';
21
21
  import { isFixtureAdvertised } from '../lib/fixtures.js';
22
+ import { req } from '../lib/requirement-ids.js';
23
+ import { softSkip } from '../lib/soft-skip.js';
22
24
 
23
25
  const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
24
26
  const FIXTURE = 'conformance-envelope-truncation-cap-exhaustion';
@@ -57,24 +59,24 @@ const PERPETUAL_TRUNCATION = Array.from({ length: 16 }, () => ({
57
59
 
58
60
  describe.skipIf(HTTP_SKIP)('envelope-truncation-cap-exhaustion: DoS-bound retry budget (RFC 0033 §B + §F)', () => {
59
61
  it('perpetual truncation → emits exactly one envelope.retry.exhausted with finalReason: "truncation"', async () => {
60
- if (!isFixtureAdvertised(FIXTURE)) return;
62
+ if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
61
63
  const seed = await programMock(PERPETUAL_TRUNCATION);
62
- if (seed.status === 404) return;
64
+ if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
63
65
  expect(seed.status).toBe(200);
64
66
 
65
67
  const result = await startRunAndRead();
66
- if (result === null) return;
68
+ if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
67
69
  const exhausted = result.events.filter((e) => e.type === 'envelope.retry.exhausted');
68
70
  expect(
69
71
  exhausted.length,
70
- driver.describe(
72
+ req('openwop.it.envelope-truncation-cap-exhaustion.perpetual-truncation-emits-exactly-one-envelope-retry-exhausted-with-finalreason',
71
73
  'RFCS/0032-envelope-reliability-events.md §B.2',
72
74
  'exactly one envelope.retry.exhausted event MUST fire when the truncation-retry budget is exhausted',
73
75
  ),
74
76
  ).toBe(1);
75
77
  expect(
76
78
  exhausted[0]!.payload?.finalReason,
77
- driver.describe(
79
+ req('openwop.it.envelope-truncation-cap-exhaustion.perpetual-truncation-emits-exactly-one-envelope-retry-exhausted-with-finalreason',
78
80
  'RFCS/0033-envelope-completion-contract.md §B',
79
81
  'finalReason MUST be "truncation" when the host exhausts truncation retries (distinguished from schema-violation per RFC 0033 §A)',
80
82
  ),
@@ -82,16 +84,16 @@ describe.skipIf(HTTP_SKIP)('envelope-truncation-cap-exhaustion: DoS-bound retry
82
84
  });
83
85
 
84
86
  it('node fails with RunSnapshot.error.code: "envelope_truncation_unrecoverable" per RFC 0033 §F', async () => {
85
- if (!isFixtureAdvertised(FIXTURE)) return;
87
+ if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
86
88
  const seed = await programMock(PERPETUAL_TRUNCATION);
87
- if (seed.status === 404) return;
89
+ if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
88
90
 
89
91
  const result = await startRunAndRead();
90
- if (result === null) return;
92
+ if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
91
93
  const code = (result.terminal as { error?: { code?: string } }).error?.code;
92
94
  expect(
93
95
  code,
94
- driver.describe(
96
+ req('openwop.it.envelope-truncation-cap-exhaustion.node-fails-with-runsnapshot-error-code-envelope-truncation-unrecoverable-per-rfc',
95
97
  'RFCS/0033-envelope-completion-contract.md §F',
96
98
  'truncation-retry-exhaustion MUST surface as RunSnapshot.error.code = envelope_truncation_unrecoverable (distinct from envelope_invalid which surfaces schema-violation-exhaustion)',
97
99
  ),
@@ -99,18 +101,18 @@ describe.skipIf(HTTP_SKIP)('envelope-truncation-cap-exhaustion: DoS-bound retry
99
101
  });
100
102
 
101
103
  it('total LLM calls bounded by maxRetryAttempts (DoS-bound — no infinite loop)', async () => {
102
- if (!isFixtureAdvertised(FIXTURE)) return;
104
+ if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
103
105
  const seed = await programMock(PERPETUAL_TRUNCATION);
104
- if (seed.status === 404) return;
106
+ if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
105
107
 
106
108
  const result = await startRunAndRead();
107
- if (result === null) return;
109
+ if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
108
110
  // Count envelope.truncated events as a proxy for LLM-call count
109
111
  // (each truncated attempt emits one).
110
112
  const truncatedCount = result.events.filter((e) => e.type === 'envelope.truncated').length;
111
113
  expect(
112
114
  truncatedCount,
113
- driver.describe(
115
+ req('openwop.it.envelope-truncation-cap-exhaustion.total-llm-calls-bounded-by-maxretryattempts-dos-bound-no-infinite-loop',
114
116
  'RFCS/0033-envelope-completion-contract.md §B',
115
117
  'truncation-retry count MUST be bounded — host cannot loop indefinitely doubling budget; expected upper-bound matches advertised maxRetryAttempts',
116
118
  ),
@@ -123,19 +125,19 @@ describe.skipIf(HTTP_SKIP)('envelope-truncation-cap-exhaustion: DoS-bound retry
123
125
  });
124
126
 
125
127
  it('envelope.retry.exhausted is emitted BEFORE node.failed (cause precedes effect)', async () => {
126
- if (!isFixtureAdvertised(FIXTURE)) return;
128
+ if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
127
129
  const seed = await programMock(PERPETUAL_TRUNCATION);
128
- if (seed.status === 404) return;
130
+ if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
129
131
 
130
132
  const result = await startRunAndRead();
131
- if (result === null) return;
133
+ if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
132
134
  const exhaustedIdx = result.events.findIndex((e) => e.type === 'envelope.retry.exhausted');
133
135
  const failedIdx = result.events.findIndex((e) => e.type === 'node.failed');
134
136
  expect(exhaustedIdx).toBeGreaterThanOrEqual(0);
135
137
  expect(failedIdx).toBeGreaterThanOrEqual(0);
136
138
  expect(
137
139
  exhaustedIdx < failedIdx,
138
- driver.describe(
140
+ req('openwop.it.envelope-truncation-cap-exhaustion.envelope-retry-exhausted-is-emitted-before-node-failed-cause-precedes-effect',
139
141
  'RFCS/0032-envelope-reliability-events.md §B.2',
140
142
  'envelope.retry.exhausted MUST be emitted BEFORE node.failed',
141
143
  ),
@@ -21,6 +21,7 @@ import { describe, it, expect } from 'vitest';
21
21
  import { readFileSync, existsSync, readdirSync } from 'node:fs';
22
22
  import { join } from 'node:path';
23
23
  import { SCHEMAS_DIR } from '../lib/paths.js';
24
+ import { req } from '../lib/requirement-ids.js';
24
25
 
25
26
  interface DiscriminatorViolation {
26
27
  path: string;
@@ -124,7 +125,7 @@ describe('envelope-variant-discriminator-static (RFC 0031 §A)', () => {
124
125
  it('local envelope-schema directory is discoverable', () => {
125
126
  expect(
126
127
  schemas.length,
127
- 'schemas/envelopes/*.schema.json MUST contain at least the four universal-kind schemas',
128
+ req('openwop.it.envelope-variant-discriminator-static.local-envelope-schema-directory-is-discoverable', 'RFC 0031 §A', 'schemas/envelopes/*.schema.json MUST contain at least the four universal-kind schemas'),
128
129
  ).toBeGreaterThanOrEqual(4);
129
130
  });
130
131
 
@@ -135,7 +136,7 @@ describe('envelope-variant-discriminator-static (RFC 0031 §A)', () => {
135
136
  walkForOneOf(schema, '#', violations);
136
137
  expect(
137
138
  violations,
138
- `${kind}.schema.json contains \`oneOf\` — REFORMULATE as \`anyOf\` + single-string-enum discriminator per RFC 0031 §A. Violations: ${JSON.stringify(violations, null, 2)}`,
139
+ req('openwop.it.envelope-variant-discriminator-static.schema-json-must-not-contain-oneof-at-any-nesting-depth-gemini-silently-drops-it', 'RFC 0031 §A', `${kind}.schema.json contains \`oneOf\` — REFORMULATE as \`anyOf\` + single-string-enum discriminator per RFC 0031 §A. Violations: ${JSON.stringify(violations, null, 2)}`),
139
140
  ).toEqual([]);
140
141
  });
141
142
 
@@ -145,7 +146,7 @@ describe('envelope-variant-discriminator-static (RFC 0031 §A)', () => {
145
146
  walkForAnyOfDiscriminators(schema, '#', violations);
146
147
  expect(
147
148
  violations,
148
- `${kind}.schema.json \`anyOf\` discriminator violations: ${JSON.stringify(violations, null, 2)}`,
149
+ req('openwop.it.envelope-variant-discriminator-static.schema-json-every-anyof-branch-declares-a-single-string-enum-discriminator-in-re', 'RFC 0031 §A', `${kind}.schema.json \`anyOf\` discriminator violations: ${JSON.stringify(violations, null, 2)}`),
149
150
  ).toEqual([]);
150
151
  });
151
152
  }
@@ -8,6 +8,7 @@
8
8
 
9
9
  import { describe, it, expect } from 'vitest';
10
10
  import { driver } from '../lib/driver.js';
11
+ import { req } from '../lib/requirement-ids.js';
11
12
 
12
13
  interface ErrorEnvelope {
13
14
  error: unknown;
@@ -16,28 +17,28 @@ interface ErrorEnvelope {
16
17
  [key: string]: unknown;
17
18
  }
18
19
 
19
- function assertErrorEnvelope(body: unknown, specSection: string): void {
20
- expect(typeof body, driver.describe(specSection, 'error response MUST be a JSON object')).toBe(
20
+ function assertErrorEnvelope(requirementId: string, body: unknown, specSection: string): void {
21
+ expect(typeof body, req(requirementId, specSection, 'error response MUST be a JSON object')).toBe(
21
22
  'object',
22
23
  );
23
24
  const env = body as ErrorEnvelope;
24
25
 
25
- expect(typeof env.error, driver.describe(
26
+ expect(typeof env.error, req(requirementId,
26
27
  specSection,
27
28
  'error envelope MUST include `error` (machine-readable string)',
28
29
  )).toBe('string');
29
30
 
30
- expect(typeof env.message, driver.describe(
31
+ expect(typeof env.message, req(requirementId,
31
32
  specSection,
32
33
  'error envelope MUST include `message` (human-readable string)',
33
34
  )).toBe('string');
34
35
 
35
36
  if (env.details !== undefined) {
36
- expect(typeof env.details, driver.describe(
37
+ expect(typeof env.details, req(requirementId,
37
38
  specSection,
38
39
  'error envelope `details` (when present) MUST be a JSON object',
39
40
  )).toBe('object');
40
- expect(env.details, driver.describe(
41
+ expect(env.details, req(requirementId,
41
42
  specSection,
42
43
  'error envelope `details` (when present) MUST NOT be null',
43
44
  )).not.toBeNull();
@@ -51,7 +52,7 @@ function assertErrorEnvelope(body: unknown, specSection: string): void {
51
52
  const extraneousKeys = Object.keys(env as Record<string, unknown>).filter(
52
53
  (k) => !allowedKeys.has(k),
53
54
  );
54
- expect(extraneousKeys, driver.describe(
55
+ expect(extraneousKeys, req(requirementId,
55
56
  'schemas/error-envelope.schema.json (additionalProperties:false)',
56
57
  `error envelope MUST NOT have keys outside {error, message, details}; extraneous: [${extraneousKeys.join(', ')}]`,
57
58
  )).toEqual([]);
@@ -64,7 +65,7 @@ function assertErrorEnvelope(body: unknown, specSection: string): void {
64
65
  * RECOMMENDED, not REQUIRED — hosts that don't emit trace IDs are still
65
66
  * conformant. This helper just pins the placement.
66
67
  */
67
- function assertCorrelationIdShape(body: unknown, specSection: string): void {
68
+ function assertCorrelationIdShape(requirementId: string, body: unknown, specSection: string): void {
68
69
  const env = body as ErrorEnvelope;
69
70
  // Top-level correlationId would be a spec violation; the assertErrorEnvelope
70
71
  // additionalProperties check above catches it. This helper additionally
@@ -72,11 +73,11 @@ function assertCorrelationIdShape(body: unknown, specSection: string): void {
72
73
  if (env.details && typeof env.details === 'object') {
73
74
  const det = env.details as Record<string, unknown>;
74
75
  if (det.correlationId !== undefined) {
75
- expect(typeof det.correlationId, driver.describe(
76
+ expect(typeof det.correlationId, req(requirementId,
76
77
  specSection,
77
78
  'when present, details.correlationId MUST be a string',
78
79
  )).toBe('string');
79
- expect((det.correlationId as string).length, driver.describe(
80
+ expect((det.correlationId as string).length, req(requirementId,
80
81
  specSection,
81
82
  'details.correlationId MUST be a non-empty string',
82
83
  )).toBeGreaterThan(0);
@@ -90,11 +91,11 @@ describe('errors: 404 envelope', () => {
90
91
 
91
92
  expect(
92
93
  [403, 404].includes(res.status),
93
- driver.describe('rest-endpoints.md', 'unknown run MUST return 404 (or 403 if leaking existence is forbidden)'),
94
+ req('openwop.it.errors.get-v1-runs-nonexistentid-returns-canonical-envelope', 'rest-endpoints.md', 'unknown run MUST return 404 (or 403 if leaking existence is forbidden)'),
94
95
  ).toBe(true);
95
96
 
96
- assertErrorEnvelope(res.json, 'rest-endpoints.md error envelope');
97
- assertCorrelationIdShape(res.json, 'rest-endpoints.md §error-envelope');
97
+ assertErrorEnvelope('openwop.it.errors.get-v1-runs-nonexistentid-returns-canonical-envelope', res.json, 'rest-endpoints.md error envelope');
98
+ assertCorrelationIdShape('openwop.it.errors.get-v1-runs-nonexistentid-returns-canonical-envelope', res.json, 'rest-endpoints.md §error-envelope');
98
99
  });
99
100
  });
100
101
 
@@ -107,12 +108,12 @@ describe('errors: 400 envelope (validation)', () => {
107
108
  // not the status code.
108
109
  expect(
109
110
  res.status,
110
- driver.describe('rest-endpoints.md', 'malformed POST /v1/runs MUST return 4xx'),
111
+ req('openwop.it.errors.post-v1-runs-with-empty-body-returns-canonical-envelope', 'rest-endpoints.md', 'malformed POST /v1/runs MUST return 4xx'),
111
112
  ).toBeGreaterThanOrEqual(400);
112
113
  expect(res.status).toBeLessThan(500);
113
114
 
114
- assertErrorEnvelope(res.json, 'rest-endpoints.md error envelope');
115
- assertCorrelationIdShape(res.json, 'rest-endpoints.md §error-envelope');
115
+ assertErrorEnvelope('openwop.it.errors.post-v1-runs-with-empty-body-returns-canonical-envelope', res.json, 'rest-endpoints.md error envelope');
116
+ assertCorrelationIdShape('openwop.it.errors.post-v1-runs-with-empty-body-returns-canonical-envelope', res.json, 'rest-endpoints.md §error-envelope');
116
117
  });
117
118
  });
118
119
 
@@ -131,12 +132,12 @@ describe('errors: details.correlationId placement convention', () => {
131
132
  const res = await driver.get('/v1/runs/openwop-conformance-correlation-probe-' + Date.now());
132
133
  expect(
133
134
  [400, 403, 404].includes(res.status),
134
- driver.describe('rest-endpoints.md', 'unknown run returns 4xx envelope'),
135
+ req('openwop.it.errors.correlationid-when-emitted-on-5xx-must-be-in-details-never-top-level', 'rest-endpoints.md', 'unknown run returns 4xx envelope'),
135
136
  ).toBe(true);
136
137
  const body = res.json as Record<string, unknown>;
137
- expect(body, driver.describe('rest-endpoints.md', 'response MUST be a JSON object')).toBeDefined();
138
+ expect(body, req('openwop.it.errors.correlationid-when-emitted-on-5xx-must-be-in-details-never-top-level', 'rest-endpoints.md', 'response MUST be a JSON object')).toBeDefined();
138
139
  // The structural constraint: no top-level correlationId.
139
- expect(body.correlationId, driver.describe(
140
+ expect(body.correlationId, req('openwop.it.errors.correlationid-when-emitted-on-5xx-must-be-in-details-never-top-level',
140
141
  'spec/v1/rest-endpoints.md §error-envelope (correlationId convention)',
141
142
  'correlationId MUST NOT appear at the top level of the error envelope; canonical home is `details.correlationId`',
142
143
  )).toBeUndefined();
@@ -19,6 +19,8 @@ import { describe, it, expect } from 'vitest';
19
19
  import { driver } from '../lib/driver.js';
20
20
  import { pollUntilTerminal } from '../lib/polling.js';
21
21
  import { isFixtureAdvertised } from '../lib/fixtures.js';
22
+ import { req } from '../lib/requirement-ids.js';
23
+ import { softSkip } from '../lib/soft-skip.js';
22
24
 
23
25
  const NOOP_WORKFLOW_ID = 'conformance-noop';
24
26
  const SKIP_NO_NOOP = !isFixtureAdvertised(NOOP_WORKFLOW_ID);
@@ -48,18 +50,18 @@ describe.skipIf(SKIP_NO_NOOP)('event-ordering: polling returns events in monoton
48
50
  await pollUntilTerminal(runId);
49
51
 
50
52
  const res = await driver.get(`/v1/runs/${encodeURIComponent(runId)}/events/poll`);
51
- if (res.status !== 200) return;
53
+ if (res.status !== 200) return softSkip('blocked', 'precondition not met — `res.status !== 200` returned early (seam, prior step, or fixture unavailable)');
52
54
 
53
55
  const body = res.json as { events?: RawEvent[] } | undefined;
54
- if (!body?.events) return;
55
- if (body.events.length < 2) return; // single-event runs have no ordering to verify
56
+ if (!body?.events) return softSkip('blocked', 'precondition not met — `!body?.events` returned early (seam, prior step, or fixture unavailable)');
57
+ if (body.events.length < 2) return softSkip('blocked', 'precondition not met — `body.events.length < 2` returned early (single-event runs have no ordering to verify) (seam, prior step, or fixture unavailable)'); // single-event runs have no ordering to verify
56
58
 
57
59
  const seqs = body.events.map(getSeq);
58
60
  for (let i = 1; i < seqs.length; i++) {
59
61
  const curr = seqs[i];
60
62
  const prev = seqs[i - 1];
61
63
  if (curr === null || prev === null) continue; // host without seq fields
62
- expect(curr, driver.describe(
64
+ expect(curr, req('openwop.it.eventOrdering.events-from-a-single-poll-have-non-decreasing-sequence-numbers',
63
65
  'observability.md §"Event ordering"',
64
66
  `event[${i}].sequence (${curr}) MUST be >= event[${i - 1}].sequence (${prev})`,
65
67
  )).toBeGreaterThanOrEqual(prev);
@@ -74,13 +76,13 @@ describe.skipIf(SKIP_NO_NOOP)('event-ordering: polling returns events in monoton
74
76
 
75
77
  const a = await driver.get(`/v1/runs/${encodeURIComponent(runId)}/events/poll`);
76
78
  const b = await driver.get(`/v1/runs/${encodeURIComponent(runId)}/events/poll`);
77
- if (a.status !== 200 || b.status !== 200) return;
79
+ if (a.status !== 200 || b.status !== 200) return softSkip('blocked', 'precondition not met — `a.status !== 200 || b.status !== 200` returned early (seam, prior step, or fixture unavailable)');
78
80
 
79
81
  const aBody = a.json as { events?: RawEvent[] } | undefined;
80
82
  const bBody = b.json as { events?: RawEvent[] } | undefined;
81
- if (!aBody?.events || !bBody?.events) return;
83
+ if (!aBody?.events || !bBody?.events) return softSkip('blocked', 'precondition not met — `!aBody?.events || !bBody?.events` returned early (seam, prior step, or fixture unavailable)');
82
84
 
83
- expect(aBody.events.length, driver.describe(
85
+ expect(aBody.events.length, req('openwop.it.eventOrdering.repeated-polls-of-a-terminal-run-yield-identical-event-sequences',
84
86
  'observability.md',
85
87
  'repeated polls of terminal run MUST return same number of events',
86
88
  )).toBe(bBody.events.length);
@@ -89,7 +91,7 @@ describe.skipIf(SKIP_NO_NOOP)('event-ordering: polling returns events in monoton
89
91
  const aSeq = getSeq(aBody.events[i]!);
90
92
  const bSeq = getSeq(bBody.events[i]!);
91
93
  if (aSeq === null || bSeq === null) continue;
92
- expect(aSeq, driver.describe(
94
+ expect(aSeq, req('openwop.it.eventOrdering.repeated-polls-of-a-terminal-run-yield-identical-event-sequences',
93
95
  'observability.md',
94
96
  `event[${i}] sequence MUST be stable across repeated polls`,
95
97
  )).toBe(bSeq);
@@ -105,15 +107,15 @@ describe.skipIf(SKIP_NO_NOOP)('event-ordering: terminal run has at most one term
105
107
  await pollUntilTerminal(runId);
106
108
 
107
109
  const res = await driver.get(`/v1/runs/${encodeURIComponent(runId)}/events/poll`);
108
- if (res.status !== 200) return;
110
+ if (res.status !== 200) return softSkip('blocked', 'precondition not met — `res.status !== 200` returned early (seam, prior step, or fixture unavailable)');
109
111
 
110
112
  const body = res.json as { events?: RawEvent[] } | undefined;
111
- if (!body?.events) return;
113
+ if (!body?.events) return softSkip('blocked', 'precondition not met — `!body?.events` returned early (seam, prior step, or fixture unavailable)');
112
114
 
113
115
  const TERMINAL_TYPES = new Set(['run.completed', 'run.failed', 'run.cancelled']);
114
116
  const terminalCount = body.events.filter((e) => typeof e.type === 'string' && TERMINAL_TYPES.has(e.type)).length;
115
117
 
116
- expect(terminalCount, driver.describe(
118
+ expect(terminalCount, req('openwop.it.eventOrdering.event-stream-contains-exactly-one-of-run-completed-run-failed-run-cancelled',
117
119
  'observability.md §"Run lifecycle events"',
118
120
  'a run MUST emit exactly one terminal event (run.completed / run.failed / run.cancelled)',
119
121
  )).toBe(1);
@@ -126,16 +128,16 @@ describe.skipIf(SKIP_NO_NOOP)('event-ordering: terminal run has at most one term
126
128
  await pollUntilTerminal(runId);
127
129
 
128
130
  const res = await driver.get(`/v1/runs/${encodeURIComponent(runId)}/events/poll`);
129
- if (res.status !== 200) return;
131
+ if (res.status !== 200) return softSkip('blocked', 'precondition not met — `res.status !== 200` returned early (seam, prior step, or fixture unavailable)');
130
132
 
131
133
  const body = res.json as { events?: RawEvent[] } | undefined;
132
- if (!body?.events || body.events.length === 0) return;
134
+ if (!body?.events || body.events.length === 0) return softSkip('blocked', 'precondition not met — `!body?.events || body.events.length === 0` returned early (seam, prior step, or fixture unavailable)');
133
135
 
134
136
  const TERMINAL_TYPES = new Set(['run.completed', 'run.failed', 'run.cancelled']);
135
137
  const lastEvent = body.events[body.events.length - 1]!;
136
138
  expect(
137
139
  typeof lastEvent.type === 'string' && TERMINAL_TYPES.has(lastEvent.type),
138
- driver.describe(
140
+ req('openwop.it.eventOrdering.the-terminal-event-is-the-last-event-in-the-stream',
139
141
  'observability.md §"Run lifecycle events"',
140
142
  'terminal event MUST be the last event in the stream — no events after a terminal type',
141
143
  ),
@@ -26,9 +26,7 @@ import { describe, it, expect } from 'vitest';
26
26
  import { readFileSync, readdirSync } from 'node:fs';
27
27
  import { join } from 'node:path';
28
28
  import { SCHEMAS_DIR } from '../lib/paths.js';
29
-
30
- /** Server-free assertion-message helper (mirrors driver.describe's "spec — requirement" shape without requiring OPENWOP_BASE_URL). */
31
- const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
29
+ import { req } from '../lib/requirement-ids.js';
32
30
 
33
31
  /**
34
32
  * Closed denylist of exec-class identifier *segments* (whole tokens). The
@@ -79,7 +77,7 @@ describe('exec-not-protocol-tier: no exec-class primitive in the protocol corpus
79
77
  }
80
78
  expect(
81
79
  offenders,
82
- why(
80
+ req('openwop.it.exec-not-protocol-tier.no-protocol-owned-core-openwop-identifier-denotes-arbitrary-command-execution',
83
81
  'host-extensions.md §exec-class tools',
84
82
  'the protocol corpus MUST NOT define a core.*/openwop.* exec-class identifier',
85
83
  ),
@@ -105,7 +103,7 @@ describe('exec-not-protocol-tier: no exec-class primitive in the protocol corpus
105
103
  walkProps(caps, 'capabilities');
106
104
  expect(
107
105
  offenders,
108
- why('host-extensions.md §exec-class tools', 'capabilities.schema.json MUST NOT declare an exec-class capability flag'),
106
+ req('openwop.it.exec-not-protocol-tier.no-capabilities-schema-json-property-name-denotes-arbitrary-command-execution', 'host-extensions.md §exec-class tools', 'capabilities.schema.json MUST NOT declare an exec-class capability flag'),
109
107
  ).toEqual([]);
110
108
  });
111
109
 
@@ -120,12 +118,12 @@ describe('exec-not-protocol-tier: no exec-class primitive in the protocol corpus
120
118
  });
121
119
  expect(
122
120
  offenders,
123
- why('host-extensions.md §exec-class tools', 'no RunEventType MUST denote arbitrary command execution'),
121
+ req('openwop.it.exec-not-protocol-tier.the-canonical-runeventtype-vocabulary-contains-no-exec-class-event', 'host-extensions.md §exec-class tools', 'no RunEventType MUST denote arbitrary command execution'),
124
122
  ).toEqual([]);
125
123
  });
126
124
 
127
125
  it('positive control: a vendor / x-host exec identifier is allowed (host-extension namespace)', () => {
128
- expect(isExecClass('vendor.acme.exec')).toBe(false);
126
+ expect(isExecClass('vendor.acme.exec'), req('openwop.it.exec-not-protocol-tier.positive-control-a-vendor-x-host-exec-identifier-is-allowed-host-extension-names', 'RFC 0069', 'positive control: a vendor / x-host exec identifier is allowed (host-extension namespace)')).toBe(false);
129
127
  expect(isExecClass('x-host-acme-exec')).toBe(false);
130
128
  expect(isExecClass('private.host.shell')).toBe(false);
131
129
  // And the denylist actually fires on a protocol-owned id:
@@ -31,6 +31,8 @@ import { driver } from '../lib/driver.js';
31
31
  import { experimentalGate } from '../lib/behavior-gate.js';
32
32
  import { __resetEnvCacheForTests } from '../lib/env.js';
33
33
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
34
+ import { req } from '../lib/requirement-ids.js';
35
+ import { softSkip } from '../lib/soft-skip.js';
34
36
 
35
37
  const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
36
38
 
@@ -62,15 +64,15 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §A schema discipline (RFC
62
64
  const em = capabilityFamily<{ executionModel?: { [k: string]: unknown; crossHostCausation?: Record<string, unknown>; replayDeterminism?: Record<string, unknown> } }>(d, 'multiAgent')?.executionModel;
63
65
  if (em === undefined) {
64
66
  ctx.skip();
65
- return;
67
+ return softSkip('blocked', 'precondition not met — `em === undefined` returned early (seam, prior step, or fixture unavailable)');
66
68
  }
67
69
  if (em.tier === undefined) {
68
70
  ctx.skip(); // tier is optional with default 'stable'
69
- return;
71
+ return softSkip('blocked', 'precondition not met — `em.tier === undefined` returned early (seam, prior step, or fixture unavailable)');
70
72
  }
71
73
  expect(
72
74
  em.tier === 'stable' || em.tier === 'experimental',
73
- driver.describe(
75
+ req('openwop.it.experimental-tier-shape.multiagent-executionmodel-tier-when-present-must-be-one-of-stable-experimental',
74
76
  'RFCS/0042-experimental-capability-tier.md §A',
75
77
  'multiAgent.executionModel.tier MUST be one of the canonical enum values',
76
78
  ),
@@ -82,12 +84,12 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §A schema discipline (RFC
82
84
  const em = capabilityFamily<{ executionModel?: { [k: string]: unknown; crossHostCausation?: Record<string, unknown>; replayDeterminism?: Record<string, unknown> } }>(d, 'multiAgent')?.executionModel;
83
85
  if (em === undefined || em.tier !== 'experimental') {
84
86
  ctx.skip();
85
- return;
87
+ return softSkip('blocked', 'precondition not met — `em === undefined || em.tier !== \'experimental\'` returned early (seam, prior step, or fixture unavailable)');
86
88
  }
87
89
 
88
90
  expect(
89
91
  typeof em.experimentalUntil,
90
- driver.describe(
92
+ req('openwop.it.experimental-tier-shape.when-tier-experimental-experimentaluntil-must-be-present-valid-date',
91
93
  'RFCS/0042-experimental-capability-tier.md §B',
92
94
  'when tier is "experimental", experimentalUntil MUST be present (the §B sunset-rule contract)',
93
95
  ),
@@ -96,7 +98,7 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §A schema discipline (RFC
96
98
  const dateStr = em.experimentalUntil as string;
97
99
  expect(
98
100
  /^\d{4}-\d{2}-\d{2}$/.test(dateStr),
99
- driver.describe(
101
+ req('openwop.it.experimental-tier-shape.when-tier-experimental-experimentaluntil-must-be-present-valid-date',
100
102
  'RFCS/0042-experimental-capability-tier.md §B',
101
103
  'experimentalUntil MUST match YYYY-MM-DD',
102
104
  ),
@@ -105,7 +107,7 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §A schema discipline (RFC
105
107
  const parsed = new Date(dateStr + 'T00:00:00Z');
106
108
  expect(
107
109
  !Number.isNaN(parsed.getTime()),
108
- driver.describe(
110
+ req('openwop.it.experimental-tier-shape.when-tier-experimental-experimentaluntil-must-be-present-valid-date',
109
111
  'RFCS/0042-experimental-capability-tier.md §B',
110
112
  'experimentalUntil MUST parse as a valid ISO-8601 date',
111
113
  ),
@@ -117,18 +119,18 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §A schema discipline (RFC
117
119
  const em = capabilityFamily<{ executionModel?: { [k: string]: unknown; crossHostCausation?: Record<string, unknown>; replayDeterminism?: Record<string, unknown> } }>(d, 'multiAgent')?.executionModel;
118
120
  if (em === undefined || em.tier !== 'experimental') {
119
121
  ctx.skip();
120
- return;
122
+ return softSkip('blocked', 'precondition not met — `em === undefined || em.tier !== \'experimental\'` returned early (seam, prior step, or fixture unavailable)');
121
123
  }
122
124
  if (typeof em.experimentalUntil !== 'string') {
123
125
  ctx.skip(); // shape probe above will fail; don't double-fail
124
- return;
126
+ return softSkip('blocked', 'precondition not met — `typeof em.experimentalUntil !== \'string\'` returned early (seam, prior step, or fixture unavailable)');
125
127
  }
126
128
  const target = new Date((em.experimentalUntil as string) + 'T00:00:00Z').getTime();
127
129
  const now = Date.now();
128
130
  const daysAhead = (target - now) / (1000 * 60 * 60 * 24);
129
131
  expect(
130
132
  daysAhead <= 365,
131
- driver.describe(
133
+ req('openwop.it.experimental-tier-shape.experimentaluntil-must-be-365-days-in-the-future-sunset-bound',
132
134
  'RFCS/0042-experimental-capability-tier.md §B',
133
135
  `experimentalUntil MUST be ≤ 365 days from now (got ${Math.floor(daysAhead)} days; advertised ${em.experimentalUntil})`,
134
136
  ),
@@ -140,17 +142,17 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §A schema discipline (RFC
140
142
  const em = capabilityFamily<{ executionModel?: { [k: string]: unknown; crossHostCausation?: Record<string, unknown>; replayDeterminism?: Record<string, unknown> } }>(d, 'multiAgent')?.executionModel;
141
143
  if (em === undefined || em.tier !== 'experimental') {
142
144
  ctx.skip();
143
- return;
145
+ return softSkip('blocked', 'precondition not met — `em === undefined || em.tier !== \'experimental\'` returned early (seam, prior step, or fixture unavailable)');
144
146
  }
145
147
  if (typeof em.experimentalUntil !== 'string') {
146
148
  ctx.skip();
147
- return;
149
+ return softSkip('blocked', 'precondition not met — `typeof em.experimentalUntil !== \'string\'` returned early (seam, prior step, or fixture unavailable)');
148
150
  }
149
151
  const target = new Date((em.experimentalUntil as string) + 'T00:00:00Z').getTime();
150
152
  const now = Date.now();
151
153
  expect(
152
154
  target >= now,
153
- driver.describe(
155
+ req('openwop.it.experimental-tier-shape.sunset-detection-experimentaluntil-in-the-past-is-non-conformant',
154
156
  'RFCS/0042-experimental-capability-tier.md §B',
155
157
  `experimentalUntil MUST NOT be in the past (advertised ${em.experimentalUntil}; host MUST either flip tier to stable, retract the advertisement, or re-advertise with a future date + open deprecation RFC)`,
156
158
  ),
@@ -168,7 +170,7 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §D experimentalGate helper
168
170
  const result = experimentalGate('test-profile', true, 'experimental', '2027-05-22');
169
171
  expect(
170
172
  result,
171
- driver.describe(
173
+ req('openwop.it.experimental-tier-shape.experimentalgate-returns-false-for-tier-experimental-without-openwop-require-exp',
172
174
  'RFCS/0042-experimental-capability-tier.md §D',
173
175
  'default mode + tier="experimental" MUST soft-skip — helper returns false',
174
176
  ),
@@ -189,7 +191,7 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §D experimentalGate helper
189
191
  __resetEnvCacheForTests();
190
192
  try {
191
193
  // Stable + advertised → proceed.
192
- expect(experimentalGate('test-stable', true, 'stable')).toBe(true);
194
+ expect(experimentalGate('test-stable', true, 'stable'), req('openwop.it.experimental-tier-shape.experimentalgate-routes-through-behaviorgate-when-tier-undefined-or-stable', 'RFC 0042 §A', 'experimentalGate routes through behaviorGate when tier === undefined or "stable"')).toBe(true);
193
195
  expect(experimentalGate('test-stable-undef', true, undefined)).toBe(true);
194
196
  // Stable + NOT advertised, default mode → skip (returns false, no throw).
195
197
  expect(experimentalGate('test-not-adv', false, 'stable')).toBe(false);