@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,6 +34,8 @@ import Ajv2020 from 'ajv/dist/2020.js';
34
34
  import addFormats from 'ajv-formats';
35
35
  import type { ErrorObject } from 'ajv';
36
36
  import { SCHEMAS_DIR, V1_DIR } from '../lib/paths.js';
37
+ import { req } from '../lib/requirement-ids.js';
38
+ import { softSkip } from '../lib/soft-skip.js';
37
39
 
38
40
  const SCHEMA_PATH = join(SCHEMAS_DIR, 'workflow-chain-pack-manifest.schema.json');
39
41
  // In-repo example pack — proves the schema validates a non-trivial
@@ -100,7 +102,7 @@ describe('category: workflow-chain-pack manifest validation', () => {
100
102
  .join('\n');
101
103
  expect(
102
104
  ok,
103
- `Positive sample MUST validate against workflow-chain-pack-manifest.schema.json — got:\n${errs}`,
105
+ req('openwop.it.workflow-chain-pack-manifest-validation.positive-a-valid-workflow-chain-pack-manifest-validates-cleanly', 'workflow-chain-packs.md', `Positive sample MUST validate against workflow-chain-pack-manifest.schema.json — got:\n${errs}`),
104
106
  ).toBe(true);
105
107
  });
106
108
 
@@ -134,7 +136,7 @@ describe('category: workflow-chain-pack manifest validation', () => {
134
136
  };
135
137
  const ok = validate(conditionalChain);
136
138
  const errs = (validate.errors ?? []).map((e: ErrorObject) => `${e.instancePath || '/'}: ${e.message}`).join('\n');
137
- expect(ok, `Object-shaped edge condition MUST validate — got:\n${errs}`).toBe(true);
139
+ expect(ok, req('openwop.it.workflow-chain-pack-manifest-validation.positive-a-fragmentedge-condition-takes-the-top-level-edgecondition-shape-rfc-00', 'RFC 0013', `Object-shaped edge condition MUST validate — got:\n${errs}`)).toBe(true);
138
140
  });
139
141
 
140
142
  it('negative: a bare-string FragmentEdge condition is rejected (the pre-2026-07-03 shape)', () => {
@@ -160,7 +162,7 @@ describe('category: workflow-chain-pack manifest validation', () => {
160
162
  },
161
163
  ],
162
164
  };
163
- expect(validate(stringCondition), 'A string edge condition MUST NOT validate under the corrected schema').toBe(false);
165
+ expect(validate(stringCondition), req('openwop.it.workflow-chain-pack-manifest-validation.negative-a-bare-string-fragmentedge-condition-is-rejected-the-pre-2026-07-03-sha', 'workflow-chain-packs.md', 'A string edge condition MUST NOT validate under the corrected schema')).toBe(false);
164
166
  });
165
167
 
166
168
  it('negative: manifest mixing chains[] AND nodes[] is rejected (pack_kind_invalid)', () => {
@@ -192,14 +194,14 @@ describe('category: workflow-chain-pack manifest validation', () => {
192
194
  const ok = validate(manifest);
193
195
  expect(
194
196
  ok,
195
- 'Manifest with both nodes[] and chains[] MUST fail workflow-chain schema validation (pack_kind_invalid at the registry surface).',
197
+ req('openwop.it.workflow-chain-pack-manifest-validation.negative-manifest-mixing-chains-and-nodes-is-rejected-pack-kind-invalid', 'workflow-chain-packs.md', 'Manifest with both nodes[] and chains[] MUST fail workflow-chain schema validation (pack_kind_invalid at the registry surface).'),
196
198
  ).toBe(false);
197
199
  const hasAdditionalPropertiesErr = (validate.errors ?? []).some(
198
200
  (e: ErrorObject) => e.keyword === 'additionalProperties',
199
201
  );
200
202
  expect(
201
203
  hasAdditionalPropertiesErr,
202
- 'Expected an `additionalProperties` violation flagging the unexpected `nodes` field.',
204
+ req('openwop.it.workflow-chain-pack-manifest-validation.negative-manifest-mixing-chains-and-nodes-is-rejected-pack-kind-invalid', 'workflow-chain-packs.md', 'Expected an `additionalProperties` violation flagging the unexpected `nodes` field.'),
203
205
  ).toBe(true);
204
206
  });
205
207
 
@@ -228,7 +230,7 @@ describe('category: workflow-chain-pack manifest validation', () => {
228
230
  const ok = validate(manifest);
229
231
  expect(
230
232
  ok,
231
- 'Manifest with invalid chainId MUST fail workflow-chain schema validation.',
233
+ req('openwop.it.workflow-chain-pack-manifest-validation.negative-chain-entry-with-invalid-chainid-is-rejected-pattern-violation', 'workflow-chain-packs.md', 'Manifest with invalid chainId MUST fail workflow-chain schema validation.'),
232
234
  ).toBe(false);
233
235
  const hasPatternErr = (validate.errors ?? []).some(
234
236
  (e: ErrorObject) =>
@@ -236,14 +238,14 @@ describe('category: workflow-chain-pack manifest validation', () => {
236
238
  );
237
239
  expect(
238
240
  hasPatternErr,
239
- 'Expected a `pattern` violation on the chains[].chainId field.',
241
+ req('openwop.it.workflow-chain-pack-manifest-validation.negative-chain-entry-with-invalid-chainid-is-rejected-pattern-violation', 'workflow-chain-packs.md', 'Expected a `pattern` violation on the chains[].chainId field.'),
240
242
  ).toBe(true);
241
243
  });
242
244
 
243
245
  it('positive: the in-repo example pack at examples/packs/workflow-chain-sample/ validates against the schema', () => {
244
246
  if (!EXAMPLE_PACK_PATH || !existsSync(EXAMPLE_PACK_PATH)) {
245
247
  // Published-tarball layout doesn't ship examples/; skip cleanly.
246
- return;
248
+ return softSkip('blocked', 'precondition not met — `!EXAMPLE_PACK_PATH || !existsSync(EXAMPLE_PACK_PATH)` returned early (Published-tarball layout doesn\'t ship examples/; skip cleanly.) (seam, prior step, or fixture unavailable)');
247
249
  }
248
250
  const manifest = JSON.parse(readFileSync(EXAMPLE_PACK_PATH, 'utf8'));
249
251
  const ok = validate(manifest);
@@ -252,15 +254,15 @@ describe('category: workflow-chain-pack manifest validation', () => {
252
254
  .join('\n');
253
255
  expect(
254
256
  ok,
255
- `examples/packs/workflow-chain-sample/pack.json MUST validate against workflow-chain-pack-manifest.schema.json (closes RFC 0013 Phase 4 in-tree path). Errors:\n${errs}`,
257
+ req('openwop.it.workflow-chain-pack-manifest-validation.positive-the-in-repo-example-pack-at-examples-packs-workflow-chain-sample-valida', 'workflow-chain-packs.md', `examples/packs/workflow-chain-sample/pack.json MUST validate against workflow-chain-pack-manifest.schema.json (closes RFC 0013 Phase 4 in-tree path). Errors:\n${errs}`),
256
258
  ).toBe(true);
257
259
  // Spot-check the structural claims the example README makes:
258
- expect(manifest.kind, 'example pack MUST declare kind: "workflow-chain"').toBe(
260
+ expect(manifest.kind, req('openwop.it.workflow-chain-pack-manifest-validation.positive-the-in-repo-example-pack-at-examples-packs-workflow-chain-sample-valida', 'workflow-chain-packs.md', 'example pack MUST declare kind: "workflow-chain"')).toBe(
259
261
  'workflow-chain',
260
262
  );
261
263
  expect(
262
264
  Array.isArray(manifest.chains) && manifest.chains.length === 2,
263
- 'example pack MUST ship exactly 2 chains (1-node + 2-node shapes) per its README contract',
265
+ req('openwop.it.workflow-chain-pack-manifest-validation.positive-the-in-repo-example-pack-at-examples-packs-workflow-chain-sample-valida', 'workflow-chain-packs.md', 'example pack MUST ship exactly 2 chains (1-node + 2-node shapes) per its README contract'),
264
266
  ).toBe(true);
265
267
  });
266
268
 
@@ -285,7 +287,7 @@ describe('category: workflow-chain-pack manifest validation', () => {
285
287
  const ok = validate(manifest);
286
288
  expect(
287
289
  ok,
288
- 'Manifest without kind: "workflow-chain" MUST fail this schema (the other path is node-pack-manifest.schema.json).',
290
+ req('openwop.it.workflow-chain-pack-manifest-validation.negative-omitting-kind-field-rejects-kind-is-required', 'workflow-chain-packs.md', 'Manifest without kind: "workflow-chain" MUST fail this schema (the other path is node-pack-manifest.schema.json).'),
289
291
  ).toBe(false);
290
292
  });
291
293
  });
@@ -30,6 +30,7 @@
30
30
 
31
31
  import { describe, it, expect } from 'vitest';
32
32
  import { generateKeyPairSync, sign, verify } from 'node:crypto';
33
+ import { req } from '../lib/requirement-ids.js';
33
34
 
34
35
  /** Canonical workflow-chain pack manifest used as the signing target.
35
36
  * Mirrors the spec doc's Positive example, kept tight so the test
@@ -76,7 +77,7 @@ describe('category: workflow-chain pack signature — Ed25519 verification reuse
76
77
  const ok = verify(null, manifest, publicKey, signature);
77
78
  expect(
78
79
  ok,
79
- 'Per workflow-chain-packs.md §"Expansion semantics" step 2: the signature recipe is IDENTICAL to node-packs (Ed25519 over pack.json bytes). A valid pair MUST verify with the canonical recipe.',
80
+ req('openwop.it.workflow-chain-pack-signature-verification.valid-manifest-valid-signature-must-verify-positive-path', 'workflow-chain-packs.md', 'Per workflow-chain-packs.md §"Expansion semantics" step 2: the signature recipe is IDENTICAL to node-packs (Ed25519 over pack.json bytes). A valid pair MUST verify with the canonical recipe.'),
80
81
  ).toBe(true);
81
82
  });
82
83
 
@@ -92,7 +93,7 @@ describe('category: workflow-chain pack signature — Ed25519 verification reuse
92
93
  const ok = verify(null, tampered, publicKey, signature);
93
94
  expect(
94
95
  ok,
95
- 'Per node-packs.md §Signing (reused unchanged for chain packs): Ed25519 verification MUST detect any byte-level tamper to the signed payload.',
96
+ req('openwop.it.workflow-chain-pack-signature-verification.tampered-manifest-must-fail-verification-sha-level-tamper-detection', 'workflow-chain-packs.md', 'Per node-packs.md §Signing (reused unchanged for chain packs): Ed25519 verification MUST detect any byte-level tamper to the signed payload.'),
96
97
  ).toBe(false);
97
98
  });
98
99
 
@@ -108,7 +109,7 @@ describe('category: workflow-chain pack signature — Ed25519 verification reuse
108
109
  const ok = verify(null, manifest, attackerPubKey, signature);
109
110
  expect(
110
111
  ok,
111
- 'Ed25519 verification MUST fail when the public key doesn\'t match the private key that produced the signature — the spec\'s trust model depends on this property.',
112
+ req('openwop.it.workflow-chain-pack-signature-verification.wrong-key-signature-must-fail-verification', 'workflow-chain-packs.md', 'Ed25519 verification MUST fail when the public key doesn\'t match the private key that produced the signature — the spec\'s trust model depends on this property.'),
112
113
  ).toBe(false);
113
114
  });
114
115
 
@@ -130,7 +131,7 @@ describe('category: workflow-chain pack signature — Ed25519 verification reuse
130
131
  };
131
132
  expect(
132
133
  signedManifest.signing.method,
133
- 'workflow-chain packs MUST accept the same signing block keys as node packs (publicKeyRef + signatureRef + method) per spec design.',
134
+ req('openwop.it.workflow-chain-pack-signature-verification.chain-pack-manifests-carry-the-same-signing-block-shape-as-node-packs', 'workflow-chain-packs.md', 'workflow-chain packs MUST accept the same signing block keys as node packs (publicKeyRef + signatureRef + method) per spec design.'),
134
135
  ).toBe('manual');
135
136
  expect(signedManifest.signing.publicKeyRef).toBe('keys/2026-05.pem');
136
137
  expect(signedManifest.signing.signatureRef).toBe('pack.json.sig');
@@ -36,6 +36,7 @@ import {
36
36
  ChainUnresolvableTypeIdError,
37
37
  type WorkflowChain,
38
38
  } from '../lib/workflow-chain-expansion.js';
39
+ import { req } from '../lib/requirement-ids.js';
39
40
 
40
41
  const CHAIN_WITH_UNKNOWN_TYPEID: WorkflowChain = {
41
42
  chainId: 'vendor.acme.someChain',
@@ -66,7 +67,7 @@ describe('category: workflow-chain expansion — unresolvable typeId rejection',
66
67
  params: {},
67
68
  isTypeIdResolvable: isKnown,
68
69
  }),
69
- 'Per spec §"Expansion semantics" step 3: hosts MUST reject expansion with `chain_unresolvable_typeid` when any fragment node\'s typeId can\'t be resolved by the destination host.',
70
+ req('openwop.it.workflow-chain-unresolvable-typeid.throws-chainunresolvabletypeiderror-when-chain-references-an-unknown-typeid', 'workflow-chain-packs.md', 'Per spec §"Expansion semantics" step 3: hosts MUST reject expansion with `chain_unresolvable_typeid` when any fragment node\'s typeId can\'t be resolved by the destination host.'),
70
71
  ).toThrow(ChainUnresolvableTypeIdError);
71
72
  });
72
73
 
@@ -79,21 +80,21 @@ describe('category: workflow-chain expansion — unresolvable typeId rejection',
79
80
  });
80
81
  expect.fail('expandChain MUST have thrown ChainUnresolvableTypeIdError');
81
82
  } catch (err) {
82
- expect(err, 'expected ChainUnresolvableTypeIdError').toBeInstanceOf(
83
+ expect(err, req('openwop.it.workflow-chain-unresolvable-typeid.error-carries-the-offending-typeid-and-the-chainid-in-details', 'workflow-chain-packs.md', 'expected ChainUnresolvableTypeIdError')).toBeInstanceOf(
83
84
  ChainUnresolvableTypeIdError,
84
85
  );
85
86
  const e = err as ChainUnresolvableTypeIdError;
86
87
  expect(
87
88
  e.code,
88
- 'Per spec §"Error codes": the wire-level error code MUST be `chain_unresolvable_typeid`.',
89
+ req('openwop.it.workflow-chain-unresolvable-typeid.error-carries-the-offending-typeid-and-the-chainid-in-details', 'workflow-chain-packs.md', 'Per spec §"Error codes": the wire-level error code MUST be `chain_unresolvable_typeid`.'),
89
90
  ).toBe('chain_unresolvable_typeid');
90
91
  expect(
91
92
  e.typeId,
92
- 'The error MUST surface the offending typeId so the host editor can render an actionable diagnostic.',
93
+ req('openwop.it.workflow-chain-unresolvable-typeid.error-carries-the-offending-typeid-and-the-chainid-in-details', 'workflow-chain-packs.md', 'The error MUST surface the offending typeId so the host editor can render an actionable diagnostic.'),
93
94
  ).toBe('made.up.foo');
94
95
  expect(
95
96
  e.chainId,
96
- 'The error MUST surface the chainId so the host editor knows which chain-pack tile triggered the rejection.',
97
+ req('openwop.it.workflow-chain-unresolvable-typeid.error-carries-the-offending-typeid-and-the-chainid-in-details', 'workflow-chain-packs.md', 'The error MUST surface the chainId so the host editor knows which chain-pack tile triggered the rejection.'),
97
98
  ).toBe('vendor.acme.someChain');
98
99
  }
99
100
  });
@@ -115,7 +116,7 @@ describe('category: workflow-chain expansion — unresolvable typeId rejection',
115
116
  }
116
117
  expect(
117
118
  threw,
118
- 'Expansion MUST throw before producing any output — host editors rely on the "no partial expansion" guarantee to keep parent workflows clean on rejection.',
119
+ req('openwop.it.workflow-chain-unresolvable-typeid.rejection-happens-before-any-id-rewriting-or-placeholder-substitution', 'workflow-chain-packs.md', 'Expansion MUST throw before producing any output — host editors rely on the "no partial expansion" guarantee to keep parent workflows clean on rejection.'),
119
120
  ).toBe(true);
120
121
  });
121
122
 
@@ -134,7 +135,7 @@ describe('category: workflow-chain expansion — unresolvable typeId rejection',
134
135
  params: {},
135
136
  isTypeIdResolvable: isKnown,
136
137
  }),
137
- 'Sanity: when every typeId resolves, expansion MUST proceed without throwing.',
138
+ req('openwop.it.workflow-chain-unresolvable-typeid.accepts-the-chain-when-every-typeid-is-resolvable', 'workflow-chain-packs.md', 'Sanity: when every typeId resolves, expansion MUST proceed without throwing.'),
138
139
  ).not.toThrow();
139
140
  });
140
141
 
@@ -163,7 +164,7 @@ describe('category: workflow-chain expansion — unresolvable typeId rejection',
163
164
  const e = err as ChainUnresolvableTypeIdError;
164
165
  expect(
165
166
  e.typeId,
166
- 'When multiple nodes have unknown typeIds, the throw MUST identify the first unknown one encountered in declaration order.',
167
+ req('openwop.it.workflow-chain-unresolvable-typeid.throws-on-the-first-unknown-typeid-encountered-fail-fast', 'workflow-chain-packs.md', 'When multiple nodes have unknown typeIds, the throw MUST identify the first unknown one encountered in declaration order.'),
167
168
  ).toBe('made.up.foo');
168
169
  }
169
170
  });
@@ -23,6 +23,7 @@ import addFormats from 'ajv-formats';
23
23
  import { readFileSync, readdirSync } from 'node:fs';
24
24
  import { join } from 'node:path';
25
25
  import { SCHEMAS_DIR } from '../lib/paths.js';
26
+ import { req } from '../lib/requirement-ids.js';
26
27
 
27
28
  function compileWorkflowDefinition(): ReturnType<Ajv2020['compile']> {
28
29
  const ajv = new Ajv2020({ strict: false, allErrors: true });
@@ -78,7 +79,7 @@ describe('workflow-primary-output-annotation: outputRole shape (RFC 0065)', () =
78
79
  baseNode('b'),
79
80
  ]);
80
81
  const ok = validate(def);
81
- expect(ok, JSON.stringify(validate.errors, null, 2)).toBe(true);
82
+ expect(ok, req('openwop.it.workflow-primary-output-annotation.accepts-a-workflow-with-one-node-declaring-outputrole-primary', 'RFC 0065', JSON.stringify(validate.errors, null, 2))).toBe(true);
82
83
  });
83
84
 
84
85
  it('accepts primary AND secondary annotations on different nodes', () => {
@@ -88,7 +89,7 @@ describe('workflow-primary-output-annotation: outputRole shape (RFC 0065)', () =
88
89
  baseNode('c'),
89
90
  ]);
90
91
  const ok = validate(def);
91
- expect(ok, JSON.stringify(validate.errors, null, 2)).toBe(true);
92
+ expect(ok, req('openwop.it.workflow-primary-output-annotation.accepts-primary-and-secondary-annotations-on-different-nodes', 'RFC 0065', JSON.stringify(validate.errors, null, 2))).toBe(true);
92
93
  });
93
94
 
94
95
  it('accepts a workflow with the field absent (additive promise)', () => {
@@ -97,7 +98,7 @@ describe('workflow-primary-output-annotation: outputRole shape (RFC 0065)', () =
97
98
  baseNode('b'),
98
99
  ]);
99
100
  const ok = validate(def);
100
- expect(ok, JSON.stringify(validate.errors, null, 2)).toBe(true);
101
+ expect(ok, req('openwop.it.workflow-primary-output-annotation.accepts-a-workflow-with-the-field-absent-additive-promise', 'RFC 0065', JSON.stringify(validate.errors, null, 2))).toBe(true);
101
102
  });
102
103
 
103
104
  it('rejects an unknown outputRole enum value', () => {
@@ -105,7 +106,7 @@ describe('workflow-primary-output-annotation: outputRole shape (RFC 0065)', () =
105
106
  baseNode('a', { outputRole: 'tertiary' }),
106
107
  ]);
107
108
  const ok = validate(def);
108
- expect(ok).toBe(false);
109
+ expect(ok, req('openwop.it.workflow-primary-output-annotation.rejects-an-unknown-outputrole-enum-value', 'RFC 0065', 'rejects an unknown outputRole enum value')).toBe(false);
109
110
  expect(validate.errors).toBeTruthy();
110
111
  });
111
112
 
@@ -114,7 +115,7 @@ describe('workflow-primary-output-annotation: outputRole shape (RFC 0065)', () =
114
115
  baseNode('a', { outputRole: 1 }),
115
116
  ]);
116
117
  const ok = validate(def);
117
- expect(ok).toBe(false);
118
+ expect(ok, req('openwop.it.workflow-primary-output-annotation.rejects-outputrole-set-to-a-non-string', 'RFC 0065', 'rejects outputRole set to a non-string')).toBe(false);
118
119
  });
119
120
 
120
121
  it('permits multiple nodes declaring outputRole="primary" (tooling decides)', () => {
@@ -127,6 +128,6 @@ describe('workflow-primary-output-annotation: outputRole shape (RFC 0065)', () =
127
128
  baseNode('b', { outputRole: 'primary' }),
128
129
  ]);
129
130
  const ok = validate(def);
130
- expect(ok, JSON.stringify(validate.errors, null, 2)).toBe(true);
131
+ expect(ok, req('openwop.it.workflow-primary-output-annotation.permits-multiple-nodes-declaring-outputrole-primary-tooling-decides', 'RFC 0065', JSON.stringify(validate.errors, null, 2))).toBe(true);
131
132
  });
132
133
  });
@@ -48,8 +48,8 @@ import { behaviorGate } from '../lib/behavior-gate.js';
48
48
  import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
49
49
  import { isFixtureAdvertised } from '../lib/fixtures.js';
50
50
  import { driver } from '../lib/driver.js';
51
-
52
- const cite = (section: string, requirement: string): string => `${section} — ${requirement}`;
51
+ import { softSkip } from '../lib/soft-skip.js';
52
+ import { req } from '../lib/requirement-ids.js';
53
53
  const WORKFLOW_DEF = join(SCHEMAS_DIR, 'workflow-definition.schema.json');
54
54
  // S38 (2026-08-17): `spec/` is NOT in the published package (`files`), so a path built
55
55
  // from SCHEMAS_DIR/../spec ENOENTs for every npm consumer — five always-on legs reddened
@@ -83,11 +83,11 @@ describe('workflow-variable-format §A: corpus (RFC 0136, always-on)', () => {
83
83
  const wv = defs.WorkflowVariable;
84
84
  const props = wv.properties as Record<string, Record<string, unknown>>;
85
85
 
86
- expect(props.format, cite('§WorkflowVariable', '`format` is declared')).toBeDefined();
87
- expect(props.format.type, cite('§WorkflowVariable', '`format` is a string')).toBe('string');
86
+ expect(props.format, req('openwop.it.workflow-variable-format.a1-workflowvariable-declares-format-as-an-advisory-string', '§WorkflowVariable', '`format` is declared')).toBeDefined();
87
+ expect(props.format.type, req('openwop.it.workflow-variable-format.a1-workflowvariable-declares-format-as-an-advisory-string', '§WorkflowVariable', '`format` is a string')).toBe('string');
88
88
  expect(
89
89
  (wv.required as string[] | undefined)?.includes('format') ?? false,
90
- cite('§WorkflowVariable', '`format` is OPTIONAL — additive per COMPATIBILITY.md §2.1'),
90
+ req('openwop.it.workflow-variable-format.a1-workflowvariable-declares-format-as-an-advisory-string', '§WorkflowVariable', '`format` is OPTIONAL — additive per COMPATIBILITY.md §2.1'),
91
91
  ).toBe(false);
92
92
  });
93
93
 
@@ -98,7 +98,7 @@ describe('workflow-variable-format §A: corpus (RFC 0136, always-on)', () => {
98
98
  const recognised = { name: 'recipientEmail', type: 'string', format: 'email' };
99
99
  expect(
100
100
  validate(recognised),
101
- cite('§WorkflowVariable', `recognised format validates: ${JSON.stringify(validate.errors)}`),
101
+ req('openwop.it.workflow-variable-format.a2-an-unrecognised-format-validates-requirement-2-unknown-plain-text-never-an-er', '§WorkflowVariable', `recognised format validates: ${JSON.stringify(validate.errors)}`),
102
102
  ).toBe(true);
103
103
 
104
104
  // OUTSIDE the table. This is the whole point: the property must NOT be an enum, or a
@@ -107,7 +107,7 @@ describe('workflow-variable-format §A: corpus (RFC 0136, always-on)', () => {
107
107
  const unrecognised = { name: 'ipAddress', type: 'string', format: 'vendor.acme.ipv4-or-hostname' };
108
108
  expect(
109
109
  validate(unrecognised),
110
- cite('§WorkflowVariable', `unrecognised format validates (RFC 0136 req 2): ${JSON.stringify(validate.errors)}`),
110
+ req('openwop.it.workflow-variable-format.a2-an-unrecognised-format-validates-requirement-2-unknown-plain-text-never-an-er', '§WorkflowVariable', `unrecognised format validates (RFC 0136 req 2): ${JSON.stringify(validate.errors)}`),
111
111
  ).toBe(true);
112
112
  });
113
113
 
@@ -116,21 +116,21 @@ describe('workflow-variable-format §A: corpus (RFC 0136, always-on)', () => {
116
116
  const both = { name: 'notifyAddress', type: 'string', format: 'email', sensitive: true };
117
117
  expect(
118
118
  validate(both),
119
- cite('§WorkflowVariable', `format + sensitive compose: ${JSON.stringify(validate.errors)}`),
119
+ req('openwop.it.workflow-variable-format.a3-format-and-sensitive-compose-on-one-variable-no-interaction', '§WorkflowVariable', `format + sensitive compose: ${JSON.stringify(validate.errors)}`),
120
120
  ).toBe(true);
121
121
  });
122
122
 
123
123
  it.skipIf(CHAIN_DOC === null)('A4 — chain-pack spec documents deferred-mode `format` propagation as mode-scoped', () => {
124
124
  const doc = readFileSync(CHAIN_DOC as string, 'utf8');
125
125
  const step1 = doc.slice(doc.indexOf('Materialize parameters as variables'));
126
- expect(step1.length > 0, cite('§Deferred-parameter expansion', 'step 1 present')).toBe(true);
126
+ expect(step1.length > 0, req('openwop.it.workflow-variable-format.a4-chain-pack-spec-documents-deferred-mode-format-propagation-as-mode-scoped', '§Deferred-parameter expansion', 'step 1 present')).toBe(true);
127
127
  expect(
128
128
  /`format`/.test(step1.slice(0, 1400)),
129
- cite('§Deferred-parameter expansion', 'step 1 copy-list names `format`'),
129
+ req('openwop.it.workflow-variable-format.a4-chain-pack-spec-documents-deferred-mode-format-propagation-as-mode-scoped', '§Deferred-parameter expansion', 'step 1 copy-list names `format`'),
130
130
  ).toBe(true);
131
131
  expect(
132
132
  /this mode only|mode only/i.test(step1.slice(0, 1400)),
133
- cite('§Deferred-parameter expansion', 'the `format` MUST is scoped to deferred mode, not universal'),
133
+ req('openwop.it.workflow-variable-format.a4-chain-pack-spec-documents-deferred-mode-format-propagation-as-mode-scoped', '§Deferred-parameter expansion', 'the `format` MUST is scoped to deferred mode, not universal'),
134
134
  ).toBe(true);
135
135
  });
136
136
 
@@ -138,7 +138,7 @@ describe('workflow-variable-format §A: corpus (RFC 0136, always-on)', () => {
138
138
  const rfc = readFileSync(RFC_DOC as string, 'utf8');
139
139
  expect(
140
140
  /configurableSchema/.test(rfc),
141
- cite('RFC 0136', 'names the configurableSchema propagation path'),
141
+ req('openwop.it.workflow-variable-format.a5-the-rfc-forbids-format-participating-in-a-configurable-validation-decision', 'RFC 0136', 'names the configurableSchema propagation path'),
142
142
  ).toBe(true);
143
143
  // The trap: run-options.md §1 makes validating `configurable` against
144
144
  // `configurableSchema` a MUST with `validation_error` on failure. A format-asserting
@@ -147,7 +147,7 @@ describe('workflow-variable-format §A: corpus (RFC 0136, always-on)', () => {
147
147
  // closes it: annotation permitted, assertion forbidden.
148
148
  expect(
149
149
  /requirement 3[\s\S]{0,600}(back door|surface-independent)/i.test(rfc),
150
- cite('RFC 0136 req 8', 'states requirement 3 is surface-independent'),
150
+ req('openwop.it.workflow-variable-format.a5-the-rfc-forbids-format-participating-in-a-configurable-validation-decision', 'RFC 0136 req 8', 'states requirement 3 is surface-independent'),
151
151
  ).toBe(true);
152
152
  });
153
153
  });
@@ -171,7 +171,7 @@ describe('workflow-variable-format §B: host behaviour (RFC 0136, capability-gat
171
171
  }
172
172
 
173
173
  it('B1 — deferred expansion mints `format` onto the WorkflowVariable: string copied (req 7), unknown copied (req 2), non-string omits it (req 1)', async () => {
174
- if (!(await deferredParamsGateOpen())) return;
174
+ if (!(await deferredParamsGateOpen())) return softSkip('blocked', 'precondition not met — `!(await deferredParamsGateOpen())` returned early (seam, prior step, or fixture unavailable)');
175
175
 
176
176
  // Drive the RFC 0124 deferred-expand seam with a chain whose parameters exercise all
177
177
  // three propagation rules in one expansion; assert the minted variables[] carry (or omit)
@@ -190,15 +190,15 @@ describe('workflow-variable-format §B: host behaviour (RFC 0136, capability-gat
190
190
  });
191
191
  // A host advertising deferredParameters but not yet serving the variables[]-returning
192
192
  // seam extension soft-skips (404), as does a host with the seam disabled in this boot.
193
- if (res.status === 404) return;
194
- expect(res.status, cite('§Deferred-parameter expansion', 'the deferred-expand seam returns 200')).toBe(200);
193
+ if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (A host advertising deferredParameters but not yet serving the variables[]-returning seam extension soft-skips (404), as does a host with the seam disabled in…');
194
+ expect(res.status, req('openwop.it.workflow-variable-format.b1-deferred-expansion-mints-format-onto-the-workflowvariable-string-copied-req-7', '§Deferred-parameter expansion', 'the deferred-expand seam returns 200')).toBe(200);
195
195
  const variables = (res.json as { variables?: Array<{ name: string; type?: string; format?: string }> }).variables;
196
- if (!Array.isArray(variables)) return; // seam present but not returning variables[] yet — soft-skip
196
+ if (!Array.isArray(variables)) return softSkip('blocked', 'precondition not met — `!Array.isArray(variables)` returned early (seam present but not returning variables[] yet — soft-skip) (seam, prior step, or fixture unavailable)'); // seam present but not returning variables[] yet — soft-skip
197
197
  const fmt = (n: string): string | undefined => variables.find((v) => v.name === n)?.format;
198
198
 
199
- expect(fmt('email'), cite('§WorkflowVariable', 'req 7: a string param\'s recognised `format` is minted verbatim')).toBe('email');
200
- expect(fmt('note'), cite('§WorkflowVariable', 'req 2: an UNRECOGNISED `format` is minted verbatim, never dropped')).toBe('vendor.acme.freeform');
201
- expect(fmt('count'), cite('§WorkflowVariable', 'req 1: a NON-STRING param mints no `format`')).toBeUndefined();
199
+ expect(fmt('email'), req('openwop.it.workflow-variable-format.b1-deferred-expansion-mints-format-onto-the-workflowvariable-string-copied-req-7', '§WorkflowVariable', 'req 7: a string param\'s recognised `format` is minted verbatim')).toBe('email');
200
+ expect(fmt('note'), req('openwop.it.workflow-variable-format.b1-deferred-expansion-mints-format-onto-the-workflowvariable-string-copied-req-7', '§WorkflowVariable', 'req 2: an UNRECOGNISED `format` is minted verbatim, never dropped')).toBe('vendor.acme.freeform');
201
+ expect(fmt('count'), req('openwop.it.workflow-variable-format.b1-deferred-expansion-mints-format-onto-the-workflowvariable-string-copied-req-7', '§WorkflowVariable', 'req 1: a NON-STRING param mints no `format`')).toBeUndefined();
202
202
  });
203
203
 
204
204
  const B2_FIXTURE = 'conformance-workflow-variable-format-advisory';
@@ -210,13 +210,13 @@ describe('workflow-variable-format §B: host behaviour (RFC 0136, capability-gat
210
210
  // endpoint, since registration is "POST /v1/workflows or equivalent" (capabilities.md)
211
211
  // and the create path is not a mandated portable surface. Gate on fixture advertisement:
212
212
  // req 3 is universal (not deferred-mode-specific), so it rides the host loading the fixture.
213
- if (!isFixtureAdvertised(B2_FIXTURE)) return;
213
+ if (!isFixtureAdvertised(B2_FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(B2_FIXTURE)` returned early (Portable pre-registered-workflow pattern (cf. agentPackHandoffSchemaValidation): the fixture `conformance-wor…');
214
214
 
215
215
  const run = await driver.post('/v1/runs', { workflowId: B2_FIXTURE });
216
- if (run.status === 404) return; // no run surface — soft-skip
216
+ if (run.status === 404) return softSkip('blocked', 'precondition not met — `run.status === 404` returned early (no run surface — soft-skip) (seam, prior step, or fixture unavailable)'); // no run surface — soft-skip
217
217
  expect(
218
218
  [200, 201],
219
- cite('POST /v1/runs', 'req 3: a run whose variable value violates its advisory `format` is accepted, not rejected'),
219
+ req('openwop.it.workflow-variable-format.b2-a-run-whose-variable-value-does-not-match-its-declared-format-is-accepted-and', 'POST /v1/runs', 'req 3: a run whose variable value violates its advisory `format` is accepted, not rejected'),
220
220
  ).toContain(run.status);
221
221
  const runId = (run.json as { runId: string }).runId;
222
222
 
@@ -232,7 +232,7 @@ describe('workflow-variable-format §B: host behaviour (RFC 0136, capability-gat
232
232
  }
233
233
  expect(
234
234
  snap?.status,
235
- cite('§WorkflowVariable', 'req 3: the run COMPLETES — `format` is advisory, a value mismatch MUST NOT fail the run'),
235
+ req('openwop.it.workflow-variable-format.b2-a-run-whose-variable-value-does-not-match-its-declared-format-is-accepted-and', '§WorkflowVariable', 'req 3: the run COMPLETES — `format` is advisory, a value mismatch MUST NOT fail the run'),
236
236
  ).toBe('completed');
237
237
  });
238
238
  });
@@ -37,6 +37,8 @@ import { readErrorCode, readRetriable } from '../lib/error-envelope.js';
37
37
  import { driver } from '../lib/driver.js';
38
38
  import { behaviorGate } from '../lib/behavior-gate.js';
39
39
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
40
+ import { req } from '../lib/requirement-ids.js';
41
+ import { softSkip } from '../lib/soft-skip.js';
40
42
 
41
43
  const PROFILE = 'openwop-workload-identity';
42
44
  /**
@@ -88,7 +90,7 @@ describe('RFC 0154 §A — workload identity resolution (capability-gated behavi
88
90
  const r = await resolve({ identity: { ...IDENTITY, audience: 'openwop-host' } });
89
91
  expect(
90
92
  r,
91
- driver.describe(
93
+ req('openwop.it.workload-identity-behavior.the-seam-is-wired-or-the-requirement-is-blocked-rather-than-skipped',
92
94
  'spec/v1/host-sample-test-seams.md §20',
93
95
  'a host advertising `auth.workloadIdentity` MUST expose the resolution seam. §A\'s ' +
94
96
  'requirements — verify, bind, resolve-before-authorize, fail closed — are invisible from ' +
@@ -102,21 +104,21 @@ describe('RFC 0154 §A — workload identity resolution (capability-gated behavi
102
104
  it('a verified identity resolves to a principal', async () => {
103
105
  if (!behaviorGate(PROFILE, (await caps())?.supported === true)) return;
104
106
  const r = await resolve({ identity: { ...IDENTITY, audience: 'openwop-host' } });
105
- if (r === null) return;
106
- expect(r.status, driver.describe('RFCS/0154 §A', 'a verified identity resolves')).toBe(200);
107
+ if (r === null) return softSkip('blocked', 'precondition not met — `r === null` returned early (seam, prior step, or fixture unavailable)');
108
+ expect(r.status, req('openwop.it.workload-identity-behavior.a-verified-identity-resolves-to-a-principal', 'RFCS/0154 §A', 'a verified identity resolves')).toBe(200);
107
109
  const principalId = (r.json as { principalId?: string }).principalId;
108
- expect(principalId, driver.describe('RFCS/0154 §A', 'resolution yields an OpenWOP principal')).toBeTruthy();
110
+ expect(principalId, req('openwop.it.workload-identity-behavior.a-verified-identity-resolves-to-a-principal', 'RFCS/0154 §A', 'resolution yields an OpenWOP principal')).toBeTruthy();
109
111
  });
110
112
 
111
113
  it('an identity for another audience is rejected', async () => {
112
114
  if (!behaviorGate(PROFILE, (await caps())?.supported === true)) return;
113
115
  const r = await resolve({ identity: { ...IDENTITY, audience: 'some-other-host' }, expectedAudience: 'openwop-host' });
114
- if (r === null) return;
116
+ if (r === null) return softSkip('blocked', 'precondition not met — `r === null` returned early (seam, prior step, or fixture unavailable)');
115
117
  // The load-bearing negative. A host that echoes its input passes every
116
118
  // positive assertion; only this distinguishes a verifier from a passthrough.
117
119
  expect(
118
120
  r.status >= 400,
119
- driver.describe(
121
+ req('openwop.it.workload-identity-behavior.an-identity-for-another-audience-is-rejected',
120
122
  'RFCS/0154 §A + RFC 0147 R12',
121
123
  'an identity minted for another host, accepted here, is how a credential valid elsewhere ' +
122
124
  'becomes a credential valid here — the confused-deputy path this profile prevents',
@@ -140,10 +142,10 @@ describe('RFC 0154 §A — workload identity resolution (capability-gated behavi
140
142
  },
141
143
  },
142
144
  });
143
- if (r === null) return;
145
+ if (r === null) return softSkip('blocked', 'precondition not met — `r === null` returned early (seam, prior step, or fixture unavailable)');
144
146
  expect(
145
147
  r.status >= 400,
146
- driver.describe(
148
+ req('openwop.it.workload-identity-behavior.an-expired-delegation-is-rejected',
147
149
  'RFCS/0154 §B',
148
150
  'a delegation without a live expiry is a standing grant, which is not what delegation means',
149
151
  ),
@@ -154,13 +156,13 @@ describe('RFC 0154 §A — workload identity resolution (capability-gated behavi
154
156
  it('a failure is non-retriable and carries a closed reason code', async () => {
155
157
  if (!behaviorGate(PROFILE, (await caps())?.supported === true)) return;
156
158
  const r = await resolve({ identity: { scheme: 'spiffe', subject: 'spiffe://example/unknown' } });
157
- if (r === null || r.status < 400) return;
159
+ if (r === null || r.status < 400) return softSkip('blocked', 'precondition not met — `r === null || r.status < 400` returned early (seam, prior step, or fixture unavailable)');
158
160
  // canonical flat envelope: code = `error`, retriable = `details.retriable`
159
161
  // (the legacy nested `error.{code,retriable}` the §20 catalog prescribed until
160
162
  // 2026-08-16 is tolerated by the helpers for the deprecation window)
161
163
  expect(
162
164
  readRetriable(r.json),
163
- driver.describe(
165
+ req('openwop.it.workload-identity-behavior.a-failure-is-non-retriable-and-carries-a-closed-reason-code',
164
166
  'spec/v1/host-sample-test-seams.md §20',
165
167
  'an identity that does not resolve will not resolve on retry. Marking it retriable invites ' +
166
168
  'a caller to hammer a failing authorization path.',
@@ -168,19 +170,19 @@ describe('RFC 0154 §A — workload identity resolution (capability-gated behavi
168
170
  ).toBe(false);
169
171
  expect(
170
172
  ['identity_unverified', 'identity_unresolvable', 'audience_mismatch', 'delegation_expired', 'sender_constraint_missing', 'delegation_chain_too_long', 'delegation_chain_cyclic', 'delegation_scope_amplified'],
171
- driver.describe('RFCS/0154 §A', 'failures use a closed reason vocabulary'),
173
+ req('openwop.it.workload-identity-behavior.a-failure-is-non-retriable-and-carries-a-closed-reason-code', 'RFCS/0154 §A', 'failures use a closed reason vocabulary'),
172
174
  ).toContain(readErrorCode(r.json));
173
175
  });
174
176
 
175
177
  it('a resolution response carries no credential material', async () => {
176
178
  if (!behaviorGate(PROFILE, (await caps())?.supported === true)) return;
177
179
  const r = await resolve({ identity: { ...IDENTITY, audience: 'openwop-host' } });
178
- if (r === null) return;
180
+ if (r === null) return softSkip('blocked', 'precondition not met — `r === null` returned early (seam, prior step, or fixture unavailable)');
179
181
  const serialized = JSON.stringify(r.json ?? {});
180
182
  for (const forbidden of ['-----BEGIN', 'Bearer ', 'eyJ']) {
181
183
  expect(
182
184
  serialized.includes(forbidden),
183
- driver.describe(
185
+ req('openwop.it.workload-identity-behavior.a-resolution-response-carries-no-credential-material',
184
186
  'RFCS/0154 §A',
185
187
  'a verified identity is a fact about a completed verification, not a container for the ' +
186
188
  `material that proved it. Found: ${forbidden}`,
@@ -36,7 +36,8 @@ import { readErrorCode, readRetriable } from '../lib/error-envelope.js';
36
36
  import { driver } from '../lib/driver.js';
37
37
  import { behaviorGate } from '../lib/behavior-gate.js';
38
38
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
39
- import { seamAbsent } from '../lib/soft-skip.js';
39
+ import { seamAbsent, softSkip } from '../lib/soft-skip.js';
40
+ import { req } from '../lib/requirement-ids.js';
40
41
 
41
42
  const DELEGATION_PROFILE = 'openwop-workload-identity-delegation';
42
43
  const SEAM = '/v1/host/sample/test/workload-identity/resolve';
@@ -76,10 +77,10 @@ function hop(n: number, scopes?: readonly string[]): Record<string, unknown> {
76
77
  }
77
78
 
78
79
  /** A refusal: 4xx, non-retriable, the named closed reason. */
79
- function expectRefusal(r: { status: number; json: unknown }, reason: string, why: string): void {
80
- expect(r.status >= 400, driver.describe('RFCS/0154 §B', why)).toBe(true);
81
- expect(readErrorCode(r.json), driver.describe('spec/v1/host-sample-test-seams.md §20', `closed reason \`${reason}\``)).toBe(reason);
82
- expect(readRetriable(r.json), driver.describe('spec/v1/host-sample-test-seams.md §20', 'a chain the host refuses will be refused on retry')).toBe(false);
80
+ function expectRefusal(requirementId: string, r: { status: number; json: unknown }, reason: string, why: string): void {
81
+ expect(r.status >= 400, req(requirementId, 'RFCS/0154 §B', why)).toBe(true);
82
+ expect(readErrorCode(r.json), req(requirementId, 'spec/v1/host-sample-test-seams.md §20', `closed reason \`${reason}\``)).toBe(reason);
83
+ expect(readRetriable(r.json), req(requirementId, 'spec/v1/host-sample-test-seams.md §20', 'a chain the host refuses will be refused on retry')).toBe(false);
83
84
  }
84
85
 
85
86
  describe('RFC 0154 §B — delegation chain bounds (capability-gated behavior)', () => {
@@ -88,13 +89,13 @@ describe('RFC 0154 §B — delegation chain bounds (capability-gated behavior)',
88
89
  if (!behaviorGate(DELEGATION_PROFILE, caps.supported)) return;
89
90
  expect(
90
91
  caps.maxChainDepth,
91
- driver.describe('spec/v1/auth.md §Bounds', 'a host advertising delegation MUST advertise a positive integer `maxChainDepth`'),
92
+ req('openwop.it.workload-identity-chain-bounds.a-chain-longer-than-the-advertised-maxchaindepth-is-refused', 'spec/v1/auth.md §Bounds', 'a host advertising delegation MUST advertise a positive integer `maxChainDepth`'),
92
93
  ).not.toBeNull();
93
94
  const depth = caps.maxChainDepth as number;
94
95
  const chain = Array.from({ length: depth + 1 }, (_, i) => hop(i + 1));
95
96
  const r = await resolve({ identity: { ...IDENTITY, delegation: { chain, audience: 'openwop-host', expiresAt: LIVE } }, expectedAudience: 'openwop-host' });
96
- if (r === null) return;
97
- expectRefusal(r, 'delegation_chain_too_long', `a chain of ${depth + 1} hops exceeds the advertised bound of ${depth} — each hop is another party the host trusts transitively`);
97
+ if (r === null) return softSkip('blocked', 'precondition not met — `r === null` returned early (seam, prior step, or fixture unavailable)');
98
+ expectRefusal('openwop.it.workload-identity-chain-bounds.a-chain-longer-than-the-advertised-maxchaindepth-is-refused', r, 'delegation_chain_too_long', `a chain of ${depth + 1} hops exceeds the advertised bound of ${depth} — each hop is another party the host trusts transitively`);
98
99
  });
99
100
 
100
101
  it('a chain that revisits a subject is refused as cyclic', async () => {
@@ -106,15 +107,15 @@ describe('RFC 0154 §B — delegation chain bounds (capability-gated behavior)',
106
107
  // and this leg accepts either reason, saying so.
107
108
  const chain = [hop(1), hop(2), hop(1)];
108
109
  const r = await resolve({ identity: { ...IDENTITY, delegation: { chain, audience: 'openwop-host', expiresAt: LIVE } }, expectedAudience: 'openwop-host' });
109
- if (r === null) return;
110
+ if (r === null) return softSkip('blocked', 'precondition not met — `r === null` returned early (seam, prior step, or fixture unavailable)');
110
111
  const bound = caps.maxChainDepth ?? Number.POSITIVE_INFINITY;
111
112
  if (bound < 3) {
112
- expect(r.status >= 400, driver.describe('RFCS/0154 §B', 'a cyclic chain is refused')).toBe(true);
113
- expect(['delegation_chain_cyclic', 'delegation_chain_too_long'], driver.describe('spec/v1/host-sample-test-seams.md §20', 'cyclic or too-long — the bound is below the cycle length')).toContain(readErrorCode(r.json));
113
+ expect(r.status >= 400, req('openwop.it.workload-identity-chain-bounds.a-chain-that-revisits-a-subject-is-refused-as-cyclic', 'RFCS/0154 §B', 'a cyclic chain is refused')).toBe(true);
114
+ expect(['delegation_chain_cyclic', 'delegation_chain_too_long'], req('openwop.it.workload-identity-chain-bounds.a-chain-that-revisits-a-subject-is-refused-as-cyclic', 'spec/v1/host-sample-test-seams.md §20', 'cyclic or too-long — the bound is below the cycle length')).toContain(readErrorCode(r.json));
114
115
  expect(readRetriable(r.json)).toBe(false);
115
- return;
116
+ return softSkip('blocked', 'precondition not met — `bound < 3` returned early (seam, prior step, or fixture unavailable)');
116
117
  }
117
- expectRefusal(r, 'delegation_chain_cyclic', 'a subject appearing twice is a chain that loops back through authority it already spent — unbounded laundering with a bounded length');
118
+ expectRefusal('openwop.it.workload-identity-chain-bounds.a-chain-that-revisits-a-subject-is-refused-as-cyclic', r, 'delegation_chain_cyclic', 'a subject appearing twice is a chain that loops back through authority it already spent — unbounded laundering with a bounded length');
118
119
  });
119
120
 
120
121
  it('a later hop claiming a scope the previous hop did not hold is refused', async () => {
@@ -122,8 +123,8 @@ describe('RFC 0154 §B — delegation chain bounds (capability-gated behavior)',
122
123
  if (!behaviorGate(DELEGATION_PROFILE, caps.supported)) return;
123
124
  const chain = [hop(1, ['runs:read']), hop(2, ['runs:read', 'runs:write'])];
124
125
  const r = await resolve({ identity: { ...IDENTITY, delegation: { chain, audience: 'openwop-host', expiresAt: LIVE } }, expectedAudience: 'openwop-host' });
125
- if (r === null) return;
126
- expectRefusal(r, 'delegation_scope_amplified', 'the effective scopes at any hop MUST NOT exceed the hop before it — a chain is provenance, and provenance cannot mint `runs:write` from `runs:read`');
126
+ if (r === null) return softSkip('blocked', 'precondition not met — `r === null` returned early (seam, prior step, or fixture unavailable)');
127
+ expectRefusal('openwop.it.workload-identity-chain-bounds.a-later-hop-claiming-a-scope-the-previous-hop-did-not-hold-is-refused', r, 'delegation_scope_amplified', 'the effective scopes at any hop MUST NOT exceed the hop before it — a chain is provenance, and provenance cannot mint `runs:write` from `runs:read`');
127
128
  });
128
129
 
129
130
  it('a well-formed bounded, acyclic, non-amplifying chain still resolves (the negatives are not a blanket refusal)', async () => {
@@ -131,11 +132,11 @@ describe('RFC 0154 §B — delegation chain bounds (capability-gated behavior)',
131
132
  if (!behaviorGate(DELEGATION_PROFILE, caps.supported)) return;
132
133
  const chain = [hop(1, ['runs:read', 'manifest:read']), hop(2, ['runs:read'])];
133
134
  const r = await resolve({ identity: { ...IDENTITY, delegation: { chain, audience: 'openwop-host', expiresAt: LIVE } }, expectedAudience: 'openwop-host' });
134
- if (r === null) return;
135
+ if (r === null) return softSkip('blocked', 'precondition not met — `r === null` returned early (seam, prior step, or fixture unavailable)');
135
136
  // A host that refuses EVERY chain passes the three negatives vacuously; this
136
137
  // is the positive that keeps them honest. Scopes narrow hop-to-hop, which
137
138
  // is the one direction §B permits.
138
- expect(r.status, driver.describe('RFCS/0154 §B', 'a compliant chain resolves; the bounds refuse laundering, not delegation')).toBe(200);
139
+ expect(r.status, req('openwop.it.workload-identity-chain-bounds.a-well-formed-bounded-acyclic-non-amplifying-chain-still-resolves-the-negatives', 'RFCS/0154 §B', 'a compliant chain resolves; the bounds refuse laundering, not delegation')).toBe(200);
139
140
  expect((r.json as { resolved?: unknown }).resolved).toBe(true);
140
141
  });
141
142
  });