@openwop/openwop-conformance 1.162.0 → 2.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/dist/cli.js +54 -1
- package/dist/lib/paths.js +25 -3
- package/dist/lib/requirement-ids.js +34 -4
- package/dist/lib/scenario-disposition.js +1 -0
- package/dist/lib/spec-coherence.js +13 -32
- package/dist/spec-artifacts.lock.json +5 -0
- package/package.json +11 -33
- package/requirements.json +20303 -11809
- package/scenario-majors.json +1343 -0
- package/schemas/CORPUS-STAMP.json +138 -6
- package/src/cli.ts +47 -1
- package/src/global-setup.ts +3 -2
- package/src/lib/corpus-stamp.ts +29 -1
- package/src/lib/paths.ts +25 -3
- package/src/lib/requirement-ids.ts +33 -4
- package/src/lib/scenario-disposition.ts +1 -0
- package/src/lib/spec-coherence.ts +15 -32
- package/src/scenarios/a2a-1-0-agent-card.test.ts +17 -16
- package/src/scenarios/a2a-1-0-task-roundtrip.test.ts +11 -10
- package/src/scenarios/a2a-card-runtime-consistency.test.ts +18 -17
- package/src/scenarios/a2a-peer-authority.test.ts +7 -6
- package/src/scenarios/a2a-task-roundtrip.test.ts +30 -29
- package/src/scenarios/a2a-version-negotiation.test.ts +16 -14
- package/src/scenarios/a2ui-surface-degrades.test.ts +5 -3
- package/src/scenarios/a2ui-surface-delta-transport.test.ts +35 -33
- package/src/scenarios/a2ui-surface-replay.test.ts +6 -4
- package/src/scenarios/a2ui-surface-shape.test.ts +11 -10
- package/src/scenarios/a2ui-surface-version-refusal.test.ts +6 -4
- package/src/scenarios/a2ui-untrusted-blocks-approval.test.ts +5 -3
- package/src/scenarios/agent-capability-degraded-projection.test.ts +12 -11
- package/src/scenarios/agent-channel-dispatch.test.ts +18 -16
- package/src/scenarios/agent-deployment-lifecycle.test.ts +20 -19
- package/src/scenarios/agent-deployment-shape.test.ts +22 -24
- package/src/scenarios/agent-eval-run.test.ts +19 -18
- package/src/scenarios/agent-eval-suite-shape.test.ts +20 -22
- package/src/scenarios/agent-live-allowlist-enforced.test.ts +6 -5
- package/src/scenarios/agent-live-invocation-bracket.test.ts +13 -12
- package/src/scenarios/agent-live-runtime-shape.test.ts +10 -12
- package/src/scenarios/agent-live-structured-output.test.ts +7 -6
- package/src/scenarios/agent-loop-iteration-monotonic.test.ts +3 -3
- package/src/scenarios/agent-loop-stateful-resume.test.ts +2 -2
- package/src/scenarios/agent-loop-version5-shape.test.ts +4 -4
- package/src/scenarios/agent-loop-workspace-snapshot.test.ts +3 -3
- package/src/scenarios/agent-manifest-role-profile.test.ts +9 -9
- package/src/scenarios/agent-manifest-runtime.test.ts +13 -12
- package/src/scenarios/agent-org-chart-scoping.test.ts +14 -13
- package/src/scenarios/agent-org-chart-shape.test.ts +11 -13
- package/src/scenarios/agent-platform-aggregate-evidence.test.ts +5 -4
- package/src/scenarios/agent-platform-profile.test.ts +18 -19
- package/src/scenarios/agent-requires-capabilities-shape.test.ts +4 -4
- package/src/scenarios/agent-roster-attribution.test.ts +18 -17
- package/src/scenarios/agent-roster-shape.test.ts +17 -19
- package/src/scenarios/agent-verifier-shape.test.ts +11 -11
- package/src/scenarios/agentConfidenceEscalation.test.ts +2 -1
- package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +6 -5
- package/src/scenarios/agentMemoryRedactionContract.test.ts +2 -1
- package/src/scenarios/agentMemoryRoundTrip.test.ts +2 -1
- package/src/scenarios/agentMemoryTtlExpiry.test.ts +4 -3
- package/src/scenarios/agentMessageReducer.test.ts +2 -1
- package/src/scenarios/agentMetadata.test.ts +2 -1
- package/src/scenarios/agentPackCatalog.test.ts +23 -21
- package/src/scenarios/agentPackExport.test.ts +4 -2
- package/src/scenarios/agentPackHandoffSchemaValidation.test.ts +5 -4
- package/src/scenarios/agentPackInstall.test.ts +4 -2
- package/src/scenarios/agentPackProvenance.test.ts +2 -1
- package/src/scenarios/agentReasoningEvents.test.ts +3 -2
- package/src/scenarios/agentReasoningStreaming.test.ts +11 -10
- package/src/scenarios/agents-run-tool-allowlist.test.ts +14 -12
- package/src/scenarios/ai-envelope-shape.test.ts +31 -29
- package/src/scenarios/aiEnvelope.capBreached.test.ts +26 -24
- package/src/scenarios/aiEnvelope.contractRefusal.test.ts +36 -34
- package/src/scenarios/aiEnvelope.correlationReplay.test.ts +17 -15
- package/src/scenarios/aiEnvelope.redaction.test.ts +22 -20
- package/src/scenarios/aiEnvelope.schemaDrift.test.ts +18 -16
- package/src/scenarios/aiEnvelope.trustBoundaryPropagation.test.ts +38 -36
- package/src/scenarios/aiEnvelope.universalKinds.test.ts +31 -29
- package/src/scenarios/aiproviders-input-shape.test.ts +5 -5
- package/src/scenarios/aiproviders-realtimevoice-shape.test.ts +14 -16
- package/src/scenarios/aiproviders-selfhosted-honesty.test.ts +6 -4
- package/src/scenarios/aiproviders-selfhosted-shape.test.ts +15 -17
- package/src/scenarios/aiproviders-speechsynth-shape.test.ts +6 -8
- package/src/scenarios/aiproviders-subscription-scope.test.ts +7 -8
- package/src/scenarios/anonymous-actor-audit-opaque.test.ts +9 -8
- package/src/scenarios/anonymous-actor-default-deny.test.ts +10 -9
- package/src/scenarios/anonymous-actor-egress-guarded.test.ts +6 -5
- package/src/scenarios/anonymous-actor-no-secret-reach.test.ts +7 -6
- package/src/scenarios/anonymous-actor-shape.test.ts +17 -19
- package/src/scenarios/anonymous-actor-write-gated.test.ts +9 -8
- package/src/scenarios/append-ordering.test.ts +7 -5
- package/src/scenarios/approval-gate-events.test.ts +4 -3
- package/src/scenarios/approval-gate-flow.test.ts +4 -3
- package/src/scenarios/approval-payload.test.ts +5 -4
- package/src/scenarios/artifact-auth.test.ts +7 -6
- package/src/scenarios/artifact-type-pack-install.test.ts +6 -6
- package/src/scenarios/artifact-type-pack-manifest-validation.test.ts +9 -8
- package/src/scenarios/artifact-type-registration-source.test.ts +20 -20
- package/src/scenarios/artifact-type-store-emission.test.ts +15 -18
- package/src/scenarios/artifact-type-store-without-render.test.ts +4 -4
- package/src/scenarios/audit-log-integrity.test.ts +8 -6
- package/src/scenarios/auth-api-key-rotation.test.ts +11 -9
- package/src/scenarios/auth-mtls.test.ts +22 -20
- package/src/scenarios/auth-oauth2-client-credentials.test.ts +16 -14
- package/src/scenarios/auth-oidc-user-bearer.test.ts +26 -24
- package/src/scenarios/auth-saml-profile.test.ts +14 -12
- package/src/scenarios/auth-scim-profile.test.ts +3 -2
- package/src/scenarios/auth-subject-link-key-class.test.ts +8 -7
- package/src/scenarios/auth-subject-link.test.ts +11 -10
- package/src/scenarios/auth.test.ts +5 -4
- package/src/scenarios/authorization-fail-closed.test.ts +3 -2
- package/src/scenarios/authorization-roles-shape.test.ts +9 -7
- package/src/scenarios/blob-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/blob-presign-expiry.test.ts +12 -10
- package/src/scenarios/blob-roundtrip.test.ts +0 -0
- package/src/scenarios/budget-enforcement.test.ts +15 -14
- package/src/scenarios/budget-policy-shape.test.ts +18 -19
- package/src/scenarios/bulk-cancel.test.ts +9 -8
- package/src/scenarios/byok-auth-modes.test.ts +13 -14
- package/src/scenarios/byok-roundtrip.test.ts +12 -10
- package/src/scenarios/cache-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/cache-ttl-expiry.test.ts +7 -5
- package/src/scenarios/callai-multimodal.test.ts +5 -3
- package/src/scenarios/cancellation.test.ts +6 -5
- package/src/scenarios/cap-breach.test.ts +15 -14
- package/src/scenarios/capability-arm-witness.test.ts +5 -3
- package/src/scenarios/certification-bundle-non-vacuous.test.ts +20 -19
- package/src/scenarios/certification-bundle-redaction.test.ts +11 -10
- package/src/scenarios/certification-bundle-v2.test.ts +11 -10
- package/src/scenarios/chain-compensation-expansion.test.ts +14 -13
- package/src/scenarios/chain-produced-var-roundtrip.test.ts +13 -14
- package/src/scenarios/chain-subchain-cycle-rejected.test.ts +9 -10
- package/src/scenarios/chain-subchain-fanout.test.ts +8 -9
- package/src/scenarios/chain-subchain-sibling.test.ts +14 -16
- package/src/scenarios/channel-presence-behavioral.test.ts +9 -7
- package/src/scenarios/channel-presence-shape.test.ts +11 -12
- package/src/scenarios/channel-ttl.test.ts +6 -5
- package/src/scenarios/chat-card-pack-execution.test.ts +4 -4
- package/src/scenarios/chat-card-pack-manifest-validation.test.ts +9 -8
- package/src/scenarios/commitment-fired.test.ts +6 -5
- package/src/scenarios/compensation-behavior.test.ts +24 -23
- package/src/scenarios/compensation-recovery.test.ts +28 -27
- package/src/scenarios/configurable-schema.test.ts +8 -6
- package/src/scenarios/conformance-advertised-seam-required.test.ts +6 -5
- package/src/scenarios/conformance-execution-witness.test.ts +9 -8
- package/src/scenarios/connection-pack-apihosts.test.ts +19 -17
- package/src/scenarios/connection-pack-manifest-valid.test.ts +12 -11
- package/src/scenarios/connection-pack-no-credential-material.test.ts +8 -6
- package/src/scenarios/connection-pack-reach-exclusive.test.ts +5 -4
- package/src/scenarios/connection-pack-write-reconsent.test.ts +6 -4
- package/src/scenarios/connection-provider-resolution.test.ts +18 -16
- package/src/scenarios/connector-manifest-validity.test.ts +8 -7
- package/src/scenarios/context-budget-transcript-bound.test.ts +17 -15
- package/src/scenarios/context-summarization-replay.test.ts +14 -12
- package/src/scenarios/contract-provenance.test.ts +18 -18
- package/src/scenarios/conversation-turn-model-provenance-shape.test.ts +14 -15
- package/src/scenarios/conversationCapabilityNegotiation.test.ts +2 -1
- package/src/scenarios/conversationLifecycle.test.ts +2 -1
- package/src/scenarios/conversationReplayDeterminism.test.ts +4 -2
- package/src/scenarios/conversationVsLegacySuspend.test.ts +2 -1
- package/src/scenarios/core-standard-profile.test.ts +8 -9
- package/src/scenarios/cost-attribution.test.ts +23 -21
- package/src/scenarios/credential-payload-redaction.test.ts +4 -3
- package/src/scenarios/credentials-capability-shape.test.ts +5 -4
- package/src/scenarios/cross-engine-append-behavior.test.ts +11 -10
- package/src/scenarios/cross-engine-append-ordering.test.ts +4 -3
- package/src/scenarios/cross-host-ancestry-endpoint.test.ts +11 -10
- package/src/scenarios/cross-host-causation-shape.test.ts +7 -6
- package/src/scenarios/cross-workspace-isolation.test.ts +4 -3
- package/src/scenarios/data-residency-admission.test.ts +15 -14
- package/src/scenarios/deadletter-capability-shape.test.ts +6 -4
- package/src/scenarios/deadletter-retry-exhaustion.test.ts +4 -3
- package/src/scenarios/debug-bundle-truncation.test.ts +9 -7
- package/src/scenarios/debugBundle.test.ts +25 -23
- package/src/scenarios/discovery-families-view.test.ts +5 -4
- package/src/scenarios/discovery.test.ts +30 -29
- package/src/scenarios/dispatch-cross-worker-handoff.test.ts +10 -8
- package/src/scenarios/dispatch-fanout-parallel.test.ts +24 -22
- package/src/scenarios/dispatch-input-mapping.test.ts +12 -10
- package/src/scenarios/dispatch-output-mapping.test.ts +9 -7
- package/src/scenarios/dispatch-per-item-input.test.ts +22 -20
- package/src/scenarios/dispatchLoop.test.ts +3 -2
- package/src/scenarios/distillation-index-roundtrip.test.ts +3 -3
- package/src/scenarios/distillation-secret-carryforward.test.ts +2 -2
- package/src/scenarios/distillation-shape.test.ts +4 -4
- package/src/scenarios/distillation-stable-archive.test.ts +6 -6
- package/src/scenarios/distillation-token-budget.test.ts +5 -5
- package/src/scenarios/durability-poison-exhaustion.test.ts +7 -6
- package/src/scenarios/egress-audience-binding.test.ts +11 -10
- package/src/scenarios/egress-decision-content-free.test.ts +6 -5
- package/src/scenarios/egress-provenance-shape.test.ts +17 -18
- package/src/scenarios/envelope-completion-distinguishes-truncation.test.ts +27 -25
- package/src/scenarios/envelope-nl-to-format-engaged.test.ts +19 -17
- package/src/scenarios/envelope-reasoning-secret-redaction.test.ts +28 -26
- package/src/scenarios/envelope-reasoning-shape.test.ts +19 -17
- package/src/scenarios/envelope-recovery-applied.test.ts +24 -22
- package/src/scenarios/envelope-refusal-shape.test.ts +31 -29
- package/src/scenarios/envelope-rendering-hint.test.ts +5 -4
- package/src/scenarios/envelope-retry-attempted.test.ts +33 -31
- package/src/scenarios/envelope-retry-exhausted.test.ts +22 -20
- package/src/scenarios/envelope-tier-one-subset-static.test.ts +9 -7
- package/src/scenarios/envelope-truncated.test.ts +19 -17
- package/src/scenarios/envelope-truncation-cap-exhaustion.test.ts +19 -17
- package/src/scenarios/envelope-variant-discriminator-static.test.ts +4 -3
- package/src/scenarios/errors.test.ts +20 -19
- package/src/scenarios/eventOrdering.test.ts +16 -14
- package/src/scenarios/exec-not-protocol-tier.test.ts +5 -7
- package/src/scenarios/experimental-tier-shape.test.ts +17 -15
- package/src/scenarios/export-bundle-portability.test.ts +17 -17
- package/src/scenarios/failure-path.test.ts +7 -6
- package/src/scenarios/feedback-capability-shape.test.ts +4 -3
- package/src/scenarios/feedback-correction-redaction.test.ts +2 -1
- package/src/scenarios/feedback-cross-tenant-isolation.test.ts +2 -1
- package/src/scenarios/feedback-fork-not-copied.test.ts +3 -2
- package/src/scenarios/feedback-on-terminal-run.test.ts +3 -2
- package/src/scenarios/feedback-record-and-list.test.ts +2 -1
- package/src/scenarios/feedback-unsupported-501.test.ts +2 -1
- package/src/scenarios/fixtures-gating.test.ts +29 -28
- package/src/scenarios/fixtures-valid.test.ts +16 -15
- package/src/scenarios/form-content-instantiation.test.ts +15 -14
- package/src/scenarios/frontend-plugin-packs.test.ts +29 -27
- package/src/scenarios/fs-path-traversal.test.ts +14 -12
- package/src/scenarios/goal-standing-continuation.test.ts +21 -21
- package/src/scenarios/grpc-transport.test.ts +9 -7
- package/src/scenarios/heartbeat-capability-shape.test.ts +3 -3
- package/src/scenarios/heartbeat-fires-once-per-tick.test.ts +2 -2
- package/src/scenarios/heartbeat-idempotent-no-spam.test.ts +3 -3
- package/src/scenarios/heartbeat-runtime-bound.test.ts +2 -2
- package/src/scenarios/highConcurrency.test.ts +14 -13
- package/src/scenarios/host-callback-declaration.test.ts +8 -7
- package/src/scenarios/http-client-ssrf.test.ts +7 -5
- package/src/scenarios/i18n-negotiation.test.ts +14 -12
- package/src/scenarios/idempotency-concurrent-claim.test.ts +14 -13
- package/src/scenarios/idempotency-key-determinism.test.ts +20 -18
- package/src/scenarios/idempotency.test.ts +8 -7
- package/src/scenarios/idempotencyRetry.test.ts +13 -11
- package/src/scenarios/identity-owner-shape.test.ts +6 -5
- package/src/scenarios/identity-passthrough.test.ts +4 -3
- package/src/scenarios/interrupt-approval.test.ts +6 -5
- package/src/scenarios/interrupt-approver-routing.test.ts +15 -15
- package/src/scenarios/interrupt-auth-required-resume.test.ts +5 -3
- package/src/scenarios/interrupt-clarification.test.ts +4 -3
- package/src/scenarios/interrupt-external-event-correlation.test.ts +6 -5
- package/src/scenarios/interrupt-parent-child-cascade.test.ts +4 -3
- package/src/scenarios/interrupt-quorum-resolution.test.ts +5 -4
- package/src/scenarios/interrupt-token-matrix.test.ts +8 -6
- package/src/scenarios/interruptRace.test.ts +12 -10
- package/src/scenarios/kv-atomic-increment.test.ts +11 -9
- package/src/scenarios/kv-cas.test.ts +14 -12
- package/src/scenarios/kv-cross-tenant-isolation.test.ts +12 -10
- package/src/scenarios/kv-ttl-expiry.test.ts +7 -5
- package/src/scenarios/localized-content-delivery.test.ts +24 -26
- package/src/scenarios/maliciousManifest.test.ts +10 -8
- package/src/scenarios/mcp-2026-07-28-discover.test.ts +15 -14
- package/src/scenarios/mcp-cache-tenant-scope.test.ts +6 -5
- package/src/scenarios/mcp-current-auth-boundary.test.ts +7 -6
- package/src/scenarios/mcp-discoverability.test.ts +9 -7
- package/src/scenarios/mcp-extension-opacity.test.ts +7 -6
- package/src/scenarios/mcp-mrtr-roundtrip.test.ts +18 -17
- package/src/scenarios/mcp-server-elicitation-bridge.test.ts +13 -12
- package/src/scenarios/mcp-server-prompt-roundtrip.test.ts +11 -10
- package/src/scenarios/mcp-server-resource-roundtrip.test.ts +11 -10
- package/src/scenarios/mcp-server-sampling-bridge.test.ts +12 -11
- package/src/scenarios/mcp-server-tool-roundtrip.test.ts +14 -13
- package/src/scenarios/mcp-server-untrusted-args.test.ts +14 -13
- package/src/scenarios/mcp-stateless-request.test.ts +11 -10
- package/src/scenarios/mcp-tool-roundtrip.test.ts +4 -3
- package/src/scenarios/mcp-toolcall-redaction.test.ts +4 -3
- package/src/scenarios/mcp-version-negotiation.test.ts +15 -14
- package/src/scenarios/media-url-inline-cap.test.ts +21 -19
- package/src/scenarios/memory-attribution-emits-on-write.test.ts +5 -5
- package/src/scenarios/memory-attribution-no-content.test.ts +4 -4
- package/src/scenarios/memory-attribution-replay-stable.test.ts +4 -3
- package/src/scenarios/memory-attribution-shape.test.ts +4 -3
- package/src/scenarios/memory-attribution-tenant-scoped.test.ts +3 -3
- package/src/scenarios/memory-capability-model-shape.test.ts +14 -15
- package/src/scenarios/memory-compaction-event-emitted.test.ts +7 -6
- package/src/scenarios/memory-compaction-provenance-tag.test.ts +3 -2
- package/src/scenarios/memory-compaction-sr1-carry-forward.test.ts +7 -6
- package/src/scenarios/memory-consolidation-idempotent.test.ts +5 -4
- package/src/scenarios/memory-consolidation-shape.test.ts +9 -11
- package/src/scenarios/memory-degraded-projection.test.ts +10 -9
- package/src/scenarios/memory-injection-budget.test.ts +15 -14
- package/src/scenarios/metric-emission.test.ts +4 -3
- package/src/scenarios/model-capability-insufficient.test.ts +19 -17
- package/src/scenarios/model-capability-substituted.test.ts +20 -18
- package/src/scenarios/multi-agent-confidence-escalation.test.ts +12 -11
- package/src/scenarios/multi-agent-handoff-state-machine.test.ts +10 -9
- package/src/scenarios/multi-agent-memory-lifecycle.test.ts +11 -10
- package/src/scenarios/multi-node-ordering.test.ts +4 -3
- package/src/scenarios/multi-party-conversation-behavioral.test.ts +6 -6
- package/src/scenarios/multi-party-conversation-shape.test.ts +21 -23
- package/src/scenarios/multi-region-idempotency-behavior.test.ts +16 -15
- package/src/scenarios/multi-region-idempotency.test.ts +6 -5
- package/src/scenarios/node-failed-payload-shape.test.ts +5 -4
- package/src/scenarios/node-module-required-capabilities-shape.test.ts +10 -8
- package/src/scenarios/oauth-authorization-code-roundtrip.test.ts +5 -4
- package/src/scenarios/oauth-capability-shape.test.ts +5 -4
- package/src/scenarios/oauth-connector-redaction.test.ts +4 -3
- package/src/scenarios/openapi-resolved-paths.test.ts +6 -5
- package/src/scenarios/orchestratorConservativePath.test.ts +2 -1
- package/src/scenarios/orchestratorDispatch.test.ts +2 -1
- package/src/scenarios/orchestratorTermination.test.ts +2 -1
- package/src/scenarios/org-position-no-authority-escalation.test.ts +2 -2
- package/src/scenarios/otel-collector-canary-inspection.test.ts +8 -7
- package/src/scenarios/otel-emission-grpc.test.ts +3 -2
- package/src/scenarios/otel-emission.test.ts +6 -5
- package/src/scenarios/otel-trace-propagation-subworkflow.test.ts +7 -6
- package/src/scenarios/otel-trace-propagation.test.ts +2 -1
- package/src/scenarios/owner-subject-echo.test.ts +13 -12
- package/src/scenarios/owner-subject-shape.test.ts +11 -10
- package/src/scenarios/pack-manifest-extension-opacity.test.ts +8 -8
- package/src/scenarios/pack-registry-isolation.test.ts +4 -3
- package/src/scenarios/pack-registry-publish.test.ts +65 -63
- package/src/scenarios/pack-registry.test.ts +38 -36
- package/src/scenarios/pause-resume.test.ts +18 -16
- package/src/scenarios/policies.test.ts +20 -18
- package/src/scenarios/production-backpressure.test.ts +18 -16
- package/src/scenarios/production-retention-expiry.test.ts +8 -6
- package/src/scenarios/profile-discovery-core-alias.test.ts +13 -11
- package/src/scenarios/profileDerivation.test.ts +34 -33
- package/src/scenarios/prompt-all-four-kinds-events.test.ts +11 -9
- package/src/scenarios/prompt-composed-secret-redaction.test.ts +11 -9
- package/src/scenarios/prompt-composed-trust-marker.test.ts +10 -8
- package/src/scenarios/prompt-end-to-end-events.test.ts +17 -15
- package/src/scenarios/prompt-list-and-fetch.test.ts +16 -14
- package/src/scenarios/prompt-mutable-lifecycle.test.ts +13 -11
- package/src/scenarios/prompt-mutation-workspace-membership-enforced.test.ts +6 -4
- package/src/scenarios/prompt-pack-install.test.ts +14 -12
- package/src/scenarios/prompt-prefix-cache.test.ts +19 -17
- package/src/scenarios/prompt-read-workspace-membership-enforced.test.ts +11 -9
- package/src/scenarios/prompt-render-deterministic.test.ts +19 -17
- package/src/scenarios/prompt-resolution-chain-agent-intrinsic.test.ts +7 -5
- package/src/scenarios/prompt-resolution-chain-event.test.ts +12 -10
- package/src/scenarios/prompt-resolution-chain-fallback-cascade.test.ts +10 -8
- package/src/scenarios/prompt-resolution-chain-node-wins.test.ts +8 -6
- package/src/scenarios/prompt-template-shape.test.ts +30 -28
- package/src/scenarios/proposal-reviewable-learning.test.ts +19 -19
- package/src/scenarios/protocol-versions-array.test.ts +10 -9
- package/src/scenarios/provider-usage.test.ts +17 -15
- package/src/scenarios/providerPolicyEnforcement.test.ts +16 -14
- package/src/scenarios/purpose-propagation.test.ts +23 -21
- package/src/scenarios/queue-ack-nack-dlq.test.ts +12 -10
- package/src/scenarios/queue-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/queue-publish-consume-roundtrip.test.ts +10 -8
- package/src/scenarios/rate-limit-envelope.test.ts +6 -5
- package/src/scenarios/redaction.test.ts +13 -11
- package/src/scenarios/redactionAdversarial.test.ts +11 -9
- package/src/scenarios/registry-public.test.ts +12 -11
- package/src/scenarios/replay-divergence-at-refusal.test.ts +16 -15
- package/src/scenarios/replay-fanout-suppression.test.ts +16 -15
- package/src/scenarios/replay-fork-arbitrary.test.ts +24 -23
- package/src/scenarios/replay-fork.test.ts +20 -19
- package/src/scenarios/replay-llm-cache-key-portable.test.ts +11 -9
- package/src/scenarios/replay-llm-cache-key.test.ts +22 -21
- package/src/scenarios/replay-observable-sequence-determinism.test.ts +4 -3
- package/src/scenarios/replay-retention-expiry.test.ts +11 -9
- package/src/scenarios/replay-side-effect-suppression.test.ts +21 -19
- package/src/scenarios/replayDeterminism.test.ts +13 -12
- package/src/scenarios/requirement-ledger.test.ts +15 -14
- package/src/scenarios/restart-during-run.test.ts +3 -2
- package/src/scenarios/route-coverage.test.ts +20 -19
- package/src/scenarios/run-diff.test.ts +22 -20
- package/src/scenarios/run-execution-bounds-shape.test.ts +14 -12
- package/src/scenarios/run-transport-economy.test.ts +13 -11
- package/src/scenarios/runner-ledger.test.ts +17 -16
- package/src/scenarios/runs-lifecycle.test.ts +7 -6
- package/src/scenarios/runtime-capabilities.test.ts +12 -10
- package/src/scenarios/runtime-requires-install-gate.test.ts +8 -8
- package/src/scenarios/runtime-requires-shape.test.ts +7 -6
- package/src/scenarios/safefetch-behavior.test.ts +22 -21
- package/src/scenarios/safefetch-live-audit.test.ts +14 -13
- package/src/scenarios/sandbox-memory-cap.test.ts +2 -1
- package/src/scenarios/sandbox-mvp-behavior.test.ts +24 -23
- package/src/scenarios/sandbox-no-host-fs-escape.test.ts +3 -2
- package/src/scenarios/sandbox-timeout-cap.test.ts +2 -1
- package/src/scenarios/sandbox-wasm-isolation.test.ts +14 -15
- package/src/scenarios/sandbox-wasm-timeout.test.ts +5 -6
- package/src/scenarios/scheduling-capability-shape.test.ts +8 -6
- package/src/scenarios/scheduling-cron-fires-once.test.ts +8 -6
- package/src/scenarios/search-bm25-roundtrip.test.ts +8 -6
- package/src/scenarios/secret-leakage-otel-attribute.test.ts +22 -20
- package/src/scenarios/self-hosted-runner.test.ts +26 -24
- package/src/scenarios/semantic-digest-vectors.test.ts +8 -7
- package/src/scenarios/speech-synthesis-roundtrip.test.ts +9 -7
- package/src/scenarios/speech-synthesis-unadvertised.test.ts +5 -3
- package/src/scenarios/sql-injection-rejection.test.ts +8 -6
- package/src/scenarios/sql-transaction-atomicity.test.ts +10 -8
- package/src/scenarios/staleClaim.test.ts +5 -4
- package/src/scenarios/stream-modes-buffer.test.ts +8 -7
- package/src/scenarios/stream-modes-mixed.test.ts +10 -9
- package/src/scenarios/stream-modes.test.ts +12 -11
- package/src/scenarios/stream-subscribe-from-beginning.test.ts +11 -9
- package/src/scenarios/stream-text-fixture.test.ts +14 -13
- package/src/scenarios/streamReconnect.test.ts +13 -11
- package/src/scenarios/strict-behavior-gate.test.ts +9 -8
- package/src/scenarios/subrun-approval-fail-closed.test.ts +2 -2
- package/src/scenarios/subrun-approval-gate.test.ts +3 -3
- package/src/scenarios/subrun-attestation-shape.test.ts +2 -2
- package/src/scenarios/subrun-checksum-stable.test.ts +4 -4
- package/src/scenarios/subworkflow-input-mapping.test.ts +16 -14
- package/src/scenarios/subworkflow.test.ts +10 -9
- package/src/scenarios/table-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/table-cursor-pagination.test.ts +8 -6
- package/src/scenarios/table-schema-enforcement.test.ts +7 -5
- package/src/scenarios/test-seam-unauthenticated.test.ts +2 -2
- package/src/scenarios/tool-catalog-compact-projection.test.ts +10 -8
- package/src/scenarios/tool-catalog-projection.test.ts +11 -9
- package/src/scenarios/tool-descriptor-shape.test.ts +14 -15
- package/src/scenarios/tool-hooks-authorization-fail-closed.test.ts +3 -3
- package/src/scenarios/tool-hooks-content-free.test.ts +4 -4
- package/src/scenarios/tool-hooks-failure-honesty.test.ts +4 -4
- package/src/scenarios/tool-hooks-rate-limit.test.ts +2 -2
- package/src/scenarios/tool-hooks-secret-redaction.test.ts +2 -2
- package/src/scenarios/tool-hooks-shape.test.ts +3 -3
- package/src/scenarios/tool-session-lifecycle.test.ts +14 -13
- package/src/scenarios/trigger-bridge-delivery.test.ts +14 -14
- package/src/scenarios/trigger-bridge-shape.test.ts +19 -20
- package/src/scenarios/trigger-ingestion.test.ts +19 -17
- package/src/scenarios/trigger-stream-cdc-sources.test.ts +16 -14
- package/src/scenarios/vector-knn-roundtrip.test.ts +9 -7
- package/src/scenarios/verifier-gating.test.ts +8 -6
- package/src/scenarios/version-fold.test.ts +9 -8
- package/src/scenarios/version-negotiation.test.ts +12 -11
- package/src/scenarios/voice-bargein-no-partial-leak.test.ts +8 -6
- package/src/scenarios/voice-event-payloads-shape.test.ts +10 -11
- package/src/scenarios/voice-interim-not-durable.test.ts +7 -5
- package/src/scenarios/voice-streamref-tenant-bound.test.ts +5 -3
- package/src/scenarios/voice-synthesis-streaming.test.ts +11 -9
- package/src/scenarios/voice-transcription-streaming.test.ts +8 -6
- package/src/scenarios/voice-transcription-unadvertised.test.ts +5 -3
- package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +3 -2
- package/src/scenarios/wasm-pack-invoke-completed.test.ts +3 -2
- package/src/scenarios/wasm-pack-invoke-suspended.test.ts +3 -2
- package/src/scenarios/wasm-pack-load.test.ts +4 -3
- package/src/scenarios/wasm-pack-memory-cap.test.ts +5 -4
- package/src/scenarios/wasm-pack-replay-determinism.test.ts +2 -1
- package/src/scenarios/webhook-negative.test.ts +11 -9
- package/src/scenarios/webhook-receiver-adversarial.test.ts +8 -7
- package/src/scenarios/webhook-sig-algorithm.test.ts +5 -3
- package/src/scenarios/webhook-signed-delivery.test.ts +16 -15
- package/src/scenarios/webhook-tenant-isolation.test.ts +16 -14
- package/src/scenarios/workflow-chain-deferred-parameters.test.ts +28 -30
- package/src/scenarios/workflow-chain-expansion.test.ts +21 -20
- package/src/scenarios/workflow-chain-host-expansion.test.ts +21 -20
- package/src/scenarios/workflow-chain-pack-manifest-validation.test.ts +14 -12
- package/src/scenarios/workflow-chain-pack-signature-verification.test.ts +5 -4
- package/src/scenarios/workflow-chain-unresolvable-typeid.test.ts +9 -8
- package/src/scenarios/workflow-primary-output-annotation.test.ts +7 -6
- package/src/scenarios/workflow-variable-format.test.ts +24 -24
- package/src/scenarios/workload-identity-behavior.test.ts +15 -13
- package/src/scenarios/workload-identity-chain-bounds.test.ts +18 -17
- package/src/scenarios/workspace-behavior.test.ts +19 -18
- package/src/scenarios/workspace-capability-shape.test.ts +3 -2
- package/src/scenarios/workspace-cross-tenant-isolation-blackbox.test.ts +6 -5
- package/src/scenarios/workspace-cross-tenant-isolation.test.ts +7 -6
- package/src/scenarios/x-openwop-form-pack-manifest.test.ts +8 -7
- package/src/setup.ts +4 -2
- package/vitest.config.ts +5 -1
- package/api/.redocly.lint-ignore.yaml +0 -22
- package/api/asyncapi.yaml +0 -834
- package/api/grpc/openwop.proto +0 -251
- package/api/openapi.yaml +0 -2693
- package/api/redocly.yaml +0 -23
- package/schemas/README.md +0 -144
- package/schemas/a2a-task-state.schema.json +0 -78
- package/schemas/a2ui-surface-delta-frame.schema.json +0 -48
- package/schemas/agent-deployment-transition.schema.json +0 -49
- package/schemas/agent-deployment.schema.json +0 -54
- package/schemas/agent-eval-suite.schema.json +0 -140
- package/schemas/agent-inventory-response.schema.json +0 -115
- package/schemas/agent-manifest.schema.json +0 -156
- package/schemas/agent-org-chart.schema.json +0 -82
- package/schemas/agent-ref.schema.json +0 -63
- package/schemas/agent-roster-entry.schema.json +0 -81
- package/schemas/agent-roster-response.schema.json +0 -21
- package/schemas/ai-envelope.schema.json +0 -134
- package/schemas/annotation-create.schema.json +0 -37
- package/schemas/annotation.schema.json +0 -56
- package/schemas/artifact-type-pack-manifest.schema.json +0 -244
- package/schemas/audit-verify-result.schema.json +0 -90
- package/schemas/budget-policy.schema.json +0 -18
- package/schemas/capabilities.schema.json +0 -4124
- package/schemas/certification-bundle-v2.schema.json +0 -118
- package/schemas/channel-presence-payload.schema.json +0 -41
- package/schemas/channel-written-payload.schema.json +0 -55
- package/schemas/chat-card-pack-manifest.schema.json +0 -253
- package/schemas/compact-tool-descriptor.schema.json +0 -51
- package/schemas/compensation-policy.schema.json +0 -64
- package/schemas/conformance-certification-bundle.schema.json +0 -86
- package/schemas/connection-pack-manifest.schema.json +0 -353
- package/schemas/conversation-event.schema.json +0 -168
- package/schemas/conversation-turn.schema.json +0 -117
- package/schemas/core-conformance-mock-agent-config.schema.json +0 -152
- package/schemas/credential-provenance.schema.json +0 -18
- package/schemas/credential-reference.schema.json +0 -21
- package/schemas/debug-bundle.schema.json +0 -196
- package/schemas/dispatch-config.schema.json +0 -112
- package/schemas/envelopes/clarification.request.schema.json +0 -52
- package/schemas/envelopes/error.schema.json +0 -30
- package/schemas/envelopes/media.audio.schema.json +0 -38
- package/schemas/envelopes/media.file.schema.json +0 -37
- package/schemas/envelopes/media.image.schema.json +0 -33
- package/schemas/envelopes/schema.request.schema.json +0 -26
- package/schemas/envelopes/schema.response.schema.json +0 -22
- package/schemas/envelopes/ui.a2ui-surface.schema.json +0 -154
- package/schemas/error-envelope.schema.json +0 -25
- package/schemas/eval-summary.schema.json +0 -92
- package/schemas/export-bundle.schema.json +0 -66
- package/schemas/form-content-pack-manifest.schema.json +0 -243
- package/schemas/frontend-plugin-manifest.schema.json +0 -109
- package/schemas/goal.schema.json +0 -104
- package/schemas/heartbeat-evaluated.schema.json +0 -14
- package/schemas/heartbeat-state-changed.schema.json +0 -14
- package/schemas/localized-content-language-settings.schema.json +0 -26
- package/schemas/localized-content-page-response.schema.json +0 -60
- package/schemas/localized-content-page.schema.json +0 -62
- package/schemas/localized-content-section.schema.json +0 -51
- package/schemas/memory-entry.schema.json +0 -36
- package/schemas/memory-list-options.schema.json +0 -37
- package/schemas/node-pack-manifest.schema.json +0 -654
- package/schemas/orchestrator-decision.schema.json +0 -90
- package/schemas/org-chart-responsibility-view.schema.json +0 -26
- package/schemas/pack-lockfile.schema.json +0 -108
- package/schemas/prompt-kind.schema.json +0 -8
- package/schemas/prompt-pack-manifest.schema.json +0 -117
- package/schemas/prompt-ref.schema.json +0 -40
- package/schemas/prompt-template.schema.json +0 -149
- package/schemas/proposal.schema.json +0 -84
- package/schemas/registry-version-manifest.schema.json +0 -208
- package/schemas/residency.schema.json +0 -16
- package/schemas/run-ancestry-response.schema.json +0 -54
- package/schemas/run-diff-response.schema.json +0 -64
- package/schemas/run-event-payloads.schema.json +0 -4452
- package/schemas/run-event.schema.json +0 -211
- package/schemas/run-options.schema.json +0 -80
- package/schemas/run-orchestrator-decided-event.schema.json +0 -20
- package/schemas/run-snapshot.schema.json +0 -148
- package/schemas/security-advisory.schema.json +0 -109
- package/schemas/self-hosted-runner-dispatch-frame.schema.json +0 -79
- package/schemas/self-hosted-runner-registration.schema.json +0 -53
- package/schemas/self-hosted-runner-result-frame.schema.json +0 -38
- package/schemas/subject.schema.json +0 -56
- package/schemas/suspend-request.schema.json +0 -207
- package/schemas/tool-descriptor.schema.json +0 -63
- package/schemas/trigger-event.schema.json +0 -222
- package/schemas/trigger-subscription-registration.schema.json +0 -67
- package/schemas/trigger-subscription.schema.json +0 -26
- package/schemas/ui-plugin-message.schema.json +0 -90
- package/schemas/workflow-chain-pack-manifest.schema.json +0 -615
- package/schemas/workflow-definition.schema.json +0 -850
- package/schemas/workload-identity.schema.json +0 -79
- package/schemas/workspace-file-create.schema.json +0 -20
- package/schemas/workspace-file.schema.json +0 -39
|
@@ -47,6 +47,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
47
47
|
import { driver } from '../lib/driver.js';
|
|
48
48
|
import { recordRequirement } from '../lib/requirement-ledger.js';
|
|
49
49
|
import { requirementIdForScenario } from '../lib/requirement-registry.js';
|
|
50
|
+
import { req } from '../lib/requirement-ids.js';
|
|
51
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
50
52
|
|
|
51
53
|
/** RFC 0148 §A requirement id this file's floor row is recorded under. */
|
|
52
54
|
const REQUIREMENT_ID = requirementIdForScenario('pack-registry.test.ts');
|
|
@@ -112,7 +114,7 @@ async function probeRegistry(): Promise<RegistryProbeResult> {
|
|
|
112
114
|
describe('pack-registry: read-endpoint shape contracts', () => {
|
|
113
115
|
it('GET /v1/packs/-/search returns an OpenWOP-shaped response (or registry is absent)', async () => {
|
|
114
116
|
const probe = await probeRegistry();
|
|
115
|
-
if (!probe.registryPresent) return; // Host doesn't ship a registry.
|
|
117
|
+
if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (Host doesn\'t ship a registry.) (seam, prior step, or fixture unavailable)'); // Host doesn't ship a registry.
|
|
116
118
|
|
|
117
119
|
const res = await driver.get('/v1/packs/-/search?q=', { authenticated: false });
|
|
118
120
|
// Spec doesn't pin empty-q behavior — both 400 (validation) and 200
|
|
@@ -120,18 +122,18 @@ describe('pack-registry: read-endpoint shape contracts', () => {
|
|
|
120
122
|
// JSON-shaped.
|
|
121
123
|
expect([200, 400]).toContain(res.status);
|
|
122
124
|
const body = res.json as Record<string, unknown> | undefined;
|
|
123
|
-
expect(body,
|
|
125
|
+
expect(body, req('openwop.it.pack-registry.get-v1-packs-search-returns-an-openwop-shaped-response-or-registry-is-absent',
|
|
124
126
|
'node-packs.md §"GET /v1/packs/-/search"',
|
|
125
127
|
'response MUST be JSON',
|
|
126
128
|
)).toBeDefined();
|
|
127
129
|
|
|
128
130
|
if (res.status === 200) {
|
|
129
|
-
expect(Array.isArray(body?.results),
|
|
131
|
+
expect(Array.isArray(body?.results), req('openwop.it.pack-registry.get-v1-packs-search-returns-an-openwop-shaped-response-or-registry-is-absent',
|
|
130
132
|
'node-packs.md §"GET /v1/packs/-/search"',
|
|
131
133
|
'search response MUST carry a `results` array',
|
|
132
134
|
)).toBe(true);
|
|
133
135
|
} else {
|
|
134
|
-
expect(typeof body?.error,
|
|
136
|
+
expect(typeof body?.error, req('openwop.it.pack-registry.get-v1-packs-search-returns-an-openwop-shaped-response-or-registry-is-absent',
|
|
135
137
|
'rest-endpoints.md §"Error envelope"',
|
|
136
138
|
'400 response MUST carry a string `error` field',
|
|
137
139
|
)).toBe('string');
|
|
@@ -140,24 +142,24 @@ describe('pack-registry: read-endpoint shape contracts', () => {
|
|
|
140
142
|
|
|
141
143
|
it('GET /v1/packs/{nonexistent} returns 404 pack_not_found with openwop error envelope', async () => {
|
|
142
144
|
const probe = await probeRegistry();
|
|
143
|
-
if (!probe.registryPresent) return;
|
|
145
|
+
if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (seam, prior step, or fixture unavailable)');
|
|
144
146
|
|
|
145
147
|
const res = await driver.get(`/v1/packs/${encodeURIComponent(NONEXISTENT_PACK)}`, {
|
|
146
148
|
authenticated: false,
|
|
147
149
|
});
|
|
148
|
-
expect(res.status,
|
|
150
|
+
expect(res.status, req('openwop.it.pack-registry.get-v1-packs-nonexistent-returns-404-pack-not-found-with-openwop-error-envelope',
|
|
149
151
|
'node-packs.md §"GET /v1/packs/{name}"',
|
|
150
152
|
'unknown pack name MUST return 404',
|
|
151
153
|
)).toBe(404);
|
|
152
154
|
|
|
153
155
|
const body = res.json as { error?: unknown } | undefined;
|
|
154
|
-
expect(typeof body?.error,
|
|
156
|
+
expect(typeof body?.error, req('openwop.it.pack-registry.get-v1-packs-nonexistent-returns-404-pack-not-found-with-openwop-error-envelope',
|
|
155
157
|
'rest-endpoints.md §"Error envelope"',
|
|
156
158
|
'404 response MUST carry a string `error` field',
|
|
157
159
|
)).toBe('string');
|
|
158
160
|
// Reference impl emits `pack_not_found`; spec doesn't pin the exact
|
|
159
161
|
// string — but the prefix `pack_` is the documented family.
|
|
160
|
-
expect((body?.error as string).length,
|
|
162
|
+
expect((body?.error as string).length, req('openwop.it.pack-registry.get-v1-packs-nonexistent-returns-404-pack-not-found-with-openwop-error-envelope',
|
|
161
163
|
'rest-endpoints.md §"Error envelope"',
|
|
162
164
|
'`error` field MUST be non-empty',
|
|
163
165
|
)).toBeGreaterThan(0);
|
|
@@ -165,19 +167,19 @@ describe('pack-registry: read-endpoint shape contracts', () => {
|
|
|
165
167
|
|
|
166
168
|
it('GET /v1/packs/{name}/-/{version}.json returns 404 for nonexistent version', async () => {
|
|
167
169
|
const probe = await probeRegistry();
|
|
168
|
-
if (!probe.registryPresent) return;
|
|
170
|
+
if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (seam, prior step, or fixture unavailable)');
|
|
169
171
|
|
|
170
172
|
const res = await driver.get(
|
|
171
173
|
`/v1/packs/${encodeURIComponent(NONEXISTENT_PACK)}/-/${NONEXISTENT_VERSION}.json`,
|
|
172
174
|
{ authenticated: false },
|
|
173
175
|
);
|
|
174
|
-
expect(res.status,
|
|
176
|
+
expect(res.status, req('openwop.it.pack-registry.get-v1-packs-name-version-json-returns-404-for-nonexistent-version',
|
|
175
177
|
'node-packs.md §"GET /v1/packs/{name}/-/{version}.json"',
|
|
176
178
|
'unknown (name, version) MUST return 404',
|
|
177
179
|
)).toBe(404);
|
|
178
180
|
|
|
179
181
|
const body = res.json as { error?: unknown } | undefined;
|
|
180
|
-
expect(typeof body?.error,
|
|
182
|
+
expect(typeof body?.error, req('openwop.it.pack-registry.get-v1-packs-name-version-json-returns-404-for-nonexistent-version',
|
|
181
183
|
'rest-endpoints.md §"Error envelope"',
|
|
182
184
|
'404 response MUST carry a string `error` field',
|
|
183
185
|
)).toBe('string');
|
|
@@ -185,7 +187,7 @@ describe('pack-registry: read-endpoint shape contracts', () => {
|
|
|
185
187
|
|
|
186
188
|
it('GET /v1/packs/{name}/-/{version}.sig returns 404 signature_not_available for nonexistent version', async () => {
|
|
187
189
|
const probe = await probeRegistry();
|
|
188
|
-
if (!probe.registryPresent) return;
|
|
190
|
+
if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (seam, prior step, or fixture unavailable)');
|
|
189
191
|
|
|
190
192
|
const res = await driver.get(
|
|
191
193
|
`/v1/packs/${encodeURIComponent(NONEXISTENT_PACK)}/-/${NONEXISTENT_VERSION}.sig`,
|
|
@@ -196,13 +198,13 @@ describe('pack-registry: read-endpoint shape contracts', () => {
|
|
|
196
198
|
// intentionally indistinguishable. A 302 redirect to a storage-
|
|
197
199
|
// backed signed URL is also spec-allowed for VALID signatures —
|
|
198
200
|
// for a NONEXISTENT pack, only 404 is correct.
|
|
199
|
-
expect(res.status,
|
|
201
|
+
expect(res.status, req('openwop.it.pack-registry.get-v1-packs-name-version-sig-returns-404-signature-not-available-for-nonexisten',
|
|
200
202
|
'node-packs.md §"GET /v1/packs/{name}/-/{version}.sig"',
|
|
201
203
|
'nonexistent (name, version) MUST return 404 signature_not_available',
|
|
202
204
|
)).toBe(404);
|
|
203
205
|
|
|
204
206
|
const body = res.json as { error?: unknown } | undefined;
|
|
205
|
-
expect(typeof body?.error,
|
|
207
|
+
expect(typeof body?.error, req('openwop.it.pack-registry.get-v1-packs-name-version-sig-returns-404-signature-not-available-for-nonexisten',
|
|
206
208
|
'node-packs.md §"GET /v1/packs/{name}/-/{version}.sig"',
|
|
207
209
|
'404 response MUST carry a string `error` field — `signature_not_available` is the canonical code',
|
|
208
210
|
)).toBe('string');
|
|
@@ -211,19 +213,19 @@ describe('pack-registry: read-endpoint shape contracts', () => {
|
|
|
211
213
|
|
|
212
214
|
it('GET /v1/packs/{bad-name}/-/{version}.json returns 400 invalid_pack_name', async () => {
|
|
213
215
|
const probe = await probeRegistry();
|
|
214
|
-
if (!probe.registryPresent) return;
|
|
216
|
+
if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (seam, prior step, or fixture unavailable)');
|
|
215
217
|
|
|
216
218
|
// Single-segment name violates the reverse-DNS pattern.
|
|
217
219
|
const res = await driver.get('/v1/packs/not-reverse-dns/-/1.0.json', {
|
|
218
220
|
authenticated: false,
|
|
219
221
|
});
|
|
220
|
-
expect(res.status,
|
|
222
|
+
expect(res.status, req('openwop.it.pack-registry.get-v1-packs-bad-name-version-json-returns-400-invalid-pack-name',
|
|
221
223
|
'node-packs.md §"GET /v1/packs/{name}/-/{version}.json"',
|
|
222
224
|
'malformed pack name MUST return 400 invalid_pack_name',
|
|
223
225
|
)).toBe(400);
|
|
224
226
|
|
|
225
227
|
const body = res.json as { error?: unknown } | undefined;
|
|
226
|
-
expect(typeof body?.error,
|
|
228
|
+
expect(typeof body?.error, req('openwop.it.pack-registry.get-v1-packs-bad-name-version-json-returns-400-invalid-pack-name',
|
|
227
229
|
'rest-endpoints.md §"Error envelope"',
|
|
228
230
|
'400 response MUST carry a string `error` field',
|
|
229
231
|
)).toBe('string');
|
|
@@ -231,14 +233,14 @@ describe('pack-registry: read-endpoint shape contracts', () => {
|
|
|
231
233
|
|
|
232
234
|
it('GET /v1/packs/{name}/-/{bad-version}.json returns 400 invalid_version', async () => {
|
|
233
235
|
const probe = await probeRegistry();
|
|
234
|
-
if (!probe.registryPresent) return;
|
|
236
|
+
if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (seam, prior step, or fixture unavailable)');
|
|
235
237
|
|
|
236
238
|
// `not-a-version` violates semver.
|
|
237
239
|
const res = await driver.get(
|
|
238
240
|
`/v1/packs/${encodeURIComponent(NONEXISTENT_PACK)}/-/not-a-version.json`,
|
|
239
241
|
{ authenticated: false },
|
|
240
242
|
);
|
|
241
|
-
expect(res.status,
|
|
243
|
+
expect(res.status, req('openwop.it.pack-registry.get-v1-packs-name-bad-version-json-returns-400-invalid-version',
|
|
242
244
|
'node-packs.md §"GET /v1/packs/{name}/-/{version}.json"',
|
|
243
245
|
'non-semver version MUST return 400 invalid_version',
|
|
244
246
|
)).toBe(400);
|
|
@@ -251,20 +253,20 @@ describe('pack-registry: read-endpoint shape contracts', () => {
|
|
|
251
253
|
describe('pack-registry: catalog response shape (when populated)', () => {
|
|
252
254
|
it('GET /v1/packs/{name} catalog records validate against the documented shape', async () => {
|
|
253
255
|
const probe = await probeRegistry();
|
|
254
|
-
if (!probe.registryPresent) return;
|
|
256
|
+
if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (seam, prior step, or fixture unavailable)');
|
|
255
257
|
|
|
256
258
|
// Probe search for a real entry. If the catalog is empty, skip.
|
|
257
259
|
const search = await driver.get('/v1/packs/-/search?q=', { authenticated: false });
|
|
258
|
-
if (search.status !== 200) return;
|
|
260
|
+
if (search.status !== 200) return softSkip('blocked', 'precondition not met — `search.status !== 200` returned early (seam, prior step, or fixture unavailable)');
|
|
259
261
|
const results = (search.json as { results?: Array<{ name?: unknown }> } | undefined)?.results;
|
|
260
|
-
if (!Array.isArray(results) || results.length === 0) return;
|
|
262
|
+
if (!Array.isArray(results) || results.length === 0) return softSkip('blocked', 'precondition not met — `!Array.isArray(results) || results.length === 0` returned early (seam, prior step, or fixture unavailable)');
|
|
261
263
|
|
|
262
264
|
// Walk up to 3 results and assert their catalog records are well-shaped.
|
|
263
265
|
const sample = results.slice(0, 3);
|
|
264
266
|
for (const entry of sample) {
|
|
265
267
|
const name = entry.name;
|
|
266
268
|
if (typeof name !== 'string') continue;
|
|
267
|
-
expect(name,
|
|
269
|
+
expect(name, req('openwop.it.pack-registry.get-v1-packs-name-catalog-records-validate-against-the-documented-shape',
|
|
268
270
|
'node-packs.md §"Naming"',
|
|
269
271
|
`search result name "${name}" MUST match reverse-DNS pattern`,
|
|
270
272
|
)).toMatch(PACK_NAME_RE);
|
|
@@ -272,7 +274,7 @@ describe('pack-registry: catalog response shape (when populated)', () => {
|
|
|
272
274
|
const cat = await driver.get(`/v1/packs/${encodeURIComponent(name)}`, {
|
|
273
275
|
authenticated: false,
|
|
274
276
|
});
|
|
275
|
-
expect(cat.status,
|
|
277
|
+
expect(cat.status, req('openwop.it.pack-registry.get-v1-packs-name-catalog-records-validate-against-the-documented-shape',
|
|
276
278
|
'node-packs.md §"GET /v1/packs/{name}"',
|
|
277
279
|
`catalog read for known pack "${name}" MUST return 200`,
|
|
278
280
|
)).toBe(200);
|
|
@@ -282,11 +284,11 @@ describe('pack-registry: catalog response shape (when populated)', () => {
|
|
|
282
284
|
versions?: Record<string, unknown>;
|
|
283
285
|
'dist-tags'?: { latest?: unknown };
|
|
284
286
|
} | undefined;
|
|
285
|
-
expect(body?.name,
|
|
287
|
+
expect(body?.name, req('openwop.it.pack-registry.get-v1-packs-name-catalog-records-validate-against-the-documented-shape',
|
|
286
288
|
'node-packs.md §"GET /v1/packs/{name}"',
|
|
287
289
|
'catalog response MUST echo the requested pack name',
|
|
288
290
|
)).toBe(name);
|
|
289
|
-
expect(typeof body?.versions,
|
|
291
|
+
expect(typeof body?.versions, req('openwop.it.pack-registry.get-v1-packs-name-catalog-records-validate-against-the-documented-shape',
|
|
290
292
|
'node-packs.md §"GET /v1/packs/{name}"',
|
|
291
293
|
'catalog response MUST carry a `versions` map',
|
|
292
294
|
)).toBe('object');
|
|
@@ -294,7 +296,7 @@ describe('pack-registry: catalog response shape (when populated)', () => {
|
|
|
294
296
|
// Every version key MUST be valid semver per the spec.
|
|
295
297
|
const versionKeys = Object.keys(body?.versions ?? {});
|
|
296
298
|
for (const v of versionKeys) {
|
|
297
|
-
expect(v,
|
|
299
|
+
expect(v, req('openwop.it.pack-registry.get-v1-packs-name-catalog-records-validate-against-the-documented-shape',
|
|
298
300
|
'node-packs.md §"Versioning"',
|
|
299
301
|
`version key "${v}" MUST match SemVer 2.0.0`,
|
|
300
302
|
)).toMatch(SEMVER_RE);
|
|
@@ -306,16 +308,16 @@ describe('pack-registry: catalog response shape (when populated)', () => {
|
|
|
306
308
|
describe('pack-registry: keychain shape (when present)', () => {
|
|
307
309
|
it('GET /v1/packs/{name}/-/keychain returns well-formed key entries when present', async () => {
|
|
308
310
|
const probe = await probeRegistry();
|
|
309
|
-
if (!probe.registryPresent) return;
|
|
311
|
+
if (!probe.registryPresent) return softSkip('blocked', 'precondition not met — `!probe.registryPresent` returned early (seam, prior step, or fixture unavailable)');
|
|
310
312
|
|
|
311
313
|
// Probe for any real pack to fetch its keychain. Skip if no packs.
|
|
312
314
|
const search = await driver.get('/v1/packs/-/search?q=', { authenticated: false });
|
|
313
|
-
if (search.status !== 200) return;
|
|
315
|
+
if (search.status !== 200) return softSkip('blocked', 'precondition not met — `search.status !== 200` returned early (seam, prior step, or fixture unavailable)');
|
|
314
316
|
const results = (search.json as { results?: Array<{ name?: unknown }> } | undefined)?.results;
|
|
315
|
-
if (!Array.isArray(results) || results.length === 0) return;
|
|
317
|
+
if (!Array.isArray(results) || results.length === 0) return softSkip('blocked', 'precondition not met — `!Array.isArray(results) || results.length === 0` returned early (seam, prior step, or fixture unavailable)');
|
|
316
318
|
|
|
317
319
|
const first = results[0]?.name;
|
|
318
|
-
if (typeof first !== 'string') return;
|
|
320
|
+
if (typeof first !== 'string') return softSkip('blocked', 'precondition not met — `typeof first !== \'string\'` returned early (seam, prior step, or fixture unavailable)');
|
|
319
321
|
|
|
320
322
|
const res = await driver.get(`/v1/packs/${encodeURIComponent(first)}/-/keychain`, {
|
|
321
323
|
authenticated: false,
|
|
@@ -323,25 +325,25 @@ describe('pack-registry: keychain shape (when present)', () => {
|
|
|
323
325
|
// 200 with `{keys: []}` and 404 are both spec-allowed (keychain is
|
|
324
326
|
// optional; not every namespace publishes one).
|
|
325
327
|
expect([200, 404]).toContain(res.status);
|
|
326
|
-
if (res.status === 404) return;
|
|
328
|
+
if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
327
329
|
|
|
328
330
|
const body = res.json as { keys?: unknown; namespace?: unknown } | undefined;
|
|
329
|
-
expect(Array.isArray(body?.keys),
|
|
331
|
+
expect(Array.isArray(body?.keys), req('openwop.it.pack-registry.get-v1-packs-name-keychain-returns-well-formed-key-entries-when-present',
|
|
330
332
|
'registry-operations.md §"Signing keychain"',
|
|
331
333
|
'keychain response MUST carry a `keys` array',
|
|
332
334
|
)).toBe(true);
|
|
333
335
|
|
|
334
336
|
const keys = body?.keys as Array<Record<string, unknown>>;
|
|
335
337
|
for (const key of keys) {
|
|
336
|
-
expect(typeof key.kid,
|
|
338
|
+
expect(typeof key.kid, req('openwop.it.pack-registry.get-v1-packs-name-keychain-returns-well-formed-key-entries-when-present',
|
|
337
339
|
'registry-operations.md §"Signing keychain"',
|
|
338
340
|
'each key MUST have a string `kid`',
|
|
339
341
|
)).toBe('string');
|
|
340
|
-
expect(typeof key.algorithm,
|
|
342
|
+
expect(typeof key.algorithm, req('openwop.it.pack-registry.get-v1-packs-name-keychain-returns-well-formed-key-entries-when-present',
|
|
341
343
|
'registry-operations.md §"Signing keychain"',
|
|
342
344
|
'each key MUST have a string `algorithm`',
|
|
343
345
|
)).toBe('string');
|
|
344
|
-
expect(typeof key.publicKey,
|
|
346
|
+
expect(typeof key.publicKey, req('openwop.it.pack-registry.get-v1-packs-name-keychain-returns-well-formed-key-entries-when-present',
|
|
345
347
|
'registry-operations.md §"Signing keychain"',
|
|
346
348
|
'each key MUST have a string `publicKey`',
|
|
347
349
|
)).toBe('string');
|
|
@@ -22,6 +22,8 @@ import { driver } from '../lib/driver.js';
|
|
|
22
22
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
23
23
|
import { pollUntilStatus, pollUntilTerminal } from '../lib/polling.js';
|
|
24
24
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
25
|
+
import { req } from '../lib/requirement-ids.js';
|
|
26
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
25
27
|
|
|
26
28
|
const FIXTURE =
|
|
27
29
|
(isFixtureAdvertised('conformance-cancellable') && 'conformance-cancellable') ||
|
|
@@ -54,9 +56,9 @@ describe.skipIf(SKIP)('pause/resume: running → paused → running → terminal
|
|
|
54
56
|
await driver.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {
|
|
55
57
|
reason: 'conformance-cleanup',
|
|
56
58
|
});
|
|
57
|
-
return;
|
|
59
|
+
return softSkip('blocked', 'precondition not met — `pause.status === 404` returned early ([pause-resume] host returned 404 for :pause — endpoint not implemented; skipping rest) (seam, prior step, or fixture unavailable)');
|
|
58
60
|
}
|
|
59
|
-
expect(pause.status,
|
|
61
|
+
expect(pause.status, req('openwop.it.pause-resume.pause-transitions-to-paused-resume-returns-the-run-to-running',
|
|
60
62
|
'rest-endpoints.md POST /v1/runs/{runId}:pause',
|
|
61
63
|
':pause MUST return 202 on a pausable run',
|
|
62
64
|
)).toBe(202);
|
|
@@ -66,7 +68,7 @@ describe.skipIf(SKIP)('pause/resume: running → paused → running → terminal
|
|
|
66
68
|
const resume = await driver.post(`/v1/runs/${encodeURIComponent(runId)}:resume`, {
|
|
67
69
|
reason: 'conformance-test',
|
|
68
70
|
});
|
|
69
|
-
expect(resume.status,
|
|
71
|
+
expect(resume.status, req('openwop.it.pause-resume.pause-transitions-to-paused-resume-returns-the-run-to-running',
|
|
70
72
|
'rest-endpoints.md POST /v1/runs/{runId}:resume',
|
|
71
73
|
':resume MUST return 202 on a paused run',
|
|
72
74
|
)).toBe(202);
|
|
@@ -92,9 +94,9 @@ describe.skipIf(SKIP)('pause/resume: :resume on a non-paused run returns 409', (
|
|
|
92
94
|
await driver.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {
|
|
93
95
|
reason: 'conformance-cleanup',
|
|
94
96
|
});
|
|
95
|
-
return;
|
|
97
|
+
return softSkip('blocked', 'precondition not met — `resume.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
96
98
|
}
|
|
97
|
-
expect(resume.status,
|
|
99
|
+
expect(resume.status, req('openwop.it.pause-resume.resuming-a-running-not-paused-run-returns-409-with-details-runstatus',
|
|
98
100
|
'rest-endpoints.md POST /v1/runs/{runId}:resume',
|
|
99
101
|
':resume on a non-paused run MUST return 409',
|
|
100
102
|
)).toBe(409);
|
|
@@ -124,7 +126,7 @@ describe.skipIf(SKIP)('pause/resume: pause is idempotent when already paused', (
|
|
|
124
126
|
await driver.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {
|
|
125
127
|
reason: 'conformance-cleanup',
|
|
126
128
|
});
|
|
127
|
-
return;
|
|
129
|
+
return softSkip('blocked', 'precondition not met — `first.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
128
130
|
}
|
|
129
131
|
expect([200, 202]).toContain(first.status);
|
|
130
132
|
await pollUntilStatus(runId, 'paused', { timeoutMs: 10_000 });
|
|
@@ -135,7 +137,7 @@ describe.skipIf(SKIP)('pause/resume: pause is idempotent when already paused', (
|
|
|
135
137
|
const second = await driver.post(`/v1/runs/${encodeURIComponent(runId)}:pause`, {});
|
|
136
138
|
expect(
|
|
137
139
|
[200, 202].includes(second.status),
|
|
138
|
-
|
|
140
|
+
req('openwop.it.pause-resume.pause-on-an-already-paused-run-is-a-no-op-200-202-idempotent',
|
|
139
141
|
'rest-endpoints.md POST /v1/runs/{runId}:pause',
|
|
140
142
|
':pause on an already-paused run MUST be idempotent (200/202), not 409',
|
|
141
143
|
),
|
|
@@ -152,13 +154,13 @@ describe.skipIf(SKIP)('pause/resume: :pause on a terminal run returns 409', () =
|
|
|
152
154
|
const create = await driver.post('/v1/runs', {
|
|
153
155
|
workflowId: 'conformance-noop',
|
|
154
156
|
});
|
|
155
|
-
if (create.status !== 201) return; // conformance-noop not seeded; skip cleanly
|
|
157
|
+
if (create.status !== 201) return softSkip('blocked', 'precondition not met — `create.status !== 201` returned early (conformance-noop not seeded; skip cleanly) (seam, prior step, or fixture unavailable)'); // conformance-noop not seeded; skip cleanly
|
|
156
158
|
const runId = (create.json as { runId: string }).runId;
|
|
157
159
|
await pollUntilTerminal(runId, { timeoutMs: 10_000 });
|
|
158
160
|
|
|
159
161
|
const pause = await driver.post(`/v1/runs/${encodeURIComponent(runId)}:pause`, {});
|
|
160
|
-
if (pause.status === 404) return;
|
|
161
|
-
expect(pause.status,
|
|
162
|
+
if (pause.status === 404) return softSkip('blocked', 'precondition not met — `pause.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
163
|
+
expect(pause.status, req('openwop.it.pause-resume.pause-on-a-completed-cancelled-failed-run-must-return-409',
|
|
162
164
|
'rest-endpoints.md POST /v1/runs/{runId}:pause',
|
|
163
165
|
':pause on a terminal run MUST return 409',
|
|
164
166
|
)).toBe(409);
|
|
@@ -185,7 +187,7 @@ describe.skipIf(SKIP)('pause/resume: :pause-during-suspend race', () => {
|
|
|
185
187
|
console.warn(
|
|
186
188
|
'[pause-resume] conformance-approval not advertised; skipping :pause-during-suspend race subtest',
|
|
187
189
|
);
|
|
188
|
-
return;
|
|
190
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(\'conformance-approval\')` returned early ([pause-resume] conformance-approval not advertised; skipping :pause-during-suspend race subtest)');
|
|
189
191
|
}
|
|
190
192
|
const create = await driver.post('/v1/runs', { workflowId: 'conformance-approval' });
|
|
191
193
|
expect(create.status).toBe(201);
|
|
@@ -200,13 +202,13 @@ describe.skipIf(SKIP)('pause/resume: :pause-during-suspend race', () => {
|
|
|
200
202
|
await driver.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {
|
|
201
203
|
reason: 'conformance-cleanup',
|
|
202
204
|
});
|
|
203
|
-
return;
|
|
205
|
+
return softSkip('blocked', 'precondition not met — `pause.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
204
206
|
}
|
|
205
207
|
|
|
206
208
|
// Either rejection (preferred) or stacked-pause is OK; silent override is not.
|
|
207
209
|
if (pause.status === 409) {
|
|
208
210
|
const body = pause.json as { details?: { runStatus?: string } };
|
|
209
|
-
expect(body.details?.runStatus,
|
|
211
|
+
expect(body.details?.runStatus, req('openwop.it.pause-resume.pause-must-not-silently-override-an-active-interrupt-suspend',
|
|
210
212
|
'rest-endpoints.md POST /v1/runs/{runId}:pause',
|
|
211
213
|
':pause-during-suspend MUST surface the active waiting-* status in the conflict envelope',
|
|
212
214
|
)).toMatch(/^waiting-/);
|
|
@@ -218,7 +220,7 @@ describe.skipIf(SKIP)('pause/resume: :pause-during-suspend race', () => {
|
|
|
218
220
|
const status = (snap.json as { status: string }).status;
|
|
219
221
|
expect(
|
|
220
222
|
status === 'paused' || status.startsWith('waiting-'),
|
|
221
|
-
':pause-during-suspend MUST NOT silently discard the active interrupt',
|
|
223
|
+
req('openwop.it.pause-resume.pause-must-not-silently-override-an-active-interrupt-suspend', 'rest-endpoints.md POST /v1/runs/{runId}:pause', ':pause-during-suspend MUST NOT silently discard the active interrupt'),
|
|
222
224
|
).toBe(true);
|
|
223
225
|
}
|
|
224
226
|
|
|
@@ -241,7 +243,7 @@ describe.skipIf(SKIP)('pause/resume: drainPolicy discrimination per capabilities
|
|
|
241
243
|
if (drainPolicies.length === 0) {
|
|
242
244
|
// eslint-disable-next-line no-console
|
|
243
245
|
console.warn('[pause-resume] host advertises no drainPolicies; skipping policy-discrimination subtest');
|
|
244
|
-
return;
|
|
246
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `drainPolicies.length === 0` returned early ([pause-resume] host advertises no drainPolicies; skipping policy-discrimination subtest)');
|
|
245
247
|
}
|
|
246
248
|
|
|
247
249
|
for (const policy of drainPolicies) {
|
|
@@ -258,7 +260,7 @@ describe.skipIf(SKIP)('pause/resume: drainPolicy discrimination per capabilities
|
|
|
258
260
|
reason: `conformance-drainpolicy-${policy}`,
|
|
259
261
|
drainPolicy: policy,
|
|
260
262
|
});
|
|
261
|
-
expect(pause.status,
|
|
263
|
+
expect(pause.status, req('openwop.it.pause-resume.every-drainpolicy-advertised-by-the-host-is-accepted-on-pause',
|
|
262
264
|
'capabilities.md §`runs.pauseResume.drainPolicies` + rest-endpoints.md POST /v1/runs/{runId}:pause',
|
|
263
265
|
`host-advertised drainPolicy='${policy}' MUST be accepted on :pause`,
|
|
264
266
|
)).toBe(202);
|
|
@@ -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
|
const CANONICAL_MODES = ['disabled', 'optional', 'required', 'restricted'] as const;
|
|
40
42
|
|
|
@@ -60,21 +62,21 @@ async function fetchAiProviders(): Promise<AiProvidersShape | undefined> {
|
|
|
60
62
|
describe('policies: /.well-known/openwop aiProviders.policies shape contract', () => {
|
|
61
63
|
it('aiProviders.policies is well-formed when present (or absent — both spec-allowed)', async () => {
|
|
62
64
|
const ap = await fetchAiProviders();
|
|
63
|
-
if (ap === undefined) return; // Optional v1 field — hosts MAY omit aiProviders entirely.
|
|
65
|
+
if (ap === undefined) return softSkip('blocked', 'precondition not met — `ap === undefined` returned early (Optional v1 field — hosts MAY omit aiProviders entirely.) (seam, prior step, or fixture unavailable)'); // Optional v1 field — hosts MAY omit aiProviders entirely.
|
|
64
66
|
|
|
65
67
|
const policies = ap.policies;
|
|
66
68
|
if (policies === undefined) {
|
|
67
69
|
// Spec-allowed: omitting `policies` means the host implements no
|
|
68
70
|
// enforcement. Clients see only `optional` semantics. Nothing
|
|
69
71
|
// further to assert.
|
|
70
|
-
return;
|
|
72
|
+
return softSkip('blocked', 'precondition not met — `policies === undefined` returned early (Spec-allowed: omitting `policies` means the host implements no enforcement. Clients see only `optional` semantics. Nothing further to assert.) (seam, pri…');
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
expect(typeof policies,
|
|
75
|
+
expect(typeof policies, req('openwop.it.policies.aiproviders-policies-is-well-formed-when-present-or-absent-both-spec-allowed',
|
|
74
76
|
'capabilities.md §"`aiProviders.policies`"',
|
|
75
77
|
'aiProviders.policies MUST be an object when present',
|
|
76
78
|
)).toBe('object');
|
|
77
|
-
expect(policies,
|
|
79
|
+
expect(policies, req('openwop.it.policies.aiproviders-policies-is-well-formed-when-present-or-absent-both-spec-allowed',
|
|
78
80
|
'capabilities.md §"`aiProviders.policies`"',
|
|
79
81
|
'aiProviders.policies MUST NOT be null when present',
|
|
80
82
|
)).not.toBeNull();
|
|
@@ -83,20 +85,20 @@ describe('policies: /.well-known/openwop aiProviders.policies shape contract', (
|
|
|
83
85
|
it('policies.modes — every advertised mode is one of the four canonical values', async () => {
|
|
84
86
|
const ap = await fetchAiProviders();
|
|
85
87
|
const modes = ap?.policies?.modes;
|
|
86
|
-
if (modes === undefined) return;
|
|
88
|
+
if (modes === undefined) return softSkip('blocked', 'precondition not met — `modes === undefined` returned early (seam, prior step, or fixture unavailable)');
|
|
87
89
|
|
|
88
|
-
expect(Array.isArray(modes),
|
|
90
|
+
expect(Array.isArray(modes), req('openwop.it.policies.policies-modes-every-advertised-mode-is-one-of-the-four-canonical-values',
|
|
89
91
|
'capabilities.md §"`aiProviders.policies`"',
|
|
90
92
|
'policies.modes MUST be a string[] when present',
|
|
91
93
|
)).toBe(true);
|
|
92
94
|
|
|
93
95
|
const arr = modes as unknown[];
|
|
94
96
|
for (const mode of arr) {
|
|
95
|
-
expect(typeof mode,
|
|
97
|
+
expect(typeof mode, req('openwop.it.policies.policies-modes-every-advertised-mode-is-one-of-the-four-canonical-values',
|
|
96
98
|
'capabilities.md §"`aiProviders.policies`"',
|
|
97
99
|
'policies.modes entries MUST be strings',
|
|
98
100
|
)).toBe('string');
|
|
99
|
-
expect(CANONICAL_MODES,
|
|
101
|
+
expect(CANONICAL_MODES, req('openwop.it.policies.policies-modes-every-advertised-mode-is-one-of-the-four-canonical-values',
|
|
100
102
|
'capabilities.md §"`aiProviders.policies`"',
|
|
101
103
|
`mode "${mode}" MUST be one of ${CANONICAL_MODES.join(', ')}`,
|
|
102
104
|
)).toContain(mode);
|
|
@@ -106,11 +108,11 @@ describe('policies: /.well-known/openwop aiProviders.policies shape contract', (
|
|
|
106
108
|
it('policies.modes — no duplicate entries (uniqueItems contract)', async () => {
|
|
107
109
|
const ap = await fetchAiProviders();
|
|
108
110
|
const modes = ap?.policies?.modes;
|
|
109
|
-
if (!Array.isArray(modes)) return;
|
|
111
|
+
if (!Array.isArray(modes)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!Array.isArray(modes)` returned early');
|
|
110
112
|
|
|
111
113
|
const arr = modes as string[];
|
|
112
114
|
const set = new Set(arr);
|
|
113
|
-
expect(set.size,
|
|
115
|
+
expect(set.size, req('openwop.it.policies.policies-modes-no-duplicate-entries-uniqueitems-contract',
|
|
114
116
|
'capabilities.schema.json — policies.modes uniqueItems: true',
|
|
115
117
|
'policies.modes MUST NOT contain duplicate entries',
|
|
116
118
|
)).toBe(arr.length);
|
|
@@ -119,27 +121,27 @@ describe('policies: /.well-known/openwop aiProviders.policies shape contract', (
|
|
|
119
121
|
it('policies.scopes — string[] of non-empty entries when present', async () => {
|
|
120
122
|
const ap = await fetchAiProviders();
|
|
121
123
|
const scopes = ap?.policies?.scopes;
|
|
122
|
-
if (scopes === undefined) return;
|
|
124
|
+
if (scopes === undefined) return softSkip('blocked', 'precondition not met — `scopes === undefined` returned early (seam, prior step, or fixture unavailable)');
|
|
123
125
|
|
|
124
|
-
expect(Array.isArray(scopes),
|
|
126
|
+
expect(Array.isArray(scopes), req('openwop.it.policies.policies-scopes-string-of-non-empty-entries-when-present',
|
|
125
127
|
'capabilities.md §"`aiProviders.policies`"',
|
|
126
128
|
'policies.scopes MUST be a string[] when present',
|
|
127
129
|
)).toBe(true);
|
|
128
130
|
|
|
129
131
|
const arr = scopes as unknown[];
|
|
130
132
|
for (const scope of arr) {
|
|
131
|
-
expect(typeof scope,
|
|
133
|
+
expect(typeof scope, req('openwop.it.policies.policies-scopes-string-of-non-empty-entries-when-present',
|
|
132
134
|
'capabilities.md §"`aiProviders.policies`"',
|
|
133
135
|
'policies.scopes entries MUST be strings',
|
|
134
136
|
)).toBe('string');
|
|
135
|
-
expect((scope as string).length,
|
|
137
|
+
expect((scope as string).length, req('openwop.it.policies.policies-scopes-string-of-non-empty-entries-when-present',
|
|
136
138
|
'capabilities.md §"`aiProviders.policies`"',
|
|
137
139
|
'policies.scopes entries MUST be non-empty',
|
|
138
140
|
)).toBeGreaterThan(0);
|
|
139
141
|
}
|
|
140
142
|
|
|
141
143
|
const set = new Set(arr as string[]);
|
|
142
|
-
expect(set.size,
|
|
144
|
+
expect(set.size, req('openwop.it.policies.policies-scopes-string-of-non-empty-entries-when-present',
|
|
143
145
|
'capabilities.schema.json — policies.scopes uniqueItems: true',
|
|
144
146
|
'policies.scopes MUST NOT contain duplicate entries',
|
|
145
147
|
)).toBe(arr.length);
|
|
@@ -148,13 +150,13 @@ describe('policies: /.well-known/openwop aiProviders.policies shape contract', (
|
|
|
148
150
|
it('policies.errorCode — non-empty string when present (defaults to provider_policy_denied)', async () => {
|
|
149
151
|
const ap = await fetchAiProviders();
|
|
150
152
|
const errorCode = ap?.policies?.errorCode;
|
|
151
|
-
if (errorCode === undefined) return;
|
|
153
|
+
if (errorCode === undefined) return softSkip('blocked', 'precondition not met — `errorCode === undefined` returned early (seam, prior step, or fixture unavailable)');
|
|
152
154
|
|
|
153
|
-
expect(typeof errorCode,
|
|
155
|
+
expect(typeof errorCode, req('openwop.it.policies.policies-errorcode-non-empty-string-when-present-defaults-to-provider-policy-den',
|
|
154
156
|
'capabilities.md §"`aiProviders.policies`"',
|
|
155
157
|
'policies.errorCode MUST be a string when present',
|
|
156
158
|
)).toBe('string');
|
|
157
|
-
expect((errorCode as string).length,
|
|
159
|
+
expect((errorCode as string).length, req('openwop.it.policies.policies-errorcode-non-empty-string-when-present-defaults-to-provider-policy-den',
|
|
158
160
|
'capabilities.md §"`aiProviders.policies`"',
|
|
159
161
|
'policies.errorCode MUST be non-empty when present',
|
|
160
162
|
)).toBeGreaterThan(0);
|