@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
|
@@ -47,6 +47,8 @@ import { SCHEMAS_DIR, FIXTURES_DIR } from '../lib/paths.js';
|
|
|
47
47
|
import { driver } from '../lib/driver.js';
|
|
48
48
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
49
49
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
50
|
+
import { req } from '../lib/requirement-ids.js';
|
|
51
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
50
52
|
|
|
51
53
|
const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
|
|
52
54
|
|
|
@@ -74,7 +76,7 @@ describe('trigger-ingestion: TriggerEvent schema (always-on, server-free)', () =
|
|
|
74
76
|
const ev = JSON.parse(readFileSync(EVENT_FIXTURE, 'utf8'));
|
|
75
77
|
expect(
|
|
76
78
|
validate(ev),
|
|
77
|
-
`trigger-event.schema.json MUST accept a conforming email TriggerEvent. Errors: ${JSON.stringify(validate.errors)}
|
|
79
|
+
req('openwop.it.trigger-ingestion.the-canonical-email-triggerevent-fixture-validates', 'RFC 0099', `trigger-event.schema.json MUST accept a conforming email TriggerEvent. Errors: ${JSON.stringify(validate.errors)}`),
|
|
78
80
|
).toBe(true);
|
|
79
81
|
});
|
|
80
82
|
|
|
@@ -83,7 +85,7 @@ describe('trigger-ingestion: TriggerEvent schema (always-on, server-free)', () =
|
|
|
83
85
|
ev.webhook = { method: 'POST', body: { x: 1 } };
|
|
84
86
|
expect(
|
|
85
87
|
validate(ev),
|
|
86
|
-
'trigger-bridge.md §F.1 — a TriggerEvent MUST carry exactly the per-source sub-object matching its `source` and MUST NOT carry the others',
|
|
88
|
+
req('openwop.it.trigger-ingestion.the-f-1-one-of-rule-holds-a-source-email-event-carrying-a-webhook-sub-object-fai', 'RFC 0099', 'trigger-bridge.md §F.1 — a TriggerEvent MUST carry exactly the per-source sub-object matching its `source` and MUST NOT carry the others'),
|
|
87
89
|
).toBe(false);
|
|
88
90
|
});
|
|
89
91
|
|
|
@@ -92,7 +94,7 @@ describe('trigger-ingestion: TriggerEvent schema (always-on, server-free)', () =
|
|
|
92
94
|
ev.contentTrust = 'trusted';
|
|
93
95
|
expect(
|
|
94
96
|
validate(ev),
|
|
95
|
-
'trigger-bridge.md §F.1 — `contentTrust` MUST be `"untrusted"`',
|
|
97
|
+
req('openwop.it.trigger-ingestion.contenttrust-must-be-the-const-untrusted', 'RFC 0099', 'trigger-bridge.md §F.1 — `contentTrust` MUST be `"untrusted"`'),
|
|
96
98
|
).toBe(false);
|
|
97
99
|
});
|
|
98
100
|
|
|
@@ -102,7 +104,7 @@ describe('trigger-ingestion: TriggerEvent schema (always-on, server-free)', () =
|
|
|
102
104
|
noRef.email.attachments = [{ filename: 'x.png' }];
|
|
103
105
|
expect(
|
|
104
106
|
validate(noRef),
|
|
105
|
-
'SECURITY invariant trigger-ingestion-ssrf — an AttachmentRef MUST carry a host-internal `ref`',
|
|
107
|
+
req('openwop.it.trigger-ingestion.attachmentref-requires-a-host-internal-ref-and-rejects-a-raw-external-url-trigge', 'RFC 0099', 'SECURITY invariant trigger-ingestion-ssrf — an AttachmentRef MUST carry a host-internal `ref`'),
|
|
106
108
|
).toBe(false);
|
|
107
109
|
|
|
108
110
|
// A raw external `url` the run would fetch itself → invalid
|
|
@@ -111,7 +113,7 @@ describe('trigger-ingestion: TriggerEvent schema (always-on, server-free)', () =
|
|
|
111
113
|
rawUrl.email.attachments = [{ ref: 'blob_a1', url: 'http://169.254.169.254/latest/meta-data/' }];
|
|
112
114
|
expect(
|
|
113
115
|
validate(rawUrl),
|
|
114
|
-
'SECURITY invariant trigger-ingestion-ssrf — the host MUST NOT hand the run an external URL to fetch itself; AttachmentRef is a host-internal handle only',
|
|
116
|
+
req('openwop.it.trigger-ingestion.attachmentref-requires-a-host-internal-ref-and-rejects-a-raw-external-url-trigge', 'RFC 0099', 'SECURITY invariant trigger-ingestion-ssrf — the host MUST NOT hand the run an external URL to fetch itself; AttachmentRef is a host-internal handle only'),
|
|
115
117
|
).toBe(false);
|
|
116
118
|
});
|
|
117
119
|
|
|
@@ -127,7 +129,7 @@ describe('trigger-ingestion: TriggerEvent schema (always-on, server-free)', () =
|
|
|
127
129
|
};
|
|
128
130
|
expect(
|
|
129
131
|
validate(ev),
|
|
130
|
-
`a conforming webhook TriggerEvent MUST validate. Errors: ${JSON.stringify(validate.errors)}
|
|
132
|
+
req('openwop.it.trigger-ingestion.a-webhook-triggerevent-with-an-allowlisted-header-validates', 'RFC 0099', `a conforming webhook TriggerEvent MUST validate. Errors: ${JSON.stringify(validate.errors)}`),
|
|
131
133
|
).toBe(true);
|
|
132
134
|
});
|
|
133
135
|
});
|
|
@@ -143,7 +145,7 @@ describe('trigger-ingestion: content-freeness of the durable trigger.delivery.at
|
|
|
143
145
|
const deliveryDef = payloads.$defs?.triggerDeliveryAttempted;
|
|
144
146
|
|
|
145
147
|
it('trigger.delivery.attempted declares only the content-free RFC 0083 §C fields', () => {
|
|
146
|
-
expect(deliveryDef, 'run-event-payloads.schema.json MUST define triggerDeliveryAttempted').toBeDefined();
|
|
148
|
+
expect(deliveryDef, req('openwop.it.trigger-ingestion.trigger-delivery-attempted-declares-only-the-content-free-rfc-0083-c-fields', 'RFC 0083 §C', 'run-event-payloads.schema.json MUST define triggerDeliveryAttempted')).toBeDefined();
|
|
147
149
|
const props = Object.keys(deliveryDef.properties ?? {});
|
|
148
150
|
// The complete content-free field set — no `body`, `headers`, `email`,
|
|
149
151
|
// `form`, `fields`, or any inbound-content carrier.
|
|
@@ -151,7 +153,7 @@ describe('trigger-ingestion: content-freeness of the durable trigger.delivery.at
|
|
|
151
153
|
for (const banned of ['body', 'headers', 'email', 'form', 'fields', 'html', 'text', 'subject']) {
|
|
152
154
|
expect(
|
|
153
155
|
props.includes(banned),
|
|
154
|
-
`trigger-ingestion-content-redaction — the durable trigger.delivery.attempted payload MUST NOT carry an inbound-content field ("${banned}")
|
|
156
|
+
req('openwop.it.trigger-ingestion.trigger-delivery-attempted-declares-only-the-content-free-rfc-0083-c-fields', 'RFC 0083 §C', `trigger-ingestion-content-redaction — the durable trigger.delivery.attempted payload MUST NOT carry an inbound-content field ("${banned}")`),
|
|
155
157
|
).toBe(false);
|
|
156
158
|
}
|
|
157
159
|
});
|
|
@@ -165,16 +167,16 @@ describe('trigger-ingestion: TriggerSubscriptionRegistration schema (always-on,
|
|
|
165
167
|
const reg = JSON.parse(readFileSync(REG_FIXTURE, 'utf8'));
|
|
166
168
|
expect(
|
|
167
169
|
validate(reg),
|
|
168
|
-
`trigger-subscription-registration.schema.json MUST accept a conforming registration. Errors: ${JSON.stringify(validate.errors)}
|
|
170
|
+
req('openwop.it.trigger-ingestion.the-canonical-registration-fixture-validates', 'RFC 0099', `trigger-subscription-registration.schema.json MUST accept a conforming registration. Errors: ${JSON.stringify(validate.errors)}`),
|
|
169
171
|
).toBe(true);
|
|
170
172
|
});
|
|
171
173
|
|
|
172
174
|
it('requires source + workflowId and rejects an out-of-enum source', () => {
|
|
173
|
-
expect(validate({ workflowId: 'w' }), 'registration MUST require `source`').toBe(false);
|
|
174
|
-
expect(validate({ source: 'email' }), 'registration MUST require `workflowId`').toBe(false);
|
|
175
|
+
expect(validate({ workflowId: 'w' }), req('openwop.it.trigger-ingestion.requires-source-workflowid-and-rejects-an-out-of-enum-source', 'RFC 0099', 'registration MUST require `source`')).toBe(false);
|
|
176
|
+
expect(validate({ source: 'email' }), req('openwop.it.trigger-ingestion.requires-source-workflowid-and-rejects-an-out-of-enum-source', 'RFC 0099', 'registration MUST require `workflowId`')).toBe(false);
|
|
175
177
|
expect(
|
|
176
178
|
validate({ source: 'schedule', workflowId: 'w' }),
|
|
177
|
-
'registration `source` MUST be one of webhook/email/form (schedule is not externally registerable)',
|
|
179
|
+
req('openwop.it.trigger-ingestion.requires-source-workflowid-and-rejects-an-out-of-enum-source', 'RFC 0099', 'registration `source` MUST be one of webhook/email/form (schedule is not externally registerable)'),
|
|
178
180
|
).toBe(false);
|
|
179
181
|
});
|
|
180
182
|
});
|
|
@@ -192,13 +194,13 @@ describe.skipIf(HTTP_SKIP)('trigger-ingestion: behavioral ingestion + SSRF (capa
|
|
|
192
194
|
verification: { mode: 'none' },
|
|
193
195
|
attachmentUrl: 'http://169.254.169.254/latest/meta-data/',
|
|
194
196
|
});
|
|
195
|
-
if (ssrf.status === 404 || ssrf.status === 403) return; // seam unwired — soft-skip
|
|
197
|
+
if (ssrf.status === 404 || ssrf.status === 403) return softSkip('blocked', 'precondition not met — `ssrf.status === 404 || ssrf.status === 403` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
196
198
|
|
|
197
199
|
const ssrfBody = ssrf.json as { triggerEvent?: { email?: { attachments?: unknown[] } }; ssrfRefused?: boolean } | undefined;
|
|
198
200
|
expect(
|
|
199
201
|
ssrfBody?.ssrfRefused === true ||
|
|
200
202
|
(ssrfBody?.triggerEvent?.email?.attachments ?? []).length === 0,
|
|
201
|
-
|
|
203
|
+
req('openwop.it.trigger-ingestion.an-ingestion-path-fetch-to-a-private-address-is-refused-a-delivered-event-is-con',
|
|
202
204
|
'trigger-bridge.md §F.4',
|
|
203
205
|
'trigger-ingestion-ssrf — an ingestion-path fetch to a private address MUST be refused (attachment dropped), the run still starting on the rest of the event',
|
|
204
206
|
),
|
|
@@ -211,14 +213,14 @@ describe.skipIf(HTTP_SKIP)('trigger-ingestion: behavioral ingestion + SSRF (capa
|
|
|
211
213
|
verification: { mode: 'none' },
|
|
212
214
|
webhook: { method: 'POST', headers: { Authorization: 'Bearer canary' }, body: { x: 1 } },
|
|
213
215
|
});
|
|
214
|
-
if (del.status === 404 || del.status === 403) return;
|
|
216
|
+
if (del.status === 404 || del.status === 403) return softSkip('blocked', 'precondition not met — `del.status === 404 || del.status === 403` returned early (seam, prior step, or fixture unavailable)');
|
|
215
217
|
const delBody = del.json as
|
|
216
218
|
| { deliveryEvent?: Record<string, unknown>; triggerEvent?: { webhook?: { headers?: Record<string, string> } } }
|
|
217
219
|
| undefined;
|
|
218
220
|
const evtJson = JSON.stringify(delBody?.deliveryEvent ?? {});
|
|
219
221
|
expect(
|
|
220
222
|
evtJson.includes('Bearer canary') === false && evtJson.includes('Authorization') === false,
|
|
221
|
-
|
|
223
|
+
req('openwop.it.trigger-ingestion.an-ingestion-path-fetch-to-a-private-address-is-refused-a-delivered-event-is-con',
|
|
222
224
|
'trigger-bridge.md §F.4',
|
|
223
225
|
'trigger-ingestion-content-redaction — the durable trigger.delivery.attempted MUST NOT carry inbound body/header content',
|
|
224
226
|
),
|
|
@@ -226,7 +228,7 @@ describe.skipIf(HTTP_SKIP)('trigger-ingestion: behavioral ingestion + SSRF (capa
|
|
|
226
228
|
const passedHeaders = delBody?.triggerEvent?.webhook?.headers ?? {};
|
|
227
229
|
expect(
|
|
228
230
|
Object.keys(passedHeaders).every((k) => k.toLowerCase() !== 'authorization'),
|
|
229
|
-
|
|
231
|
+
req('openwop.it.trigger-ingestion.an-ingestion-path-fetch-to-a-private-address-is-refused-a-delivered-event-is-con',
|
|
230
232
|
'trigger-bridge.md §F.1',
|
|
231
233
|
'webhook.headers MUST be a host-curated allowlist — Authorization MUST NOT pass through',
|
|
232
234
|
),
|
|
@@ -48,6 +48,8 @@ import { driver } from '../lib/driver.js';
|
|
|
48
48
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
49
49
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
50
50
|
import { driveDelivery } from '../lib/triggerBridge.js';
|
|
51
|
+
import { req } from '../lib/requirement-ids.js';
|
|
52
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
51
53
|
|
|
52
54
|
const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
|
|
53
55
|
|
|
@@ -71,7 +73,7 @@ describe('trigger-stream-cdc: TriggerEvent schema for stream/change (always-on,
|
|
|
71
73
|
const ev = JSON.parse(readFileSync(STREAM_FIXTURE, 'utf8'));
|
|
72
74
|
expect(
|
|
73
75
|
validate(ev),
|
|
74
|
-
`trigger-event.schema.json MUST accept a conforming stream TriggerEvent (RFC 0127 §2). Errors: ${JSON.stringify(validate.errors)}
|
|
76
|
+
req('openwop.it.trigger-stream-cdc-sources.the-canonical-stream-triggerevent-fixture-validates', 'RFC 0127', `trigger-event.schema.json MUST accept a conforming stream TriggerEvent (RFC 0127 §2). Errors: ${JSON.stringify(validate.errors)}`),
|
|
75
77
|
).toBe(true);
|
|
76
78
|
});
|
|
77
79
|
|
|
@@ -79,7 +81,7 @@ describe('trigger-stream-cdc: TriggerEvent schema for stream/change (always-on,
|
|
|
79
81
|
const ev = JSON.parse(readFileSync(CHANGE_FIXTURE, 'utf8'));
|
|
80
82
|
expect(
|
|
81
83
|
validate(ev),
|
|
82
|
-
`trigger-event.schema.json MUST accept a conforming change TriggerEvent (RFC 0127 §2 / RFC 0128 §1). Errors: ${JSON.stringify(validate.errors)}
|
|
84
|
+
req('openwop.it.trigger-stream-cdc-sources.the-canonical-change-triggerevent-fixture-validates-op-permittedpurposes-present', 'RFC 0127', `trigger-event.schema.json MUST accept a conforming change TriggerEvent (RFC 0127 §2 / RFC 0128 §1). Errors: ${JSON.stringify(validate.errors)}`),
|
|
83
85
|
).toBe(true);
|
|
84
86
|
});
|
|
85
87
|
|
|
@@ -88,7 +90,7 @@ describe('trigger-stream-cdc: TriggerEvent schema for stream/change (always-on,
|
|
|
88
90
|
delete ev.change.op;
|
|
89
91
|
expect(
|
|
90
92
|
validate(ev),
|
|
91
|
-
'RFC 0127 §2 — `op` (insert|update|delete) is REQUIRED on a change event (schema-enforced in the ChangeEvent $def)',
|
|
93
|
+
req('openwop.it.trigger-stream-cdc-sources.a-change-event-without-op-fails-changeevent-requires-the-operation-discriminator', 'RFC 0127', 'RFC 0127 §2 — `op` (insert|update|delete) is REQUIRED on a change event (schema-enforced in the ChangeEvent $def)'),
|
|
92
94
|
).toBe(false);
|
|
93
95
|
});
|
|
94
96
|
|
|
@@ -97,7 +99,7 @@ describe('trigger-stream-cdc: TriggerEvent schema for stream/change (always-on,
|
|
|
97
99
|
ev.change.op = 'upsert';
|
|
98
100
|
expect(
|
|
99
101
|
validate(ev),
|
|
100
|
-
'RFC 0127 §2 — `op` MUST be one of insert|update|delete',
|
|
102
|
+
req('openwop.it.trigger-stream-cdc-sources.an-out-of-enum-op-fails', 'RFC 0127', 'RFC 0127 §2 — `op` MUST be one of insert|update|delete'),
|
|
101
103
|
).toBe(false);
|
|
102
104
|
});
|
|
103
105
|
|
|
@@ -106,7 +108,7 @@ describe('trigger-stream-cdc: TriggerEvent schema for stream/change (always-on,
|
|
|
106
108
|
ev.change = { op: 'insert', table: 't' };
|
|
107
109
|
expect(
|
|
108
110
|
validate(ev),
|
|
109
|
-
'trigger-bridge.md §F.1 — a TriggerEvent MUST carry exactly the per-source sub-object matching its `source` and MUST NOT carry the others (extends to stream/change per RFC 0127)',
|
|
111
|
+
req('openwop.it.trigger-stream-cdc-sources.the-f-1-exactly-one-rule-extends-a-source-stream-event-carrying-a-change-sub-obj', 'RFC 0127', 'trigger-bridge.md §F.1 — a TriggerEvent MUST carry exactly the per-source sub-object matching its `source` and MUST NOT carry the others (extends to stream/change per RFC 0127)'),
|
|
110
112
|
).toBe(false);
|
|
111
113
|
});
|
|
112
114
|
});
|
|
@@ -121,7 +123,7 @@ describe('trigger-stream-cdc: registration + capabilities vocabulary (always-on,
|
|
|
121
123
|
for (const source of ['stream', 'change']) {
|
|
122
124
|
expect(
|
|
123
125
|
validateReg({ source, workflowId: 'wf_1' }),
|
|
124
|
-
`trigger-subscription-registration.schema.json MUST accept source:"${source}" (RFC 0127 §1). Errors: ${JSON.stringify(validateReg.errors)}
|
|
126
|
+
req('openwop.it.trigger-stream-cdc-sources.a-registration-accepts-source-stream-and-source-change', 'RFC 0127', `trigger-subscription-registration.schema.json MUST accept source:"${source}" (RFC 0127 §1). Errors: ${JSON.stringify(validateReg.errors)}`),
|
|
125
127
|
).toBe(true);
|
|
126
128
|
}
|
|
127
129
|
});
|
|
@@ -132,8 +134,8 @@ describe('trigger-stream-cdc: registration + capabilities vocabulary (always-on,
|
|
|
132
134
|
const sources: string[] = tb.sources?.items?.enum ?? [];
|
|
133
135
|
const external: string[] = tb.ingestion?.properties?.externalSources?.items?.enum ?? [];
|
|
134
136
|
for (const v of ['stream', 'change']) {
|
|
135
|
-
expect(sources.includes(v), `RFC 0127 §1 — triggerBridge.sources[] enum MUST include "${v}"`).toBe(true);
|
|
136
|
-
expect(external.includes(v), `RFC 0127 §3 — ingestion.externalSources[] enum MUST include "${v}"`).toBe(true);
|
|
137
|
+
expect(sources.includes(v), req('openwop.it.trigger-stream-cdc-sources.the-capabilities-source-enums-include-stream-change-regression-pin', 'RFC 0127', `RFC 0127 §1 — triggerBridge.sources[] enum MUST include "${v}"`)).toBe(true);
|
|
138
|
+
expect(external.includes(v), req('openwop.it.trigger-stream-cdc-sources.the-capabilities-source-enums-include-stream-change-regression-pin', 'RFC 0127', `RFC 0127 §3 — ingestion.externalSources[] enum MUST include "${v}"`)).toBe(true);
|
|
137
139
|
}
|
|
138
140
|
});
|
|
139
141
|
});
|
|
@@ -169,7 +171,7 @@ describe.skipIf(HTTP_SKIP)('trigger-stream-cdc: behavioral ingestion + dedup (ca
|
|
|
169
171
|
if ((res.status === 404 || res.status === 405 || res.status === 400 || res.status === 422) && !advertisesNew) continue; // pre-0127 host — soft-skip this source
|
|
170
172
|
expect(
|
|
171
173
|
res.status < 400,
|
|
172
|
-
|
|
174
|
+
req('openwop.it.trigger-stream-cdc-sources.a-stream-and-a-change-event-each-ingest-to-a-run-with-a-schema-valid-envelope-an',
|
|
173
175
|
'trigger-bridge.md §F.5',
|
|
174
176
|
`a host advertising ingestion of "${body.source as string}" MUST ingest it (advertise-only-what-you-honor, RFC 0127 §Negative example) — got HTTP ${res.status}`,
|
|
175
177
|
),
|
|
@@ -178,7 +180,7 @@ describe.skipIf(HTTP_SKIP)('trigger-stream-cdc: behavioral ingestion + dedup (ca
|
|
|
178
180
|
const out = res.json as { triggerEvent?: Record<string, unknown>; deliveryEvent?: Record<string, unknown> } | undefined;
|
|
179
181
|
expect(
|
|
180
182
|
validate(out?.triggerEvent),
|
|
181
|
-
|
|
183
|
+
req('openwop.it.trigger-stream-cdc-sources.a-stream-and-a-change-event-each-ingest-to-a-run-with-a-schema-valid-envelope-an',
|
|
182
184
|
'trigger-bridge.md §F.5',
|
|
183
185
|
`the delivered ${body.source as string} envelope MUST validate against trigger-event.schema.json. Errors: ${JSON.stringify(validate.errors)}`,
|
|
184
186
|
),
|
|
@@ -188,7 +190,7 @@ describe.skipIf(HTTP_SKIP)('trigger-stream-cdc: behavioral ingestion + dedup (ca
|
|
|
188
190
|
const durable = JSON.stringify(out?.deliveryEvent ?? {});
|
|
189
191
|
expect(
|
|
190
192
|
!durable.includes('CANARY-STREAM-BODY') && !durable.includes('CANARY-CHANGE-BODY'),
|
|
191
|
-
|
|
193
|
+
req('openwop.it.trigger-stream-cdc-sources.a-stream-and-a-change-event-each-ingest-to-a-run-with-a-schema-valid-envelope-an',
|
|
192
194
|
'trigger-bridge.md §F.5 (SR-1)',
|
|
193
195
|
'the durable trigger.delivery.attempted MUST be content-free — the broker message / CDC row body has no slot on the event log',
|
|
194
196
|
),
|
|
@@ -202,11 +204,11 @@ describe.skipIf(HTTP_SKIP)('trigger-stream-cdc: behavioral ingestion + dedup (ca
|
|
|
202
204
|
if (!behaviorGate('triggerBridge.ingestion', (external.length ?? 0) > 0)) return;
|
|
203
205
|
|
|
204
206
|
const first = await driveDelivery({ scenario: 'dedup', dedupKey: 'events:3:99001', source: 'stream' });
|
|
205
|
-
if (first === null) return; // delivery seam unwired — soft-skip
|
|
206
|
-
if (first.outcome === undefined && !external.includes('stream')) return; // pre-0127 host — soft-skip
|
|
207
|
+
if (first === null) return softSkip('blocked', 'precondition not met — `first === null` returned early (delivery seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // delivery seam unwired — soft-skip
|
|
208
|
+
if (first.outcome === undefined && !external.includes('stream')) return softSkip('blocked', 'precondition not met — `first.outcome === undefined && !external.includes(\'stream\')` returned early (pre-0127 host — soft-skip) (seam, prior step, or fixture unavailable)'); // pre-0127 host — soft-skip
|
|
207
209
|
expect(
|
|
208
210
|
first.deliveredCount === 1 || first.outcome === 'delivered',
|
|
209
|
-
|
|
211
|
+
req('openwop.it.trigger-stream-cdc-sources.stream-dedup-the-same-broker-coordinates-delivered-twice-are-effectively-once-c',
|
|
210
212
|
'trigger-bridge.md §F.5 / §C-1',
|
|
211
213
|
'a stream event dedup-keyed on (topic,partition,offset) redelivered within the window MUST be effectively-once — reuses the RFC 0083 §C-1 ≥24h floor unchanged',
|
|
212
214
|
),
|
|
@@ -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 DiscoveryDoc {
|
|
21
23
|
capabilities?: Record<string, unknown>;
|
|
@@ -32,10 +34,10 @@ async function readCap(): Promise<Record<string, unknown> | null> {
|
|
|
32
34
|
describe('vector-knn-roundtrip: advertisement shape (RFC 0018)', () => {
|
|
33
35
|
it('capabilities.vectorStore is either absent or a well-formed object', async () => {
|
|
34
36
|
const cap = await readCap();
|
|
35
|
-
if (cap === null) return; // host doesn't advertise — skip
|
|
37
|
+
if (cap === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap === null` returned early (host doesn\'t advertise — skip)'); // host doesn't advertise — skip
|
|
36
38
|
expect(
|
|
37
39
|
typeof cap.supported,
|
|
38
|
-
|
|
40
|
+
req('openwop.it.vector-knn-roundtrip.capabilities-vectorstore-is-either-absent-or-a-well-formed-object',
|
|
39
41
|
'capabilities.schema.json §vectorStore',
|
|
40
42
|
'capabilities.vectorStore.supported MUST be a boolean when present',
|
|
41
43
|
),
|
|
@@ -50,7 +52,7 @@ async function call(op: string, args: Record<string, unknown>) {
|
|
|
50
52
|
describe('vector-knn-roundtrip: behavioral (RFC 0018 §A.vectorStore)', () => {
|
|
51
53
|
it('upsert 10 vectors → query with one of them returns it as the top match', async () => {
|
|
52
54
|
const probe = await call('query', { namespace: '__probe__', vector: [1, 0], topK: 1 });
|
|
53
|
-
if (probe.status === 404) return; // seam not exposed
|
|
55
|
+
if (probe.status === 404) return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (seam not exposed) (seam, prior step, or fixture unavailable)'); // seam not exposed
|
|
54
56
|
const namespace = `knn-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
55
57
|
const items = Array.from({ length: 10 }, (_, i) => ({
|
|
56
58
|
id: `vec-${i}`,
|
|
@@ -62,17 +64,17 @@ describe('vector-knn-roundtrip: behavioral (RFC 0018 §A.vectorStore)', () => {
|
|
|
62
64
|
const queryRes = await call('query', { namespace, vector: items[3]!.vector, topK: 1 });
|
|
63
65
|
expect(queryRes.status).toBe(200);
|
|
64
66
|
const body = queryRes.json as { matches?: Array<{ id?: string; score?: number }> };
|
|
65
|
-
expect(Array.isArray(body.matches), 'matches MUST be an array').toBe(true);
|
|
67
|
+
expect(Array.isArray(body.matches), req('openwop.it.vector-knn-roundtrip.upsert-10-vectors-query-with-one-of-them-returns-it-as-the-top-match', 'RFC 0018 §A.vectorStore', 'matches MUST be an array')).toBe(true);
|
|
66
68
|
expect(body.matches!.length).toBeGreaterThan(0);
|
|
67
69
|
expect(
|
|
68
70
|
body.matches![0]!.id,
|
|
69
|
-
|
|
71
|
+
req('openwop.it.vector-knn-roundtrip.upsert-10-vectors-query-with-one-of-them-returns-it-as-the-top-match', 'RFC 0018 §A.vectorStore', 'query with an indexed vector MUST return it as the top match'),
|
|
70
72
|
).toBe('vec-3');
|
|
71
73
|
});
|
|
72
74
|
|
|
73
75
|
it('topK respects the configured limit', async () => {
|
|
74
76
|
const probe = await call('query', { namespace: '__probe__', vector: [1, 0], topK: 1 });
|
|
75
|
-
if (probe.status === 404) return;
|
|
77
|
+
if (probe.status === 404) return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
76
78
|
const namespace = `topk-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
77
79
|
const items = Array.from({ length: 8 }, (_, i) => ({
|
|
78
80
|
id: `t-${i}`,
|
|
@@ -83,7 +85,7 @@ describe('vector-knn-roundtrip: behavioral (RFC 0018 §A.vectorStore)', () => {
|
|
|
83
85
|
const body = r3.json as { matches?: unknown[] };
|
|
84
86
|
expect(
|
|
85
87
|
Array.isArray(body.matches) && body.matches.length <= 3,
|
|
86
|
-
|
|
88
|
+
req('openwop.it.vector-knn-roundtrip.topk-respects-the-configured-limit', 'RFC 0018 §A.vectorStore', 'query MUST return at most topK matches'),
|
|
87
89
|
).toBe(true);
|
|
88
90
|
});
|
|
89
91
|
});
|
|
@@ -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/agents/verify-run';
|
|
28
30
|
|
|
@@ -48,26 +50,26 @@ describe('verifier-gating (RFC 0090 §B)', () => {
|
|
|
48
50
|
|
|
49
51
|
// FAIL verdict → must NOT complete as success; agent.verified{fail} emitted.
|
|
50
52
|
const failRes = await driver.post(SEAM, { simulateVerdict: 'fail' });
|
|
51
|
-
if (failRes.status === 404) return; // seam unwired — soft-skip
|
|
53
|
+
if (failRes.status === 404) return softSkip('blocked', 'precondition not met — `failRes.status === 404` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
52
54
|
expect(
|
|
53
55
|
verifiedVerdict(failRes.json) === 'fail',
|
|
54
|
-
|
|
56
|
+
req('openwop.it.verifier-gating.a-fail-verdict-blocks-commit-on-a-gating-host-a-pass-verdict-completes', 'RFC 0090 §A', 'a verify-run forcing a fail MUST emit agent.verified{verdict:"fail"}'),
|
|
55
57
|
).toBe(true);
|
|
56
58
|
expect(
|
|
57
59
|
!isSuccess(failRes.json),
|
|
58
|
-
|
|
60
|
+
req('openwop.it.verifier-gating.a-fail-verdict-blocks-commit-on-a-gating-host-a-pass-verdict-completes', 'RFC 0090 §B', 'on a gating host a fail verdict MUST block commit/terminate-as-success'),
|
|
59
61
|
).toBe(true);
|
|
60
62
|
|
|
61
63
|
// PASS verdict → completes normally.
|
|
62
64
|
const passRes = await driver.post(SEAM, { simulateVerdict: 'pass' });
|
|
63
|
-
if (passRes.status === 404) return;
|
|
65
|
+
if (passRes.status === 404) return softSkip('blocked', 'precondition not met — `passRes.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
64
66
|
expect(
|
|
65
67
|
verifiedVerdict(passRes.json) === 'pass',
|
|
66
|
-
|
|
68
|
+
req('openwop.it.verifier-gating.a-fail-verdict-blocks-commit-on-a-gating-host-a-pass-verdict-completes', 'RFC 0090 §A', 'a verify-run forcing a pass MUST emit agent.verified{verdict:"pass"}'),
|
|
67
69
|
).toBe(true);
|
|
68
70
|
expect(
|
|
69
71
|
isSuccess(passRes.json),
|
|
70
|
-
|
|
72
|
+
req('openwop.it.verifier-gating.a-fail-verdict-blocks-commit-on-a-gating-host-a-pass-verdict-completes', 'RFC 0090 §B', 'a pass verdict MUST allow the turn to complete'),
|
|
71
73
|
).toBe(true);
|
|
72
74
|
});
|
|
73
75
|
});
|
|
@@ -39,6 +39,7 @@ import { driver, type OpenWOPResponse } from '../lib/driver.js';
|
|
|
39
39
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
40
40
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
41
41
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
42
|
+
import { req } from '../lib/requirement-ids.js';
|
|
42
43
|
|
|
43
44
|
const FIXTURE_ID = 'conformance-version-fold';
|
|
44
45
|
const SKIP_NO_FIXTURE = !isFixtureAdvertised(FIXTURE_ID);
|
|
@@ -94,7 +95,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('version-fold: forced engine versions fold-best
|
|
|
94
95
|
);
|
|
95
96
|
expect(
|
|
96
97
|
versions.length,
|
|
97
|
-
|
|
98
|
+
req('openwop.it.version-fold.each-version-in-min-current-max-completes-snapshot-and-event-log-stay-readable',
|
|
98
99
|
'version-negotiation.md §Conformance via X-Force-Engine-Version',
|
|
99
100
|
'forceEngineVersionRange MUST describe a non-empty integer range (min <= max)',
|
|
100
101
|
),
|
|
@@ -109,7 +110,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('version-fold: forced engine versions fold-best
|
|
|
109
110
|
}
|
|
110
111
|
expect(
|
|
111
112
|
create.status,
|
|
112
|
-
|
|
113
|
+
req('openwop.it.version-fold.each-version-in-min-current-max-completes-snapshot-and-event-log-stay-readable',
|
|
113
114
|
'version-negotiation.md §Conformance via X-Force-Engine-Version',
|
|
114
115
|
`POST /v1/runs with X-Force-Engine-Version: ${v} (within the advertised range) MUST be accepted on a test key`,
|
|
115
116
|
),
|
|
@@ -120,7 +121,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('version-fold: forced engine versions fold-best
|
|
|
120
121
|
const terminal = await pollUntilTerminal(runId);
|
|
121
122
|
expect(
|
|
122
123
|
terminal.status,
|
|
123
|
-
|
|
124
|
+
req('openwop.it.version-fold.each-version-in-min-current-max-completes-snapshot-and-event-log-stay-readable',
|
|
124
125
|
'version-negotiation.md §Cross-version interop matrix',
|
|
125
126
|
`the conformance-version-fold noop run MUST complete under forced engine version ${v} (fold-best-effort)`,
|
|
126
127
|
),
|
|
@@ -130,7 +131,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('version-fold: forced engine versions fold-best
|
|
|
130
131
|
const snap = await driver.get(`/v1/runs/${encodeURIComponent(runId)}`);
|
|
131
132
|
expect(
|
|
132
133
|
snap.status,
|
|
133
|
-
|
|
134
|
+
req('openwop.it.version-fold.each-version-in-min-current-max-completes-snapshot-and-event-log-stay-readable',
|
|
134
135
|
'version-negotiation.md §Cross-version interop matrix',
|
|
135
136
|
`GET /v1/runs/{runId} MUST return a readable RunSnapshot under forced engine version ${v}`,
|
|
136
137
|
),
|
|
@@ -145,7 +146,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('version-fold: forced engine versions fold-best
|
|
|
145
146
|
);
|
|
146
147
|
expect(
|
|
147
148
|
events.status,
|
|
148
|
-
|
|
149
|
+
req('openwop.it.version-fold.each-version-in-min-current-max-completes-snapshot-and-event-log-stay-readable',
|
|
149
150
|
'version-negotiation.md §Conformance via X-Force-Engine-Version',
|
|
150
151
|
`the event log MUST be readable via events/poll under forced engine version ${v}`,
|
|
151
152
|
),
|
|
@@ -153,7 +154,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('version-fold: forced engine versions fold-best
|
|
|
153
154
|
const eventList = (events.json as { events?: unknown[] } | undefined)?.events ?? [];
|
|
154
155
|
expect(
|
|
155
156
|
eventList.length,
|
|
156
|
-
|
|
157
|
+
req('openwop.it.version-fold.each-version-in-min-current-max-completes-snapshot-and-event-log-stay-readable',
|
|
157
158
|
'version-negotiation.md §Conformance via X-Force-Engine-Version',
|
|
158
159
|
`events/poll MUST return a non-empty events[] for the forced-version run (v=${v})`,
|
|
159
160
|
),
|
|
@@ -178,14 +179,14 @@ describe.skipIf(SKIP_NO_FIXTURE)('version-fold: forced engine versions fold-best
|
|
|
178
179
|
}
|
|
179
180
|
expect(
|
|
180
181
|
create.status,
|
|
181
|
-
|
|
182
|
+
req('openwop.it.version-fold.a-forced-version-outside-the-advertised-range-is-rejected-with-400-unsupported-f',
|
|
182
183
|
'version-negotiation.md §Conformance via X-Force-Engine-Version',
|
|
183
184
|
`X-Force-Engine-Version: ${outOfRange} (outside the advertised range) MUST return 400`,
|
|
184
185
|
),
|
|
185
186
|
).toBe(400);
|
|
186
187
|
expect(
|
|
187
188
|
errCode(create.json),
|
|
188
|
-
|
|
189
|
+
req('openwop.it.version-fold.a-forced-version-outside-the-advertised-range-is-rejected-with-400-unsupported-f',
|
|
189
190
|
'version-negotiation.md §Conformance via X-Force-Engine-Version',
|
|
190
191
|
'the out-of-range refusal MUST carry the canonical unsupported_force_engine_version code',
|
|
191
192
|
),
|
|
@@ -26,6 +26,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
26
26
|
import { driver } from '../lib/driver.js';
|
|
27
27
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
28
28
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
29
|
+
import { req } from '../lib/requirement-ids.js';
|
|
29
30
|
|
|
30
31
|
const NOOP_WORKFLOW_ID = 'conformance-noop';
|
|
31
32
|
const SKIP_NO_NOOP = !isFixtureAdvertised(NOOP_WORKFLOW_ID);
|
|
@@ -47,7 +48,7 @@ describe('version-negotiation: Capabilities advertises a protocolVersion', () =>
|
|
|
47
48
|
expect(res.status).toBe(200);
|
|
48
49
|
|
|
49
50
|
const caps = res.json as { protocolVersion?: unknown };
|
|
50
|
-
expect(typeof caps.protocolVersion,
|
|
51
|
+
expect(typeof caps.protocolVersion, req('openwop.it.version-negotiation.get-well-known-openwop-returns-capabilities-with-protocolversion-string',
|
|
51
52
|
'capabilities.md §3 + version-negotiation.md',
|
|
52
53
|
'Capabilities.protocolVersion MUST be a non-empty string',
|
|
53
54
|
)).toBe('string');
|
|
@@ -69,34 +70,34 @@ describe.skipIf(SKIP_NO_NOOP)('version-negotiation: persisted events carry the c
|
|
|
69
70
|
expect(eventsRes.status).toBe(200);
|
|
70
71
|
|
|
71
72
|
const events = (eventsRes.json as { events?: RunEvent[] } | undefined)?.events ?? [];
|
|
72
|
-
expect(events.length, 'noop run MUST emit at least one event').toBeGreaterThan(0);
|
|
73
|
+
expect(events.length, req('openwop.it.version-negotiation.every-event-has-the-6-required-runeventdoc-fields-per-run-event-schema-json', 'run-event.schema.json §required', 'noop run MUST emit at least one event')).toBeGreaterThan(0);
|
|
73
74
|
|
|
74
75
|
for (const e of events) {
|
|
75
|
-
expect(typeof e.eventId,
|
|
76
|
+
expect(typeof e.eventId, req('openwop.it.version-negotiation.every-event-has-the-6-required-runeventdoc-fields-per-run-event-schema-json',
|
|
76
77
|
'run-event.schema.json §required',
|
|
77
78
|
'eventId MUST be a string',
|
|
78
79
|
)).toBe('string');
|
|
79
|
-
expect(typeof e.runId,
|
|
80
|
+
expect(typeof e.runId, req('openwop.it.version-negotiation.every-event-has-the-6-required-runeventdoc-fields-per-run-event-schema-json',
|
|
80
81
|
'run-event.schema.json §required',
|
|
81
82
|
'runId MUST be a string',
|
|
82
83
|
)).toBe('string');
|
|
83
|
-
expect(typeof e.type,
|
|
84
|
+
expect(typeof e.type, req('openwop.it.version-negotiation.every-event-has-the-6-required-runeventdoc-fields-per-run-event-schema-json',
|
|
84
85
|
'run-event.schema.json §required',
|
|
85
86
|
'type MUST be a string (RunEventType discriminator)',
|
|
86
87
|
)).toBe('string');
|
|
87
|
-
expect(e.payload,
|
|
88
|
+
expect(e.payload, req('openwop.it.version-negotiation.every-event-has-the-6-required-runeventdoc-fields-per-run-event-schema-json',
|
|
88
89
|
'run-event.schema.json §required',
|
|
89
90
|
'payload MUST be present (any JSON value, including null)',
|
|
90
91
|
)).not.toBe(undefined);
|
|
91
|
-
expect(typeof e.timestamp,
|
|
92
|
+
expect(typeof e.timestamp, req('openwop.it.version-negotiation.every-event-has-the-6-required-runeventdoc-fields-per-run-event-schema-json',
|
|
92
93
|
'run-event.schema.json §required',
|
|
93
94
|
'timestamp MUST be an ISO 8601 string',
|
|
94
95
|
)).toBe('string');
|
|
95
|
-
expect(Number.isInteger(e.sequence),
|
|
96
|
+
expect(Number.isInteger(e.sequence), req('openwop.it.version-negotiation.every-event-has-the-6-required-runeventdoc-fields-per-run-event-schema-json',
|
|
96
97
|
'run-event.schema.json §required',
|
|
97
98
|
'sequence MUST be a non-negative integer',
|
|
98
99
|
)).toBe(true);
|
|
99
|
-
expect(e.sequence, 'sequence MUST be >= 0').toBeGreaterThanOrEqual(0);
|
|
100
|
+
expect(e.sequence, req('openwop.it.version-negotiation.every-event-has-the-6-required-runeventdoc-fields-per-run-event-schema-json', 'run-event.schema.json §required', 'sequence MUST be >= 0')).toBeGreaterThanOrEqual(0);
|
|
100
101
|
}
|
|
101
102
|
});
|
|
102
103
|
|
|
@@ -118,7 +119,7 @@ describe.skipIf(SKIP_NO_NOOP)('version-negotiation: persisted events carry the c
|
|
|
118
119
|
const curr = sequences[i] ?? -1;
|
|
119
120
|
expect(
|
|
120
121
|
curr,
|
|
121
|
-
|
|
122
|
+
req('openwop.it.version-negotiation.event-sequences-within-a-run-are-strictly-monotonic',
|
|
122
123
|
'run-event.schema.json §sequence + idempotency.md',
|
|
123
124
|
`event[${i}].sequence (${curr}) MUST be > event[${i - 1}].sequence (${prev}) — strictly monotonic per run`,
|
|
124
125
|
),
|
|
@@ -144,7 +145,7 @@ describe.skipIf(SKIP_NO_NOOP)('version-negotiation: events/poll forward-compat t
|
|
|
144
145
|
|
|
145
146
|
expect(
|
|
146
147
|
eventsRes.status,
|
|
147
|
-
|
|
148
|
+
req('openwop.it.version-negotiation.events-poll-with-lastsequence-past-current-end-returns-empty-events-iscomplete',
|
|
148
149
|
'rest-endpoints.md GET /v1/runs/{runId}/events/poll',
|
|
149
150
|
'lastSequence beyond the current end MUST return 200 with empty events, not 4xx',
|
|
150
151
|
),
|
|
@@ -21,6 +21,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
21
21
|
import { driver } from '../lib/driver.js';
|
|
22
22
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
23
23
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
24
|
+
import { req } from '../lib/requirement-ids.js';
|
|
25
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
24
26
|
|
|
25
27
|
const SEAM = '/v1/host/sample/voice/barge-in';
|
|
26
28
|
|
|
@@ -40,22 +42,22 @@ describe('voice-bargein-no-partial-leak (RFC 0106 §F INV-3)', () => {
|
|
|
40
42
|
if (!behaviorGate('openwop-voice-bargein', advertised)) return;
|
|
41
43
|
|
|
42
44
|
const res = await driver.post(SEAM, {});
|
|
43
|
-
if (res.status === 404) return; // seam unwired — soft-skip
|
|
45
|
+
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
|
|
44
46
|
|
|
45
|
-
expect(res.status === 200,
|
|
47
|
+
expect(res.status === 200, req('openwop.it.voice-bargein-no-partial-leak.barge-in-cancels-with-no-synthesis-chunk-emitted-after-voice-cancelled', 'RFC 0106 §F', 'the barge-in seam MUST return 200')).toBe(true);
|
|
46
48
|
|
|
47
49
|
const types = eventsOf(res.json).map((e) => e.type);
|
|
48
50
|
const bargeIdx = types.indexOf('voice.barge_in');
|
|
49
51
|
const cancelIdx = types.indexOf('voice.cancelled');
|
|
50
|
-
expect(bargeIdx >= 0,
|
|
51
|
-
expect(cancelIdx >= 0,
|
|
52
|
-
expect(cancelIdx > bargeIdx,
|
|
52
|
+
expect(bargeIdx >= 0, req('openwop.it.voice-bargein-no-partial-leak.barge-in-cancels-with-no-synthesis-chunk-emitted-after-voice-cancelled', 'RFC 0106 §D', 'a barge-in MUST emit voice.barge_in')).toBe(true);
|
|
53
|
+
expect(cancelIdx >= 0, req('openwop.it.voice-bargein-no-partial-leak.barge-in-cancels-with-no-synthesis-chunk-emitted-after-voice-cancelled', 'RFC 0106 §D', 'a barge-in that cancels work MUST emit voice.cancelled')).toBe(true);
|
|
54
|
+
expect(cancelIdx > bargeIdx, req('openwop.it.voice-bargein-no-partial-leak.barge-in-cancels-with-no-synthesis-chunk-emitted-after-voice-cancelled', 'RFC 0106 §D', 'voice.cancelled MUST follow voice.barge_in')).toBe(true);
|
|
53
55
|
|
|
54
56
|
// The load-bearing INV-3 assertion: nothing partial after the cancel.
|
|
55
57
|
const afterCancel = types.slice(cancelIdx + 1);
|
|
56
58
|
expect(
|
|
57
59
|
!afterCancel.includes('voice.synthesis_chunk'),
|
|
58
|
-
|
|
60
|
+
req('openwop.it.voice-bargein-no-partial-leak.barge-in-cancels-with-no-synthesis-chunk-emitted-after-voice-cancelled', 'RFC 0106 §F INV-3', 'NO voice.synthesis_chunk (partial output) may be emitted AFTER voice.cancelled'),
|
|
59
61
|
).toBe(true);
|
|
60
62
|
});
|
|
61
63
|
});
|
|
@@ -32,8 +32,7 @@ import { join } from 'node:path';
|
|
|
32
32
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
33
33
|
import addFormats from 'ajv-formats';
|
|
34
34
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
35
|
-
|
|
36
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
35
|
+
import { req } from '../lib/requirement-ids.js';
|
|
37
36
|
|
|
38
37
|
function loadSchema(name: string): Record<string, any> {
|
|
39
38
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, any>;
|
|
@@ -63,7 +62,7 @@ describe('voice-event-payloads-shape: the voice.* run-event taxonomy (RFC 0106
|
|
|
63
62
|
it('all seven voice.* types are in the RunEventType enum', () => {
|
|
64
63
|
const enumVals: string[] = loadSchema('run-event.schema.json').$defs.RunEventType.enum;
|
|
65
64
|
for (const t of VOICE_TYPES) {
|
|
66
|
-
expect(enumVals.includes(t),
|
|
65
|
+
expect(enumVals.includes(t), req('openwop.it.voice-event-payloads-shape.all-seven-voice-types-are-in-the-runeventtype-enum', 'run-event.schema.json §RunEventType', `${t} MUST be a RunEventType`)).toBe(true);
|
|
67
66
|
}
|
|
68
67
|
});
|
|
69
68
|
|
|
@@ -71,8 +70,8 @@ describe('voice-event-payloads-shape: the voice.* run-event taxonomy (RFC 0106
|
|
|
71
70
|
const payloads = loadSchema('run-event-payloads.schema.json');
|
|
72
71
|
const typeIndex = payloads.$defs._typeIndex.properties as Record<string, { $ref: string }>;
|
|
73
72
|
for (const t of VOICE_TYPES) {
|
|
74
|
-
expect(typeIndex[t],
|
|
75
|
-
expect(payloads.$defs[DEF_KEY[t]],
|
|
73
|
+
expect(typeIndex[t], req('openwop.it.voice-event-payloads-shape.each-voice-type-has-a-payload-def-reachable-via-typeindex', 'run-event-payloads.schema.json §typeIndex', `${t} MUST map to a $def`)).toBeDefined();
|
|
74
|
+
expect(payloads.$defs[DEF_KEY[t]], req('openwop.it.voice-event-payloads-shape.each-voice-type-has-a-payload-def-reachable-via-typeindex', 'run-event-payloads.schema.json', `$defs.${DEF_KEY[t]} MUST exist`)).toBeDefined();
|
|
76
75
|
}
|
|
77
76
|
});
|
|
78
77
|
|
|
@@ -84,15 +83,15 @@ describe('voice-event-payloads-shape: the voice.* run-event taxonomy (RFC 0106
|
|
|
84
83
|
|
|
85
84
|
expect(
|
|
86
85
|
validate({ text: 'book a table', isFinal: true, atMs: 1200, contentTrust: 'untrusted' }),
|
|
87
|
-
|
|
86
|
+
req('openwop.it.voice-event-payloads-shape.voice-transcript-requires-contenttrust-untrusted-security-voice-transcript-untru', 'RFC 0106 §F INV-2', 'a transcript marked untrusted MUST validate'),
|
|
88
87
|
).toBe(true);
|
|
89
88
|
expect(
|
|
90
89
|
validate({ text: 'book a table', isFinal: true, atMs: 1200 }),
|
|
91
|
-
|
|
90
|
+
req('openwop.it.voice-event-payloads-shape.voice-transcript-requires-contenttrust-untrusted-security-voice-transcript-untru', 'RFC 0106 §F INV-2 (voice-transcript-untrusted)', 'a transcript WITHOUT contentTrust MUST be rejected'),
|
|
92
91
|
).toBe(false);
|
|
93
92
|
expect(
|
|
94
93
|
validate({ text: 'book a table', isFinal: true, atMs: 1200, contentTrust: 'trusted' }),
|
|
95
|
-
|
|
94
|
+
req('openwop.it.voice-event-payloads-shape.voice-transcript-requires-contenttrust-untrusted-security-voice-transcript-untru', 'RFC 0106 §F INV-2 (voice-transcript-untrusted)', 'contentTrust MUST be the const "untrusted" — never promoted'),
|
|
96
95
|
).toBe(false);
|
|
97
96
|
});
|
|
98
97
|
|
|
@@ -104,11 +103,11 @@ describe('voice-event-payloads-shape: the voice.* run-event taxonomy (RFC 0106
|
|
|
104
103
|
|
|
105
104
|
expect(
|
|
106
105
|
validate({ seq: 0, mimeType: 'audio/mpeg', durationMs: 240, url: 'https://host/seg/0', final: false }),
|
|
107
|
-
|
|
106
|
+
req('openwop.it.voice-event-payloads-shape.voice-synthesis-chunk-is-metadata-shaped-seq-mimetype-required-bytes-by-referenc', 'RFC 0106 §C', 'a metadata chunk referencing bytes by url MUST validate'),
|
|
108
107
|
).toBe(true);
|
|
109
108
|
expect(
|
|
110
109
|
validate({ mimeType: 'audio/mpeg' }),
|
|
111
|
-
|
|
110
|
+
req('openwop.it.voice-event-payloads-shape.voice-synthesis-chunk-is-metadata-shaped-seq-mimetype-required-bytes-by-referenc', 'RFC 0106 §C', 'a chunk without seq MUST be rejected'),
|
|
112
111
|
).toBe(false);
|
|
113
112
|
});
|
|
114
113
|
|
|
@@ -120,7 +119,7 @@ describe('voice-event-payloads-shape: the voice.* run-event taxonomy (RFC 0106
|
|
|
120
119
|
const validate = ajv.compile(payloads.$defs[key]);
|
|
121
120
|
expect(
|
|
122
121
|
validate({ atMs: 100, transcriptBody: 'should not be here' }),
|
|
123
|
-
|
|
122
|
+
req('openwop.it.voice-event-payloads-shape.content-free-voice-events-reject-unknown-properties-additionalproperties-false', 'RFC 0106 §D', `${key} MUST reject an unknown (content-bearing) property`),
|
|
124
123
|
).toBe(false);
|
|
125
124
|
}
|
|
126
125
|
});
|