@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
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
import { describe, it, expect } from 'vitest';
|
|
21
21
|
import { driver } from '../lib/driver.js';
|
|
22
22
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
23
|
+
import { req } from '../lib/requirement-ids.js';
|
|
24
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
23
25
|
|
|
24
26
|
interface DiscoveryDeadLetter {
|
|
25
27
|
supported?: boolean;
|
|
@@ -39,10 +41,10 @@ async function readDeadLetter(): Promise<DiscoveryDeadLetter | null> {
|
|
|
39
41
|
describe('deadletter-capability-shape: advertisement shape (RFC 0053 §A)', () => {
|
|
40
42
|
it('capabilities.deadLetter is either absent or well-formed', async () => {
|
|
41
43
|
const dl = await readDeadLetter();
|
|
42
|
-
if (dl === null) return; // host doesn't advertise deadLetter at all
|
|
44
|
+
if (dl === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `dl === null` returned early (host doesn\'t advertise deadLetter at all)'); // host doesn't advertise deadLetter at all
|
|
43
45
|
expect(
|
|
44
46
|
typeof dl.supported,
|
|
45
|
-
|
|
47
|
+
req('openwop.it.deadletter-capability-shape.capabilities-deadletter-is-either-absent-or-well-formed',
|
|
46
48
|
'capabilities.schema.json §deadLetter',
|
|
47
49
|
'capabilities.deadLetter.supported MUST be a boolean when deadLetter is advertised',
|
|
48
50
|
),
|
|
@@ -51,10 +53,10 @@ describe('deadletter-capability-shape: advertisement shape (RFC 0053 §A)', () =
|
|
|
51
53
|
|
|
52
54
|
it('retentionDays is an integer >= 1 when present + supported', async () => {
|
|
53
55
|
const dl = await readDeadLetter();
|
|
54
|
-
if (!dl?.supported || dl.retentionDays === undefined) return;
|
|
56
|
+
if (!dl?.supported || dl.retentionDays === undefined) return softSkip('blocked', 'precondition not met — `!dl?.supported || dl.retentionDays === undefined` returned early (seam, prior step, or fixture unavailable)');
|
|
55
57
|
expect(
|
|
56
58
|
Number.isInteger(dl.retentionDays) && dl.retentionDays >= 1,
|
|
57
|
-
|
|
59
|
+
req('openwop.it.deadletter-capability-shape.retentiondays-is-an-integer-1-when-present-supported', 'RFC 0053 §A', `capabilities.deadLetter.retentionDays MUST be an integer >= 1, got: ${dl.retentionDays}`),
|
|
58
60
|
).toBe(true);
|
|
59
61
|
});
|
|
60
62
|
});
|
|
@@ -25,6 +25,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
25
25
|
import { softSkip } from '../lib/soft-skip.js';
|
|
26
26
|
import { driver } from '../lib/driver.js';
|
|
27
27
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
28
|
+
import { req } from '../lib/requirement-ids.js';
|
|
28
29
|
|
|
29
30
|
interface DiscoveryDoc {
|
|
30
31
|
capabilities?: { deadLetter?: { supported?: boolean } };
|
|
@@ -43,11 +44,11 @@ describe('deadletter-retry-exhaustion: retry exhaustion → dead-lettered + fork
|
|
|
43
44
|
const body = res.json as { event?: { type?: string; payload?: { attempts?: number; runId?: string } } } | undefined;
|
|
44
45
|
expect(
|
|
45
46
|
body?.event?.type,
|
|
46
|
-
|
|
47
|
+
req('openwop.it.deadletter-retry-exhaustion.a-retry-exhausted-run-emits-run-dead-lettered-with-attempts', 'RFC 0053 §C.1', 'retry exhaustion MUST emit a run.dead_lettered event'),
|
|
47
48
|
).toBe('run.dead_lettered');
|
|
48
49
|
expect(
|
|
49
50
|
typeof body?.event?.payload?.attempts === 'number' && body.event.payload.attempts >= 1,
|
|
50
|
-
|
|
51
|
+
req('openwop.it.deadletter-retry-exhaustion.a-retry-exhausted-run-emits-run-dead-lettered-with-attempts', 'RFC 0053 §C.1', 'run.dead_lettered MUST carry the total attempts (>= 1)'),
|
|
51
52
|
).toBe(true);
|
|
52
53
|
});
|
|
53
54
|
|
|
@@ -58,7 +59,7 @@ describe('deadletter-retry-exhaustion: retry exhaustion → dead-lettered + fork
|
|
|
58
59
|
const body = res.json as { forkEligible?: boolean } | undefined;
|
|
59
60
|
expect(
|
|
60
61
|
body?.forkEligible,
|
|
61
|
-
|
|
62
|
+
req('openwop.it.deadletter-retry-exhaustion.the-dead-lettered-run-is-fork-eligible-rfc-0011', 'RFC 0053 §C.2', 'a dead-lettered run MUST remain fork-eligible within the retention window'),
|
|
62
63
|
).toBe(true);
|
|
63
64
|
});
|
|
64
65
|
});
|
|
@@ -23,6 +23,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
23
23
|
import { driver } from '../lib/driver.js';
|
|
24
24
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
25
25
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
26
|
+
import { req } from '../lib/requirement-ids.js';
|
|
27
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
26
28
|
|
|
27
29
|
// `conformance-multi-node` produces enough events (run.started, three
|
|
28
30
|
// node.started/completed pairs, run.completed = ~8 events) that
|
|
@@ -36,7 +38,7 @@ describe('debug-bundle-truncation: truncated: true contract', () => {
|
|
|
36
38
|
console.warn(
|
|
37
39
|
`[debug-bundle-truncation] ${FIXTURE} not advertised; skipping (host doesn't seed a multi-event fixture)`,
|
|
38
40
|
);
|
|
39
|
-
return;
|
|
41
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early ([debug-bundle-truncation] … not advertised; skipping (host doesn\'t seed a multi-event fixture))');
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
|
|
@@ -49,8 +51,8 @@ describe('debug-bundle-truncation: truncated: true contract', () => {
|
|
|
49
51
|
expect(full.status).toBe(200);
|
|
50
52
|
const fullBody = full.json as { events?: unknown[]; truncated?: boolean };
|
|
51
53
|
const fullEventCount = (fullBody.events ?? []).length;
|
|
52
|
-
expect(fullEventCount, 'multi-node fixture MUST emit ≥ 3 events').toBeGreaterThanOrEqual(3);
|
|
53
|
-
expect(fullBody.truncated ?? false, 'baseline bundle MUST NOT be truncated').toBe(false);
|
|
54
|
+
expect(fullEventCount, req('openwop.it.debug-bundle-truncation.host-that-supports-maxevents-n-or-otherwise-caps-surfaces-truncated-truncatedrea', 'debug-bundle.md §"Bundle size limits"', 'multi-node fixture MUST emit ≥ 3 events')).toBeGreaterThanOrEqual(3);
|
|
55
|
+
expect(fullBody.truncated ?? false, req('openwop.it.debug-bundle-truncation.host-that-supports-maxevents-n-or-otherwise-caps-surfaces-truncated-truncatedrea', 'debug-bundle.md §"Bundle size limits"', 'baseline bundle MUST NOT be truncated')).toBe(false);
|
|
54
56
|
|
|
55
57
|
// Force truncation via the host's optional maxEvents override.
|
|
56
58
|
const truncated = await driver.get(
|
|
@@ -73,21 +75,21 @@ describe('debug-bundle-truncation: truncated: true contract', () => {
|
|
|
73
75
|
console.warn(
|
|
74
76
|
'[debug-bundle-truncation] host does not honor ?maxEvents=; skipping truncated-shape assertions',
|
|
75
77
|
);
|
|
76
|
-
return;
|
|
78
|
+
return softSkip('blocked', 'precondition not met — `body.truncated !== true` returned early ([debug-bundle-truncation] host does not honor ?maxEvents=; skipping truncated-shape assertions) (seam, prior step, or fixture unavailable)');
|
|
77
79
|
}
|
|
78
80
|
|
|
79
|
-
expect(typeof body.truncatedReason,
|
|
81
|
+
expect(typeof body.truncatedReason, req('openwop.it.debug-bundle-truncation.host-that-supports-maxevents-n-or-otherwise-caps-surfaces-truncated-truncatedrea',
|
|
80
82
|
'debug-bundle.md §"Bundle size limits"',
|
|
81
83
|
'truncated: true MUST be accompanied by a non-empty truncatedReason string',
|
|
82
84
|
)).toBe('string');
|
|
83
85
|
expect((body.truncatedReason ?? '').length).toBeGreaterThan(0);
|
|
84
86
|
|
|
85
|
-
expect((body.events ?? []).length,
|
|
87
|
+
expect((body.events ?? []).length, req('openwop.it.debug-bundle-truncation.host-that-supports-maxevents-n-or-otherwise-caps-surfaces-truncated-truncatedrea',
|
|
86
88
|
'debug-bundle.md §"Bundle size limits"',
|
|
87
89
|
'truncated events array MUST be a prefix (≤ maxEvents)',
|
|
88
90
|
)).toBeLessThanOrEqual(2);
|
|
89
91
|
|
|
90
|
-
expect(body.metrics?.eventCount,
|
|
92
|
+
expect(body.metrics?.eventCount, req('openwop.it.debug-bundle-truncation.host-that-supports-maxevents-n-or-otherwise-caps-surfaces-truncated-truncatedrea',
|
|
91
93
|
'debug-bundle.md §"Bundle size limits"',
|
|
92
94
|
'metrics.eventCount MUST reflect the TOTAL event count, not the truncated length',
|
|
93
95
|
)).toBe(fullEventCount);
|
|
@@ -35,6 +35,8 @@ import { driver } from '../lib/driver.js';
|
|
|
35
35
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
36
36
|
import { CANARY_MARKER, getCanary } from '../lib/canaries.js';
|
|
37
37
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
38
|
+
import { req } from '../lib/requirement-ids.js';
|
|
39
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
38
40
|
|
|
39
41
|
const NOOP_WORKFLOW_ID = 'conformance-noop';
|
|
40
42
|
const SKIP_NO_NOOP = !isFixtureAdvertised(NOOP_WORKFLOW_ID);
|
|
@@ -61,7 +63,7 @@ async function isAdvertised(): Promise<boolean> {
|
|
|
61
63
|
|
|
62
64
|
describe.skipIf(SKIP_NO_NOOP)('debug-bundle: GET /v1/runs/{runId}/debug-bundle response shape', () => {
|
|
63
65
|
it('host advertising capabilities.debugBundle.supported returns 200 with valid bundle', async () => {
|
|
64
|
-
if (!(await isAdvertised())) return; // skip-equivalent
|
|
66
|
+
if (!(await isAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isAdvertised())` returned early (skip-equivalent)'); // skip-equivalent
|
|
65
67
|
|
|
66
68
|
const create = await driver.post('/v1/runs', { workflowId: NOOP_WORKFLOW_ID });
|
|
67
69
|
expect(create.status).toBe(201);
|
|
@@ -69,58 +71,58 @@ describe.skipIf(SKIP_NO_NOOP)('debug-bundle: GET /v1/runs/{runId}/debug-bundle r
|
|
|
69
71
|
await pollUntilTerminal(runId);
|
|
70
72
|
|
|
71
73
|
const res = await driver.get(`/v1/runs/${encodeURIComponent(runId)}/debug-bundle`);
|
|
72
|
-
expect(res.status,
|
|
74
|
+
expect(res.status, req('openwop.it.debugBundle.host-advertising-capabilities-debugbundle-supported-returns-200-with-valid-bundl',
|
|
73
75
|
'spec/v1/debug-bundle.md §Endpoint',
|
|
74
76
|
'host advertising debugBundle.supported MUST return 200 on /debug-bundle',
|
|
75
77
|
)).toBe(200);
|
|
76
78
|
|
|
77
79
|
const bundle = res.json as DebugBundleShape | undefined;
|
|
78
|
-
expect(bundle,
|
|
80
|
+
expect(bundle, req('openwop.it.debugBundle.host-advertising-capabilities-debugbundle-supported-returns-200-with-valid-bundl',
|
|
79
81
|
'spec/v1/debug-bundle.md',
|
|
80
82
|
'response MUST be JSON',
|
|
81
83
|
)).toBeDefined();
|
|
82
84
|
|
|
83
|
-
expect(typeof bundle?.bundleVersion,
|
|
85
|
+
expect(typeof bundle?.bundleVersion, req('openwop.it.debugBundle.host-advertising-capabilities-debugbundle-supported-returns-200-with-valid-bundl',
|
|
84
86
|
'debug-bundle.md §Field reference',
|
|
85
87
|
'bundleVersion MUST be a string',
|
|
86
88
|
)).toBe('string');
|
|
87
|
-
expect(typeof bundle?.generatedAt,
|
|
89
|
+
expect(typeof bundle?.generatedAt, req('openwop.it.debugBundle.host-advertising-capabilities-debugbundle-supported-returns-200-with-valid-bundl',
|
|
88
90
|
'debug-bundle.md',
|
|
89
91
|
'generatedAt MUST be a string',
|
|
90
92
|
)).toBe('string');
|
|
91
|
-
expect(typeof bundle?.host?.name,
|
|
93
|
+
expect(typeof bundle?.host?.name, req('openwop.it.debugBundle.host-advertising-capabilities-debugbundle-supported-returns-200-with-valid-bundl',
|
|
92
94
|
'debug-bundle.md',
|
|
93
95
|
'host.name MUST be a string',
|
|
94
96
|
)).toBe('string');
|
|
95
|
-
expect(typeof bundle?.host?.version,
|
|
97
|
+
expect(typeof bundle?.host?.version, req('openwop.it.debugBundle.host-advertising-capabilities-debugbundle-supported-returns-200-with-valid-bundl',
|
|
96
98
|
'debug-bundle.md',
|
|
97
99
|
'host.version MUST be a string',
|
|
98
100
|
)).toBe('string');
|
|
99
|
-
expect(typeof bundle?.run?.runId,
|
|
101
|
+
expect(typeof bundle?.run?.runId, req('openwop.it.debugBundle.host-advertising-capabilities-debugbundle-supported-returns-200-with-valid-bundl',
|
|
100
102
|
'debug-bundle.md',
|
|
101
103
|
'run.runId MUST be a string',
|
|
102
104
|
)).toBe('string');
|
|
103
|
-
expect(Array.isArray(bundle?.events),
|
|
105
|
+
expect(Array.isArray(bundle?.events), req('openwop.it.debugBundle.host-advertising-capabilities-debugbundle-supported-returns-200-with-valid-bundl',
|
|
104
106
|
'debug-bundle.md',
|
|
105
107
|
'events MUST be an array',
|
|
106
108
|
)).toBe(true);
|
|
107
|
-
expect(typeof bundle?.redactionApplied,
|
|
109
|
+
expect(typeof bundle?.redactionApplied, req('openwop.it.debugBundle.host-advertising-capabilities-debugbundle-supported-returns-200-with-valid-bundl',
|
|
108
110
|
'debug-bundle.md',
|
|
109
111
|
'redactionApplied MUST be a boolean',
|
|
110
112
|
)).toBe('boolean');
|
|
111
|
-
expect(typeof bundle?.redactionMode,
|
|
113
|
+
expect(typeof bundle?.redactionMode, req('openwop.it.debugBundle.host-advertising-capabilities-debugbundle-supported-returns-200-with-valid-bundl',
|
|
112
114
|
'debug-bundle.md',
|
|
113
115
|
'redactionMode MUST be a string',
|
|
114
116
|
)).toBe('string');
|
|
115
117
|
});
|
|
116
118
|
|
|
117
119
|
it('hosts not advertising debugBundle return 404 on the endpoint', async () => {
|
|
118
|
-
if (await isAdvertised()) return; // skip-equivalent for hosts that DO advertise
|
|
120
|
+
if (await isAdvertised()) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `await isAdvertised()` returned early (skip-equivalent for hosts that DO advertise)'); // skip-equivalent for hosts that DO advertise
|
|
119
121
|
|
|
120
122
|
// Use any runId — even a synthetic one — since the host should 404
|
|
121
123
|
// on the endpoint regardless of run existence.
|
|
122
124
|
const res = await driver.get('/v1/runs/openwop-conformance-no-such-run/debug-bundle');
|
|
123
|
-
expect(res.status,
|
|
125
|
+
expect(res.status, req('openwop.it.debugBundle.hosts-not-advertising-debugbundle-return-404-on-the-endpoint',
|
|
124
126
|
'debug-bundle.md §Endpoint',
|
|
125
127
|
'host NOT advertising debugBundle.supported MUST return 404',
|
|
126
128
|
)).toBe(404);
|
|
@@ -129,7 +131,7 @@ describe.skipIf(SKIP_NO_NOOP)('debug-bundle: GET /v1/runs/{runId}/debug-bundle r
|
|
|
129
131
|
|
|
130
132
|
describe.skipIf(SKIP_NO_NOOP)('debug-bundle: invariants per debug-bundle.md', () => {
|
|
131
133
|
it('metrics.eventCount equals events.length', async () => {
|
|
132
|
-
if (!(await isAdvertised())) return;
|
|
134
|
+
if (!(await isAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isAdvertised())` returned early');
|
|
133
135
|
|
|
134
136
|
const create = await driver.post('/v1/runs', { workflowId: NOOP_WORKFLOW_ID });
|
|
135
137
|
expect(create.status).toBe(201);
|
|
@@ -141,7 +143,7 @@ describe.skipIf(SKIP_NO_NOOP)('debug-bundle: invariants per debug-bundle.md', ()
|
|
|
141
143
|
const bundle = res.json as DebugBundleShape;
|
|
142
144
|
|
|
143
145
|
if (bundle.metrics?.eventCount !== undefined) {
|
|
144
|
-
expect(bundle.metrics.eventCount,
|
|
146
|
+
expect(bundle.metrics.eventCount, req('openwop.it.debugBundle.metrics-eventcount-equals-events-length',
|
|
145
147
|
'debug-bundle.md §"Field reference"',
|
|
146
148
|
'metrics.eventCount MUST equal events.length',
|
|
147
149
|
)).toBe(bundle.events?.length ?? 0);
|
|
@@ -149,7 +151,7 @@ describe.skipIf(SKIP_NO_NOOP)('debug-bundle: invariants per debug-bundle.md', ()
|
|
|
149
151
|
});
|
|
150
152
|
|
|
151
153
|
it('redactionApplied=true is incompatible with redactionMode=passthrough', async () => {
|
|
152
|
-
if (!(await isAdvertised())) return;
|
|
154
|
+
if (!(await isAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isAdvertised())` returned early');
|
|
153
155
|
|
|
154
156
|
const create = await driver.post('/v1/runs', { workflowId: NOOP_WORKFLOW_ID });
|
|
155
157
|
expect(create.status).toBe(201);
|
|
@@ -161,7 +163,7 @@ describe.skipIf(SKIP_NO_NOOP)('debug-bundle: invariants per debug-bundle.md', ()
|
|
|
161
163
|
const bundle = res.json as DebugBundleShape;
|
|
162
164
|
|
|
163
165
|
if (bundle.redactionApplied === true) {
|
|
164
|
-
expect(bundle.redactionMode,
|
|
166
|
+
expect(bundle.redactionMode, req('openwop.it.debugBundle.redactionapplied-true-is-incompatible-with-redactionmode-passthrough',
|
|
165
167
|
'debug-bundle.md §"Redaction guarantees"',
|
|
166
168
|
'redactionApplied=true MUST NOT coexist with redactionMode=passthrough — that combination is malformed',
|
|
167
169
|
)).not.toBe('passthrough');
|
|
@@ -169,7 +171,7 @@ describe.skipIf(SKIP_NO_NOOP)('debug-bundle: invariants per debug-bundle.md', ()
|
|
|
169
171
|
});
|
|
170
172
|
|
|
171
173
|
it('bundle events agree with /events/poll for the same run', async () => {
|
|
172
|
-
if (!(await isAdvertised())) return;
|
|
174
|
+
if (!(await isAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isAdvertised())` returned early');
|
|
173
175
|
|
|
174
176
|
const create = await driver.post('/v1/runs', { workflowId: NOOP_WORKFLOW_ID });
|
|
175
177
|
expect(create.status).toBe(201);
|
|
@@ -178,12 +180,12 @@ describe.skipIf(SKIP_NO_NOOP)('debug-bundle: invariants per debug-bundle.md', ()
|
|
|
178
180
|
|
|
179
181
|
const bundleRes = await driver.get(`/v1/runs/${encodeURIComponent(runId)}/debug-bundle`);
|
|
180
182
|
const eventsRes = await driver.get(`/v1/runs/${encodeURIComponent(runId)}/events/poll`);
|
|
181
|
-
if (eventsRes.status !== 200) return; // host without polling
|
|
183
|
+
if (eventsRes.status !== 200) return softSkip('blocked', 'precondition not met — `eventsRes.status !== 200` returned early (host without polling) (seam, prior step, or fixture unavailable)'); // host without polling
|
|
182
184
|
|
|
183
185
|
const bundle = bundleRes.json as DebugBundleShape;
|
|
184
186
|
const polledEvents = (eventsRes.json as { events?: unknown[] }).events ?? [];
|
|
185
187
|
|
|
186
|
-
expect(bundle.events?.length,
|
|
188
|
+
expect(bundle.events?.length, req('openwop.it.debugBundle.bundle-events-agree-with-events-poll-for-the-same-run',
|
|
187
189
|
'debug-bundle.md',
|
|
188
190
|
'bundle event count MUST agree with /events/poll for the same run',
|
|
189
191
|
)).toBe(polledEvents.length);
|
|
@@ -192,7 +194,7 @@ describe.skipIf(SKIP_NO_NOOP)('debug-bundle: invariants per debug-bundle.md', ()
|
|
|
192
194
|
|
|
193
195
|
describe.skipIf(SKIP_NO_NOOP)('debug-bundle: redaction inheritance per SECURITY/invariants.yaml secret-leakage-debug-bundle', () => {
|
|
194
196
|
it('canary in workflow inputs MUST NOT appear verbatim in the bundle response', async () => {
|
|
195
|
-
if (!(await isAdvertised())) return;
|
|
197
|
+
if (!(await isAdvertised())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isAdvertised())` returned early');
|
|
196
198
|
|
|
197
199
|
const canary = getCanary('byok-credential-ref').value;
|
|
198
200
|
|
|
@@ -210,11 +212,11 @@ describe.skipIf(SKIP_NO_NOOP)('debug-bundle: redaction inheritance per SECURITY/
|
|
|
210
212
|
expect(res.status).toBe(200);
|
|
211
213
|
|
|
212
214
|
const bundleText = res.text;
|
|
213
|
-
expect(bundleText.includes(canary),
|
|
215
|
+
expect(bundleText.includes(canary), req('openwop.it.debugBundle.canary-in-workflow-inputs-must-not-appear-verbatim-in-the-bundle-response',
|
|
214
216
|
'SECURITY/invariants.yaml secret-leakage-debug-bundle',
|
|
215
217
|
'BYOK-shaped canary submitted as workflow input MUST NOT appear verbatim in the debug bundle',
|
|
216
218
|
)).toBe(false);
|
|
217
|
-
expect(bundleText.includes(CANARY_MARKER),
|
|
219
|
+
expect(bundleText.includes(CANARY_MARKER), req('openwop.it.debugBundle.canary-in-workflow-inputs-must-not-appear-verbatim-in-the-bundle-response',
|
|
218
220
|
'SECURITY/invariants.yaml secret-leakage-debug-bundle',
|
|
219
221
|
'canary marker MUST NOT appear in the debug bundle',
|
|
220
222
|
)).toBe(false);
|
|
@@ -13,25 +13,26 @@
|
|
|
13
13
|
|
|
14
14
|
import { describe, it, expect } from 'vitest';
|
|
15
15
|
import { discoveryFamilies } from '../lib/discovery-capabilities.js';
|
|
16
|
+
import { req } from '../lib/requirement-ids.js';
|
|
16
17
|
|
|
17
18
|
describe('RFC 0073 — discoveryFamilies is root-first with the deprecated wrapper as fallback', () => {
|
|
18
19
|
it('a root-only document is read as-is (the RFC 0073 shape)', () => {
|
|
19
|
-
expect(discoveryFamilies({ protocolVersion: '1.0', kvStorage: { supported: true } })).toEqual({ protocolVersion: '1.0', kvStorage: { supported: true } });
|
|
20
|
+
expect(discoveryFamilies({ protocolVersion: '1.0', kvStorage: { supported: true } }), req('openwop.it.discovery-families-view.a-root-only-document-is-read-as-is-the-rfc-0073-shape', 'RFC 0073', 'a root-only document is read as-is (the RFC 0073 shape)')).toEqual({ protocolVersion: '1.0', kvStorage: { supported: true } });
|
|
20
21
|
});
|
|
21
22
|
|
|
22
23
|
it('a wrapper-only family is still visible, and the wrapper key itself is not a family', () => {
|
|
23
24
|
const v = discoveryFamilies({ protocolVersion: '1.0', capabilities: { heartbeat: { supported: true } } });
|
|
24
|
-
expect(v['heartbeat']).toEqual({ supported: true });
|
|
25
|
+
expect(v['heartbeat'], req('openwop.it.discovery-families-view.a-wrapper-only-family-is-still-visible-and-the-wrapper-key-itself-is-not-a-famil', 'RFC 0073', 'a wrapper-only family is still visible, and the wrapper key itself is not a family')).toEqual({ supported: true });
|
|
25
26
|
expect('capabilities' in v).toBe(false);
|
|
26
27
|
});
|
|
27
28
|
|
|
28
29
|
it('when both carry a family, the root wins', () => {
|
|
29
30
|
const v = discoveryFamilies({ feedback: { supported: true, root: true }, capabilities: { feedback: { supported: false, root: false } } });
|
|
30
|
-
expect(v['feedback']).toEqual({ supported: true, root: true });
|
|
31
|
+
expect(v['feedback'], req('openwop.it.discovery-families-view.when-both-carry-a-family-the-root-wins', 'RFC 0073', 'when both carry a family, the root wins')).toEqual({ supported: true, root: true });
|
|
31
32
|
});
|
|
32
33
|
|
|
33
34
|
it('a non-object document reads as empty', () => {
|
|
34
|
-
expect(discoveryFamilies(null)).toEqual({});
|
|
35
|
+
expect(discoveryFamilies(null), req('openwop.it.discovery-families-view.a-non-object-document-reads-as-empty', 'RFC 0073', 'a non-object document reads as empty')).toEqual({});
|
|
35
36
|
expect(discoveryFamilies('nope')).toEqual({});
|
|
36
37
|
expect(discoveryFamilies({ capabilities: ['not', 'an', 'object'] })).toEqual({});
|
|
37
38
|
});
|
|
@@ -11,22 +11,23 @@ import { driver } from '../lib/driver.js';
|
|
|
11
11
|
import { discoveryFamilies } from '../lib/discovery-capabilities.js';
|
|
12
12
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
13
13
|
import { softSkip } from '../lib/soft-skip.js';
|
|
14
|
+
import { req } from '../lib/requirement-ids.js';
|
|
14
15
|
|
|
15
16
|
describe('discovery: /.well-known/openwop', () => {
|
|
16
17
|
it('returns 200 with required Capabilities fields per capabilities.md §2', async () => {
|
|
17
18
|
const res = await driver.get('/.well-known/openwop', { authenticated: false });
|
|
18
19
|
|
|
19
|
-
expect(res.status,
|
|
20
|
+
expect(res.status, req('openwop.it.discovery.returns-200-with-required-capabilities-fields-per-capabilities-md-2',
|
|
20
21
|
'capabilities.md §2',
|
|
21
22
|
'discovery endpoint MUST be reachable without auth and return 200',
|
|
22
23
|
)).toBe(200);
|
|
23
24
|
|
|
24
25
|
const body = res.json as Record<string, unknown> | undefined;
|
|
25
|
-
expect(body,
|
|
26
|
+
expect(body, req('openwop.it.discovery.returns-200-with-required-capabilities-fields-per-capabilities-md-2', 'capabilities.md §2', 'response MUST be JSON')).toBeDefined();
|
|
26
27
|
|
|
27
28
|
// Per capabilities.md §3 (in-package shape), these 4 fields are REQUIRED.
|
|
28
29
|
for (const required of ['protocolVersion', 'supportedEnvelopes', 'schemaVersions', 'limits']) {
|
|
29
|
-
expect(body?.[required],
|
|
30
|
+
expect(body?.[required], req('openwop.it.discovery.returns-200-with-required-capabilities-fields-per-capabilities-md-2',
|
|
30
31
|
'capabilities.md §3',
|
|
31
32
|
`Capabilities.${required} MUST be present`,
|
|
32
33
|
)).toBeDefined();
|
|
@@ -37,7 +38,7 @@ describe('discovery: /.well-known/openwop', () => {
|
|
|
37
38
|
const res = await driver.get('/.well-known/openwop', { authenticated: false });
|
|
38
39
|
const cacheControl = res.headers.get('cache-control');
|
|
39
40
|
|
|
40
|
-
expect(cacheControl,
|
|
41
|
+
expect(cacheControl, req('openwop.it.discovery.serves-cache-control-per-capabilities-md-4-caching-guidance',
|
|
41
42
|
'capabilities.md §4',
|
|
42
43
|
'response SHOULD carry a Cache-Control header to allow client caching',
|
|
43
44
|
)).toBeTruthy();
|
|
@@ -49,9 +50,9 @@ describe('discovery: /.well-known/openwop', () => {
|
|
|
49
50
|
if (etag === null) {
|
|
50
51
|
return softSkip('inapplicable', 'host does not send a standard ETag on the discovery document (RFC 0165 §C.2 — SHOULD, presence-gated)');
|
|
51
52
|
}
|
|
52
|
-
expect(etag.trim().length,
|
|
53
|
+
expect(etag.trim().length, req('openwop.it.discovery.if-a-standard-etag-is-present-a-matching-if-none-match-yields-304-rfc-0165-c-2-p', 'RFC 0165 §C.2', 'ETag, when present, MUST be a non-empty validator')).toBeGreaterThan(0);
|
|
53
54
|
const second = await driver.get('/.well-known/openwop', { authenticated: false, headers: { 'If-None-Match': etag } });
|
|
54
|
-
expect(second.status,
|
|
55
|
+
expect(second.status, req('openwop.it.discovery.if-a-standard-etag-is-present-a-matching-if-none-match-yields-304-rfc-0165-c-2-p', 'capabilities-change-detection.md §"Cache validators"', 'a matching If-None-Match SHOULD yield 304 Not Modified')).toBe(304);
|
|
55
56
|
});
|
|
56
57
|
|
|
57
58
|
it('IF Capabilities-Etag is present, it is non-empty and stable within the cache window', async () => {
|
|
@@ -59,14 +60,14 @@ describe('discovery: /.well-known/openwop', () => {
|
|
|
59
60
|
const firstEtag = first.headers.get('capabilities-etag');
|
|
60
61
|
|
|
61
62
|
if (firstEtag === null) {
|
|
62
|
-
expect(firstEtag,
|
|
63
|
+
expect(firstEtag, req('openwop.it.discovery.if-capabilities-etag-is-present-it-is-non-empty-and-stable-within-the-cache-wind',
|
|
63
64
|
'capabilities-change-detection.md §Capabilities-Etag',
|
|
64
65
|
'Capabilities-Etag is optional; hosts that omit it remain conformant',
|
|
65
66
|
)).toBeNull();
|
|
66
|
-
return;
|
|
67
|
+
return softSkip('blocked', 'precondition not met — `firstEtag === null` returned early (seam, prior step, or fixture unavailable)');
|
|
67
68
|
}
|
|
68
69
|
|
|
69
|
-
expect(firstEtag.trim().length,
|
|
70
|
+
expect(firstEtag.trim().length, req('openwop.it.discovery.if-capabilities-etag-is-present-it-is-non-empty-and-stable-within-the-cache-wind',
|
|
70
71
|
'capabilities-change-detection.md §Capabilities-Etag',
|
|
71
72
|
'Capabilities-Etag, when present, MUST be a non-empty opaque string',
|
|
72
73
|
)).toBeGreaterThan(0);
|
|
@@ -74,7 +75,7 @@ describe('discovery: /.well-known/openwop', () => {
|
|
|
74
75
|
const second = await driver.get('/.well-known/openwop', { authenticated: false });
|
|
75
76
|
const secondEtag = second.headers.get('capabilities-etag');
|
|
76
77
|
|
|
77
|
-
expect(secondEtag,
|
|
78
|
+
expect(secondEtag, req('openwop.it.discovery.if-capabilities-etag-is-present-it-is-non-empty-and-stable-within-the-cache-wind',
|
|
78
79
|
'capabilities-change-detection.md §Conformance expectations',
|
|
79
80
|
'repeated discovery calls without host changes SHOULD return the same Capabilities-Etag within the cache window',
|
|
80
81
|
)).toBe(firstEtag);
|
|
@@ -84,18 +85,18 @@ describe('discovery: /.well-known/openwop', () => {
|
|
|
84
85
|
const res = await driver.get('/.well-known/openwop', { authenticated: false });
|
|
85
86
|
const limits = (res.json as { limits?: Record<string, number> } | undefined)?.limits;
|
|
86
87
|
|
|
87
|
-
expect(limits,
|
|
88
|
+
expect(limits, req('openwop.it.discovery.declares-non-zero-limits-per-capabilities-md-3-capabilitylimiter-shape',
|
|
88
89
|
'capabilities.md §3',
|
|
89
90
|
'Capabilities.limits MUST be present',
|
|
90
91
|
)).toBeDefined();
|
|
91
92
|
|
|
92
93
|
for (const k of ['clarificationRounds', 'schemaRounds', 'envelopesPerTurn']) {
|
|
93
94
|
const v = limits?.[k];
|
|
94
|
-
expect(typeof v,
|
|
95
|
+
expect(typeof v, req('openwop.it.discovery.declares-non-zero-limits-per-capabilities-md-3-capabilitylimiter-shape',
|
|
95
96
|
'capabilities.md §3',
|
|
96
97
|
`limits.${k} MUST be a non-negative integer`,
|
|
97
98
|
)).toBe('number');
|
|
98
|
-
expect(v ?? -1,
|
|
99
|
+
expect(v ?? -1, req('openwop.it.discovery.declares-non-zero-limits-per-capabilities-md-3-capabilitylimiter-shape',
|
|
99
100
|
'capabilities.md §3',
|
|
100
101
|
`limits.${k} MUST be >= 0`,
|
|
101
102
|
)).toBeGreaterThanOrEqual(0);
|
|
@@ -115,28 +116,28 @@ describe('discovery: /.well-known/openwop fixtures field shape per RFC 0003', ()
|
|
|
115
116
|
// RFC 0003 makes the field OPTIONAL — pre-RFC hosts and hosts
|
|
116
117
|
// that opt out advertise nothing. Assertion passes trivially.
|
|
117
118
|
expect(fixtures).toBeUndefined();
|
|
118
|
-
return;
|
|
119
|
+
return softSkip('blocked', 'precondition not met — `fixtures === undefined` returned early (seam, prior step, or fixture unavailable)');
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
expect(Array.isArray(fixtures),
|
|
122
|
+
expect(Array.isArray(fixtures), req('openwop.it.discovery.if-fixtures-is-present-it-must-be-a-string-of-unique-non-empty-entries',
|
|
122
123
|
'capabilities.md §`fixtures` (RFC 0003)',
|
|
123
124
|
'fixtures MUST be an array when present',
|
|
124
125
|
)).toBe(true);
|
|
125
126
|
|
|
126
127
|
const arr = fixtures as unknown[];
|
|
127
128
|
for (const entry of arr) {
|
|
128
|
-
expect(typeof entry,
|
|
129
|
+
expect(typeof entry, req('openwop.it.discovery.if-fixtures-is-present-it-must-be-a-string-of-unique-non-empty-entries',
|
|
129
130
|
'capabilities.md §`fixtures` (RFC 0003)',
|
|
130
131
|
'every fixtures entry MUST be a string',
|
|
131
132
|
)).toBe('string');
|
|
132
|
-
expect((entry as string).length,
|
|
133
|
+
expect((entry as string).length, req('openwop.it.discovery.if-fixtures-is-present-it-must-be-a-string-of-unique-non-empty-entries',
|
|
133
134
|
'capabilities.md §`fixtures` (RFC 0003)',
|
|
134
135
|
'every fixtures entry MUST be non-empty',
|
|
135
136
|
)).toBeGreaterThan(0);
|
|
136
137
|
}
|
|
137
138
|
|
|
138
139
|
const unique = new Set(arr as string[]);
|
|
139
|
-
expect(unique.size,
|
|
140
|
+
expect(unique.size, req('openwop.it.discovery.if-fixtures-is-present-it-must-be-a-string-of-unique-non-empty-entries',
|
|
140
141
|
'capabilities.md §`fixtures` (RFC 0003)',
|
|
141
142
|
'fixtures entries SHOULD be unique (consumers MUST tolerate duplicates by deduplicating)',
|
|
142
143
|
)).toBe(arr.length);
|
|
@@ -147,13 +148,13 @@ describe('discovery: /v1/openapi.json', () => {
|
|
|
147
148
|
it('returns 200 with a parseable OpenAPI 3.1 document', async () => {
|
|
148
149
|
const res = await driver.get('/v1/openapi.json', { authenticated: false });
|
|
149
150
|
|
|
150
|
-
expect(res.status,
|
|
151
|
+
expect(res.status, req('openwop.it.discovery.returns-200-with-a-parseable-openapi-3-1-document',
|
|
151
152
|
'rest-endpoints.md',
|
|
152
153
|
'self-describing OpenAPI endpoint MUST return 200',
|
|
153
154
|
)).toBe(200);
|
|
154
155
|
|
|
155
156
|
const body = res.json as { openapi?: string } | undefined;
|
|
156
|
-
expect(body?.openapi,
|
|
157
|
+
expect(body?.openapi, req('openwop.it.discovery.returns-200-with-a-parseable-openapi-3-1-document',
|
|
157
158
|
'rest-endpoints.md',
|
|
158
159
|
'response MUST declare openapi >= 3.1',
|
|
159
160
|
)).toMatch(/^3\.[1-9]/);
|
|
@@ -210,7 +211,7 @@ describe('discovery: auth-scoped capability shape', () => {
|
|
|
210
211
|
return;
|
|
211
212
|
}
|
|
212
213
|
|
|
213
|
-
expect(disc?.authScoped?.supported,
|
|
214
|
+
expect(disc?.authScoped?.supported, req('openwop.it.discovery.host-claiming-auth-scoped-discovery-advertises-required-fields',
|
|
214
215
|
'capabilities-change-detection.md §"Scoped capability views"',
|
|
215
216
|
'capabilities.discovery.authScoped.supported MUST be true when the profile is claimed',
|
|
216
217
|
)).toBe(true);
|
|
@@ -218,7 +219,7 @@ describe('discovery: auth-scoped capability shape', () => {
|
|
|
218
219
|
if (disc?.authScoped?.mode !== undefined) {
|
|
219
220
|
expect(
|
|
220
221
|
['same-endpoint', 'extension-endpoint'].includes(disc.authScoped.mode),
|
|
221
|
-
|
|
222
|
+
req('openwop.it.discovery.host-claiming-auth-scoped-discovery-advertises-required-fields',
|
|
222
223
|
'capabilities.schema.json discovery.authScoped.mode',
|
|
223
224
|
'mode MUST be one of same-endpoint / extension-endpoint when advertised',
|
|
224
225
|
),
|
|
@@ -229,7 +230,7 @@ describe('discovery: auth-scoped capability shape', () => {
|
|
|
229
230
|
expect(
|
|
230
231
|
typeof disc.authScoped.endpointPath === 'string' &&
|
|
231
232
|
disc.authScoped.endpointPath.startsWith('/'),
|
|
232
|
-
|
|
233
|
+
req('openwop.it.discovery.host-claiming-auth-scoped-discovery-advertises-required-fields',
|
|
233
234
|
'RFCS/0011-auth-scoped-discovery.md §A',
|
|
234
235
|
'extension-endpoint mode MUST advertise endpointPath as a leading-slash relative path',
|
|
235
236
|
),
|
|
@@ -254,13 +255,13 @@ describe('discovery: auth-scoped view satisfies base schema', () => {
|
|
|
254
255
|
|
|
255
256
|
const res = await driver.get(path);
|
|
256
257
|
|
|
257
|
-
expect(res.status,
|
|
258
|
+
expect(res.status, req('openwop.it.discovery.authenticated-discovery-preserves-required-capabilities-fields',
|
|
258
259
|
'capabilities-change-detection.md §"Scoped capability views"',
|
|
259
260
|
'authenticated discovery MUST return 200',
|
|
260
261
|
)).toBe(200);
|
|
261
262
|
|
|
262
263
|
const body = res.json as Record<string, unknown> | undefined;
|
|
263
|
-
expect(body, 'authenticated discovery body MUST be JSON').toBeDefined();
|
|
264
|
+
expect(body, req('openwop.it.discovery.authenticated-discovery-preserves-required-capabilities-fields', 'capabilities-change-detection.md §"Scoped capability views"', 'authenticated discovery body MUST be JSON')).toBeDefined();
|
|
264
265
|
|
|
265
266
|
// Required fields per capabilities.md §3 preserved in the
|
|
266
267
|
// authenticated view (per spec annex: "MUST still satisfy the
|
|
@@ -271,7 +272,7 @@ describe('discovery: auth-scoped view satisfies base schema', () => {
|
|
|
271
272
|
'schemaVersions',
|
|
272
273
|
'limits',
|
|
273
274
|
]) {
|
|
274
|
-
expect(body?.[required],
|
|
275
|
+
expect(body?.[required], req('openwop.it.discovery.authenticated-discovery-preserves-required-capabilities-fields',
|
|
275
276
|
'capabilities-change-detection.md §"Scoped capability views"',
|
|
276
277
|
`auth-scoped view MUST preserve required field "${required}" from capabilities.md §3`,
|
|
277
278
|
)).toBeDefined();
|
|
@@ -293,7 +294,7 @@ describe('discovery: auth-scoped is not an authorization oracle', () => {
|
|
|
293
294
|
console.warn(
|
|
294
295
|
'[discovery: auth-scoped] OPENWOP_TEST_UNAUTHORIZED_API_KEY not supplied; skipping authorization-oracle probe',
|
|
295
296
|
);
|
|
296
|
-
return;
|
|
297
|
+
return softSkip('blocked', 'precondition not met — `!unauthorizedKey` returned early ([discovery: auth-scoped] OPENWOP_TEST_UNAUTHORIZED_API_KEY not supplied; skipping authorization-oracle probe) (seam, prior step, or fixture unavailable)');
|
|
297
298
|
}
|
|
298
299
|
|
|
299
300
|
const mode = disc?.authScoped?.mode ?? 'same-endpoint';
|
|
@@ -314,7 +315,7 @@ describe('discovery: auth-scoped is not an authorization oracle', () => {
|
|
|
314
315
|
if (unauthorized.status === 401 || unauthorized.status === 403) {
|
|
315
316
|
// Host rejected the unauthorized key outright — that's fine.
|
|
316
317
|
// The oracle probe is moot when the host refuses the bearer.
|
|
317
|
-
return;
|
|
318
|
+
return softSkip('blocked', 'precondition not met — `unauthorized.status === 401 || unauthorized.status === 403` returned early (Host rejected the unauthorized key outright — that\'s fine. The oracle probe is moot when the host refuses the bearer.…');
|
|
318
319
|
}
|
|
319
320
|
expect(unauthorized.status).toBe(200);
|
|
320
321
|
|
|
@@ -330,7 +331,7 @@ describe('discovery: auth-scoped is not an authorization oracle', () => {
|
|
|
330
331
|
// primary view's keys (no capabilities the unauthorized caller
|
|
331
332
|
// can use that the primary cannot).
|
|
332
333
|
const extras = unauthorizedCaps.filter((c) => !primaryCaps.includes(c));
|
|
333
|
-
expect(extras.length,
|
|
334
|
+
expect(extras.length, req('openwop.it.discovery.unauthorized-key-must-not-reveal-capabilities-outside-its-authorization',
|
|
334
335
|
'capabilities-change-detection.md §"Scoped capability views"',
|
|
335
336
|
'unauthorized view MUST NOT expose capability keys absent from the primary (authorized) view',
|
|
336
337
|
)).toBe(0);
|
|
@@ -25,6 +25,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
25
25
|
import { driver } from '../lib/driver.js';
|
|
26
26
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
27
27
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
28
|
+
import { req } from '../lib/requirement-ids.js';
|
|
29
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
28
30
|
|
|
29
31
|
const PARENT = 'conformance-dispatch-cross-worker-handoff';
|
|
30
32
|
const CHILD_A = 'conformance-dispatch-cross-worker-handoff-child-a';
|
|
@@ -53,14 +55,14 @@ describe.skipIf(SKIP)('dispatch-cross-worker-handoff: sequential child→parent
|
|
|
53
55
|
const parentRunId = (create.json as { runId: string }).runId;
|
|
54
56
|
|
|
55
57
|
const parentTerminal = (await pollUntilTerminal(parentRunId)) as RunSnapshot;
|
|
56
|
-
expect(parentTerminal.status,
|
|
58
|
+
expect(parentTerminal.status, req('openwop.it.dispatch-cross-worker-handoff.hvmap-1c-child-a-writes-via-perworkeroutputmappings-child-b-reads-via-perworkeri',
|
|
57
59
|
'RFCS/0022-dispatch-input-output-mapping.md §D',
|
|
58
60
|
'parent run MUST reach terminal `completed` once both children finish sequentially',
|
|
59
61
|
)).toBe('completed');
|
|
60
62
|
|
|
61
63
|
// Parent's sharedVar MUST be 'hello' — set by child-a's outputMapping.
|
|
62
64
|
const parentVars = parentTerminal.variables ?? {};
|
|
63
|
-
expect(parentVars.sharedVar,
|
|
65
|
+
expect(parentVars.sharedVar, req('openwop.it.dispatch-cross-worker-handoff.hvmap-1c-child-a-writes-via-perworkeroutputmappings-child-b-reads-via-perworkeri',
|
|
64
66
|
'RFCS/0022-dispatch-input-output-mapping.md §A',
|
|
65
67
|
'parent `sharedVar` MUST be child-a\'s `output` projection ("hello") via perWorkerOutputMappings',
|
|
66
68
|
)).toBe('hello');
|
|
@@ -72,7 +74,7 @@ describe.skipIf(SKIP)('dispatch-cross-worker-handoff: sequential child→parent
|
|
|
72
74
|
const dispatchedB = events.find(
|
|
73
75
|
(e) => e.type === 'node.dispatched' && e.payload?.childWorkflowId === CHILD_B,
|
|
74
76
|
);
|
|
75
|
-
expect(dispatchedB,
|
|
77
|
+
expect(dispatchedB, req('openwop.it.dispatch-cross-worker-handoff.hvmap-1c-child-a-writes-via-perworkeroutputmappings-child-b-reads-via-perworkeri',
|
|
76
78
|
'RFCS/0022-dispatch-input-output-mapping.md §D',
|
|
77
79
|
'parent event log MUST contain a `node.dispatched` event for child-b after child-a completes (sequential fan-out)',
|
|
78
80
|
)).toBeDefined();
|
|
@@ -86,7 +88,7 @@ describe.skipIf(SKIP)('dispatch-cross-worker-handoff: sequential child→parent
|
|
|
86
88
|
const childBSnapshot = childBSnapshotRes.json as RunSnapshot;
|
|
87
89
|
expect(childBSnapshot.status).toBe('completed');
|
|
88
90
|
const childBInputs = childBSnapshot.inputs ?? {};
|
|
89
|
-
expect(childBInputs.input,
|
|
91
|
+
expect(childBInputs.input, req('openwop.it.dispatch-cross-worker-handoff.hvmap-1c-child-a-writes-via-perworkeroutputmappings-child-b-reads-via-perworkeri',
|
|
90
92
|
'RFCS/0022-dispatch-input-output-mapping.md §A + §D',
|
|
91
93
|
'child-b `inputs.input` MUST be parent\'s `sharedVar` ("hello") — written by child-a, read by child-b via shared parent variable bag',
|
|
92
94
|
)).toBe('hello');
|
|
@@ -94,7 +96,7 @@ describe.skipIf(SKIP)('dispatch-cross-worker-handoff: sequential child→parent
|
|
|
94
96
|
|
|
95
97
|
it('HVMAP-1c-override: per-worker mapping overrides default mapping per §A effectiveInputMapping precedence', async () => {
|
|
96
98
|
const PARENT_OVERRIDE = 'conformance-dispatch-per-worker-override';
|
|
97
|
-
if (!isFixtureAdvertised(PARENT_OVERRIDE)) return; // fixture not seeded — soft-skip
|
|
99
|
+
if (!isFixtureAdvertised(PARENT_OVERRIDE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(PARENT_OVERRIDE)` returned early (fixture not seeded — soft-skip)'); // fixture not seeded — soft-skip
|
|
98
100
|
const create = await driver.post('/v1/runs', { workflowId: PARENT_OVERRIDE });
|
|
99
101
|
expect(create.status).toBe(201);
|
|
100
102
|
const parentRunId = (create.json as { runId: string }).runId;
|
|
@@ -104,7 +106,7 @@ describe.skipIf(SKIP)('dispatch-cross-worker-handoff: sequential child→parent
|
|
|
104
106
|
const events = ((eventsRes.json as { events?: RunEvent[] } | undefined)?.events ?? []);
|
|
105
107
|
const dispatchedA = events.find((e) => e.type === 'node.dispatched' && e.payload?.childWorkflowId === CHILD_A);
|
|
106
108
|
const dispatchedB = events.find((e) => e.type === 'node.dispatched' && e.payload?.childWorkflowId === CHILD_B);
|
|
107
|
-
if (!dispatchedA || !dispatchedB) return;
|
|
109
|
+
if (!dispatchedA || !dispatchedB) return softSkip('blocked', 'precondition not met — `!dispatchedA || !dispatchedB` returned early (seam, prior step, or fixture unavailable)');
|
|
108
110
|
|
|
109
111
|
const childARes = await driver.get(`/v1/runs/${encodeURIComponent(dispatchedA.payload!.childRunId!)}`);
|
|
110
112
|
const childBRes = await driver.get(`/v1/runs/${encodeURIComponent(dispatchedB.payload!.childRunId!)}`);
|
|
@@ -113,14 +115,14 @@ describe.skipIf(SKIP)('dispatch-cross-worker-handoff: sequential child→parent
|
|
|
113
115
|
|
|
114
116
|
expect(
|
|
115
117
|
childAInputs.input,
|
|
116
|
-
|
|
118
|
+
req('openwop.it.dispatch-cross-worker-handoff.hvmap-1c-override-per-worker-mapping-overrides-default-mapping-per-a-effectivein',
|
|
117
119
|
'RFCS/0022-dispatch-input-output-mapping.md §A',
|
|
118
120
|
'child-a uses the DEFAULT inputMapping; input MUST come from parent.defaultX',
|
|
119
121
|
),
|
|
120
122
|
).toBe('default-x-value');
|
|
121
123
|
expect(
|
|
122
124
|
childBInputs.input,
|
|
123
|
-
|
|
125
|
+
req('openwop.it.dispatch-cross-worker-handoff.hvmap-1c-override-per-worker-mapping-overrides-default-mapping-per-a-effectivein',
|
|
124
126
|
'RFCS/0022-dispatch-input-output-mapping.md §A',
|
|
125
127
|
'child-b uses the per-worker OVERRIDE; input MUST come from parent.sharedVar (NOT defaultX)',
|
|
126
128
|
),
|