@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.
- package/README.md +11 -2
- package/dist/cli.js +54 -1
- package/dist/lib/paths.js +25 -3
- package/dist/lib/requirement-ids.js +34 -4
- package/dist/lib/scenario-disposition.js +1 -0
- package/dist/lib/spec-coherence.js +13 -32
- package/dist/spec-artifacts.lock.json +5 -0
- package/package.json +11 -33
- package/requirements.json +20303 -11809
- package/scenario-majors.json +1343 -0
- package/schemas/CORPUS-STAMP.json +138 -6
- package/src/cli.ts +47 -1
- package/src/global-setup.ts +3 -2
- package/src/lib/corpus-stamp.ts +29 -1
- package/src/lib/paths.ts +25 -3
- package/src/lib/requirement-ids.ts +33 -4
- package/src/lib/scenario-disposition.ts +1 -0
- package/src/lib/spec-coherence.ts +15 -32
- package/src/scenarios/a2a-1-0-agent-card.test.ts +17 -16
- package/src/scenarios/a2a-1-0-task-roundtrip.test.ts +11 -10
- package/src/scenarios/a2a-card-runtime-consistency.test.ts +18 -17
- package/src/scenarios/a2a-peer-authority.test.ts +7 -6
- package/src/scenarios/a2a-task-roundtrip.test.ts +30 -29
- package/src/scenarios/a2a-version-negotiation.test.ts +16 -14
- package/src/scenarios/a2ui-surface-degrades.test.ts +5 -3
- package/src/scenarios/a2ui-surface-delta-transport.test.ts +35 -33
- package/src/scenarios/a2ui-surface-replay.test.ts +6 -4
- package/src/scenarios/a2ui-surface-shape.test.ts +11 -10
- package/src/scenarios/a2ui-surface-version-refusal.test.ts +6 -4
- package/src/scenarios/a2ui-untrusted-blocks-approval.test.ts +5 -3
- package/src/scenarios/agent-capability-degraded-projection.test.ts +12 -11
- package/src/scenarios/agent-channel-dispatch.test.ts +18 -16
- package/src/scenarios/agent-deployment-lifecycle.test.ts +20 -19
- package/src/scenarios/agent-deployment-shape.test.ts +22 -24
- package/src/scenarios/agent-eval-run.test.ts +19 -18
- package/src/scenarios/agent-eval-suite-shape.test.ts +20 -22
- package/src/scenarios/agent-live-allowlist-enforced.test.ts +6 -5
- package/src/scenarios/agent-live-invocation-bracket.test.ts +13 -12
- package/src/scenarios/agent-live-runtime-shape.test.ts +10 -12
- package/src/scenarios/agent-live-structured-output.test.ts +7 -6
- package/src/scenarios/agent-loop-iteration-monotonic.test.ts +3 -3
- package/src/scenarios/agent-loop-stateful-resume.test.ts +2 -2
- package/src/scenarios/agent-loop-version5-shape.test.ts +4 -4
- package/src/scenarios/agent-loop-workspace-snapshot.test.ts +3 -3
- package/src/scenarios/agent-manifest-role-profile.test.ts +9 -9
- package/src/scenarios/agent-manifest-runtime.test.ts +13 -12
- package/src/scenarios/agent-org-chart-scoping.test.ts +14 -13
- package/src/scenarios/agent-org-chart-shape.test.ts +11 -13
- package/src/scenarios/agent-platform-aggregate-evidence.test.ts +5 -4
- package/src/scenarios/agent-platform-profile.test.ts +18 -19
- package/src/scenarios/agent-requires-capabilities-shape.test.ts +4 -4
- package/src/scenarios/agent-roster-attribution.test.ts +18 -17
- package/src/scenarios/agent-roster-shape.test.ts +17 -19
- package/src/scenarios/agent-verifier-shape.test.ts +11 -11
- package/src/scenarios/agentConfidenceEscalation.test.ts +2 -1
- package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +6 -5
- package/src/scenarios/agentMemoryRedactionContract.test.ts +2 -1
- package/src/scenarios/agentMemoryRoundTrip.test.ts +2 -1
- package/src/scenarios/agentMemoryTtlExpiry.test.ts +4 -3
- package/src/scenarios/agentMessageReducer.test.ts +2 -1
- package/src/scenarios/agentMetadata.test.ts +2 -1
- package/src/scenarios/agentPackCatalog.test.ts +23 -21
- package/src/scenarios/agentPackExport.test.ts +4 -2
- package/src/scenarios/agentPackHandoffSchemaValidation.test.ts +5 -4
- package/src/scenarios/agentPackInstall.test.ts +4 -2
- package/src/scenarios/agentPackProvenance.test.ts +2 -1
- package/src/scenarios/agentReasoningEvents.test.ts +3 -2
- package/src/scenarios/agentReasoningStreaming.test.ts +11 -10
- package/src/scenarios/agents-run-tool-allowlist.test.ts +14 -12
- package/src/scenarios/ai-envelope-shape.test.ts +31 -29
- package/src/scenarios/aiEnvelope.capBreached.test.ts +26 -24
- package/src/scenarios/aiEnvelope.contractRefusal.test.ts +36 -34
- package/src/scenarios/aiEnvelope.correlationReplay.test.ts +17 -15
- package/src/scenarios/aiEnvelope.redaction.test.ts +22 -20
- package/src/scenarios/aiEnvelope.schemaDrift.test.ts +18 -16
- package/src/scenarios/aiEnvelope.trustBoundaryPropagation.test.ts +38 -36
- package/src/scenarios/aiEnvelope.universalKinds.test.ts +31 -29
- package/src/scenarios/aiproviders-input-shape.test.ts +5 -5
- package/src/scenarios/aiproviders-realtimevoice-shape.test.ts +14 -16
- package/src/scenarios/aiproviders-selfhosted-honesty.test.ts +6 -4
- package/src/scenarios/aiproviders-selfhosted-shape.test.ts +15 -17
- package/src/scenarios/aiproviders-speechsynth-shape.test.ts +6 -8
- package/src/scenarios/aiproviders-subscription-scope.test.ts +7 -8
- package/src/scenarios/anonymous-actor-audit-opaque.test.ts +9 -8
- package/src/scenarios/anonymous-actor-default-deny.test.ts +10 -9
- package/src/scenarios/anonymous-actor-egress-guarded.test.ts +6 -5
- package/src/scenarios/anonymous-actor-no-secret-reach.test.ts +7 -6
- package/src/scenarios/anonymous-actor-shape.test.ts +17 -19
- package/src/scenarios/anonymous-actor-write-gated.test.ts +9 -8
- package/src/scenarios/append-ordering.test.ts +7 -5
- package/src/scenarios/approval-gate-events.test.ts +4 -3
- package/src/scenarios/approval-gate-flow.test.ts +4 -3
- package/src/scenarios/approval-payload.test.ts +5 -4
- package/src/scenarios/artifact-auth.test.ts +7 -6
- package/src/scenarios/artifact-type-pack-install.test.ts +6 -6
- package/src/scenarios/artifact-type-pack-manifest-validation.test.ts +9 -8
- package/src/scenarios/artifact-type-registration-source.test.ts +20 -20
- package/src/scenarios/artifact-type-store-emission.test.ts +15 -18
- package/src/scenarios/artifact-type-store-without-render.test.ts +4 -4
- package/src/scenarios/audit-log-integrity.test.ts +8 -6
- package/src/scenarios/auth-api-key-rotation.test.ts +11 -9
- package/src/scenarios/auth-mtls.test.ts +22 -20
- package/src/scenarios/auth-oauth2-client-credentials.test.ts +16 -14
- package/src/scenarios/auth-oidc-user-bearer.test.ts +26 -24
- package/src/scenarios/auth-saml-profile.test.ts +14 -12
- package/src/scenarios/auth-scim-profile.test.ts +3 -2
- package/src/scenarios/auth-subject-link-key-class.test.ts +8 -7
- package/src/scenarios/auth-subject-link.test.ts +11 -10
- package/src/scenarios/auth.test.ts +5 -4
- package/src/scenarios/authorization-fail-closed.test.ts +3 -2
- package/src/scenarios/authorization-roles-shape.test.ts +9 -7
- package/src/scenarios/blob-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/blob-presign-expiry.test.ts +12 -10
- package/src/scenarios/blob-roundtrip.test.ts +0 -0
- package/src/scenarios/budget-enforcement.test.ts +15 -14
- package/src/scenarios/budget-policy-shape.test.ts +18 -19
- package/src/scenarios/bulk-cancel.test.ts +9 -8
- package/src/scenarios/byok-auth-modes.test.ts +13 -14
- package/src/scenarios/byok-roundtrip.test.ts +12 -10
- package/src/scenarios/cache-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/cache-ttl-expiry.test.ts +7 -5
- package/src/scenarios/callai-multimodal.test.ts +5 -3
- package/src/scenarios/cancellation.test.ts +6 -5
- package/src/scenarios/cap-breach.test.ts +15 -14
- package/src/scenarios/capability-arm-witness.test.ts +5 -3
- package/src/scenarios/certification-bundle-non-vacuous.test.ts +20 -19
- package/src/scenarios/certification-bundle-redaction.test.ts +11 -10
- package/src/scenarios/certification-bundle-v2.test.ts +11 -10
- package/src/scenarios/chain-compensation-expansion.test.ts +14 -13
- package/src/scenarios/chain-produced-var-roundtrip.test.ts +13 -14
- package/src/scenarios/chain-subchain-cycle-rejected.test.ts +9 -10
- package/src/scenarios/chain-subchain-fanout.test.ts +8 -9
- package/src/scenarios/chain-subchain-sibling.test.ts +14 -16
- package/src/scenarios/channel-presence-behavioral.test.ts +9 -7
- package/src/scenarios/channel-presence-shape.test.ts +11 -12
- package/src/scenarios/channel-ttl.test.ts +6 -5
- package/src/scenarios/chat-card-pack-execution.test.ts +4 -4
- package/src/scenarios/chat-card-pack-manifest-validation.test.ts +9 -8
- package/src/scenarios/commitment-fired.test.ts +6 -5
- package/src/scenarios/compensation-behavior.test.ts +24 -23
- package/src/scenarios/compensation-recovery.test.ts +28 -27
- package/src/scenarios/configurable-schema.test.ts +8 -6
- package/src/scenarios/conformance-advertised-seam-required.test.ts +6 -5
- package/src/scenarios/conformance-execution-witness.test.ts +9 -8
- package/src/scenarios/connection-pack-apihosts.test.ts +19 -17
- package/src/scenarios/connection-pack-manifest-valid.test.ts +12 -11
- package/src/scenarios/connection-pack-no-credential-material.test.ts +8 -6
- package/src/scenarios/connection-pack-reach-exclusive.test.ts +5 -4
- package/src/scenarios/connection-pack-write-reconsent.test.ts +6 -4
- package/src/scenarios/connection-provider-resolution.test.ts +18 -16
- package/src/scenarios/connector-manifest-validity.test.ts +8 -7
- package/src/scenarios/context-budget-transcript-bound.test.ts +17 -15
- package/src/scenarios/context-summarization-replay.test.ts +14 -12
- package/src/scenarios/contract-provenance.test.ts +18 -18
- package/src/scenarios/conversation-turn-model-provenance-shape.test.ts +14 -15
- package/src/scenarios/conversationCapabilityNegotiation.test.ts +2 -1
- package/src/scenarios/conversationLifecycle.test.ts +2 -1
- package/src/scenarios/conversationReplayDeterminism.test.ts +4 -2
- package/src/scenarios/conversationVsLegacySuspend.test.ts +2 -1
- package/src/scenarios/core-standard-profile.test.ts +8 -9
- package/src/scenarios/cost-attribution.test.ts +23 -21
- package/src/scenarios/credential-payload-redaction.test.ts +4 -3
- package/src/scenarios/credentials-capability-shape.test.ts +5 -4
- package/src/scenarios/cross-engine-append-behavior.test.ts +11 -10
- package/src/scenarios/cross-engine-append-ordering.test.ts +4 -3
- package/src/scenarios/cross-host-ancestry-endpoint.test.ts +11 -10
- package/src/scenarios/cross-host-causation-shape.test.ts +7 -6
- package/src/scenarios/cross-workspace-isolation.test.ts +4 -3
- package/src/scenarios/data-residency-admission.test.ts +15 -14
- package/src/scenarios/deadletter-capability-shape.test.ts +6 -4
- package/src/scenarios/deadletter-retry-exhaustion.test.ts +4 -3
- package/src/scenarios/debug-bundle-truncation.test.ts +9 -7
- package/src/scenarios/debugBundle.test.ts +25 -23
- package/src/scenarios/discovery-families-view.test.ts +5 -4
- package/src/scenarios/discovery.test.ts +30 -29
- package/src/scenarios/dispatch-cross-worker-handoff.test.ts +10 -8
- package/src/scenarios/dispatch-fanout-parallel.test.ts +24 -22
- package/src/scenarios/dispatch-input-mapping.test.ts +12 -10
- package/src/scenarios/dispatch-output-mapping.test.ts +9 -7
- package/src/scenarios/dispatch-per-item-input.test.ts +22 -20
- package/src/scenarios/dispatchLoop.test.ts +3 -2
- package/src/scenarios/distillation-index-roundtrip.test.ts +3 -3
- package/src/scenarios/distillation-secret-carryforward.test.ts +2 -2
- package/src/scenarios/distillation-shape.test.ts +4 -4
- package/src/scenarios/distillation-stable-archive.test.ts +6 -6
- package/src/scenarios/distillation-token-budget.test.ts +5 -5
- package/src/scenarios/durability-poison-exhaustion.test.ts +7 -6
- package/src/scenarios/egress-audience-binding.test.ts +11 -10
- package/src/scenarios/egress-decision-content-free.test.ts +6 -5
- package/src/scenarios/egress-provenance-shape.test.ts +17 -18
- package/src/scenarios/envelope-completion-distinguishes-truncation.test.ts +27 -25
- package/src/scenarios/envelope-nl-to-format-engaged.test.ts +19 -17
- package/src/scenarios/envelope-reasoning-secret-redaction.test.ts +28 -26
- package/src/scenarios/envelope-reasoning-shape.test.ts +19 -17
- package/src/scenarios/envelope-recovery-applied.test.ts +24 -22
- package/src/scenarios/envelope-refusal-shape.test.ts +31 -29
- package/src/scenarios/envelope-rendering-hint.test.ts +5 -4
- package/src/scenarios/envelope-retry-attempted.test.ts +33 -31
- package/src/scenarios/envelope-retry-exhausted.test.ts +22 -20
- package/src/scenarios/envelope-tier-one-subset-static.test.ts +9 -7
- package/src/scenarios/envelope-truncated.test.ts +19 -17
- package/src/scenarios/envelope-truncation-cap-exhaustion.test.ts +19 -17
- package/src/scenarios/envelope-variant-discriminator-static.test.ts +4 -3
- package/src/scenarios/errors.test.ts +20 -19
- package/src/scenarios/eventOrdering.test.ts +16 -14
- package/src/scenarios/exec-not-protocol-tier.test.ts +5 -7
- package/src/scenarios/experimental-tier-shape.test.ts +17 -15
- package/src/scenarios/export-bundle-portability.test.ts +17 -17
- package/src/scenarios/failure-path.test.ts +7 -6
- package/src/scenarios/feedback-capability-shape.test.ts +4 -3
- package/src/scenarios/feedback-correction-redaction.test.ts +2 -1
- package/src/scenarios/feedback-cross-tenant-isolation.test.ts +2 -1
- package/src/scenarios/feedback-fork-not-copied.test.ts +3 -2
- package/src/scenarios/feedback-on-terminal-run.test.ts +3 -2
- package/src/scenarios/feedback-record-and-list.test.ts +2 -1
- package/src/scenarios/feedback-unsupported-501.test.ts +2 -1
- package/src/scenarios/fixtures-gating.test.ts +29 -28
- package/src/scenarios/fixtures-valid.test.ts +16 -15
- package/src/scenarios/form-content-instantiation.test.ts +15 -14
- package/src/scenarios/frontend-plugin-packs.test.ts +29 -27
- package/src/scenarios/fs-path-traversal.test.ts +14 -12
- package/src/scenarios/goal-standing-continuation.test.ts +21 -21
- package/src/scenarios/grpc-transport.test.ts +9 -7
- package/src/scenarios/heartbeat-capability-shape.test.ts +3 -3
- package/src/scenarios/heartbeat-fires-once-per-tick.test.ts +2 -2
- package/src/scenarios/heartbeat-idempotent-no-spam.test.ts +3 -3
- package/src/scenarios/heartbeat-runtime-bound.test.ts +2 -2
- package/src/scenarios/highConcurrency.test.ts +14 -13
- package/src/scenarios/host-callback-declaration.test.ts +8 -7
- package/src/scenarios/http-client-ssrf.test.ts +7 -5
- package/src/scenarios/i18n-negotiation.test.ts +14 -12
- package/src/scenarios/idempotency-concurrent-claim.test.ts +14 -13
- package/src/scenarios/idempotency-key-determinism.test.ts +20 -18
- package/src/scenarios/idempotency.test.ts +8 -7
- package/src/scenarios/idempotencyRetry.test.ts +13 -11
- package/src/scenarios/identity-owner-shape.test.ts +6 -5
- package/src/scenarios/identity-passthrough.test.ts +4 -3
- package/src/scenarios/interrupt-approval.test.ts +6 -5
- package/src/scenarios/interrupt-approver-routing.test.ts +15 -15
- package/src/scenarios/interrupt-auth-required-resume.test.ts +5 -3
- package/src/scenarios/interrupt-clarification.test.ts +4 -3
- package/src/scenarios/interrupt-external-event-correlation.test.ts +6 -5
- package/src/scenarios/interrupt-parent-child-cascade.test.ts +4 -3
- package/src/scenarios/interrupt-quorum-resolution.test.ts +5 -4
- package/src/scenarios/interrupt-token-matrix.test.ts +8 -6
- package/src/scenarios/interruptRace.test.ts +12 -10
- package/src/scenarios/kv-atomic-increment.test.ts +11 -9
- package/src/scenarios/kv-cas.test.ts +14 -12
- package/src/scenarios/kv-cross-tenant-isolation.test.ts +12 -10
- package/src/scenarios/kv-ttl-expiry.test.ts +7 -5
- package/src/scenarios/localized-content-delivery.test.ts +24 -26
- package/src/scenarios/maliciousManifest.test.ts +10 -8
- package/src/scenarios/mcp-2026-07-28-discover.test.ts +15 -14
- package/src/scenarios/mcp-cache-tenant-scope.test.ts +6 -5
- package/src/scenarios/mcp-current-auth-boundary.test.ts +7 -6
- package/src/scenarios/mcp-discoverability.test.ts +9 -7
- package/src/scenarios/mcp-extension-opacity.test.ts +7 -6
- package/src/scenarios/mcp-mrtr-roundtrip.test.ts +18 -17
- package/src/scenarios/mcp-server-elicitation-bridge.test.ts +13 -12
- package/src/scenarios/mcp-server-prompt-roundtrip.test.ts +11 -10
- package/src/scenarios/mcp-server-resource-roundtrip.test.ts +11 -10
- package/src/scenarios/mcp-server-sampling-bridge.test.ts +12 -11
- package/src/scenarios/mcp-server-tool-roundtrip.test.ts +14 -13
- package/src/scenarios/mcp-server-untrusted-args.test.ts +14 -13
- package/src/scenarios/mcp-stateless-request.test.ts +11 -10
- package/src/scenarios/mcp-tool-roundtrip.test.ts +4 -3
- package/src/scenarios/mcp-toolcall-redaction.test.ts +4 -3
- package/src/scenarios/mcp-version-negotiation.test.ts +15 -14
- package/src/scenarios/media-url-inline-cap.test.ts +21 -19
- package/src/scenarios/memory-attribution-emits-on-write.test.ts +5 -5
- package/src/scenarios/memory-attribution-no-content.test.ts +4 -4
- package/src/scenarios/memory-attribution-replay-stable.test.ts +4 -3
- package/src/scenarios/memory-attribution-shape.test.ts +4 -3
- package/src/scenarios/memory-attribution-tenant-scoped.test.ts +3 -3
- package/src/scenarios/memory-capability-model-shape.test.ts +14 -15
- package/src/scenarios/memory-compaction-event-emitted.test.ts +7 -6
- package/src/scenarios/memory-compaction-provenance-tag.test.ts +3 -2
- package/src/scenarios/memory-compaction-sr1-carry-forward.test.ts +7 -6
- package/src/scenarios/memory-consolidation-idempotent.test.ts +5 -4
- package/src/scenarios/memory-consolidation-shape.test.ts +9 -11
- package/src/scenarios/memory-degraded-projection.test.ts +10 -9
- package/src/scenarios/memory-injection-budget.test.ts +15 -14
- package/src/scenarios/metric-emission.test.ts +4 -3
- package/src/scenarios/model-capability-insufficient.test.ts +19 -17
- package/src/scenarios/model-capability-substituted.test.ts +20 -18
- package/src/scenarios/multi-agent-confidence-escalation.test.ts +12 -11
- package/src/scenarios/multi-agent-handoff-state-machine.test.ts +10 -9
- package/src/scenarios/multi-agent-memory-lifecycle.test.ts +11 -10
- package/src/scenarios/multi-node-ordering.test.ts +4 -3
- package/src/scenarios/multi-party-conversation-behavioral.test.ts +6 -6
- package/src/scenarios/multi-party-conversation-shape.test.ts +21 -23
- package/src/scenarios/multi-region-idempotency-behavior.test.ts +16 -15
- package/src/scenarios/multi-region-idempotency.test.ts +6 -5
- package/src/scenarios/node-failed-payload-shape.test.ts +5 -4
- package/src/scenarios/node-module-required-capabilities-shape.test.ts +10 -8
- package/src/scenarios/oauth-authorization-code-roundtrip.test.ts +5 -4
- package/src/scenarios/oauth-capability-shape.test.ts +5 -4
- package/src/scenarios/oauth-connector-redaction.test.ts +4 -3
- package/src/scenarios/openapi-resolved-paths.test.ts +6 -5
- package/src/scenarios/orchestratorConservativePath.test.ts +2 -1
- package/src/scenarios/orchestratorDispatch.test.ts +2 -1
- package/src/scenarios/orchestratorTermination.test.ts +2 -1
- package/src/scenarios/org-position-no-authority-escalation.test.ts +2 -2
- package/src/scenarios/otel-collector-canary-inspection.test.ts +8 -7
- package/src/scenarios/otel-emission-grpc.test.ts +3 -2
- package/src/scenarios/otel-emission.test.ts +6 -5
- package/src/scenarios/otel-trace-propagation-subworkflow.test.ts +7 -6
- package/src/scenarios/otel-trace-propagation.test.ts +2 -1
- package/src/scenarios/owner-subject-echo.test.ts +13 -12
- package/src/scenarios/owner-subject-shape.test.ts +11 -10
- package/src/scenarios/pack-manifest-extension-opacity.test.ts +8 -8
- package/src/scenarios/pack-registry-isolation.test.ts +4 -3
- package/src/scenarios/pack-registry-publish.test.ts +65 -63
- package/src/scenarios/pack-registry.test.ts +38 -36
- package/src/scenarios/pause-resume.test.ts +18 -16
- package/src/scenarios/policies.test.ts +20 -18
- package/src/scenarios/production-backpressure.test.ts +18 -16
- package/src/scenarios/production-retention-expiry.test.ts +8 -6
- package/src/scenarios/profile-discovery-core-alias.test.ts +13 -11
- package/src/scenarios/profileDerivation.test.ts +34 -33
- package/src/scenarios/prompt-all-four-kinds-events.test.ts +11 -9
- package/src/scenarios/prompt-composed-secret-redaction.test.ts +11 -9
- package/src/scenarios/prompt-composed-trust-marker.test.ts +10 -8
- package/src/scenarios/prompt-end-to-end-events.test.ts +17 -15
- package/src/scenarios/prompt-list-and-fetch.test.ts +16 -14
- package/src/scenarios/prompt-mutable-lifecycle.test.ts +13 -11
- package/src/scenarios/prompt-mutation-workspace-membership-enforced.test.ts +6 -4
- package/src/scenarios/prompt-pack-install.test.ts +14 -12
- package/src/scenarios/prompt-prefix-cache.test.ts +19 -17
- package/src/scenarios/prompt-read-workspace-membership-enforced.test.ts +11 -9
- package/src/scenarios/prompt-render-deterministic.test.ts +19 -17
- package/src/scenarios/prompt-resolution-chain-agent-intrinsic.test.ts +7 -5
- package/src/scenarios/prompt-resolution-chain-event.test.ts +12 -10
- package/src/scenarios/prompt-resolution-chain-fallback-cascade.test.ts +10 -8
- package/src/scenarios/prompt-resolution-chain-node-wins.test.ts +8 -6
- package/src/scenarios/prompt-template-shape.test.ts +30 -28
- package/src/scenarios/proposal-reviewable-learning.test.ts +19 -19
- package/src/scenarios/protocol-versions-array.test.ts +10 -9
- package/src/scenarios/provider-usage.test.ts +17 -15
- package/src/scenarios/providerPolicyEnforcement.test.ts +16 -14
- package/src/scenarios/purpose-propagation.test.ts +23 -21
- package/src/scenarios/queue-ack-nack-dlq.test.ts +12 -10
- package/src/scenarios/queue-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/queue-publish-consume-roundtrip.test.ts +10 -8
- package/src/scenarios/rate-limit-envelope.test.ts +6 -5
- package/src/scenarios/redaction.test.ts +13 -11
- package/src/scenarios/redactionAdversarial.test.ts +11 -9
- package/src/scenarios/registry-public.test.ts +12 -11
- package/src/scenarios/replay-divergence-at-refusal.test.ts +16 -15
- package/src/scenarios/replay-fanout-suppression.test.ts +16 -15
- package/src/scenarios/replay-fork-arbitrary.test.ts +24 -23
- package/src/scenarios/replay-fork.test.ts +20 -19
- package/src/scenarios/replay-llm-cache-key-portable.test.ts +11 -9
- package/src/scenarios/replay-llm-cache-key.test.ts +22 -21
- package/src/scenarios/replay-observable-sequence-determinism.test.ts +4 -3
- package/src/scenarios/replay-retention-expiry.test.ts +11 -9
- package/src/scenarios/replay-side-effect-suppression.test.ts +21 -19
- package/src/scenarios/replayDeterminism.test.ts +13 -12
- package/src/scenarios/requirement-ledger.test.ts +15 -14
- package/src/scenarios/restart-during-run.test.ts +3 -2
- package/src/scenarios/route-coverage.test.ts +20 -19
- package/src/scenarios/run-diff.test.ts +22 -20
- package/src/scenarios/run-execution-bounds-shape.test.ts +14 -12
- package/src/scenarios/run-transport-economy.test.ts +13 -11
- package/src/scenarios/runner-ledger.test.ts +17 -16
- package/src/scenarios/runs-lifecycle.test.ts +7 -6
- package/src/scenarios/runtime-capabilities.test.ts +12 -10
- package/src/scenarios/runtime-requires-install-gate.test.ts +8 -8
- package/src/scenarios/runtime-requires-shape.test.ts +7 -6
- package/src/scenarios/safefetch-behavior.test.ts +22 -21
- package/src/scenarios/safefetch-live-audit.test.ts +14 -13
- package/src/scenarios/sandbox-memory-cap.test.ts +2 -1
- package/src/scenarios/sandbox-mvp-behavior.test.ts +24 -23
- package/src/scenarios/sandbox-no-host-fs-escape.test.ts +3 -2
- package/src/scenarios/sandbox-timeout-cap.test.ts +2 -1
- package/src/scenarios/sandbox-wasm-isolation.test.ts +14 -15
- package/src/scenarios/sandbox-wasm-timeout.test.ts +5 -6
- package/src/scenarios/scheduling-capability-shape.test.ts +8 -6
- package/src/scenarios/scheduling-cron-fires-once.test.ts +8 -6
- package/src/scenarios/search-bm25-roundtrip.test.ts +8 -6
- package/src/scenarios/secret-leakage-otel-attribute.test.ts +22 -20
- package/src/scenarios/self-hosted-runner.test.ts +26 -24
- package/src/scenarios/semantic-digest-vectors.test.ts +8 -7
- package/src/scenarios/speech-synthesis-roundtrip.test.ts +9 -7
- package/src/scenarios/speech-synthesis-unadvertised.test.ts +5 -3
- package/src/scenarios/sql-injection-rejection.test.ts +8 -6
- package/src/scenarios/sql-transaction-atomicity.test.ts +10 -8
- package/src/scenarios/staleClaim.test.ts +5 -4
- package/src/scenarios/stream-modes-buffer.test.ts +8 -7
- package/src/scenarios/stream-modes-mixed.test.ts +10 -9
- package/src/scenarios/stream-modes.test.ts +12 -11
- package/src/scenarios/stream-subscribe-from-beginning.test.ts +11 -9
- package/src/scenarios/stream-text-fixture.test.ts +14 -13
- package/src/scenarios/streamReconnect.test.ts +13 -11
- package/src/scenarios/strict-behavior-gate.test.ts +9 -8
- package/src/scenarios/subrun-approval-fail-closed.test.ts +2 -2
- package/src/scenarios/subrun-approval-gate.test.ts +3 -3
- package/src/scenarios/subrun-attestation-shape.test.ts +2 -2
- package/src/scenarios/subrun-checksum-stable.test.ts +4 -4
- package/src/scenarios/subworkflow-input-mapping.test.ts +16 -14
- package/src/scenarios/subworkflow.test.ts +10 -9
- package/src/scenarios/table-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/table-cursor-pagination.test.ts +8 -6
- package/src/scenarios/table-schema-enforcement.test.ts +7 -5
- package/src/scenarios/test-seam-unauthenticated.test.ts +2 -2
- package/src/scenarios/tool-catalog-compact-projection.test.ts +10 -8
- package/src/scenarios/tool-catalog-projection.test.ts +11 -9
- package/src/scenarios/tool-descriptor-shape.test.ts +14 -15
- package/src/scenarios/tool-hooks-authorization-fail-closed.test.ts +3 -3
- package/src/scenarios/tool-hooks-content-free.test.ts +4 -4
- package/src/scenarios/tool-hooks-failure-honesty.test.ts +4 -4
- package/src/scenarios/tool-hooks-rate-limit.test.ts +2 -2
- package/src/scenarios/tool-hooks-secret-redaction.test.ts +2 -2
- package/src/scenarios/tool-hooks-shape.test.ts +3 -3
- package/src/scenarios/tool-session-lifecycle.test.ts +14 -13
- package/src/scenarios/trigger-bridge-delivery.test.ts +14 -14
- package/src/scenarios/trigger-bridge-shape.test.ts +19 -20
- package/src/scenarios/trigger-ingestion.test.ts +19 -17
- package/src/scenarios/trigger-stream-cdc-sources.test.ts +16 -14
- package/src/scenarios/vector-knn-roundtrip.test.ts +9 -7
- package/src/scenarios/verifier-gating.test.ts +8 -6
- package/src/scenarios/version-fold.test.ts +9 -8
- package/src/scenarios/version-negotiation.test.ts +12 -11
- package/src/scenarios/voice-bargein-no-partial-leak.test.ts +8 -6
- package/src/scenarios/voice-event-payloads-shape.test.ts +10 -11
- package/src/scenarios/voice-interim-not-durable.test.ts +7 -5
- package/src/scenarios/voice-streamref-tenant-bound.test.ts +5 -3
- package/src/scenarios/voice-synthesis-streaming.test.ts +11 -9
- package/src/scenarios/voice-transcription-streaming.test.ts +8 -6
- package/src/scenarios/voice-transcription-unadvertised.test.ts +5 -3
- package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +3 -2
- package/src/scenarios/wasm-pack-invoke-completed.test.ts +3 -2
- package/src/scenarios/wasm-pack-invoke-suspended.test.ts +3 -2
- package/src/scenarios/wasm-pack-load.test.ts +4 -3
- package/src/scenarios/wasm-pack-memory-cap.test.ts +5 -4
- package/src/scenarios/wasm-pack-replay-determinism.test.ts +2 -1
- package/src/scenarios/webhook-negative.test.ts +11 -9
- package/src/scenarios/webhook-receiver-adversarial.test.ts +8 -7
- package/src/scenarios/webhook-sig-algorithm.test.ts +5 -3
- package/src/scenarios/webhook-signed-delivery.test.ts +16 -15
- package/src/scenarios/webhook-tenant-isolation.test.ts +16 -14
- package/src/scenarios/workflow-chain-deferred-parameters.test.ts +28 -30
- package/src/scenarios/workflow-chain-expansion.test.ts +21 -20
- package/src/scenarios/workflow-chain-host-expansion.test.ts +21 -20
- package/src/scenarios/workflow-chain-pack-manifest-validation.test.ts +14 -12
- package/src/scenarios/workflow-chain-pack-signature-verification.test.ts +5 -4
- package/src/scenarios/workflow-chain-unresolvable-typeid.test.ts +9 -8
- package/src/scenarios/workflow-primary-output-annotation.test.ts +7 -6
- package/src/scenarios/workflow-variable-format.test.ts +24 -24
- package/src/scenarios/workload-identity-behavior.test.ts +15 -13
- package/src/scenarios/workload-identity-chain-bounds.test.ts +18 -17
- package/src/scenarios/workspace-behavior.test.ts +19 -18
- package/src/scenarios/workspace-capability-shape.test.ts +3 -2
- package/src/scenarios/workspace-cross-tenant-isolation-blackbox.test.ts +6 -5
- package/src/scenarios/workspace-cross-tenant-isolation.test.ts +7 -6
- package/src/scenarios/x-openwop-form-pack-manifest.test.ts +8 -7
- package/src/setup.ts +4 -2
- package/vitest.config.ts +5 -1
- package/api/.redocly.lint-ignore.yaml +0 -22
- package/api/asyncapi.yaml +0 -834
- package/api/grpc/openwop.proto +0 -251
- package/api/openapi.yaml +0 -2693
- package/api/redocly.yaml +0 -23
- package/schemas/README.md +0 -144
- package/schemas/a2a-task-state.schema.json +0 -78
- package/schemas/a2ui-surface-delta-frame.schema.json +0 -48
- package/schemas/agent-deployment-transition.schema.json +0 -49
- package/schemas/agent-deployment.schema.json +0 -54
- package/schemas/agent-eval-suite.schema.json +0 -140
- package/schemas/agent-inventory-response.schema.json +0 -115
- package/schemas/agent-manifest.schema.json +0 -156
- package/schemas/agent-org-chart.schema.json +0 -82
- package/schemas/agent-ref.schema.json +0 -63
- package/schemas/agent-roster-entry.schema.json +0 -81
- package/schemas/agent-roster-response.schema.json +0 -21
- package/schemas/ai-envelope.schema.json +0 -134
- package/schemas/annotation-create.schema.json +0 -37
- package/schemas/annotation.schema.json +0 -56
- package/schemas/artifact-type-pack-manifest.schema.json +0 -244
- package/schemas/audit-verify-result.schema.json +0 -90
- package/schemas/budget-policy.schema.json +0 -18
- package/schemas/capabilities.schema.json +0 -4124
- package/schemas/certification-bundle-v2.schema.json +0 -118
- package/schemas/channel-presence-payload.schema.json +0 -41
- package/schemas/channel-written-payload.schema.json +0 -55
- package/schemas/chat-card-pack-manifest.schema.json +0 -253
- package/schemas/compact-tool-descriptor.schema.json +0 -51
- package/schemas/compensation-policy.schema.json +0 -64
- package/schemas/conformance-certification-bundle.schema.json +0 -86
- package/schemas/connection-pack-manifest.schema.json +0 -353
- package/schemas/conversation-event.schema.json +0 -168
- package/schemas/conversation-turn.schema.json +0 -117
- package/schemas/core-conformance-mock-agent-config.schema.json +0 -152
- package/schemas/credential-provenance.schema.json +0 -18
- package/schemas/credential-reference.schema.json +0 -21
- package/schemas/debug-bundle.schema.json +0 -196
- package/schemas/dispatch-config.schema.json +0 -112
- package/schemas/envelopes/clarification.request.schema.json +0 -52
- package/schemas/envelopes/error.schema.json +0 -30
- package/schemas/envelopes/media.audio.schema.json +0 -38
- package/schemas/envelopes/media.file.schema.json +0 -37
- package/schemas/envelopes/media.image.schema.json +0 -33
- package/schemas/envelopes/schema.request.schema.json +0 -26
- package/schemas/envelopes/schema.response.schema.json +0 -22
- package/schemas/envelopes/ui.a2ui-surface.schema.json +0 -154
- package/schemas/error-envelope.schema.json +0 -25
- package/schemas/eval-summary.schema.json +0 -92
- package/schemas/export-bundle.schema.json +0 -66
- package/schemas/form-content-pack-manifest.schema.json +0 -243
- package/schemas/frontend-plugin-manifest.schema.json +0 -109
- package/schemas/goal.schema.json +0 -104
- package/schemas/heartbeat-evaluated.schema.json +0 -14
- package/schemas/heartbeat-state-changed.schema.json +0 -14
- package/schemas/localized-content-language-settings.schema.json +0 -26
- package/schemas/localized-content-page-response.schema.json +0 -60
- package/schemas/localized-content-page.schema.json +0 -62
- package/schemas/localized-content-section.schema.json +0 -51
- package/schemas/memory-entry.schema.json +0 -36
- package/schemas/memory-list-options.schema.json +0 -37
- package/schemas/node-pack-manifest.schema.json +0 -654
- package/schemas/orchestrator-decision.schema.json +0 -90
- package/schemas/org-chart-responsibility-view.schema.json +0 -26
- package/schemas/pack-lockfile.schema.json +0 -108
- package/schemas/prompt-kind.schema.json +0 -8
- package/schemas/prompt-pack-manifest.schema.json +0 -117
- package/schemas/prompt-ref.schema.json +0 -40
- package/schemas/prompt-template.schema.json +0 -149
- package/schemas/proposal.schema.json +0 -84
- package/schemas/registry-version-manifest.schema.json +0 -208
- package/schemas/residency.schema.json +0 -16
- package/schemas/run-ancestry-response.schema.json +0 -54
- package/schemas/run-diff-response.schema.json +0 -64
- package/schemas/run-event-payloads.schema.json +0 -4452
- package/schemas/run-event.schema.json +0 -211
- package/schemas/run-options.schema.json +0 -80
- package/schemas/run-orchestrator-decided-event.schema.json +0 -20
- package/schemas/run-snapshot.schema.json +0 -148
- package/schemas/security-advisory.schema.json +0 -109
- package/schemas/self-hosted-runner-dispatch-frame.schema.json +0 -79
- package/schemas/self-hosted-runner-registration.schema.json +0 -53
- package/schemas/self-hosted-runner-result-frame.schema.json +0 -38
- package/schemas/subject.schema.json +0 -56
- package/schemas/suspend-request.schema.json +0 -207
- package/schemas/tool-descriptor.schema.json +0 -63
- package/schemas/trigger-event.schema.json +0 -222
- package/schemas/trigger-subscription-registration.schema.json +0 -67
- package/schemas/trigger-subscription.schema.json +0 -26
- package/schemas/ui-plugin-message.schema.json +0 -90
- package/schemas/workflow-chain-pack-manifest.schema.json +0 -615
- package/schemas/workflow-definition.schema.json +0 -850
- package/schemas/workload-identity.schema.json +0 -79
- package/schemas/workspace-file-create.schema.json +0 -20
- package/schemas/workspace-file.schema.json +0 -39
|
@@ -20,6 +20,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
20
20
|
import { driver } from '../lib/driver.js';
|
|
21
21
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
22
22
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
23
|
+
import { req } from '../lib/requirement-ids.js';
|
|
24
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
23
25
|
|
|
24
26
|
const SEAM = '/v1/host/sample/ai/call-transcriber';
|
|
25
27
|
|
|
@@ -42,18 +44,18 @@ describe('voice-interim-not-durable (RFC 0106 §F INV-1)', () => {
|
|
|
42
44
|
if (!behaviorGate('openwop-voice-interim-not-durable', advertised)) return;
|
|
43
45
|
|
|
44
46
|
const res = await driver.post(SEAM, { audio: { streamRef: 'stream:conformance/mic' }, interimResults: true });
|
|
45
|
-
if (res.status === 404) return; // seam unwired — soft-skip
|
|
46
|
-
if (errCode(res.json) === 'transcription_unsupported') return; // §E live transport / no test-seam arm — soft-skip
|
|
47
|
+
if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
48
|
+
if (errCode(res.json) === 'transcription_unsupported') return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `errCode(res.json) === \'transcription_unsupported\'` returned early (§E live transport / no test-seam arm — soft-skip)'); // §E live transport / no test-seam arm — soft-skip
|
|
47
49
|
|
|
48
50
|
const events = eventsOf(res.json);
|
|
49
|
-
if (events.length === 0) return; // host returned only the result envelope — nothing observable to assert
|
|
51
|
+
if (events.length === 0) return softSkip('blocked', 'precondition not met — `events.length === 0` returned early (host returned only the result envelope — nothing observable to assert) (seam, prior step, or fixture unavailable)'); // host returned only the result envelope — nothing observable to assert
|
|
50
52
|
|
|
51
53
|
const commitIdx = events.findIndex((e) => e.type === 'voice.turn_commit');
|
|
52
|
-
if (commitIdx < 0) return; // no commit observed — soft-skip
|
|
54
|
+
if (commitIdx < 0) return softSkip('blocked', 'precondition not met — `commitIdx < 0` returned early (no commit observed — soft-skip) (seam, prior step, or fixture unavailable)'); // no commit observed — soft-skip
|
|
53
55
|
const afterCommit = events.slice(commitIdx + 1);
|
|
54
56
|
expect(
|
|
55
57
|
!afterCommit.some((e) => e.type === 'voice.transcript' && (e.payload ?? {}).isFinal === false),
|
|
56
|
-
|
|
58
|
+
req('openwop.it.voice-interim-not-durable.no-provisional-isfinal-false-transcript-is-durable-past-voice-turn-commit', 'RFC 0106 §F INV-1', 'a provisional (isFinal:false) transcript MUST NOT appear after voice.turn_commit'),
|
|
57
59
|
).toBe(true);
|
|
58
60
|
});
|
|
59
61
|
});
|
|
@@ -22,6 +22,8 @@ import { readErrorCode } from '../lib/error-envelope.js';
|
|
|
22
22
|
import { driver } from '../lib/driver.js';
|
|
23
23
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
24
24
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
25
|
+
import { req } from '../lib/requirement-ids.js';
|
|
26
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
25
27
|
|
|
26
28
|
const SEAM = '/v1/host/sample/ai/call-transcriber';
|
|
27
29
|
|
|
@@ -44,8 +46,8 @@ describe('voice-streamref-tenant-bound (RFC 0106 §F INV-4)', () => {
|
|
|
44
46
|
// conduit to bind cross-tenant — soft-skip (the invariant stays reference-impl
|
|
45
47
|
// until a host with live streamRef transport proves it).
|
|
46
48
|
const res = await driver.post(SEAM, { audio: { streamRef: 'stream:tenant-a/mic' } });
|
|
47
|
-
if (res.status === 404) return;
|
|
48
|
-
if (errCode(res.json) === 'transcription_unsupported') return; // §E — no live transport on this host
|
|
49
|
+
if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
50
|
+
if (errCode(res.json) === 'transcription_unsupported') return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `errCode(res.json) === \'transcription_unsupported\'` returned early (§E — no live transport on this host)'); // §E — no live transport on this host
|
|
49
51
|
|
|
50
52
|
// A host that DOES accept a live streamRef must bind it: a second tenant presenting
|
|
51
53
|
// tenant-A's streamRef MUST be rejected (cross-handle read forbidden). Exercising the
|
|
@@ -54,7 +56,7 @@ describe('voice-streamref-tenant-bound (RFC 0106 §F INV-4)', () => {
|
|
|
54
56
|
// another tenant's buffered audio for an unknown streamRef.
|
|
55
57
|
expect(
|
|
56
58
|
res.status !== 200 || (res.json as { finalText?: unknown })?.finalText !== undefined,
|
|
57
|
-
|
|
59
|
+
req('openwop.it.voice-streamref-tenant-bound.a-live-streamref-is-tenant-session-bound-host-internal-transport-per-e-soft-skip', 'RFC 0106 §F INV-4', 'a host honoring a live streamRef MUST bind it to its tenant+session'),
|
|
58
60
|
).toBe(true);
|
|
59
61
|
});
|
|
60
62
|
});
|
|
@@ -23,6 +23,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
23
23
|
import { driver } from '../lib/driver.js';
|
|
24
24
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
25
25
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
26
|
+
import { req } from '../lib/requirement-ids.js';
|
|
27
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
26
28
|
|
|
27
29
|
const SEAM = '/v1/host/sample/ai/call-speech-synthesizer';
|
|
28
30
|
|
|
@@ -42,36 +44,36 @@ describe('voice-synthesis-streaming (RFC 0106 §C)', () => {
|
|
|
42
44
|
if (!behaviorGate('openwop-voice-synthesis', advertised)) return;
|
|
43
45
|
|
|
44
46
|
const res = await driver.post(SEAM, { text: 'Welcome to the weekly digest.', voiceId: 'host:narrator-test', stream: true });
|
|
45
|
-
if (res.status === 404) return; // seam unwired — soft-skip
|
|
47
|
+
if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
46
48
|
|
|
47
49
|
expect(
|
|
48
50
|
res.status === 200,
|
|
49
|
-
|
|
51
|
+
req('openwop.it.voice-synthesis-streaming.stream-true-resolves-the-finalized-asset-and-emits-metadata-only-voice-synthesis', 'RFC 0106 §C', 'an advertised host MUST resolve stream:true with 200'),
|
|
50
52
|
).toBe(true);
|
|
51
53
|
|
|
52
54
|
const audio = (res.json as { audio?: Record<string, unknown> })?.audio;
|
|
53
|
-
expect(audio !== undefined,
|
|
55
|
+
expect(audio !== undefined, req('openwop.it.voice-synthesis-streaming.stream-true-resolves-the-finalized-asset-and-emits-metadata-only-voice-synthesis', 'RFC 0106 §C', 'the response MUST carry the finalized `audio` asset')).toBe(true);
|
|
54
56
|
if (audio) {
|
|
55
57
|
const hasUrl = typeof audio.url === 'string' && (audio.url as string).length > 0;
|
|
56
58
|
const hasB64 = typeof audio.base64 === 'string' && (audio.base64 as string).length > 0;
|
|
57
|
-
expect(hasUrl !== hasB64,
|
|
59
|
+
expect(hasUrl !== hasB64, req('openwop.it.voice-synthesis-streaming.stream-true-resolves-the-finalized-asset-and-emits-metadata-only-voice-synthesis', 'RFC 0106 §C', 'audio MUST carry EXACTLY ONE of url/base64')).toBe(true);
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
const chunks = eventsOf(res.json).filter((e) => e.type === 'voice.synthesis_chunk');
|
|
61
|
-
expect(chunks.length >= 1,
|
|
63
|
+
expect(chunks.length >= 1, req('openwop.it.voice-synthesis-streaming.stream-true-resolves-the-finalized-asset-and-emits-metadata-only-voice-synthesis', 'RFC 0106 §C', 'MUST emit ≥1 voice.synthesis_chunk run-event')).toBe(true);
|
|
62
64
|
for (const c of chunks) {
|
|
63
65
|
const p = c.payload ?? {};
|
|
64
|
-
expect(typeof p.seq === 'number',
|
|
65
|
-
expect(typeof p.mimeType === 'string',
|
|
66
|
+
expect(typeof p.seq === 'number', req('openwop.it.voice-synthesis-streaming.stream-true-resolves-the-finalized-asset-and-emits-metadata-only-voice-synthesis', 'RFC 0106 §C', 'each chunk carries a numeric seq')).toBe(true);
|
|
67
|
+
expect(typeof p.mimeType === 'string', req('openwop.it.voice-synthesis-streaming.stream-true-resolves-the-finalized-asset-and-emits-metadata-only-voice-synthesis', 'RFC 0106 §C', 'each chunk carries a mimeType')).toBe(true);
|
|
66
68
|
// Metadata-only: bytes by reference. An inline base64 is permitted ONLY under the host cap;
|
|
67
69
|
// a chunk over the RFC 0055 256 KiB inline cap MUST be a url/streamRef reference.
|
|
68
70
|
if (typeof p.base64 === 'string') {
|
|
69
|
-
expect((p.base64 as string).length <= 262144,
|
|
71
|
+
expect((p.base64 as string).length <= 262144, req('openwop.it.voice-synthesis-streaming.stream-true-resolves-the-finalized-asset-and-emits-metadata-only-voice-synthesis', 'RFC 0106 §C (G8)', 'inline chunk base64 MUST stay under the 256 KiB cap; else url/streamRef')).toBe(true);
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
expect(
|
|
73
75
|
chunks.some((c) => (c.payload ?? {}).final === true),
|
|
74
|
-
|
|
76
|
+
req('openwop.it.voice-synthesis-streaming.stream-true-resolves-the-finalized-asset-and-emits-metadata-only-voice-synthesis', 'RFC 0106 §C', 'a terminal chunk MUST carry final:true'),
|
|
75
77
|
).toBe(true);
|
|
76
78
|
});
|
|
77
79
|
});
|
|
@@ -23,6 +23,8 @@ import { readErrorCode } from '../lib/error-envelope.js';
|
|
|
23
23
|
import { driver } from '../lib/driver.js';
|
|
24
24
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
25
25
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
26
|
+
import { req } from '../lib/requirement-ids.js';
|
|
27
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
26
28
|
|
|
27
29
|
const SEAM = '/v1/host/sample/ai/call-transcriber';
|
|
28
30
|
|
|
@@ -45,21 +47,21 @@ describe('voice-transcription-streaming (RFC 0106 §B)', () => {
|
|
|
45
47
|
if (!behaviorGate('openwop-voice-transcription', advertised)) return;
|
|
46
48
|
|
|
47
49
|
const res = await driver.post(SEAM, { audio: { streamRef: 'stream:conformance/mic' }, languageCode: 'en-US' });
|
|
48
|
-
if (res.status === 404) return; // seam unwired — soft-skip
|
|
50
|
+
if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
49
51
|
// §E: a stateless host with no live transport honestly rejects a live streamRef.
|
|
50
|
-
if (errCode(res.json) === 'transcription_unsupported') return; // soft-skip (no live transport / no test-seam arm)
|
|
52
|
+
if (errCode(res.json) === 'transcription_unsupported') return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `errCode(res.json) === \'transcription_unsupported\'` returned early (soft-skip (no live transport / no test-seam arm))'); // soft-skip (no live transport / no test-seam arm)
|
|
51
53
|
|
|
52
|
-
expect(res.status === 200,
|
|
54
|
+
expect(res.status === 200, req('openwop.it.voice-transcription-streaming.resolves-a-settled-turn-at-voice-turn-commit-with-untrusted-transcript-events', 'RFC 0106 §B', 'a produced turn MUST return 200')).toBe(true);
|
|
53
55
|
|
|
54
56
|
const finalText = (res.json as { finalText?: unknown })?.finalText;
|
|
55
|
-
expect(typeof finalText === 'string' && (finalText as string).length > 0,
|
|
57
|
+
expect(typeof finalText === 'string' && (finalText as string).length > 0, req('openwop.it.voice-transcription-streaming.resolves-a-settled-turn-at-voice-turn-commit-with-untrusted-transcript-events', 'RFC 0106 §B', 'the turn MUST resolve a non-empty finalText at turn_commit')).toBe(true);
|
|
56
58
|
|
|
57
59
|
const events = eventsOf(res.json);
|
|
58
60
|
if (events.length > 0) {
|
|
59
61
|
for (const ev of events.filter((e) => e.type === 'voice.transcript')) {
|
|
60
|
-
expect((ev.payload ?? {}).contentTrust === 'untrusted',
|
|
62
|
+
expect((ev.payload ?? {}).contentTrust === 'untrusted', req('openwop.it.voice-transcription-streaming.resolves-a-settled-turn-at-voice-turn-commit-with-untrusted-transcript-events', 'RFC 0106 §F INV-2', 'every voice.transcript MUST carry contentTrust:"untrusted"')).toBe(true);
|
|
61
63
|
}
|
|
62
|
-
expect(events.some((e) => e.type === 'voice.turn_commit'),
|
|
64
|
+
expect(events.some((e) => e.type === 'voice.turn_commit'), req('openwop.it.voice-transcription-streaming.resolves-a-settled-turn-at-voice-turn-commit-with-untrusted-transcript-events', 'RFC 0106 §B', 'the turn MUST terminate with voice.turn_commit')).toBe(true);
|
|
63
65
|
}
|
|
64
66
|
});
|
|
65
67
|
});
|
|
@@ -18,6 +18,8 @@ import { readErrorCode } from '../lib/error-envelope.js';
|
|
|
18
18
|
import { driver } from '../lib/driver.js';
|
|
19
19
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
20
20
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
21
|
+
import { req } from '../lib/requirement-ids.js';
|
|
22
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
21
23
|
|
|
22
24
|
const SEAM = '/v1/host/sample/ai/call-transcriber';
|
|
23
25
|
|
|
@@ -36,12 +38,12 @@ describe('voice-transcription-unadvertised (RFC 0106 §B)', () => {
|
|
|
36
38
|
if (!behaviorGate('openwop-voice-transcription-unadvertised', !advertised)) return;
|
|
37
39
|
|
|
38
40
|
const res = await driver.post(SEAM, { audio: { streamRef: 'stream:conformance/mic' } });
|
|
39
|
-
if (res.status === 404) return; // seam unwired — soft-skip
|
|
41
|
+
if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
40
42
|
|
|
41
|
-
expect(res.status !== 200,
|
|
43
|
+
expect(res.status !== 200, req('openwop.it.voice-transcription-unadvertised.a-host-not-advertising-realtimevoice-transcription-must-reject-with-transcriptio', 'RFC 0106 §B', 'an unadvertising host MUST NOT return a 200 (never a no-op)')).toBe(true);
|
|
42
44
|
expect(
|
|
43
45
|
errCode(res.json) === 'transcription_unsupported',
|
|
44
|
-
|
|
46
|
+
req('openwop.it.voice-transcription-unadvertised.a-host-not-advertising-realtimevoice-transcription-must-reject-with-transcriptio', 'RFC 0106 §B', 'the call MUST be rejected with `transcription_unsupported`'),
|
|
45
47
|
).toBe(true);
|
|
46
48
|
});
|
|
47
49
|
});
|
|
@@ -28,6 +28,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
28
28
|
import { softSkip } from '../lib/soft-skip.js';
|
|
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
|
const MISBEHAVING_PACK_NAME = 'vendor.openwop.misbehaving-abi';
|
|
33
34
|
const WELL_BEHAVED_PACK_NAME = 'vendor.openwop.rust-hello';
|
|
@@ -40,7 +41,7 @@ describe('wasm-pack-abi-version-rejection: host advertises supported ABI version
|
|
|
40
41
|
|
|
41
42
|
if (!wasm?.supported) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!wasm?.supported` returned early');
|
|
42
43
|
|
|
43
|
-
expect(Array.isArray(wasm.abiVersions),
|
|
44
|
+
expect(Array.isArray(wasm.abiVersions), req('openwop.it.wasm-pack-abi-version-rejection.abiversions-contains-positive-integers-loader-rejects-unsupported-versions',
|
|
44
45
|
'RFCS/0008-wasm-abi.md §H',
|
|
45
46
|
'capabilities.nodePackRuntimes.wasm.abiVersions MUST be an array',
|
|
46
47
|
)).toBe(true);
|
|
@@ -90,7 +91,7 @@ describe('wasm-pack-abi-version-rejection: positive path via misbehaving pack',
|
|
|
90
91
|
|
|
91
92
|
// Core assertion: ABI 999 pack MUST NOT be in loadedPacks[]
|
|
92
93
|
// regardless of filesystem state. The host's loader rejected it.
|
|
93
|
-
expect(wasm.loadedPacks.includes(MISBEHAVING_PACK_NAME),
|
|
94
|
+
expect(wasm.loadedPacks.includes(MISBEHAVING_PACK_NAME), req('openwop.it.wasm-pack-abi-version-rejection.misbehaving-abi-pack-declares-abi-999-must-not-appear-in-loadedpacks',
|
|
94
95
|
'RFCS/0008-wasm-abi.md §H',
|
|
95
96
|
`host MUST reject packs whose declared ABI is not in abiVersions[]; ${MISBEHAVING_PACK_NAME} declares 999 and MUST NOT appear in loadedPacks[]`,
|
|
96
97
|
)).toBe(false);
|
|
@@ -18,6 +18,7 @@ import { driver } from '../lib/driver.js';
|
|
|
18
18
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
19
19
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
20
20
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
21
|
+
import { req } from '../lib/requirement-ids.js';
|
|
21
22
|
|
|
22
23
|
const FIXTURE = 'conformance-wasm-pack-roundtrip';
|
|
23
24
|
|
|
@@ -45,7 +46,7 @@ describe('wasm-pack-invoke-completed: round-trip output', () => {
|
|
|
45
46
|
const runId = (create.json as { runId: string }).runId;
|
|
46
47
|
|
|
47
48
|
const terminal = await pollUntilTerminal(runId, { timeoutMs: 15_000 });
|
|
48
|
-
expect(terminal.status,
|
|
49
|
+
expect(terminal.status, req('openwop.it.wasm-pack-invoke-completed.greet-node-returns-hello-name-and-run-reaches-completed',
|
|
49
50
|
'RFCS/0008-wasm-abi.md §D',
|
|
50
51
|
"WASM 'completed' outcome MUST drive terminal 'completed' run status",
|
|
51
52
|
)).toBe('completed');
|
|
@@ -61,7 +62,7 @@ describe('wasm-pack-invoke-completed: round-trip output', () => {
|
|
|
61
62
|
const fullLog = JSON.stringify(list).toLowerCase();
|
|
62
63
|
expect(
|
|
63
64
|
haystack.includes('hello, openwop') || fullLog.includes('hello, openwop'),
|
|
64
|
-
|
|
65
|
+
req('openwop.it.wasm-pack-invoke-completed.greet-node-returns-hello-name-and-run-reaches-completed',
|
|
65
66
|
'RFCS/0008-wasm-abi.md §D',
|
|
66
67
|
"WASM completion output MUST be surfaced somewhere in the run's event log so consumers can read it",
|
|
67
68
|
),
|
|
@@ -22,6 +22,7 @@ import { driver } from '../lib/driver.js';
|
|
|
22
22
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
23
23
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
24
24
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
25
|
+
import { req } from '../lib/requirement-ids.js';
|
|
25
26
|
|
|
26
27
|
const FIXTURE = 'conformance-wasm-pack-roundtrip';
|
|
27
28
|
|
|
@@ -60,11 +61,11 @@ describe('wasm-pack-invoke-suspended: suspend → resume round-trip', () => {
|
|
|
60
61
|
const ok =
|
|
61
62
|
haystack.includes('wasm_suspend_not_implemented') ||
|
|
62
63
|
haystack.includes('suspend_not_supported');
|
|
63
|
-
expect(ok,
|
|
64
|
+
expect(ok, req('openwop.it.wasm-pack-invoke-suspended.host-either-suspends-the-run-or-explicitly-reports-wasm-suspend-not-implemented',
|
|
64
65
|
'RFCS/0008-wasm-abi.md §D',
|
|
65
66
|
"if a host doesn't implement WASM-driven suspends it MUST surface a recognizable code",
|
|
66
67
|
)).toBe(true);
|
|
67
|
-
return;
|
|
68
|
+
return softSkip('blocked', 'precondition not met — `terminal.status === \'failed\'` returned early (seam, prior step, or fixture unavailable)');
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
// Completed path: the reference pack never suspends. Asserting
|
|
@@ -17,6 +17,7 @@ import { softSkip } from '../lib/soft-skip.js';
|
|
|
17
17
|
import { driver } from '../lib/driver.js';
|
|
18
18
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
19
19
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
20
|
+
import { req } from '../lib/requirement-ids.js';
|
|
20
21
|
|
|
21
22
|
const FIXTURE = 'conformance-wasm-pack-roundtrip';
|
|
22
23
|
|
|
@@ -42,11 +43,11 @@ describe('wasm-pack-load: discovery surfaces WASM runtime support', () => {
|
|
|
42
43
|
console.warn('[wasm-pack-load] host does not advertise WASM support; skipping');
|
|
43
44
|
return softSkip('inapplicable', '[wasm-pack-load] host does not advertise WASM support; skipping');
|
|
44
45
|
}
|
|
45
|
-
expect(Array.isArray(wasm.abiVersions),
|
|
46
|
+
expect(Array.isArray(wasm.abiVersions), req('openwop.it.wasm-pack-load.a-host-claiming-wasm-support-advertises-abiversions-including-1',
|
|
46
47
|
'RFCS/0008-wasm-abi.md §H',
|
|
47
48
|
'capabilities.nodePackRuntimes.wasm.abiVersions MUST be an array',
|
|
48
49
|
)).toBe(true);
|
|
49
|
-
expect(wasm.abiVersions?.includes(1),
|
|
50
|
+
expect(wasm.abiVersions?.includes(1), req('openwop.it.wasm-pack-load.a-host-claiming-wasm-support-advertises-abiversions-including-1',
|
|
50
51
|
'RFCS/0008-wasm-abi.md §H',
|
|
51
52
|
'abiVersions MUST include 1 (this RFC) when supported',
|
|
52
53
|
)).toBe(true);
|
|
@@ -66,7 +67,7 @@ describe('wasm-pack-load: loaded pack typeIds are dispatchable', () => {
|
|
|
66
67
|
// WASM-provided typeId. A host that loaded the pack accepts the
|
|
67
68
|
// POST /v1/runs; one that didn't would return 400/404.
|
|
68
69
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE, inputs: { name: 'world' } });
|
|
69
|
-
expect(create.status,
|
|
70
|
+
expect(create.status, req('openwop.it.wasm-pack-load.host-accepts-a-workflow-whose-node-typeid-is-provided-by-a-loaded-wasm-pack',
|
|
70
71
|
'RFCS/0008-wasm-abi.md §B + node-packs.md §Reserved typeIds',
|
|
71
72
|
'host MUST accept runs whose nodes reference loaded-pack typeIds',
|
|
72
73
|
)).toBe(201);
|
|
@@ -28,6 +28,7 @@ import { driver } from '../lib/driver.js';
|
|
|
28
28
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
29
29
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
30
30
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
31
|
+
import { req } from '../lib/requirement-ids.js';
|
|
31
32
|
|
|
32
33
|
const CAP_BREACH_FIXTURE = 'conformance-wasm-pack-memory-cap-breach';
|
|
33
34
|
|
|
@@ -41,7 +42,7 @@ describe('wasm-pack-memory-cap: host advertises maxMemoryBytes', () => {
|
|
|
41
42
|
|
|
42
43
|
// The cap is REQUIRED to enforce §K. Hosts MUST surface the configured
|
|
43
44
|
// ceiling so clients can size their packs accordingly.
|
|
44
|
-
expect(typeof wasm.maxMemoryBytes,
|
|
45
|
+
expect(typeof wasm.maxMemoryBytes, req('openwop.it.wasm-pack-memory-cap.capabilities-nodepackruntimes-wasm-maxmemorybytes-is-a-plausible-number',
|
|
45
46
|
'RFCS/0008-wasm-abi.md §K',
|
|
46
47
|
'capabilities.nodePackRuntimes.wasm.maxMemoryBytes MUST be advertised as a number when WASM is supported',
|
|
47
48
|
)).toBe('number');
|
|
@@ -75,7 +76,7 @@ describe('wasm-pack-memory-cap: positive path via misbehaving pack', () => {
|
|
|
75
76
|
const runId = (create.json as { runId: string }).runId;
|
|
76
77
|
|
|
77
78
|
const terminal = await pollUntilTerminal(runId, { timeoutMs: 15_000 });
|
|
78
|
-
expect(terminal.status,
|
|
79
|
+
expect(terminal.status, req('openwop.it.wasm-pack-memory-cap.misbehaving-pack-triggers-cap-breached-with-kind-wasm-memory-and-terminal-failed',
|
|
79
80
|
'RFCS/0008-wasm-abi.md §K',
|
|
80
81
|
'WASM memory-cap breach MUST drive terminal `failed` (RFC 0008 §K: "kill the instance, emit cap.breached")',
|
|
81
82
|
)).toBe('failed');
|
|
@@ -83,14 +84,14 @@ describe('wasm-pack-memory-cap: positive path via misbehaving pack', () => {
|
|
|
83
84
|
const events = await driver.get(`/v1/runs/${encodeURIComponent(runId)}/events`);
|
|
84
85
|
const list = (events.json as { events?: Array<{ type: string; payload?: unknown }> }).events ?? [];
|
|
85
86
|
const breachEvent = list.find((e) => e.type === 'cap.breached');
|
|
86
|
-
expect(breachEvent,
|
|
87
|
+
expect(breachEvent, req('openwop.it.wasm-pack-memory-cap.misbehaving-pack-triggers-cap-breached-with-kind-wasm-memory-and-terminal-failed',
|
|
87
88
|
'RFCS/0008-wasm-abi.md §K',
|
|
88
89
|
'host MUST emit cap.breached when a WASM module exceeds its memory ceiling',
|
|
89
90
|
)).toBeDefined();
|
|
90
91
|
// Event detail rides under the canonical `payload` envelope field (per
|
|
91
92
|
// run-event-payloads.schema.json + every other event scenario), not `data`.
|
|
92
93
|
const breachKind = (breachEvent?.payload as { kind?: string } | undefined)?.kind;
|
|
93
|
-
expect(breachKind,
|
|
94
|
+
expect(breachKind, req('openwop.it.wasm-pack-memory-cap.misbehaving-pack-triggers-cap-breached-with-kind-wasm-memory-and-terminal-failed',
|
|
94
95
|
'RFCS/0008-wasm-abi.md §K',
|
|
95
96
|
'cap.breached payload MUST carry kind: "wasm-memory" for memory-ceiling breaches',
|
|
96
97
|
)).toBe('wasm-memory');
|
|
@@ -16,6 +16,7 @@ import { driver } from '../lib/driver.js';
|
|
|
16
16
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
17
17
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
18
18
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
19
|
+
import { req } from '../lib/requirement-ids.js';
|
|
19
20
|
|
|
20
21
|
const FIXTURE = 'conformance-wasm-pack-roundtrip';
|
|
21
22
|
|
|
@@ -53,7 +54,7 @@ describe('wasm-pack-replay-determinism: same inputs → same output', () => {
|
|
|
53
54
|
const a = await run();
|
|
54
55
|
const b = await run();
|
|
55
56
|
|
|
56
|
-
expect(a,
|
|
57
|
+
expect(a, req('openwop.it.wasm-pack-replay-determinism.two-independent-runs-with-same-inputs-produce-same-wasm-output',
|
|
57
58
|
'RFCS/0008-wasm-abi.md §G',
|
|
58
59
|
'WASM-node output MUST be reproducible given the same inputs',
|
|
59
60
|
)).not.toBeNull();
|
|
@@ -24,6 +24,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
24
24
|
import { driver } from '../lib/driver.js';
|
|
25
25
|
import { discoveryFamilies } from '../lib/discovery-capabilities.js';
|
|
26
26
|
import { discoverOwnedTenant } from '../lib/webhook-receiver.js';
|
|
27
|
+
import { req } from '../lib/requirement-ids.js';
|
|
28
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
27
29
|
|
|
28
30
|
async function isWebhookSupported(): Promise<boolean> {
|
|
29
31
|
const disco = await driver.get('/.well-known/openwop');
|
|
@@ -36,7 +38,7 @@ describe('webhook-negative: SSRF guard rejects private destinations', () => {
|
|
|
36
38
|
if (!(await isWebhookSupported())) {
|
|
37
39
|
// eslint-disable-next-line no-console
|
|
38
40
|
console.warn('[webhook-negative] host does not advertise webhook support; skipping');
|
|
39
|
-
return;
|
|
41
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isWebhookSupported())` returned early ([webhook-negative] host does not advertise webhook support; skipping)');
|
|
40
42
|
}
|
|
41
43
|
// Spec-complete except for the private URL, so validation passes and the
|
|
42
44
|
// request reaches the SSRF guard. `{ url }` alone 400s `validation_error`
|
|
@@ -59,9 +61,9 @@ describe('webhook-negative: SSRF guard rejects private destinations', () => {
|
|
|
59
61
|
if (body.subscriptionId) {
|
|
60
62
|
await driver.delete(`/v1/webhooks/${encodeURIComponent(body.subscriptionId)}`);
|
|
61
63
|
}
|
|
62
|
-
return;
|
|
64
|
+
return softSkip('blocked', 'precondition not met — `reg.status === 201` returned early ([webhook-negative] host accepts loopback destinations; SSRF guard not enforced) (seam, prior step, or fixture unavailable)');
|
|
63
65
|
}
|
|
64
|
-
expect(reg.status,
|
|
66
|
+
expect(reg.status, req('openwop.it.webhook-negative.host-with-ssrf-guard-returns-400-webhook-url-rejected-for-loopback',
|
|
65
67
|
'webhooks.md + review §"Webhook SSRF guard"',
|
|
66
68
|
'host with SSRF guard MUST return 400 for loopback / RFC1918 / link-local destinations',
|
|
67
69
|
)).toBe(400);
|
|
@@ -72,17 +74,17 @@ describe('webhook-negative: SSRF guard rejects private destinations', () => {
|
|
|
72
74
|
|
|
73
75
|
describe('webhook-negative: validation errors', () => {
|
|
74
76
|
it('malformed url returns 400 validation_error', async () => {
|
|
75
|
-
if (!(await isWebhookSupported())) return;
|
|
77
|
+
if (!(await isWebhookSupported())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isWebhookSupported())` returned early');
|
|
76
78
|
const reg = await driver.post('/v1/webhooks', { url: 'not a url' });
|
|
77
|
-
expect([400, 422]).toContain(reg.status);
|
|
79
|
+
expect([400, 422], req('openwop.it.webhook-negative.malformed-url-returns-400-validation-error', 'webhooks.md', 'malformed url returns 400 validation_error')).toContain(reg.status);
|
|
78
80
|
const body = reg.json as { error?: string };
|
|
79
81
|
expect(['validation_error', 'webhook_url_rejected']).toContain(body.error);
|
|
80
82
|
});
|
|
81
83
|
|
|
82
84
|
it('missing url returns 400 validation_error', async () => {
|
|
83
|
-
if (!(await isWebhookSupported())) return;
|
|
85
|
+
if (!(await isWebhookSupported())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isWebhookSupported())` returned early');
|
|
84
86
|
const reg = await driver.post('/v1/webhooks', { eventTypes: ['run.completed'] });
|
|
85
|
-
expect(reg.status).toBe(400);
|
|
87
|
+
expect(reg.status, req('openwop.it.webhook-negative.missing-url-returns-400-validation-error', 'webhooks.md', 'missing url returns 400 validation_error')).toBe(400);
|
|
86
88
|
const body = reg.json as { error?: string };
|
|
87
89
|
expect(body.error).toBe('validation_error');
|
|
88
90
|
});
|
|
@@ -90,9 +92,9 @@ describe('webhook-negative: validation errors', () => {
|
|
|
90
92
|
|
|
91
93
|
describe('webhook-negative: unregister of unknown subscription', () => {
|
|
92
94
|
it('DELETE /v1/webhooks/{unknown} returns 404 subscription_not_found', async () => {
|
|
93
|
-
if (!(await isWebhookSupported())) return;
|
|
95
|
+
if (!(await isWebhookSupported())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isWebhookSupported())` returned early');
|
|
94
96
|
const del = await driver.delete('/v1/webhooks/wh-does-not-exist');
|
|
95
|
-
expect(del.status).toBe(404);
|
|
97
|
+
expect(del.status, req('openwop.it.webhook-negative.delete-v1-webhooks-unknown-returns-404-subscription-not-found', 'webhooks.md', 'DELETE /v1/webhooks/{unknown} returns 404 subscription_not_found')).toBe(404);
|
|
96
98
|
const body = del.json as { error?: string };
|
|
97
99
|
expect(body.error).toBe('subscription_not_found');
|
|
98
100
|
});
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
verifyWebhookDelivery,
|
|
45
45
|
SIGNATURE_PREFIX,
|
|
46
46
|
} from '../lib/webhook-receiver.js';
|
|
47
|
+
import { req } from '../lib/requirement-ids.js';
|
|
47
48
|
|
|
48
49
|
describe('webhook-receiver-adversarial: receiver rejects five canonical attacks', () => {
|
|
49
50
|
const secret = 'test-secret-do-not-use-in-prod';
|
|
@@ -65,7 +66,7 @@ describe('webhook-receiver-adversarial: receiver rejects five canonical attacks'
|
|
|
65
66
|
);
|
|
66
67
|
expect(
|
|
67
68
|
result.accepted,
|
|
68
|
-
'webhooks.md §"Signature recipe": valid signature + fresh timestamp + correct algorithm MUST be accepted',
|
|
69
|
+
req('openwop.it.webhook-receiver-adversarial.positive-control-receiver-accepts-a-freshly-signed-valid-delivery', 'spec/v1/webhooks.md', 'webhooks.md §"Signature recipe": valid signature + fresh timestamp + correct algorithm MUST be accepted'),
|
|
69
70
|
).toBe(true);
|
|
70
71
|
});
|
|
71
72
|
|
|
@@ -86,7 +87,7 @@ describe('webhook-receiver-adversarial: receiver rejects five canonical attacks'
|
|
|
86
87
|
if (!result.accepted) {
|
|
87
88
|
expect(
|
|
88
89
|
result.reason,
|
|
89
|
-
'webhooks.md §"Signature recipe": tampered body MUST be rejected with signature_mismatch',
|
|
90
|
+
req('openwop.it.webhook-receiver-adversarial.case-1-tampered-body-signature-mismatch', 'spec/v1/webhooks.md', 'webhooks.md §"Signature recipe": tampered body MUST be rejected with signature_mismatch'),
|
|
90
91
|
).toBe('signature_mismatch');
|
|
91
92
|
}
|
|
92
93
|
});
|
|
@@ -113,7 +114,7 @@ describe('webhook-receiver-adversarial: receiver rejects five canonical attacks'
|
|
|
113
114
|
state,
|
|
114
115
|
{ nowSeconds: nowSec },
|
|
115
116
|
);
|
|
116
|
-
expect(result.accepted).toBe(false);
|
|
117
|
+
expect(result.accepted, req('openwop.it.webhook-receiver-adversarial.case-2-tampered-hmac-signature-mismatch', 'spec/v1/webhooks.md', 'case 2: tampered HMAC → signature_mismatch')).toBe(false);
|
|
117
118
|
if (!result.accepted) {
|
|
118
119
|
expect(result.reason).toBe('signature_mismatch');
|
|
119
120
|
}
|
|
@@ -136,7 +137,7 @@ describe('webhook-receiver-adversarial: receiver rejects five canonical attacks'
|
|
|
136
137
|
if (!result.accepted) {
|
|
137
138
|
expect(
|
|
138
139
|
result.reason,
|
|
139
|
-
'webhooks.md §"Replay-attack resistance": timestamp older than freshness window MUST be rejected with timestamp_expired',
|
|
140
|
+
req('openwop.it.webhook-receiver-adversarial.case-3-stale-timestamp-timestamp-expired', 'spec/v1/webhooks.md', 'webhooks.md §"Replay-attack resistance": timestamp older than freshness window MUST be rejected with timestamp_expired'),
|
|
140
141
|
).toBe('timestamp_expired');
|
|
141
142
|
}
|
|
142
143
|
});
|
|
@@ -170,7 +171,7 @@ describe('webhook-receiver-adversarial: receiver rejects five canonical attacks'
|
|
|
170
171
|
if (!replay.accepted) {
|
|
171
172
|
expect(
|
|
172
173
|
replay.reason,
|
|
173
|
-
'webhooks.md §"Replay-attack resistance": replay of an already-accepted signature MUST be rejected with duplicate_signature',
|
|
174
|
+
req('openwop.it.webhook-receiver-adversarial.case-4-replayed-signature-duplicate-signature', 'spec/v1/webhooks.md', 'webhooks.md §"Replay-attack resistance": replay of an already-accepted signature MUST be rejected with duplicate_signature'),
|
|
174
175
|
).toBe('duplicate_signature');
|
|
175
176
|
}
|
|
176
177
|
});
|
|
@@ -191,7 +192,7 @@ describe('webhook-receiver-adversarial: receiver rejects five canonical attacks'
|
|
|
191
192
|
if (!result.accepted) {
|
|
192
193
|
expect(
|
|
193
194
|
result.reason,
|
|
194
|
-
'webhooks.md §"Signature algorithm versioning": algorithm other than v1 MUST be rejected with wrong_algorithm by a v1-only receiver',
|
|
195
|
+
req('openwop.it.webhook-receiver-adversarial.case-5-wrong-algorithm-wrong-algorithm', 'spec/v1/webhooks.md', 'webhooks.md §"Signature algorithm versioning": algorithm other than v1 MUST be rejected with wrong_algorithm by a v1-only receiver'),
|
|
195
196
|
).toBe('wrong_algorithm');
|
|
196
197
|
}
|
|
197
198
|
});
|
|
@@ -208,7 +209,7 @@ describe('webhook-receiver-adversarial: receiver rejects five canonical attacks'
|
|
|
208
209
|
state,
|
|
209
210
|
{ nowSeconds: nowSec },
|
|
210
211
|
);
|
|
211
|
-
expect(result.accepted).toBe(false);
|
|
212
|
+
expect(result.accepted, req('openwop.it.webhook-receiver-adversarial.case-6-malformed-signature-header-malformed-signature-header', 'spec/v1/webhooks.md', 'case 6: malformed signature header → malformed_signature_header')).toBe(false);
|
|
212
213
|
if (!result.accepted) {
|
|
213
214
|
expect(result.reason).toBe('malformed_signature_header');
|
|
214
215
|
}
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
import { describe, it, expect } from 'vitest';
|
|
17
17
|
import { driver } from '../lib/driver.js';
|
|
18
18
|
import { discoveryFamilies } from '../lib/discovery-capabilities.js';
|
|
19
|
+
import { req } from '../lib/requirement-ids.js';
|
|
20
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
19
21
|
|
|
20
22
|
interface DiscoveryWebhooks {
|
|
21
23
|
webhooks?: {
|
|
@@ -33,7 +35,7 @@ describe('webhook-sig-algorithm: host advertises supported algorithm set', () =>
|
|
|
33
35
|
if (!webhooks?.supported) {
|
|
34
36
|
// eslint-disable-next-line no-console
|
|
35
37
|
console.warn('[webhook-sig-algorithm] host does not advertise webhook support; skipping');
|
|
36
|
-
return;
|
|
38
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!webhooks?.supported` returned early ([webhook-sig-algorithm] host does not advertise webhook support; skipping)');
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
const algos = webhooks.signatureAlgorithms;
|
|
@@ -45,10 +47,10 @@ describe('webhook-sig-algorithm: host advertises supported algorithm set', () =>
|
|
|
45
47
|
console.warn(
|
|
46
48
|
'[webhook-sig-algorithm] host does not advertise webhooks.signatureAlgorithms (pre-v1.1); skipping shape check',
|
|
47
49
|
);
|
|
48
|
-
return;
|
|
50
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!Array.isArray(algos)` returned early ([webhook-sig-algorithm] host does not advertise webhooks.signatureAlgorithms (pre-v1.1); skipping shape check)');
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
expect(algos.includes('v1'),
|
|
53
|
+
expect(algos.includes('v1'), req('openwop.it.webhook-sig-algorithm.discovery-surfaces-a-webhooks-signaturealgorithms-array-including-v1',
|
|
52
54
|
'webhooks.md §"Signature algorithm versioning"',
|
|
53
55
|
'webhooks.signatureAlgorithms MUST include "v1" when surfaced (canonical baseline)',
|
|
54
56
|
)).toBe(true);
|