@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
@@ -19,6 +19,7 @@ import { driver } from '../lib/driver.js';
19
19
  import { isFixtureAdvertised } from '../lib/fixtures.js';
20
20
  import { softSkip } from '../lib/soft-skip.js';
21
21
  import { pollUntil } from '../lib/polling.js';
22
+ import { req } from '../lib/requirement-ids.js';
22
23
 
23
24
  const NOOP_WORKFLOW_ID = 'conformance-noop';
24
25
  const SKIP_NO_NOOP = !isFixtureAdvertised(NOOP_WORKFLOW_ID);
@@ -88,19 +89,19 @@ describe.skipIf(SKIP_NO_NOOP)('owner-subject-echo: consistency, echo, fork (RFC
88
89
  if (subject === undefined) {
89
90
  return softSkip('inapplicable', 'host does not emit owner.subject on new runs (RFC 0165 §B — optional in v1.x)');
90
91
  }
91
- expect(subject.tenant, driver.describe('RFC 0165 §B.2', 'subject.tenant MUST equal owner.tenant')).toBe(snap.owner?.tenant);
92
+ expect(subject.tenant, req('openwop.it.owner-subject-echo.subject-is-consistent-with-the-owner-triple-b-2-and-the-actor-chain-is-bounded-b', 'RFC 0165 §B.2', 'subject.tenant MUST equal owner.tenant')).toBe(snap.owner?.tenant);
92
93
  if (snap.owner?.principal !== undefined) {
93
- expect(subject.subjectId, driver.describe('RFC 0165 §B.2', 'subject.subjectId MUST equal owner.principal when both are present')).toBe(snap.owner.principal);
94
+ expect(subject.subjectId, req('openwop.it.owner-subject-echo.subject-is-consistent-with-the-owner-triple-b-2-and-the-actor-chain-is-bounded-b', 'RFC 0165 §B.2', 'subject.subjectId MUST equal owner.principal when both are present')).toBe(snap.owner.principal);
94
95
  }
95
96
  if (snap.owner?.principalKind !== undefined) {
96
- expect(subject.kind, driver.describe('RFC 0165 §B.2', 'subject.kind MUST equal owner.principalKind when both are present')).toBe(snap.owner.principalKind);
97
+ expect(subject.kind, req('openwop.it.owner-subject-echo.subject-is-consistent-with-the-owner-triple-b-2-and-the-actor-chain-is-bounded-b', 'RFC 0165 §B.2', 'subject.kind MUST equal owner.principalKind when both are present')).toBe(snap.owner.principalKind);
97
98
  }
98
99
  if (subject.lane === 'saml' || subject.lane === 'scim') {
99
- expect(subject.keyClass, driver.describe('RFC 0165 §B.2', 'keyClass MUST be present on a linkable lane')).toBeDefined();
100
+ expect(subject.keyClass, req('openwop.it.owner-subject-echo.subject-is-consistent-with-the-owner-triple-b-2-and-the-actor-chain-is-bounded-b', 'RFC 0165 §B.2', 'keyClass MUST be present on a linkable lane')).toBeDefined();
100
101
  } else {
101
- expect(subject.keyClass, driver.describe('RFC 0165 §B.2', 'keyClass MUST be absent off the linkable lanes')).toBeUndefined();
102
+ expect(subject.keyClass, req('openwop.it.owner-subject-echo.subject-is-consistent-with-the-owner-triple-b-2-and-the-actor-chain-is-bounded-b', 'RFC 0165 §B.2', 'keyClass MUST be absent off the linkable lanes')).toBeUndefined();
102
103
  }
103
- expect(actorDepth(subject), driver.describe('RFC 0165 §B.5', 'actor chain depth MUST NOT exceed 4')).toBeLessThanOrEqual(4);
104
+ expect(actorDepth(subject), req('openwop.it.owner-subject-echo.subject-is-consistent-with-the-owner-triple-b-2-and-the-actor-chain-is-bounded-b', 'RFC 0165 §B.5', 'actor chain depth MUST NOT exceed 4')).toBeLessThanOrEqual(4);
104
105
  });
105
106
 
106
107
  it('run.started echoes the snapshot subject verbatim (§B.2, RFC 0048 §C)', async () => {
@@ -116,8 +117,8 @@ describe.skipIf(SKIP_NO_NOOP)('owner-subject-echo: consistency, echo, fork (RFC
116
117
  // leave `pending` first, then the event is a fact, not a timing.
117
118
  await pollUntil(runId, (s) => s.status !== 'pending', { label: 'run left pending (run.started appended)' });
118
119
  const started = (await events(runId)).find((e) => e.type === 'run.started');
119
- expect(started, driver.describe('RFC 0048 §C', 'run.started MUST be present in the event log')).toBeDefined();
120
- expect(started?.payload?.owner?.subject, driver.describe('RFC 0165 §B.2', 'run.started owner.subject MUST equal RunSnapshot.owner.subject')).toEqual(snap.owner.subject);
120
+ expect(started, req('openwop.it.owner-subject-echo.run-started-echoes-the-snapshot-subject-verbatim-b-2-rfc-0048-c', 'RFC 0048 §C', 'run.started MUST be present in the event log')).toBeDefined();
121
+ expect(started?.payload?.owner?.subject, req('openwop.it.owner-subject-echo.run-started-echoes-the-snapshot-subject-verbatim-b-2-rfc-0048-c', 'RFC 0165 §B.2', 'run.started owner.subject MUST equal RunSnapshot.owner.subject')).toEqual(snap.owner.subject);
121
122
  });
122
123
 
123
124
  it('a fork copies owner.tenant and owner.subject verbatim onto the child (§B.4)', async () => {
@@ -130,11 +131,11 @@ describe.skipIf(SKIP_NO_NOOP)('owner-subject-echo: consistency, echo, fork (RFC
130
131
  if (fork.status === 404 || fork.status === 501 || fork.status === 403) {
131
132
  return softSkip('inapplicable', `host does not offer :fork on this run (${fork.status}); the fork-copy rule has no fork to check`);
132
133
  }
133
- expect(fork.status, driver.describe('replay.md §"Fork ownership"', ':fork MUST return 201 for a completed noop run')).toBe(201);
134
+ expect(fork.status, req('openwop.it.owner-subject-echo.a-fork-copies-owner-tenant-and-owner-subject-verbatim-onto-the-child-b-4', 'replay.md §"Fork ownership"', ':fork MUST return 201 for a completed noop run')).toBe(201);
134
135
  const childId = (fork.json as { runId?: string }).runId;
135
- expect(typeof childId, 'fork response MUST include the child runId').toBe('string');
136
+ expect(typeof childId, req('openwop.it.owner-subject-echo.a-fork-copies-owner-tenant-and-owner-subject-verbatim-onto-the-child-b-4', 'replay.md §"Fork ownership"', 'fork response MUST include the child runId')).toBe('string');
136
137
  const child = await snapshot(childId as string);
137
- expect(child.owner?.tenant, driver.describe('RFC 0165 §B.4', 'the child owner.tenant MUST equal the source')).toBe(source.owner.tenant);
138
- expect(child.owner?.subject, driver.describe('RFC 0165 §B.4', 'the child owner.subject MUST be copied verbatim from the source')).toEqual(source.owner.subject);
138
+ expect(child.owner?.tenant, req('openwop.it.owner-subject-echo.a-fork-copies-owner-tenant-and-owner-subject-verbatim-onto-the-child-b-4', 'RFC 0165 §B.4', 'the child owner.tenant MUST equal the source')).toBe(source.owner.tenant);
139
+ expect(child.owner?.subject, req('openwop.it.owner-subject-echo.a-fork-copies-owner-tenant-and-owner-subject-verbatim-onto-the-child-b-4', 'RFC 0165 §B.4', 'the child owner.subject MUST be copied verbatim from the source')).toEqual(source.owner.subject);
139
140
  });
140
141
  });
@@ -23,6 +23,7 @@ import { readFileSync } from 'node:fs';
23
23
  import { join } from 'node:path';
24
24
  import Ajv2020 from 'ajv/dist/2020.js';
25
25
  import { SCHEMAS_DIR } from '../lib/paths.js';
26
+ import { req } from '../lib/requirement-ids.js';
26
27
 
27
28
  const read = (f: string): Record<string, unknown> => JSON.parse(readFileSync(join(SCHEMAS_DIR, f), 'utf8')) as Record<string, unknown>;
28
29
  const subject = read('subject.schema.json');
@@ -41,28 +42,28 @@ describe('owner-subject-shape: subject.schema.json (RFC 0165 §B.1)', () => {
41
42
  const validate = ajvWithSubject().compile(subject);
42
43
 
43
44
  it('compiles and accepts a full SAML subject', () => {
44
- expect(validate(VALID), JSON.stringify(validate.errors)).toBe(true);
45
+ expect(validate(VALID), req('openwop.it.owner-subject-shape.compiles-and-accepts-a-full-saml-subject', 'RFC 0165 §B.1–§B.2', JSON.stringify(validate.errors))).toBe(true);
45
46
  });
46
47
 
47
48
  it('accepts the legacy issuer form (RFC 0165 §B.3) and a workload subject', () => {
48
- expect(validate({ issuer: 'urn:openwop:legacy', subjectId: 'user_42', tenant: 'acme', lane: 'api-key', kind: 'user' }), JSON.stringify(validate.errors)).toBe(true);
49
+ expect(validate({ issuer: 'urn:openwop:legacy', subjectId: 'user_42', tenant: 'acme', lane: 'api-key', kind: 'user' }), req('openwop.it.owner-subject-shape.accepts-the-legacy-issuer-form-rfc-0165-b-3-and-a-workload-subject', 'RFC 0165 §B.3', JSON.stringify(validate.errors))).toBe(true);
49
50
  expect(validate({ issuer: 'spiffe://trust.example', subjectId: 'spiffe://trust.example/ns/a/sa/b', tenant: 'acme', lane: 'workload', kind: 'workload' }), JSON.stringify(validate.errors)).toBe(true);
50
51
  });
51
52
 
52
53
  it('rejects an email-shaped or whitespace subjectId (invariant subject-record-opaque)', () => {
53
- expect(validate({ ...VALID, subjectId: 'alice@example.com' }), 'RFC 0165 §B.2: subjectId MUST NOT be an email address').toBe(false);
54
- expect(validate({ ...VALID, subjectId: 'Alice Example' }), 'RFC 0165 §B.2: subjectId MUST NOT contain whitespace (a display name)').toBe(false);
54
+ expect(validate({ ...VALID, subjectId: 'alice@example.com' }), req('openwop.it.owner-subject-shape.rejects-an-email-shaped-or-whitespace-subjectid-invariant-subject-record-opaque', 'RFC 0165 §B.1–§B.2', 'RFC 0165 §B.2: subjectId MUST NOT be an email address')).toBe(false);
55
+ expect(validate({ ...VALID, subjectId: 'Alice Example' }), req('openwop.it.owner-subject-shape.rejects-an-email-shaped-or-whitespace-subjectid-invariant-subject-record-opaque', 'RFC 0165 §B.1–§B.2', 'RFC 0165 §B.2: subjectId MUST NOT contain whitespace (a display name)')).toBe(false);
55
56
  });
56
57
 
57
58
  it('requires keyClass on the saml/scim lanes and forbids it elsewhere', () => {
58
59
  const { keyClass: _k, ...noKey } = VALID;
59
- expect(validate(noKey), 'RFC 0165 §B.2: keyClass MUST be present when lane is saml or scim').toBe(false);
60
- expect(validate({ ...VALID, lane: 'oidc' }), 'RFC 0165 §B.2: keyClass MUST be absent when the lane is not linkable').toBe(false);
60
+ expect(validate(noKey), req('openwop.it.owner-subject-shape.requires-keyclass-on-the-saml-scim-lanes-and-forbids-it-elsewhere', 'RFC 0165 §B.1–§B.2', 'RFC 0165 §B.2: keyClass MUST be present when lane is saml or scim')).toBe(false);
61
+ expect(validate({ ...VALID, lane: 'oidc' }), req('openwop.it.owner-subject-shape.requires-keyclass-on-the-saml-scim-lanes-and-forbids-it-elsewhere', 'RFC 0165 §B.1–§B.2', 'RFC 0165 §B.2: keyClass MUST be absent when the lane is not linkable')).toBe(false);
61
62
  expect(validate({ ...noKey, lane: 'oidc' }), JSON.stringify(validate.errors)).toBe(true);
62
63
  });
63
64
 
64
65
  it('rejects an unknown member and a missing required member', () => {
65
- expect(validate({ ...VALID, email: 'x' })).toBe(false);
66
+ expect(validate({ ...VALID, email: 'x' }), req('openwop.it.owner-subject-shape.rejects-an-unknown-member-and-a-missing-required-member', 'RFC 0165 §B.1–§B.2', 'rejects an unknown member and a missing required member')).toBe(false);
66
67
  const { tenant: _t, ...noTenant } = VALID;
67
68
  expect(validate(noTenant)).toBe(false);
68
69
  });
@@ -70,7 +71,7 @@ describe('owner-subject-shape: subject.schema.json (RFC 0165 §B.1)', () => {
70
71
  it('accepts a nested actor chain (depth is a host obligation, RFC 0165 §B.5)', () => {
71
72
  const peer = { issuer: 'https://peer.example', subjectId: 'peer-1', tenant: 'acme', lane: 'workload', kind: 'workload' };
72
73
  const { keyClass: _k, ...anon } = { ...VALID, kind: 'anonymous', lane: 'api-key', actor: peer };
73
- expect(validate(anon), JSON.stringify(validate.errors)).toBe(true);
74
+ expect(validate(anon), req('openwop.it.owner-subject-shape.accepts-a-nested-actor-chain-depth-is-a-host-obligation-rfc-0165-b-5', 'RFC 0165 §B.5', JSON.stringify(validate.errors))).toBe(true);
74
75
  });
75
76
  });
76
77
 
@@ -82,14 +83,14 @@ describe('owner-subject-shape: both owner sub-objects declare `subject` (RFC 016
82
83
  it('RunSnapshot.owner accepts a subject and still rejects an undeclared member', () => {
83
84
  const v = ajvWithSubject().compile(ownerSnap);
84
85
  expect(v({ tenant: 'acme', principal: 'idp-op-8f3a', principalKind: 'user', subject: VALID }), JSON.stringify(v.errors)).toBe(true);
85
- expect(v({ tenant: 'acme', role: 'admin' })).toBe(false);
86
+ expect(v({ tenant: 'acme', role: 'admin' }), req('openwop.it.owner-subject-shape.runsnapshot-owner-accepts-a-subject-and-still-rejects-an-undeclared-member', 'RFC 0165 §B.1–§B.2', 'RunSnapshot.owner accepts a subject and still rejects an undeclared member')).toBe(false);
86
87
  });
87
88
 
88
89
  it('run.started owner echo accepts the same subject AND principalKind (the echo matches the snapshot)', () => {
89
90
  const v = ajvWithSubject().compile(ownerEcho);
90
91
  expect(
91
92
  v({ tenant: 'acme', principal: 'idp-op-8f3a', principalKind: 'user', subject: VALID }),
92
- 'RFC 0165 §B.1: run.started owner MUST declare subject and principalKind so a host can echo its snapshot owner verbatim',
93
+ req('openwop.it.owner-subject-shape.run-started-owner-echo-accepts-the-same-subject-and-principalkind-the-echo-match', 'RFC 0165 §B.1–§B.2', 'RFC 0165 §B.1: run.started owner MUST declare subject and principalKind so a host can echo its snapshot owner verbatim'),
93
94
  ).toBe(true);
94
95
  expect(v({ tenant: 'acme', subject: { ...VALID, subjectId: 'a@b' } })).toBe(false);
95
96
  });
@@ -60,7 +60,6 @@
60
60
  */
61
61
 
62
62
  import { describe, it, expect } from 'vitest';
63
- import { driver } from '../lib/driver.js';
64
63
  import { behaviorGate, behaviorGatePresent } from '../lib/behavior-gate.js';
65
64
  import {
66
65
  readArtifactTypesCap,
@@ -71,6 +70,7 @@ import {
71
70
  withExtensions,
72
71
  canonicalJson,
73
72
  } from '../lib/artifactTypes.js';
73
+ import { req } from '../lib/requirement-ids.js';
74
74
 
75
75
  const PROFILE = 'openwop-artifact-type-packs';
76
76
 
@@ -102,7 +102,7 @@ describe('pack-manifest-extension-opacity: a host MUST accept an extended manife
102
102
  if (!behaviorGatePresent(PROFILE, res)) return; // seam absent: skip default, FAIL strict
103
103
  expect(
104
104
  ok(res.status),
105
- driver.describe('node-packs.md §"Vendor extensions on pack manifests"', 'a consumer MUST ignore an unrecognized extension and MUST NOT reject the pack for its presence'),
105
+ req('openwop.it.pack-manifest-extension-opacity.leg-1-an-extension-bearing-manifest-installs', 'node-packs.md §"Vendor extensions on pack manifests"', 'a consumer MUST ignore an unrecognized extension and MUST NOT reject the pack for its presence'),
106
106
  ).toBe(true);
107
107
  });
108
108
 
@@ -113,7 +113,7 @@ describe('pack-manifest-extension-opacity: a host MUST accept an extended manife
113
113
  if (!behaviorGatePresent(PROFILE, res)) return;
114
114
  expect(
115
115
  ok(res.status),
116
- driver.describe('RFC 0139 §Conformance', 'baseline — guards against a differential that passes because EVERYTHING fails'),
116
+ req('openwop.it.pack-manifest-extension-opacity.leg-2-the-same-manifest-without-the-extension-installs-baseline', 'RFC 0139 §Conformance', 'baseline — guards against a differential that passes because EVERYTHING fails'),
117
117
  ).toBe(true);
118
118
  });
119
119
  });
@@ -130,12 +130,12 @@ describe('pack-manifest-extension-opacity: the differential — behavior MUST NO
130
130
 
131
131
  expect(
132
132
  ok(plain.status) === ok(extended.status),
133
- driver.describe('RFC 0139 §"The differential-install contract"', 'acceptance MUST NOT be a function of an unrecognized extension — a host MUST accept both or reject both for the same reason'),
133
+ req('openwop.it.pack-manifest-extension-opacity.leg-3-projections-for-m-and-m-are-equal-modulo-the-extensions-load-bearing', 'RFC 0139 §"The differential-install contract"', 'acceptance MUST NOT be a function of an unrecognized extension — a host MUST accept both or reject both for the same reason'),
134
134
  ).toBe(true);
135
135
 
136
136
  expect(
137
137
  canonicalJson(stripExtensions(extended.json)),
138
- driver.describe('RFC 0139 §"The differential-install contract"', 'the registration projection MUST be identical modulo the extension properties — a difference means the host acted on an extension it does not recognize (rendering catalog, derived facet, or code-path switch)'),
138
+ req('openwop.it.pack-manifest-extension-opacity.leg-3-projections-for-m-and-m-are-equal-modulo-the-extensions-load-bearing', 'RFC 0139 §"The differential-install contract"', 'the registration projection MUST be identical modulo the extension properties — a difference means the host acted on an extension it does not recognize (rendering catalog, derived facet, or code-path switch)'),
139
139
  ).toBe(canonicalJson(stripExtensions(plain.json)));
140
140
  });
141
141
 
@@ -150,12 +150,12 @@ describe('pack-manifest-extension-opacity: the differential — behavior MUST NO
150
150
 
151
151
  expect(
152
152
  ok(hostile.status),
153
- driver.describe('node-packs.md §"Vendor extensions on pack manifests"', 'an extension value is PACK-AUTHORED and therefore untrusted, but untrusted is not a rejection reason — the host MUST install and ignore'),
153
+ req('openwop.it.pack-manifest-extension-opacity.leg-4-an-extension-carrying-markup-a-templating-directive-is-not-interpreted-at', 'node-packs.md §"Vendor extensions on pack manifests"', 'an extension value is PACK-AUTHORED and therefore untrusted, but untrusted is not a rejection reason — the host MUST install and ignore'),
154
154
  ).toBe(true);
155
155
 
156
156
  expect(
157
157
  canonicalJson(stripExtensions(hostile.json)),
158
- driver.describe('node-packs.md §"Vendor extensions on pack manifests"', 'MUST NOT render it, execute it, or interpret it as markup or a templating directive — a projection that differs from baseline means the value reached an interpreter'),
158
+ req('openwop.it.pack-manifest-extension-opacity.leg-4-an-extension-carrying-markup-a-templating-directive-is-not-interpreted-at', 'node-packs.md §"Vendor extensions on pack manifests"', 'MUST NOT render it, execute it, or interpret it as markup or a templating directive — a projection that differs from baseline means the value reached an interpreter'),
159
159
  ).toBe(canonicalJson(stripExtensions(plain.json)));
160
160
  });
161
161
  });
@@ -173,7 +173,7 @@ describe('pack-manifest-extension-opacity: the HOST reader stays narrow (RFC 013
173
173
  if (!behaviorGatePresent(PROFILE, res)) return;
174
174
  expect(
175
175
  ok(res.status),
176
- driver.describe('node-packs.md §"Vendor extensions on pack manifests"', 'the hatch admits DECLARED extensions, not arbitrary keys — a host that widened its own reader to additionalProperties:true to "support extensions" accepts everything, which is not the same as accepting extensions'),
176
+ req('openwop.it.pack-manifest-extension-opacity.leg-5-a-misspelled-canonical-field-is-still-rejected-by-the-host-not-merely-by-t', 'node-packs.md §"Vendor extensions on pack manifests"', 'the hatch admits DECLARED extensions, not arbitrary keys — a host that widened its own reader to additionalProperties:true to "support extensions" accepts everything, which is not the same as accepting extensions'),
177
177
  ).toBe(false);
178
178
  });
179
179
  });
@@ -23,6 +23,7 @@ import { describe, it, expect } from 'vitest';
23
23
  import { softSkip } from '../lib/soft-skip.js';
24
24
  import { driver } from '../lib/driver.js';
25
25
  import { discoveryFamilies } from '../lib/discovery-capabilities.js';
26
+ import { req } from '../lib/requirement-ids.js';
26
27
 
27
28
  interface DiscoveryDoc {
28
29
  capabilities?: Record<string, unknown>;
@@ -88,12 +89,12 @@ describe('pack-registry-isolation: test catalog MUST NOT bleed into production (
88
89
  const stringified = body ? JSON.stringify(body) : '';
89
90
  expect(
90
91
  stringified.includes(name),
91
- driver.describe(
92
+ req('openwop.it.pack-registry-isolation.a-pack-put-to-v1-packs-test-name-must-not-appear-in-get-v1-packs-name',
92
93
  'RFCS/0025-test-mode-registry-namespace.md §C point 1',
93
94
  `pack name '${name}' was written via /v1/packs-test/${name}@${version} but appeared in /v1/packs/${name} response body — test-catalog isolation MUST hold`,
94
95
  ),
95
96
  ).toBe(false);
96
- return;
97
+ return softSkip('blocked', 'precondition not met — `prodRes.status === 200` returned early (404 is the canonical "not found" — exactly what isolation requires. 200 with a payload that does NOT name our pack would mean the host returned a listing…');
97
98
  }
98
99
 
99
100
  // Acceptable: 4xx range (404 pack_not_found is the spec-canonical
@@ -101,7 +102,7 @@ describe('pack-registry-isolation: test catalog MUST NOT bleed into production (
101
102
  // signal satisfies the invariant).
102
103
  expect(
103
104
  prodRes.status >= 400 && prodRes.status < 500,
104
- driver.describe(
105
+ req('openwop.it.pack-registry-isolation.a-pack-put-to-v1-packs-test-name-must-not-appear-in-get-v1-packs-name',
105
106
  'RFCS/0025-test-mode-registry-namespace.md §C point 1',
106
107
  `expected production-namespace GET to return 4xx for a test-namespace-only pack '${name}', got ${prodRes.status}`,
107
108
  ),
@@ -26,6 +26,8 @@ import { driver } from '../lib/driver.js';
26
26
  import { discoveryFamilies } from '../lib/discovery-capabilities.js';
27
27
  import { recordRequirement } from '../lib/requirement-ledger.js';
28
28
  import { requirementIdForScenario } from '../lib/requirement-registry.js';
29
+ import { req } from '../lib/requirement-ids.js';
30
+ import { softSkip } from '../lib/soft-skip.js';
29
31
 
30
32
  interface DiscoveryDoc {
31
33
  capabilities?: Record<string, unknown>;
@@ -145,48 +147,48 @@ function errorCode(body: unknown): string | undefined {
145
147
 
146
148
  describe('pack-registry-publish: URL / scope error catalog (RFC 0025)', () => {
147
149
  it('PUT with non-spec scope MUST return 400 invalid_pack_scope', async () => {
148
- if (!(await isTestModeAdvertised())) return;
150
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
149
151
  const res = await putTest('bogus.unsupported-scope.pack', '1.0.0', Buffer.from([]));
150
- if (res.status === 404) return; // seam not exposed
152
+ if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam not exposed) (seam, prior step, or fixture unavailable)'); // seam not exposed
151
153
  expect(res.status).toBeGreaterThanOrEqual(400);
152
154
  expect(res.status).toBeLessThan(500);
153
155
  expect(
154
156
  errorCode(res.json),
155
- driver.describe('node-packs.md §"PUT /v1/packs/{name}/-/{version}.tgz"', 'non-spec scope MUST return invalid_pack_scope'),
157
+ req('openwop.it.pack-registry-publish.put-with-non-spec-scope-must-return-400-invalid-pack-scope', 'node-packs.md §"PUT /v1/packs/{name}/-/{version}.tgz"', 'non-spec scope MUST return invalid_pack_scope'),
156
158
  ).toBe('invalid_pack_scope');
157
159
  });
158
160
 
159
161
  it('PUT with a single-segment URL pack name MUST return 400 invalid_pack_name', async () => {
160
- if (!(await isTestModeAdvertised())) return;
162
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
161
163
  const res = await putTest('singleseg', '1.0.0', Buffer.from([]));
162
- if (res.status === 404) return;
163
- expect(res.status).toBe(400);
164
+ if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam, prior step, or fixture unavailable)');
165
+ expect(res.status, req('openwop.it.pack-registry-publish.put-with-a-single-segment-url-pack-name-must-return-400-invalid-pack-name', 'node-packs.md', 'PUT with a single-segment URL pack name MUST return 400 invalid_pack_name')).toBe(400);
164
166
  expect(errorCode(res.json)).toBe('invalid_pack_name');
165
167
  });
166
168
 
167
169
  it('PUT with a non-semver URL version MUST return 400 invalid_version', async () => {
168
- if (!(await isTestModeAdvertised())) return;
170
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
169
171
  const res = await putTest(freshPackName(), 'not-a-semver', Buffer.from([]));
170
- if (res.status === 404) return;
171
- expect(res.status).toBe(400);
172
+ if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam, prior step, or fixture unavailable)');
173
+ expect(res.status, req('openwop.it.pack-registry-publish.put-with-a-non-semver-url-version-must-return-400-invalid-version', 'node-packs.md', 'PUT with a non-semver URL version MUST return 400 invalid_version')).toBe(400);
172
174
  expect(errorCode(res.json)).toBe('invalid_version');
173
175
  });
174
176
  });
175
177
 
176
178
  describe('pack-registry-publish: body-shape error catalog (RFC 0025)', () => {
177
179
  it('PUT with a JSON body (instead of tarball bytes) MUST return 400 invalid_body', async () => {
178
- if (!(await isTestModeAdvertised())) return;
180
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
179
181
  const res = await driver.put(`/v1/packs-test/${encodeURIComponent(freshPackName())}/-/1.0.0.tgz`, JSON.stringify({}), { headers: { 'Content-Type': 'application/json' } });
180
- if (res.status === 404) return;
181
- expect(res.status).toBe(400);
182
+ if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam, prior step, or fixture unavailable)');
183
+ expect(res.status, req('openwop.it.pack-registry-publish.put-with-a-json-body-instead-of-tarball-bytes-must-return-400-invalid-body', 'node-packs.md', 'PUT with a JSON body (instead of tarball bytes) MUST return 400 invalid_body')).toBe(400);
182
184
  expect(errorCode(res.json)).toBe('invalid_body');
183
185
  });
184
186
 
185
187
  it('PUT with an empty body MUST return 400 invalid_body', async () => {
186
- if (!(await isTestModeAdvertised())) return;
188
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
187
189
  const res = await putTest(freshPackName(), '1.0.0', Buffer.from([]));
188
- if (res.status === 404) return;
189
- expect(res.status).toBe(400);
190
+ if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam, prior step, or fixture unavailable)');
191
+ expect(res.status, req('openwop.it.pack-registry-publish.put-with-an-empty-body-must-return-400-invalid-body', 'node-packs.md', 'PUT with an empty body MUST return 400 invalid_body')).toBe(400);
190
192
  expect(errorCode(res.json)).toBe('invalid_body');
191
193
  });
192
194
  });
@@ -196,15 +198,15 @@ describe('pack-registry-publish: tarball extraction error catalog (RFC 0025)', (
196
198
  // For shape-only assertions, we don't need real gzip; the host's
197
199
  // gunzip step fails first, surfacing tarball_gunzip_failed.
198
200
  it('PUT with a body that isn\'t a valid gzip stream MUST return 400 tarball_gunzip_failed', async () => {
199
- if (!(await isTestModeAdvertised())) return;
201
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
200
202
  const res = await putTest(freshPackName(), '1.0.0', Buffer.from('not a gzip stream'));
201
- if (res.status === 404) return;
202
- expect(res.status).toBe(400);
203
+ if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam, prior step, or fixture unavailable)');
204
+ expect(res.status, req('openwop.it.pack-registry-publish.put-with-a-body-that-isn-t-a-valid-gzip-stream-must-return-400-tarball-gunzip-fa', 'node-packs.md', 'PUT with a body that isn\'t a valid gzip stream MUST return 400 tarball_gunzip_failed')).toBe(400);
203
205
  expect(errorCode(res.json)).toBe('tarball_gunzip_failed');
204
206
  });
205
207
 
206
208
  it('PUT with decompressed bytes exceeding the registry\'s cap MUST return 400 tarball_too_large', async () => {
207
- if (!(await isTestModeAdvertised())) return;
209
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
208
210
  // S45 (2026-08-18): the cap is on DECOMPRESSED bytes (node-packs.md
209
211
  // §"Tarball extraction", recommended default 50 MB), so the probe is a
210
212
  // small VALID gzip stream that inflates past it — 64 MiB of zeros gzips
@@ -219,142 +221,142 @@ describe('pack-registry-publish: tarball extraction error catalog (RFC 0025)', (
219
221
  // tar layer means the cap is absent or above 64 MiB (> the recommended
220
222
  // default) — say so in the run record if that is a deliberate host choice.
221
223
  const res = await putTest(freshPackName(), '1.0.0', OVERSIZED_GZIP);
222
- if (res.status === 404) return;
224
+ if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam, prior step, or fixture unavailable)');
223
225
  expect(
224
226
  res.status,
225
- driver.describe('node-packs.md §"Tarball extraction"', 'a valid gzip inflating past the decompressed cap MUST be refused with 400 (a 413 here means a proxy refused the wire body — this probe is ~64 KB on the wire, so that is a host/proxy misconfiguration, not the cap)'),
227
+ req('openwop.it.pack-registry-publish.put-with-decompressed-bytes-exceeding-the-registry-s-cap-must-return-400-tarball', 'node-packs.md §"Tarball extraction"', 'a valid gzip inflating past the decompressed cap MUST be refused with 400 (a 413 here means a proxy refused the wire body — this probe is ~64 KB on the wire, so that is a host/proxy misconfiguration, not the cap)'),
226
228
  ).toBe(400);
227
229
  expect(
228
230
  errorCode(res.json),
229
- driver.describe('node-packs.md §"Tarball extraction"', 'decompressed bytes exceeding the registry cap MUST surface tarball_too_large (recommended default cap 50 MB; this probe inflates to 64 MiB)'),
231
+ req('openwop.it.pack-registry-publish.put-with-decompressed-bytes-exceeding-the-registry-s-cap-must-return-400-tarball', 'node-packs.md §"Tarball extraction"', 'decompressed bytes exceeding the registry cap MUST surface tarball_too_large (recommended default cap 50 MB; this probe inflates to 64 MiB)'),
230
232
  ).toBe('tarball_too_large');
231
233
  });
232
234
 
233
235
  it('PUT with no `pack.json` at the tarball root MUST return 400 tarball_manifest_missing', async () => {
234
- if (!(await isTestModeAdvertised())) return;
236
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
235
237
  // Stub: a real test would build a minimal gzip+tar with no pack.json.
236
238
  // For now, soft-skip when the host needs a real tarball structure to reach this code path.
237
- return;
239
+ return softSkip('skipped', 'suite-side limitation — Stub: a real test would build a minimal gzip+tar with no pack.json. For now, soft-skip when the host needs a real tarball structure to reach this code path.');
238
240
  });
239
241
 
240
242
  it('PUT with `pack.json` exceeding the registry\'s per-file cap MUST return 400 tarball_manifest_too_large', async () => {
241
- if (!(await isTestModeAdvertised())) return;
242
- return; // requires a real tarball builder — defer to host-side test
243
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
244
+ return softSkip('skipped', 'suite-side limitation — requires a real tarball builder — defer to host-side test'); // requires a real tarball builder — defer to host-side test
243
245
  });
244
246
 
245
247
  it('PUT with `pack.json` that isn\'t valid JSON MUST return 400 tarball_manifest_not_json', async () => {
246
- if (!(await isTestModeAdvertised())) return;
247
- return; // requires a real tarball builder
248
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
249
+ return softSkip('skipped', 'suite-side limitation — requires a real tarball builder'); // requires a real tarball builder
248
250
  });
249
251
 
250
252
  it('PUT with `manifest.runtime.entry` declaring a path that isn\'t in the tarball MUST return 400 tarball_entry_missing', async () => {
251
- if (!(await isTestModeAdvertised())) return;
252
- return; // requires a real tarball builder
253
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
254
+ return softSkip('skipped', 'suite-side limitation — requires a real tarball builder'); // requires a real tarball builder
253
255
  });
254
256
 
255
257
  it('PUT with an entry source exceeding the registry\'s per-file cap MUST return 400 tarball_entry_too_large', async () => {
256
- if (!(await isTestModeAdvertised())) return;
257
- return; // requires a real tarball builder
258
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
259
+ return softSkip('skipped', 'suite-side limitation — requires a real tarball builder'); // requires a real tarball builder
258
260
  });
259
261
 
260
262
  it('PUT with a tarball entry whose name contains `..` or otherwise escapes the pack root MUST return 400 tarball_path_traversal', async () => {
261
- if (!(await isTestModeAdvertised())) return;
262
- return; // requires a real tarball builder
263
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
264
+ return softSkip('skipped', 'suite-side limitation — requires a real tarball builder'); // requires a real tarball builder
263
265
  });
264
266
 
265
267
  it('PUT with a tar stream that the parser can\'t read past the gzip layer MUST return 400 tarball_tar_parse_failed', async () => {
266
- if (!(await isTestModeAdvertised())) return;
268
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
267
269
  // A gzip stream of garbage (header valid, payload not a tar)
268
270
  const garbage = Buffer.from([0x1f, 0x8b, 0x08, 0x00, 0, 0, 0, 0, 0, 0xff, 0x01, 0x02]);
269
271
  const res = await putTest(freshPackName(), '1.0.0', garbage);
270
- if (res.status === 404) return;
271
- if (res.status < 400 || res.status >= 500) return; // host may not reach this code path with garbage gzip
272
+ if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam, prior step, or fixture unavailable)');
273
+ if (res.status < 400 || res.status >= 500) return softSkip('blocked', 'precondition not met — `res.status < 400 || res.status >= 500` returned early (host may not reach this code path with garbage gzip) (seam, prior step, or fixture unavailable)'); // host may not reach this code path with garbage gzip
272
274
  const code = errorCode(res.json);
273
275
  expect(
274
276
  ['tarball_tar_parse_failed', 'tarball_gunzip_failed'].includes(code ?? ''),
275
- driver.describe('node-packs.md', 'garbage gzip stream MUST surface tarball_tar_parse_failed or tarball_gunzip_failed'),
277
+ req('openwop.it.pack-registry-publish.put-with-a-tar-stream-that-the-parser-can-t-read-past-the-gzip-layer-must-return', 'node-packs.md', 'garbage gzip stream MUST surface tarball_tar_parse_failed or tarball_gunzip_failed'),
276
278
  ).toBe(true);
277
279
  });
278
280
  });
279
281
 
280
282
  describe('pack-registry-publish: manifest contents error catalog (RFC 0025)', () => {
281
283
  it('PUT with a `pack.json` that fails schema validation MUST return 400 invalid_manifest', async () => {
282
- if (!(await isTestModeAdvertised())) return;
283
- return; // requires a real tarball builder + intentionally-invalid manifest
284
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
285
+ return softSkip('skipped', 'suite-side limitation — requires a real tarball builder + intentionally-invalid manifest'); // requires a real tarball builder + intentionally-invalid manifest
284
286
  });
285
287
 
286
288
  it('PUT with `manifest.name`/`manifest.version` differing from URL MUST return 400 manifest_mismatch (or granular pair)', async () => {
287
- if (!(await isTestModeAdvertised())) return;
288
- return; // requires a real tarball builder
289
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
290
+ return softSkip('skipped', 'suite-side limitation — requires a real tarball builder'); // requires a real tarball builder
289
291
  });
290
292
 
291
293
  it('PUT with server-computed SHA-256 not matching `X-Pack-Sha256` MUST return 400 pack_integrity_failure', async () => {
292
- if (!(await isTestModeAdvertised())) return;
294
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
293
295
  const res = await putTest(freshPackName(), '1.0.0', Buffer.from([0x1f, 0x8b, 0]), { 'X-Pack-Sha256': '0'.repeat(64) });
294
- if (res.status === 404) return;
295
- if (res.status < 400) return; // host may not validate header on garbage gzip
296
+ if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam, prior step, or fixture unavailable)');
297
+ if (res.status < 400) return softSkip('blocked', 'precondition not met — `res.status < 400` returned early (host may not validate header on garbage gzip) (seam, prior step, or fixture unavailable)'); // host may not validate header on garbage gzip
296
298
  const code = errorCode(res.json);
297
299
  expect(
298
300
  ['pack_integrity_failure', 'tarball_gunzip_failed', 'invalid_body'].includes(code ?? ''),
299
- driver.describe('node-packs.md', 'SHA-256 mismatch MUST be detectable; absence of valid gzip masks this case for the test'),
301
+ req('openwop.it.pack-registry-publish.put-with-server-computed-sha-256-not-matching-x-pack-sha256-must-return-400-pack', 'node-packs.md', 'SHA-256 mismatch MUST be detectable; absence of valid gzip masks this case for the test'),
300
302
  ).toBe(true);
301
303
  });
302
304
 
303
305
  it('PUT with `runtime.language` value not accepted by the registry MUST return 400 unsupported_runtime', async () => {
304
- if (!(await isTestModeAdvertised())) return;
305
- return; // requires a real tarball builder + manifest with unsupported runtime
306
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
307
+ return softSkip('skipped', 'suite-side limitation — requires a real tarball builder + manifest with unsupported runtime'); // requires a real tarball builder + manifest with unsupported runtime
306
308
  });
307
309
  });
308
310
 
309
311
  describe('pack-registry-publish: authorization + conflict (RFC 0025)', () => {
310
312
  it('PUT without `packs:publish` scope or namespace claim MUST return 403 forbidden', async () => {
311
- if (!(await isTestModeAdvertised())) return;
313
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
312
314
  // The test-mode catalog typically allows the conformance suite's API key
313
315
  // by design; this assertion gates on the host returning 403 with the
314
316
  // canonical code when scope IS missing (some hosts MAY accept the suite
315
317
  // key universally — in that case the test soft-skips).
316
- return;
318
+ return softSkip('skipped', 'suite-side limitation — The test-mode catalog typically allows the conformance suite\'s API key by design; this assertion gates on the host returning 403 with the canonical code when scope IS missing (some hosts MAY ac…');
317
319
  });
318
320
 
319
321
  it('PUT for an existing (name, version) with DIFFERENT content MUST return 409 conflict', async () => {
320
- if (!(await isTestModeAdvertised())) return;
321
- return; // requires successful first PUT then conflicting second PUT
322
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
323
+ return softSkip('skipped', 'suite-side limitation — requires successful first PUT then conflicting second PUT'); // requires successful first PUT then conflicting second PUT
322
324
  });
323
325
 
324
326
  it('PUT for an existing (name, version) with IDENTICAL sha256 content MUST return 200 OK (idempotent re-publish)', async () => {
325
- if (!(await isTestModeAdvertised())) return;
326
- return; // requires successful first PUT, then identical second PUT
327
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
328
+ return softSkip('skipped', 'suite-side limitation — requires successful first PUT, then identical second PUT'); // requires successful first PUT, then identical second PUT
327
329
  });
328
330
  });
329
331
 
330
332
  describe('pack-registry-publish: unpublish window (RFC 0025)', () => {
331
333
  it('DELETE for a version older than the unpublish window MUST return 400 unpublish_window_expired', async () => {
332
- if (!(await isTestModeAdvertised())) return;
333
- return; // requires time-travel or an explicit aged-version fixture
334
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
335
+ return softSkip('skipped', 'suite-side limitation — requires time-travel or an explicit aged-version fixture'); // requires time-travel or an explicit aged-version fixture
334
336
  });
335
337
  });
336
338
 
337
339
  describe('pack-registry-publish: signature endpoint pairing (RFC 0025)', () => {
338
340
  it('after PUT WITHOUT signature, GET /sig MUST return 404 signature_not_available', async () => {
339
- if (!(await isTestModeAdvertised())) return;
341
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
340
342
  const name = freshPackName();
341
343
  const sigRes = await getTestSignature(name, '1.0.0');
342
344
  if (sigRes.status === 404) {
343
345
  // Could be either "seam returns 404 on missing pack" OR "signature_not_available 404"
344
346
  const code = errorCode(sigRes.json);
345
- if (code === 'signature_not_available' || code === undefined) return; // shape-conformant either way
347
+ if (code === 'signature_not_available' || code === undefined) return softSkip('blocked', 'precondition not met — `code === \'signature_not_available\' || code === undefined` returned early (shape-conformant either way) (seam, prior step, or fixture unavailable)'); // shape-conformant either way
346
348
  }
347
349
  // If a real test had PUT a pack without sig and gotten 200 back, the next GET .sig MUST be 404.
348
- return; // soft-skip — requires successful prior PUT
350
+ return softSkip('skipped', 'suite-side limitation — soft-skip — requires successful prior PUT'); // soft-skip — requires successful prior PUT
349
351
  });
350
352
 
351
353
  it('after PUT WITH signature blob, GET /sig MUST return 200 (or 302 to signed URL)', async () => {
352
- if (!(await isTestModeAdvertised())) return;
353
- return; // requires real tarball with signature.sig at root
354
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
355
+ return softSkip('skipped', 'suite-side limitation — requires real tarball with signature.sig at root'); // requires real tarball with signature.sig at root
354
356
  });
355
357
 
356
358
  it('after YANK, GET /sig MUST return 404 signature_not_available', async () => {
357
- if (!(await isTestModeAdvertised())) return;
358
- return; // requires successful PUT then YANK
359
+ if (!(await isTestModeAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isTestModeAdvertised())` returned early');
360
+ return softSkip('skipped', 'suite-side limitation — requires successful PUT then YANK'); // requires successful PUT then YANK
359
361
  });
360
362
  });