@openwop/openwop-conformance 1.163.0 → 2.0.0-rc.1

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 (611) hide show
  1. package/README.md +11 -2
  2. package/dist/cli.js +262 -14
  3. package/dist/lib/certification-bundle-v3.js +142 -0
  4. package/dist/lib/paths.js +25 -3
  5. package/dist/lib/requirement-ids.js +57 -4
  6. package/dist/lib/requirement-ledger.js +1 -0
  7. package/dist/lib/scenario-disposition.js +6 -1
  8. package/dist/lib/spec-coherence.js +13 -32
  9. package/dist/spec-artifacts.lock.json +5 -0
  10. package/package.json +11 -33
  11. package/requirements.json +22892 -11701
  12. package/scenario-majors.json +1493 -0
  13. package/schemas/CORPUS-STAMP.json +137 -5
  14. package/src/cli.ts +236 -19
  15. package/src/global-setup.ts +3 -2
  16. package/src/lib/certification-bundle-v3.ts +175 -0
  17. package/src/lib/corpus-stamp.ts +29 -1
  18. package/src/lib/driver.ts +9 -1
  19. package/src/lib/era2-seed.ts +197 -0
  20. package/src/lib/paths.ts +25 -3
  21. package/src/lib/requirement-ids.ts +54 -4
  22. package/src/lib/requirement-ledger.ts +7 -1
  23. package/src/lib/scenario-disposition.ts +6 -1
  24. package/src/lib/seams.ts +45 -0
  25. package/src/lib/spec-coherence.ts +15 -32
  26. package/src/lib/v2.ts +62 -0
  27. package/src/scenarios/a2a-1-0-agent-card.test.ts +17 -16
  28. package/src/scenarios/a2a-1-0-task-roundtrip.test.ts +11 -10
  29. package/src/scenarios/a2a-card-runtime-consistency.test.ts +18 -17
  30. package/src/scenarios/a2a-peer-authority.test.ts +7 -6
  31. package/src/scenarios/a2a-task-roundtrip.test.ts +30 -29
  32. package/src/scenarios/a2a-version-negotiation.test.ts +16 -14
  33. package/src/scenarios/a2ui-surface-degrades.test.ts +5 -3
  34. package/src/scenarios/a2ui-surface-delta-transport.test.ts +35 -33
  35. package/src/scenarios/a2ui-surface-replay.test.ts +6 -4
  36. package/src/scenarios/a2ui-surface-shape.test.ts +11 -10
  37. package/src/scenarios/a2ui-surface-version-refusal.test.ts +6 -4
  38. package/src/scenarios/a2ui-untrusted-blocks-approval.test.ts +5 -3
  39. package/src/scenarios/agent-capability-degraded-projection.test.ts +12 -11
  40. package/src/scenarios/agent-channel-dispatch.test.ts +18 -16
  41. package/src/scenarios/agent-deployment-lifecycle.test.ts +20 -19
  42. package/src/scenarios/agent-deployment-shape.test.ts +22 -24
  43. package/src/scenarios/agent-eval-run.test.ts +19 -18
  44. package/src/scenarios/agent-eval-suite-shape.test.ts +20 -22
  45. package/src/scenarios/agent-live-allowlist-enforced.test.ts +6 -5
  46. package/src/scenarios/agent-live-invocation-bracket.test.ts +13 -12
  47. package/src/scenarios/agent-live-runtime-shape.test.ts +10 -12
  48. package/src/scenarios/agent-live-structured-output.test.ts +7 -6
  49. package/src/scenarios/agent-loop-iteration-monotonic.test.ts +3 -3
  50. package/src/scenarios/agent-loop-stateful-resume.test.ts +2 -2
  51. package/src/scenarios/agent-loop-version5-shape.test.ts +4 -4
  52. package/src/scenarios/agent-loop-workspace-snapshot.test.ts +3 -3
  53. package/src/scenarios/agent-manifest-role-profile.test.ts +9 -9
  54. package/src/scenarios/agent-manifest-runtime.test.ts +13 -12
  55. package/src/scenarios/agent-org-chart-scoping.test.ts +14 -13
  56. package/src/scenarios/agent-org-chart-shape.test.ts +11 -13
  57. package/src/scenarios/agent-platform-aggregate-evidence.test.ts +5 -4
  58. package/src/scenarios/agent-platform-profile.test.ts +18 -19
  59. package/src/scenarios/agent-requires-capabilities-shape.test.ts +4 -4
  60. package/src/scenarios/agent-roster-attribution.test.ts +18 -17
  61. package/src/scenarios/agent-roster-shape.test.ts +17 -19
  62. package/src/scenarios/agent-verifier-shape.test.ts +11 -11
  63. package/src/scenarios/agentConfidenceEscalation.test.ts +2 -1
  64. package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +6 -5
  65. package/src/scenarios/agentMemoryRedactionContract.test.ts +2 -1
  66. package/src/scenarios/agentMemoryRoundTrip.test.ts +2 -1
  67. package/src/scenarios/agentMemoryTtlExpiry.test.ts +4 -3
  68. package/src/scenarios/agentMessageReducer.test.ts +2 -1
  69. package/src/scenarios/agentMetadata.test.ts +2 -1
  70. package/src/scenarios/agentPackCatalog.test.ts +23 -21
  71. package/src/scenarios/agentPackExport.test.ts +4 -2
  72. package/src/scenarios/agentPackHandoffSchemaValidation.test.ts +5 -4
  73. package/src/scenarios/agentPackInstall.test.ts +4 -2
  74. package/src/scenarios/agentPackProvenance.test.ts +2 -1
  75. package/src/scenarios/agentReasoningEvents.test.ts +3 -2
  76. package/src/scenarios/agentReasoningStreaming.test.ts +11 -10
  77. package/src/scenarios/agents-run-tool-allowlist.test.ts +14 -12
  78. package/src/scenarios/ai-envelope-shape.test.ts +31 -29
  79. package/src/scenarios/aiEnvelope.capBreached.test.ts +26 -24
  80. package/src/scenarios/aiEnvelope.contractRefusal.test.ts +36 -34
  81. package/src/scenarios/aiEnvelope.correlationReplay.test.ts +17 -15
  82. package/src/scenarios/aiEnvelope.redaction.test.ts +22 -20
  83. package/src/scenarios/aiEnvelope.schemaDrift.test.ts +18 -16
  84. package/src/scenarios/aiEnvelope.trustBoundaryPropagation.test.ts +38 -36
  85. package/src/scenarios/aiEnvelope.universalKinds.test.ts +31 -29
  86. package/src/scenarios/aiproviders-input-shape.test.ts +5 -5
  87. package/src/scenarios/aiproviders-realtimevoice-shape.test.ts +14 -16
  88. package/src/scenarios/aiproviders-selfhosted-honesty.test.ts +6 -4
  89. package/src/scenarios/aiproviders-selfhosted-shape.test.ts +15 -17
  90. package/src/scenarios/aiproviders-speechsynth-shape.test.ts +6 -8
  91. package/src/scenarios/aiproviders-subscription-scope.test.ts +7 -8
  92. package/src/scenarios/anonymous-actor-audit-opaque.test.ts +9 -8
  93. package/src/scenarios/anonymous-actor-default-deny.test.ts +10 -9
  94. package/src/scenarios/anonymous-actor-egress-guarded.test.ts +6 -5
  95. package/src/scenarios/anonymous-actor-no-secret-reach.test.ts +7 -6
  96. package/src/scenarios/anonymous-actor-shape.test.ts +17 -19
  97. package/src/scenarios/anonymous-actor-write-gated.test.ts +9 -8
  98. package/src/scenarios/append-ordering.test.ts +7 -5
  99. package/src/scenarios/approval-gate-events.test.ts +4 -3
  100. package/src/scenarios/approval-gate-flow.test.ts +4 -3
  101. package/src/scenarios/approval-payload.test.ts +5 -4
  102. package/src/scenarios/artifact-auth.test.ts +7 -6
  103. package/src/scenarios/artifact-type-pack-install.test.ts +6 -6
  104. package/src/scenarios/artifact-type-pack-manifest-validation.test.ts +9 -8
  105. package/src/scenarios/artifact-type-registration-source.test.ts +20 -20
  106. package/src/scenarios/artifact-type-store-emission.test.ts +15 -18
  107. package/src/scenarios/artifact-type-store-without-render.test.ts +4 -4
  108. package/src/scenarios/audit-log-integrity.test.ts +8 -6
  109. package/src/scenarios/auth-api-key-rotation.test.ts +11 -9
  110. package/src/scenarios/auth-mtls.test.ts +22 -20
  111. package/src/scenarios/auth-oauth2-client-credentials.test.ts +16 -14
  112. package/src/scenarios/auth-oidc-user-bearer.test.ts +26 -24
  113. package/src/scenarios/auth-saml-profile.test.ts +14 -12
  114. package/src/scenarios/auth-scim-profile.test.ts +3 -2
  115. package/src/scenarios/auth-subject-link-key-class.test.ts +8 -7
  116. package/src/scenarios/auth-subject-link.test.ts +11 -10
  117. package/src/scenarios/auth.test.ts +5 -4
  118. package/src/scenarios/authorization-fail-closed.test.ts +3 -2
  119. package/src/scenarios/authorization-roles-shape.test.ts +9 -7
  120. package/src/scenarios/blob-cross-tenant-isolation.test.ts +8 -6
  121. package/src/scenarios/blob-presign-expiry.test.ts +12 -10
  122. package/src/scenarios/blob-roundtrip.test.ts +0 -0
  123. package/src/scenarios/budget-enforcement.test.ts +15 -14
  124. package/src/scenarios/budget-policy-shape.test.ts +18 -19
  125. package/src/scenarios/bulk-cancel.test.ts +9 -8
  126. package/src/scenarios/byok-auth-modes.test.ts +13 -14
  127. package/src/scenarios/byok-roundtrip.test.ts +12 -10
  128. package/src/scenarios/cache-cross-tenant-isolation.test.ts +8 -6
  129. package/src/scenarios/cache-ttl-expiry.test.ts +7 -5
  130. package/src/scenarios/callai-multimodal.test.ts +5 -3
  131. package/src/scenarios/cancellation.test.ts +6 -5
  132. package/src/scenarios/cap-breach.test.ts +15 -14
  133. package/src/scenarios/capability-arm-witness.test.ts +5 -3
  134. package/src/scenarios/certification-bundle-non-vacuous.test.ts +20 -19
  135. package/src/scenarios/certification-bundle-redaction.test.ts +11 -10
  136. package/src/scenarios/certification-bundle-v2.test.ts +11 -10
  137. package/src/scenarios/chain-compensation-expansion.test.ts +14 -13
  138. package/src/scenarios/chain-produced-var-roundtrip.test.ts +13 -14
  139. package/src/scenarios/chain-subchain-cycle-rejected.test.ts +9 -10
  140. package/src/scenarios/chain-subchain-fanout.test.ts +8 -9
  141. package/src/scenarios/chain-subchain-sibling.test.ts +14 -16
  142. package/src/scenarios/channel-presence-behavioral.test.ts +9 -7
  143. package/src/scenarios/channel-presence-shape.test.ts +11 -12
  144. package/src/scenarios/channel-ttl.test.ts +6 -5
  145. package/src/scenarios/chat-card-pack-execution.test.ts +4 -4
  146. package/src/scenarios/chat-card-pack-manifest-validation.test.ts +9 -8
  147. package/src/scenarios/commitment-fired.test.ts +6 -5
  148. package/src/scenarios/compensation-behavior.test.ts +24 -23
  149. package/src/scenarios/compensation-recovery.test.ts +28 -27
  150. package/src/scenarios/configurable-schema.test.ts +8 -6
  151. package/src/scenarios/conformance-advertised-seam-required.test.ts +6 -5
  152. package/src/scenarios/conformance-execution-witness.test.ts +9 -8
  153. package/src/scenarios/connection-pack-apihosts.test.ts +19 -17
  154. package/src/scenarios/connection-pack-manifest-valid.test.ts +12 -11
  155. package/src/scenarios/connection-pack-no-credential-material.test.ts +8 -6
  156. package/src/scenarios/connection-pack-reach-exclusive.test.ts +5 -4
  157. package/src/scenarios/connection-pack-write-reconsent.test.ts +6 -4
  158. package/src/scenarios/connection-provider-resolution.test.ts +18 -16
  159. package/src/scenarios/connector-manifest-validity.test.ts +8 -7
  160. package/src/scenarios/context-budget-transcript-bound.test.ts +17 -15
  161. package/src/scenarios/context-summarization-replay.test.ts +14 -12
  162. package/src/scenarios/contract-provenance.test.ts +18 -18
  163. package/src/scenarios/conversation-turn-model-provenance-shape.test.ts +14 -15
  164. package/src/scenarios/conversationCapabilityNegotiation.test.ts +2 -1
  165. package/src/scenarios/conversationLifecycle.test.ts +2 -1
  166. package/src/scenarios/conversationReplayDeterminism.test.ts +4 -2
  167. package/src/scenarios/conversationVsLegacySuspend.test.ts +2 -1
  168. package/src/scenarios/core-standard-profile.test.ts +8 -9
  169. package/src/scenarios/cost-attribution.test.ts +23 -21
  170. package/src/scenarios/credential-payload-redaction.test.ts +4 -3
  171. package/src/scenarios/credentials-capability-shape.test.ts +5 -4
  172. package/src/scenarios/cross-engine-append-behavior.test.ts +11 -10
  173. package/src/scenarios/cross-engine-append-ordering.test.ts +4 -3
  174. package/src/scenarios/cross-host-ancestry-endpoint.test.ts +11 -10
  175. package/src/scenarios/cross-host-causation-shape.test.ts +7 -6
  176. package/src/scenarios/cross-workspace-isolation.test.ts +4 -3
  177. package/src/scenarios/data-residency-admission.test.ts +15 -14
  178. package/src/scenarios/deadletter-capability-shape.test.ts +6 -4
  179. package/src/scenarios/deadletter-retry-exhaustion.test.ts +4 -3
  180. package/src/scenarios/debug-bundle-truncation.test.ts +9 -7
  181. package/src/scenarios/debugBundle.test.ts +25 -23
  182. package/src/scenarios/discovery-families-view.test.ts +5 -4
  183. package/src/scenarios/discovery.test.ts +30 -29
  184. package/src/scenarios/dispatch-cross-worker-handoff.test.ts +10 -8
  185. package/src/scenarios/dispatch-fanout-parallel.test.ts +24 -22
  186. package/src/scenarios/dispatch-input-mapping.test.ts +12 -10
  187. package/src/scenarios/dispatch-output-mapping.test.ts +9 -7
  188. package/src/scenarios/dispatch-per-item-input.test.ts +22 -20
  189. package/src/scenarios/dispatchLoop.test.ts +3 -2
  190. package/src/scenarios/distillation-index-roundtrip.test.ts +3 -3
  191. package/src/scenarios/distillation-secret-carryforward.test.ts +2 -2
  192. package/src/scenarios/distillation-shape.test.ts +4 -4
  193. package/src/scenarios/distillation-stable-archive.test.ts +6 -6
  194. package/src/scenarios/distillation-token-budget.test.ts +5 -5
  195. package/src/scenarios/durability-poison-exhaustion.test.ts +7 -6
  196. package/src/scenarios/egress-audience-binding.test.ts +11 -10
  197. package/src/scenarios/egress-decision-content-free.test.ts +6 -5
  198. package/src/scenarios/egress-provenance-shape.test.ts +17 -18
  199. package/src/scenarios/envelope-completion-distinguishes-truncation.test.ts +27 -25
  200. package/src/scenarios/envelope-nl-to-format-engaged.test.ts +19 -17
  201. package/src/scenarios/envelope-reasoning-secret-redaction.test.ts +28 -26
  202. package/src/scenarios/envelope-reasoning-shape.test.ts +19 -17
  203. package/src/scenarios/envelope-recovery-applied.test.ts +24 -22
  204. package/src/scenarios/envelope-refusal-shape.test.ts +31 -29
  205. package/src/scenarios/envelope-rendering-hint.test.ts +5 -4
  206. package/src/scenarios/envelope-retry-attempted.test.ts +33 -31
  207. package/src/scenarios/envelope-retry-exhausted.test.ts +22 -20
  208. package/src/scenarios/envelope-tier-one-subset-static.test.ts +9 -7
  209. package/src/scenarios/envelope-truncated.test.ts +19 -17
  210. package/src/scenarios/envelope-truncation-cap-exhaustion.test.ts +19 -17
  211. package/src/scenarios/envelope-variant-discriminator-static.test.ts +4 -3
  212. package/src/scenarios/errors.test.ts +20 -19
  213. package/src/scenarios/eventOrdering.test.ts +16 -14
  214. package/src/scenarios/exec-not-protocol-tier.test.ts +5 -7
  215. package/src/scenarios/experimental-tier-shape.test.ts +17 -15
  216. package/src/scenarios/export-bundle-portability.test.ts +17 -17
  217. package/src/scenarios/failure-path.test.ts +7 -6
  218. package/src/scenarios/feedback-capability-shape.test.ts +4 -3
  219. package/src/scenarios/feedback-correction-redaction.test.ts +2 -1
  220. package/src/scenarios/feedback-cross-tenant-isolation.test.ts +2 -1
  221. package/src/scenarios/feedback-fork-not-copied.test.ts +3 -2
  222. package/src/scenarios/feedback-on-terminal-run.test.ts +3 -2
  223. package/src/scenarios/feedback-record-and-list.test.ts +2 -1
  224. package/src/scenarios/feedback-unsupported-501.test.ts +2 -1
  225. package/src/scenarios/fixtures-gating.test.ts +29 -28
  226. package/src/scenarios/fixtures-valid.test.ts +16 -15
  227. package/src/scenarios/form-content-instantiation.test.ts +15 -14
  228. package/src/scenarios/frontend-plugin-packs.test.ts +29 -27
  229. package/src/scenarios/fs-path-traversal.test.ts +14 -12
  230. package/src/scenarios/goal-standing-continuation.test.ts +21 -21
  231. package/src/scenarios/grpc-transport.test.ts +9 -7
  232. package/src/scenarios/heartbeat-capability-shape.test.ts +3 -3
  233. package/src/scenarios/heartbeat-fires-once-per-tick.test.ts +2 -2
  234. package/src/scenarios/heartbeat-idempotent-no-spam.test.ts +3 -3
  235. package/src/scenarios/heartbeat-runtime-bound.test.ts +2 -2
  236. package/src/scenarios/highConcurrency.test.ts +14 -13
  237. package/src/scenarios/host-callback-declaration.test.ts +8 -7
  238. package/src/scenarios/http-client-ssrf.test.ts +7 -5
  239. package/src/scenarios/i18n-negotiation.test.ts +14 -12
  240. package/src/scenarios/idempotency-concurrent-claim.test.ts +14 -13
  241. package/src/scenarios/idempotency-key-determinism.test.ts +20 -18
  242. package/src/scenarios/idempotency.test.ts +8 -7
  243. package/src/scenarios/idempotencyRetry.test.ts +13 -11
  244. package/src/scenarios/identity-owner-shape.test.ts +6 -5
  245. package/src/scenarios/identity-passthrough.test.ts +4 -3
  246. package/src/scenarios/interrupt-approval.test.ts +6 -5
  247. package/src/scenarios/interrupt-approver-routing.test.ts +15 -15
  248. package/src/scenarios/interrupt-auth-required-resume.test.ts +5 -3
  249. package/src/scenarios/interrupt-clarification.test.ts +4 -3
  250. package/src/scenarios/interrupt-external-event-correlation.test.ts +6 -5
  251. package/src/scenarios/interrupt-parent-child-cascade.test.ts +4 -3
  252. package/src/scenarios/interrupt-quorum-resolution.test.ts +5 -4
  253. package/src/scenarios/interrupt-token-matrix.test.ts +8 -6
  254. package/src/scenarios/interruptRace.test.ts +12 -10
  255. package/src/scenarios/kv-atomic-increment.test.ts +11 -9
  256. package/src/scenarios/kv-cas.test.ts +14 -12
  257. package/src/scenarios/kv-cross-tenant-isolation.test.ts +12 -10
  258. package/src/scenarios/kv-ttl-expiry.test.ts +7 -5
  259. package/src/scenarios/localized-content-delivery.test.ts +24 -26
  260. package/src/scenarios/maliciousManifest.test.ts +10 -8
  261. package/src/scenarios/mcp-2026-07-28-discover.test.ts +15 -14
  262. package/src/scenarios/mcp-cache-tenant-scope.test.ts +6 -5
  263. package/src/scenarios/mcp-current-auth-boundary.test.ts +7 -6
  264. package/src/scenarios/mcp-discoverability.test.ts +9 -7
  265. package/src/scenarios/mcp-extension-opacity.test.ts +7 -6
  266. package/src/scenarios/mcp-mrtr-roundtrip.test.ts +18 -17
  267. package/src/scenarios/mcp-server-elicitation-bridge.test.ts +13 -12
  268. package/src/scenarios/mcp-server-prompt-roundtrip.test.ts +11 -10
  269. package/src/scenarios/mcp-server-resource-roundtrip.test.ts +11 -10
  270. package/src/scenarios/mcp-server-sampling-bridge.test.ts +12 -11
  271. package/src/scenarios/mcp-server-tool-roundtrip.test.ts +14 -13
  272. package/src/scenarios/mcp-server-untrusted-args.test.ts +14 -13
  273. package/src/scenarios/mcp-stateless-request.test.ts +11 -10
  274. package/src/scenarios/mcp-tool-roundtrip.test.ts +4 -3
  275. package/src/scenarios/mcp-toolcall-redaction.test.ts +4 -3
  276. package/src/scenarios/mcp-version-negotiation.test.ts +15 -14
  277. package/src/scenarios/media-url-inline-cap.test.ts +21 -19
  278. package/src/scenarios/memory-attribution-emits-on-write.test.ts +5 -5
  279. package/src/scenarios/memory-attribution-no-content.test.ts +4 -4
  280. package/src/scenarios/memory-attribution-replay-stable.test.ts +4 -3
  281. package/src/scenarios/memory-attribution-shape.test.ts +4 -3
  282. package/src/scenarios/memory-attribution-tenant-scoped.test.ts +3 -3
  283. package/src/scenarios/memory-capability-model-shape.test.ts +14 -15
  284. package/src/scenarios/memory-compaction-event-emitted.test.ts +7 -6
  285. package/src/scenarios/memory-compaction-provenance-tag.test.ts +3 -2
  286. package/src/scenarios/memory-compaction-sr1-carry-forward.test.ts +7 -6
  287. package/src/scenarios/memory-consolidation-idempotent.test.ts +5 -4
  288. package/src/scenarios/memory-consolidation-shape.test.ts +9 -11
  289. package/src/scenarios/memory-degraded-projection.test.ts +10 -9
  290. package/src/scenarios/memory-injection-budget.test.ts +15 -14
  291. package/src/scenarios/metric-emission.test.ts +4 -3
  292. package/src/scenarios/model-capability-insufficient.test.ts +19 -17
  293. package/src/scenarios/model-capability-substituted.test.ts +20 -18
  294. package/src/scenarios/multi-agent-confidence-escalation.test.ts +12 -11
  295. package/src/scenarios/multi-agent-handoff-state-machine.test.ts +10 -9
  296. package/src/scenarios/multi-agent-memory-lifecycle.test.ts +11 -10
  297. package/src/scenarios/multi-node-ordering.test.ts +4 -3
  298. package/src/scenarios/multi-party-conversation-behavioral.test.ts +6 -6
  299. package/src/scenarios/multi-party-conversation-shape.test.ts +21 -23
  300. package/src/scenarios/multi-region-idempotency-behavior.test.ts +16 -15
  301. package/src/scenarios/multi-region-idempotency.test.ts +6 -5
  302. package/src/scenarios/node-failed-payload-shape.test.ts +5 -4
  303. package/src/scenarios/node-module-required-capabilities-shape.test.ts +10 -8
  304. package/src/scenarios/oauth-authorization-code-roundtrip.test.ts +5 -4
  305. package/src/scenarios/oauth-capability-shape.test.ts +5 -4
  306. package/src/scenarios/oauth-connector-redaction.test.ts +4 -3
  307. package/src/scenarios/openapi-resolved-paths.test.ts +6 -5
  308. package/src/scenarios/orchestratorConservativePath.test.ts +2 -1
  309. package/src/scenarios/orchestratorDispatch.test.ts +2 -1
  310. package/src/scenarios/orchestratorTermination.test.ts +2 -1
  311. package/src/scenarios/org-position-no-authority-escalation.test.ts +2 -2
  312. package/src/scenarios/otel-collector-canary-inspection.test.ts +8 -7
  313. package/src/scenarios/otel-emission-grpc.test.ts +3 -2
  314. package/src/scenarios/otel-emission.test.ts +6 -5
  315. package/src/scenarios/otel-trace-propagation-subworkflow.test.ts +7 -6
  316. package/src/scenarios/otel-trace-propagation.test.ts +2 -1
  317. package/src/scenarios/owner-subject-echo.test.ts +13 -12
  318. package/src/scenarios/owner-subject-shape.test.ts +11 -10
  319. package/src/scenarios/pack-manifest-extension-opacity.test.ts +8 -8
  320. package/src/scenarios/pack-registry-isolation.test.ts +4 -3
  321. package/src/scenarios/pack-registry-publish.test.ts +65 -63
  322. package/src/scenarios/pack-registry.test.ts +38 -36
  323. package/src/scenarios/pause-resume.test.ts +18 -16
  324. package/src/scenarios/policies.test.ts +20 -18
  325. package/src/scenarios/production-backpressure.test.ts +18 -16
  326. package/src/scenarios/production-retention-expiry.test.ts +8 -6
  327. package/src/scenarios/profile-discovery-core-alias.test.ts +13 -11
  328. package/src/scenarios/profileDerivation.test.ts +34 -33
  329. package/src/scenarios/prompt-all-four-kinds-events.test.ts +11 -9
  330. package/src/scenarios/prompt-composed-secret-redaction.test.ts +11 -9
  331. package/src/scenarios/prompt-composed-trust-marker.test.ts +10 -8
  332. package/src/scenarios/prompt-end-to-end-events.test.ts +17 -15
  333. package/src/scenarios/prompt-list-and-fetch.test.ts +16 -14
  334. package/src/scenarios/prompt-mutable-lifecycle.test.ts +13 -11
  335. package/src/scenarios/prompt-mutation-workspace-membership-enforced.test.ts +6 -4
  336. package/src/scenarios/prompt-pack-install.test.ts +14 -12
  337. package/src/scenarios/prompt-prefix-cache.test.ts +19 -17
  338. package/src/scenarios/prompt-read-workspace-membership-enforced.test.ts +11 -9
  339. package/src/scenarios/prompt-render-deterministic.test.ts +19 -17
  340. package/src/scenarios/prompt-resolution-chain-agent-intrinsic.test.ts +7 -5
  341. package/src/scenarios/prompt-resolution-chain-event.test.ts +12 -10
  342. package/src/scenarios/prompt-resolution-chain-fallback-cascade.test.ts +10 -8
  343. package/src/scenarios/prompt-resolution-chain-node-wins.test.ts +8 -6
  344. package/src/scenarios/prompt-template-shape.test.ts +30 -28
  345. package/src/scenarios/proposal-reviewable-learning.test.ts +19 -19
  346. package/src/scenarios/protocol-versions-array.test.ts +10 -9
  347. package/src/scenarios/provider-usage.test.ts +17 -15
  348. package/src/scenarios/providerPolicyEnforcement.test.ts +16 -14
  349. package/src/scenarios/purpose-propagation.test.ts +23 -21
  350. package/src/scenarios/queue-ack-nack-dlq.test.ts +12 -10
  351. package/src/scenarios/queue-cross-tenant-isolation.test.ts +8 -6
  352. package/src/scenarios/queue-publish-consume-roundtrip.test.ts +10 -8
  353. package/src/scenarios/rate-limit-envelope.test.ts +6 -5
  354. package/src/scenarios/redaction.test.ts +13 -11
  355. package/src/scenarios/redactionAdversarial.test.ts +11 -9
  356. package/src/scenarios/registry-public.test.ts +12 -11
  357. package/src/scenarios/replay-divergence-at-refusal.test.ts +16 -15
  358. package/src/scenarios/replay-fanout-suppression.test.ts +16 -15
  359. package/src/scenarios/replay-fork-arbitrary.test.ts +24 -23
  360. package/src/scenarios/replay-fork.test.ts +20 -19
  361. package/src/scenarios/replay-llm-cache-key-portable.test.ts +11 -9
  362. package/src/scenarios/replay-llm-cache-key.test.ts +22 -21
  363. package/src/scenarios/replay-observable-sequence-determinism.test.ts +4 -3
  364. package/src/scenarios/replay-retention-expiry.test.ts +11 -9
  365. package/src/scenarios/replay-side-effect-suppression.test.ts +21 -19
  366. package/src/scenarios/replayDeterminism.test.ts +13 -12
  367. package/src/scenarios/requirement-ledger.test.ts +15 -14
  368. package/src/scenarios/restart-during-run.test.ts +3 -2
  369. package/src/scenarios/route-coverage.test.ts +20 -19
  370. package/src/scenarios/run-diff.test.ts +22 -20
  371. package/src/scenarios/run-execution-bounds-shape.test.ts +14 -12
  372. package/src/scenarios/run-transport-economy.test.ts +13 -11
  373. package/src/scenarios/runner-ledger.test.ts +17 -16
  374. package/src/scenarios/runs-lifecycle.test.ts +7 -6
  375. package/src/scenarios/runtime-capabilities.test.ts +12 -10
  376. package/src/scenarios/runtime-requires-install-gate.test.ts +8 -8
  377. package/src/scenarios/runtime-requires-shape.test.ts +7 -6
  378. package/src/scenarios/safefetch-behavior.test.ts +22 -21
  379. package/src/scenarios/safefetch-live-audit.test.ts +14 -13
  380. package/src/scenarios/sandbox-memory-cap.test.ts +2 -1
  381. package/src/scenarios/sandbox-mvp-behavior.test.ts +24 -23
  382. package/src/scenarios/sandbox-no-host-fs-escape.test.ts +3 -2
  383. package/src/scenarios/sandbox-timeout-cap.test.ts +2 -1
  384. package/src/scenarios/sandbox-wasm-isolation.test.ts +14 -15
  385. package/src/scenarios/sandbox-wasm-timeout.test.ts +5 -6
  386. package/src/scenarios/scheduling-capability-shape.test.ts +8 -6
  387. package/src/scenarios/scheduling-cron-fires-once.test.ts +8 -6
  388. package/src/scenarios/search-bm25-roundtrip.test.ts +8 -6
  389. package/src/scenarios/secret-leakage-otel-attribute.test.ts +22 -20
  390. package/src/scenarios/self-hosted-runner.test.ts +26 -24
  391. package/src/scenarios/semantic-digest-vectors.test.ts +8 -7
  392. package/src/scenarios/speech-synthesis-roundtrip.test.ts +9 -7
  393. package/src/scenarios/speech-synthesis-unadvertised.test.ts +5 -3
  394. package/src/scenarios/sql-injection-rejection.test.ts +8 -6
  395. package/src/scenarios/sql-transaction-atomicity.test.ts +10 -8
  396. package/src/scenarios/staleClaim.test.ts +5 -4
  397. package/src/scenarios/stream-modes-buffer.test.ts +8 -7
  398. package/src/scenarios/stream-modes-mixed.test.ts +10 -9
  399. package/src/scenarios/stream-modes.test.ts +12 -11
  400. package/src/scenarios/stream-subscribe-from-beginning.test.ts +11 -9
  401. package/src/scenarios/stream-text-fixture.test.ts +14 -13
  402. package/src/scenarios/streamReconnect.test.ts +13 -11
  403. package/src/scenarios/strict-behavior-gate.test.ts +9 -8
  404. package/src/scenarios/subrun-approval-fail-closed.test.ts +2 -2
  405. package/src/scenarios/subrun-approval-gate.test.ts +3 -3
  406. package/src/scenarios/subrun-attestation-shape.test.ts +2 -2
  407. package/src/scenarios/subrun-checksum-stable.test.ts +4 -4
  408. package/src/scenarios/subworkflow-input-mapping.test.ts +16 -14
  409. package/src/scenarios/subworkflow.test.ts +10 -9
  410. package/src/scenarios/table-cross-tenant-isolation.test.ts +8 -6
  411. package/src/scenarios/table-cursor-pagination.test.ts +8 -6
  412. package/src/scenarios/table-schema-enforcement.test.ts +7 -5
  413. package/src/scenarios/test-seam-unauthenticated.test.ts +2 -2
  414. package/src/scenarios/tool-catalog-compact-projection.test.ts +10 -8
  415. package/src/scenarios/tool-catalog-projection.test.ts +11 -9
  416. package/src/scenarios/tool-descriptor-shape.test.ts +14 -15
  417. package/src/scenarios/tool-hooks-authorization-fail-closed.test.ts +3 -3
  418. package/src/scenarios/tool-hooks-content-free.test.ts +4 -4
  419. package/src/scenarios/tool-hooks-failure-honesty.test.ts +4 -4
  420. package/src/scenarios/tool-hooks-rate-limit.test.ts +2 -2
  421. package/src/scenarios/tool-hooks-secret-redaction.test.ts +2 -2
  422. package/src/scenarios/tool-hooks-shape.test.ts +3 -3
  423. package/src/scenarios/tool-session-lifecycle.test.ts +14 -13
  424. package/src/scenarios/trigger-bridge-delivery.test.ts +14 -14
  425. package/src/scenarios/trigger-bridge-shape.test.ts +19 -20
  426. package/src/scenarios/trigger-ingestion.test.ts +19 -17
  427. package/src/scenarios/trigger-stream-cdc-sources.test.ts +16 -14
  428. package/src/scenarios/v2-approver-enforced.test.ts +97 -0
  429. package/src/scenarios/v2-assurance-downgrade-audited.test.ts +75 -0
  430. package/src/scenarios/v2-bundle-v3-signed.test.ts +96 -0
  431. package/src/scenarios/v2-capabilities-root-closed.test.ts +63 -0
  432. package/src/scenarios/v2-capability-record-shape.test.ts +104 -0
  433. package/src/scenarios/v2-chain-pin-exact.test.ts +118 -0
  434. package/src/scenarios/v2-coherence-not-in-bundle.test.ts +98 -0
  435. package/src/scenarios/v2-compensation-read-projection.test.ts +85 -0
  436. package/src/scenarios/v2-configurable-closed.test.ts +67 -0
  437. package/src/scenarios/v2-dual-stack-negotiation.test.ts +118 -0
  438. package/src/scenarios/v2-effect-identity-business-key.test.ts +132 -0
  439. package/src/scenarios/v2-effect-seam-manifest.test.ts +163 -0
  440. package/src/scenarios/v2-enum-growth-rule.test.ts +69 -0
  441. package/src/scenarios/v2-era-key.test.ts +86 -0
  442. package/src/scenarios/v2-error-registry.test.ts +89 -0
  443. package/src/scenarios/v2-event-type-closed.test.ts +97 -0
  444. package/src/scenarios/v2-fork-a-v1-run.test.ts +109 -0
  445. package/src/scenarios/v2-form-when-reuses-edge-conditions.test.ts +70 -0
  446. package/src/scenarios/v2-header-scheme.test.ts +67 -0
  447. package/src/scenarios/v2-id-grammar.test.ts +77 -0
  448. package/src/scenarios/v2-idempotency-key-grammar.test.ts +65 -0
  449. package/src/scenarios/v2-interrupt-token-scheme.test.ts +55 -0
  450. package/src/scenarios/v2-lane-issuer-advertised.test.ts +62 -0
  451. package/src/scenarios/v2-legacy-profiles-absent.test.ts +78 -0
  452. package/src/scenarios/v2-manifest-ceiling-refused.test.ts +130 -0
  453. package/src/scenarios/v2-manifest-hatch-carried.test.ts +120 -0
  454. package/src/scenarios/v2-min-client-version.test.ts +48 -0
  455. package/src/scenarios/v2-minimum-version-refused.test.ts +141 -0
  456. package/src/scenarios/v2-mrtr-rounds-ceiling.test.ts +95 -0
  457. package/src/scenarios/v2-negotiation-authenticated.test.ts +125 -0
  458. package/src/scenarios/v2-negotiation-decided-emitted.test.ts +149 -0
  459. package/src/scenarios/v2-no-transport-advertisement.test.ts +57 -0
  460. package/src/scenarios/v2-owner-subject-required.test.ts +72 -0
  461. package/src/scenarios/v2-pack-isolation.test.ts +147 -0
  462. package/src/scenarios/v2-payload-registry-closed.test.ts +110 -0
  463. package/src/scenarios/v2-peer-dependency-declared.test.ts +132 -0
  464. package/src/scenarios/v2-pinned-run-disposition.test.ts +124 -0
  465. package/src/scenarios/v2-poll-cursor-v2.test.ts +114 -0
  466. package/src/scenarios/v2-preferred-version-default.test.ts +83 -0
  467. package/src/scenarios/v2-profiles-derived-only.test.ts +70 -0
  468. package/src/scenarios/v2-provider-conflict.test.ts +115 -0
  469. package/src/scenarios/v2-refresh-sla.test.ts +78 -0
  470. package/src/scenarios/v2-relaxation-recorded.test.ts +134 -0
  471. package/src/scenarios/v2-revocation-honored.test.ts +66 -0
  472. package/src/scenarios/v2-subject-link-record.test.ts +102 -0
  473. package/src/scenarios/v2-unmapped-type-refused.test.ts +95 -0
  474. package/src/scenarios/v2-v1-events-translated.test.ts +105 -0
  475. package/src/scenarios/v2-v1-signed-webhook-accepted.test.ts +109 -0
  476. package/src/scenarios/v2-webhook-durable-delivery.test.ts +238 -0
  477. package/src/scenarios/v2-well-known-one-resource.test.ts +129 -0
  478. package/src/scenarios/vector-knn-roundtrip.test.ts +9 -7
  479. package/src/scenarios/verifier-gating.test.ts +8 -6
  480. package/src/scenarios/version-fold.test.ts +9 -8
  481. package/src/scenarios/version-negotiation.test.ts +12 -11
  482. package/src/scenarios/voice-bargein-no-partial-leak.test.ts +8 -6
  483. package/src/scenarios/voice-event-payloads-shape.test.ts +10 -11
  484. package/src/scenarios/voice-interim-not-durable.test.ts +7 -5
  485. package/src/scenarios/voice-streamref-tenant-bound.test.ts +5 -3
  486. package/src/scenarios/voice-synthesis-streaming.test.ts +11 -9
  487. package/src/scenarios/voice-transcription-streaming.test.ts +8 -6
  488. package/src/scenarios/voice-transcription-unadvertised.test.ts +5 -3
  489. package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +3 -2
  490. package/src/scenarios/wasm-pack-invoke-completed.test.ts +3 -2
  491. package/src/scenarios/wasm-pack-invoke-suspended.test.ts +3 -2
  492. package/src/scenarios/wasm-pack-load.test.ts +4 -3
  493. package/src/scenarios/wasm-pack-memory-cap.test.ts +5 -4
  494. package/src/scenarios/wasm-pack-replay-determinism.test.ts +2 -1
  495. package/src/scenarios/webhook-negative.test.ts +11 -9
  496. package/src/scenarios/webhook-receiver-adversarial.test.ts +8 -7
  497. package/src/scenarios/webhook-sig-algorithm.test.ts +5 -3
  498. package/src/scenarios/webhook-signed-delivery.test.ts +16 -15
  499. package/src/scenarios/webhook-tenant-isolation.test.ts +16 -14
  500. package/src/scenarios/workflow-chain-deferred-parameters.test.ts +28 -30
  501. package/src/scenarios/workflow-chain-expansion.test.ts +21 -20
  502. package/src/scenarios/workflow-chain-host-expansion.test.ts +21 -20
  503. package/src/scenarios/workflow-chain-pack-manifest-validation.test.ts +14 -12
  504. package/src/scenarios/workflow-chain-pack-signature-verification.test.ts +5 -4
  505. package/src/scenarios/workflow-chain-unresolvable-typeid.test.ts +9 -8
  506. package/src/scenarios/workflow-primary-output-annotation.test.ts +7 -6
  507. package/src/scenarios/workflow-variable-format.test.ts +24 -24
  508. package/src/scenarios/workload-identity-behavior.test.ts +15 -13
  509. package/src/scenarios/workload-identity-chain-bounds.test.ts +18 -17
  510. package/src/scenarios/workspace-behavior.test.ts +19 -18
  511. package/src/scenarios/workspace-capability-shape.test.ts +3 -2
  512. package/src/scenarios/workspace-cross-tenant-isolation-blackbox.test.ts +6 -5
  513. package/src/scenarios/workspace-cross-tenant-isolation.test.ts +7 -6
  514. package/src/scenarios/x-openwop-form-pack-manifest.test.ts +8 -7
  515. package/src/setup.ts +44 -6
  516. package/vitest.config.ts +5 -1
  517. package/api/.redocly.lint-ignore.yaml +0 -22
  518. package/api/asyncapi.yaml +0 -834
  519. package/api/grpc/openwop.proto +0 -251
  520. package/api/openapi.yaml +0 -2693
  521. package/api/redocly.yaml +0 -23
  522. package/schemas/README.md +0 -144
  523. package/schemas/a2a-task-state.schema.json +0 -78
  524. package/schemas/a2ui-surface-delta-frame.schema.json +0 -48
  525. package/schemas/agent-deployment-transition.schema.json +0 -49
  526. package/schemas/agent-deployment.schema.json +0 -54
  527. package/schemas/agent-eval-suite.schema.json +0 -140
  528. package/schemas/agent-inventory-response.schema.json +0 -115
  529. package/schemas/agent-manifest.schema.json +0 -156
  530. package/schemas/agent-org-chart.schema.json +0 -82
  531. package/schemas/agent-ref.schema.json +0 -63
  532. package/schemas/agent-roster-entry.schema.json +0 -81
  533. package/schemas/agent-roster-response.schema.json +0 -21
  534. package/schemas/ai-envelope.schema.json +0 -134
  535. package/schemas/annotation-create.schema.json +0 -37
  536. package/schemas/annotation.schema.json +0 -56
  537. package/schemas/artifact-type-pack-manifest.schema.json +0 -244
  538. package/schemas/audit-verify-result.schema.json +0 -90
  539. package/schemas/budget-policy.schema.json +0 -18
  540. package/schemas/capabilities.schema.json +0 -4160
  541. package/schemas/certification-bundle-v2.schema.json +0 -118
  542. package/schemas/channel-presence-payload.schema.json +0 -41
  543. package/schemas/channel-written-payload.schema.json +0 -55
  544. package/schemas/chat-card-pack-manifest.schema.json +0 -253
  545. package/schemas/compact-tool-descriptor.schema.json +0 -51
  546. package/schemas/compensation-policy.schema.json +0 -64
  547. package/schemas/conformance-certification-bundle.schema.json +0 -86
  548. package/schemas/connection-pack-manifest.schema.json +0 -353
  549. package/schemas/conversation-event.schema.json +0 -168
  550. package/schemas/conversation-turn.schema.json +0 -117
  551. package/schemas/core-conformance-mock-agent-config.schema.json +0 -152
  552. package/schemas/credential-provenance.schema.json +0 -18
  553. package/schemas/credential-reference.schema.json +0 -21
  554. package/schemas/debug-bundle.schema.json +0 -196
  555. package/schemas/dispatch-config.schema.json +0 -112
  556. package/schemas/envelopes/clarification.request.schema.json +0 -52
  557. package/schemas/envelopes/error.schema.json +0 -30
  558. package/schemas/envelopes/media.audio.schema.json +0 -38
  559. package/schemas/envelopes/media.file.schema.json +0 -37
  560. package/schemas/envelopes/media.image.schema.json +0 -33
  561. package/schemas/envelopes/schema.request.schema.json +0 -26
  562. package/schemas/envelopes/schema.response.schema.json +0 -22
  563. package/schemas/envelopes/ui.a2ui-surface.schema.json +0 -154
  564. package/schemas/error-envelope.schema.json +0 -25
  565. package/schemas/eval-summary.schema.json +0 -92
  566. package/schemas/export-bundle.schema.json +0 -66
  567. package/schemas/form-content-pack-manifest.schema.json +0 -243
  568. package/schemas/frontend-plugin-manifest.schema.json +0 -109
  569. package/schemas/goal.schema.json +0 -104
  570. package/schemas/heartbeat-evaluated.schema.json +0 -14
  571. package/schemas/heartbeat-state-changed.schema.json +0 -14
  572. package/schemas/localized-content-language-settings.schema.json +0 -26
  573. package/schemas/localized-content-page-response.schema.json +0 -60
  574. package/schemas/localized-content-page.schema.json +0 -62
  575. package/schemas/localized-content-section.schema.json +0 -51
  576. package/schemas/memory-entry.schema.json +0 -36
  577. package/schemas/memory-list-options.schema.json +0 -37
  578. package/schemas/node-pack-manifest.schema.json +0 -654
  579. package/schemas/orchestrator-decision.schema.json +0 -90
  580. package/schemas/org-chart-responsibility-view.schema.json +0 -26
  581. package/schemas/pack-lockfile.schema.json +0 -108
  582. package/schemas/prompt-kind.schema.json +0 -8
  583. package/schemas/prompt-pack-manifest.schema.json +0 -117
  584. package/schemas/prompt-ref.schema.json +0 -40
  585. package/schemas/prompt-template.schema.json +0 -149
  586. package/schemas/proposal.schema.json +0 -84
  587. package/schemas/registry-version-manifest.schema.json +0 -208
  588. package/schemas/residency.schema.json +0 -16
  589. package/schemas/run-ancestry-response.schema.json +0 -54
  590. package/schemas/run-diff-response.schema.json +0 -64
  591. package/schemas/run-event-payloads.schema.json +0 -4452
  592. package/schemas/run-event.schema.json +0 -211
  593. package/schemas/run-options.schema.json +0 -80
  594. package/schemas/run-orchestrator-decided-event.schema.json +0 -20
  595. package/schemas/run-snapshot.schema.json +0 -148
  596. package/schemas/security-advisory.schema.json +0 -109
  597. package/schemas/self-hosted-runner-dispatch-frame.schema.json +0 -79
  598. package/schemas/self-hosted-runner-registration.schema.json +0 -53
  599. package/schemas/self-hosted-runner-result-frame.schema.json +0 -38
  600. package/schemas/subject.schema.json +0 -56
  601. package/schemas/suspend-request.schema.json +0 -207
  602. package/schemas/tool-descriptor.schema.json +0 -63
  603. package/schemas/trigger-event.schema.json +0 -222
  604. package/schemas/trigger-subscription-registration.schema.json +0 -67
  605. package/schemas/trigger-subscription.schema.json +0 -26
  606. package/schemas/ui-plugin-message.schema.json +0 -90
  607. package/schemas/workflow-chain-pack-manifest.schema.json +0 -615
  608. package/schemas/workflow-definition.schema.json +0 -850
  609. package/schemas/workload-identity.schema.json +0 -79
  610. package/schemas/workspace-file-create.schema.json +0 -20
  611. package/schemas/workspace-file.schema.json +0 -39
@@ -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);
@@ -30,8 +30,7 @@ import {
30
30
  ProducedVarProducerUnknownError,
31
31
  type WorkflowChain,
32
32
  } from '../lib/workflow-chain-expansion.js';
33
-
34
- const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
33
+ import { req } from '../lib/requirement-ids.js';
35
34
  const SPEC = 'workflow-chain-packs.md §"Produced (run-scoped) variables (RFC 0133)"';
36
35
 
37
36
  /** `generate` writes `plan`; `decompose` reads it via a variable binding. */
@@ -56,19 +55,19 @@ const PLAN_GEN: WorkflowChain = {
56
55
  describe('chain-produced-var-roundtrip: run-scoped variables (RFC 0133 §2, server-free)', () => {
57
56
  it('emits declared producedVariables into variables[] (name + type, NO value)', () => {
58
57
  const vars = emitProducedVariables(PLAN_GEN);
59
- expect(vars, why(SPEC, '§2.3 — one run-scoped variable emitted')).toEqual([{ name: 'plan', type: 'object' }]);
58
+ expect(vars, req('openwop.it.chain-produced-var-roundtrip.emits-declared-producedvariables-into-variables-name-type-no-value', SPEC, '§2.3 — one run-scoped variable emitted')).toEqual([{ name: 'plan', type: 'object' }]);
60
59
  // Run-scoped: NO author-time value rides the emitted entry.
61
- expect('value' in (vars[0] as object), why(SPEC, '§2.3 — no author-time value (distinct from parameters)')).toBe(
60
+ expect('value' in (vars[0] as object), req('openwop.it.chain-produced-var-roundtrip.emits-declared-producedvariables-into-variables-name-type-no-value', SPEC, '§2.3 — no author-time value (distinct from parameters)')).toBe(
62
61
  false,
63
62
  );
64
63
  expect(
65
64
  'defaultValue' in (vars[0] as object),
66
- why(SPEC, '§2.3 — no default value (a produced var is written during the run)'),
65
+ req('openwop.it.chain-produced-var-roundtrip.emits-declared-producedvariables-into-variables-name-type-no-value', SPEC, '§2.3 — no default value (a produced var is written during the run)'),
67
66
  ).toBe(false);
68
67
  });
69
68
 
70
69
  it('validates a variable read of a DECLARED producedVariables name', () => {
71
- expect(() => validateVariableReads(PLAN_GEN), why(SPEC, '§2.2 — declared read is closed-world valid')).not.toThrow();
70
+ expect(() => validateVariableReads(PLAN_GEN), req('openwop.it.chain-produced-var-roundtrip.validates-a-variable-read-of-a-declared-producedvariables-name', SPEC, '§2.2 — declared read is closed-world valid')).not.toThrow();
72
71
  });
73
72
 
74
73
  it('rejects a variable read of an UNDECLARED name (variable_undeclared)', () => {
@@ -86,14 +85,14 @@ describe('chain-produced-var-roundtrip: run-scoped variables (RFC 0133 §2, serv
86
85
  ],
87
86
  },
88
87
  };
89
- expect(() => validateVariableReads(undeclared), why(SPEC, '§2.2 — undeclared read MUST reject')).toThrow(
88
+ expect(() => validateVariableReads(undeclared), req('openwop.it.chain-produced-var-roundtrip.rejects-a-variable-read-of-an-undeclared-name-variable-undeclared', SPEC, '§2.2 — undeclared read MUST reject')).toThrow(
90
89
  VariableUndeclaredError,
91
90
  );
92
91
  try {
93
92
  validateVariableReads(undeclared);
94
93
  } catch (e) {
95
- expect((e as VariableUndeclaredError).code, why(SPEC, 'wire code variable_undeclared')).toBe('variable_undeclared');
96
- expect((e as VariableUndeclaredError).variableName, why(SPEC, 'names the offending variable')).toBe('plan');
94
+ expect((e as VariableUndeclaredError).code, req('openwop.it.chain-produced-var-roundtrip.rejects-a-variable-read-of-an-undeclared-name-variable-undeclared', SPEC, 'wire code variable_undeclared')).toBe('variable_undeclared');
95
+ expect((e as VariableUndeclaredError).variableName, req('openwop.it.chain-produced-var-roundtrip.rejects-a-variable-read-of-an-undeclared-name-variable-undeclared', SPEC, 'names the offending variable')).toBe('plan');
97
96
  }
98
97
  });
99
98
 
@@ -104,12 +103,12 @@ describe('chain-produced-var-roundtrip: run-scoped variables (RFC 0133 §2, serv
104
103
  };
105
104
  expect(
106
105
  () => validateVariableReads(badProducer),
107
- why(SPEC, '§2.2 — producedBy MUST be a real fragment node'),
106
+ req('openwop.it.chain-produced-var-roundtrip.rejects-a-producedvariables-whose-producedby-names-a-non-existent-node-produced', SPEC, '§2.2 — producedBy MUST be a real fragment node'),
108
107
  ).toThrow(ProducedVarProducerUnknownError);
109
108
  try {
110
109
  validateVariableReads(badProducer);
111
110
  } catch (e) {
112
- expect((e as ProducedVarProducerUnknownError).code, why(SPEC, 'distinct wire code')).toBe(
111
+ expect((e as ProducedVarProducerUnknownError).code, req('openwop.it.chain-produced-var-roundtrip.rejects-a-producedvariables-whose-producedby-names-a-non-existent-node-produced', SPEC, 'distinct wire code')).toBe(
113
112
  'produced_var_producer_unknown',
114
113
  );
115
114
  }
@@ -123,7 +122,7 @@ describe('chain-produced-var-roundtrip: run-scoped variables (RFC 0133 §2, serv
123
122
  };
124
123
  expect(
125
124
  () => validateVariableReads(collide),
126
- why(SPEC, '§2.2 — a produced var colliding with a parameter name MUST reject'),
125
+ req('openwop.it.chain-produced-var-roundtrip.rejects-a-producedvariables-name-that-collides-with-a-parameter-channels-must-be', SPEC, '§2.2 — a produced var colliding with a parameter name MUST reject'),
127
126
  ).toThrow(VariableUndeclaredError);
128
127
  });
129
128
 
@@ -141,12 +140,12 @@ describe('chain-produced-var-roundtrip: run-scoped variables (RFC 0133 §2, serv
141
140
  };
142
141
  expect(
143
142
  () => validateVariableReads(paramRead, new Set(['seed'])),
144
- why(SPEC, '§2.2 — a materialized-parameter read validates'),
143
+ req('openwop.it.chain-produced-var-roundtrip.validates-a-read-of-a-materialized-parameter-name-params-produced-vars-compose', SPEC, '§2.2 — a materialized-parameter read validates'),
145
144
  ).not.toThrow();
146
145
  // …but still rejects when the name is neither a produced var nor a param.
147
146
  expect(
148
147
  () => validateVariableReads(paramRead, new Set()),
149
- why(SPEC, '§2.2 — same read with no matching param still rejects'),
148
+ req('openwop.it.chain-produced-var-roundtrip.validates-a-read-of-a-materialized-parameter-name-params-produced-vars-compose', SPEC, '§2.2 — same read with no matching param still rejects'),
150
149
  ).toThrow(VariableUndeclaredError);
151
150
  });
152
151
  });
@@ -30,8 +30,7 @@ import {
30
30
  DEFAULT_MAX_SUB_CHAIN_DEPTH,
31
31
  type WorkflowChain,
32
32
  } from '../lib/workflow-chain-expansion.js';
33
-
34
- const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
33
+ import { req } from '../lib/requirement-ids.js';
35
34
  const SPEC = 'workflow-chain-packs.md §"Sub-chain composition (RFC 0133)"';
36
35
 
37
36
  /** A chain node that dispatches a named sub-chain ref. */
@@ -88,14 +87,14 @@ const ctxFor = (siblings: Map<string, WorkflowChain>, maxDepth?: number) => ({
88
87
  describe('chain-subchain-cycle-rejected: bounded recursion (RFC 0133, server-free)', () => {
89
88
  it('rejects a chain that transitively composes itself (sub_chain_cycle)', () => {
90
89
  const siblings = new Map([[selfRef.chainId, selfRef]]);
91
- expect(() => expandChainTree(selfRef, ctxFor(siblings)), why(SPEC, 'self-composition MUST reject')).toThrow(
90
+ expect(() => expandChainTree(selfRef, ctxFor(siblings)), req('openwop.it.chain-subchain-cycle-rejected.rejects-a-chain-that-transitively-composes-itself-sub-chain-cycle', SPEC, 'self-composition MUST reject')).toThrow(
92
91
  SubChainCycleError,
93
92
  );
94
93
  try {
95
94
  expandChainTree(selfRef, ctxFor(siblings));
96
95
  } catch (e) {
97
- expect((e as SubChainCycleError).code, why(SPEC, 'wire code sub_chain_cycle')).toBe('sub_chain_cycle');
98
- expect((e as SubChainCycleError).httpStatus, why(SPEC, 'HTTP 400')).toBe(400);
96
+ expect((e as SubChainCycleError).code, req('openwop.it.chain-subchain-cycle-rejected.rejects-a-chain-that-transitively-composes-itself-sub-chain-cycle', SPEC, 'wire code sub_chain_cycle')).toBe('sub_chain_cycle');
97
+ expect((e as SubChainCycleError).httpStatus, req('openwop.it.chain-subchain-cycle-rejected.rejects-a-chain-that-transitively-composes-itself-sub-chain-cycle', SPEC, 'HTTP 400')).toBe(400);
99
98
  }
100
99
  });
101
100
 
@@ -105,13 +104,13 @@ describe('chain-subchain-cycle-rejected: bounded recursion (RFC 0133, server-fre
105
104
  const { root, siblings } = linearChain(5);
106
105
  expect(
107
106
  () => expandChainTree(root, ctxFor(siblings, 2)),
108
- why(SPEC, 'depth breach MUST reject (bounded recursion)'),
107
+ req('openwop.it.chain-subchain-cycle-rejected.rejects-nesting-past-maxsubchaindepth-with-a-distinct-sub-chain-max-depth-exceed', SPEC, 'depth breach MUST reject (bounded recursion)'),
109
108
  ).toThrow(SubChainDepthExceededError);
110
109
  try {
111
110
  expandChainTree(root, ctxFor(siblings, 2));
112
111
  } catch (e) {
113
- expect((e as SubChainDepthExceededError).code, why(SPEC, 'distinct wire code')).toBe('sub_chain_max_depth_exceeded');
114
- expect(e instanceof SubChainCycleError, why(SPEC, 'a depth breach is NOT a cycle')).toBe(false);
112
+ expect((e as SubChainDepthExceededError).code, req('openwop.it.chain-subchain-cycle-rejected.rejects-nesting-past-maxsubchaindepth-with-a-distinct-sub-chain-max-depth-exceed', SPEC, 'distinct wire code')).toBe('sub_chain_max_depth_exceeded');
113
+ expect(e instanceof SubChainCycleError, req('openwop.it.chain-subchain-cycle-rejected.rejects-nesting-past-maxsubchaindepth-with-a-distinct-sub-chain-max-depth-exceed', SPEC, 'a depth breach is NOT a cycle')).toBe(false);
115
114
  }
116
115
  });
117
116
 
@@ -119,7 +118,7 @@ describe('chain-subchain-cycle-rejected: bounded recursion (RFC 0133, server-fre
119
118
  const { root, siblings } = linearChain(3);
120
119
  const { children } = expandChainTree(root, ctxFor(siblings, DEFAULT_MAX_SUB_CHAIN_DEPTH));
121
120
  // 3 composing links (link.0..link.2 each compose their successor) ⇒ 3 co-registered children.
122
- expect(children.length, why(SPEC, 'benign acyclic tree within the bound expands')).toBe(3);
121
+ expect(children.length, req('openwop.it.chain-subchain-cycle-rejected.expands-an-acyclic-tree-within-the-bound-cleanly-guard-rejects-bombs-not-benign', SPEC, 'benign acyclic tree within the bound expands')).toBe(3);
123
122
  });
124
123
 
125
124
  it('rejects a subChainRef that resolves to no sibling chain (sub_chain_unresolved)', () => {
@@ -135,7 +134,7 @@ describe('chain-subchain-cycle-rejected: bounded recursion (RFC 0133, server-fre
135
134
  const siblings = new Map([[orphan.chainId, orphan]]);
136
135
  expect(
137
136
  () => expandChainTree(orphan, ctxFor(siblings)),
138
- why(SPEC, '§1.1 — unresolvable ref rejects distinctly'),
137
+ req('openwop.it.chain-subchain-cycle-rejected.rejects-a-subchainref-that-resolves-to-no-sibling-chain-sub-chain-unresolved', SPEC, '§1.1 — unresolvable ref rejects distinctly'),
139
138
  ).toThrow(SubChainUnresolvedError);
140
139
  });
141
140
  });