@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
@@ -22,6 +22,7 @@ import { describe, it, expect } from 'vitest';
22
22
  import { driver } from '../lib/driver.js';
23
23
  import { pollUntilTerminal } from '../lib/polling.js';
24
24
  import { isFixtureAdvertised } from '../lib/fixtures.js';
25
+ import { req } from '../lib/requirement-ids.js';
25
26
 
26
27
  const WORKFLOW_ID = 'conformance-cap-breach';
27
28
  const RECURSION_LIMIT = 3;
@@ -42,7 +43,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('cap-breach: conformance-cap-breach fixture fai
42
43
  workflowId: WORKFLOW_ID,
43
44
  configurable: { recursionLimit: RECURSION_LIMIT },
44
45
  });
45
- expect(create.status, driver.describe(
46
+ expect(create.status, req('openwop.it.cap-breach.emits-cap-breached-transitions-to-terminal-failed-when-configurable-recursionlim',
46
47
  'rest-endpoints.md POST /v1/runs',
47
48
  'run creation MUST accept the request even when configurable.recursionLimit is below the workflow size',
48
49
  )).toBe(201);
@@ -50,17 +51,17 @@ describe.skipIf(SKIP_NO_FIXTURE)('cap-breach: conformance-cap-breach fixture fai
50
51
 
51
52
  const terminal = await pollUntilTerminal(runId);
52
53
 
53
- expect(terminal.status, driver.describe(
54
+ expect(terminal.status, req('openwop.it.cap-breach.emits-cap-breached-transitions-to-terminal-failed-when-configurable-recursionlim',
54
55
  'fixtures.md conformance-cap-breach §Terminal status',
55
56
  'fixture MUST reach terminal `failed` when recursion limit is exceeded',
56
57
  )).toBe('failed');
57
58
 
58
- expect(terminal.error?.code, driver.describe(
59
+ expect(terminal.error?.code, req('openwop.it.cap-breach.emits-cap-breached-transitions-to-terminal-failed-when-configurable-recursionlim',
59
60
  'run-options.md §recursionLimit',
60
61
  'RunSnapshot.error.code MUST equal "recursion_limit_exceeded"',
61
62
  )).toBe('recursion_limit_exceeded');
62
63
 
63
- expect(typeof terminal.error?.message, driver.describe(
64
+ expect(typeof terminal.error?.message, req('openwop.it.cap-breach.emits-cap-breached-transitions-to-terminal-failed-when-configurable-recursionlim',
64
65
  'rest-endpoints.md RunSnapshot.error.message',
65
66
  'RunSnapshot.error.message MUST be a string',
66
67
  )).toBe('string');
@@ -72,7 +73,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('cap-breach: conformance-cap-breach fixture fai
72
73
  const events = (eventsRes.json as { events?: RunEvent[] } | undefined)?.events ?? [];
73
74
 
74
75
  const capBreachEvents = events.filter((e) => e.type === 'cap.breached');
75
- expect(capBreachEvents.length, driver.describe(
76
+ expect(capBreachEvents.length, req('openwop.it.cap-breach.emits-cap-breached-transitions-to-terminal-failed-when-configurable-recursionlim',
76
77
  'observability.md §cap.breached',
77
78
  'exactly one cap.breached event MUST be emitted on recursion-limit exceedance',
78
79
  )).toBe(1);
@@ -82,23 +83,23 @@ describe.skipIf(SKIP_NO_FIXTURE)('cap-breach: conformance-cap-breach fixture fai
82
83
  | { kind?: string; limit?: number; observed?: number; nodeId?: string }
83
84
  | undefined;
84
85
 
85
- expect(payload?.kind, driver.describe(
86
+ expect(payload?.kind, req('openwop.it.cap-breach.emits-cap-breached-transitions-to-terminal-failed-when-configurable-recursionlim',
86
87
  'run-event-payloads.schema.json §capBreached.kind',
87
88
  'cap.breached payload MUST carry kind="node-executions"',
88
89
  )).toBe('node-executions');
89
90
 
90
- expect(payload?.limit, driver.describe(
91
+ expect(payload?.limit, req('openwop.it.cap-breach.emits-cap-breached-transitions-to-terminal-failed-when-configurable-recursionlim',
91
92
  'run-event-payloads.schema.json §capBreached.limit',
92
93
  'cap.breached payload MUST carry the resolved limit (3 from configurable.recursionLimit)',
93
94
  )).toBe(RECURSION_LIMIT);
94
95
 
95
- expect(typeof payload?.observed, driver.describe(
96
+ expect(typeof payload?.observed, req('openwop.it.cap-breach.emits-cap-breached-transitions-to-terminal-failed-when-configurable-recursionlim',
96
97
  'run-event-payloads.schema.json §capBreached.observed',
97
98
  'cap.breached payload MUST carry the observed count as a number',
98
99
  )).toBe('number');
99
100
  expect(payload?.observed).toBeGreaterThan(RECURSION_LIMIT);
100
101
 
101
- expect(typeof payload?.nodeId, driver.describe(
102
+ expect(typeof payload?.nodeId, req('openwop.it.cap-breach.emits-cap-breached-transitions-to-terminal-failed-when-configurable-recursionlim',
102
103
  'run-event-payloads.schema.json §capBreached.nodeId',
103
104
  'cap.breached payload MUST carry the offending nodeId for node-executions kind',
104
105
  )).toBe('string');
@@ -121,10 +122,10 @@ describe.skipIf(SKIP_NO_FIXTURE)('cap-breach: conformance-cap-breach fixture fai
121
122
  const capBreach = events.find((e) => e.type === 'cap.breached');
122
123
  const runFailed = events.find((e) => e.type === 'run.failed');
123
124
 
124
- expect(capBreach, 'cap.breached MUST be emitted').toBeDefined();
125
- expect(runFailed, 'run.failed MUST be emitted').toBeDefined();
125
+ expect(capBreach, req('openwop.it.cap-breach.cap-breached-precedes-run-failed-in-the-event-sequence-breach-detected-before-ov', 'observability.md §event ordering', 'cap.breached MUST be emitted')).toBeDefined();
126
+ expect(runFailed, req('openwop.it.cap-breach.cap-breached-precedes-run-failed-in-the-event-sequence-breach-detected-before-ov', 'observability.md §event ordering', 'run.failed MUST be emitted')).toBeDefined();
126
127
 
127
- expect(capBreach!.sequence, driver.describe(
128
+ expect(capBreach!.sequence, req('openwop.it.cap-breach.cap-breached-precedes-run-failed-in-the-event-sequence-breach-detected-before-ov',
128
129
  'observability.md §event ordering',
129
130
  'cap.breached MUST precede run.failed in sequence (breach detected BEFORE over-limit node fires)',
130
131
  )).toBeLessThan(runFailed!.sequence);
@@ -137,11 +138,11 @@ describe.skipIf(SKIP_NO_FIXTURE)('cap-breach: conformance-cap-breach fixture fai
137
138
  // short) — those would emit fewer than `RECURSION_LIMIT` started
138
139
  // events while still satisfying the invariant.
139
140
  const nodeStarted = events.filter((e) => e.type === 'node.started');
140
- expect(nodeStarted.length, driver.describe(
141
+ expect(nodeStarted.length, req('openwop.it.cap-breach.cap-breached-precedes-run-failed-in-the-event-sequence-breach-detected-before-ov',
141
142
  'run-options.md §recursionLimit',
142
143
  'at most `limit` node.started events MUST be emitted; the over-limit node MUST NOT receive node.started',
143
144
  )).toBeLessThanOrEqual(RECURSION_LIMIT);
144
- expect(nodeStarted.length, driver.describe(
145
+ expect(nodeStarted.length, req('openwop.it.cap-breach.cap-breached-precedes-run-failed-in-the-event-sequence-breach-detected-before-ov',
145
146
  'run-options.md §recursionLimit',
146
147
  'at least one node MUST start before the breach (otherwise the workflow never executed)',
147
148
  )).toBeGreaterThan(0);
@@ -45,6 +45,8 @@ import { readFileSync } from 'node:fs';
45
45
  import { join } from 'node:path';
46
46
  import { driver } from '../lib/driver.js';
47
47
  import { SCHEMAS_DIR } from '../lib/paths.js';
48
+ import { req } from '../lib/requirement-ids.js';
49
+ import { softSkip } from '../lib/soft-skip.js';
48
50
 
49
51
  type Arm = 'root-plain' | 'root-dotted' | 'wrapper-plain' | 'wrapper-dotted';
50
52
 
@@ -84,11 +86,11 @@ describe('capability-arm-witness: which resolver arm the host lands on (RFC 0144
84
86
  const res = await driver.get('/.well-known/openwop');
85
87
  // No discovery document ⇒ inapplicable. Other scenarios own that failure; this leg is
86
88
  // about WHERE families sit, not whether the host has any.
87
- if (res.status !== 200 || res.json === undefined || res.json === null) return;
89
+ if (res.status !== 200 || res.json === undefined || res.json === null) return softSkip('blocked', 'precondition not met — `res.status !== 200 || res.json === undefined || res.json === null` returned early (No discovery document ⇒ inapplicable. Other scenarios own that failure; this leg is about WHERE families sit, …');
88
90
 
89
91
  const doc = res.json as Record<string, unknown>;
90
92
  const placements = classify(doc, declaredFamilies());
91
- if (placements.length === 0) return; // advertises no declared family — inapplicable
93
+ if (placements.length === 0) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `placements.length === 0` returned early (advertises no declared family — inapplicable)'); // advertises no declared family — inapplicable
92
94
 
93
95
  const canonical = placements.filter((p) => p.arms.includes('root-plain'));
94
96
  const rootDotted = placements.filter(
@@ -110,7 +112,7 @@ describe('capability-arm-witness: which resolver arm the host lands on (RFC 0144
110
112
  // ASSERT — the existing MUST, not a new one.
111
113
  expect(
112
114
  wrapperOnly.map((p) => p.family),
113
- driver.describe(
115
+ req('openwop.it.capability-arm-witness.no-capability-family-is-reachable-only-under-the-deprecated-capabilities-wrapper',
114
116
  'capabilities.md §"Document-root layout (normative — RFC 0073)"',
115
117
  'every family MUST appear at the document root; a host that serves families EXCLUSIVELY under the deprecated `capabilities` wrapper is non-conformant and is graded as such. The suite\'s helpers fall back to the wrapper, so without this leg such a host reads exactly like a migrated one — which is RFC 0144 G1',
116
118
  ),
@@ -34,7 +34,8 @@ import {
34
34
  type BundleV2Like,
35
35
  type BundleV2Requirement,
36
36
  } from '../lib/certification-bundle-verify.js';
37
- import { UNCLASSIFIED_RETURN_DETAIL } from '../lib/soft-skip.js';
37
+ import { UNCLASSIFIED_RETURN_DETAIL, softSkip } from '../lib/soft-skip.js';
38
+ import { req } from '../lib/requirement-ids.js';
38
39
 
39
40
  export const HOST_CALLBACK_NOT_REQUIRED = 'server-free: verifies bundle documents built in-process; no host is contacted';
40
41
 
@@ -103,7 +104,7 @@ describe('RFC 0148 §A/§C — certification-bundle-non-vacuous (consumer re-der
103
104
  const b = bundle(witnessedRows());
104
105
  expect(schemaValid(b), JSON.stringify(schemaValid.errors)).toBe(true);
105
106
  const v = verifyBundleV2(b);
106
- expect(v.rejections).toEqual([]);
107
+ expect(v.rejections, req('openwop.it.certification-bundle-non-vacuous.valid-v2-every-required-row-witnessed-evidence-valid-and-certified-and-the-vecto', 'RFC 0148 §A/§C', 'valid-v2: every required row witnessed → evidence valid AND certified (and the vector is schema-valid)')).toEqual([]);
107
108
  expect(v.evidenceValid).toBe(true);
108
109
  expect(v.certified).toBe(true);
109
110
  const p = v.profiles.find((x) => x.profile === PROFILE);
@@ -115,7 +116,7 @@ describe('RFC 0148 §A/§C — certification-bundle-non-vacuous (consumer re-der
115
116
  const rows = witnessedRows();
116
117
  rows[0] = { ...(rows[0] as BundleV2Requirement), assertionCount: 0 };
117
118
  const v = verifyBundleV2(bundle(rows));
118
- expect(v.evidenceValid).toBe(false);
119
+ expect(v.evidenceValid, req('openwop.it.certification-bundle-non-vacuous.early-return-a-required-row-that-is-executed-pass-with-assertioncount-0-rejects', 'RFC 0148 §A/§C', 'early-return: a required row that is executed-pass with assertionCount 0 REJECTS the evidence')).toBe(false);
119
120
  expect(v.certified).toBe(false);
120
121
  const p = v.profiles[0];
121
122
  expect(p?.rejections.map((r) => r.kind)).toEqual(['vacuous-pass']);
@@ -127,7 +128,7 @@ describe('RFC 0148 §A/§C — certification-bundle-non-vacuous (consumer re-der
127
128
  const { assertionCount: _drop, ...noCount } = rows[1] as BundleV2Requirement & { assertionCount: number };
128
129
  rows[1] = noCount;
129
130
  const v = verifyBundleV2(bundle(rows));
130
- expect(v.evidenceValid).toBe(false);
131
+ expect(v.evidenceValid, req('openwop.it.certification-bundle-non-vacuous.early-return-no-count-at-all-an-executed-pass-with-no-assertioncount-is-unwitnes', 'RFC 0148 §A/§C', 'early-return (no count at all): an executed-pass with NO assertionCount is unwitnessed and REJECTS')).toBe(false);
131
132
  expect(v.profiles[0]?.rejections[0]?.kind).toBe('vacuous-pass');
132
133
  });
133
134
 
@@ -135,7 +136,7 @@ describe('RFC 0148 §A/§C — certification-bundle-non-vacuous (consumer re-der
135
136
  const rows = witnessedRows();
136
137
  rows[0] = { requirementId: (rows[0] as BundleV2Requirement).requirementId, scenarioId: (rows[0] as BundleV2Requirement).scenarioId, disposition: 'blocked', detail: UNCLASSIFIED_RETURN_DETAIL, assertionCount: 0 };
137
138
  const v = verifyBundleV2(bundle(rows));
138
- expect(v.evidenceValid).toBe(false);
139
+ expect(v.evidenceValid, req('openwop.it.certification-bundle-non-vacuous.the-emitter-s-a-resolution-of-a-silent-zero-assertion-file-blocked-marker-detail', 'RFC 0148 §A/§C', 'the emitter\'s §A resolution of a silent zero-assertion file (blocked + marker detail) on a required row REJECTS as unwitnessed')).toBe(false);
139
140
  expect(v.profiles[0]?.rejections.map((r) => r.kind)).toEqual(['unwitnessed-requirement']);
140
141
  });
141
142
 
@@ -143,7 +144,7 @@ describe('RFC 0148 §A/§C — certification-bundle-non-vacuous (consumer re-der
143
144
  const rows = witnessedRows().slice(1); // drop the first floor row
144
145
  const dropped = requirementIdForScenario(floor().files[0] as string);
145
146
  const v = verifyBundleV2(bundle(rows));
146
- expect(v.evidenceValid).toBe(false);
147
+ expect(v.evidenceValid, req('openwop.it.certification-bundle-non-vacuous.missing-floor-a-required-requirement-with-no-row-at-all-rejects-unwitnessed-neve', 'RFC 0148 §A/§C', 'missing-floor: a required requirement with no row at all REJECTS (unwitnessed), never "not certified"')).toBe(false);
147
148
  const rej = v.profiles[0]?.rejections ?? [];
148
149
  expect(rej.map((r) => r.kind)).toEqual(['unwitnessed-requirement']);
149
150
  expect(rej[0]?.requirementId).toBe(dropped);
@@ -153,10 +154,10 @@ describe('RFC 0148 §A/§C — certification-bundle-non-vacuous (consumer re-der
153
154
 
154
155
  it('missing prefix witness: no `interrupt-*` scenario row and no summary row REJECTS the prefix requirement', () => {
155
156
  const { prefixes } = floor();
156
- if (prefixes.length === 0) return; // floor without a prefix requirement — nothing to assert here
157
+ if (prefixes.length === 0) return softSkip('blocked', 'precondition not met — `prefixes.length === 0` returned early (floor without a prefix requirement — nothing to assert here) (seam, prior step, or fixture unavailable)'); // floor without a prefix requirement — nothing to assert here
157
158
  const rows = witnessedRows().filter((r) => !prefixes.some((p) => r.scenarioId.startsWith(p)));
158
159
  const v = verifyBundleV2(bundle(rows));
159
- expect(v.evidenceValid).toBe(false);
160
+ expect(v.evidenceValid, req('openwop.it.certification-bundle-non-vacuous.missing-prefix-witness-no-interrupt-scenario-row-and-no-summary-row-rejects-the', 'RFC 0148 §A/§C', 'missing prefix witness: no `interrupt-*` scenario row and no summary row REJECTS the prefix requirement')).toBe(false);
160
161
  expect(v.profiles[0]?.rejections.some((r) => r.kind === 'unwitnessed-requirement' && r.requirementId === requirementIdForPrefix(prefixes[0] as string))).toBe(true);
161
162
  });
162
163
 
@@ -164,7 +165,7 @@ describe('RFC 0148 §A/§C — certification-bundle-non-vacuous (consumer re-der
164
165
  const rows = witnessedRows();
165
166
  rows.push({ ...(rows[0] as BundleV2Requirement) });
166
167
  const v = verifyBundleV2(bundle(rows));
167
- expect(v.evidenceValid).toBe(false);
168
+ expect(v.evidenceValid, req('openwop.it.certification-bundle-non-vacuous.duplicate-requirement-two-rows-for-one-requirement-rejects-exactly-one-dispositi', 'RFC 0148 §A/§C', 'duplicate-requirement: two rows for one requirement REJECTS (exactly one disposition per requirement)')).toBe(false);
168
169
  expect(v.rejections.map((r) => r.kind)).toContain('duplicate-requirement');
169
170
  });
170
171
 
@@ -173,7 +174,7 @@ describe('RFC 0148 §A/§C — certification-bundle-non-vacuous (consumer re-der
173
174
  const b = bundle(rows);
174
175
  const tampered = { ...b, results: { ...b.results, totals: { ...b.results.totals, blocked: 0, executedPass: b.results.totals.executedPass + 1 } } };
175
176
  const v = verifyBundleV2(tampered);
176
- expect(v.evidenceValid).toBe(false);
177
+ expect(v.evidenceValid, req('openwop.it.certification-bundle-non-vacuous.tampered-witness-totals-that-disagree-with-the-rows-reject-a-hand-edited-count-i', 'RFC 0148 §A/§C', 'tampered-witness: totals that disagree with the rows REJECT — a hand-edited count is not a witness')).toBe(false);
177
178
  expect(v.rejections.map((r) => r.kind)).toEqual(['totals-mismatch']);
178
179
  });
179
180
 
@@ -181,7 +182,7 @@ describe('RFC 0148 §A/§C — certification-bundle-non-vacuous (consumer re-der
181
182
  const rows = witnessedRows();
182
183
  rows[0] = { ...(rows[0] as BundleV2Requirement), disposition: 'passed' };
183
184
  const v = verifyBundleV2(bundle(rows));
184
- expect(v.rejections.some((r) => r.kind === 'unknown-disposition')).toBe(true);
185
+ expect(v.rejections.some((r) => r.kind === 'unknown-disposition'), req('openwop.it.certification-bundle-non-vacuous.tampered-witness-an-invented-disposition-name-rejects', 'RFC 0148 §A/§C', 'tampered-witness: an invented disposition name REJECTS')).toBe(true);
185
186
  expect(v.evidenceValid).toBe(false);
186
187
  });
187
188
 
@@ -189,7 +190,7 @@ describe('RFC 0148 §A/§C — certification-bundle-non-vacuous (consumer re-der
189
190
  const rows = witnessedRows();
190
191
  rows[0] = { requirementId: (rows[0] as BundleV2Requirement).requirementId, scenarioId: (rows[0] as BundleV2Requirement).scenarioId, disposition: 'blocked' };
191
192
  const v = verifyBundleV2(bundle(rows));
192
- expect(v.rejections.map((r) => r.kind)).toContain('reason-missing');
193
+ expect(v.rejections.map((r) => r.kind), req('openwop.it.certification-bundle-non-vacuous.a-non-pass-without-a-reason-rejects-rfc-0148-a-anything-other-than-executed-pass', 'RFC 0148 §A', 'a non-pass without a reason REJECTS (RFC 0148 §A: anything other than executed-pass says why)')).toContain('reason-missing');
193
194
  });
194
195
 
195
196
  it('blocked-floor: an HONEST blocked row on a required requirement is VALID evidence that does NOT certify', () => {
@@ -197,7 +198,7 @@ describe('RFC 0148 §A/§C — certification-bundle-non-vacuous (consumer re-der
197
198
  rows[0] = { ...(rows[0] as BundleV2Requirement), disposition: 'blocked', detail: 'seam unreachable from this runner (host-callback)' };
198
199
  delete (rows[0] as { assertionCount?: number }).assertionCount;
199
200
  const v = verifyBundleV2(bundle(rows));
200
- expect(v.rejections).toEqual([]);
201
+ expect(v.rejections, req('openwop.it.certification-bundle-non-vacuous.blocked-floor-an-honest-blocked-row-on-a-required-requirement-is-valid-evidence', 'RFC 0148 §A/§C', 'blocked-floor: an HONEST blocked row on a required requirement is VALID evidence that does NOT certify')).toEqual([]);
201
202
  expect(v.evidenceValid).toBe(true);
202
203
  expect(v.certified).toBe(false);
203
204
  expect(v.profiles[0]?.notCertifiable).toEqual([(rows[0] as BundleV2Requirement).requirementId]);
@@ -207,7 +208,7 @@ describe('RFC 0148 §A/§C — certification-bundle-non-vacuous (consumer re-der
207
208
  const rows = witnessedRows();
208
209
  rows[2] = { ...(rows[2] as BundleV2Requirement), disposition: 'executed-fail', detail: 'assertion failed against target', assertionCount: 4 };
209
210
  const v = verifyBundleV2(bundle(rows));
210
- expect(v.evidenceValid).toBe(true);
211
+ expect(v.evidenceValid, req('openwop.it.certification-bundle-non-vacuous.executed-fail-on-a-required-row-valid-evidence-not-certified', 'RFC 0148 §A/§C', 'executed-fail on a required row: valid evidence, not certified')).toBe(true);
211
212
  expect(v.certified).toBe(false);
212
213
  });
213
214
 
@@ -216,14 +217,14 @@ describe('RFC 0148 §A/§C — certification-bundle-non-vacuous (consumer re-der
216
217
  rows[0] = { requirementId: (rows[0] as BundleV2Requirement).requirementId, scenarioId: (rows[0] as BundleV2Requirement).scenarioId, disposition: 'inapplicable', detail: 'profile not advertised in the captured discovery set', assertionCount: 0 };
217
218
  rows[1] = { requirementId: (rows[1] as BundleV2Requirement).requirementId, scenarioId: (rows[1] as BundleV2Requirement).scenarioId, disposition: 'skipped', detail: 'operator opted the profile out (OPENWOP_OPTED_OUT_PROFILES)' };
218
219
  const v = verifyBundleV2(bundle(rows));
219
- expect(v.evidenceValid).toBe(true);
220
+ expect(v.evidenceValid, req('openwop.it.certification-bundle-non-vacuous.inapplicable-skipped-with-a-reason-on-a-required-row-certifies-rfc-0148-a-certif', 'RFC 0148 §A', 'inapplicable / skipped with a reason on a required row certifies (RFC 0148 §A CERTIFIABLE set)')).toBe(true);
220
221
  expect(v.certified).toBe(true);
221
222
  });
222
223
 
223
224
  it('a claim the discovery document does not derive is not certified even with perfect rows (RFC 0089 §B(1))', () => {
224
225
  const b = bundle(witnessedRows(), { discovery: { document: { protocolVersion: '1.0' } } });
225
226
  const v = verifyBundleV2(b);
226
- expect(v.evidenceValid).toBe(true);
227
+ expect(v.evidenceValid, req('openwop.it.certification-bundle-non-vacuous.a-claim-the-discovery-document-does-not-derive-is-not-certified-even-with-perfec', 'RFC 0089 §B(1', 'a claim the discovery document does not derive is not certified even with perfect rows (RFC 0089 §B(1))')).toBe(true);
227
228
  expect(v.profiles[0]?.derivable).toBe(false);
228
229
  expect(v.certified).toBe(false);
229
230
  });
@@ -231,19 +232,19 @@ describe('RFC 0148 §A/§C — certification-bundle-non-vacuous (consumer re-der
231
232
  it('a claimed profile with NO defined floor is unprovable — floorUnspecified, never certified (G6)', () => {
232
233
  const b = bundle(witnessedRows(), { claimedProfiles: ['openwop-not-a-profile'] });
233
234
  const v = verifyBundleV2(b);
234
- expect(v.profiles[0]?.floorUnspecified).toBe(true);
235
+ expect(v.profiles[0]?.floorUnspecified, req('openwop.it.certification-bundle-non-vacuous.a-claimed-profile-with-no-defined-floor-is-unprovable-floorunspecified-never-cer', 'RFC 0148 §A/§C', 'a claimed profile with NO defined floor is unprovable — floorUnspecified, never certified (G6)')).toBe(true);
235
236
  expect(v.certified).toBe(false);
236
237
  });
237
238
 
238
239
  it('a discovery-only profile certifies on derivation alone (RFC 0155 §A: openwop-discovery-core)', () => {
239
240
  const b = bundle(witnessedRows(), { claimedProfiles: ['openwop-discovery-core'] });
240
241
  const v = verifyBundleV2(b);
241
- expect(v.certified).toBe(true);
242
+ expect(v.certified, req('openwop.it.certification-bundle-non-vacuous.a-discovery-only-profile-certifies-on-derivation-alone-rfc-0155-a-openwop-discov', 'RFC 0155 §A', 'a discovery-only profile certifies on derivation alone (RFC 0155 §A: openwop-discovery-core)')).toBe(true);
242
243
  });
243
244
 
244
245
  it('a v1 document handed to the v2 verifier is rejected as not-v2 rather than silently read', () => {
245
246
  const b = bundle(witnessedRows(), { bundleVersion: '1' });
246
247
  const v = verifyBundleV2(b);
247
- expect(v.rejections.map((r) => r.kind)).toContain('not-v2');
248
+ expect(v.rejections.map((r) => r.kind), req('openwop.it.certification-bundle-non-vacuous.a-v1-document-handed-to-the-v2-verifier-is-rejected-as-not-v2-rather-than-silent', 'RFC 0148 §A/§C', 'a v1 document handed to the v2 verifier is rejected as not-v2 rather than silently read')).toContain('not-v2');
248
249
  });
249
250
  });
@@ -42,6 +42,7 @@ import {
42
42
  type BundleV2Like,
43
43
  type BundleV2Requirement,
44
44
  } from '../lib/certification-bundle-verify.js';
45
+ import { req } from '../lib/requirement-ids.js';
45
46
 
46
47
  export const HOST_CALLBACK_NOT_REQUIRED = 'server-free: scrubs and verifies bundle documents built in-process; no host is contacted';
47
48
 
@@ -89,7 +90,7 @@ describe('RFC 0148 §C — certification-bundle-redaction: secret canaries never
89
90
  rs[0] = { ...(rs[0] as BundleV2Requirement), disposition: 'executed-fail', assertionCount: 3, detail: `401 body: {"error":"bad key ${API_KEY}"} (canary ${CONFORMANCE_SECRET_CANARY} echoed)` };
90
91
  const b = bundle(rs);
91
92
  const { value, redactedAt } = scrubEvidence(b, [API_KEY, CONFORMANCE_SECRET_CANARY]);
92
- expect(findLiteral(value, API_KEY)).toEqual([]);
93
+ expect(findLiteral(value, API_KEY), req('openwop.it.certification-bundle-redaction.scrubevidence-replaces-every-occurrence-of-every-secret-in-nested-values-and-rep', 'RFC 0148 §C', 'scrubEvidence replaces every occurrence of every secret in nested values and reports where')).toEqual([]);
93
94
  expect(findLiteral(value, CONFORMANCE_SECRET_CANARY)).toEqual([]);
94
95
  expect(redactedAt).toEqual(['$.results.requirements[0].detail']);
95
96
  const detail = value.results.requirements[0]?.detail ?? '';
@@ -103,7 +104,7 @@ describe('RFC 0148 §C — certification-bundle-redaction: secret canaries never
103
104
  it('scrubs the captured discovery document and host self-description too — evidence is the WHOLE document', () => {
104
105
  const b = bundle(rows(), { name: 'h', version: '1', vendor: `acme (token ${API_KEY})` }, { ...DISCOVERY, ['x-host-debug']: { echo: CONFORMANCE_SECRET_CANARY } });
105
106
  const { value, redactedAt } = scrubEvidence(b, [API_KEY, CONFORMANCE_SECRET_CANARY]);
106
- expect(findLiteral(value, API_KEY)).toEqual([]);
107
+ expect(findLiteral(value, API_KEY), req('openwop.it.certification-bundle-redaction.scrubs-the-captured-discovery-document-and-host-self-description-too-evidence-is', 'RFC 0148 §C', 'scrubs the captured discovery document and host self-description too — evidence is the WHOLE document')).toEqual([]);
107
108
  expect(findLiteral(value, CONFORMANCE_SECRET_CANARY)).toEqual([]);
108
109
  expect([...redactedAt].sort()).toEqual(['$.discovery.document.x-host-debug.echo', '$.host.vendor']);
109
110
  });
@@ -111,7 +112,7 @@ describe('RFC 0148 §C — certification-bundle-redaction: secret canaries never
111
112
  it('scrubs object KEYS as well as values — a secret used as a map key is still a secret', () => {
112
113
  const doc = { ...DISCOVERY, ['x-host-keys']: { [API_KEY]: 'tenant-a' } };
113
114
  const { value } = scrubEvidence(bundle(rows(), { name: 'h', version: '1' }, doc), [API_KEY]);
114
- expect(findLiteral(value, API_KEY)).toEqual([]);
115
+ expect(findLiteral(value, API_KEY), req('openwop.it.certification-bundle-redaction.scrubs-object-keys-as-well-as-values-a-secret-used-as-a-map-key-is-still-a-secre', 'RFC 0148 §C', 'scrubs object KEYS as well as values — a secret used as a map key is still a secret')).toEqual([]);
115
116
  const keys = Object.keys((value.discovery.document as Record<string, Record<string, unknown>>)['x-host-keys'] ?? {});
116
117
  expect(keys).toEqual([redactionMarker(API_KEY)]);
117
118
  });
@@ -119,7 +120,7 @@ describe('RFC 0148 §C — certification-bundle-redaction: secret canaries never
119
120
  it('empty and whitespace-only secrets are ignored (scrubbing "" would blank every string)', () => {
120
121
  const b = bundle(rows());
121
122
  const { value, redactedAt } = scrubEvidence(b, ['', ' ']);
122
- expect(value).toEqual(b);
123
+ expect(value, req('openwop.it.certification-bundle-redaction.empty-and-whitespace-only-secrets-are-ignored-scrubbing-would-blank-every-string', 'RFC 0148 §C', 'empty and whitespace-only secrets are ignored (scrubbing "" would blank every string)')).toEqual(b);
123
124
  expect(redactedAt).toEqual([]);
124
125
  });
125
126
 
@@ -127,12 +128,12 @@ describe('RFC 0148 §C — certification-bundle-redaction: secret canaries never
127
128
  const short = 'abc123';
128
129
  const long = 'abc123-longer-secret';
129
130
  const { value } = scrubEvidence({ detail: `x ${long} y ${short} z` }, [short, long]);
130
- expect(value.detail).toBe(`x ${redactionMarker(long)} y ${redactionMarker(short)} z`);
131
+ expect(value.detail, req('openwop.it.certification-bundle-redaction.when-one-secret-is-a-prefix-of-another-the-longer-one-is-scrubbed-whole-no-parti', 'RFC 0148 §C', 'when one secret is a prefix of another the longer one is scrubbed whole (no partial marker inside a marker)')).toBe(`x ${redactionMarker(long)} y ${redactionMarker(short)} z`);
131
132
  });
132
133
 
133
134
  it('the redaction marker is stable, non-reversible, and does not contain the secret', () => {
134
135
  const m = redactionMarker(API_KEY);
135
- expect(m).toBe(redactionMarker(API_KEY));
136
+ expect(m, req('openwop.it.certification-bundle-redaction.the-redaction-marker-is-stable-non-reversible-and-does-not-contain-the-secret', 'RFC 0148 §C', 'the redaction marker is stable, non-reversible, and does not contain the secret')).toBe(redactionMarker(API_KEY));
136
137
  expect(m).not.toContain(API_KEY);
137
138
  expect(m).toMatch(/^«redacted:[0-9a-f]{12}»$/);
138
139
  expect(redactionMarker('other')).not.toBe(m);
@@ -150,7 +151,7 @@ describe('RFC 0148 §C — certification-bundle-redaction: secret canaries never
150
151
  SOME_OTHER_TOKEN: 'not-ours',
151
152
  } as NodeJS.ProcessEnv;
152
153
  const secrets = evidenceSecretsFromEnv(env, [API_KEY, undefined, '']);
153
- expect(secrets.sort()).toEqual([API_KEY, 'k1', 'p1', 's1', 't1', CONFORMANCE_SECRET_CANARY].sort());
154
+ expect(secrets.sort(), req('openwop.it.certification-bundle-redaction.evidencesecretsfromenv-selects-openwop-key-token-secret-password-variables-the-h', 'RFC 0148 §C', 'evidenceSecretsFromEnv selects OPENWOP_* key/token/secret/password variables, the handed credential, and ALWAYS the canary')).toEqual([API_KEY, 'k1', 'p1', 's1', 't1', CONFORMANCE_SECRET_CANARY].sort());
154
155
  });
155
156
 
156
157
  it('verifyBundleV2 REJECTS a bundle carrying the conformance canary anywhere, and accepts the scrubbed twin', () => {
@@ -158,7 +159,7 @@ describe('RFC 0148 §C — certification-bundle-redaction: secret canaries never
158
159
  rs[1] = { ...(rs[1] as BundleV2Requirement), detail: `resolved ${CONFORMANCE_SECRET_CANARY} in plain text` };
159
160
  const leaked = bundle(rs);
160
161
  const v = verifyBundleV2(leaked);
161
- expect(v.evidenceValid).toBe(false);
162
+ expect(v.evidenceValid, req('openwop.it.certification-bundle-redaction.verifybundlev2-rejects-a-bundle-carrying-the-conformance-canary-anywhere-and-acc', 'RFC 0148 §C', 'verifyBundleV2 REJECTS a bundle carrying the conformance canary anywhere, and accepts the scrubbed twin')).toBe(false);
162
163
  expect(v.rejections.map((r) => r.kind)).toEqual(['secret-canary']);
163
164
  expect(v.rejections[0]?.detail).toContain('$.results.requirements[1].detail');
164
165
 
@@ -171,12 +172,12 @@ describe('RFC 0148 §C — certification-bundle-redaction: secret canaries never
171
172
  it('the canary in a discovery-document KEY is still a rejection (keys are evidence)', () => {
172
173
  const doc = { ...DISCOVERY, [`x-host-${CONFORMANCE_SECRET_CANARY}`]: true };
173
174
  const v = verifyBundleV2(bundle(rows(), { name: 'h', version: '1' }, doc));
174
- expect(v.rejections.some((r) => r.kind === 'secret-canary')).toBe(true);
175
+ expect(v.rejections.some((r) => r.kind === 'secret-canary'), req('openwop.it.certification-bundle-redaction.the-canary-in-a-discovery-document-key-is-still-a-rejection-keys-are-evidence', 'RFC 0148 §C', 'the canary in a discovery-document KEY is still a rejection (keys are evidence)')).toBe(true);
175
176
  });
176
177
 
177
178
  it('the target-configuration digest is a digest: the API key is not a substring of any string in the document', () => {
178
179
  const b = bundle(rows());
179
- expect((b as unknown as { targetConfigurationSha256: string }).targetConfigurationSha256).toMatch(/^[0-9a-f]{64}$/);
180
+ expect((b as unknown as { targetConfigurationSha256: string }).targetConfigurationSha256, req('openwop.it.certification-bundle-redaction.the-target-configuration-digest-is-a-digest-the-api-key-is-not-a-substring-of-an', 'RFC 0148 §C', 'the target-configuration digest is a digest: the API key is not a substring of any string in the document')).toMatch(/^[0-9a-f]{64}$/);
180
181
  expect(findLiteral(b, API_KEY)).toEqual([]);
181
182
  });
182
183
  });
@@ -30,6 +30,7 @@ import { readFileSync } from 'node:fs';
30
30
  import { join } from 'node:path';
31
31
  import Ajv2020 from 'ajv/dist/2020.js';
32
32
  import { SCHEMAS_DIR } from '../lib/paths.js';
33
+ import { req } from '../lib/requirement-ids.js';
33
34
 
34
35
  const HEX = 'a'.repeat(64);
35
36
 
@@ -74,7 +75,7 @@ describe('RFC 0148 §C — certification bundle v2', () => {
74
75
  const validate = validator();
75
76
 
76
77
  it('a well-formed v2 bundle validates', () => {
77
- expect(validate(bundle()), JSON.stringify(validate.errors)).toBe(true);
78
+ expect(validate(bundle()), req('openwop.it.certification-bundle-v2.a-well-formed-v2-bundle-validates', 'RFC 0148 §C', JSON.stringify(validate.errors))).toBe(true);
78
79
  });
79
80
 
80
81
  it('all five totals are required, including blocked', () => {
@@ -86,8 +87,8 @@ describe('RFC 0148 §C — certification bundle v2', () => {
86
87
  delete totals[missing];
87
88
  expect(
88
89
  validate(bundle({ results: { totals, requirements: bundle().results.requirements } })),
89
- `RFC 0148 §C: \`${missing}\` MUST be present — an omitted total reads as zero without ` +
90
- 'anyone having asserted it',
90
+ req('openwop.it.certification-bundle-v2.all-five-totals-are-required-including-blocked', 'RFC 0148 §C', `RFC 0148 §C: \`${missing}\` MUST be present — an omitted total reads as zero without ` +
91
+ 'anyone having asserted it'),
91
92
  ).toBe(false);
92
93
  }
93
94
  });
@@ -95,8 +96,8 @@ describe('RFC 0148 §C — certification bundle v2', () => {
95
96
  it('the requirement list cannot be empty', () => {
96
97
  expect(
97
98
  validate(bundle({ results: { totals: bundle().results.totals, requirements: [] } })),
98
- 'RFC 0148 §C: a bundle with no requirement rows records no execution. An empty evidence set ' +
99
- 'reading as proof is the defect this section exists to close.',
99
+ req('openwop.it.certification-bundle-v2.the-requirement-list-cannot-be-empty', 'RFC 0148 §C', 'RFC 0148 §C: a bundle with no requirement rows records no execution. An empty evidence set ' +
100
+ 'reading as proof is the defect this section exists to close.'),
100
101
  ).toBe(false);
101
102
  });
102
103
 
@@ -107,7 +108,7 @@ describe('RFC 0148 §C — certification bundle v2', () => {
107
108
  totals: bundle().results.totals,
108
109
  requirements: [{ requirementId: 'x', scenarioId: 'y', disposition: 'probably-fine' }],
109
110
  },
110
- })),
111
+ })), req('openwop.it.certification-bundle-v2.a-disposition-outside-the-a-vocabulary-is-rejected', 'RFC 0148 §C', 'a disposition outside the §A vocabulary is rejected'),
111
112
  ).toBe(false);
112
113
  });
113
114
 
@@ -127,14 +128,14 @@ describe('RFC 0148 §C — certification bundle v2', () => {
127
128
  const rows = (b.results as { requirements: { disposition: string; assertionCount?: number }[] }).requirements;
128
129
  expect(
129
130
  rows.some((r) => r.disposition === 'executed-pass' && r.assertionCount === 0),
130
- 'the vacuous shape MUST remain inspectable in the artifact — v1 could not express it at all',
131
+ req('openwop.it.certification-bundle-v2.a-vacuous-pass-is-representable-and-therefore-visible', 'RFC 0148 §C', 'the vacuous shape MUST remain inspectable in the artifact — v1 could not express it at all'),
131
132
  ).toBe(true);
132
133
  });
133
134
 
134
135
  it('claimed profiles use canonical IDs, not deprecated aliases', () => {
135
136
  // RFC 0155 §E. A badge substantiated by a name that no longer means what it
136
137
  // did is the `openwop-core` ambiguity in bundle form.
137
- expect(validate(bundle({ claimedProfiles: ['openwop-core-standard'] }))).toBe(true);
138
+ expect(validate(bundle({ claimedProfiles: ['openwop-core-standard'] })), req('openwop.it.certification-bundle-v2.claimed-profiles-use-canonical-ids-not-deprecated-aliases', 'RFC 0148 §C', 'claimed profiles use canonical IDs, not deprecated aliases')).toBe(true);
138
139
  expect(validate(bundle({ claimedProfiles: ['Legacy Core'] }))).toBe(false);
139
140
  expect(validate(bundle({ claimedProfiles: [] }))).toBe(false);
140
141
  });
@@ -146,12 +147,12 @@ describe('RFC 0148 §C — certification bundle v2', () => {
146
147
  for (const field of ['scenarioManifestSha256', 'targetConfigurationSha256']) {
147
148
  const b = bundle() as Record<string, unknown>;
148
149
  delete b[field];
149
- expect(validate(b), `RFC 0147 §A.4: ${field} MUST be present`).toBe(false);
150
+ expect(validate(b), req('openwop.it.certification-bundle-v2.provenance-digests-are-required-and-hex-shaped', 'RFC 0148 §C', `RFC 0147 §A.4: ${field} MUST be present`)).toBe(false);
150
151
  }
151
152
  expect(validate(bundle({ scenarioManifestSha256: 'not-a-digest' }))).toBe(false);
152
153
  });
153
154
 
154
155
  it('bundleVersion is pinned to 2', () => {
155
- expect(validate(bundle({ bundleVersion: '1' })), 'a v1 bundle MUST NOT validate as v2').toBe(false);
156
+ expect(validate(bundle({ bundleVersion: '1' })), req('openwop.it.certification-bundle-v2.bundleversion-is-pinned-to-2', 'RFC 0148 §C', 'a v1 bundle MUST NOT validate as v2')).toBe(false);
156
157
  });
157
158
  });
@@ -54,6 +54,7 @@ import {
54
54
  type ChainCompensationPolicy,
55
55
  type FragmentNodeCompensation,
56
56
  } from '../lib/workflow-chain-expansion.js';
57
+ import { req } from '../lib/requirement-ids.js';
57
58
 
58
59
  function schema(name: string): Record<string, unknown> {
59
60
  return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
@@ -131,7 +132,7 @@ describe('RFC 0157 — schema: the chain manifest mirrors the compensation shape
131
132
  $defs: { FragmentNode: { properties: Record<string, unknown> }; WorkflowChain: { properties: Record<string, unknown> } };
132
133
  };
133
134
  const wd = schema('workflow-definition.schema.json') as { $defs: { WorkflowNode: { properties: Record<string, unknown> } } };
134
- expect(chainSchema.$defs.FragmentNode.properties['compensation'], 'FragmentNode MUST carry `compensation`').toBeDefined();
135
+ expect(chainSchema.$defs.FragmentNode.properties['compensation'], req('openwop.it.chain-compensation-expansion.fragmentnode-compensation-is-a-byte-mirror-of-workflownode-compensation', 'RFC 0157', 'FragmentNode MUST carry `compensation`')).toBeDefined();
135
136
  expect(stripDescription(chainSchema.$defs.FragmentNode.properties['compensation'])).toEqual(
136
137
  stripDescription(wd.$defs.WorkflowNode.properties['compensation']),
137
138
  );
@@ -140,14 +141,14 @@ describe('RFC 0157 — schema: the chain manifest mirrors the compensation shape
140
141
  it('WorkflowChain.compensation is a byte-mirror of compensation-policy.schema.json', () => {
141
142
  const chainSchema = schema('workflow-chain-pack-manifest.schema.json') as { $defs: { WorkflowChain: { properties: Record<string, unknown> } } };
142
143
  const policy = schema('compensation-policy.schema.json');
143
- expect(chainSchema.$defs.WorkflowChain.properties['compensation'], 'WorkflowChain MUST carry the policy').toBeDefined();
144
+ expect(chainSchema.$defs.WorkflowChain.properties['compensation'], req('openwop.it.chain-compensation-expansion.workflowchain-compensation-is-a-byte-mirror-of-compensation-policy-schema-json', 'RFC 0157', 'WorkflowChain MUST carry the policy')).toBeDefined();
144
145
  expect(stripDescription(chainSchema.$defs.WorkflowChain.properties['compensation'])).toEqual(stripDescription(policy));
145
146
  });
146
147
 
147
148
  it('the manifest stays self-contained — no cross-file $ref was introduced', () => {
148
149
  const raw = readFileSync(join(SCHEMAS_DIR, 'workflow-chain-pack-manifest.schema.json'), 'utf8');
149
150
  const refs = [...raw.matchAll(/"\$ref":\s*"([^"]+)"/g)].map((m) => m[1]);
150
- for (const r of refs) expect(r.startsWith('#/'), `cross-file $ref ${r} would break fixed-list validators downstream`).toBe(true);
151
+ for (const r of refs) expect(r.startsWith('#/'), req('openwop.it.chain-compensation-expansion.the-manifest-stays-self-contained-no-cross-file-ref-was-introduced', 'RFC 0157', `cross-file $ref ${r} would break fixed-list validators downstream`)).toBe(true);
151
152
  });
152
153
 
153
154
  it('a manifest declaring both surfaces validates; a foreign key inside either is rejected', () => {
@@ -155,13 +156,13 @@ describe('RFC 0157 — schema: the chain manifest mirrors the compensation shape
155
156
  expect(validate(manifestWith(CHAIN)), JSON.stringify(validate.errors)).toBe(true);
156
157
  const badNode = structuredClone(CHAIN) as unknown as { dag: { nodes: Array<Record<string, unknown>> } };
157
158
  (badNode.dag.nodes[0]!['compensation'] as Record<string, unknown>)['rollback'] = true;
158
- expect(validate(manifestWith(badNode)), 'the mirrored node declaration MUST stay closed').toBe(false);
159
+ expect(validate(manifestWith(badNode)), req('openwop.it.chain-compensation-expansion.a-manifest-declaring-both-surfaces-validates-a-foreign-key-inside-either-is-reje', 'RFC 0157', 'the mirrored node declaration MUST stay closed')).toBe(false);
159
160
  const badPolicy = structuredClone(CHAIN) as unknown as { compensation: Record<string, unknown> };
160
161
  badPolicy.compensation['approvalScope'] = 'none';
161
- expect(validate(manifestWith(badPolicy)), 'the mirrored policy keeps the escalate-only approval scope').toBe(false);
162
+ expect(validate(manifestWith(badPolicy)), req('openwop.it.chain-compensation-expansion.a-manifest-declaring-both-surfaces-validates-a-foreign-key-inside-either-is-reje', 'RFC 0157', 'the mirrored policy keeps the escalate-only approval scope')).toBe(false);
162
163
  const noTriggers = structuredClone(CHAIN) as unknown as { compensation: Record<string, unknown> };
163
164
  noTriggers.compensation = {};
164
- expect(validate(manifestWith(noTriggers)), 'a policy without triggers is not a policy').toBe(false);
165
+ expect(validate(manifestWith(noTriggers)), req('openwop.it.chain-compensation-expansion.a-manifest-declaring-both-surfaces-validates-a-foreign-key-inside-either-is-reje', 'RFC 0157', 'a policy without triggers is not a policy')).toBe(false);
165
166
  });
166
167
 
167
168
  it('FragmentNode.irreversibleEffect (RFC 0151 UQ4) is a sibling boolean, mutually exclusive with compensation — in the manifest and in expansion', () => {
@@ -180,7 +181,7 @@ describe('RFC 0157 — schema: the chain manifest mirrors the compensation shape
180
181
  // Both on one node: the schema rejects it AND expansion refuses fail-closed before emitting.
181
182
  const both = structuredClone(CHAIN) as unknown as { dag: { nodes: Array<Record<string, unknown>> } };
182
183
  both.dag.nodes[0]!['irreversibleEffect'] = true; // node 0 (`reserve`) declares a compensation
183
- expect(validate(manifestWith(both)), 'compensation.md §B: irreversibleEffect: true + compensation is contradictory').toBe(false);
184
+ expect(validate(manifestWith(both)), req('openwop.it.chain-compensation-expansion.fragmentnode-irreversibleeffect-rfc-0151-uq4-is-a-sibling-boolean-mutually-exclu', 'RFC 0151', 'compensation.md §B: irreversibleEffect: true + compensation is contradictory')).toBe(false);
184
185
  let thrown: unknown;
185
186
  try {
186
187
  expandChainWithCompensation(both as unknown as typeof CHAIN, CTX);
@@ -206,7 +207,7 @@ describe('RFC 0157 — expansion carries the declaration and the policy', () =>
206
207
  expect(reserve?.compensation?.inputMapping?.['reservationId']).toBe(`\${nodes.${reserve?.id}.output.id}`);
207
208
  expect(charge?.compensation?.inputMapping?.['chargeId']).toBe(`nodes.${charge?.id}.output.chargeId`);
208
209
  expect(charge?.compensation?.requiresApproval).toBe(true);
209
- expect(notify?.compensation, 'a node that declared none gains none').toBeUndefined();
210
+ expect(notify?.compensation, req('openwop.it.chain-compensation-expansion.the-declaration-survives-verbatim-onto-the-expanded-node-params-substituted-node', 'RFC 0157', 'a node that declared none gains none')).toBeUndefined();
210
211
  // the mirrored core is untouched: plain expandChain still knows nothing of it
211
212
  const plain = expandChain(CHAIN, CTX);
212
213
  expect((plain.nodes[0] as { compensation?: unknown }).compensation).toBeUndefined();
@@ -220,7 +221,7 @@ describe('RFC 0157 — expansion carries the declaration and the policy', () =>
220
221
  const out = expandChainWithCompensation(chain, CTX);
221
222
  const nodes = out.nodes as ReadonlyArray<(typeof out.nodes)[number] & { compensation?: FragmentNodeCompensation }>;
222
223
  const reserve = nodes.find((n) => n.id.endsWith('_reserve'));
223
- expect(reserve?.compensation?.inputMapping?.['parentRef']).toBe('${nodes.upstream-in-parent.output.x}');
224
+ expect(reserve?.compensation?.inputMapping?.['parentRef'], req('openwop.it.chain-compensation-expansion.a-reference-to-a-non-fragment-node-id-inside-inputmapping-passes-through-unchang', 'RFC 0157', 'a reference to a NON-fragment node id inside inputMapping passes through unchanged')).toBe('${nodes.upstream-in-parent.output.x}');
224
225
  });
225
226
 
226
227
  it('an unresolvable compensation.nodeTypeId fails with chain_unresolvable_typeid before any node is emitted', () => {
@@ -231,7 +232,7 @@ describe('RFC 0157 — expansion carries the declaration and the policy', () =>
231
232
  } catch (e) {
232
233
  thrown = e;
233
234
  }
234
- expect(thrown).toBeInstanceOf(ChainUnresolvableTypeIdError);
235
+ expect(thrown, req('openwop.it.chain-compensation-expansion.an-unresolvable-compensation-nodetypeid-fails-with-chain-unresolvable-typeid-bef', 'RFC 0157', 'an unresolvable compensation.nodeTypeId fails with chain_unresolvable_typeid before any node is emitted')).toBeInstanceOf(ChainUnresolvableTypeIdError);
235
236
  expect((thrown as ChainUnresolvableTypeIdError).typeId).toBe('vendor.pay.refund');
236
237
  expect(String((thrown as Error).message)).toContain('chain_unresolvable_typeid');
237
238
  });
@@ -239,12 +240,12 @@ describe('RFC 0157 — expansion carries the declaration and the policy', () =>
239
240
  it('the chain policy becomes settings.compensation when the parent has none', () => {
240
241
  const out = expandChainWithCompensation(CHAIN, CTX);
241
242
  expect(out.settingsCompensation).toEqual(POLICY);
242
- expect(out.settingsCompensation, 'a copy, not the same object').not.toBe(POLICY);
243
+ expect(out.settingsCompensation, req('openwop.it.chain-compensation-expansion.the-chain-policy-becomes-settings-compensation-when-the-parent-has-none', 'RFC 0157', 'a copy, not the same object')).not.toBe(POLICY);
243
244
  });
244
245
 
245
246
  it('a parent policy that is deep-equal (any key order) is accepted; a differing one is refused, never merged', () => {
246
247
  const sameOtherOrder: ChainCompensationPolicy = { approvalScope: 'declared', triggers: ['node-failure', 'run-cancel'] };
247
- expect(expandChainWithCompensation(CHAIN, CTX, sameOtherOrder).settingsCompensation).toEqual(sameOtherOrder);
248
+ expect(expandChainWithCompensation(CHAIN, CTX, sameOtherOrder).settingsCompensation, req('openwop.it.chain-compensation-expansion.a-parent-policy-that-is-deep-equal-any-key-order-is-accepted-a-differing-one-is', 'RFC 0157', 'a parent policy that is deep-equal (any key order) is accepted; a differing one is refused, never merged')).toEqual(sameOtherOrder);
248
249
  const differs: ChainCompensationPolicy = { triggers: ['node-failure'] };
249
250
  let thrown: unknown;
250
251
  try {
@@ -262,7 +263,7 @@ describe('RFC 0157 — expansion carries the declaration and the policy', () =>
262
263
  for (const n of chain.dag.nodes as unknown as Array<{ compensation?: unknown }>) delete n.compensation;
263
264
  const parent: ChainCompensationPolicy = { triggers: ['operator-request'] };
264
265
  const withParent = expandChainWithCompensation(chain, CTX, parent);
265
- expect(withParent.settingsCompensation).toBe(parent);
266
+ expect(withParent.settingsCompensation, req('openwop.it.chain-compensation-expansion.a-chain-with-no-policy-inherits-the-parent-policy-or-none-and-carries-no-declara', 'RFC 0157', 'a chain with no policy inherits the parent policy (or none) and carries no declaration it did not make')).toBe(parent);
266
267
  const nodes = withParent.nodes as ReadonlyArray<{ compensation?: unknown }>;
267
268
  expect(nodes.every((n) => n.compensation === undefined)).toBe(true);
268
269
  const withoutParent = carryCompensation(chain, expandChain(chain, CTX), CTX);