@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
|
@@ -35,8 +35,7 @@ import { join } from 'node:path';
|
|
|
35
35
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
36
36
|
import addFormats from 'ajv-formats';
|
|
37
37
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
38
|
-
|
|
39
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
38
|
+
import { req } from '../lib/requirement-ids.js';
|
|
40
39
|
|
|
41
40
|
function loadSchema(name: string): Record<string, unknown> {
|
|
42
41
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
|
|
@@ -52,17 +51,17 @@ describe('budget-policy-shape: BudgetPolicy (RFC 0084 §A, server-free)', () =>
|
|
|
52
51
|
it('a conforming budget policy validates', () => {
|
|
53
52
|
expect(
|
|
54
53
|
validate({ maxTokens: 200000, maxCostUsd: 1.0, maxToolCalls: 50, maxRetries: 10, modelAllow: ['claude-*'], modelDeny: ['gpt-4-32k'], thresholdPercent: 80, onExhaustion: 'fail' }),
|
|
55
|
-
|
|
54
|
+
req('openwop.it.budget-policy-shape.a-conforming-budget-policy-validates', 'budget-policy.md §A', 'a conforming BudgetPolicy MUST validate'),
|
|
56
55
|
).toBe(true);
|
|
57
56
|
});
|
|
58
57
|
|
|
59
58
|
it('the orthogonality guard: a wall-time field is rejected (it is RFC 0058 runTimeoutMs)', () => {
|
|
60
|
-
expect(validate({ maxCostUsd: 1.0, maxWallTimeMs: 60000 }),
|
|
59
|
+
expect(validate({ maxCostUsd: 1.0, maxWallTimeMs: 60000 }), req('openwop.it.budget-policy-shape.the-orthogonality-guard-a-wall-time-field-is-rejected-it-is-rfc-0058-runtimeoutm', 'budget-policy.md §A/§E', 'wall-time is NOT a budget dimension')).toBe(false);
|
|
61
60
|
});
|
|
62
61
|
|
|
63
62
|
it('rejects an out-of-range thresholdPercent and an out-of-enum onExhaustion', () => {
|
|
64
|
-
expect(validate({ thresholdPercent: 120 }),
|
|
65
|
-
expect(validate({ onExhaustion: 'explode' }),
|
|
63
|
+
expect(validate({ thresholdPercent: 120 }), req('openwop.it.budget-policy-shape.rejects-an-out-of-range-thresholdpercent-and-an-out-of-enum-onexhaustion', 'budget-policy.md §A', 'thresholdPercent MUST be 0..100')).toBe(false);
|
|
64
|
+
expect(validate({ onExhaustion: 'explode' }), req('openwop.it.budget-policy-shape.rejects-an-out-of-range-thresholdpercent-and-an-out-of-enum-onexhaustion', 'budget-policy.md §A', 'onExhaustion is a closed enum')).toBe(false);
|
|
66
65
|
});
|
|
67
66
|
});
|
|
68
67
|
|
|
@@ -76,21 +75,21 @@ describe('budget-policy-shape: budget.* events + cap.breached kinds (RFC 0084 §
|
|
|
76
75
|
} as Record<string, unknown>);
|
|
77
76
|
|
|
78
77
|
it('the four budget.* payloads validate conforming content-free records', () => {
|
|
79
|
-
expect(compile('budgetReserved')({ effectiveBudget: { maxCostUsd: 1.0 }, scope: 'run' }),
|
|
80
|
-
expect(compile('budgetConsumed')({ dimension: 'cost', consumed: 0.7, limit: 1.0, remaining: 0.3 }),
|
|
81
|
-
expect(compile('budgetThresholdCrossed')({ dimension: 'cost', consumed: 0.8, limit: 1.0, percent: 80 }),
|
|
82
|
-
expect(compile('budgetExhausted')({ dimension: 'cost', consumed: 1.02, limit: 1.0 }),
|
|
78
|
+
expect(compile('budgetReserved')({ effectiveBudget: { maxCostUsd: 1.0 }, scope: 'run' }), req('openwop.it.budget-policy-shape.the-four-budget-payloads-validate-conforming-content-free-records', 'budget-policy.md §C', 'budget.reserved MUST validate')).toBe(true);
|
|
79
|
+
expect(compile('budgetConsumed')({ dimension: 'cost', consumed: 0.7, limit: 1.0, remaining: 0.3 }), req('openwop.it.budget-policy-shape.the-four-budget-payloads-validate-conforming-content-free-records', 'budget-policy.md §C', 'budget.consumed MUST validate')).toBe(true);
|
|
80
|
+
expect(compile('budgetThresholdCrossed')({ dimension: 'cost', consumed: 0.8, limit: 1.0, percent: 80 }), req('openwop.it.budget-policy-shape.the-four-budget-payloads-validate-conforming-content-free-records', 'budget-policy.md §C', 'budget.threshold.crossed MUST validate')).toBe(true);
|
|
81
|
+
expect(compile('budgetExhausted')({ dimension: 'cost', consumed: 1.02, limit: 1.0 }), req('openwop.it.budget-policy-shape.the-four-budget-payloads-validate-conforming-content-free-records', 'budget-policy.md §C', 'budget.exhausted MUST validate')).toBe(true);
|
|
83
82
|
});
|
|
84
83
|
|
|
85
84
|
it('rejects an out-of-enum dimension and a missing required field', () => {
|
|
86
|
-
expect(compile('budgetConsumed')({ dimension: 'vibes', consumed: 1, limit: 2 }),
|
|
87
|
-
expect(compile('budgetExhausted')({ dimension: 'cost', consumed: 1.0 }),
|
|
85
|
+
expect(compile('budgetConsumed')({ dimension: 'vibes', consumed: 1, limit: 2 }), req('openwop.it.budget-policy-shape.rejects-an-out-of-enum-dimension-and-a-missing-required-field', 'budget-policy.md §C', 'dimension is a closed enum')).toBe(false);
|
|
86
|
+
expect(compile('budgetExhausted')({ dimension: 'cost', consumed: 1.0 }), req('openwop.it.budget-policy-shape.rejects-an-out-of-enum-dimension-and-a-missing-required-field', 'budget-policy.md §C', 'limit is REQUIRED')).toBe(false);
|
|
88
87
|
});
|
|
89
88
|
|
|
90
89
|
it('the cap.breached kind enum carries the four budget-* values', () => {
|
|
91
90
|
const kinds = ((payloads.$defs as Record<string, { properties?: Record<string, { enum?: string[] }> }>).capBreached.properties?.kind?.enum) ?? [];
|
|
92
91
|
for (const k of ['budget-tokens', 'budget-cost', 'budget-tool-calls', 'budget-retries']) {
|
|
93
|
-
expect(kinds.includes(k),
|
|
92
|
+
expect(kinds.includes(k), req('openwop.it.budget-policy-shape.the-cap-breached-kind-enum-carries-the-four-budget-values', 'budget-policy.md §D', `cap.breached.kind MUST include ${k}`)).toBe(true);
|
|
94
93
|
}
|
|
95
94
|
});
|
|
96
95
|
|
|
@@ -98,7 +97,7 @@ describe('budget-policy-shape: budget.* events + cap.breached kinds (RFC 0084 §
|
|
|
98
97
|
const runEvent = loadSchema('run-event.schema.json');
|
|
99
98
|
const enumVals = ((runEvent.$defs as Record<string, { enum?: string[] }>).RunEventType?.enum) ?? [];
|
|
100
99
|
for (const name of ['budget.reserved', 'budget.consumed', 'budget.threshold.crossed', 'budget.exhausted']) {
|
|
101
|
-
expect(enumVals.includes(name),
|
|
100
|
+
expect(enumVals.includes(name), req('openwop.it.budget-policy-shape.all-four-budget-event-names-appear-in-the-runeventtype-enum', 'run-event.schema.json', `${name} MUST be in the RunEventType enum`)).toBe(true);
|
|
102
101
|
}
|
|
103
102
|
});
|
|
104
103
|
|
|
@@ -106,7 +105,7 @@ describe('budget-policy-shape: budget.* events + cap.breached kinds (RFC 0084 §
|
|
|
106
105
|
const defs = payloads.$defs as Record<string, { properties?: Record<string, unknown> }>;
|
|
107
106
|
for (const def of ['budgetReserved', 'budgetConsumed', 'budgetThresholdCrossed', 'budgetExhausted']) {
|
|
108
107
|
for (const p of Object.keys(defs[def].properties ?? {})) {
|
|
109
|
-
expect(PRICING_PROP_NAMES.includes(p.toLowerCase()),
|
|
108
|
+
expect(PRICING_PROP_NAMES.includes(p.toLowerCase()), req('openwop.it.budget-policy-shape.the-budget-payloads-declare-no-pricing-credential-property-budget-no-pricing-lea', 'budget-no-pricing-leak', `${def} MUST NOT declare a pricing-bearing property (${p})`)).toBe(false);
|
|
110
109
|
}
|
|
111
110
|
}
|
|
112
111
|
});
|
|
@@ -114,10 +113,10 @@ describe('budget-policy-shape: budget.* events + cap.breached kinds (RFC 0084 §
|
|
|
114
113
|
it('the budget.* payloads are additionalProperties:false — a rate-card field on an INSTANCE is rejected', () => {
|
|
115
114
|
// The aggregate cost total (the user's own budget) is permitted; the host's per-unit rate card is not.
|
|
116
115
|
// additionalProperties:false makes the rejection structural, not just a declared-property check.
|
|
117
|
-
expect(compile('budgetConsumed')({ dimension: 'cost', consumed: 0.8, limit: 1.0 }),
|
|
116
|
+
expect(compile('budgetConsumed')({ dimension: 'cost', consumed: 0.8, limit: 1.0 }), req('openwop.it.budget-policy-shape.the-budget-payloads-are-additionalproperties-false-a-rate-card-field-on-an-insta', 'budget-policy.md §F', 'an aggregate cost total (the user budget) MUST validate')).toBe(true);
|
|
118
117
|
expect(
|
|
119
118
|
compile('budgetConsumed')({ dimension: 'cost', consumed: 0.8, limit: 1.0, ratePerToken: 0.000003 }),
|
|
120
|
-
|
|
119
|
+
req('openwop.it.budget-policy-shape.the-budget-payloads-are-additionalproperties-false-a-rate-card-field-on-an-insta', 'budget-no-pricing-leak', 'a rate-card / per-token-price field MUST be rejected (additionalProperties:false)'),
|
|
121
120
|
).toBe(false);
|
|
122
121
|
});
|
|
123
122
|
});
|
|
@@ -127,10 +126,10 @@ describe('budget-policy-shape: capability advertisement (RFC 0084 §E, server-fr
|
|
|
127
126
|
const caps = loadSchema('capabilities.schema.json');
|
|
128
127
|
const props = caps.properties as Record<string, { properties?: Record<string, unknown> }>;
|
|
129
128
|
for (const flag of ['supported', 'dimensions', 'enforce', 'scopes']) {
|
|
130
|
-
expect(props.budget?.properties?.[flag],
|
|
129
|
+
expect(props.budget?.properties?.[flag], req('openwop.it.budget-policy-shape.capabilities-budget-limits-maxbudget-tokens-costusd-are-declared', 'budget-policy.md §E', `capabilities.budget.${flag} MUST be declared`)).toBeDefined();
|
|
131
130
|
}
|
|
132
131
|
for (const ceiling of ['maxBudgetTokens', 'maxBudgetCostUsd']) {
|
|
133
|
-
expect(props.limits?.properties?.[ceiling],
|
|
132
|
+
expect(props.limits?.properties?.[ceiling], req('openwop.it.budget-policy-shape.capabilities-budget-limits-maxbudget-tokens-costusd-are-declared', 'budget-policy.md §E', `limits.${ceiling} MUST be declared`)).toBeDefined();
|
|
134
133
|
}
|
|
135
134
|
});
|
|
136
135
|
});
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
import { describe, it, expect } from 'vitest';
|
|
21
21
|
import { driver } from '../lib/driver.js';
|
|
22
22
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
23
|
+
import { req } from '../lib/requirement-ids.js';
|
|
23
24
|
|
|
24
25
|
const CANCELLABLE = 'conformance-cancellable';
|
|
25
26
|
const NOOP = 'conformance-noop';
|
|
@@ -48,22 +49,22 @@ describe.skipIf(SKIP)('bulk-cancel: POST /v1/runs:bulk-cancel', () => {
|
|
|
48
49
|
runIds: [inflightRunId, 'run-does-not-exist-xxxxxxxx'],
|
|
49
50
|
reason: 'conformance bulk-cancel test',
|
|
50
51
|
});
|
|
51
|
-
expect(res.status,
|
|
52
|
+
expect(res.status, req('openwop.it.bulk-cancel.mixed-outcome-request-returns-per-id-results-in-order',
|
|
52
53
|
'rest-endpoints.md §"POST /v1/runs:bulk-cancel"',
|
|
53
54
|
'top-level operation MUST return 200 when the request reached the host (per-id outcomes carry partial failure)',
|
|
54
55
|
)).toBe(200);
|
|
55
56
|
|
|
56
57
|
const body = res.json as { results: BulkResult[] };
|
|
57
58
|
expect(Array.isArray(body.results)).toBe(true);
|
|
58
|
-
expect(body.results.length, 'results MUST have one entry per request runId').toBe(2);
|
|
59
|
+
expect(body.results.length, req('openwop.it.bulk-cancel.mixed-outcome-request-returns-per-id-results-in-order', 'rest-endpoints.md §"POST /v1/runs:bulk-cancel"', 'results MUST have one entry per request runId')).toBe(2);
|
|
59
60
|
|
|
60
|
-
expect(body.results[0]!.runId, 'results order MUST mirror the request order').toBe(inflightRunId);
|
|
61
|
+
expect(body.results[0]!.runId, req('openwop.it.bulk-cancel.mixed-outcome-request-returns-per-id-results-in-order', 'rest-endpoints.md §"POST /v1/runs:bulk-cancel"', 'results order MUST mirror the request order')).toBe(inflightRunId);
|
|
61
62
|
expect(body.results[0]!.ok).toBe(true);
|
|
62
63
|
expect(['cancelling', 'cancelled']).toContain(body.results[0]!.status);
|
|
63
64
|
|
|
64
65
|
expect(body.results[1]!.runId).toBe('run-does-not-exist-xxxxxxxx');
|
|
65
|
-
expect(body.results[1]!.ok, 'unknown runId entry MUST have ok=false').toBe(false);
|
|
66
|
-
expect(body.results[1]!.error?.code,
|
|
66
|
+
expect(body.results[1]!.ok, req('openwop.it.bulk-cancel.mixed-outcome-request-returns-per-id-results-in-order', 'rest-endpoints.md §"POST /v1/runs:bulk-cancel"', 'unknown runId entry MUST have ok=false')).toBe(false);
|
|
67
|
+
expect(body.results[1]!.error?.code, req('openwop.it.bulk-cancel.mixed-outcome-request-returns-per-id-results-in-order',
|
|
67
68
|
'rest-endpoints.md §"POST /v1/runs:bulk-cancel"',
|
|
68
69
|
'unknown runId outcomes carry `error.code === "not_found"`',
|
|
69
70
|
)).toBe('not_found');
|
|
@@ -71,7 +72,7 @@ describe.skipIf(SKIP)('bulk-cancel: POST /v1/runs:bulk-cancel', () => {
|
|
|
71
72
|
|
|
72
73
|
it('empty runIds array returns 400 validation_error', async () => {
|
|
73
74
|
const res = await driver.post('/v1/runs:bulk-cancel', { runIds: [] });
|
|
74
|
-
expect(res.status).toBe(400);
|
|
75
|
+
expect(res.status, req('openwop.it.bulk-cancel.empty-runids-array-returns-400-validation-error', 'rest-endpoints.md §Open', 'empty runIds array returns 400 validation_error')).toBe(400);
|
|
75
76
|
const body = res.json as { error?: string };
|
|
76
77
|
expect(body.error).toBe('validation_error');
|
|
77
78
|
});
|
|
@@ -83,7 +84,7 @@ describe.skipIf(SKIP)('bulk-cancel: POST /v1/runs:bulk-cancel', () => {
|
|
|
83
84
|
expect(res.status).toBe(400);
|
|
84
85
|
const body = res.json as { error?: string; details?: { maxRunIds?: number } };
|
|
85
86
|
expect(body.error).toBe('validation_error');
|
|
86
|
-
expect(typeof body.details?.maxRunIds,
|
|
87
|
+
expect(typeof body.details?.maxRunIds, req('openwop.it.bulk-cancel.oversized-runids-array-returns-400-with-details-maxrunids',
|
|
87
88
|
'rest-endpoints.md §"POST /v1/runs:bulk-cancel"',
|
|
88
89
|
'over-cap request MUST carry details.maxRunIds disclosing the configured ceiling',
|
|
89
90
|
)).toBe('number');
|
|
@@ -103,7 +104,7 @@ describe.skipIf(SKIP)('bulk-cancel: POST /v1/runs:bulk-cancel', () => {
|
|
|
103
104
|
const second = await driver.post('/v1/runs:bulk-cancel', { runIds: [runId] });
|
|
104
105
|
expect(second.status).toBe(200);
|
|
105
106
|
const body = second.json as { results: BulkResult[] };
|
|
106
|
-
expect(body.results[0]!.ok,
|
|
107
|
+
expect(body.results[0]!.ok, req('openwop.it.bulk-cancel.re-bulk-cancel-after-first-cancel-is-idempotent',
|
|
107
108
|
'rest-endpoints.md §"POST /v1/runs:bulk-cancel" §Idempotency',
|
|
108
109
|
're-cancelling an already-cancelling/cancelled run MUST be ok: true (idempotent)',
|
|
109
110
|
)).toBe(true);
|
|
@@ -30,9 +30,8 @@ import Ajv2020 from 'ajv/dist/2020.js';
|
|
|
30
30
|
import addFormats from 'ajv-formats';
|
|
31
31
|
import { driver } from '../lib/driver.js';
|
|
32
32
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
33
|
+
import { req } from '../lib/requirement-ids.js';
|
|
34
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
36
35
|
|
|
37
36
|
interface AuthModeCapabilities {
|
|
38
37
|
aiProviders?: {
|
|
@@ -70,9 +69,9 @@ describe('byok-auth-modes: schema shape (RFC 0067, server-free)', () => {
|
|
|
70
69
|
| undefined;
|
|
71
70
|
expect(
|
|
72
71
|
authModes,
|
|
73
|
-
|
|
72
|
+
req('openwop.it.byok-auth-modes.the-capabilities-schema-declares-aiproviders-authmodes-with-the-five-mode-enum', 'capabilities.md §aiProviders.authModes', 'the schema MUST declare aiProviders.authModes'),
|
|
74
73
|
).toBeDefined();
|
|
75
|
-
expect(authModes?.additionalProperties?.items?.enum).toEqual([
|
|
74
|
+
expect(authModes?.additionalProperties?.items?.enum, req('openwop.it.byok-auth-modes.the-capabilities-schema-declares-aiproviders-authmodes-with-the-five-mode-enum', 'RFC 0067', 'the capabilities schema declares aiProviders.authModes with the five-mode enum')).toEqual([
|
|
76
75
|
'apiKey',
|
|
77
76
|
'oauth-pkce',
|
|
78
77
|
'oauth-device',
|
|
@@ -85,10 +84,10 @@ describe('byok-auth-modes: schema shape (RFC 0067, server-free)', () => {
|
|
|
85
84
|
const validate = authModesValidator();
|
|
86
85
|
expect(
|
|
87
86
|
validate({ anthropic: ['apiKey'], vertex: ['oauth-pkce'], ollama: ['none'] }),
|
|
88
|
-
|
|
87
|
+
req('openwop.it.byok-auth-modes.a-conforming-authmodes-map-validates-malformed-maps-are-rejected', 'RFC 0067 §A', 'a conforming authModes map MUST validate'),
|
|
89
88
|
).toBe(true);
|
|
90
89
|
// Negative: empty array fails minItems.
|
|
91
|
-
expect(validate({ anthropic: [] })).toBe(false);
|
|
90
|
+
expect(validate({ anthropic: [] }), req('openwop.it.byok-auth-modes.a-conforming-authmodes-map-validates-malformed-maps-are-rejected', 'RFC 0067', 'a conforming authModes map validates; malformed maps are rejected')).toBe(false);
|
|
92
91
|
// Negative: unknown mode (`device` — canonical is `oauth-device`) fails the enum.
|
|
93
92
|
expect(validate({ anthropic: ['device'] })).toBe(false);
|
|
94
93
|
});
|
|
@@ -97,10 +96,10 @@ describe('byok-auth-modes: schema shape (RFC 0067, server-free)', () => {
|
|
|
97
96
|
describe('byok-auth-modes: cross-field consistency (gated on advertisement)', () => {
|
|
98
97
|
it('a host advertising authModes MUST satisfy the §B contract', async () => {
|
|
99
98
|
const res = await driver.get('/.well-known/openwop', { authenticated: false });
|
|
100
|
-
if (res.status !== 200) return; // discovery unavailable — skip cleanly
|
|
99
|
+
if (res.status !== 200) return softSkip('blocked', 'precondition not met — `res.status !== 200` returned early (discovery unavailable — skip cleanly) (seam, prior step, or fixture unavailable)'); // discovery unavailable — skip cleanly
|
|
101
100
|
const caps = res.json as AuthModeCapabilities;
|
|
102
101
|
const authModes = caps.aiProviders?.authModes;
|
|
103
|
-
if (!authModes) return; // host does not advertise authModes — gated skip
|
|
102
|
+
if (!authModes) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!authModes` returned early (host does not advertise authModes — gated skip)'); // host does not advertise authModes — gated skip
|
|
104
103
|
|
|
105
104
|
const supported = new Set(caps.aiProviders?.supported ?? []);
|
|
106
105
|
const byok = new Set(caps.aiProviders?.byok ?? []);
|
|
@@ -110,14 +109,14 @@ describe('byok-auth-modes: cross-field consistency (gated on advertisement)', ()
|
|
|
110
109
|
// §B.1 — every key is in `supported`.
|
|
111
110
|
expect(
|
|
112
111
|
supported.has(provider),
|
|
113
|
-
|
|
112
|
+
req('openwop.it.byok-auth-modes.a-host-advertising-authmodes-must-satisfy-the-b-contract', 'RFC 0067 §B.1', `authModes key '${provider}' MUST appear in aiProviders.supported`),
|
|
114
113
|
).toBe(true);
|
|
115
114
|
|
|
116
115
|
// §B.2 — an `apiKey` provider is in `byok`.
|
|
117
116
|
if (modes.includes('apiKey')) {
|
|
118
117
|
expect(
|
|
119
118
|
byok.has(provider),
|
|
120
|
-
|
|
119
|
+
req('openwop.it.byok-auth-modes.a-host-advertising-authmodes-must-satisfy-the-b-contract', 'RFC 0067 §B.2', `provider '${provider}' with apiKey MUST appear in aiProviders.byok`),
|
|
121
120
|
).toBe(true);
|
|
122
121
|
}
|
|
123
122
|
|
|
@@ -125,7 +124,7 @@ describe('byok-auth-modes: cross-field consistency (gated on advertisement)', ()
|
|
|
125
124
|
if (modes.includes('subscription')) {
|
|
126
125
|
expect(
|
|
127
126
|
byok.has(provider),
|
|
128
|
-
|
|
127
|
+
req('openwop.it.byok-auth-modes.a-host-advertising-authmodes-must-satisfy-the-b-contract', 'RFC 0121 §B.7', `provider '${provider}' with subscription MUST appear in aiProviders.byok`),
|
|
129
128
|
).toBe(true);
|
|
130
129
|
}
|
|
131
130
|
|
|
@@ -133,7 +132,7 @@ describe('byok-auth-modes: cross-field consistency (gated on advertisement)', ()
|
|
|
133
132
|
if (modes.length === 1 && modes[0] === 'none') {
|
|
134
133
|
expect(
|
|
135
134
|
byok.has(provider),
|
|
136
|
-
|
|
135
|
+
req('openwop.it.byok-auth-modes.a-host-advertising-authmodes-must-satisfy-the-b-contract', 'RFC 0067 §B.3', `provider '${provider}' with modes ["none"] MUST NOT appear in aiProviders.byok`),
|
|
137
136
|
).toBe(false);
|
|
138
137
|
}
|
|
139
138
|
|
|
@@ -142,7 +141,7 @@ describe('byok-auth-modes: cross-field consistency (gated on advertisement)', ()
|
|
|
142
141
|
if ((modes.includes('oauth-pkce') || modes.includes('oauth-device')) && oauthIds.size > 0) {
|
|
143
142
|
expect(
|
|
144
143
|
oauthIds.has(provider),
|
|
145
|
-
|
|
144
|
+
req('openwop.it.byok-auth-modes.a-host-advertising-authmodes-must-satisfy-the-b-contract', 'RFC 0067 §B.4', `oauth provider '${provider}' SHOULD have a matching capabilities.oauth.providers[].id`),
|
|
146
145
|
).toBe(true);
|
|
147
146
|
}
|
|
148
147
|
}
|
|
@@ -26,6 +26,8 @@ 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';
|
|
30
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
29
31
|
|
|
30
32
|
/** SHA-256 hex regex — 64 lowercase hex chars exactly. */
|
|
31
33
|
const SHA256_HEX_RE = /^[0-9a-f]{64}$/;
|
|
@@ -42,17 +44,17 @@ describe.skipIf(SKIP_NO_FIXTURE)('byok: end-to-end credentialRef resolution roun
|
|
|
42
44
|
// Fixture absent OR canary not provisioned — host doesn't opt in.
|
|
43
45
|
// Scenario passes trivially.
|
|
44
46
|
if (create.status === 404 || create.status === 422) {
|
|
45
|
-
return;
|
|
47
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `create.status === 404 || create.status === 422` returned early (Fixture absent OR canary not provisioned — host doesn\'t opt in. Scenario passes trivially.)');
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
expect(create.status,
|
|
50
|
+
expect(create.status, req('openwop.it.byok-roundtrip.the-canary-fixture-run-must-resolve-a-host-provisioned-secret-and-emit-sha-256-h',
|
|
49
51
|
'rest-endpoints.md POST /v1/runs',
|
|
50
52
|
'starting openwop-smoke-byok-roundtrip MUST succeed when OPENWOP_CONFORMANCE_FIXTURES=1 is advertised AND openwop-conformance-canary-secret is provisioned',
|
|
51
53
|
)).toBe(201);
|
|
52
54
|
const runId = (create.json as { runId: string }).runId;
|
|
53
55
|
|
|
54
56
|
const terminal = await pollUntilTerminal(runId);
|
|
55
|
-
expect(terminal.status,
|
|
57
|
+
expect(terminal.status, req('openwop.it.byok-roundtrip.the-canary-fixture-run-must-resolve-a-host-provisioned-secret-and-emit-sha-256-h',
|
|
56
58
|
'auth.md §"Secret resolution"',
|
|
57
59
|
'BYOK fixture run MUST reach terminal completed when canary is provisioned',
|
|
58
60
|
)).toBe('completed');
|
|
@@ -79,17 +81,17 @@ describe.skipIf(SKIP_NO_FIXTURE)('byok: end-to-end credentialRef resolution roun
|
|
|
79
81
|
// Host doesn't expose node outputs in variables/outputs map —
|
|
80
82
|
// some hosts only expose them on the events stream. Skip the
|
|
81
83
|
// shape check; the run-completed assertion above is sufficient.
|
|
82
|
-
return;
|
|
84
|
+
return softSkip('blocked', 'precondition not met — `!candidate || typeof candidate !== \'object\'` returned early (Host doesn\'t expose node outputs in variables/outputs map — some hosts only expose them on the events stream. Skip the shape check; …');
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
if ('secretSha256' in candidate && typeof candidate.secretSha256 === 'string') {
|
|
86
|
-
expect(candidate.secretSha256,
|
|
88
|
+
expect(candidate.secretSha256, req('openwop.it.byok-roundtrip.the-canary-fixture-run-must-resolve-a-host-provisioned-secret-and-emit-sha-256-h',
|
|
87
89
|
'auth.md §"Secret resolution"',
|
|
88
90
|
'fixture-emitted SHA-256 hex MUST be 64 lowercase hex chars',
|
|
89
91
|
)).toMatch(SHA256_HEX_RE);
|
|
90
92
|
}
|
|
91
93
|
if ('secretLength' in candidate && typeof candidate.secretLength === 'number') {
|
|
92
|
-
expect(candidate.secretLength,
|
|
94
|
+
expect(candidate.secretLength, req('openwop.it.byok-roundtrip.the-canary-fixture-run-must-resolve-a-host-provisioned-secret-and-emit-sha-256-h',
|
|
93
95
|
'auth.md §"Secret resolution"',
|
|
94
96
|
'resolved canary length MUST be > 0 (non-empty)',
|
|
95
97
|
)).toBeGreaterThan(0);
|
|
@@ -101,7 +103,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('byok: end-to-end credentialRef resolution roun
|
|
|
101
103
|
workflowId: 'openwop-smoke-byok-roundtrip',
|
|
102
104
|
});
|
|
103
105
|
if (create.status === 404 || create.status === 422) {
|
|
104
|
-
return;
|
|
106
|
+
return softSkip('blocked', 'precondition not met — `create.status === 404 || create.status === 422` returned early (seam, prior step, or fixture unavailable)');
|
|
105
107
|
}
|
|
106
108
|
expect(create.status).toBe(201);
|
|
107
109
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -116,7 +118,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('byok: end-to-end credentialRef resolution roun
|
|
|
116
118
|
const completed = events.filter(
|
|
117
119
|
(e) => e.type === 'node.completed' && e.nodeId === 'resolve-secret',
|
|
118
120
|
);
|
|
119
|
-
expect(completed.length,
|
|
121
|
+
expect(completed.length, req('openwop.it.byok-roundtrip.byok-fixture-run-must-emit-a-node-completed-event-for-the-resolve-step',
|
|
120
122
|
'event-log.md §node.completed',
|
|
121
123
|
'BYOK fixture node MUST emit exactly one node.completed event when secrets.resolve succeeds',
|
|
122
124
|
)).toBe(1);
|
|
@@ -127,7 +129,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('byok: end-to-end credentialRef resolution roun
|
|
|
127
129
|
workflowId: 'openwop-smoke-byok-roundtrip',
|
|
128
130
|
});
|
|
129
131
|
if (create.status === 404 || create.status === 422) {
|
|
130
|
-
return;
|
|
132
|
+
return softSkip('blocked', 'precondition not met — `create.status === 404 || create.status === 422` returned early (seam, prior step, or fixture unavailable)');
|
|
131
133
|
}
|
|
132
134
|
expect(create.status).toBe(201);
|
|
133
135
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -157,7 +159,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('byok: end-to-end credentialRef resolution roun
|
|
|
157
159
|
/"raw_secret"\s*:\s*"[^"]{8,}"/,
|
|
158
160
|
];
|
|
159
161
|
for (const pat of suspiciousPatterns) {
|
|
160
|
-
expect(dump,
|
|
162
|
+
expect(dump, req('openwop.it.byok-roundtrip.byok-fixture-run-event-log-must-not-echo-the-resolved-secret-value-redaction',
|
|
161
163
|
'observability.md §"Redaction"',
|
|
162
164
|
`event log MUST NOT contain a payload matching ${pat} — secret.echo fixture only emits hash + length`,
|
|
163
165
|
)).not.toMatch(pat);
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
import { describe, it, expect } from 'vitest';
|
|
11
11
|
import { driver } from '../lib/driver.js';
|
|
12
12
|
import { discoveryFamilies } from '../lib/discovery-capabilities.js';
|
|
13
|
+
import { req } from '../lib/requirement-ids.js';
|
|
14
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
13
15
|
|
|
14
16
|
interface DiscoveryDoc {
|
|
15
17
|
capabilities?: Record<string, unknown>;
|
|
@@ -30,10 +32,10 @@ async function call(tenantId: string, op: string, args: Record<string, unknown>)
|
|
|
30
32
|
describe('cache-cross-tenant-isolation: advertisement shape (RFC 0019)', () => {
|
|
31
33
|
it('capabilities.cache is either absent or a well-formed object', async () => {
|
|
32
34
|
const cap = await readCap();
|
|
33
|
-
if (cap === null) return;
|
|
35
|
+
if (cap === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap === null` returned early');
|
|
34
36
|
expect(
|
|
35
37
|
typeof cap.supported,
|
|
36
|
-
|
|
38
|
+
req('openwop.it.cache-cross-tenant-isolation.capabilities-cache-is-either-absent-or-a-well-formed-object',
|
|
37
39
|
'capabilities.schema.json §cache',
|
|
38
40
|
'capabilities.cache.supported MUST be a boolean when present',
|
|
39
41
|
),
|
|
@@ -44,19 +46,19 @@ describe('cache-cross-tenant-isolation: advertisement shape (RFC 0019)', () => {
|
|
|
44
46
|
describe('cache-cross-tenant-isolation: behavioral (RFC 0019 §B point 2)', () => {
|
|
45
47
|
it('put under tenant A → get under tenant B returns miss', async () => {
|
|
46
48
|
const cap = await readCap();
|
|
47
|
-
if (!cap || cap.supported !== true) return;
|
|
49
|
+
if (!cap || cap.supported !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!cap || cap.supported !== true` returned early');
|
|
48
50
|
const key = `xtenant-cache-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
49
51
|
|
|
50
52
|
const putRes = await call('tenant-a', 'put', { key, value: 'from-A', ttlSeconds: 60 });
|
|
51
|
-
if (putRes.status === 404) return;
|
|
52
|
-
expect(putRes.status, 'put MUST succeed').toBe(200);
|
|
53
|
+
if (putRes.status === 404) return softSkip('blocked', 'precondition not met — `putRes.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
54
|
+
expect(putRes.status, req('openwop.it.cache-cross-tenant-isolation.put-under-tenant-a-get-under-tenant-b-returns-miss', 'RFC 0019 §B point 2', 'put MUST succeed')).toBe(200);
|
|
53
55
|
|
|
54
56
|
const getRes = await call('tenant-b', 'get', { key });
|
|
55
57
|
expect(getRes.status).toBe(200);
|
|
56
58
|
const body = getRes.json as { hit?: boolean };
|
|
57
59
|
expect(
|
|
58
60
|
body.hit,
|
|
59
|
-
|
|
61
|
+
req('openwop.it.cache-cross-tenant-isolation.put-under-tenant-a-get-under-tenant-b-returns-miss', 'RFC 0019 §B point 2', 'tenant B MUST NOT hit tenant A cache entry at same key'),
|
|
60
62
|
).toBe(false);
|
|
61
63
|
});
|
|
62
64
|
});
|
|
@@ -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('cache-ttl-expiry: advertisement shape (RFC 0019)', () => {
|
|
33
35
|
it('capabilities.cache 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.cache-ttl-expiry.capabilities-cache-is-either-absent-or-a-well-formed-object',
|
|
39
41
|
'capabilities.schema.json §cache',
|
|
40
42
|
'capabilities.cache.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('cache-ttl-expiry: behavioral (RFC 0019 §B point 2 — 1s TTL drift)', () => {
|
|
51
53
|
it('put with ttlSeconds=2 → hit within window; miss after expiry', async () => {
|
|
52
54
|
const probe = await call('get', { key: '__cache-probe__' });
|
|
53
|
-
if (probe.status === 404) return;
|
|
55
|
+
if (probe.status === 404) return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
54
56
|
const key = `c-ttl-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
55
57
|
const putRes = await call('put', { key, value: 'evicts-soon', ttlSeconds: 2 });
|
|
56
58
|
expect(putRes.status).toBe(200);
|
|
@@ -59,7 +61,7 @@ describe('cache-ttl-expiry: behavioral (RFC 0019 §B point 2 — 1s TTL drift)',
|
|
|
59
61
|
const withinBody = within.json as { value?: unknown; found?: boolean };
|
|
60
62
|
expect(
|
|
61
63
|
withinBody.value,
|
|
62
|
-
|
|
64
|
+
req('openwop.it.cache-ttl-expiry.put-with-ttlseconds-2-hit-within-window-miss-after-expiry', 'RFC 0019 §B point 2', 'cache get within TTL MUST return the stored value'),
|
|
63
65
|
).toBe('evicts-soon');
|
|
64
66
|
|
|
65
67
|
await new Promise((r) => setTimeout(r, 3000));
|
|
@@ -68,7 +70,7 @@ describe('cache-ttl-expiry: behavioral (RFC 0019 §B point 2 — 1s TTL drift)',
|
|
|
68
70
|
const afterBody = after.json as { value?: unknown; found?: boolean };
|
|
69
71
|
expect(
|
|
70
72
|
afterBody.found,
|
|
71
|
-
|
|
73
|
+
req('openwop.it.cache-ttl-expiry.put-with-ttlseconds-2-hit-within-window-miss-after-expiry', 'RFC 0019 §B point 2', 'cache get after TTL expiry MUST surface as found:false (≤1s drift)'),
|
|
72
74
|
).toBe(false);
|
|
73
75
|
});
|
|
74
76
|
});
|
|
@@ -22,6 +22,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
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 ALL_MODALITIES = ['text', 'image', 'audio', 'document'];
|
|
27
29
|
|
|
@@ -60,10 +62,10 @@ describe('callai-multimodal (RFC 0091 §A/§B)', () => {
|
|
|
60
62
|
const okRes = await driver.post(SEAM, {
|
|
61
63
|
messages: [{ role: 'user', content: [{ type: 'text', text: 'describe this' }, okPart] }],
|
|
62
64
|
});
|
|
63
|
-
if (okRes.status === 404) return; // seam unwired — soft-skip the whole behavioral suite
|
|
65
|
+
if (okRes.status === 404) return softSkip('blocked', 'precondition not met — `okRes.status === 404` returned early (seam unwired — soft-skip the whole behavioral suite) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip the whole behavioral suite
|
|
64
66
|
expect(
|
|
65
67
|
errCode(okRes.json) !== 'unsupported_modality',
|
|
66
|
-
|
|
68
|
+
req('openwop.it.callai-multimodal.accepts-an-advertised-non-text-modality-and-rejects-an-unadvertised-one-with-uns', 'RFC 0091 §B', `an advertised modality (${accepted}) MUST NOT be rejected as unsupported_modality`),
|
|
67
69
|
).toBe(true);
|
|
68
70
|
|
|
69
71
|
// 2) An unadvertised modality MUST be rejected with unsupported_modality.
|
|
@@ -79,7 +81,7 @@ describe('callai-multimodal (RFC 0091 §A/§B)', () => {
|
|
|
79
81
|
});
|
|
80
82
|
expect(
|
|
81
83
|
errCode(badRes.json) === 'unsupported_modality',
|
|
82
|
-
|
|
84
|
+
req('openwop.it.callai-multimodal.accepts-an-advertised-non-text-modality-and-rejects-an-unadvertised-one-with-uns', 'RFC 0091 §A', `an unadvertised modality (${unadvertised}) MUST be rejected with unsupported_modality (never silently dropped)`),
|
|
83
85
|
).toBe(true);
|
|
84
86
|
}
|
|
85
87
|
});
|
|
@@ -11,6 +11,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
11
11
|
import { driver } from '../lib/driver.js';
|
|
12
12
|
import { pollUntilStatus } from '../lib/polling.js';
|
|
13
13
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
14
|
+
import { req } from '../lib/requirement-ids.js';
|
|
14
15
|
|
|
15
16
|
const WORKFLOW_ID = 'conformance-cancellable';
|
|
16
17
|
const SKIP_NO_FIXTURE = !isFixtureAdvertised(WORKFLOW_ID);
|
|
@@ -21,7 +22,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('cancellation: in-flight :cancel reaches termin
|
|
|
21
22
|
workflowId: WORKFLOW_ID,
|
|
22
23
|
inputs: { delayMs: 10_000 },
|
|
23
24
|
});
|
|
24
|
-
expect(create.status,
|
|
25
|
+
expect(create.status, req('openwop.it.cancellation.post-v1-runs-runid-cancel-returns-200-and-run-terminates-as-cancelled',
|
|
25
26
|
'rest-endpoints.md',
|
|
26
27
|
'POST /v1/runs MUST return 201 on accepted run',
|
|
27
28
|
)).toBe(201);
|
|
@@ -35,7 +36,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('cancellation: in-flight :cancel reaches termin
|
|
|
35
36
|
`/v1/runs/${encodeURIComponent(runId)}/cancel`,
|
|
36
37
|
{ reason: 'conformance-cancellation-test' },
|
|
37
38
|
);
|
|
38
|
-
expect(cancel.status,
|
|
39
|
+
expect(cancel.status, req('openwop.it.cancellation.post-v1-runs-runid-cancel-returns-200-and-run-terminates-as-cancelled',
|
|
39
40
|
'rest-endpoints.md POST /v1/runs/{runId}/cancel',
|
|
40
41
|
'cancel MUST return 200 on accepted cancellation',
|
|
41
42
|
)).toBe(200);
|
|
@@ -43,14 +44,14 @@ describe.skipIf(SKIP_NO_FIXTURE)('cancellation: in-flight :cancel reaches termin
|
|
|
43
44
|
const cancelBody = cancel.json as { status?: string };
|
|
44
45
|
expect(
|
|
45
46
|
['cancelled', 'cancelling'].includes(cancelBody.status ?? ''),
|
|
46
|
-
|
|
47
|
+
req('openwop.it.cancellation.post-v1-runs-runid-cancel-returns-200-and-run-terminates-as-cancelled',
|
|
47
48
|
'rest-endpoints.md POST /v1/runs/{runId}/cancel',
|
|
48
49
|
'cancel response status MUST be one of `cancelled` or `cancelling`',
|
|
49
50
|
),
|
|
50
51
|
).toBe(true);
|
|
51
52
|
|
|
52
53
|
const terminal = await pollUntilStatus(runId, 'cancelled', { timeoutMs: 5_000 });
|
|
53
|
-
expect(terminal.status,
|
|
54
|
+
expect(terminal.status, req('openwop.it.cancellation.post-v1-runs-runid-cancel-returns-200-and-run-terminates-as-cancelled',
|
|
54
55
|
'fixtures.md conformance-cancellable §Terminal status',
|
|
55
56
|
'fixture MUST reach terminal `cancelled` within 5s of cancel',
|
|
56
57
|
)).toBe('cancelled');
|
|
@@ -62,7 +63,7 @@ describe('cancellation: cancelling an unknown run returns 404', () => {
|
|
|
62
63
|
const res = await driver.post('/v1/runs/openwop-conformance-no-such-run/cancel', {});
|
|
63
64
|
expect(
|
|
64
65
|
[403, 404].includes(res.status),
|
|
65
|
-
|
|
66
|
+
req('openwop.it.cancellation.post-v1-runs-nonexistentid-cancel-returns-404', 'rest-endpoints.md', 'cancel on unknown run MUST return 404 or 403'),
|
|
66
67
|
).toBe(true);
|
|
67
68
|
});
|
|
68
69
|
});
|