@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
|
@@ -32,11 +32,9 @@ 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
|
-
import { driver } from '../lib/driver.js';
|
|
36
35
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
37
36
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
38
|
-
|
|
39
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
37
|
+
import { req } from '../lib/requirement-ids.js';
|
|
40
38
|
function loadSchema(name: string): Record<string, unknown> {
|
|
41
39
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
|
|
42
40
|
}
|
|
@@ -90,12 +88,12 @@ describe('localized-content: capability advertisement shape (localized-content.m
|
|
|
90
88
|
it('capabilities schema declares content with its required fields', () => {
|
|
91
89
|
const caps = loadSchema('capabilities.schema.json');
|
|
92
90
|
const content = (caps.properties as Record<string, { required?: string[]; properties?: Record<string, unknown> }>).content;
|
|
93
|
-
expect(content,
|
|
94
|
-
expect(content?.required,
|
|
91
|
+
expect(content, req('openwop.it.localized-content-delivery.capabilities-schema-declares-content-with-its-required-fields', 'capabilities.schema.json §content', 'the content block MUST be declared')).toBeDefined();
|
|
92
|
+
expect(content?.required, req('openwop.it.localized-content-delivery.capabilities-schema-declares-content-with-its-required-fields', 'localized-content.md §A', 'supported + baseLocale + supportedLocales MUST be required')).toEqual(
|
|
95
93
|
expect.arrayContaining(['supported', 'baseLocale', 'supportedLocales']),
|
|
96
94
|
);
|
|
97
95
|
for (const f of ['supported', 'baseLocale', 'supportedLocales']) {
|
|
98
|
-
expect(content?.properties?.[f],
|
|
96
|
+
expect(content?.properties?.[f], req('openwop.it.localized-content-delivery.capabilities-schema-declares-content-with-its-required-fields', 'localized-content.md §A', `content.${f} MUST be declared`)).toBeDefined();
|
|
99
97
|
}
|
|
100
98
|
});
|
|
101
99
|
});
|
|
@@ -119,29 +117,29 @@ describe('localized-content: schema shapes (localized-content.md §B, server-fre
|
|
|
119
117
|
};
|
|
120
118
|
|
|
121
119
|
it('a conforming section validates', () => {
|
|
122
|
-
expect(section(goodSection),
|
|
120
|
+
expect(section(goodSection), req('openwop.it.localized-content-delivery.a-conforming-section-validates', 'RFC 0103 §B', `a conforming section MUST validate. Errors: ${JSON.stringify(section.errors)}`)).toBe(true);
|
|
123
121
|
});
|
|
124
122
|
it('a localizations key with wrong case/underscore is rejected', () => {
|
|
125
|
-
expect(section({ ...goodSection, localizations: { EN: { heading: 'x' } } }),
|
|
126
|
-
expect(section({ ...goodSection, localizations: { en_US: { heading: 'x' } } }),
|
|
123
|
+
expect(section({ ...goodSection, localizations: { EN: { heading: 'x' } } }), req('openwop.it.localized-content-delivery.a-localizations-key-with-wrong-case-underscore-is-rejected', 'RFC 0103 §B', 'a non-BCP-47-subset key MUST be rejected')).toBe(false);
|
|
124
|
+
expect(section({ ...goodSection, localizations: { en_US: { heading: 'x' } } }), req('openwop.it.localized-content-delivery.a-localizations-key-with-wrong-case-underscore-is-rejected', 'RFC 0103 §B', 'an underscore locale key MUST be rejected')).toBe(false);
|
|
127
125
|
});
|
|
128
126
|
it('a section missing a required field is rejected', () => {
|
|
129
127
|
const { status: _omit, ...noStatus } = goodSection;
|
|
130
|
-
expect(section(noStatus),
|
|
128
|
+
expect(section(noStatus), req('openwop.it.localized-content-delivery.a-section-missing-a-required-field-is-rejected', 'RFC 0103 §B', 'status is REQUIRED')).toBe(false);
|
|
131
129
|
});
|
|
132
130
|
it('a status outside the enum is rejected', () => {
|
|
133
|
-
expect(section({ ...goodSection, status: 'archived' }),
|
|
131
|
+
expect(section({ ...goodSection, status: 'archived' }), req('openwop.it.localized-content-delivery.a-status-outside-the-enum-is-rejected', 'RFC 0103 §B', 'status MUST be draft|published')).toBe(false);
|
|
134
132
|
});
|
|
135
133
|
|
|
136
134
|
it('a conforming page validates; a bad slug is rejected', () => {
|
|
137
135
|
const goodPage = { pageId: 'home', slug: 'home', name: 'Home', status: 'published', sectionOrder: ['hero'] };
|
|
138
|
-
expect(page(goodPage),
|
|
139
|
-
expect(page({ ...goodPage, slug: 'Home Page' }),
|
|
136
|
+
expect(page(goodPage), req('openwop.it.localized-content-delivery.a-conforming-page-validates-a-bad-slug-is-rejected', 'RFC 0103 §B', `a conforming page MUST validate. Errors: ${JSON.stringify(page.errors)}`)).toBe(true);
|
|
137
|
+
expect(page({ ...goodPage, slug: 'Home Page' }), req('openwop.it.localized-content-delivery.a-conforming-page-validates-a-bad-slug-is-rejected', 'RFC 0103 §B', 'slug MUST match ^[a-z][a-z0-9-]*$')).toBe(false);
|
|
140
138
|
});
|
|
141
139
|
|
|
142
140
|
it('a conforming language-settings validates', () => {
|
|
143
141
|
const good = { baseLocale: 'en', supportedLocales: ['es', 'pt-BR', 'fr'], autoTranslateOnPublish: false };
|
|
144
|
-
expect(settings(good),
|
|
142
|
+
expect(settings(good), req('openwop.it.localized-content-delivery.a-conforming-language-settings-validates', 'RFC 0103 §B', `settings MUST validate. Errors: ${JSON.stringify(settings.errors)}`)).toBe(true);
|
|
145
143
|
});
|
|
146
144
|
|
|
147
145
|
it('a conforming page-response validates', () => {
|
|
@@ -153,7 +151,7 @@ describe('localized-content: schema shapes (localized-content.md §B, server-fre
|
|
|
153
151
|
page: { pageId: 'home', slug: 'home', name: 'Home' },
|
|
154
152
|
sections: [{ sectionId: 'hero', sectionType: 'hero', data: { heading: 'Bem-vindo', cta: 'Get started' } }],
|
|
155
153
|
};
|
|
156
|
-
expect(response(good),
|
|
154
|
+
expect(response(good), req('openwop.it.localized-content-delivery.a-conforming-page-response-validates', 'RFC 0103 §D', `a resolved response MUST validate. Errors: ${JSON.stringify(response.errors)}`)).toBe(true);
|
|
157
155
|
});
|
|
158
156
|
});
|
|
159
157
|
|
|
@@ -164,43 +162,43 @@ describe('localized-content: per-section field merge (localized-content.md §C,
|
|
|
164
162
|
};
|
|
165
163
|
|
|
166
164
|
it('exact-locale hit overrides matching fields', () => {
|
|
167
|
-
expect(resolveSection(section, 'es', 'en'),
|
|
165
|
+
expect(resolveSection(section, 'es', 'en'), req('openwop.it.localized-content-delivery.exact-locale-hit-overrides-matching-fields', 'RFC 0103 §C', 'exact hit MUST overlay locale fields onto data')).toEqual({
|
|
168
166
|
heading: 'Bienvenido',
|
|
169
167
|
cta: 'Empezar',
|
|
170
168
|
});
|
|
171
169
|
});
|
|
172
170
|
it('partial translation falls through to base for missing fields', () => {
|
|
173
|
-
expect(resolveSection(section, 'pt-BR', 'en'),
|
|
171
|
+
expect(resolveSection(section, 'pt-BR', 'en'), req('openwop.it.localized-content-delivery.partial-translation-falls-through-to-base-for-missing-fields', 'RFC 0103 §C', 'missing locale fields MUST fall through to data')).toEqual({
|
|
174
172
|
heading: 'Bem-vindo',
|
|
175
173
|
cta: 'Get started',
|
|
176
174
|
});
|
|
177
175
|
});
|
|
178
176
|
it('language-family fallback applies when exact tag is absent', () => {
|
|
179
177
|
const s: Section = { data: { h: 'Hi' }, localizations: { pt: { h: 'Oi' } } };
|
|
180
|
-
expect(resolveSection(s, 'pt-BR', 'en'),
|
|
178
|
+
expect(resolveSection(s, 'pt-BR', 'en'), req('openwop.it.localized-content-delivery.language-family-fallback-applies-when-exact-tag-is-absent', 'RFC 0103 §C', 'pt-BR MUST fall back to the pt family override')).toEqual({ h: 'Oi' });
|
|
181
179
|
});
|
|
182
180
|
it('unsupported/base locale returns base data unchanged', () => {
|
|
183
|
-
expect(resolveSection(section, 'de', 'en'),
|
|
184
|
-
expect(resolveSection(section, 'en', 'en'),
|
|
181
|
+
expect(resolveSection(section, 'de', 'en'), req('openwop.it.localized-content-delivery.unsupported-base-locale-returns-base-data-unchanged', 'RFC 0103 §C', 'no match MUST return base data')).toEqual(section.data);
|
|
182
|
+
expect(resolveSection(section, 'en', 'en'), req('openwop.it.localized-content-delivery.unsupported-base-locale-returns-base-data-unchanged', 'RFC 0103 §C', 'base locale MUST return base data')).toEqual(section.data);
|
|
185
183
|
});
|
|
186
184
|
});
|
|
187
185
|
|
|
188
186
|
describe('localized-content: §A capability coherence predicate (server-free)', () => {
|
|
189
187
|
const i18n: I18nCap = { supported: true, defaultLocale: 'en', supportedLocales: ['en', 'es', 'pt-BR', 'fr'] };
|
|
190
188
|
it('a coherent advertisement passes', () => {
|
|
191
|
-
expect(contentCoherent({ supported: true, baseLocale: 'en', supportedLocales: ['es', 'pt-BR', 'fr'] }, i18n),
|
|
189
|
+
expect(contentCoherent({ supported: true, baseLocale: 'en', supportedLocales: ['es', 'pt-BR', 'fr'] }, i18n), req('openwop.it.localized-content-delivery.a-coherent-advertisement-passes', 'RFC 0103 §A', 'a coherent content block MUST pass')).toBe(true);
|
|
192
190
|
});
|
|
193
191
|
it('content without i18n is incoherent', () => {
|
|
194
|
-
expect(contentCoherent({ supported: true, baseLocale: 'en', supportedLocales: ['es'] }, { supported: false }),
|
|
192
|
+
expect(contentCoherent({ supported: true, baseLocale: 'en', supportedLocales: ['es'] }, { supported: false }), req('openwop.it.localized-content-delivery.content-without-i18n-is-incoherent', 'RFC 0103 §A.1', 'content requires i18n.supported')).toBe(false);
|
|
195
193
|
});
|
|
196
194
|
it('baseLocale != i18n.defaultLocale is incoherent', () => {
|
|
197
|
-
expect(contentCoherent({ supported: true, baseLocale: 'es', supportedLocales: ['fr'] }, i18n),
|
|
195
|
+
expect(contentCoherent({ supported: true, baseLocale: 'es', supportedLocales: ['fr'] }, i18n), req('openwop.it.localized-content-delivery.baselocale-i18n-defaultlocale-is-incoherent', 'RFC 0103 §A.2', 'baseLocale MUST equal i18n.defaultLocale')).toBe(false);
|
|
198
196
|
});
|
|
199
197
|
it('a supportedLocales not ⊆ i18n.supportedLocales is incoherent', () => {
|
|
200
|
-
expect(contentCoherent({ supported: true, baseLocale: 'en', supportedLocales: ['de'] }, i18n),
|
|
198
|
+
expect(contentCoherent({ supported: true, baseLocale: 'en', supportedLocales: ['de'] }, i18n), req('openwop.it.localized-content-delivery.a-supportedlocales-not-i18n-supportedlocales-is-incoherent', 'RFC 0103 §A.3', 'content locales MUST be a subset of i18n locales')).toBe(false);
|
|
201
199
|
});
|
|
202
200
|
it('baseLocale appearing in supportedLocales is incoherent', () => {
|
|
203
|
-
expect(contentCoherent({ supported: true, baseLocale: 'en', supportedLocales: ['en', 'es'] }, i18n),
|
|
201
|
+
expect(contentCoherent({ supported: true, baseLocale: 'en', supportedLocales: ['en', 'es'] }, i18n), req('openwop.it.localized-content-delivery.baselocale-appearing-in-supportedlocales-is-incoherent', 'RFC 0103 §A.4', 'baseLocale MUST NOT appear in supportedLocales')).toBe(false);
|
|
204
202
|
});
|
|
205
203
|
});
|
|
206
204
|
|
|
@@ -215,7 +213,7 @@ describe.skipIf(HTTP_SKIP)('localized-content: live advertisement coherence (loc
|
|
|
215
213
|
const i18n = (await readCapabilityFamily<I18nCap>('i18n')) ?? {};
|
|
216
214
|
expect(
|
|
217
215
|
contentCoherent(content!, i18n),
|
|
218
|
-
|
|
216
|
+
req('openwop.it.localized-content-delivery.advertised-content-block-is-coherent-with-the-advertised-i18n-block', 'localized-content.md §A', 'the advertised content block MUST satisfy the i18n-subset + baseLocale invariants'),
|
|
219
217
|
).toBe(true);
|
|
220
218
|
});
|
|
221
219
|
});
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
|
|
36
36
|
import { describe, it, expect } from 'vitest';
|
|
37
37
|
import { driver } from '../lib/driver.js';
|
|
38
|
+
import { req } from '../lib/requirement-ids.js';
|
|
39
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
38
40
|
|
|
39
41
|
interface RegistryProbe {
|
|
40
42
|
available: boolean;
|
|
@@ -57,7 +59,7 @@ async function probeRegistry(): Promise<RegistryProbe> {
|
|
|
57
59
|
describe('malicious-manifest: pack-name validation per spec/v1/node-packs.md §Naming', () => {
|
|
58
60
|
it('GET /v1/packs/{bad-name}/-/{version}.json returns 400 invalid_pack_name', async () => {
|
|
59
61
|
const probe = await probeRegistry();
|
|
60
|
-
if (!probe.available) return; // host doesn't claim openwop-node-packs
|
|
62
|
+
if (!probe.available) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!probe.available` returned early (host doesn\'t claim openwop-node-packs)'); // host doesn't claim openwop-node-packs
|
|
61
63
|
|
|
62
64
|
// Bad name shapes the registry SHOULD reject:
|
|
63
65
|
// - Reserved scope without authorization (`core.foo`)
|
|
@@ -71,7 +73,7 @@ describe('malicious-manifest: pack-name validation per spec/v1/node-packs.md §N
|
|
|
71
73
|
);
|
|
72
74
|
expect(
|
|
73
75
|
[400, 404].includes(res.status),
|
|
74
|
-
|
|
76
|
+
req('openwop.it.maliciousManifest.get-v1-packs-bad-name-version-json-returns-400-invalid-pack-name',
|
|
75
77
|
'spec/v1/node-packs.md §Registry HTTP API',
|
|
76
78
|
`bad pack name "${badName}" MUST yield 400 (invalid_pack_name) or 404 (treated as unknown)`,
|
|
77
79
|
),
|
|
@@ -83,7 +85,7 @@ describe('malicious-manifest: pack-name validation per spec/v1/node-packs.md §N
|
|
|
83
85
|
describe('malicious-manifest: version validation', () => {
|
|
84
86
|
it('GET /v1/packs/{name}/-/{bad-version}.json returns 400 invalid_version', async () => {
|
|
85
87
|
const probe = await probeRegistry();
|
|
86
|
-
if (!probe.available) return;
|
|
88
|
+
if (!probe.available) return softSkip('blocked', 'precondition not met — `!probe.available` returned early (seam, prior step, or fixture unavailable)');
|
|
87
89
|
|
|
88
90
|
const badVersions = ['not-semver', '1', '1.0.0', 'v1.0'];
|
|
89
91
|
|
|
@@ -93,7 +95,7 @@ describe('malicious-manifest: version validation', () => {
|
|
|
93
95
|
);
|
|
94
96
|
expect(
|
|
95
97
|
[400, 404].includes(res.status),
|
|
96
|
-
|
|
98
|
+
req('openwop.it.maliciousManifest.get-v1-packs-name-bad-version-json-returns-400-invalid-version',
|
|
97
99
|
'spec/v1/node-packs.md §Registry HTTP API',
|
|
98
100
|
`bad version "${bad}" MUST yield 400 (invalid_version) or 404`,
|
|
99
101
|
),
|
|
@@ -105,10 +107,10 @@ describe('malicious-manifest: version validation', () => {
|
|
|
105
107
|
describe('malicious-manifest: signature endpoint contract per openwop/openwop@434c8f2', () => {
|
|
106
108
|
it('GET /v1/packs/{name}/-/{version}.sig of a non-existent pack returns 404 signature_not_available', async () => {
|
|
107
109
|
const probe = await probeRegistry();
|
|
108
|
-
if (!probe.available) return;
|
|
110
|
+
if (!probe.available) return softSkip('blocked', 'precondition not met — `!probe.available` returned early (seam, prior step, or fixture unavailable)');
|
|
109
111
|
|
|
110
112
|
const res = await driver.get('/v1/packs/community.no-such-pack/-/1.0.sig');
|
|
111
|
-
expect(res.status,
|
|
113
|
+
expect(res.status, req('openwop.it.maliciousManifest.get-v1-packs-name-version-sig-of-a-non-existent-pack-returns-404-signature-not-a',
|
|
112
114
|
'spec/v1/node-packs.md §`GET .sig`',
|
|
113
115
|
'missing/yanked/unsigned signature MUST return 404',
|
|
114
116
|
)).toBe(404);
|
|
@@ -120,7 +122,7 @@ describe('malicious-manifest: signature endpoint contract per openwop/openwop@43
|
|
|
120
122
|
// shape; the assertion is permissive on the error code itself
|
|
121
123
|
// but strict on the status.
|
|
122
124
|
if (typeof body.error === 'string') {
|
|
123
|
-
expect(body.error.length,
|
|
125
|
+
expect(body.error.length, req('openwop.it.maliciousManifest.get-v1-packs-name-version-sig-of-a-non-existent-pack-returns-404-signature-not-a',
|
|
124
126
|
'spec/v1/node-packs.md',
|
|
125
127
|
'404 response MUST carry a structured error envelope with a non-empty error code',
|
|
126
128
|
)).toBeGreaterThan(0);
|
|
@@ -146,7 +148,7 @@ describe('malicious-manifest: documented error catalog (per openwop/openwop@434c
|
|
|
146
148
|
'tarball_path_traversal',
|
|
147
149
|
'tarball_tar_parse_failed',
|
|
148
150
|
] as const;
|
|
149
|
-
expect(TARBALL_ERRORS.length,
|
|
151
|
+
expect(TARBALL_ERRORS.length, req('openwop.it.maliciousManifest.lists-are-non-empty-sanity-check-on-doc-drift',
|
|
150
152
|
'spec/v1/node-packs.md',
|
|
151
153
|
'documented tarball-error catalog is non-empty',
|
|
152
154
|
)).toBe(9);
|
|
@@ -30,6 +30,7 @@ import { mcpServerMount } from '../lib/mcp-mount.js';
|
|
|
30
30
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
31
31
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
32
32
|
import { McpFakeServer, MCP_ERR } from '../lib/mcp-fake-server.js';
|
|
33
|
+
import { req } from '../lib/requirement-ids.js';
|
|
33
34
|
|
|
34
35
|
// The first describe drives the in-process McpFakeServer itself; the second (gated) describe
|
|
35
36
|
// talks to the host's own MCP server mount and hands it nothing harness-hosted.
|
|
@@ -58,7 +59,7 @@ describe('RFC 0153 §B — the suite MCP server speaks 2026-07-28 (dual-era McpF
|
|
|
58
59
|
|
|
59
60
|
it('server/discover: resultType complete, supportedVersions, capabilities, cache hints, _meta.serverInfo', async () => {
|
|
60
61
|
const r = await call(server.endpoint(), 'server/discover', {}, { version: '2026-07-28', mcpMethod: 'server/discover' });
|
|
61
|
-
expect(r.error).toBeUndefined();
|
|
62
|
+
expect(r.error, req('openwop.it.mcp-2026-07-28-discover.server-discover-resulttype-complete-supportedversions-capabilities-cache-hints-m', 'RFC 0153 §B', 'server/discover: resultType complete, supportedVersions, capabilities, cache hints, _meta.serverInfo')).toBeUndefined();
|
|
62
63
|
expect(r.result?.['resultType']).toBe('complete');
|
|
63
64
|
expect(r.result?.['supportedVersions']).toEqual(['2026-07-28', '2025-06-18']);
|
|
64
65
|
expect(typeof r.result?.['ttlMs']).toBe('number');
|
|
@@ -68,13 +69,13 @@ describe('RFC 0153 §B — the suite MCP server speaks 2026-07-28 (dual-era McpF
|
|
|
68
69
|
|
|
69
70
|
it('a request without _meta.protocolVersion under the current revision is refused -32020', async () => {
|
|
70
71
|
const r = await call(server.endpoint(), 'tools/list', {}, { version: '2026-07-28', withMeta: false });
|
|
71
|
-
expect(r.status).toBe(400);
|
|
72
|
+
expect(r.status, req('openwop.it.mcp-2026-07-28-discover.a-request-without-meta-protocolversion-under-the-current-revision-is-refused-320', 'RFC 0153 §B', 'a request without _meta.protocolVersion under the current revision is refused -32020')).toBe(400);
|
|
72
73
|
expect(r.error?.code).toBe(MCP_ERR.HEADER_MISMATCH);
|
|
73
74
|
});
|
|
74
75
|
|
|
75
76
|
it('header ≠ body revision ⇒ -32020 HeaderMismatch (400); Mcp-Method ≠ method ⇒ -32020', async () => {
|
|
76
77
|
const r1 = await call(server.endpoint(), 'tools/list', { _meta: { [META_V]: '2025-11-25', [META_C]: {} } }, { version: '2026-07-28', withMeta: false });
|
|
77
|
-
expect(r1.status).toBe(400);
|
|
78
|
+
expect(r1.status, req('openwop.it.mcp-2026-07-28-discover.header-body-revision-32020-headermismatch-400-mcp-method-method-32020', 'RFC 0153 §B', 'header ≠ body revision ⇒ -32020 HeaderMismatch (400); Mcp-Method ≠ method ⇒ -32020')).toBe(400);
|
|
78
79
|
expect(r1.error?.code).toBe(MCP_ERR.HEADER_MISMATCH);
|
|
79
80
|
const r2 = await call(server.endpoint(), 'tools/list', {}, { version: '2026-07-28', mcpMethod: 'tools/call' });
|
|
80
81
|
expect(r2.error?.code).toBe(MCP_ERR.HEADER_MISMATCH);
|
|
@@ -82,7 +83,7 @@ describe('RFC 0153 §B — the suite MCP server speaks 2026-07-28 (dual-era McpF
|
|
|
82
83
|
|
|
83
84
|
it('an unsupported revision ⇒ -32022 UnsupportedProtocolVersion with data.supported[] (400)', async () => {
|
|
84
85
|
const r = await call(server.endpoint(), 'tools/list', {}, { version: '1999-01-01' });
|
|
85
|
-
expect(r.status).toBe(400);
|
|
86
|
+
expect(r.status, req('openwop.it.mcp-2026-07-28-discover.an-unsupported-revision-32022-unsupportedprotocolversion-with-data-supported-400', 'RFC 0153 §B', 'an unsupported revision ⇒ -32022 UnsupportedProtocolVersion with data.supported[] (400)')).toBe(400);
|
|
86
87
|
expect(r.error?.code).toBe(MCP_ERR.UNSUPPORTED_PROTOCOL_VERSION);
|
|
87
88
|
expect(r.error?.data?.['supported']).toEqual(['2026-07-28', '2025-06-18']);
|
|
88
89
|
expect(r.error?.data?.['requested']).toBe('1999-01-01');
|
|
@@ -90,7 +91,7 @@ describe('RFC 0153 §B — the suite MCP server speaks 2026-07-28 (dual-era McpF
|
|
|
90
91
|
|
|
91
92
|
it('header-less ⇒ legacy semantics on a dual-era server (initialize works); ⇒ -32022 on a current-only server', async () => {
|
|
92
93
|
const legacy = await call(server.endpoint(), 'initialize', { protocolVersion: '2025-06-18', capabilities: {}, clientInfo: { name: 'x', version: '0' } });
|
|
93
|
-
expect(legacy.error).toBeUndefined();
|
|
94
|
+
expect(legacy.error, req('openwop.it.mcp-2026-07-28-discover.header-less-legacy-semantics-on-a-dual-era-server-initialize-works-32022-on-a-cu', 'RFC 0153 §B', 'header-less ⇒ legacy semantics on a dual-era server (initialize works); ⇒ -32022 on a current-only server')).toBeUndefined();
|
|
94
95
|
expect(typeof legacy.result?.['protocolVersion']).toBe('string');
|
|
95
96
|
const only = new McpFakeServer({ protocolVersions: ['2026-07-28'] });
|
|
96
97
|
await only.start(0);
|
|
@@ -105,13 +106,13 @@ describe('RFC 0153 §B — the suite MCP server speaks 2026-07-28 (dual-era McpF
|
|
|
105
106
|
|
|
106
107
|
it('initialize under 2026-07-28 is method-not-found — the handshake does not exist in this revision', async () => {
|
|
107
108
|
const r = await call(server.endpoint(), 'initialize', {}, { version: '2026-07-28' });
|
|
108
|
-
expect(r.status).toBe(404);
|
|
109
|
+
expect(r.status, req('openwop.it.mcp-2026-07-28-discover.initialize-under-2026-07-28-is-method-not-found-the-handshake-does-not-exist-in', 'RFC 0153 §B', 'initialize under 2026-07-28 is method-not-found — the handshake does not exist in this revision')).toBe(404);
|
|
109
110
|
expect(r.error?.code).toBe(-32601);
|
|
110
111
|
});
|
|
111
112
|
|
|
112
113
|
it('tools/list @2026-07-28 carries resultType + CacheableResult hints and lists the MRTR tool', async () => {
|
|
113
114
|
const r = await call(server.endpoint(), 'tools/list', {}, { version: '2026-07-28', mcpMethod: 'tools/list' });
|
|
114
|
-
expect(r.result?.['resultType']).toBe('complete');
|
|
115
|
+
expect(r.result?.['resultType'], req('openwop.it.mcp-2026-07-28-discover.tools-list-2026-07-28-carries-resulttype-cacheableresult-hints-and-lists-the-mrt', 'RFC 0153 §B', 'tools/list @2026-07-28 carries resultType + CacheableResult hints and lists the MRTR tool')).toBe('complete');
|
|
115
116
|
expect(typeof r.result?.['ttlMs']).toBe('number');
|
|
116
117
|
expect(r.result?.['cacheScope']).toBe('public');
|
|
117
118
|
const names = (r.result?.['tools'] as Array<{ name: string }>).map((t) => t.name);
|
|
@@ -124,7 +125,7 @@ describe('RFC 0153 §B — the suite MCP server speaks 2026-07-28 (dual-era McpF
|
|
|
124
125
|
it('MRTR: needs_input answers input_required (elicitation + opaque requestState) and completes on the retry; missing capability ⇒ -32021', async () => {
|
|
125
126
|
server.reset();
|
|
126
127
|
const noCap = await call(server.endpoint(), 'tools/call', { name: 'needs_input', arguments: {} }, { version: '2026-07-28', mcpMethod: 'tools/call', mcpName: 'needs_input' });
|
|
127
|
-
expect(noCap.error?.code).toBe(MCP_ERR.MISSING_REQUIRED_CLIENT_CAPABILITY);
|
|
128
|
+
expect(noCap.error?.code, req('openwop.it.mcp-2026-07-28-discover.mrtr-needs-input-answers-input-required-elicitation-opaque-requeststate-and-comp', 'RFC 0153 §B', 'MRTR: needs_input answers input_required (elicitation + opaque requestState) and completes on the retry; missing capability ⇒ -32021')).toBe(MCP_ERR.MISSING_REQUIRED_CLIENT_CAPABILITY);
|
|
128
129
|
expect(noCap.error?.data?.['requiredCapabilities']).toEqual(['elicitation']);
|
|
129
130
|
|
|
130
131
|
const first = await call(server.endpoint(), 'tools/call', { name: 'needs_input', arguments: {}, _meta: { [META_C]: { elicitation: {} } } }, { version: '2026-07-28', mcpMethod: 'tools/call', mcpName: 'needs_input' });
|
|
@@ -182,10 +183,10 @@ describe.skipIf(!process.env.OPENWOP_BASE_URL)('RFC 0153 §B — host as MCP ser
|
|
|
182
183
|
const caps = (await mcp())!;
|
|
183
184
|
const r = await hostRpc('server/discover', { _meta: { [META_V]: '2026-07-28', [META_C]: {} } }, { 'MCP-Protocol-Version': '2026-07-28', 'Mcp-Method': 'server/discover' });
|
|
184
185
|
if (r.status === 404 || r.status === 403) return seamAbsent(`host advertises an MCP server mount but the mount (capabilities.mcp.serverUrls[0], else /v1/host/sample/mcp) answered ${r.status} — RFC 0153 §B is unobservable at the path the host itself advertised`);
|
|
185
|
-
expect(r.status,
|
|
186
|
+
expect(r.status, req('openwop.it.mcp-2026-07-28-discover.server-discover-reports-supportedversions-equal-to-capabilities-mcp-protocolvers', 'mcp-integration.md §B', 'server/discover is a server MUST under 2026-07-28')).toBe(200);
|
|
186
187
|
expect(r.body.result?.['resultType']).toBe('complete');
|
|
187
|
-
expect([...((r.body.result?.['supportedVersions'] as string[] | undefined) ?? [])].sort(),
|
|
188
|
-
expect(typeof r.body.result?.['ttlMs'],
|
|
188
|
+
expect([...((r.body.result?.['supportedVersions'] as string[] | undefined) ?? [])].sort(), req('openwop.it.mcp-2026-07-28-discover.server-discover-reports-supportedversions-equal-to-capabilities-mcp-protocolvers', 'mcp-integration.md §B', 'server/discover.supportedVersions MUST equal capabilities.mcp.protocolVersions — two documents, one fact')).toEqual([...(caps.protocolVersions ?? [])].sort());
|
|
189
|
+
expect(typeof r.body.result?.['ttlMs'], req('openwop.it.mcp-2026-07-28-discover.server-discover-reports-supportedversions-equal-to-capabilities-mcp-protocolvers', 'mcp-integration.md §D', 'server/discover is cacheable: ttlMs REQUIRED')).toBe('number');
|
|
189
190
|
expect(['public', 'private']).toContain(r.body.result?.['cacheScope']);
|
|
190
191
|
});
|
|
191
192
|
|
|
@@ -199,11 +200,11 @@ describe.skipIf(!process.env.OPENWOP_BASE_URL)('RFC 0153 §B — host as MCP ser
|
|
|
199
200
|
// (mcp-integration.md §B: agreement is checked before selection).
|
|
200
201
|
const mismatch = await hostRpc('tools/list', { _meta: { [META_V]: '2025-06-18', [META_C]: {} } }, { 'MCP-Protocol-Version': '2026-07-28', 'Mcp-Method': 'tools/list' });
|
|
201
202
|
if (mismatch.status === 404 || mismatch.status === 403) return seamAbsent(`host advertises an MCP server mount but the mount (capabilities.mcp.serverUrls[0], else /v1/host/sample/mcp) answered ${mismatch.status} — RFC 0153 §B is unobservable at the path the host itself advertised`);
|
|
202
|
-
expect(mismatch.status,
|
|
203
|
+
expect(mismatch.status, req('openwop.it.mcp-2026-07-28-discover.a-header-body-revision-mismatch-is-refused-400-32020-an-unsupported-revision-400', 'mcp-integration.md §B', 'header ≠ body MUST be refused 400 (HeaderMismatchError -32020) — fail closed')).toBe(400);
|
|
203
204
|
expect(mismatch.body.error?.code).toBe(MCP_ERR.HEADER_MISMATCH);
|
|
204
205
|
const unsupported = await hostRpc('tools/list', { _meta: { [META_V]: '1999-01-01', [META_C]: {} } }, { 'MCP-Protocol-Version': '1999-01-01', 'Mcp-Method': 'tools/list' });
|
|
205
|
-
expect(unsupported.status,
|
|
206
|
+
expect(unsupported.status, req('openwop.it.mcp-2026-07-28-discover.a-header-body-revision-mismatch-is-refused-400-32020-an-unsupported-revision-400', 'mcp-integration.md §B', 'an unsupported revision MUST be refused 400 (UnsupportedProtocolVersionError -32022)')).toBe(400);
|
|
206
207
|
expect(unsupported.body.error?.code).toBe(MCP_ERR.UNSUPPORTED_PROTOCOL_VERSION);
|
|
207
|
-
expect(Array.isArray(unsupported.body.error?.data?.['supported']),
|
|
208
|
+
expect(Array.isArray(unsupported.body.error?.data?.['supported']), req('openwop.it.mcp-2026-07-28-discover.a-header-body-revision-mismatch-is-refused-400-32020-an-unsupported-revision-400', 'mcp-integration.md §B', 'the refusal MUST list data.supported[]')).toBe(true);
|
|
208
209
|
});
|
|
209
210
|
});
|
|
@@ -22,10 +22,11 @@
|
|
|
22
22
|
|
|
23
23
|
import { describe, it, expect } from 'vitest';
|
|
24
24
|
import { driver } from '../lib/driver.js';
|
|
25
|
-
import { seamAbsent } from '../lib/soft-skip.js';
|
|
25
|
+
import { seamAbsent, softSkip } from '../lib/soft-skip.js';
|
|
26
26
|
import { mcpServerMount } from '../lib/mcp-mount.js';
|
|
27
27
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
28
28
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
29
|
+
import { req } from '../lib/requirement-ids.js';
|
|
29
30
|
|
|
30
31
|
const PROFILE = 'mcp-2026-07-28';
|
|
31
32
|
const META_V = 'io.modelcontextprotocol/protocolVersion';
|
|
@@ -49,19 +50,19 @@ describe.skipIf(!process.env.OPENWOP_BASE_URL)('RFC 0153 §D — mcp-cache-tenan
|
|
|
49
50
|
if (!behaviorGate(PROFILE, await claimsCurrent())) return;
|
|
50
51
|
const mine = await listAs();
|
|
51
52
|
if (mine.status === 404 || mine.status === 403) return seamAbsent(`host advertises an MCP server mount but the mount (capabilities.mcp.serverUrls[0], else /v1/host/sample/mcp) answered ${mine.status} — RFC 0153 §B is unobservable at the path the host itself advertised`);
|
|
52
|
-
expect(['public', 'private'],
|
|
53
|
+
expect(['public', 'private'], req('openwop.it.mcp-cache-tenant-scope.a-per-caller-list-is-cachescope-private-a-public-list-is-byte-identical-across-c', 'mcp-integration.md §D', 'cacheScope MUST be present on tools/list')).toContain(mine.body.result?.cacheScope);
|
|
53
54
|
const other = process.env.OPENWOP_TEST_SECONDARY_API_KEY;
|
|
54
55
|
if (!other) {
|
|
55
56
|
// Shape only; the cross-caller half is unobservable without a second credential.
|
|
56
57
|
// eslint-disable-next-line no-console
|
|
57
58
|
console.warn('[mcp-cache-tenant-scope] OPENWOP_TEST_SECONDARY_API_KEY not set — cross-caller half not exercised (blocked)');
|
|
58
|
-
return;
|
|
59
|
+
return softSkip('blocked', 'precondition not met — `!other` returned early ([mcp-cache-tenant-scope] OPENWOP_TEST_SECONDARY_API_KEY not set — cross-caller half not exercised (blocked)) (seam, prior step, or fixture unavailable)');
|
|
59
60
|
}
|
|
60
61
|
const theirs = await listAs(other);
|
|
61
|
-
if (theirs.status !== 200) return;
|
|
62
|
+
if (theirs.status !== 200) return softSkip('blocked', 'precondition not met — `theirs.status !== 200` returned early (seam, prior step, or fixture unavailable)');
|
|
62
63
|
const same = JSON.stringify(mine.body.result?.tools) === JSON.stringify(theirs.body.result?.tools);
|
|
63
64
|
if (!same) {
|
|
64
|
-
expect(mine.body.result?.cacheScope,
|
|
65
|
+
expect(mine.body.result?.cacheScope, req('openwop.it.mcp-cache-tenant-scope.a-per-caller-list-is-cachescope-private-a-public-list-is-byte-identical-across-c', 'mcp-integration.md §D', 'a list that differs per caller MUST be cacheScope private — a public list that differs is cross-context cache poisoning (mcp-cache-tenant-scoped)')).toBe('private');
|
|
65
66
|
expect(theirs.body.result?.cacheScope).toBe('private');
|
|
66
67
|
}
|
|
67
68
|
});
|
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
|
|
19
19
|
import { describe, it, expect } from 'vitest';
|
|
20
20
|
import { driver } from '../lib/driver.js';
|
|
21
|
-
import { seamAbsent } from '../lib/soft-skip.js';
|
|
21
|
+
import { seamAbsent, softSkip } from '../lib/soft-skip.js';
|
|
22
22
|
import { mcpServerMount } from '../lib/mcp-mount.js';
|
|
23
23
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
24
24
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
25
|
+
import { req } from '../lib/requirement-ids.js';
|
|
25
26
|
|
|
26
27
|
const PROFILE = 'mcp-2026-07-28';
|
|
27
28
|
const META_V = 'io.modelcontextprotocol/protocolVersion';
|
|
@@ -50,16 +51,16 @@ describe.skipIf(!process.env.OPENWOP_BASE_URL)('RFC 0153 §E — mcp-current-aut
|
|
|
50
51
|
if (!behaviorGate(PROFILE, claims)) return;
|
|
51
52
|
const authed = await driver.post(await mcpServerMount(), REQ, { headers: HDR });
|
|
52
53
|
if (authed.status === 404 || authed.status === 403) return seamAbsent(`host advertises an MCP server mount but the mount (capabilities.mcp.serverUrls[0], else /v1/host/sample/mcp) answered ${authed.status} — RFC 0153 §B is unobservable at the path the host itself advertised`);
|
|
53
|
-
expect(authed.status,
|
|
54
|
+
expect(authed.status, req('openwop.it.mcp-current-auth-boundary.an-unauthenticated-current-profile-request-is-refused-unless-anonymousactor-is-a', 'mcp-integration.md §E', 'the authenticated call MUST succeed at the same path, so a refusal below is not a wrong path')).toBe(200);
|
|
54
55
|
const anonymous = await driver.post(await mcpServerMount(), REQ, { headers: HDR, authenticated: false });
|
|
55
56
|
if (anon?.supported === true) {
|
|
56
57
|
// Anonymous is permitted only through the RFC 0132 surface; a 200 here is that surface answering.
|
|
57
58
|
expect([200, 401, 403]).toContain(anonymous.status);
|
|
58
|
-
return;
|
|
59
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `anon?.supported === true` returned early');
|
|
59
60
|
}
|
|
60
61
|
expect(
|
|
61
62
|
[401, 403],
|
|
62
|
-
|
|
63
|
+
req('openwop.it.mcp-current-auth-boundary.an-unauthenticated-current-profile-request-is-refused-unless-anonymousactor-is-a', 'mcp-integration.md §E', 'an anonymous MCP principal MUST NOT be the production default for an advertised current profile — refuse (401/403) or advertise anonymousActor'),
|
|
63
64
|
).toContain(anonymous.status);
|
|
64
65
|
|
|
65
66
|
// S30 (2026-08-17, openwop-app H43): a cookie-posture host may MINT an anonymous
|
|
@@ -72,12 +73,12 @@ describe.skipIf(!process.env.OPENWOP_BASE_URL)('RFC 0153 §E — mcp-current-aut
|
|
|
72
73
|
...setCookies(anonymous.headers),
|
|
73
74
|
...setCookies((await driver.get('/.well-known/openwop', { authenticated: false })).headers),
|
|
74
75
|
];
|
|
75
|
-
if (minted.length === 0) return; // host mints no anonymous session; the bare probe was the whole observation
|
|
76
|
+
if (minted.length === 0) return softSkip('blocked', 'precondition not met — `minted.length === 0` returned early (host mints no anonymous session; the bare probe was the whole observation) (seam, prior step, or fixture unavailable)'); // host mints no anonymous session; the bare probe was the whole observation
|
|
76
77
|
const cookie = minted.map((c) => c.split(';')[0]).join('; ');
|
|
77
78
|
const withSession = await driver.post(await mcpServerMount(), REQ, { headers: { ...HDR, Cookie: cookie }, authenticated: false });
|
|
78
79
|
expect(
|
|
79
80
|
[401, 403],
|
|
80
|
-
|
|
81
|
+
req('openwop.it.mcp-current-auth-boundary.an-unauthenticated-current-profile-request-is-refused-unless-anonymousactor-is-a', 'mcp-integration.md §E', 'a caller holding only a host-minted anonymous session cookie is still an anonymous principal — refuse (401/403) unless anonymousActor is advertised (S30)'),
|
|
81
82
|
).toContain(withSession.status);
|
|
82
83
|
});
|
|
83
84
|
});
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
|
|
30
30
|
import { describe, it, expect } from 'vitest';
|
|
31
31
|
import { driver } from '../lib/driver.js';
|
|
32
|
+
import { req } from '../lib/requirement-ids.js';
|
|
33
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
32
34
|
|
|
33
35
|
interface McpAdvertisement {
|
|
34
36
|
supported?: unknown;
|
|
@@ -75,28 +77,28 @@ async function fetchMcpAdvertisements(): Promise<DiscoveredMcp[]> {
|
|
|
75
77
|
describe('mcp: discoverability shape', () => {
|
|
76
78
|
it('any advertised MCP capability has well-formed shape ({supported, serverUrls})', async () => {
|
|
77
79
|
const advertisements = await fetchMcpAdvertisements();
|
|
78
|
-
if (advertisements.length === 0) return; // skip-equivalent: host does not advertise MCP
|
|
80
|
+
if (advertisements.length === 0) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `advertisements.length === 0` returned early (skip-equivalent: host does not advertise MCP)'); // skip-equivalent: host does not advertise MCP
|
|
79
81
|
|
|
80
82
|
for (const { path, ad } of advertisements) {
|
|
81
|
-
expect(typeof ad.supported,
|
|
83
|
+
expect(typeof ad.supported, req('openwop.it.mcp-discoverability.any-advertised-mcp-capability-has-well-formed-shape-supported-serverurls',
|
|
82
84
|
'spec/v1/mcp-integration.md §"Conformance + interop"',
|
|
83
85
|
`${path}.supported MUST be boolean when advertised`,
|
|
84
86
|
)).toBe('boolean');
|
|
85
87
|
|
|
86
88
|
if (ad.supported === true) {
|
|
87
|
-
expect(Array.isArray(ad.serverUrls),
|
|
89
|
+
expect(Array.isArray(ad.serverUrls), req('openwop.it.mcp-discoverability.any-advertised-mcp-capability-has-well-formed-shape-supported-serverurls',
|
|
88
90
|
'spec/v1/mcp-integration.md',
|
|
89
91
|
`${path}.serverUrls MUST be an array when supported:true`,
|
|
90
92
|
)).toBe(true);
|
|
91
93
|
|
|
92
94
|
if (Array.isArray(ad.serverUrls)) {
|
|
93
|
-
expect(ad.serverUrls.length,
|
|
95
|
+
expect(ad.serverUrls.length, req('openwop.it.mcp-discoverability.any-advertised-mcp-capability-has-well-formed-shape-supported-serverurls',
|
|
94
96
|
'spec/v1/mcp-integration.md',
|
|
95
97
|
`${path}.serverUrls MUST be non-empty when supported:true`,
|
|
96
98
|
)).toBeGreaterThan(0);
|
|
97
99
|
|
|
98
100
|
for (const url of ad.serverUrls) {
|
|
99
|
-
expect(typeof url,
|
|
101
|
+
expect(typeof url, req('openwop.it.mcp-discoverability.any-advertised-mcp-capability-has-well-formed-shape-supported-serverurls',
|
|
100
102
|
'spec/v1/mcp-integration.md',
|
|
101
103
|
`${path}.serverUrls entries MUST be strings`,
|
|
102
104
|
)).toBe('string');
|
|
@@ -108,7 +110,7 @@ describe('mcp: discoverability shape', () => {
|
|
|
108
110
|
|
|
109
111
|
it('serverUrls are valid URL paths or absolute URLs', async () => {
|
|
110
112
|
const advertisements = await fetchMcpAdvertisements();
|
|
111
|
-
if (advertisements.length === 0) return; // skip-equivalent
|
|
113
|
+
if (advertisements.length === 0) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `advertisements.length === 0` returned early (skip-equivalent)'); // skip-equivalent
|
|
112
114
|
|
|
113
115
|
for (const { path, ad } of advertisements) {
|
|
114
116
|
if (ad.supported !== true || !Array.isArray(ad.serverUrls)) continue;
|
|
@@ -119,7 +121,7 @@ describe('mcp: discoverability shape', () => {
|
|
|
119
121
|
// ambiguous to a client trying to connect.
|
|
120
122
|
const isHostRelative = url.startsWith('/');
|
|
121
123
|
const isAbsoluteHttp = url.startsWith('http://') || url.startsWith('https://');
|
|
122
|
-
expect(isHostRelative || isAbsoluteHttp,
|
|
124
|
+
expect(isHostRelative || isAbsoluteHttp, req('openwop.it.mcp-discoverability.serverurls-are-valid-url-paths-or-absolute-urls',
|
|
123
125
|
'spec/v1/mcp-integration.md',
|
|
124
126
|
`${path}.serverUrls entry "${url}" MUST be a leading-slash path or absolute http(s) URL`,
|
|
125
127
|
)).toBe(true);
|
|
@@ -27,6 +27,7 @@ import { mcpServerMount } from '../lib/mcp-mount.js';
|
|
|
27
27
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
28
28
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
29
29
|
import { getMcpFakeServer } from '../lib/mcp-fake-server.js';
|
|
30
|
+
import { req } from '../lib/requirement-ids.js';
|
|
30
31
|
|
|
31
32
|
export const REQUIRES_HOST_CALLBACK = "the host issues MCP JSON-RPC calls to the suite's fake server via /v1/host/sample/mcp/invoke (serverUrl), which the server records";
|
|
32
33
|
|
|
@@ -53,14 +54,14 @@ describe.skipIf(!process.env.OPENWOP_BASE_URL)('RFC 0153 §D — mcp-extension-o
|
|
|
53
54
|
// an advertised-missing seam (RFC 0148 §B). A 403 is NOT a pass.
|
|
54
55
|
return seamAbsent(`host advertises mcp-2026-07-28 but the invoke seam /v1/host/sample/mcp/invoke answered ${drive.status}`);
|
|
55
56
|
}
|
|
56
|
-
expect(server.invocations().some((i) => i.method === 'tools/call'), 'the host MUST have called the tool').toBe(true);
|
|
57
|
+
expect(server.invocations().some((i) => i.method === 'tools/call'), req('openwop.it.mcp-extension-opacity.client-half-an-extension-meta-asserting-authority-in-a-tool-result-changes-nothi', 'host-sample-test-seams.md §23', 'the host MUST have called the tool')).toBe(true);
|
|
57
58
|
const rep = (drive.json as { extensionAuthority?: { scopesWidened?: boolean; approvalAdvanced?: boolean } }).extensionAuthority;
|
|
58
59
|
if (rep === undefined) {
|
|
59
|
-
expect(rep,
|
|
60
|
-
return;
|
|
60
|
+
expect(rep, req('openwop.it.mcp-extension-opacity.client-half-an-extension-meta-asserting-authority-in-a-tool-result-changes-nothi', 'host-sample-test-seams.md §23', 'the seam SHOULD report `extensionAuthority: { scopesWidened, approvalAdvanced }` for scenario "extension-asserts-authority"; until it does the requirement is `blocked`, not passed')).toBeDefined();
|
|
61
|
+
return softSkip('blocked', 'precondition not met — `rep === undefined` returned early (seam, prior step, or fixture unavailable)');
|
|
61
62
|
}
|
|
62
|
-
expect(rep.scopesWidened,
|
|
63
|
-
expect(rep.approvalAdvanced,
|
|
63
|
+
expect(rep.scopesWidened, req('openwop.it.mcp-extension-opacity.client-half-an-extension-meta-asserting-authority-in-a-tool-result-changes-nothi', 'mcp-integration.md §D', 'an extension MUST NOT gain tool authority or scope by appearing in _meta (mcp-extension-no-authority)')).toBe(false);
|
|
64
|
+
expect(rep.approvalAdvanced, req('openwop.it.mcp-extension-opacity.client-half-an-extension-meta-asserting-authority-in-a-tool-result-changes-nothi', 'mcp-integration.md §D/§E', 'MCP content MUST NOT advance approval gates')).toBe(false);
|
|
64
65
|
});
|
|
65
66
|
|
|
66
67
|
it('server half: unknown _meta extension keys on a request are ignored, not refused and not honoured', async () => {
|
|
@@ -73,7 +74,7 @@ describe.skipIf(!process.env.OPENWOP_BASE_URL)('RFC 0153 §D — mcp-extension-o
|
|
|
73
74
|
}, { headers: { 'MCP-Protocol-Version': '2026-07-28', 'Mcp-Method': 'tools/list' } });
|
|
74
75
|
if (res.status === 404 || res.status === 403) return seamAbsent(`host advertises mcp-2026-07-28 but the MCP server mount /v1/host/sample/mcp answered ${res.status}`);
|
|
75
76
|
const body = res.json as { result?: { resultType?: string }; error?: { code: number } };
|
|
76
|
-
expect(res.status,
|
|
77
|
+
expect(res.status, req('openwop.it.mcp-extension-opacity.server-half-unknown-meta-extension-keys-on-a-request-are-ignored-not-refused-and', 'mcp-integration.md §D', 'an unknown extension is opaque: the request MUST be processed normally, neither refused nor granted anything')).toBe(200);
|
|
77
78
|
expect(body.error).toBeUndefined();
|
|
78
79
|
expect(body.result?.resultType).toBe('complete');
|
|
79
80
|
});
|