@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
|
@@ -34,6 +34,8 @@ import { join } from 'node:path';
|
|
|
34
34
|
import { driver } from '../lib/driver.js';
|
|
35
35
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
36
36
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
37
|
+
import { req } from '../lib/requirement-ids.js';
|
|
38
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
37
39
|
|
|
38
40
|
const PROMPT_KIND_VALUES = ['system', 'user', 'few-shot', 'schema-hint'] as const;
|
|
39
41
|
|
|
@@ -92,11 +94,11 @@ describe('prompt-template-shape: schema compile (RFC 0027 §A)', () => {
|
|
|
92
94
|
// Reuse the already-registered validator when present — Ajv refuses
|
|
93
95
|
// to re-`compile` a schema whose `$id` it already knows.
|
|
94
96
|
const validate = ajv.getSchema(schema['$id'] as string) ?? ajv.compile(schema);
|
|
95
|
-
expect(validate, 'RFC 0027 §A: prompt-kind.schema.json MUST compile').toBeTypeOf('function');
|
|
96
|
-
expect(schema.type, 'prompt-kind MUST be type: string').toBe('string');
|
|
97
|
+
expect(validate, req('openwop.it.prompt-template-shape.prompt-kind-schema-json-compiles-and-is-a-string-enum-of-the-four-canonical-kind', 'spec/v1/prompts.md §PromptTemplate', 'RFC 0027 §A: prompt-kind.schema.json MUST compile')).toBeTypeOf('function');
|
|
98
|
+
expect(schema.type, req('openwop.it.prompt-template-shape.prompt-kind-schema-json-compiles-and-is-a-string-enum-of-the-four-canonical-kind', 'spec/v1/prompts.md §PromptTemplate', 'prompt-kind MUST be type: string')).toBe('string');
|
|
97
99
|
expect(
|
|
98
100
|
schema.enum,
|
|
99
|
-
|
|
101
|
+
req('openwop.it.prompt-template-shape.prompt-kind-schema-json-compiles-and-is-a-string-enum-of-the-four-canonical-kind',
|
|
100
102
|
'spec/v1/prompts.md §PromptTemplate',
|
|
101
103
|
'prompt-kind enum MUST contain exactly the four canonical values',
|
|
102
104
|
),
|
|
@@ -106,13 +108,13 @@ describe('prompt-template-shape: schema compile (RFC 0027 §A)', () => {
|
|
|
106
108
|
it('prompt-template.schema.json compiles with cross-ref to prompt-kind', () => {
|
|
107
109
|
const schema = loadSchema('prompt-template.schema.json');
|
|
108
110
|
const validate = ajv.compile(schema);
|
|
109
|
-
expect(validate, 'RFC 0027 §A: prompt-template.schema.json MUST compile').toBeTypeOf('function');
|
|
111
|
+
expect(validate, req('openwop.it.prompt-template-shape.prompt-template-schema-json-compiles-with-cross-ref-to-prompt-kind', 'RFC 0027 §A', 'RFC 0027 §A: prompt-template.schema.json MUST compile')).toBeTypeOf('function');
|
|
110
112
|
});
|
|
111
113
|
|
|
112
114
|
it('prompt-ref.schema.json compiles', () => {
|
|
113
115
|
const schema = loadSchema('prompt-ref.schema.json');
|
|
114
116
|
const validate = ajv.compile(schema);
|
|
115
|
-
expect(validate, 'RFC 0027 §B: prompt-ref.schema.json MUST compile').toBeTypeOf('function');
|
|
117
|
+
expect(validate, req('openwop.it.prompt-template-shape.prompt-ref-schema-json-compiles', 'RFC 0027 §A', 'RFC 0027 §B: prompt-ref.schema.json MUST compile')).toBeTypeOf('function');
|
|
116
118
|
});
|
|
117
119
|
});
|
|
118
120
|
|
|
@@ -130,7 +132,7 @@ describe('prompt-template-shape: PromptTemplate round-trip (RFC 0027 §A)', () =
|
|
|
130
132
|
const ok = validate(positive);
|
|
131
133
|
expect(
|
|
132
134
|
ok,
|
|
133
|
-
`RFC 0027 §A: minimal PromptTemplate MUST validate; errors: ${JSON.stringify(validate.errors)}
|
|
135
|
+
req('openwop.it.prompt-template-shape.accepts-a-minimal-positive-prompttemplate-fixture', 'RFC 0027 §A', `RFC 0027 §A: minimal PromptTemplate MUST validate; errors: ${JSON.stringify(validate.errors)}`),
|
|
134
136
|
).toBe(true);
|
|
135
137
|
});
|
|
136
138
|
|
|
@@ -157,7 +159,7 @@ describe('prompt-template-shape: PromptTemplate round-trip (RFC 0027 §A)', () =
|
|
|
157
159
|
const ok = validate(positive);
|
|
158
160
|
expect(
|
|
159
161
|
ok,
|
|
160
|
-
`RFC 0027 §A: full PromptTemplate MUST validate; errors: ${JSON.stringify(validate.errors)}
|
|
162
|
+
req('openwop.it.prompt-template-shape.accepts-a-prompttemplate-with-typed-variables-modelhints-meta', 'RFC 0027 §A', `RFC 0027 §A: full PromptTemplate MUST validate; errors: ${JSON.stringify(validate.errors)}`),
|
|
161
163
|
).toBe(true);
|
|
162
164
|
});
|
|
163
165
|
|
|
@@ -170,7 +172,7 @@ describe('prompt-template-shape: PromptTemplate round-trip (RFC 0027 §A)', () =
|
|
|
170
172
|
};
|
|
171
173
|
expect(
|
|
172
174
|
validate(negative),
|
|
173
|
-
|
|
175
|
+
req('openwop.it.prompt-template-shape.rejects-a-prompttemplate-missing-required-text',
|
|
174
176
|
'spec/v1/prompts.md §PromptTemplate',
|
|
175
177
|
'PromptTemplate MUST require text field',
|
|
176
178
|
),
|
|
@@ -186,7 +188,7 @@ describe('prompt-template-shape: PromptTemplate round-trip (RFC 0027 §A)', () =
|
|
|
186
188
|
};
|
|
187
189
|
expect(
|
|
188
190
|
validate(negative),
|
|
189
|
-
|
|
191
|
+
req('openwop.it.prompt-template-shape.rejects-a-prompttemplate-with-non-semver-version',
|
|
190
192
|
'spec/v1/prompts.md §PromptTemplate',
|
|
191
193
|
'PromptTemplate.version MUST match SemVer 2.0.0 pattern',
|
|
192
194
|
),
|
|
@@ -202,7 +204,7 @@ describe('prompt-template-shape: PromptTemplate round-trip (RFC 0027 §A)', () =
|
|
|
202
204
|
};
|
|
203
205
|
expect(
|
|
204
206
|
validate(negative),
|
|
205
|
-
|
|
207
|
+
req('openwop.it.prompt-template-shape.rejects-a-prompttemplate-with-invalid-templateid-uppercase-letter',
|
|
206
208
|
'spec/v1/prompts.md §PromptTemplate',
|
|
207
209
|
'PromptTemplate.templateId MUST match ^[a-z0-9][a-z0-9._-]{0,127}$',
|
|
208
210
|
),
|
|
@@ -218,7 +220,7 @@ describe('prompt-template-shape: PromptTemplate round-trip (RFC 0027 §A)', () =
|
|
|
218
220
|
};
|
|
219
221
|
expect(
|
|
220
222
|
validate(negative),
|
|
221
|
-
|
|
223
|
+
req('openwop.it.prompt-template-shape.rejects-a-prompttemplate-with-kind-not-in-the-prompt-kind-enum',
|
|
222
224
|
'spec/v1/prompts.md §PromptTemplate',
|
|
223
225
|
'PromptTemplate.kind MUST be one of the four canonical values',
|
|
224
226
|
),
|
|
@@ -235,7 +237,7 @@ describe('prompt-template-shape: PromptTemplate round-trip (RFC 0027 §A)', () =
|
|
|
235
237
|
};
|
|
236
238
|
expect(
|
|
237
239
|
validate(negative),
|
|
238
|
-
|
|
240
|
+
req('openwop.it.prompt-template-shape.rejects-an-unknown-top-level-property-additionalproperties-false',
|
|
239
241
|
'spec/v1/prompts.md §PromptTemplate',
|
|
240
242
|
'PromptTemplate top-level additionalProperties:false MUST reject unknown fields',
|
|
241
243
|
),
|
|
@@ -252,7 +254,7 @@ describe('prompt-template-shape: PromptTemplate round-trip (RFC 0027 §A)', () =
|
|
|
252
254
|
};
|
|
253
255
|
expect(
|
|
254
256
|
validate(negative),
|
|
255
|
-
|
|
257
|
+
req('openwop.it.prompt-template-shape.rejects-a-promptvariable-with-bad-name-pattern-dash',
|
|
256
258
|
'spec/v1/prompts.md §PromptTemplate',
|
|
257
259
|
'PromptVariable.name MUST match common-templating identifier pattern (no dashes)',
|
|
258
260
|
),
|
|
@@ -265,19 +267,19 @@ describe('prompt-template-shape: PromptRef round-trip (RFC 0027 §B)', () => {
|
|
|
265
267
|
const validate = ajv.compile(loadSchema('prompt-ref.schema.json'));
|
|
266
268
|
|
|
267
269
|
it('accepts stringy form without version', () => {
|
|
268
|
-
expect(validate('prompt:writer-system')).toBe(true);
|
|
270
|
+
expect(validate('prompt:writer-system'), req('openwop.it.prompt-template-shape.accepts-stringy-form-without-version', 'RFC 0027 §A', 'accepts stringy form without version')).toBe(true);
|
|
269
271
|
});
|
|
270
272
|
|
|
271
273
|
it('accepts stringy form with version', () => {
|
|
272
|
-
expect(validate('prompt:writer-system@1.2.3')).toBe(true);
|
|
274
|
+
expect(validate('prompt:writer-system@1.2.3'), req('openwop.it.prompt-template-shape.accepts-stringy-form-with-version', 'RFC 0027 §A', 'accepts stringy form with version')).toBe(true);
|
|
273
275
|
});
|
|
274
276
|
|
|
275
277
|
it('accepts stringy form with vendor-prefixed templateId', () => {
|
|
276
|
-
expect(validate('prompt:vendor.acme.writer.v2@2.0.0')).toBe(true);
|
|
278
|
+
expect(validate('prompt:vendor.acme.writer.v2@2.0.0'), req('openwop.it.prompt-template-shape.accepts-stringy-form-with-vendor-prefixed-templateid', 'RFC 0027 §A', 'accepts stringy form with vendor-prefixed templateId')).toBe(true);
|
|
277
279
|
});
|
|
278
280
|
|
|
279
281
|
it('accepts object form with templateId only', () => {
|
|
280
|
-
expect(validate({ templateId: 'writer-system' })).toBe(true);
|
|
282
|
+
expect(validate({ templateId: 'writer-system' }), req('openwop.it.prompt-template-shape.accepts-object-form-with-templateid-only', 'RFC 0027 §A', 'accepts object form with templateId only')).toBe(true);
|
|
281
283
|
});
|
|
282
284
|
|
|
283
285
|
it('accepts object form with libraryId, version, and variableOverrides', () => {
|
|
@@ -287,35 +289,35 @@ describe('prompt-template-shape: PromptRef round-trip (RFC 0027 §B)', () => {
|
|
|
287
289
|
templateId: 'writer-system',
|
|
288
290
|
version: '1.0.0',
|
|
289
291
|
variableOverrides: { tone: 'formal' },
|
|
290
|
-
}),
|
|
292
|
+
}), req('openwop.it.prompt-template-shape.accepts-object-form-with-libraryid-version-and-variableoverrides', 'RFC 0027 §A', 'accepts object form with libraryId, version, and variableOverrides'),
|
|
291
293
|
).toBe(true);
|
|
292
294
|
});
|
|
293
295
|
|
|
294
296
|
it('rejects stringy form without `prompt:` prefix', () => {
|
|
295
297
|
expect(
|
|
296
298
|
validate('writer-system'),
|
|
297
|
-
|
|
299
|
+
req('openwop.it.prompt-template-shape.rejects-stringy-form-without-prompt-prefix', 'spec/v1/prompts.md §PromptRef', 'stringy PromptRef MUST start with prompt:'),
|
|
298
300
|
).toBe(false);
|
|
299
301
|
});
|
|
300
302
|
|
|
301
303
|
it('rejects stringy form with non-SemVer version', () => {
|
|
302
304
|
expect(
|
|
303
305
|
validate('prompt:writer-system@latest'),
|
|
304
|
-
|
|
306
|
+
req('openwop.it.prompt-template-shape.rejects-stringy-form-with-non-semver-version', 'spec/v1/prompts.md §PromptRef', 'stringy PromptRef version MUST be SemVer'),
|
|
305
307
|
).toBe(false);
|
|
306
308
|
});
|
|
307
309
|
|
|
308
310
|
it('rejects object form missing required templateId', () => {
|
|
309
311
|
expect(
|
|
310
312
|
validate({ version: '1.0.0' }),
|
|
311
|
-
|
|
313
|
+
req('openwop.it.prompt-template-shape.rejects-object-form-missing-required-templateid', 'spec/v1/prompts.md §PromptRef', 'object PromptRef MUST require templateId'),
|
|
312
314
|
).toBe(false);
|
|
313
315
|
});
|
|
314
316
|
|
|
315
317
|
it('rejects object form with unknown additional property', () => {
|
|
316
318
|
expect(
|
|
317
319
|
validate({ templateId: 'writer-system', unknownExtra: true }),
|
|
318
|
-
|
|
320
|
+
req('openwop.it.prompt-template-shape.rejects-object-form-with-unknown-additional-property',
|
|
319
321
|
'spec/v1/prompts.md §PromptRef',
|
|
320
322
|
'object PromptRef additionalProperties:false MUST reject unknown fields',
|
|
321
323
|
),
|
|
@@ -326,24 +328,24 @@ describe('prompt-template-shape: PromptRef round-trip (RFC 0027 §B)', () => {
|
|
|
326
328
|
describe.skipIf(HTTP_SKIP)('prompt-template-shape: capabilities.prompts advertisement (RFC 0027 §D)', () => {
|
|
327
329
|
it('capabilities.prompts (when present) carries the optional shape per RFC 0027 §D', async () => {
|
|
328
330
|
const d = await readDiscovery();
|
|
329
|
-
if (d === null) return;
|
|
331
|
+
if (d === null) return softSkip('blocked', 'precondition not met — `d === null` returned early (seam, prior step, or fixture unavailable)');
|
|
330
332
|
const prompts = capabilityFamily(d, 'prompts');
|
|
331
|
-
if (prompts === undefined) return; // optional block; host MAY omit
|
|
333
|
+
if (prompts === undefined) return softSkip('blocked', 'precondition not met — `prompts === undefined` returned early (optional block; host MAY omit) (seam, prior step, or fixture unavailable)'); // optional block; host MAY omit
|
|
332
334
|
expect(
|
|
333
335
|
typeof prompts.supported,
|
|
334
|
-
|
|
336
|
+
req('openwop.it.prompt-template-shape.capabilities-prompts-when-present-carries-the-optional-shape-per-rfc-0027-d',
|
|
335
337
|
'spec/v1/prompts.md §Capability advertisement',
|
|
336
338
|
'capabilities.prompts.supported MUST be boolean when block is advertised',
|
|
337
339
|
),
|
|
338
340
|
).toBe('boolean');
|
|
339
341
|
if (prompts.templateKinds !== undefined) {
|
|
340
|
-
expect(Array.isArray(prompts.templateKinds), 'templateKinds MUST be an array').toBe(true);
|
|
342
|
+
expect(Array.isArray(prompts.templateKinds), req('openwop.it.prompt-template-shape.capabilities-prompts-when-present-carries-the-optional-shape-per-rfc-0027-d', 'spec/v1/prompts.md §Capability advertisement', 'templateKinds MUST be an array')).toBe(true);
|
|
341
343
|
for (const k of prompts.templateKinds as unknown[]) {
|
|
342
344
|
expect((PROMPT_KIND_VALUES as readonly string[]).includes(String(k))).toBe(true);
|
|
343
345
|
}
|
|
344
346
|
}
|
|
345
347
|
if (prompts.variableSources !== undefined) {
|
|
346
|
-
expect(Array.isArray(prompts.variableSources), 'variableSources MUST be an array').toBe(true);
|
|
348
|
+
expect(Array.isArray(prompts.variableSources), req('openwop.it.prompt-template-shape.capabilities-prompts-when-present-carries-the-optional-shape-per-rfc-0027-d', 'spec/v1/prompts.md §Capability advertisement', 'variableSources MUST be an array')).toBe(true);
|
|
347
349
|
for (const s of prompts.variableSources as unknown[]) {
|
|
348
350
|
expect(['input', 'variable', 'secret', 'context']).toContain(String(s));
|
|
349
351
|
}
|
|
@@ -352,7 +354,7 @@ describe.skipIf(HTTP_SKIP)('prompt-template-shape: capabilities.prompts advertis
|
|
|
352
354
|
expect(['off', 'hashed', 'full']).toContain(String(prompts.observability));
|
|
353
355
|
}
|
|
354
356
|
if (prompts.maxTemplateBytes !== undefined) {
|
|
355
|
-
expect(typeof prompts.maxTemplateBytes, 'maxTemplateBytes MUST be integer').toBe('number');
|
|
357
|
+
expect(typeof prompts.maxTemplateBytes, req('openwop.it.prompt-template-shape.capabilities-prompts-when-present-carries-the-optional-shape-per-rfc-0027-d', 'spec/v1/prompts.md §Capability advertisement', 'maxTemplateBytes MUST be integer')).toBe('number');
|
|
356
358
|
expect((prompts.maxTemplateBytes as number) > 0).toBe(true);
|
|
357
359
|
expect((prompts.maxTemplateBytes as number) <= 65536).toBe(true);
|
|
358
360
|
}
|
|
@@ -31,8 +31,8 @@ import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
|
31
31
|
import { driver } from '../lib/driver.js';
|
|
32
32
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
33
33
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
import { req } from '../lib/requirement-ids.js';
|
|
35
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
36
36
|
function loadSchema(name: string): Record<string, unknown> {
|
|
37
37
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
|
|
38
38
|
}
|
|
@@ -43,21 +43,21 @@ describe('proposal-reviewable-learning: capability advertisement (RFC 0096 §A,
|
|
|
43
43
|
|
|
44
44
|
it('capabilities schema declares agents.proposals with its required sub-flags', () => {
|
|
45
45
|
const proposals = agents?.properties?.proposals;
|
|
46
|
-
expect(proposals,
|
|
46
|
+
expect(proposals, req('openwop.it.proposal-reviewable-learning.capabilities-schema-declares-agents-proposals-with-its-required-sub-flags', 'capabilities.md §agents', 'agents.proposals MUST be declared')).toBeDefined();
|
|
47
47
|
for (const flag of ['artifactKinds', 'duplicationDetection', 'activation']) {
|
|
48
|
-
expect(proposals?.properties?.[flag],
|
|
48
|
+
expect(proposals?.properties?.[flag], req('openwop.it.proposal-reviewable-learning.capabilities-schema-declares-agents-proposals-with-its-required-sub-flags', 'RFC 0096 §A', `agents.proposals.${flag} MUST be declared`)).toBeDefined();
|
|
49
49
|
}
|
|
50
|
-
expect(proposals?.required,
|
|
50
|
+
expect(proposals?.required, req('openwop.it.proposal-reviewable-learning.capabilities-schema-declares-agents-proposals-with-its-required-sub-flags', 'RFC 0096 §A', 'artifactKinds + activation MUST be required')).toEqual(
|
|
51
51
|
expect.arrayContaining(['artifactKinds', 'activation']),
|
|
52
52
|
);
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
it('the `rule` artifact kind is NOT in the enum (dropped — no defining RFC)', () => {
|
|
56
56
|
const kinds = (agents?.properties?.proposals as { properties?: Record<string, { items?: { enum?: string[] } }> })?.properties?.artifactKinds?.items?.enum ?? [];
|
|
57
|
-
expect(kinds,
|
|
57
|
+
expect(kinds, req('openwop.it.proposal-reviewable-learning.the-rule-artifact-kind-is-not-in-the-enum-dropped-no-defining-rfc', 'RFC 0096 §A', 'artifactKinds enumerates the four kinds with a defining RFC')).toEqual(
|
|
58
58
|
expect.arrayContaining(['agent-pack', 'workflow-chain-pack', 'prompt-template', 'automation']),
|
|
59
59
|
);
|
|
60
|
-
expect(kinds,
|
|
60
|
+
expect(kinds, req('openwop.it.proposal-reviewable-learning.the-rule-artifact-kind-is-not-in-the-enum-dropped-no-defining-rfc', 'RFC 0096 §A', '`rule` MUST NOT appear — it has no defining RFC, so its artifact is unvalidatable')).not.toContain('rule');
|
|
61
61
|
});
|
|
62
62
|
});
|
|
63
63
|
|
|
@@ -77,13 +77,13 @@ describe('proposal-reviewable-learning: Proposal shape (RFC 0096 §B, server-fre
|
|
|
77
77
|
};
|
|
78
78
|
|
|
79
79
|
it('validates a conforming draft proposal', () => {
|
|
80
|
-
expect(validate(good),
|
|
80
|
+
expect(validate(good), req('openwop.it.proposal-reviewable-learning.validates-a-conforming-draft-proposal', 'RFC 0096 §B', `a conforming proposal MUST validate. Errors: ${JSON.stringify(validate.errors)}`)).toBe(true);
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
it('rejects an unknown state and the dropped `rule` kind', () => {
|
|
84
|
-
expect(validate({ ...good, state: 'live' }),
|
|
85
|
-
expect(validate({ ...good, kind: 'rule' }),
|
|
86
|
-
expect(validate({ ...good, owner: { workspace: 'x' } }),
|
|
84
|
+
expect(validate({ ...good, state: 'live' }), req('openwop.it.proposal-reviewable-learning.rejects-an-unknown-state-and-the-dropped-rule-kind', 'RFC 0096 §B', 'a state outside the lifecycle enum MUST be rejected')).toBe(false);
|
|
85
|
+
expect(validate({ ...good, kind: 'rule' }), req('openwop.it.proposal-reviewable-learning.rejects-an-unknown-state-and-the-dropped-rule-kind', 'RFC 0096 §B', '`kind: "rule"` MUST be rejected (dropped from the enum)')).toBe(false);
|
|
86
|
+
expect(validate({ ...good, owner: { workspace: 'x' } }), req('openwop.it.proposal-reviewable-learning.rejects-an-unknown-state-and-the-dropped-rule-kind', 'RFC 0048', 'owner without tenant MUST be rejected')).toBe(false);
|
|
87
87
|
});
|
|
88
88
|
});
|
|
89
89
|
|
|
@@ -96,15 +96,15 @@ describe('proposal-reviewable-learning: content-free events (RFC 0096 §D, serve
|
|
|
96
96
|
|
|
97
97
|
it('proposal.created and proposal.activated are in the RunEventType enum', () => {
|
|
98
98
|
const en = (runEvent.$defs as Record<string, { enum?: string[] }>).RunEventType?.enum ?? [];
|
|
99
|
-
expect(en).toContain('proposal.created');
|
|
99
|
+
expect(en, req('openwop.it.proposal-reviewable-learning.proposal-created-and-proposal-activated-are-in-the-runeventtype-enum', 'RFC 0096', 'proposal.created and proposal.activated are in the RunEventType enum')).toContain('proposal.created');
|
|
100
100
|
expect(en).toContain('proposal.activated');
|
|
101
101
|
});
|
|
102
102
|
|
|
103
103
|
it('proposal.created is content-free — an artifact body and rationale text are rejected', () => {
|
|
104
104
|
const created = ajv.getSchema('payloads#/$defs/proposalCreated')!;
|
|
105
|
-
expect(created({ proposalId: 'p1', kind: 'agent-pack', sourceRunIds: ['r1'], duplicateOf: null }),
|
|
106
|
-
expect(created({ proposalId: 'p1', kind: 'agent-pack', artifact: { x: 1 } }),
|
|
107
|
-
expect(created({ proposalId: 'p1', kind: 'agent-pack', rationale: 'because…' }),
|
|
105
|
+
expect(created({ proposalId: 'p1', kind: 'agent-pack', sourceRunIds: ['r1'], duplicateOf: null }), req('openwop.it.proposal-reviewable-learning.proposal-created-is-content-free-an-artifact-body-and-rationale-text-are-rejecte', 'RFC 0096 §D', 'a content-free proposal.created MUST validate')).toBe(true);
|
|
106
|
+
expect(created({ proposalId: 'p1', kind: 'agent-pack', artifact: { x: 1 } }), req('openwop.it.proposal-reviewable-learning.proposal-created-is-content-free-an-artifact-body-and-rationale-text-are-rejecte', 'SECURITY invariant proposal-inert-until-applied', 'proposal.created MUST NOT carry the artifact body')).toBe(false);
|
|
107
|
+
expect(created({ proposalId: 'p1', kind: 'agent-pack', rationale: 'because…' }), req('openwop.it.proposal-reviewable-learning.proposal-created-is-content-free-an-artifact-body-and-rationale-text-are-rejecte', 'RFC 0096 §D', 'proposal.created MUST NOT carry rationale text')).toBe(false);
|
|
108
108
|
});
|
|
109
109
|
});
|
|
110
110
|
|
|
@@ -114,16 +114,16 @@ describe('proposal-reviewable-learning: behavioral (RFC 0096 §E, capability-gat
|
|
|
114
114
|
if (!behaviorGate('agents.proposals', agents?.proposals !== undefined)) return;
|
|
115
115
|
|
|
116
116
|
const list = await driver.get('/v1/host/sample/proposals?state=draft');
|
|
117
|
-
if (list.status === 404 || list.status === 403) return; // seam unwired — soft-skip
|
|
117
|
+
if (list.status === 404 || list.status === 403) return softSkip('blocked', 'precondition not met — `list.status === 404 || list.status === 403` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
118
118
|
const proposals = (list.json as { proposals?: Array<{ id: string }> })?.proposals ?? [];
|
|
119
|
-
if (proposals.length === 0) return; // nothing to act on — soft-skip
|
|
119
|
+
if (proposals.length === 0) return softSkip('blocked', 'precondition not met — `proposals.length === 0` returned early (nothing to act on — soft-skip) (seam, prior step, or fixture unavailable)'); // nothing to act on — soft-skip
|
|
120
120
|
|
|
121
121
|
// Apply with no auth/scope — MUST be refused.
|
|
122
122
|
const res = await driver.post(`/v1/host/sample/proposals/${proposals[0]!.id}/apply`, {});
|
|
123
|
-
if (res.status === 404) return;
|
|
123
|
+
if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
124
124
|
expect(
|
|
125
125
|
res.status,
|
|
126
|
-
|
|
126
|
+
req('openwop.it.proposal-reviewable-learning.apply-without-the-activation-scope-is-denied-403-and-installs-nothing', 'agent-memory.md §"Reviewable learning" clause 2', 'apply without the activation scope MUST be denied (403)'),
|
|
127
127
|
).toBe(403);
|
|
128
128
|
});
|
|
129
129
|
});
|
|
@@ -29,6 +29,7 @@ import { driver } from '../lib/driver.js';
|
|
|
29
29
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
30
30
|
import { isCore, type DiscoveryPayload } from '../lib/profiles.js';
|
|
31
31
|
import { softSkip } from '../lib/soft-skip.js';
|
|
32
|
+
import { req } from '../lib/requirement-ids.js';
|
|
32
33
|
|
|
33
34
|
interface CapsSchema {
|
|
34
35
|
$schema: string;
|
|
@@ -43,7 +44,7 @@ const caps = JSON.parse(readFileSync(join(SCHEMAS_DIR, 'capabilities.schema.json
|
|
|
43
44
|
describe('protocol-versions-array: schema shape (RFC 0165 §A.1, server-free)', () => {
|
|
44
45
|
it('declares root `protocolVersions` as a unique, non-empty string array', () => {
|
|
45
46
|
const p = caps.properties.protocolVersions;
|
|
46
|
-
expect(p, 'RFC 0165 §A.1: capabilities.schema.json MUST declare root `protocolVersions`').toBeDefined();
|
|
47
|
+
expect(p, req('openwop.it.protocol-versions-array.declares-root-protocolversions-as-a-unique-non-empty-string-array', 'RFC 0165 §A', 'RFC 0165 §A.1: capabilities.schema.json MUST declare root `protocolVersions`')).toBeDefined();
|
|
47
48
|
expect(p?.type).toBe('array');
|
|
48
49
|
expect(p?.minItems).toBe(1);
|
|
49
50
|
expect(p?.uniqueItems).toBe(true);
|
|
@@ -52,14 +53,14 @@ describe('protocol-versions-array: schema shape (RFC 0165 §A.1, server-free)',
|
|
|
52
53
|
it('item grammar equals the scalar grammar (one axis, one grammar)', () => {
|
|
53
54
|
expect(
|
|
54
55
|
caps.properties.protocolVersions?.items?.pattern,
|
|
55
|
-
'RFC 0165 §A.1: protocolVersions items MUST use the RFC 0149 §C grammar, not the A2A item pattern',
|
|
56
|
+
req('openwop.it.protocol-versions-array.item-grammar-equals-the-scalar-grammar-one-axis-one-grammar', 'RFC 0165 §A', 'RFC 0165 §A.1: protocolVersions items MUST use the RFC 0149 §C grammar, not the A2A item pattern'),
|
|
56
57
|
).toBe(caps.properties.protocolVersion?.pattern);
|
|
57
58
|
});
|
|
58
59
|
|
|
59
60
|
it('accepts ["1.11", "2.0"], rejects "01.0", "1.0.0", duplicates, and empty', () => {
|
|
60
61
|
const ajv = new Ajv2020({ allErrors: true, strict: false });
|
|
61
62
|
const validate = ajv.compile({ $schema: caps.$schema, ...(caps.properties.protocolVersions as Record<string, unknown>) });
|
|
62
|
-
expect(validate(['1.11', '2.0'])).toBe(true);
|
|
63
|
+
expect(validate(['1.11', '2.0']), req('openwop.it.protocol-versions-array.accepts-1-11-2-0-rejects-01-0-1-0-0-duplicates-and-empty', 'RFC 0165 §A', 'accepts ["1.11", "2.0"], rejects "01.0", "1.0.0", duplicates, and empty')).toBe(true);
|
|
63
64
|
expect(validate(['01.0'])).toBe(false);
|
|
64
65
|
expect(validate(['1.0.0'])).toBe(false);
|
|
65
66
|
expect(validate(['1.0', '1.0'])).toBe(false);
|
|
@@ -77,14 +78,14 @@ describe('protocol-versions-array: advertisement (RFC 0165 §A.2 — presence-ga
|
|
|
77
78
|
if (arr === undefined) {
|
|
78
79
|
return softSkip('inapplicable', 'host does not advertise protocolVersions (RFC 0165 §A — optional in v1.x)');
|
|
79
80
|
}
|
|
80
|
-
expect(Array.isArray(arr),
|
|
81
|
+
expect(Array.isArray(arr), req('openwop.it.protocol-versions-array.when-advertised-every-item-matches-the-grammar-items-are-unique-and-the-array-co', 'RFC 0165 §A.1', 'protocolVersions MUST be an array')).toBe(true);
|
|
81
82
|
const items = arr as unknown[];
|
|
82
|
-
expect(items.length,
|
|
83
|
+
expect(items.length, req('openwop.it.protocol-versions-array.when-advertised-every-item-matches-the-grammar-items-are-unique-and-the-array-co', 'RFC 0165 §A.1', 'protocolVersions MUST be non-empty')).toBeGreaterThan(0);
|
|
83
84
|
for (const v of items) {
|
|
84
|
-
expect(typeof v === 'string' && GRAMMAR.test(v),
|
|
85
|
+
expect(typeof v === 'string' && GRAMMAR.test(v), req('openwop.it.protocol-versions-array.when-advertised-every-item-matches-the-grammar-items-are-unique-and-the-array-co', 'RFC 0165 §A.1', `protocolVersions item ${JSON.stringify(v)} MUST match the RFC 0149 §C grammar`)).toBe(true);
|
|
85
86
|
}
|
|
86
|
-
expect(new Set(items).size,
|
|
87
|
-
expect(items,
|
|
87
|
+
expect(new Set(items).size, req('openwop.it.protocol-versions-array.when-advertised-every-item-matches-the-grammar-items-are-unique-and-the-array-co', 'RFC 0165 §A.1', 'protocolVersions items MUST be unique')).toBe(items.length);
|
|
88
|
+
expect(items, req('openwop.it.protocol-versions-array.when-advertised-every-item-matches-the-grammar-items-are-unique-and-the-array-co', 'RFC 0165 §A.2', 'protocolVersions MUST contain the value of protocolVersion')).toContain(doc['protocolVersion']);
|
|
88
89
|
});
|
|
89
90
|
|
|
90
91
|
it('profile derivation is unchanged by the array (isCore reads the scalar only)', async () => {
|
|
@@ -97,7 +98,7 @@ describe('protocol-versions-array: advertisement (RFC 0165 §A.2 — presence-ga
|
|
|
97
98
|
delete without['protocolVersions'];
|
|
98
99
|
expect(
|
|
99
100
|
isCore(doc),
|
|
100
|
-
|
|
101
|
+
req('openwop.it.protocol-versions-array.profile-derivation-is-unchanged-by-the-array-iscore-reads-the-scalar-only', 'RFC 0165 §A.2', 'openwop-discovery-core derivation MUST NOT depend on protocolVersions in v1.x'),
|
|
101
102
|
).toBe(isCore(without as DiscoveryPayload));
|
|
102
103
|
});
|
|
103
104
|
});
|
|
@@ -28,6 +28,8 @@ import { driver } from '../lib/driver.js';
|
|
|
28
28
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
29
29
|
import { queryTestEvents, isEventLogSeamAvailable, resetTestSeam } from '../lib/event-log-query.js';
|
|
30
30
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
31
|
+
import { req } from '../lib/requirement-ids.js';
|
|
32
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
31
33
|
|
|
32
34
|
interface DiscoveryDoc {
|
|
33
35
|
capabilities?: {
|
|
@@ -45,21 +47,21 @@ async function readProviderUsageCap(): Promise<{ supported?: boolean; costEstima
|
|
|
45
47
|
describe('provider-usage: capability advertisement (RFC 0026 §E)', () => {
|
|
46
48
|
it('capabilities.providerUsage is either absent or a well-formed object', async () => {
|
|
47
49
|
const cap = await readProviderUsageCap();
|
|
48
|
-
if (cap === null) return; // host doesn't advertise — skip
|
|
50
|
+
if (cap === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap === null` returned early (host doesn\'t advertise — skip)'); // host doesn't advertise — skip
|
|
49
51
|
expect(
|
|
50
52
|
typeof cap.supported,
|
|
51
|
-
|
|
53
|
+
req('openwop.it.provider-usage.capabilities-providerusage-is-either-absent-or-a-well-formed-object', 'RFC 0026 §E', 'capabilities.providerUsage.supported MUST be a boolean when the block is present'),
|
|
52
54
|
).toBe('boolean');
|
|
53
55
|
if (cap.costEstimates !== undefined) {
|
|
54
56
|
expect(
|
|
55
57
|
typeof cap.costEstimates,
|
|
56
|
-
|
|
58
|
+
req('openwop.it.provider-usage.capabilities-providerusage-is-either-absent-or-a-well-formed-object', 'RFC 0026 §E', 'capabilities.providerUsage.costEstimates MUST be a boolean when present'),
|
|
57
59
|
).toBe('boolean');
|
|
58
60
|
}
|
|
59
61
|
if (cap.currency !== undefined) {
|
|
60
62
|
expect(
|
|
61
63
|
/^[A-Z]{3}$/.test(cap.currency),
|
|
62
|
-
|
|
64
|
+
req('openwop.it.provider-usage.capabilities-providerusage-is-either-absent-or-a-well-formed-object', 'RFC 0026 §E', 'capabilities.providerUsage.currency MUST be a 3-letter uppercase ISO 4217 code when present'),
|
|
63
65
|
).toBe(true);
|
|
64
66
|
}
|
|
65
67
|
});
|
|
@@ -84,7 +86,7 @@ describe('provider-usage: schema round-trip (RFC 0026 §A)', () => {
|
|
|
84
86
|
cacheHit: false,
|
|
85
87
|
nodeId: 'chat-respond',
|
|
86
88
|
});
|
|
87
|
-
expect(ok, `positive fixture MUST validate; errors: ${JSON.stringify(validate.errors)}`).toBe(true);
|
|
89
|
+
expect(ok, req('openwop.it.provider-usage.positive-fixture-validates', 'RFC 0026', `positive fixture MUST validate; errors: ${JSON.stringify(validate.errors)}`)).toBe(true);
|
|
88
90
|
});
|
|
89
91
|
|
|
90
92
|
it('negative fixture (missing required field) MUST be rejected', () => {
|
|
@@ -96,7 +98,7 @@ describe('provider-usage: schema round-trip (RFC 0026 §A)', () => {
|
|
|
96
98
|
});
|
|
97
99
|
expect(
|
|
98
100
|
ok,
|
|
99
|
-
|
|
101
|
+
req('openwop.it.provider-usage.negative-fixture-missing-required-field-must-be-rejected', 'RFC 0026 §A', 'payload missing required `outputTokens` MUST fail schema validation'),
|
|
100
102
|
).toBe(false);
|
|
101
103
|
});
|
|
102
104
|
|
|
@@ -110,7 +112,7 @@ describe('provider-usage: schema round-trip (RFC 0026 §A)', () => {
|
|
|
110
112
|
});
|
|
111
113
|
expect(
|
|
112
114
|
ok,
|
|
113
|
-
|
|
115
|
+
req('openwop.it.provider-usage.negative-fixture-additionalproperties-credentialref-leak-must-be-rejected', 'RFC 0026 §D', 'additionalProperties:false MUST reject credentialRef-shaped fields per provider-usage-no-credential-leak'),
|
|
114
116
|
).toBe(false);
|
|
115
117
|
});
|
|
116
118
|
|
|
@@ -121,13 +123,13 @@ describe('provider-usage: schema round-trip (RFC 0026 §A)', () => {
|
|
|
121
123
|
inputTokens: 100.5, // non-integer
|
|
122
124
|
outputTokens: 50,
|
|
123
125
|
});
|
|
124
|
-
expect(ok, 'inputTokens MUST be integer per §A').toBe(false);
|
|
126
|
+
expect(ok, req('openwop.it.provider-usage.negative-fixture-non-integer-token-count-must-be-rejected', 'RFC 0026', 'inputTokens MUST be integer per §A')).toBe(false);
|
|
125
127
|
});
|
|
126
128
|
});
|
|
127
129
|
|
|
128
130
|
describe('provider-usage: event presence via emit-seam + event-log query (RFC 0026 §B)', () => {
|
|
129
131
|
it('emit-seam projects exactly one provider.usage event with required fields populated', async () => {
|
|
130
|
-
if (!(await isEventLogSeamAvailable())) return; // E.1 seam not exposed — soft-skip
|
|
132
|
+
if (!(await isEventLogSeamAvailable())) return softSkip('blocked', 'precondition not met — `!(await isEventLogSeamAvailable())` returned early (E.1 seam not exposed — soft-skip) (seam, prior step, or fixture unavailable)'); // E.1 seam not exposed — soft-skip
|
|
131
133
|
const runId = `r-pu-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
132
134
|
const correlationId = `${runId}:node-1:turn-0:pu-1`;
|
|
133
135
|
const payload = {
|
|
@@ -139,14 +141,14 @@ describe('provider-usage: event presence via emit-seam + event-log query (RFC 00
|
|
|
139
141
|
nodeId: 'node-1',
|
|
140
142
|
};
|
|
141
143
|
const emit = await driver.post('/v1/host/sample/test/emit-provider-usage', { runId, payload, correlationId, nodeId: 'node-1' });
|
|
142
|
-
if (emit.status === 404) return; // emit seam not exposed
|
|
144
|
+
if (emit.status === 404) return softSkip('blocked', 'precondition not met — `emit.status === 404` returned early (emit seam not exposed) (seam, prior step, or fixture unavailable)'); // emit seam not exposed
|
|
143
145
|
expect(emit.status).toBe(200);
|
|
144
146
|
|
|
145
147
|
const events = await queryTestEvents(runId, { type: 'provider.usage' });
|
|
146
|
-
if (!events.ok) return;
|
|
148
|
+
if (!events.ok) return softSkip('blocked', 'precondition not met — `!events.ok` returned early (seam, prior step, or fixture unavailable)');
|
|
147
149
|
expect(
|
|
148
150
|
events.events.length,
|
|
149
|
-
|
|
151
|
+
req('openwop.it.provider-usage.emit-seam-projects-exactly-one-provider-usage-event-with-required-fields-populat', 'RFC 0026 §B', 'emit-seam MUST project exactly one provider.usage event'),
|
|
150
152
|
).toBe(1);
|
|
151
153
|
const e = events.events[0]!;
|
|
152
154
|
expect(e.payload.provider).toBe('anthropic');
|
|
@@ -159,7 +161,7 @@ describe('provider-usage: event presence via emit-seam + event-log query (RFC 00
|
|
|
159
161
|
});
|
|
160
162
|
|
|
161
163
|
it('emit-seam refuses payloads containing credentialRef-shaped content (provider-usage-no-credential-leak invariant)', async () => {
|
|
162
|
-
if (!(await isEventLogSeamAvailable())) return;
|
|
164
|
+
if (!(await isEventLogSeamAvailable())) return softSkip('blocked', 'precondition not met — `!(await isEventLogSeamAvailable())` returned early (seam, prior step, or fixture unavailable)');
|
|
163
165
|
const runId = `r-pu-leak-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
164
166
|
// Inject a credentialRef-shaped field via a synthetic payload that
|
|
165
167
|
// contains 'secret:' in a string field. The seam's defense-in-depth
|
|
@@ -175,10 +177,10 @@ describe('provider-usage: event presence via emit-seam + event-log query (RFC 00
|
|
|
175
177
|
nodeId: 'secret:tenant:byok-anthropic:v1', // banned content
|
|
176
178
|
},
|
|
177
179
|
});
|
|
178
|
-
if (res.status === 404) return;
|
|
180
|
+
if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
179
181
|
expect(
|
|
180
182
|
res.status,
|
|
181
|
-
|
|
183
|
+
req('openwop.it.provider-usage.emit-seam-refuses-payloads-containing-credentialref-shaped-content-provider-usag', 'SECURITY/invariants.yaml provider-usage-no-credential-leak', 'payload with credentialRef-shaped content MUST be refused'),
|
|
182
184
|
).toBe(400);
|
|
183
185
|
expect(readErrorCode(res.json)).toBe('provider_usage_credential_leak');
|
|
184
186
|
await resetTestSeam();
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
|
|
31
31
|
import { describe, it, expect } from 'vitest';
|
|
32
32
|
import { driver } from '../lib/driver.js';
|
|
33
|
+
import { req } from '../lib/requirement-ids.js';
|
|
34
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
33
35
|
|
|
34
36
|
const CANONICAL_MODES = ['disabled', 'optional', 'required', 'restricted'] as const;
|
|
35
37
|
|
|
@@ -57,16 +59,16 @@ async function fetchPolicies(): Promise<PoliciesShape | null> {
|
|
|
57
59
|
describe('provider-policy-enforcement: closed mode set per spec/v1/capabilities.md §`aiProviders.policies`', () => {
|
|
58
60
|
it('every advertised mode is one of the four canonical values', async () => {
|
|
59
61
|
const policies = await fetchPolicies();
|
|
60
|
-
if (policies === null || !Array.isArray(policies.modes)) return;
|
|
62
|
+
if (policies === null || !Array.isArray(policies.modes)) return softSkip('blocked', 'precondition not met — `policies === null || !Array.isArray(policies.modes)` returned early (seam, prior step, or fixture unavailable)');
|
|
61
63
|
|
|
62
64
|
for (const mode of policies.modes) {
|
|
63
|
-
expect(typeof mode,
|
|
65
|
+
expect(typeof mode, req('openwop.it.providerPolicyEnforcement.every-advertised-mode-is-one-of-the-four-canonical-values',
|
|
64
66
|
'capabilities.md §"`aiProviders.policies`"',
|
|
65
67
|
'each entry in policies.modes MUST be a string',
|
|
66
68
|
)).toBe('string');
|
|
67
69
|
expect(
|
|
68
70
|
(CANONICAL_MODES as readonly string[]).includes(mode as string),
|
|
69
|
-
|
|
71
|
+
req('openwop.it.providerPolicyEnforcement.every-advertised-mode-is-one-of-the-four-canonical-values',
|
|
70
72
|
'capabilities.md §"`aiProviders.policies`"',
|
|
71
73
|
`mode "${String(mode)}" is not in the closed canonical set [${CANONICAL_MODES.join(', ')}]`,
|
|
72
74
|
),
|
|
@@ -76,10 +78,10 @@ describe('provider-policy-enforcement: closed mode set per spec/v1/capabilities.
|
|
|
76
78
|
|
|
77
79
|
it('hosts that support `restricted` MUST also support `optional` (default no-restriction case)', async () => {
|
|
78
80
|
const policies = await fetchPolicies();
|
|
79
|
-
if (policies === null || !Array.isArray(policies.modes)) return;
|
|
81
|
+
if (policies === null || !Array.isArray(policies.modes)) return softSkip('blocked', 'precondition not met — `policies === null || !Array.isArray(policies.modes)` returned early (seam, prior step, or fixture unavailable)');
|
|
80
82
|
const modes = policies.modes as string[];
|
|
81
|
-
if (!modes.includes('restricted')) return;
|
|
82
|
-
expect(modes.includes('optional'),
|
|
83
|
+
if (!modes.includes('restricted')) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!modes.includes(\'restricted\')` returned early');
|
|
84
|
+
expect(modes.includes('optional'), req('openwop.it.providerPolicyEnforcement.hosts-that-support-restricted-must-also-support-optional-default-no-restriction',
|
|
83
85
|
'spec/v1/profiles.md §`openwop-provider-policy`',
|
|
84
86
|
'a host advertising `restricted` MUST also advertise `optional` so workflows without policy hit the default permissive case',
|
|
85
87
|
)).toBe(true);
|
|
@@ -87,12 +89,12 @@ describe('provider-policy-enforcement: closed mode set per spec/v1/capabilities.
|
|
|
87
89
|
|
|
88
90
|
it('errorCode is a non-empty string when present', async () => {
|
|
89
91
|
const policies = await fetchPolicies();
|
|
90
|
-
if (policies === null || policies.errorCode === undefined) return;
|
|
91
|
-
expect(typeof policies.errorCode,
|
|
92
|
+
if (policies === null || policies.errorCode === undefined) return softSkip('blocked', 'precondition not met — `policies === null || policies.errorCode === undefined` returned early (seam, prior step, or fixture unavailable)');
|
|
93
|
+
expect(typeof policies.errorCode, req('openwop.it.providerPolicyEnforcement.errorcode-is-a-non-empty-string-when-present',
|
|
92
94
|
'capabilities.md §"`aiProviders.policies`"',
|
|
93
95
|
'aiProviders.policies.errorCode MUST be a string when present',
|
|
94
96
|
)).toBe('string');
|
|
95
|
-
expect((policies.errorCode as string).length,
|
|
97
|
+
expect((policies.errorCode as string).length, req('openwop.it.providerPolicyEnforcement.errorcode-is-a-non-empty-string-when-present',
|
|
96
98
|
'capabilities.md §"`aiProviders.policies`"',
|
|
97
99
|
'aiProviders.policies.errorCode MUST be non-empty when present',
|
|
98
100
|
)).toBeGreaterThan(0);
|
|
@@ -102,11 +104,11 @@ describe('provider-policy-enforcement: closed mode set per spec/v1/capabilities.
|
|
|
102
104
|
describe('provider-policy-enforcement: scope advertisement', () => {
|
|
103
105
|
it('scopes contains only non-empty strings when present', async () => {
|
|
104
106
|
const policies = await fetchPolicies();
|
|
105
|
-
if (policies === null) return;
|
|
106
|
-
if (!Array.isArray(policies.scopes)) return;
|
|
107
|
+
if (policies === null) return softSkip('blocked', 'precondition not met — `policies === null` returned early (seam, prior step, or fixture unavailable)');
|
|
108
|
+
if (!Array.isArray(policies.scopes)) return softSkip('blocked', 'precondition not met — `!Array.isArray(policies.scopes)` returned early (seam, prior step, or fixture unavailable)');
|
|
107
109
|
|
|
108
110
|
for (const scope of policies.scopes) {
|
|
109
|
-
expect(typeof scope === 'string' && scope.length > 0,
|
|
111
|
+
expect(typeof scope === 'string' && scope.length > 0, req('openwop.it.providerPolicyEnforcement.scopes-contains-only-non-empty-strings-when-present',
|
|
110
112
|
'capabilities.md §"`aiProviders.policies`"',
|
|
111
113
|
'each entry in policies.scopes MUST be a non-empty string',
|
|
112
114
|
)).toBe(true);
|
|
@@ -120,11 +122,11 @@ describe('provider-policy-enforcement: documented denial reasons enumeration', (
|
|
|
120
122
|
// file isn't updated, scenario authors will be surprised. This
|
|
121
123
|
// assertion catches that — an empty CANONICAL_MODES or DOCUMENTED_
|
|
122
124
|
// DENIAL_REASONS would indicate the test file got truncated.
|
|
123
|
-
expect(CANONICAL_MODES.length,
|
|
125
|
+
expect(CANONICAL_MODES.length, req('openwop.it.providerPolicyEnforcement.lists-are-non-empty-sanity-check-on-documentation-drift',
|
|
124
126
|
'spec/v1/capabilities.md §"`aiProviders.policies`"',
|
|
125
127
|
'closed mode set MUST be the four canonical values',
|
|
126
128
|
)).toBe(4);
|
|
127
|
-
expect(DOCUMENTED_DENIAL_REASONS.length,
|
|
129
|
+
expect(DOCUMENTED_DENIAL_REASONS.length, req('openwop.it.providerPolicyEnforcement.lists-are-non-empty-sanity-check-on-documentation-drift',
|
|
128
130
|
'openwop/openwop@0bebfb0 — denial-reason enum alignment',
|
|
129
131
|
'documented denial-reason set is non-empty',
|
|
130
132
|
)).toBeGreaterThan(0);
|