@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
@@ -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
  });
@@ -24,6 +24,7 @@ import { describe, it, expect } from 'vitest';
24
24
  import { driver } from '../lib/driver.js';
25
25
  import { softSkip, seamAbsent } from '../lib/soft-skip.js';
26
26
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
27
+ import { req } from '../lib/requirement-ids.js';
27
28
 
28
29
  interface DiscoveryWorkspace {
29
30
  supported?: boolean;
@@ -54,26 +55,26 @@ describe('workspace-behavior: §C CRUD round-trip (RFC 0059)', () => {
54
55
  const path = 'behavior/CRUD.md';
55
56
 
56
57
  const created = await driver.put(`${FILES}/${encodeURIComponent(path)}`, { content: 'first body' });
57
- expect(created.status, driver.describe('agent-workspace.md §C PUT', 'create MUST return 200')).toBe(200);
58
+ expect(created.status, req('openwop.it.workspace-behavior.put-creates-v1-get-returns-it-list-omits-content-delete-removes-it', 'agent-workspace.md §C PUT', 'create MUST return 200')).toBe(200);
58
59
  const file = created.json as WorkspaceFile;
59
- expect(file.version, driver.describe('agent-workspace.md §File model', 'version MUST start at 1')).toBe(1);
60
- expect(typeof file.etag, driver.describe('agent-workspace.md §File model', 'PUT MUST return an etag')).toBe('string');
60
+ expect(file.version, req('openwop.it.workspace-behavior.put-creates-v1-get-returns-it-list-omits-content-delete-removes-it', 'agent-workspace.md §File model', 'version MUST start at 1')).toBe(1);
61
+ expect(typeof file.etag, req('openwop.it.workspace-behavior.put-creates-v1-get-returns-it-list-omits-content-delete-removes-it', 'agent-workspace.md §File model', 'PUT MUST return an etag')).toBe('string');
61
62
 
62
63
  const got = await driver.get(`${FILES}/${encodeURIComponent(path)}`);
63
- expect((got.json as WorkspaceFile).content, driver.describe('agent-workspace.md §C GET', 'GET MUST return the content')).toBe('first body');
64
+ expect((got.json as WorkspaceFile).content, req('openwop.it.workspace-behavior.put-creates-v1-get-returns-it-list-omits-content-delete-removes-it', 'agent-workspace.md §C GET', 'GET MUST return the content')).toBe('first body');
64
65
 
65
66
  const list = await driver.get(FILES);
66
67
  const rows = (list.json as { files?: WorkspaceFile[] }).files ?? [];
67
68
  const row = rows.find((r) => r.path === path);
68
- expect(row, driver.describe('agent-workspace.md §C list', 'list MUST include the created file')).toBeDefined();
69
- expect('content' in (row as object), driver.describe('agent-workspace.md §C list', 'list MUST NOT include file bodies (metadata only)')).toBe(false);
69
+ expect(row, req('openwop.it.workspace-behavior.put-creates-v1-get-returns-it-list-omits-content-delete-removes-it', 'agent-workspace.md §C list', 'list MUST include the created file')).toBeDefined();
70
+ expect('content' in (row as object), req('openwop.it.workspace-behavior.put-creates-v1-get-returns-it-list-omits-content-delete-removes-it', 'agent-workspace.md §C list', 'list MUST NOT include file bodies (metadata only)')).toBe(false);
70
71
 
71
72
  const del = await driver.del(`${FILES}/${encodeURIComponent(path)}`);
72
- expect(del.status, driver.describe('agent-workspace.md §C DELETE', 'DELETE MUST return 2xx')).toBeGreaterThanOrEqual(200);
73
- expect(del.status, 'DELETE MUST be < 300').toBeLessThan(300);
73
+ expect(del.status, req('openwop.it.workspace-behavior.put-creates-v1-get-returns-it-list-omits-content-delete-removes-it', 'agent-workspace.md §C DELETE', 'DELETE MUST return 2xx')).toBeGreaterThanOrEqual(200);
74
+ expect(del.status, req('openwop.it.workspace-behavior.put-creates-v1-get-returns-it-list-omits-content-delete-removes-it', 'agent-workspace.md §C DELETE', 'DELETE MUST be < 300')).toBeLessThan(300);
74
75
 
75
76
  const gone = await driver.get(`${FILES}/${encodeURIComponent(path)}`);
76
- expect(gone.status, driver.describe('agent-workspace.md §C GET', 'GET after DELETE MUST be 404')).toBe(404);
77
+ expect(gone.status, req('openwop.it.workspace-behavior.put-creates-v1-get-returns-it-list-omits-content-delete-removes-it', 'agent-workspace.md §C GET', 'GET after DELETE MUST be 404')).toBe(404);
77
78
  });
78
79
  });
79
80
 
@@ -86,14 +87,14 @@ describe('workspace-behavior: §C optimistic concurrency (RFC 0059)', () => {
86
87
  const etag = (v1.json as WorkspaceFile).etag!;
87
88
 
88
89
  const stale = await driver.put(`${FILES}/${encodeURIComponent(path)}`, { content: 'nope' }, { headers: { 'If-Match': '"definitely-stale"' } });
89
- expect(stale.status, driver.describe('agent-workspace.md §C PUT', 'a stale If-Match MUST return 409 workspace_conflict')).toBe(409);
90
- expect((stale.json as { error?: string }).error, driver.describe('rest-endpoints.md workspace_conflict', 'error code MUST be workspace_conflict')).toBe('workspace_conflict');
90
+ expect(stale.status, req('openwop.it.workspace-behavior.stale-if-match-409-workspace-conflict-matching-if-match-bumps-version', 'agent-workspace.md §C PUT', 'a stale If-Match MUST return 409 workspace_conflict')).toBe(409);
91
+ expect((stale.json as { error?: string }).error, req('openwop.it.workspace-behavior.stale-if-match-409-workspace-conflict-matching-if-match-bumps-version', 'rest-endpoints.md workspace_conflict', 'error code MUST be workspace_conflict')).toBe('workspace_conflict');
91
92
  const details = (stale.json as { details?: { currentVersion?: number } }).details;
92
- expect(typeof details?.currentVersion, driver.describe('agent-workspace.md §C PUT', '409 MUST carry details.currentVersion')).toBe('number');
93
+ expect(typeof details?.currentVersion, req('openwop.it.workspace-behavior.stale-if-match-409-workspace-conflict-matching-if-match-bumps-version', 'agent-workspace.md §C PUT', '409 MUST carry details.currentVersion')).toBe('number');
93
94
 
94
95
  const v2 = await driver.put(`${FILES}/${encodeURIComponent(path)}`, { content: 'v2' }, { headers: { 'If-Match': etag } });
95
- expect(v2.status, 'a matching If-Match MUST succeed').toBe(200);
96
- expect((v2.json as WorkspaceFile).version, driver.describe('agent-workspace.md §File model', 'a successful PUT MUST bump version')).toBe(2);
96
+ expect(v2.status, req('openwop.it.workspace-behavior.stale-if-match-409-workspace-conflict-matching-if-match-bumps-version', 'agent-workspace.md §C PUT', 'a matching If-Match MUST succeed')).toBe(200);
97
+ expect((v2.json as WorkspaceFile).version, req('openwop.it.workspace-behavior.stale-if-match-409-workspace-conflict-matching-if-match-bumps-version', 'agent-workspace.md §File model', 'a successful PUT MUST bump version')).toBe(2);
97
98
 
98
99
  await driver.del(`${FILES}/${encodeURIComponent(path)}`);
99
100
  });
@@ -106,8 +107,8 @@ describe('workspace-behavior: §C size ceiling (RFC 0059)', () => {
106
107
  const max = typeof cap.maxFileBytes === 'number' ? cap.maxFileBytes : 1_048_576;
107
108
  const tooBig = 'x'.repeat(max + 1);
108
109
  const res = await driver.put(`${FILES}/${encodeURIComponent('behavior/TOOBIG.md')}`, { content: tooBig });
109
- expect(res.status, driver.describe('agent-workspace.md §C PUT', 'oversize content MUST be rejected (4xx)')).toBeGreaterThanOrEqual(400);
110
- expect((res.json as { error?: string }).error, driver.describe('rest-endpoints.md workspace_too_large', 'error code MUST be workspace_too_large')).toBe('workspace_too_large');
110
+ expect(res.status, req('openwop.it.workspace-behavior.content-beyond-maxfilebytes-returns-workspace-too-large', 'agent-workspace.md §C PUT', 'oversize content MUST be rejected (4xx)')).toBeGreaterThanOrEqual(400);
111
+ expect((res.json as { error?: string }).error, req('openwop.it.workspace-behavior.content-beyond-maxfilebytes-returns-workspace-too-large', 'rest-endpoints.md workspace_too_large', 'error code MUST be workspace_too_large')).toBe('workspace_too_large');
111
112
  });
112
113
  });
113
114
 
@@ -124,10 +125,10 @@ describe('workspace-behavior: §D run-start snapshot (RFC 0059)', () => {
124
125
 
125
126
  const snap = await driver.get(`/v1/runs/${encodeURIComponent(runId)}`);
126
127
  const ws = (snap.json as { workspace?: Array<{ path: string }> }).workspace;
127
- if (ws === undefined) return; // host doesn't expose the snapshot field — skip
128
+ if (ws === undefined) return softSkip('blocked', 'precondition not met — `ws === undefined` returned early (host doesn\'t expose the snapshot field — skip) (seam, prior step, or fixture unavailable)'); // host doesn't expose the snapshot field — skip
128
129
  expect(
129
130
  ws.some((f) => f.path === path),
130
- driver.describe('agent-workspace.md §D', 'the run snapshot MUST reflect a workspace file present at run start'),
131
+ req('openwop.it.workspace-behavior.a-run-started-after-a-write-exposes-the-workspace-snapshot', 'agent-workspace.md §D', 'the run snapshot MUST reflect a workspace file present at run start'),
131
132
  ).toBe(true);
132
133
 
133
134
  await driver.del(`${FILES}/${encodeURIComponent(path)}`);
@@ -25,6 +25,7 @@ import { describe, it, expect } from 'vitest';
25
25
  import { driver } from '../lib/driver.js';
26
26
  import { softSkip } from '../lib/soft-skip.js';
27
27
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
28
+ import { req } from '../lib/requirement-ids.js';
28
29
 
29
30
  interface DiscoveryWorkspace {
30
31
  supported?: boolean;
@@ -52,7 +53,7 @@ describe('workspace-capability-shape: advertisement shape (RFC 0059 §A)', () =>
52
53
  if (ws === null) return softSkip('inapplicable', 'optional advertisement — `capabilities.workspace` not advertised by this host (RFC 0059 §A)');
53
54
  expect(
54
55
  typeof ws.supported,
55
- driver.describe(
56
+ req('openwop.it.workspace-capability-shape.capabilities-workspace-is-either-absent-or-well-formed',
56
57
  'capabilities.schema.json §workspace',
57
58
  'capabilities.workspace.supported MUST be a boolean when workspace is advertised',
58
59
  ),
@@ -67,7 +68,7 @@ describe('workspace-capability-shape: advertisement shape (RFC 0059 §A)', () =>
67
68
  if (v === undefined) continue;
68
69
  expect(
69
70
  Number.isInteger(v) && v >= 1,
70
- driver.describe('RFC 0059 §A', `capabilities.workspace.${field} MUST be an integer >= 1, got: ${v}`),
71
+ req('openwop.it.workspace-capability-shape.maxfilebytes-maxfiles-maxversions-are-positive-integers-when-present', 'RFC 0059 §A', `capabilities.workspace.${field} MUST be an integer >= 1, got: ${v}`),
71
72
  ).toBe(true);
72
73
  }
73
74
  });
@@ -28,6 +28,7 @@ import { softSkip } from '../lib/soft-skip.js';
28
28
  import { randomUUID } from 'node:crypto';
29
29
  import { driver } from '../lib/driver.js';
30
30
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
31
+ import { req } from '../lib/requirement-ids.js';
31
32
 
32
33
  interface DiscoveryDoc {
33
34
  workspace?: { supported?: boolean };
@@ -55,18 +56,18 @@ describe('workspace-cross-tenant-isolation (black-box): a §C file MUST NOT leak
55
56
 
56
57
  // Owner A (default credential) writes the file via the NORMATIVE PUT.
57
58
  const put = await driver.put(`/v1/host/workspace/files/${path}`, { content: secret });
58
- expect(put.status, driver.describe('agent-workspace.md §C PUT', 'the owning workspace MUST create its file (200)')).toBe(200);
59
+ expect(put.status, req('openwop.it.workspace-cross-tenant-isolation-blackbox.a-file-written-by-owner-a-is-unreadable-un-enumerable-by-a-second-tenant-credent', 'agent-workspace.md §C PUT', 'the owning workspace MUST create its file (200)')).toBe(200);
59
60
 
60
61
  try {
61
62
  // Owner B (second-tenant credential) MUST NOT read it — fail closed, no existence leak.
62
63
  const crossRead = await driver.get(`/v1/host/workspace/files/${path}`, asTenantB);
63
64
  expect(
64
65
  crossRead.status === 404 || crossRead.status === 403,
65
- driver.describe('agent-workspace.md §E WCT-1', `a second-tenant read MUST fail closed (404/403), got ${crossRead.status}`),
66
+ req('openwop.it.workspace-cross-tenant-isolation-blackbox.a-file-written-by-owner-a-is-unreadable-un-enumerable-by-a-second-tenant-credent', 'agent-workspace.md §E WCT-1', `a second-tenant read MUST fail closed (404/403), got ${crossRead.status}`),
66
67
  ).toBe(true);
67
68
  expect(
68
69
  !JSON.stringify(crossRead.json ?? '').includes(secret),
69
- driver.describe('agent-workspace.md §E WCT-1', 'a second-tenant read MUST NOT surface the owner\'s content'),
70
+ req('openwop.it.workspace-cross-tenant-isolation-blackbox.a-file-written-by-owner-a-is-unreadable-un-enumerable-by-a-second-tenant-credent', 'agent-workspace.md §E WCT-1', 'a second-tenant read MUST NOT surface the owner\'s content'),
70
71
  ).toBe(true);
71
72
 
72
73
  // Owner B MUST NOT enumerate it in the list projection.
@@ -74,13 +75,13 @@ describe('workspace-cross-tenant-isolation (black-box): a §C file MUST NOT leak
74
75
  if (crossList.status === 200) {
75
76
  expect(
76
77
  !JSON.stringify((crossList.json as { files?: unknown })?.files ?? []).includes(path),
77
- driver.describe('agent-workspace.md §E WCT-1', 'a second-tenant list MUST NOT enumerate the owner\'s path'),
78
+ req('openwop.it.workspace-cross-tenant-isolation-blackbox.a-file-written-by-owner-a-is-unreadable-un-enumerable-by-a-second-tenant-credent', 'agent-workspace.md §E WCT-1', 'a second-tenant list MUST NOT enumerate the owner\'s path'),
78
79
  ).toBe(true);
79
80
  }
80
81
 
81
82
  // Isolation, not loss: owner A still reads its own file.
82
83
  const ownerRead = await driver.get(`/v1/host/workspace/files/${path}`);
83
- expect(ownerRead.status, driver.describe('agent-workspace.md §C GET', 'the owning workspace MUST still read its own file')).toBe(200);
84
+ expect(ownerRead.status, req('openwop.it.workspace-cross-tenant-isolation-blackbox.a-file-written-by-owner-a-is-unreadable-un-enumerable-by-a-second-tenant-credent', 'agent-workspace.md §C GET', 'the owning workspace MUST still read its own file')).toBe(200);
84
85
  expect((ownerRead.json as { content?: string } | undefined)?.content).toBe(secret);
85
86
  } finally {
86
87
  // Best-effort cleanup as owner A.
@@ -22,6 +22,7 @@ import { describe, it, expect } from 'vitest';
22
22
  import { softSkip } from '../lib/soft-skip.js';
23
23
  import { driver } from '../lib/driver.js';
24
24
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
25
+ import { req } from '../lib/requirement-ids.js';
25
26
 
26
27
  interface DiscoveryDoc {
27
28
  capabilities?: { workspace?: { supported?: boolean } };
@@ -46,25 +47,25 @@ describe('workspace-cross-tenant-isolation: a workspace file MUST NOT leak acros
46
47
  // Owner A writes a file.
47
48
  const put = await seam({ tenant: 'wct1-tenant-a', workspace: 'ws-a', op: 'put', path: SEAM_PATH, content: 'A-only secret body' });
48
49
  if (put.status === 404) return softSkip('blocked', 'seam unwired — soft-skip the behavioral probe');
49
- expect(put.status, driver.describe('agent-workspace.md §C PUT', 'seam put MUST succeed for the owning workspace')).toBe(200);
50
+ expect(put.status, req('openwop.it.workspace-cross-tenant-isolation.a-file-written-under-tenant-a-workspace-a-is-not-readable-under-a-different-owne', 'agent-workspace.md §C PUT', 'seam put MUST succeed for the owning workspace')).toBe(200);
50
51
 
51
52
  // A DIFFERENT workspace (same tenant) MUST NOT read it.
52
53
  const crossWs = await seam({ tenant: 'wct1-tenant-a', workspace: 'ws-b', op: 'get', path: SEAM_PATH });
53
54
  expect(
54
55
  crossWs.status === 404 || crossWs.status === 403,
55
- driver.describe('agent-workspace.md §E WCT-1', `a cross-workspace get MUST fail closed (404/403, no existence leak), got ${crossWs.status}`),
56
+ req('openwop.it.workspace-cross-tenant-isolation.a-file-written-under-tenant-a-workspace-a-is-not-readable-under-a-different-owne', 'agent-workspace.md §E WCT-1', `a cross-workspace get MUST fail closed (404/403, no existence leak), got ${crossWs.status}`),
56
57
  ).toBe(true);
57
58
  const crossWsBody = JSON.stringify(crossWs.json ?? '');
58
59
  expect(
59
60
  !crossWsBody.includes('A-only secret body'),
60
- driver.describe('agent-workspace.md §E WCT-1', 'a cross-workspace read MUST NOT surface the other owner\'s content'),
61
+ req('openwop.it.workspace-cross-tenant-isolation.a-file-written-under-tenant-a-workspace-a-is-not-readable-under-a-different-owne', 'agent-workspace.md §E WCT-1', 'a cross-workspace read MUST NOT surface the other owner\'s content'),
61
62
  ).toBe(true);
62
63
 
63
64
  // A DIFFERENT tenant MUST NOT read it either.
64
65
  const crossTenant = await seam({ tenant: 'wct1-tenant-b', workspace: 'ws-a', op: 'get', path: SEAM_PATH });
65
66
  expect(
66
67
  crossTenant.status === 404 || crossTenant.status === 403,
67
- driver.describe('agent-workspace.md §E WCT-1', `a cross-tenant get MUST fail closed, got ${crossTenant.status}`),
68
+ req('openwop.it.workspace-cross-tenant-isolation.a-file-written-under-tenant-a-workspace-a-is-not-readable-under-a-different-owne', 'agent-workspace.md §E WCT-1', `a cross-tenant get MUST fail closed, got ${crossTenant.status}`),
68
69
  ).toBe(true);
69
70
 
70
71
  // And list MUST NOT enumerate the other owner's path.
@@ -72,14 +73,14 @@ describe('workspace-cross-tenant-isolation: a workspace file MUST NOT leak acros
72
73
  const listed = JSON.stringify((crossList.json as { files?: unknown })?.files ?? []);
73
74
  expect(
74
75
  !listed.includes(SEAM_PATH),
75
- driver.describe('agent-workspace.md §E WCT-1', 'a cross-workspace list MUST NOT enumerate another owner\'s file'),
76
+ req('openwop.it.workspace-cross-tenant-isolation.a-file-written-under-tenant-a-workspace-a-is-not-readable-under-a-different-owne', 'agent-workspace.md §E WCT-1', 'a cross-workspace list MUST NOT enumerate another owner\'s file'),
76
77
  ).toBe(true);
77
78
 
78
79
  // Sanity: the owner itself still reads its file (isolation, not loss).
79
80
  const ownerRead = await seam({ tenant: 'wct1-tenant-a', workspace: 'ws-a', op: 'get', path: SEAM_PATH });
80
81
  expect(
81
82
  (ownerRead.json as { content?: string } | undefined)?.content,
82
- driver.describe('agent-workspace.md §C GET', 'the owning workspace MUST still read its own file'),
83
+ req('openwop.it.workspace-cross-tenant-isolation.a-file-written-under-tenant-a-workspace-a-is-not-readable-under-a-different-owne', 'agent-workspace.md §C GET', 'the owning workspace MUST still read its own file'),
83
84
  ).toBe('A-only secret body');
84
85
  });
85
86
  });
@@ -38,6 +38,7 @@ import { describe, it, expect } from 'vitest';
38
38
  import Ajv2020 from 'ajv/dist/2020.js';
39
39
  import addFormats from 'ajv-formats';
40
40
  import type { ErrorObject } from 'ajv';
41
+ import { req } from '../lib/requirement-ids.js';
41
42
 
42
43
  /** The §A `x-openwop-form` annotation shape. `kind` is the only required
43
44
  * field; it is an OPEN string (unknown kinds are valid per the forward-compat
@@ -103,11 +104,11 @@ describe('category: x-openwop-form pack-manifest shape (RFC 0066)', () => {
103
104
  let validateConfig: ReturnType<typeof ajv.compile> | undefined;
104
105
  expect(() => {
105
106
  validateConfig = ajv.compile(annotatedConfigSchema());
106
- }, 'node-packs.md §x-openwop-form: an annotated configSchema MUST remain a valid 2020-12 schema').not.toThrow();
107
+ }, req('openwop.it.x-openwop-form-pack-manifest.positive-a-configschema-carrying-x-openwop-form-remains-a-valid-json-schema-2020', 'node-packs.md', 'node-packs.md §x-openwop-form: an annotated configSchema MUST remain a valid 2020-12 schema')).not.toThrow();
107
108
  // The annotations do not alter schema semantics: valid config passes…
108
109
  expect(
109
110
  validateConfig!({ provider: 'anthropic', model: 'claude', temperature: 1 }),
110
- 'x-openwop-form is advisory — it MUST NOT change what the schema accepts',
111
+ req('openwop.it.x-openwop-form-pack-manifest.positive-a-configschema-carrying-x-openwop-form-remains-a-valid-json-schema-2020', 'node-packs.md', 'x-openwop-form is advisory — it MUST NOT change what the schema accepts'),
111
112
  ).toBe(true);
112
113
  // …and a type violation on an annotated field still rejects.
113
114
  expect(validateConfig!({ provider: 123 })).toBe(false);
@@ -120,7 +121,7 @@ describe('category: x-openwop-form pack-manifest shape (RFC 0066)', () => {
120
121
  if (ann === undefined) continue;
121
122
  expect(
122
123
  validateShape(ann),
123
- `node-packs.md §x-openwop-form: the annotation on "${name}" MUST match the §A shape. Errors: ${JSON.stringify(validateShape.errors)}`,
124
+ req('openwop.it.x-openwop-form-pack-manifest.positive-each-documented-x-openwop-form-annotation-matches-the-a-shape', 'node-packs.md', `node-packs.md §x-openwop-form: the annotation on "${name}" MUST match the §A shape. Errors: ${JSON.stringify(validateShape.errors)}`),
124
125
  ).toBe(true);
125
126
  }
126
127
  });
@@ -128,28 +129,28 @@ describe('category: x-openwop-form pack-manifest shape (RFC 0066)', () => {
128
129
  it('forward-compat: an unknown kind is a structurally valid annotation (renderer falls back per the §A MUST)', () => {
129
130
  expect(
130
131
  validateShape({ kind: 'unknown-future-picker' }),
131
- 'node-packs.md §x-openwop-form: an unknown kind MUST validate (kind is an open string, not a closed enum) so future vocabulary is forward-compatible',
132
+ req('openwop.it.x-openwop-form-pack-manifest.forward-compat-an-unknown-kind-is-a-structurally-valid-annotation-renderer-falls', 'node-packs.md', 'node-packs.md §x-openwop-form: an unknown kind MUST validate (kind is an open string, not a closed enum) so future vocabulary is forward-compatible'),
132
133
  ).toBe(true);
133
134
  });
134
135
 
135
136
  it('negative: an annotation missing kind is rejected', () => {
136
137
  expect(
137
138
  shapeFailsWith({ dependsOn: 'provider' }, 'required').length,
138
- 'node-packs.md §x-openwop-form: kind is the one REQUIRED sub-field',
139
+ req('openwop.it.x-openwop-form-pack-manifest.negative-an-annotation-missing-kind-is-rejected', 'node-packs.md', 'node-packs.md §x-openwop-form: kind is the one REQUIRED sub-field'),
139
140
  ).toBeGreaterThan(0);
140
141
  });
141
142
 
142
143
  it('negative: a non-string kind is rejected', () => {
143
144
  expect(
144
145
  shapeFailsWith({ kind: 42 }, 'type').length,
145
- 'node-packs.md §x-openwop-form: kind MUST be a string',
146
+ req('openwop.it.x-openwop-form-pack-manifest.negative-a-non-string-kind-is-rejected', 'node-packs.md', 'node-packs.md §x-openwop-form: kind MUST be a string'),
146
147
  ).toBeGreaterThan(0);
147
148
  });
148
149
 
149
150
  it('negative: a non-string dependsOn is rejected', () => {
150
151
  expect(
151
152
  shapeFailsWith({ kind: 'model-picker', dependsOn: ['provider'] }, 'type').length,
152
- 'node-packs.md §x-openwop-form: dependsOn names a sibling property — it MUST be a string',
153
+ req('openwop.it.x-openwop-form-pack-manifest.negative-a-non-string-dependson-is-rejected', 'node-packs.md', 'node-packs.md §x-openwop-form: dependsOn names a sibling property — it MUST be a string'),
153
154
  ).toBeGreaterThan(0);
154
155
  });
155
156
  });
package/src/setup.ts CHANGED
@@ -292,7 +292,8 @@ afterEach(({ task }) => {
292
292
  // which prove fixtures and helpers, not a host, and must never become bundle
293
293
  // evidence (that is why RFC 0163 G5 / RFC 0050 G1 moved them out of scenarios).
294
294
  const filepath = (task as { file?: { filepath?: string } }).file?.filepath ?? '';
295
- if (!/[\\/]src[\\/]scenarios[\\/]/.test(filepath)) {
295
+ // Suite 2.0.0: src/coherence/ rows feed the corpus ledger (scripts/check-spec-coherence.mjs), not a host bundle.
296
+ if (!/[\\/]src[\\/](scenarios|coherence)[\\/]/.test(filepath)) {
296
297
  takeExplicitRequirementId();
297
298
  return;
298
299
  }
@@ -304,7 +305,8 @@ afterEach(({ task }) => {
304
305
  const gate = since.find((e) => e.disposition === 'inapplicable') ?? since.find((e) => e.disposition === 'skipped');
305
306
  let disposition: 'executed-pass' | 'executed-fail' | 'skipped' | 'inapplicable' | 'blocked';
306
307
  let detail: string | undefined;
307
- if (SPEC_COHERENCE_SCENARIOS.has(file) || SPEC_COHERENCE_SCENARIOS.has(file.replace(/\.test\.ts$/, ''))) {
308
+ // Suite 2.0.0: under the corpus gate (scripts/check-spec-coherence.mjs sets OPENWOP_CORPUS_GATE) a coherence scenario IS the subject; its rows are real dispositions for evidence/corpus-ledger.json.
309
+ if (process.env.OPENWOP_CORPUS_GATE !== '1' && (SPEC_COHERENCE_SCENARIOS.has(file) || SPEC_COHERENCE_SCENARIOS.has(file.replace(/\.test\.ts$/, '')))) {
308
310
  // Corpus-coherence scenario: it reads spec/v1 and asserts nothing about a
309
311
  // host, in any layout. Its rows are `inapplicable` to every host — the same
310
312
  // rule `resolveFileRecord` applies to the file in the published layout.
package/vitest.config.ts CHANGED
@@ -6,7 +6,11 @@ export default defineConfig({
6
6
  // scenarios; `src/lib/**/*.test.ts` are server-free unit tests for
7
7
  // suite-internal helpers (e.g., the synthetic OIDC issuer harness)
8
8
  // that don't depend on OPENWOP_BASE_URL.
9
- include: ['src/scenarios/**/*.test.ts', 'src/lib/**/*.test.ts'],
9
+ include: ['src/scenarios/**/*.test.ts'],
10
+ // Suite 2.0.0: the corpus-coherence scenarios (src/coherence/) and the suite
11
+ // self-tests (src/lib/*.test.ts) are NOT host scenarios; they run under
12
+ // vitest.coherence.config.ts (scripts/check-spec-coherence.mjs) and
13
+ // vitest.selftest.config.ts. A host run never executes them.
10
14
  testTimeout: 30_000,
11
15
  hookTimeout: 30_000,
12
16
  globals: true,
@@ -1,22 +0,0 @@
1
- # Scoped, documented exceptions to redocly's `no-ambiguous-paths` rule.
2
- # The rule stays ENABLED globally (it catches genuinely ambiguous new paths);
3
- # only the two known-safe occurrences below are exempted.
4
- #
5
- # Why these are safe (NOT merely tolerated):
6
- # The rule flags the structural template/literal swap between
7
- # `/v1/agents/{agentId}/deployments` and the sibling collection routes
8
- # `/v1/agents/roster/{rosterId}` + `/v1/agents/org-chart/{departmentId}`
9
- # (e.g. `/v1/agents/roster/deployments` could in principle parse two ways).
10
- # It is resolved deterministically:
11
- # 1. `agentId` carries `pattern: '^(?!roster$|org-chart$).+$'` (see the two
12
- # `/v1/agents/{agentId}*` paths), so `{agentId}` can NEVER be `roster` or
13
- # `org-chart` — a pattern-aware validator/router cannot misroute.
14
- # 2. OpenAPI 3.x matching is concrete-before-templated, so `/v1/agents/roster/*`
15
- # and `/v1/agents/org-chart/*` bind to the literal collection routes first.
16
- # The paths are all normative (RFC 0072/0082/0086/0087), so they can't be
17
- # renamed without a breaking change; the pattern is the non-breaking fix and
18
- # redocly's purely-structural rule can't see it.
19
- openapi.yaml:
20
- no-ambiguous-paths:
21
- - '#/paths/~1v1~1agents~1roster~1{rosterId}'
22
- - '#/paths/~1v1~1agents~1org-chart~1{departmentId}'