@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
|
@@ -29,6 +29,8 @@ import { behaviorGate } from '../lib/behavior-gate.js';
|
|
|
29
29
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
30
30
|
import { getA2AFakePeer } from '../lib/a2a-fake-peer.js';
|
|
31
31
|
import { readErrorCode } from '../lib/error-envelope.js';
|
|
32
|
+
import { req } from '../lib/requirement-ids.js';
|
|
33
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
32
34
|
|
|
33
35
|
/**
|
|
34
36
|
* Callback-shaped: the host issues A2A calls to the suite's fake peer, which records the negotiated version header.
|
|
@@ -63,7 +65,7 @@ describe('RFC 0152 §B — A2A version negotiation', () => {
|
|
|
63
65
|
const caps = await a2a();
|
|
64
66
|
expect(
|
|
65
67
|
caps?.protocolVersions ?? [],
|
|
66
|
-
|
|
68
|
+
req('openwop.it.a2a-version-negotiation.the-advertised-preferred-version-is-one-the-host-actually-claims',
|
|
67
69
|
'RFCS/0152-a2a-1-0-versioned-composition.md §A',
|
|
68
70
|
'`preferredVersion` MUST be present in `protocolVersions`. Preferring a version you do ' +
|
|
69
71
|
'not list is a claim no peer can act on.',
|
|
@@ -74,28 +76,28 @@ describe('RFC 0152 §B — A2A version negotiation', () => {
|
|
|
74
76
|
it('outbound calls carry an explicit A2A-Version header', async () => {
|
|
75
77
|
if (!behaviorGate(PROFILE, await negotiationAdvertised())) return;
|
|
76
78
|
const peer = getA2AFakePeer();
|
|
77
|
-
if (peer === null) return; // no fake peer wired in this run
|
|
79
|
+
if (peer === null) return softSkip('blocked', 'precondition not met — `peer === null` returned early (no fake peer wired in this run) (seam, prior step, or fixture unavailable)'); // no fake peer wired in this run
|
|
78
80
|
peer.reset();
|
|
79
81
|
const drive = await driver.post('/v1/host/sample/a2a/invoke', { peerUrl: peer.endpoint() });
|
|
80
82
|
if (drive.status === 404 || drive.status === 403) {
|
|
81
83
|
// Seam absent. RFC 0148 §A: unobservable resolves to `blocked`, not a pass.
|
|
82
84
|
expect(
|
|
83
85
|
drive.status,
|
|
84
|
-
|
|
86
|
+
req('openwop.it.a2a-version-negotiation.outbound-calls-carry-an-explicit-a2a-version-header',
|
|
85
87
|
'RFCS/0152 §B',
|
|
86
88
|
'a host advertising A2A version negotiation MUST expose an invoke seam so the negotiated ' +
|
|
87
89
|
'version is observable. Without it the requirement cannot be witnessed and resolves to ' +
|
|
88
90
|
'`blocked` per RFC 0148 §A.',
|
|
89
91
|
),
|
|
90
92
|
).not.toBe(404);
|
|
91
|
-
return;
|
|
93
|
+
return softSkip('blocked', 'precondition not met — `drive.status === 404 || drive.status === 403` returned early (seam, prior step, or fixture unavailable)');
|
|
92
94
|
}
|
|
93
95
|
const calls = peer.invocations().filter((i: { method: string }) => i.method !== 'GET');
|
|
94
|
-
expect(calls.length, 'the host MUST have called the peer').toBeGreaterThan(0);
|
|
96
|
+
expect(calls.length, req('openwop.it.a2a-version-negotiation.outbound-calls-carry-an-explicit-a2a-version-header', 'RFCS/0152 §B', 'the host MUST have called the peer')).toBeGreaterThan(0);
|
|
95
97
|
for (const c of calls) {
|
|
96
98
|
expect(
|
|
97
99
|
c.headers['a2a-version'],
|
|
98
|
-
|
|
100
|
+
req('openwop.it.a2a-version-negotiation.outbound-calls-carry-an-explicit-a2a-version-header',
|
|
99
101
|
'RFCS/0152 §B',
|
|
100
102
|
'the sender MUST send `A2A-Version`. An absent header leaves the receiver guessing, and ' +
|
|
101
103
|
'a guess that happens to be right is not a negotiation.',
|
|
@@ -107,7 +109,7 @@ describe('RFC 0152 §B — A2A version negotiation', () => {
|
|
|
107
109
|
it('an authenticated request is not silently downgraded', async () => {
|
|
108
110
|
if (!behaviorGate(PROFILE, await negotiationAdvertised())) return;
|
|
109
111
|
const peer = getA2AFakePeer();
|
|
110
|
-
if (peer === null) return;
|
|
112
|
+
if (peer === null) return softSkip('blocked', 'precondition not met — `peer === null` returned early (seam, prior step, or fixture unavailable)');
|
|
111
113
|
const caps = await a2a();
|
|
112
114
|
const preferred = caps?.preferredVersion;
|
|
113
115
|
peer.reset();
|
|
@@ -116,7 +118,7 @@ describe('RFC 0152 §B — A2A version negotiation', () => {
|
|
|
116
118
|
authenticated: true,
|
|
117
119
|
peerOffersOnly: '0.3',
|
|
118
120
|
});
|
|
119
|
-
if (drive.status === 404 || drive.status === 403) return; // covered above
|
|
121
|
+
if (drive.status === 404 || drive.status === 403) return softSkip('blocked', 'precondition not met — `drive.status === 404 || drive.status === 403` returned early (covered above) (seam, prior step, or fixture unavailable)'); // covered above
|
|
120
122
|
|
|
121
123
|
// THE load-bearing leg. A silent downgrade is dangerous precisely because it
|
|
122
124
|
// SUCCEEDS: the caller believes it negotiated `preferred`, the peer answered
|
|
@@ -127,7 +129,7 @@ describe('RFC 0152 §B — A2A version negotiation', () => {
|
|
|
127
129
|
const negotiated = (drive.json as { negotiatedVersion?: string }).negotiatedVersion;
|
|
128
130
|
expect(
|
|
129
131
|
negotiated,
|
|
130
|
-
|
|
132
|
+
req('openwop.it.a2a-version-negotiation.an-authenticated-request-is-not-silently-downgraded',
|
|
131
133
|
'RFCS/0152 §B',
|
|
132
134
|
'a host that proceeds after a downgrade MUST report the version it actually negotiated. ' +
|
|
133
135
|
'Reporting the preferred version while having used a lower one is the silent downgrade ' +
|
|
@@ -138,7 +140,7 @@ describe('RFC 0152 §B — A2A version negotiation', () => {
|
|
|
138
140
|
for (const c of calls) {
|
|
139
141
|
expect(
|
|
140
142
|
c.headers['a2a-version'],
|
|
141
|
-
|
|
143
|
+
req('openwop.it.a2a-version-negotiation.an-authenticated-request-is-not-silently-downgraded', 'RFCS/0152 §B', 'the wire header MUST match the reported negotiated version'),
|
|
142
144
|
).toBe(negotiated);
|
|
143
145
|
}
|
|
144
146
|
}
|
|
@@ -147,18 +149,18 @@ describe('RFC 0152 §B — A2A version negotiation', () => {
|
|
|
147
149
|
it('an unsupported version fails through the canonical interop error envelope', async () => {
|
|
148
150
|
if (!behaviorGate(PROFILE, await negotiationAdvertised())) return;
|
|
149
151
|
const peer = getA2AFakePeer();
|
|
150
|
-
if (peer === null) return;
|
|
152
|
+
if (peer === null) return softSkip('blocked', 'precondition not met — `peer === null` returned early (seam, prior step, or fixture unavailable)');
|
|
151
153
|
const drive = await driver.post('/v1/host/sample/a2a/invoke', {
|
|
152
154
|
peerUrl: peer.endpoint(),
|
|
153
155
|
requestVersion: '99.0',
|
|
154
156
|
});
|
|
155
|
-
if (drive.status === 404 || drive.status === 403) return;
|
|
156
|
-
expect(drive.status >= 400,
|
|
157
|
+
if (drive.status === 404 || drive.status === 403) return softSkip('blocked', 'precondition not met — `drive.status === 404 || drive.status === 403` returned early (seam, prior step, or fixture unavailable)');
|
|
158
|
+
expect(drive.status >= 400, req('openwop.it.a2a-version-negotiation.an-unsupported-version-fails-through-the-canonical-interop-error-envelope', 'RFCS/0152 §B', 'an unsupported version MUST fail')).toBe(true);
|
|
157
159
|
// S28: canonical envelope is FLAT (`error: "<code>"`, error-envelope.schema.json);
|
|
158
160
|
// the nested `error.code` is tolerated by the lib only through the deprecation window.
|
|
159
161
|
expect(
|
|
160
162
|
readErrorCode(drive.json),
|
|
161
|
-
|
|
163
|
+
req('openwop.it.a2a-version-negotiation.an-unsupported-version-fails-through-the-canonical-interop-error-envelope',
|
|
162
164
|
'RFCS/0152 §B',
|
|
163
165
|
'the upstream version error MUST be projected through the canonical OpenWOP interop error ' +
|
|
164
166
|
'envelope when the failure crosses an OpenWOP boundary — a raw upstream body leaves the ' +
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
import { describe, it, expect } from 'vitest';
|
|
19
19
|
import { driver } from '../lib/driver.js';
|
|
20
|
+
import { req } from '../lib/requirement-ids.js';
|
|
21
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
20
22
|
|
|
21
23
|
const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
|
|
22
24
|
const UNIVERSAL_KINDS = ['clarification.request', 'schema.request', 'schema.response', 'error'];
|
|
@@ -25,7 +27,7 @@ describe('a2ui-surface-degrades: optional-advertised, not universal (RFC 0102 §
|
|
|
25
27
|
it('ui.a2ui-surface is NOT a MUST-recognize universal kind', () => {
|
|
26
28
|
expect(
|
|
27
29
|
UNIVERSAL_KINDS.includes('ui.a2ui-surface'),
|
|
28
|
-
'ai-envelope.md §"A2UI surfaces": ui.a2ui-surface MUST be optional/advertised so an unrecognizing consumer may store-without-render',
|
|
30
|
+
req('openwop.it.a2ui-surface-degrades.ui-a2ui-surface-is-not-a-must-recognize-universal-kind', 'RFC 0102 §A', 'ai-envelope.md §"A2UI surfaces": ui.a2ui-surface MUST be optional/advertised so an unrecognizing consumer may store-without-render'),
|
|
29
31
|
).toBe(false);
|
|
30
32
|
});
|
|
31
33
|
});
|
|
@@ -43,12 +45,12 @@ describe.skipIf(HTTP_SKIP)('a2ui-surface-degrades: unadvertised kind is gated, r
|
|
|
43
45
|
},
|
|
44
46
|
hostSupportedEnvelopes: ['clarification.request'], // does not advertise ui.a2ui-surface
|
|
45
47
|
});
|
|
46
|
-
if (res.status === 404) return; // seam absent — soft-skip
|
|
48
|
+
if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam absent — soft-skip) (seam, prior step, or fixture unavailable)'); // seam absent — soft-skip
|
|
47
49
|
expect(res.status).toBe(200);
|
|
48
50
|
const body = res.json as { status?: string };
|
|
49
51
|
expect(
|
|
50
52
|
body.status,
|
|
51
|
-
|
|
53
|
+
req('openwop.it.a2ui-surface-degrades.posting-ui-a2ui-surface-to-a-host-that-does-not-advertise-it-gated-not-failed', 'RFC 0102 §A (N6)', 'an unadvertised ui.a2ui-surface MUST be gated, never crash the run'),
|
|
52
54
|
).toBe('gated');
|
|
53
55
|
});
|
|
54
56
|
});
|
|
@@ -47,6 +47,8 @@ import { join } from 'node:path';
|
|
|
47
47
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
48
48
|
import { driver } from '../lib/driver.js';
|
|
49
49
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
50
|
+
import { req } from '../lib/requirement-ids.js';
|
|
51
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
50
52
|
|
|
51
53
|
const ajv = new Ajv2020({ strict: false, allErrors: true });
|
|
52
54
|
|
|
@@ -270,7 +272,7 @@ function fullSurfaceMaterializedAfterUpdates(): { [key: string]: JsonValue } {
|
|
|
270
272
|
|
|
271
273
|
describe('a2ui-surface-delta-transport: frame schema + op set (RFC 0114)', () => {
|
|
272
274
|
it('a2ui-surface-delta-frame.schema.json compiles under Ajv2020', () => {
|
|
273
|
-
expect(validateFrame, 'RFC 0114: the delta-frame schema MUST compile').toBeTypeOf('function');
|
|
275
|
+
expect(validateFrame, req('openwop.it.a2ui-surface-delta-transport.a2ui-surface-delta-frame-schema-json-compiles-under-ajv2020', 'RFC 0114 §"Delta transport', 'RFC 0114: the delta-frame schema MUST compile')).toBeTypeOf('function');
|
|
274
276
|
});
|
|
275
277
|
|
|
276
278
|
it('accepts a positive delta frame', () => {
|
|
@@ -281,18 +283,18 @@ describe('a2ui-surface-delta-transport: frame schema + op set (RFC 0114)', () =>
|
|
|
281
283
|
};
|
|
282
284
|
expect(
|
|
283
285
|
validateFrame(frame),
|
|
284
|
-
`RFC 0114: a positive delta frame MUST validate; errors: ${JSON.stringify(validateFrame.errors)}
|
|
286
|
+
req('openwop.it.a2ui-surface-delta-transport.accepts-a-positive-delta-frame', 'RFC 0114 §"Delta transport', `RFC 0114: a positive delta frame MUST validate; errors: ${JSON.stringify(validateFrame.errors)}`),
|
|
285
287
|
).toBe(true);
|
|
286
288
|
});
|
|
287
289
|
|
|
288
290
|
it('rejects a frame missing the required `surfaceRef`/`catalogVersion`/`patch`', () => {
|
|
289
|
-
expect(validateFrame({ catalogVersion: CATALOG_VERSION, patch: [{ op: 'replace', path: '/x' }] })).toBe(false);
|
|
291
|
+
expect(validateFrame({ catalogVersion: CATALOG_VERSION, patch: [{ op: 'replace', path: '/x' }] }), req('openwop.it.a2ui-surface-delta-transport.rejects-a-frame-missing-the-required-surfaceref-catalogversion-patch', 'RFC 0114 §"Delta transport', 'rejects a frame missing the required `surfaceRef`/`catalogVersion`/`patch`')).toBe(false);
|
|
290
292
|
expect(validateFrame({ surfaceRef: 'e', patch: [{ op: 'replace', path: '/x' }] })).toBe(false);
|
|
291
293
|
expect(validateFrame({ surfaceRef: 'e', catalogVersion: CATALOG_VERSION })).toBe(false);
|
|
292
294
|
});
|
|
293
295
|
|
|
294
296
|
it('rejects an empty patch (minItems: 1)', () => {
|
|
295
|
-
expect(validateFrame({ surfaceRef: 'e', catalogVersion: CATALOG_VERSION, patch: [] })).toBe(false);
|
|
297
|
+
expect(validateFrame({ surfaceRef: 'e', catalogVersion: CATALOG_VERSION, patch: [] }), req('openwop.it.a2ui-surface-delta-transport.rejects-an-empty-patch-minitems-1', 'RFC 0114 §"Delta transport', 'rejects an empty patch (minItems: 1)')).toBe(false);
|
|
296
298
|
});
|
|
297
299
|
|
|
298
300
|
it('EXCLUDES the `test` op from the op enum', () => {
|
|
@@ -303,7 +305,7 @@ describe('a2ui-surface-delta-transport: frame schema + op set (RFC 0114)', () =>
|
|
|
303
305
|
};
|
|
304
306
|
expect(
|
|
305
307
|
validateFrame(frame),
|
|
306
|
-
'RFC 0114: a fire-and-forget transport frame cannot act on a failed conditional; `test` is excluded',
|
|
308
|
+
req('openwop.it.a2ui-surface-delta-transport.excludes-the-test-op-from-the-op-enum', 'RFC 0114 §"Delta transport', 'RFC 0114: a fire-and-forget transport frame cannot act on a failed conditional; `test` is excluded'),
|
|
307
309
|
).toBe(false);
|
|
308
310
|
});
|
|
309
311
|
|
|
@@ -313,7 +315,7 @@ describe('a2ui-surface-delta-transport: frame schema + op set (RFC 0114)', () =>
|
|
|
313
315
|
catalogVersion: CATALOG_VERSION,
|
|
314
316
|
patch: [{ op: 'replace', path: '/x', value: 1, onApply: "fetch('https://evil')" }],
|
|
315
317
|
};
|
|
316
|
-
expect(validateFrame(frame)).toBe(false);
|
|
318
|
+
expect(validateFrame(frame), req('openwop.it.a2ui-surface-delta-transport.rejects-a-patch-item-carrying-an-out-of-set-property-additionalproperties-false', 'RFC 0114 §"Delta transport', 'rejects a patch item carrying an out-of-set property (additionalProperties:false)')).toBe(false);
|
|
317
319
|
});
|
|
318
320
|
});
|
|
319
321
|
|
|
@@ -337,11 +339,11 @@ describe('a2ui-surface-delta-transport: full + deltas reconstruct the materializ
|
|
|
337
339
|
];
|
|
338
340
|
|
|
339
341
|
for (const frame of frames) {
|
|
340
|
-
expect(validateFrame(frame), `RFC 0114: each delta frame MUST validate; ${JSON.stringify(validateFrame.errors)}`).toBe(true);
|
|
342
|
+
expect(validateFrame(frame), req('openwop.it.a2ui-surface-delta-transport.a-full-surface-delta-frames-reconstruct-the-tree-and-equal-the-non-negotiating-f', 'RFC 0114 §"Delta transport', `RFC 0114: each delta frame MUST validate; ${JSON.stringify(validateFrame.errors)}`)).toBe(true);
|
|
341
343
|
// catalogVersion on a delta MUST equal the referenced full surface's.
|
|
342
344
|
expect(
|
|
343
345
|
frame.catalogVersion,
|
|
344
|
-
'RFC 0114: a delta frame\'s catalogVersion MUST equal the referenced full surface\'s',
|
|
346
|
+
req('openwop.it.a2ui-surface-delta-transport.a-full-surface-delta-frames-reconstruct-the-tree-and-equal-the-non-negotiating-f', 'RFC 0114 §"Delta transport', 'RFC 0114: a delta frame\'s catalogVersion MUST equal the referenced full surface\'s'),
|
|
345
347
|
).toBe(full.catalogVersion);
|
|
346
348
|
}
|
|
347
349
|
|
|
@@ -354,13 +356,13 @@ describe('a2ui-surface-delta-transport: full + deltas reconstruct the materializ
|
|
|
354
356
|
// (a) the reconstruction equals the full surface a non-negotiating subscriber materializes.
|
|
355
357
|
expect(
|
|
356
358
|
reconstructed,
|
|
357
|
-
'RFC 0114: the delta reconstruction MUST equal the full surface the host materializes for a non-negotiating subscriber',
|
|
359
|
+
req('openwop.it.a2ui-surface-delta-transport.a-full-surface-delta-frames-reconstruct-the-tree-and-equal-the-non-negotiating-f', 'RFC 0114 §"Delta transport', 'RFC 0114: the delta reconstruction MUST equal the full surface the host materializes for a non-negotiating subscriber'),
|
|
358
360
|
).toEqual(fullSurfaceMaterializedAfterUpdates());
|
|
359
361
|
|
|
360
362
|
// (b) the reconstruction re-validates against the closed catalog before render.
|
|
361
363
|
expect(
|
|
362
364
|
validateRecorded(reconstructed),
|
|
363
|
-
`RFC 0114: the post-patch surface MUST re-validate against the closed catalog; ${JSON.stringify(validateRecorded.errors)}
|
|
365
|
+
req('openwop.it.a2ui-surface-delta-transport.a-full-surface-delta-frames-reconstruct-the-tree-and-equal-the-non-negotiating-f', 'RFC 0114 §"Delta transport', `RFC 0114: the post-patch surface MUST re-validate against the closed catalog; ${JSON.stringify(validateRecorded.errors)}`),
|
|
364
366
|
).toBe(true);
|
|
365
367
|
});
|
|
366
368
|
});
|
|
@@ -390,7 +392,7 @@ describe('a2ui-surface-delta-transport: out-of-catalog delta fails closed (RFC 0
|
|
|
390
392
|
// The `a2ui-surface-no-code-exec` boundary holds on the post-patch surface.
|
|
391
393
|
expect(
|
|
392
394
|
validateRecorded(postPatch),
|
|
393
|
-
'RFC 0114 / a2ui-surface-no-code-exec: an out-of-catalog component reached by a delta MUST fail closed-catalog validation post-patch',
|
|
395
|
+
req('openwop.it.a2ui-surface-delta-transport.a-delta-that-add-s-an-out-of-catalog-component-yields-a-post-patch-surface-that', 'RFC 0114 §"Delta transport', 'RFC 0114 / a2ui-surface-no-code-exec: an out-of-catalog component reached by a delta MUST fail closed-catalog validation post-patch'),
|
|
394
396
|
).toBe(false);
|
|
395
397
|
});
|
|
396
398
|
|
|
@@ -411,7 +413,7 @@ describe('a2ui-surface-delta-transport: out-of-catalog delta fails closed (RFC 0
|
|
|
411
413
|
const postPatch = applyPatch(full, frame.patch);
|
|
412
414
|
expect(
|
|
413
415
|
validateRecorded(postPatch),
|
|
414
|
-
'RFC 0114: a delta MUST NOT be a path by which a smuggled code field reaches render',
|
|
416
|
+
req('openwop.it.a2ui-surface-delta-transport.a-delta-replace-smuggling-a-script-bearing-property-also-fails-closed-catalog-va', 'RFC 0114 §"Delta transport', 'RFC 0114: a delta MUST NOT be a path by which a smuggled code field reaches render'),
|
|
415
417
|
).toBe(false);
|
|
416
418
|
});
|
|
417
419
|
});
|
|
@@ -425,14 +427,14 @@ describe('a2ui-surface-delta-transport: the recorded envelope is always full (RF
|
|
|
425
427
|
};
|
|
426
428
|
expect(
|
|
427
429
|
validateRecorded(frame),
|
|
428
|
-
'RFC 0114: the recorded envelope is NEVER a delta — a delta frame MUST NOT validate as a recorded ui.a2ui-surface payload',
|
|
430
|
+
req('openwop.it.a2ui-surface-delta-transport.a-delta-frame-does-not-validate-against-the-recorded-ui-a2ui-surface-envelope-sc', 'RFC 0114 §"Delta transport', 'RFC 0114: the recorded envelope is NEVER a delta — a delta frame MUST NOT validate as a recorded ui.a2ui-surface payload'),
|
|
429
431
|
).toBe(false);
|
|
430
432
|
});
|
|
431
433
|
|
|
432
434
|
it('the full surface validates as the recorded ui.a2ui-surface payload (event-log read / replay shape)', () => {
|
|
433
435
|
expect(
|
|
434
436
|
validateRecorded(fullSurfaceV0()),
|
|
435
|
-
'RFC 0114: the event-log read / replay always sees the FULL surface, which MUST validate as the recorded payload',
|
|
437
|
+
req('openwop.it.a2ui-surface-delta-transport.the-full-surface-validates-as-the-recorded-ui-a2ui-surface-payload-event-log-rea', 'RFC 0114 §"Delta transport', 'RFC 0114: the event-log read / replay always sees the FULL surface, which MUST validate as the recorded payload'),
|
|
436
438
|
).toBe(true);
|
|
437
439
|
});
|
|
438
440
|
});
|
|
@@ -507,9 +509,9 @@ async function getEvents(runId: string, deltaOptIn: boolean): Promise<unknown> {
|
|
|
507
509
|
describe.skipIf(HTTP_SKIP)('a2ui-surface-delta-transport: live host delta transport (RFC 0114, gated)', () => {
|
|
508
510
|
it('drives the emit-surface seam: a ?a2uiDelta=1 subscriber reconstruction equals the non-negotiating full', async () => {
|
|
509
511
|
const disco = await driver.get('/.well-known/openwop');
|
|
510
|
-
if (disco.status !== 200) return; // no discovery — soft-skip
|
|
512
|
+
if (disco.status !== 200) return softSkip('blocked', 'precondition not met — `disco.status !== 200` returned early (no discovery — soft-skip) (seam, prior step, or fixture unavailable)'); // no discovery — soft-skip
|
|
511
513
|
const cap = capabilityFamily<A2uiSurfaceCap>(disco.json, 'a2uiSurface');
|
|
512
|
-
if (cap?.deltaTransport !== true) return; // capability not advertised — soft-skip
|
|
514
|
+
if (cap?.deltaTransport !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap?.deltaTransport !== true` returned early (capability not advertised — soft-skip)'); // capability not advertised — soft-skip
|
|
513
515
|
|
|
514
516
|
const runId = `conf-a2ui-delta-${Date.now()}`;
|
|
515
517
|
const surfaceA = fullSurfaceV0();
|
|
@@ -517,53 +519,53 @@ describe.skipIf(HTTP_SKIP)('a2ui-surface-delta-transport: live host delta transp
|
|
|
517
519
|
|
|
518
520
|
// Baseline (surface A) through the host's REAL surface-emit path.
|
|
519
521
|
const emitA = await driver.post('/v1/host/sample/a2ui/emit-surface', { runId, surface: surfaceA });
|
|
520
|
-
if (emitA.status === 404 || emitA.status === 405) return; // seam absent — soft-skip the live leg
|
|
522
|
+
if (emitA.status === 404 || emitA.status === 405) return softSkip('blocked', 'precondition not met — `emitA.status === 404 || emitA.status === 405` returned early (seam absent — soft-skip the live leg) (seam, prior step, or fixture unavailable)'); // seam absent — soft-skip the live leg
|
|
521
523
|
expect(
|
|
522
524
|
emitA.status,
|
|
523
|
-
|
|
525
|
+
req('openwop.it.a2ui-surface-delta-transport.drives-the-emit-surface-seam-a-a2uidelta-1-subscriber-reconstruction-equals-the', 'RFC 0114 §15', 'the emit-surface seam MUST record the baseline full surface'),
|
|
524
526
|
).toBeLessThan(300);
|
|
525
527
|
|
|
526
528
|
// Second full surface (surface B) — recorded full AND transported as a delta
|
|
527
529
|
// to any ?a2uiDelta=1 subscriber.
|
|
528
530
|
const emitB = await driver.post('/v1/host/sample/a2ui/emit-surface', { runId, surface: surfaceB });
|
|
529
|
-
if (emitB.status === 404 || emitB.status === 405) return;
|
|
530
|
-
expect(emitB.status,
|
|
531
|
+
if (emitB.status === 404 || emitB.status === 405) return softSkip('blocked', 'precondition not met — `emitB.status === 404 || emitB.status === 405` returned early (seam, prior step, or fixture unavailable)');
|
|
532
|
+
expect(emitB.status, req('openwop.it.a2ui-surface-delta-transport.drives-the-emit-surface-seam-a-a2uidelta-1-subscriber-reconstruction-equals-the', 'RFC 0114 §15', 'the second emit MUST succeed')).toBeLessThan(300);
|
|
531
533
|
const refB = (emitB.json as EmitSurfaceResponse)?.surfaceRef;
|
|
532
534
|
|
|
533
535
|
// ?a2uiDelta=1 subscriber: locate the delta frame the host transported.
|
|
534
536
|
const deltaEvents = await getEvents(runId, true);
|
|
535
|
-
if (deltaEvents === null) return; // events stream unavailable in JSON — soft-skip
|
|
537
|
+
if (deltaEvents === null) return softSkip('blocked', 'precondition not met — `deltaEvents === null` returned early (events stream unavailable in JSON — soft-skip) (seam, prior step, or fixture unavailable)'); // events stream unavailable in JSON — soft-skip
|
|
536
538
|
const frames = collectMatching(deltaEvents, validateFrame)
|
|
537
539
|
.map(readFrame)
|
|
538
540
|
.filter((f): f is { surfaceRef: string; catalogVersion: string; patch: PatchOp[] } => f !== null)
|
|
539
541
|
.filter((f) => refB === undefined || f.surfaceRef === refB);
|
|
540
|
-
if (frames.length === 0) return; // host streams SSE-only or buffered the frame — soft-skip
|
|
542
|
+
if (frames.length === 0) return softSkip('blocked', 'precondition not met — `frames.length === 0` returned early (host streams SSE-only or buffered the frame — soft-skip) (seam, prior step, or fixture unavailable)'); // host streams SSE-only or buffered the frame — soft-skip
|
|
541
543
|
const frame = frames[frames.length - 1];
|
|
542
544
|
|
|
543
545
|
// catalogVersion on the delta MUST equal the baseline full surface's.
|
|
544
546
|
expect(
|
|
545
547
|
frame.catalogVersion,
|
|
546
|
-
|
|
548
|
+
req('openwop.it.a2ui-surface-delta-transport.drives-the-emit-surface-seam-a-a2uidelta-1-subscriber-reconstruction-equals-the', 'RFC 0114', 'a delta frame catalogVersion MUST equal the referenced full surface'),
|
|
547
549
|
).toBe(surfaceA.catalogVersion);
|
|
548
550
|
|
|
549
551
|
// Reconstruct: apply the host's real delta to the baseline, re-validate against the closed catalog.
|
|
550
552
|
const reconstructed = applyPatch(surfaceA, frame.patch);
|
|
551
553
|
expect(
|
|
552
554
|
validateRecorded(reconstructed),
|
|
553
|
-
|
|
555
|
+
req('openwop.it.a2ui-surface-delta-transport.drives-the-emit-surface-seam-a-a2uidelta-1-subscriber-reconstruction-equals-the', 'RFC 0114 §"Delta transport"', 'the post-patch surface MUST re-validate against the closed catalog'),
|
|
554
556
|
).toBe(true);
|
|
555
557
|
|
|
556
558
|
// Non-negotiating subscriber: the host materializes the FULL surface for the same update.
|
|
557
559
|
const fullEvents = await getEvents(runId, false);
|
|
558
|
-
if (fullEvents === null) return;
|
|
560
|
+
if (fullEvents === null) return softSkip('blocked', 'precondition not met — `fullEvents === null` returned early (seam, prior step, or fixture unavailable)');
|
|
559
561
|
const fulls = collectMatching(fullEvents, validateRecorded);
|
|
560
|
-
if (fulls.length === 0) return; // soft-skip — no full surface observed on the non-delta stream
|
|
562
|
+
if (fulls.length === 0) return softSkip('blocked', 'precondition not met — `fulls.length === 0` returned early (soft-skip — no full surface observed on the non-delta stream) (seam, prior step, or fixture unavailable)'); // soft-skip — no full surface observed on the non-delta stream
|
|
561
563
|
const materializedFull = fulls[fulls.length - 1];
|
|
562
564
|
|
|
563
565
|
// delta and full agree — the core RFC 0114 transport guarantee, witnessed live.
|
|
564
566
|
expect(
|
|
565
567
|
reconstructed,
|
|
566
|
-
|
|
568
|
+
req('openwop.it.a2ui-surface-delta-transport.drives-the-emit-surface-seam-a-a2uidelta-1-subscriber-reconstruction-equals-the',
|
|
567
569
|
'RFC 0114 §"Delta transport"',
|
|
568
570
|
'a ?a2uiDelta=1 reconstruction MUST equal the full surface a non-negotiating subscriber materializes',
|
|
569
571
|
),
|
|
@@ -572,14 +574,14 @@ describe.skipIf(HTTP_SKIP)('a2ui-surface-delta-transport: live host delta transp
|
|
|
572
574
|
|
|
573
575
|
it('the emit-surface seam rejects an out-of-catalog surface fail-closed (real catalog gate)', async () => {
|
|
574
576
|
const disco = await driver.get('/.well-known/openwop');
|
|
575
|
-
if (disco.status !== 200) return;
|
|
577
|
+
if (disco.status !== 200) return softSkip('blocked', 'precondition not met — `disco.status !== 200` returned early (seam, prior step, or fixture unavailable)');
|
|
576
578
|
const cap = capabilityFamily<A2uiSurfaceCap>(disco.json, 'a2uiSurface');
|
|
577
|
-
if (cap?.deltaTransport !== true) return;
|
|
579
|
+
if (cap?.deltaTransport !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap?.deltaTransport !== true` returned early');
|
|
578
580
|
|
|
579
581
|
const runId = `conf-a2ui-delta-oob-${Date.now()}`;
|
|
580
582
|
const baseline = await driver.post('/v1/host/sample/a2ui/emit-surface', { runId, surface: fullSurfaceV0() });
|
|
581
|
-
if (baseline.status === 404 || baseline.status === 405) return; // seam absent — soft-skip
|
|
582
|
-
expect(baseline.status,
|
|
583
|
+
if (baseline.status === 404 || baseline.status === 405) return softSkip('blocked', 'precondition not met — `baseline.status === 404 || baseline.status === 405` returned early (seam absent — soft-skip) (seam, prior step, or fixture unavailable)'); // seam absent — soft-skip
|
|
584
|
+
expect(baseline.status, req('openwop.it.a2ui-surface-delta-transport.the-emit-surface-seam-rejects-an-out-of-catalog-surface-fail-closed-real-catalog', 'RFC 0114 §15', 'baseline emit MUST succeed')).toBeLessThan(300);
|
|
583
585
|
|
|
584
586
|
// An out-of-catalog surface MUST be rejected by the host's REAL closed-catalog
|
|
585
587
|
// validator — no delta transported, the a2ui-surface-no-code-exec boundary holds.
|
|
@@ -588,10 +590,10 @@ describe.skipIf(HTTP_SKIP)('a2ui-surface-delta-transport: live host delta transp
|
|
|
588
590
|
surface: { components: [{ component: 'iframe', src: 'https://evil.example/x' }] },
|
|
589
591
|
};
|
|
590
592
|
const rejected = await driver.post('/v1/host/sample/a2ui/emit-surface', { runId, surface: outOfCatalog });
|
|
591
|
-
if (rejected.status === 404 || rejected.status === 405) return;
|
|
593
|
+
if (rejected.status === 404 || rejected.status === 405) return softSkip('blocked', 'precondition not met — `rejected.status === 404 || rejected.status === 405` returned early (seam, prior step, or fixture unavailable)');
|
|
592
594
|
expect(
|
|
593
595
|
rejected.status,
|
|
594
|
-
|
|
596
|
+
req('openwop.it.a2ui-surface-delta-transport.the-emit-surface-seam-rejects-an-out-of-catalog-surface-fail-closed-real-catalog',
|
|
595
597
|
'RFC 0114 §15 / a2ui-surface-no-code-exec',
|
|
596
598
|
'an out-of-catalog surface MUST be rejected fail-closed by the host real catalog validator',
|
|
597
599
|
),
|
|
@@ -21,6 +21,8 @@ import { readFileSync } from 'node:fs';
|
|
|
21
21
|
import { join } from 'node:path';
|
|
22
22
|
import { driver } from '../lib/driver.js';
|
|
23
23
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
24
|
+
import { req } from '../lib/requirement-ids.js';
|
|
25
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
24
26
|
|
|
25
27
|
const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
|
|
26
28
|
const schema = JSON.parse(
|
|
@@ -45,7 +47,7 @@ describe('a2ui-surface-replay: self-contained surface (RFC 0102 §A.6)', () => {
|
|
|
45
47
|
const external = refs.filter((r) => !r.startsWith('#'));
|
|
46
48
|
expect(
|
|
47
49
|
external,
|
|
48
|
-
'RFC 0102 §A.6: a stored surface MUST be self-contained for deterministic :fork/replay (no live external-catalog ref)',
|
|
50
|
+
req('openwop.it.a2ui-surface-replay.all-refs-are-internal-surface-renders-from-the-payload-alone-on-replay', 'RFC 0102 §A.6', 'RFC 0102 §A.6: a stored surface MUST be self-contained for deterministic :fork/replay (no live external-catalog ref)'),
|
|
49
51
|
).toEqual([]);
|
|
50
52
|
});
|
|
51
53
|
});
|
|
@@ -63,7 +65,7 @@ describe.skipIf(HTTP_SKIP)('a2ui-surface-replay: correlationId conflict on type
|
|
|
63
65
|
envelope: { ...base, type: 'ui.a2ui-surface', envelopeId: 'env-a2ui-rep-1' },
|
|
64
66
|
hostSupportedEnvelopes: ['ui.a2ui-surface', 'clarification.request'],
|
|
65
67
|
});
|
|
66
|
-
if (first.status === 404) return; // seam absent — soft-skip
|
|
68
|
+
if (first.status === 404) return softSkip('blocked', 'precondition not met — `first.status === 404` returned early (seam absent — soft-skip) (seam, prior step, or fixture unavailable)'); // seam absent — soft-skip
|
|
67
69
|
|
|
68
70
|
const conflict = await driver.post('/v1/host/sample/envelope/accept', {
|
|
69
71
|
envelope: {
|
|
@@ -74,11 +76,11 @@ describe.skipIf(HTTP_SKIP)('a2ui-surface-replay: correlationId conflict on type
|
|
|
74
76
|
},
|
|
75
77
|
hostSupportedEnvelopes: ['ui.a2ui-surface', 'clarification.request'],
|
|
76
78
|
});
|
|
77
|
-
if (conflict.status === 404) return;
|
|
79
|
+
if (conflict.status === 404) return softSkip('blocked', 'precondition not met — `conflict.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
78
80
|
const body = conflict.json as { status?: string; reason?: string };
|
|
79
81
|
expect(
|
|
80
82
|
body.reason ?? '',
|
|
81
|
-
|
|
83
|
+
req('openwop.it.a2ui-surface-replay.re-emission-with-same-correlationid-different-type-envelope-correlation-conflict', 'ai-envelope.md §"Replay determinism"', 'same correlationId + divergent type MUST conflict'),
|
|
82
84
|
).toContain('envelope_correlation_conflict');
|
|
83
85
|
});
|
|
84
86
|
});
|
|
@@ -31,6 +31,7 @@ import Ajv2020 from 'ajv/dist/2020.js';
|
|
|
31
31
|
import { readFileSync } from 'node:fs';
|
|
32
32
|
import { join } from 'node:path';
|
|
33
33
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
34
|
+
import { req } from '../lib/requirement-ids.js';
|
|
34
35
|
|
|
35
36
|
const ajv = new Ajv2020({ strict: false, allErrors: true });
|
|
36
37
|
const schema = JSON.parse(
|
|
@@ -70,7 +71,7 @@ describe('a2ui-surface-shape: schema compile + round-trip (RFC 0102 §A)', () =>
|
|
|
70
71
|
it('ui.a2ui-surface.schema.json compiles under Ajv2020', () => {
|
|
71
72
|
expect(
|
|
72
73
|
validate,
|
|
73
|
-
'RFC 0102 §A: the core ui.a2ui-surface payload schema MUST compile',
|
|
74
|
+
req('openwop.it.a2ui-surface-shape.ui-a2ui-surface-schema-json-compiles-under-ajv2020', 'RFC 0102 §A', 'RFC 0102 §A: the core ui.a2ui-surface payload schema MUST compile'),
|
|
74
75
|
).toBeTypeOf('function');
|
|
75
76
|
});
|
|
76
77
|
|
|
@@ -78,7 +79,7 @@ describe('a2ui-surface-shape: schema compile + round-trip (RFC 0102 §A)', () =>
|
|
|
78
79
|
const ok = validate(positivePayload());
|
|
79
80
|
expect(
|
|
80
81
|
ok,
|
|
81
|
-
`RFC 0102 §A: positive surface MUST validate; errors: ${JSON.stringify(validate.errors)}
|
|
82
|
+
req('openwop.it.a2ui-surface-shape.accepts-a-positive-surface-payload-closed-catalog-confined-action', 'RFC 0102 §A', `RFC 0102 §A: positive surface MUST validate; errors: ${JSON.stringify(validate.errors)}`),
|
|
82
83
|
).toBe(true);
|
|
83
84
|
});
|
|
84
85
|
|
|
@@ -87,19 +88,19 @@ describe('a2ui-surface-shape: schema compile + round-trip (RFC 0102 §A)', () =>
|
|
|
87
88
|
(p.surface as { components: Array<Record<string, unknown>> }).components = [
|
|
88
89
|
{ component: 'action.button', id: 'send', label: 'Send', action: { target: 'exchange' } },
|
|
89
90
|
];
|
|
90
|
-
expect(validate(p)).toBe(true);
|
|
91
|
+
expect(validate(p), req('openwop.it.a2ui-surface-shape.accepts-an-exchange-targeted-action-the-other-confined-target', 'RFC 0102 §A', 'accepts an exchange-targeted action (the other confined target)')).toBe(true);
|
|
91
92
|
});
|
|
92
93
|
|
|
93
94
|
it('rejects a payload missing required `catalogVersion`', () => {
|
|
94
95
|
const p = positivePayload();
|
|
95
96
|
delete p.catalogVersion;
|
|
96
|
-
expect(validate(p), 'RFC 0102 §A: `catalogVersion` is REQUIRED').toBe(false);
|
|
97
|
+
expect(validate(p), req('openwop.it.a2ui-surface-shape.rejects-a-payload-missing-required-catalogversion', 'RFC 0102 §A', 'RFC 0102 §A: `catalogVersion` is REQUIRED')).toBe(false);
|
|
97
98
|
});
|
|
98
99
|
|
|
99
100
|
it('rejects a payload missing required `surface`', () => {
|
|
100
101
|
const p = positivePayload();
|
|
101
102
|
delete p.surface;
|
|
102
|
-
expect(validate(p), 'RFC 0102 §A: `surface` is REQUIRED').toBe(false);
|
|
103
|
+
expect(validate(p), req('openwop.it.a2ui-surface-shape.rejects-a-payload-missing-required-surface', 'RFC 0102 §A', 'RFC 0102 §A: `surface` is REQUIRED')).toBe(false);
|
|
103
104
|
});
|
|
104
105
|
|
|
105
106
|
it('rejects a `catalogVersion` the schema does not enumerate (unknown version)', () => {
|
|
@@ -107,7 +108,7 @@ describe('a2ui-surface-shape: schema compile + round-trip (RFC 0102 §A)', () =>
|
|
|
107
108
|
p.catalogVersion = '9.9.9';
|
|
108
109
|
expect(
|
|
109
110
|
validate(p),
|
|
110
|
-
'RFC 0102 §A.3: an unadvertised catalogVersion MUST fail the enumerated set',
|
|
111
|
+
req('openwop.it.a2ui-surface-shape.rejects-a-catalogversion-the-schema-does-not-enumerate-unknown-version', 'RFC 0102 §A', 'RFC 0102 §A.3: an unadvertised catalogVersion MUST fail the enumerated set'),
|
|
111
112
|
).toBe(false);
|
|
112
113
|
});
|
|
113
114
|
|
|
@@ -118,7 +119,7 @@ describe('a2ui-surface-shape: schema compile + round-trip (RFC 0102 §A)', () =>
|
|
|
118
119
|
];
|
|
119
120
|
expect(
|
|
120
121
|
validate(p),
|
|
121
|
-
'RFC 0102 §A.1: an out-of-catalog component MUST fail the closed anyOf',
|
|
122
|
+
req('openwop.it.a2ui-surface-shape.rejects-an-out-of-catalog-component-object-additionalproperties-false', 'RFC 0102 §A', 'RFC 0102 §A.1: an out-of-catalog component MUST fail the closed anyOf'),
|
|
122
123
|
).toBe(false);
|
|
123
124
|
});
|
|
124
125
|
|
|
@@ -129,7 +130,7 @@ describe('a2ui-surface-shape: schema compile + round-trip (RFC 0102 §A)', () =>
|
|
|
129
130
|
];
|
|
130
131
|
expect(
|
|
131
132
|
validate(p),
|
|
132
|
-
'RFC 0102 §A.1: additionalProperties:false MUST reject a smuggled code field',
|
|
133
|
+
req('openwop.it.a2ui-surface-shape.rejects-a-known-component-carrying-an-extra-script-bearing-property', 'RFC 0102 §A', 'RFC 0102 §A.1: additionalProperties:false MUST reject a smuggled code field'),
|
|
133
134
|
).toBe(false);
|
|
134
135
|
});
|
|
135
136
|
|
|
@@ -140,7 +141,7 @@ describe('a2ui-surface-shape: schema compile + round-trip (RFC 0102 §A)', () =>
|
|
|
140
141
|
];
|
|
141
142
|
expect(
|
|
142
143
|
validate(p),
|
|
143
|
-
'RFC 0102 §A.4 / SECURITY a2ui-action-confinement: an action target outside resume/exchange MUST be rejected',
|
|
144
|
+
req('openwop.it.a2ui-surface-shape.rejects-an-action-whose-target-is-outside-enum-resume-exchange-action-confinemen', 'RFC 0102 §A', 'RFC 0102 §A.4 / SECURITY a2ui-action-confinement: an action target outside resume/exchange MUST be rejected'),
|
|
144
145
|
).toBe(false);
|
|
145
146
|
});
|
|
146
147
|
|
|
@@ -156,7 +157,7 @@ describe('a2ui-surface-shape: schema compile + round-trip (RFC 0102 §A)', () =>
|
|
|
156
157
|
];
|
|
157
158
|
expect(
|
|
158
159
|
validate(p),
|
|
159
|
-
'RFC 0102 §A.4: action additionalProperties:false MUST reject an arbitrary URL target',
|
|
160
|
+
req('openwop.it.a2ui-surface-shape.rejects-an-action-carrying-an-arbitrary-url-field-no-unconfined-egress-target', 'RFC 0102 §A', 'RFC 0102 §A.4: action additionalProperties:false MUST reject an arbitrary URL target'),
|
|
160
161
|
).toBe(false);
|
|
161
162
|
});
|
|
162
163
|
});
|
|
@@ -20,6 +20,8 @@ import { readFileSync } from 'node:fs';
|
|
|
20
20
|
import { join } from 'node:path';
|
|
21
21
|
import { driver } from '../lib/driver.js';
|
|
22
22
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
23
|
+
import { req } from '../lib/requirement-ids.js';
|
|
24
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
23
25
|
|
|
24
26
|
const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
|
|
25
27
|
const schema = JSON.parse(
|
|
@@ -42,14 +44,14 @@ describe('a2ui-surface-version-refusal: enumerated catalogVersion (RFC 0102 §A.
|
|
|
42
44
|
const ok = validate({ catalogVersion: '9.9.9', surface: { components: [] } });
|
|
43
45
|
expect(
|
|
44
46
|
ok,
|
|
45
|
-
'RFC 0102 §A.3: a catalogVersion outside the host-enumerated set MUST fail (→ unknown_schema_version at runtime)',
|
|
47
|
+
req('openwop.it.a2ui-surface-version-refusal.catalogversion-is-a-closed-enum-an-unadvertised-version-fails-validation', 'RFC 0102 §A.3', 'RFC 0102 §A.3: a catalogVersion outside the host-enumerated set MUST fail (→ unknown_schema_version at runtime)'),
|
|
46
48
|
).toBe(false);
|
|
47
49
|
});
|
|
48
50
|
|
|
49
51
|
it('surface payload is self-contained — no external $ref (replay determinism)', () => {
|
|
50
52
|
expect(
|
|
51
53
|
hasAbsoluteRef(schema),
|
|
52
|
-
'RFC 0102 §A.3: the surface MUST be self-contained, never a live external-catalog reference',
|
|
54
|
+
req('openwop.it.a2ui-surface-version-refusal.surface-payload-is-self-contained-no-external-ref-replay-determinism', 'RFC 0102 §A.3', 'RFC 0102 §A.3: the surface MUST be self-contained, never a live external-catalog reference'),
|
|
53
55
|
).toBe(false);
|
|
54
56
|
});
|
|
55
57
|
});
|
|
@@ -67,11 +69,11 @@ describe.skipIf(HTTP_SKIP)('a2ui-surface-version-refusal: live host refuses unad
|
|
|
67
69
|
},
|
|
68
70
|
hostSupportedEnvelopes: ['ui.a2ui-surface'],
|
|
69
71
|
});
|
|
70
|
-
if (res.status === 404) return; // seam absent — soft-skip
|
|
72
|
+
if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam absent — soft-skip) (seam, prior step, or fixture unavailable)'); // seam absent — soft-skip
|
|
71
73
|
const body = res.json as { status?: string; reason?: string };
|
|
72
74
|
expect(
|
|
73
75
|
body.status === 'invalid' || body.status === 'refused',
|
|
74
|
-
|
|
76
|
+
req('openwop.it.a2ui-surface-version-refusal.ui-a2ui-surface-with-an-unadvertised-catalogversion-refused', 'RFC 0102 §A.3', 'an unadvertised catalogVersion MUST be refused'),
|
|
75
77
|
).toBe(true);
|
|
76
78
|
});
|
|
77
79
|
});
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
|
|
22
22
|
import { describe, it, expect } from 'vitest';
|
|
23
23
|
import { driver } from '../lib/driver.js';
|
|
24
|
+
import { req } from '../lib/requirement-ids.js';
|
|
25
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
24
26
|
|
|
25
27
|
const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
|
|
26
28
|
const UNIVERSAL_KINDS = ['clarification.request', 'schema.request', 'schema.response', 'error'];
|
|
@@ -29,7 +31,7 @@ describe('a2ui-untrusted-blocks-approval: trust machinery applies (RFC 0102 §A.
|
|
|
29
31
|
it('ui.a2ui-surface is an advertised kind subject to meta.contentTrust gating (not universal/always-allowed)', () => {
|
|
30
32
|
expect(
|
|
31
33
|
UNIVERSAL_KINDS.includes('ui.a2ui-surface'),
|
|
32
|
-
'ai-envelope.md §"A2UI surfaces" / §"Trust boundary": a ui.a2ui-surface is trust-gated like any advertised envelope, so an untrusted surface is subject to untrusted_content_blocks_approval',
|
|
34
|
+
req('openwop.it.a2ui-untrusted-blocks-approval.ui-a2ui-surface-is-an-advertised-kind-subject-to-meta-contenttrust-gating-not-un', 'RFC 0102 §A.5', 'ai-envelope.md §"A2UI surfaces" / §"Trust boundary": a ui.a2ui-surface is trust-gated like any advertised envelope, so an untrusted surface is subject to untrusted_content_blocks_approval'),
|
|
33
35
|
).toBe(false);
|
|
34
36
|
});
|
|
35
37
|
});
|
|
@@ -55,11 +57,11 @@ describe.skipIf(HTTP_SKIP)('a2ui-untrusted-blocks-approval: untrusted surface ca
|
|
|
55
57
|
hostSupportedEnvelopes: ['ui.a2ui-surface'],
|
|
56
58
|
boundInterruptKind: 'approval',
|
|
57
59
|
});
|
|
58
|
-
if (res.status === 404) return; // seam absent — soft-skip
|
|
60
|
+
if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam absent — soft-skip) (seam, prior step, or fixture unavailable)'); // seam absent — soft-skip
|
|
59
61
|
const body = res.json as { status?: string; reason?: string };
|
|
60
62
|
expect(
|
|
61
63
|
body.status === 'blocked' || (body.reason ?? '').includes('untrusted'),
|
|
62
|
-
|
|
64
|
+
req('openwop.it.a2ui-untrusted-blocks-approval.an-untrusted-marked-ui-a2ui-surface-bound-to-an-approval-interrupt-is-refused',
|
|
63
65
|
'RFC 0102 §A.5',
|
|
64
66
|
'an untrusted ui.a2ui-surface MUST NOT advance an approval interrupt (untrusted_content_blocks_approval)',
|
|
65
67
|
),
|