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

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