@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
|
@@ -23,6 +23,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
23
23
|
import { driver } from '../lib/driver.js';
|
|
24
24
|
import { pollUntilStatus, 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
|
const WORKFLOW_ID = 'conformance-interrupt-auth-required';
|
|
28
30
|
const NODE_ID = 'gate';
|
|
@@ -40,7 +42,7 @@ describe.skipIf(SKIP)('interrupt: auth-required — bearer resume succeeds', ()
|
|
|
40
42
|
`/v1/runs/${encodeURIComponent(runId)}/interrupts/${encodeURIComponent(NODE_ID)}`,
|
|
41
43
|
{ resumeValue: { action: 'accept' } },
|
|
42
44
|
);
|
|
43
|
-
expect(resolve.status,
|
|
45
|
+
expect(resolve.status, req('openwop.it.interrupt-auth-required-resume.valid-bearer-with-approvals-respond-drives-terminal-completed',
|
|
44
46
|
'interrupt-profiles.md §openwop-interrupt-auth-required',
|
|
45
47
|
'bearer-token resume with approvals:respond scope MUST succeed',
|
|
46
48
|
)).toBeGreaterThanOrEqual(200);
|
|
@@ -63,7 +65,7 @@ describe.skipIf(SKIP)('interrupt: auth-required — insufficient scope returns 4
|
|
|
63
65
|
'[interrupt-auth-required-resume] skipping insufficient-scope subtest: ' +
|
|
64
66
|
'OPENWOP_TEST_LOW_SCOPE_KEY not set',
|
|
65
67
|
);
|
|
66
|
-
return;
|
|
68
|
+
return softSkip('blocked', 'precondition not met — `!lowScopeKey` returned early (seam, prior step, or fixture unavailable)');
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
const create = await driver.post('/v1/runs', { workflowId: WORKFLOW_ID });
|
|
@@ -76,7 +78,7 @@ describe.skipIf(SKIP)('interrupt: auth-required — insufficient scope returns 4
|
|
|
76
78
|
{ resumeValue: { action: 'accept' } },
|
|
77
79
|
{ headers: { Authorization: `Bearer ${lowScopeKey}` } },
|
|
78
80
|
);
|
|
79
|
-
expect(resolve.status,
|
|
81
|
+
expect(resolve.status, req('openwop.it.interrupt-auth-required-resume.bearer-without-approvals-respond-scope-is-rejected',
|
|
80
82
|
'auth.md §scopes + interrupt-profiles.md §openwop-interrupt-auth-required',
|
|
81
83
|
'bearer without approvals:respond scope MUST return 403',
|
|
82
84
|
)).toBe(403);
|
|
@@ -10,6 +10,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
10
10
|
import { driver } from '../lib/driver.js';
|
|
11
11
|
import { pollUntilStatus, pollUntilTerminal } from '../lib/polling.js';
|
|
12
12
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
13
|
+
import { req } from '../lib/requirement-ids.js';
|
|
13
14
|
|
|
14
15
|
const WORKFLOW_ID = 'conformance-clarification';
|
|
15
16
|
const NODE_ID = 'ask';
|
|
@@ -22,7 +23,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('interrupt: clarification answers resume to `co
|
|
|
22
23
|
const runId = (create.json as { runId: string }).runId;
|
|
23
24
|
|
|
24
25
|
const suspended = await pollUntilStatus(runId, 'waiting-input', { timeoutMs: 10_000 });
|
|
25
|
-
expect(suspended.currentNodeId,
|
|
26
|
+
expect(suspended.currentNodeId, req('openwop.it.interrupt-clarification.run-suspends-at-ask-answers-payload-drives-terminal-completed',
|
|
26
27
|
'fixtures.md conformance-clarification',
|
|
27
28
|
'suspended run MUST report currentNodeId === "ask"',
|
|
28
29
|
)).toBe(NODE_ID);
|
|
@@ -31,13 +32,13 @@ describe.skipIf(SKIP_NO_FIXTURE)('interrupt: clarification answers resume to `co
|
|
|
31
32
|
`/v1/runs/${encodeURIComponent(runId)}/interrupts/${encodeURIComponent(NODE_ID)}`,
|
|
32
33
|
{ resumeValue: { answers: { q1: 'blue' } } },
|
|
33
34
|
);
|
|
34
|
-
expect(resolve.status,
|
|
35
|
+
expect(resolve.status, req('openwop.it.interrupt-clarification.run-suspends-at-ask-answers-payload-drives-terminal-completed',
|
|
35
36
|
'rest-endpoints.md POST /v1/runs/{runId}/interrupts/{nodeId}',
|
|
36
37
|
'valid clarification resolve MUST return 200',
|
|
37
38
|
)).toBe(200);
|
|
38
39
|
|
|
39
40
|
const terminal = await pollUntilTerminal(runId, { timeoutMs: 10_000 });
|
|
40
|
-
expect(terminal.status,
|
|
41
|
+
expect(terminal.status, req('openwop.it.interrupt-clarification.run-suspends-at-ask-answers-payload-drives-terminal-completed',
|
|
41
42
|
'fixtures.md conformance-clarification §Terminal status',
|
|
42
43
|
'fixture after resolve MUST reach terminal `completed`',
|
|
43
44
|
)).toBe('completed');
|
|
@@ -22,6 +22,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
22
22
|
import { driver } from '../lib/driver.js';
|
|
23
23
|
import { pollUntilStatus, pollUntilTerminal } from '../lib/polling.js';
|
|
24
24
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
25
|
+
import { req } from '../lib/requirement-ids.js';
|
|
25
26
|
|
|
26
27
|
const WORKFLOW_ID = 'conformance-interrupt-external-event';
|
|
27
28
|
const SKIP = !isFixtureAdvertised(WORKFLOW_ID);
|
|
@@ -51,7 +52,7 @@ describe.skipIf(SKIP)('interrupt: external-event — matching correlation resume
|
|
|
51
52
|
await pollUntilStatus(runId, 'waiting-external', { timeoutMs: 10_000 });
|
|
52
53
|
|
|
53
54
|
const token = await fetchInterruptToken(runId);
|
|
54
|
-
expect(token,
|
|
55
|
+
expect(token, req('openwop.it.interrupt-external-event-correlation.signed-token-post-with-matching-correlation-drives-terminal-completed',
|
|
55
56
|
'interrupt.md §Signed-token callback',
|
|
56
57
|
'suspended external-event interrupt MUST expose a signed token to the caller',
|
|
57
58
|
)).not.toBeNull();
|
|
@@ -63,14 +64,14 @@ describe.skipIf(SKIP)('interrupt: external-event — matching correlation resume
|
|
|
63
64
|
externalReference: 'conformance-test-123',
|
|
64
65
|
},
|
|
65
66
|
});
|
|
66
|
-
expect(resolve.status,
|
|
67
|
+
expect(resolve.status, req('openwop.it.interrupt-external-event-correlation.signed-token-post-with-matching-correlation-drives-terminal-completed',
|
|
67
68
|
'rest-endpoints.md POST /v1/interrupts/{token}',
|
|
68
69
|
'token resolve with matching correlation MUST return 2xx',
|
|
69
70
|
)).toBeGreaterThanOrEqual(200);
|
|
70
71
|
expect(resolve.status).toBeLessThan(300);
|
|
71
72
|
|
|
72
73
|
const terminal = await pollUntilTerminal(runId, { timeoutMs: 10_000 });
|
|
73
|
-
expect(terminal.status,
|
|
74
|
+
expect(terminal.status, req('openwop.it.interrupt-external-event-correlation.signed-token-post-with-matching-correlation-drives-terminal-completed',
|
|
74
75
|
'fixtures.md conformance-interrupt-external-event',
|
|
75
76
|
'matching external event MUST drive terminal completed',
|
|
76
77
|
)).toBe('completed');
|
|
@@ -96,7 +97,7 @@ describe.skipIf(SKIP)('interrupt: external-event — mismatched correlation reje
|
|
|
96
97
|
});
|
|
97
98
|
expect(
|
|
98
99
|
[422, 400].includes(resolve.status),
|
|
99
|
-
|
|
100
|
+
req('openwop.it.interrupt-external-event-correlation.correlation-mismatch-returns-422-and-leaves-run-suspended',
|
|
100
101
|
'interrupt-profiles.md §openwop-interrupt-external-event',
|
|
101
102
|
'correlation mismatch MUST return 422 (or 400) without resuming',
|
|
102
103
|
),
|
|
@@ -104,7 +105,7 @@ describe.skipIf(SKIP)('interrupt: external-event — mismatched correlation reje
|
|
|
104
105
|
|
|
105
106
|
const still = await driver.get(`/v1/runs/${encodeURIComponent(runId)}`);
|
|
106
107
|
const status = (still.json as { status: string }).status;
|
|
107
|
-
expect(status, 'run MUST remain suspended after correlation rejection').toMatch(/^waiting-/);
|
|
108
|
+
expect(status, req('openwop.it.interrupt-external-event-correlation.correlation-mismatch-returns-422-and-leaves-run-suspended', 'interrupt-profiles.md §openwop-interrupt-external-event', 'run MUST remain suspended after correlation rejection')).toMatch(/^waiting-/);
|
|
108
109
|
|
|
109
110
|
await driver.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {
|
|
110
111
|
reason: 'conformance-cleanup',
|
|
@@ -21,6 +21,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
21
21
|
import { driver } from '../lib/driver.js';
|
|
22
22
|
import { pollUntilStatus, pollUntilTerminal } from '../lib/polling.js';
|
|
23
23
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
24
|
+
import { req } from '../lib/requirement-ids.js';
|
|
24
25
|
|
|
25
26
|
const PARENT_WORKFLOW = 'conformance-interrupt-parent-child-cancel';
|
|
26
27
|
const CHILD_WORKFLOW = 'conformance-interrupt-parent-child-cancel-child';
|
|
@@ -50,7 +51,7 @@ describe.skipIf(SKIP)('interrupt: parent/child — parent cancel cascades to chi
|
|
|
50
51
|
await pollUntilStatus(parentRunId, 'waiting-approval', { timeoutMs: 15_000 });
|
|
51
52
|
|
|
52
53
|
const childRunId = await findChildRunId(parentRunId);
|
|
53
|
-
expect(childRunId,
|
|
54
|
+
expect(childRunId, req('openwop.it.interrupt-parent-child-cascade.child-transitions-to-cancelled-when-parent-is-cancelled-mid-suspend',
|
|
54
55
|
'fixtures.md conformance-interrupt-parent-child-cancel',
|
|
55
56
|
'parent snapshot MUST surface the spawned child runId',
|
|
56
57
|
)).not.toBeNull();
|
|
@@ -65,7 +66,7 @@ describe.skipIf(SKIP)('interrupt: parent/child — parent cancel cascades to chi
|
|
|
65
66
|
expect(parentTerminal.status).toBe('cancelled');
|
|
66
67
|
|
|
67
68
|
const childTerminal = await pollUntilTerminal(childRunId!, { timeoutMs: 10_000 });
|
|
68
|
-
expect(childTerminal.status,
|
|
69
|
+
expect(childTerminal.status, req('openwop.it.interrupt-parent-child-cascade.child-transitions-to-cancelled-when-parent-is-cancelled-mid-suspend',
|
|
69
70
|
'interrupt-profiles.md §openwop-interrupt-parent-child',
|
|
70
71
|
'parent cancellation MUST cascade — child run MUST reach terminal cancelled',
|
|
71
72
|
)).toBe('cancelled');
|
|
@@ -93,7 +94,7 @@ describe.skipIf(SKIP)('interrupt: parent/child — post-cascade resolve of child
|
|
|
93
94
|
);
|
|
94
95
|
expect(
|
|
95
96
|
[410, 409, 404].includes(lateResolve.status),
|
|
96
|
-
|
|
97
|
+
req('openwop.it.interrupt-parent-child-cascade.attempting-to-resolve-the-cascaded-child-interrupt-is-rejected',
|
|
97
98
|
'interrupt-profiles.md §openwop-interrupt-parent-child',
|
|
98
99
|
'resolving a cascaded child interrupt MUST be rejected (410 Gone preferred, 409/404 acceptable)',
|
|
99
100
|
),
|
|
@@ -21,6 +21,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
21
21
|
import { driver } from '../lib/driver.js';
|
|
22
22
|
import { pollUntilStatus, pollUntilTerminal } from '../lib/polling.js';
|
|
23
23
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
24
|
+
import { req } from '../lib/requirement-ids.js';
|
|
24
25
|
|
|
25
26
|
// Profile gating: a host claims `openwop-interrupt-quorum` support by
|
|
26
27
|
// advertising the fixture. Hosts that don't support quorum semantics
|
|
@@ -42,7 +43,7 @@ describe.skipIf(SKIP)('interrupt: quorum — three accepts resume to completed',
|
|
|
42
43
|
`/v1/runs/${encodeURIComponent(runId)}/interrupts/${encodeURIComponent(NODE_ID)}`,
|
|
43
44
|
{ resumeValue: { action: 'accept', voter: `approver-${i}` } },
|
|
44
45
|
);
|
|
45
|
-
expect(partial.status,
|
|
46
|
+
expect(partial.status, req('openwop.it.interrupt-quorum-resolution.first-two-accepts-persist-without-resuming-third-accept-drives-terminal-complete',
|
|
46
47
|
'interrupt-profiles.md §openwop-interrupt-quorum',
|
|
47
48
|
`partial vote ${i}/3 MUST be accepted (2xx) without terminating the run`,
|
|
48
49
|
)).toBeGreaterThanOrEqual(200);
|
|
@@ -50,7 +51,7 @@ describe.skipIf(SKIP)('interrupt: quorum — three accepts resume to completed',
|
|
|
50
51
|
|
|
51
52
|
const stillWaiting = await driver.get(`/v1/runs/${encodeURIComponent(runId)}`);
|
|
52
53
|
const status = (stillWaiting.json as { status: string }).status;
|
|
53
|
-
expect(status,
|
|
54
|
+
expect(status, req('openwop.it.interrupt-quorum-resolution.first-two-accepts-persist-without-resuming-third-accept-drives-terminal-complete',
|
|
54
55
|
'interrupt-profiles.md §openwop-interrupt-quorum',
|
|
55
56
|
`run MUST still be in waiting-approval after ${i}/3 votes (quorum not met)`,
|
|
56
57
|
)).toBe('waiting-approval');
|
|
@@ -64,7 +65,7 @@ describe.skipIf(SKIP)('interrupt: quorum — three accepts resume to completed',
|
|
|
64
65
|
expect(final.status).toBeLessThan(300);
|
|
65
66
|
|
|
66
67
|
const terminal = await pollUntilTerminal(runId, { timeoutMs: 10_000 });
|
|
67
|
-
expect(terminal.status,
|
|
68
|
+
expect(terminal.status, req('openwop.it.interrupt-quorum-resolution.first-two-accepts-persist-without-resuming-third-accept-drives-terminal-complete',
|
|
68
69
|
'fixtures.md conformance-interrupt-quorum §Terminal status',
|
|
69
70
|
'three accepts (quorum met) MUST drive terminal completed',
|
|
70
71
|
)).toBe('completed');
|
|
@@ -89,7 +90,7 @@ describe.skipIf(SKIP)('interrupt: quorum — majority reject fails the gate', ()
|
|
|
89
90
|
);
|
|
90
91
|
|
|
91
92
|
const terminal = await pollUntilTerminal(runId, { timeoutMs: 10_000 });
|
|
92
|
-
expect(['failed', 'rejected'],
|
|
93
|
+
expect(['failed', 'rejected'], req('openwop.it.interrupt-quorum-resolution.two-rejects-out-of-three-votes-trigger-the-majority-reject-termination',
|
|
93
94
|
'interrupt-profiles.md §openwop-interrupt-quorum (rejectionPolicy: majority)',
|
|
94
95
|
'majority rejection MUST drive a non-completed terminal state',
|
|
95
96
|
)).toContain(terminal.status);
|
|
@@ -30,6 +30,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
30
30
|
import { driver } from '../lib/driver.js';
|
|
31
31
|
import { pollUntilStatus } from '../lib/polling.js';
|
|
32
32
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
33
|
+
import { req } from '../lib/requirement-ids.js';
|
|
34
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
33
35
|
|
|
34
36
|
const FIXTURE = 'conformance-interrupt-external-event';
|
|
35
37
|
const SKIP = !isFixtureAdvertised(FIXTURE);
|
|
@@ -45,7 +47,7 @@ describe.skipIf(SKIP)('interrupt-token-matrix: GET /v1/interrupts/{token} negati
|
|
|
45
47
|
const malformed = '!!!not-a-valid-token!!!';
|
|
46
48
|
const res = await driver.get(`/v1/interrupts/${encodeURIComponent(malformed)}`);
|
|
47
49
|
expect([400, 404]).toContain(res.status);
|
|
48
|
-
expect(res.status,
|
|
50
|
+
expect(res.status, req('openwop.it.interrupt-token-matrix.malformed-token-returns-400-or-404-never-200',
|
|
49
51
|
'rest-endpoints.md GET /v1/interrupts/{token}',
|
|
50
52
|
'malformed interrupt token MUST NOT return 200',
|
|
51
53
|
)).not.toBe(200);
|
|
@@ -55,7 +57,7 @@ describe.skipIf(SKIP)('interrupt-token-matrix: GET /v1/interrupts/{token} negati
|
|
|
55
57
|
// Plausibly-shaped opaque token that the host has no record of.
|
|
56
58
|
const unknown = `tok_${randomBytesB64(32)}`;
|
|
57
59
|
const res = await driver.get(`/v1/interrupts/${encodeURIComponent(unknown)}`);
|
|
58
|
-
expect(res.status,
|
|
60
|
+
expect(res.status, req('openwop.it.interrupt-token-matrix.well-formed-but-unknown-token-returns-404',
|
|
59
61
|
'rest-endpoints.md GET /v1/interrupts/{token}',
|
|
60
62
|
'unknown interrupt token MUST return 404',
|
|
61
63
|
)).toBe(404);
|
|
@@ -83,7 +85,7 @@ describe.skipIf(SKIP)('interrupt-token-matrix: POST /v1/interrupts/{token} negat
|
|
|
83
85
|
await driver.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {
|
|
84
86
|
reason: 'conformance-cleanup',
|
|
85
87
|
});
|
|
86
|
-
return;
|
|
88
|
+
return softSkip('blocked', 'precondition not met — `typeof token !== \'string\'` returned early ([interrupt-token-matrix] host did not surface an interrupt token; skipping replay subtest) (seam, prior step, or fixture unavailable)');
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
const resolve1 = await driver.post(`/v1/interrupts/${encodeURIComponent(token)}`, {
|
|
@@ -97,13 +99,13 @@ describe.skipIf(SKIP)('interrupt-token-matrix: POST /v1/interrupts/{token} negat
|
|
|
97
99
|
await driver.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {
|
|
98
100
|
reason: 'conformance-cleanup',
|
|
99
101
|
});
|
|
100
|
-
return;
|
|
102
|
+
return softSkip('blocked', 'precondition not met — `resolve1.status !== 200 && resolve1.status !== 202` returned early ([interrupt-token-matrix] first resolve returned …; can\'t exercise replay path. Skipping.) (seam, prior step, or fixture unava…');
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
const resolve2 = await driver.post(`/v1/interrupts/${encodeURIComponent(token)}`, {
|
|
104
106
|
correlation: { orderId: 'order-token-matrix', status: 'accepted' },
|
|
105
107
|
});
|
|
106
|
-
expect([404, 409, 410],
|
|
108
|
+
expect([404, 409, 410], req('openwop.it.interrupt-token-matrix.replay-after-successful-resolve-returns-409-or-404',
|
|
107
109
|
'rest-endpoints.md POST /v1/interrupts/{token}',
|
|
108
110
|
'replay of an already-resolved interrupt token MUST NOT return 2xx (host MAY 404/409/410)',
|
|
109
111
|
)).toContain(resolve2.status);
|
|
@@ -118,7 +120,7 @@ describe.skipIf(SKIP)('interrupt-token-matrix: POST /v1/interrupts/{token} negat
|
|
|
118
120
|
const res = await driver.post(`/v1/interrupts/${encodeURIComponent(unknown)}`, {
|
|
119
121
|
correlation: { orderId: 'noop', status: 'whatever' },
|
|
120
122
|
});
|
|
121
|
-
expect(res.status,
|
|
123
|
+
expect(res.status, req('openwop.it.interrupt-token-matrix.unknown-token-returns-404-on-post',
|
|
122
124
|
'rest-endpoints.md POST /v1/interrupts/{token}',
|
|
123
125
|
'POST on an unknown interrupt token MUST return 404',
|
|
124
126
|
)).toBe(404);
|
|
@@ -23,6 +23,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
23
23
|
import { driver } from '../lib/driver.js';
|
|
24
24
|
import { pollUntil } 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
|
const APPROVAL_WORKFLOW_ID = 'conformance-approval';
|
|
28
30
|
const NOOP_WORKFLOW_ID = 'conformance-noop';
|
|
@@ -45,13 +47,13 @@ describe.skipIf(SKIP_NO_APPROVAL)('interrupt-race: concurrent cancel + resolve d
|
|
|
45
47
|
it(
|
|
46
48
|
'concurrent cancel + interrupt-resolve resolves to one of: cancelled or completed',
|
|
47
49
|
async () => {
|
|
48
|
-
if (!(await hostClaimsInterrupts())) return; // skip-equivalent
|
|
50
|
+
if (!(await hostClaimsInterrupts())) return softSkip('blocked', 'precondition not met — `!(await hostClaimsInterrupts())` returned early (skip-equivalent) (seam, prior step, or fixture unavailable)'); // skip-equivalent
|
|
49
51
|
|
|
50
52
|
// Phase 1: start a workflow that suspends at an approval gate.
|
|
51
53
|
const create = await driver.post('/v1/runs', { workflowId: APPROVAL_WORKFLOW_ID });
|
|
52
54
|
if (create.status !== 201) {
|
|
53
55
|
// Host may not seed conformance-approval fixture; skip.
|
|
54
|
-
return;
|
|
56
|
+
return softSkip('blocked', 'precondition not met — `create.status !== 201` returned early (Host may not seed conformance-approval fixture; skip.) (seam, prior step, or fixture unavailable)');
|
|
55
57
|
}
|
|
56
58
|
const runId = (create.json as { runId: string }).runId;
|
|
57
59
|
|
|
@@ -72,7 +74,7 @@ describe.skipIf(SKIP_NO_APPROVAL)('interrupt-race: concurrent cancel + resolve d
|
|
|
72
74
|
if (typeof nodeId !== 'string') {
|
|
73
75
|
// Host doesn't expose currentNodeId on suspended snapshots —
|
|
74
76
|
// can't drive the race deterministically; skip-equivalent.
|
|
75
|
-
return;
|
|
77
|
+
return softSkip('blocked', 'precondition not met — `typeof nodeId !== \'string\'` returned early (Host doesn\'t expose currentNodeId on suspended snapshots — can\'t drive the race deterministically; skip-equivalent.) (seam, prior step, or fixture un…');
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
// Phase 3: fire cancel + resolve concurrently. Promise.all so
|
|
@@ -92,14 +94,14 @@ describe.skipIf(SKIP_NO_APPROVAL)('interrupt-race: concurrent cancel + resolve d
|
|
|
92
94
|
// (operation lost the race). Anything else is non-deterministic.
|
|
93
95
|
expect(
|
|
94
96
|
[200, 202, 409].includes(cancelRes.status),
|
|
95
|
-
|
|
97
|
+
req('openwop.it.interruptRace.concurrent-cancel-interrupt-resolve-resolves-to-one-of-cancelled-or-completed',
|
|
96
98
|
'spec/v1/interrupt.md',
|
|
97
99
|
`cancel response under race MUST be 200/202/409; got ${cancelRes.status}`,
|
|
98
100
|
),
|
|
99
101
|
).toBe(true);
|
|
100
102
|
expect(
|
|
101
103
|
[200, 202, 400, 404, 409].includes(resolveRes.status),
|
|
102
|
-
|
|
104
|
+
req('openwop.it.interruptRace.concurrent-cancel-interrupt-resolve-resolves-to-one-of-cancelled-or-completed',
|
|
103
105
|
'spec/v1/interrupt.md',
|
|
104
106
|
`resolve response under race MUST be 200/202/400/404/409; got ${resolveRes.status}`,
|
|
105
107
|
),
|
|
@@ -110,7 +112,7 @@ describe.skipIf(SKIP_NO_APPROVAL)('interrupt-race: concurrent cancel + resolve d
|
|
|
110
112
|
const resolveSucceeded = resolveRes.status === 200 || resolveRes.status === 202;
|
|
111
113
|
expect(
|
|
112
114
|
cancelSucceeded || resolveSucceeded,
|
|
113
|
-
|
|
115
|
+
req('openwop.it.interruptRace.concurrent-cancel-interrupt-resolve-resolves-to-one-of-cancelled-or-completed',
|
|
114
116
|
'spec/v1/interrupt.md',
|
|
115
117
|
'under cancel/resolve race, at least one operation MUST succeed',
|
|
116
118
|
),
|
|
@@ -131,7 +133,7 @@ describe.skipIf(SKIP_NO_APPROVAL)('interrupt-race: concurrent cancel + resolve d
|
|
|
131
133
|
|
|
132
134
|
expect(
|
|
133
135
|
['completed', 'cancelled', 'failed'].includes(terminal.status),
|
|
134
|
-
|
|
136
|
+
req('openwop.it.interruptRace.concurrent-cancel-interrupt-resolve-resolves-to-one-of-cancelled-or-completed',
|
|
135
137
|
'spec/v1/interrupt.md',
|
|
136
138
|
'race outcome MUST converge on a terminal status, not stay in waiting-approval forever',
|
|
137
139
|
),
|
|
@@ -143,7 +145,7 @@ describe.skipIf(SKIP_NO_APPROVAL)('interrupt-race: concurrent cancel + resolve d
|
|
|
143
145
|
// (assuming the workflow has nothing else to fail on after the
|
|
144
146
|
// approval gate).
|
|
145
147
|
if (cancelSucceeded && !resolveSucceeded) {
|
|
146
|
-
expect(terminal.status,
|
|
148
|
+
expect(terminal.status, req('openwop.it.interruptRace.concurrent-cancel-interrupt-resolve-resolves-to-one-of-cancelled-or-completed',
|
|
147
149
|
'spec/v1/interrupt.md',
|
|
148
150
|
'when cancel wins the race, run MUST terminate as cancelled',
|
|
149
151
|
)).toBe('cancelled');
|
|
@@ -158,7 +160,7 @@ describe.skipIf(SKIP_NO_NOOP)('interrupt-race: cancel against a non-suspended ru
|
|
|
158
160
|
// Self-test that doesn't require a race. Runs against any host
|
|
159
161
|
// that supports cancel (every conforming host does).
|
|
160
162
|
const create = await driver.post('/v1/runs', { workflowId: 'conformance-noop' });
|
|
161
|
-
if (create.status !== 201) return;
|
|
163
|
+
if (create.status !== 201) return softSkip('blocked', 'precondition not met — `create.status !== 201` returned early (seam, prior step, or fixture unavailable)');
|
|
162
164
|
const runId = (create.json as { runId: string }).runId;
|
|
163
165
|
|
|
164
166
|
await pollUntil(
|
|
@@ -168,7 +170,7 @@ describe.skipIf(SKIP_NO_NOOP)('interrupt-race: cancel against a non-suspended ru
|
|
|
168
170
|
);
|
|
169
171
|
|
|
170
172
|
const cancel = await driver.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {});
|
|
171
|
-
expect(cancel.status,
|
|
173
|
+
expect(cancel.status, req('openwop.it.interruptRace.cancel-of-a-completed-run-returns-200-with-the-existing-terminal-status-idempote',
|
|
172
174
|
'spec/v1/rest-endpoints.md POST /v1/runs/{runId}/cancel',
|
|
173
175
|
'cancel of an already-terminal run MUST return 200 (idempotent)',
|
|
174
176
|
)).toBe(200);
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
import { describe, it, expect } from 'vitest';
|
|
12
12
|
import { driver } from '../lib/driver.js';
|
|
13
13
|
import { discoveryFamilies } from '../lib/discovery-capabilities.js';
|
|
14
|
+
import { req } from '../lib/requirement-ids.js';
|
|
15
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
14
16
|
|
|
15
17
|
interface DiscoveryDoc {
|
|
16
18
|
capabilities?: Record<string, unknown>;
|
|
@@ -31,10 +33,10 @@ async function call(op: string, args: Record<string, unknown>) {
|
|
|
31
33
|
describe('kv-atomic-increment: advertisement shape (RFC 0015)', () => {
|
|
32
34
|
it('capabilities.kvStorage is either absent or a well-formed object', async () => {
|
|
33
35
|
const cap = await readCap();
|
|
34
|
-
if (cap === null) return;
|
|
36
|
+
if (cap === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap === null` returned early');
|
|
35
37
|
expect(
|
|
36
38
|
typeof cap.supported,
|
|
37
|
-
|
|
39
|
+
req('openwop.it.kv-atomic-increment.capabilities-kvstorage-is-either-absent-or-a-well-formed-object',
|
|
38
40
|
'capabilities.schema.json §kvStorage',
|
|
39
41
|
'capabilities.kvStorage.supported MUST be a boolean when present',
|
|
40
42
|
),
|
|
@@ -43,33 +45,33 @@ describe('kv-atomic-increment: advertisement shape (RFC 0015)', () => {
|
|
|
43
45
|
|
|
44
46
|
it('atomicIncrement is a boolean when set', async () => {
|
|
45
47
|
const cap = await readCap();
|
|
46
|
-
if (!cap || cap.supported !== true) return;
|
|
48
|
+
if (!cap || cap.supported !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!cap || cap.supported !== true` returned early');
|
|
47
49
|
const sub = cap.atomicIncrement;
|
|
48
|
-
if (sub === undefined) return;
|
|
49
|
-
expect(typeof sub,
|
|
50
|
+
if (sub === undefined) return softSkip('blocked', 'precondition not met — `sub === undefined` returned early (seam, prior step, or fixture unavailable)');
|
|
51
|
+
expect(typeof sub, req('openwop.it.kv-atomic-increment.atomicincrement-is-a-boolean-when-set', 'RFC 0015 §A', 'atomicIncrement MUST be boolean when present')).toBe('boolean');
|
|
50
52
|
});
|
|
51
53
|
});
|
|
52
54
|
|
|
53
55
|
describe('kv-atomic-increment: behavioral (RFC 0015 §B point 4)', () => {
|
|
54
56
|
it('N concurrent +1 increments converge to exactly N', async () => {
|
|
55
57
|
const cap = await readCap();
|
|
56
|
-
if (!cap || cap.supported !== true || cap.atomicIncrement !== true) return;
|
|
58
|
+
if (!cap || cap.supported !== true || cap.atomicIncrement !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!cap || cap.supported !== true || cap.atomicIncrement !== true` returned early');
|
|
57
59
|
const key = `atomic-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
58
60
|
const probe = await call('atomicIncrement', { key, delta: 0 });
|
|
59
|
-
if (probe.status === 404) return; // seam not exposed
|
|
61
|
+
if (probe.status === 404) return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (seam not exposed) (seam, prior step, or fixture unavailable)'); // seam not exposed
|
|
60
62
|
|
|
61
63
|
const N = 50;
|
|
62
64
|
const results = await Promise.all(
|
|
63
65
|
Array.from({ length: N }, () => call('atomicIncrement', { key, delta: 1 })),
|
|
64
66
|
);
|
|
65
67
|
for (const r of results) {
|
|
66
|
-
expect(r.status, 'each increment MUST succeed').toBe(200);
|
|
68
|
+
expect(r.status, req('openwop.it.kv-atomic-increment.n-concurrent-1-increments-converge-to-exactly-n', 'RFC 0015 §B point 4', 'each increment MUST succeed')).toBe(200);
|
|
67
69
|
}
|
|
68
70
|
const finalRes = await call('get', { key });
|
|
69
71
|
const finalBody = finalRes.json as { value?: unknown };
|
|
70
72
|
expect(
|
|
71
73
|
finalBody.value,
|
|
72
|
-
|
|
74
|
+
req('openwop.it.kv-atomic-increment.n-concurrent-1-increments-converge-to-exactly-n', 'RFC 0015 §B point 4', `${N} concurrent increments MUST converge to exactly ${N}`),
|
|
73
75
|
).toBe(N);
|
|
74
76
|
});
|
|
75
77
|
});
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
import { describe, it, expect } from 'vitest';
|
|
12
12
|
import { driver } from '../lib/driver.js';
|
|
13
13
|
import { discoveryFamilies } from '../lib/discovery-capabilities.js';
|
|
14
|
+
import { req } from '../lib/requirement-ids.js';
|
|
15
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
14
16
|
|
|
15
17
|
interface DiscoveryDoc {
|
|
16
18
|
capabilities?: Record<string, unknown>;
|
|
@@ -31,10 +33,10 @@ async function call(op: string, args: Record<string, unknown>) {
|
|
|
31
33
|
describe('kv-cas: advertisement shape (RFC 0015)', () => {
|
|
32
34
|
it('capabilities.kvStorage is either absent or a well-formed object', async () => {
|
|
33
35
|
const cap = await readCap();
|
|
34
|
-
if (cap === null) return;
|
|
36
|
+
if (cap === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap === null` returned early');
|
|
35
37
|
expect(
|
|
36
38
|
typeof cap.supported,
|
|
37
|
-
|
|
39
|
+
req('openwop.it.kv-cas.capabilities-kvstorage-is-either-absent-or-a-well-formed-object',
|
|
38
40
|
'capabilities.schema.json §kvStorage',
|
|
39
41
|
'capabilities.kvStorage.supported MUST be a boolean when present',
|
|
40
42
|
),
|
|
@@ -43,34 +45,34 @@ describe('kv-cas: advertisement shape (RFC 0015)', () => {
|
|
|
43
45
|
|
|
44
46
|
it('compareAndSwap is a boolean when set', async () => {
|
|
45
47
|
const cap = await readCap();
|
|
46
|
-
if (!cap || cap.supported !== true) return;
|
|
48
|
+
if (!cap || cap.supported !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!cap || cap.supported !== true` returned early');
|
|
47
49
|
const sub = cap.compareAndSwap;
|
|
48
|
-
if (sub === undefined) return;
|
|
49
|
-
expect(typeof sub,
|
|
50
|
+
if (sub === undefined) return softSkip('blocked', 'precondition not met — `sub === undefined` returned early (seam, prior step, or fixture unavailable)');
|
|
51
|
+
expect(typeof sub, req('openwop.it.kv-cas.compareandswap-is-a-boolean-when-set', 'RFC 0015 §A', 'compareAndSwap MUST be boolean when present')).toBe('boolean');
|
|
50
52
|
});
|
|
51
53
|
});
|
|
52
54
|
|
|
53
55
|
describe('kv-cas: behavioral (RFC 0015 §B point 5)', () => {
|
|
54
56
|
it('CAS with matching expect succeeds; stale expect fails with swapped:false', async () => {
|
|
55
57
|
const cap = await readCap();
|
|
56
|
-
if (!cap || cap.supported !== true || cap.compareAndSwap !== true) return;
|
|
58
|
+
if (!cap || cap.supported !== true || cap.compareAndSwap !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!cap || cap.supported !== true || cap.compareAndSwap !== true` returned early');
|
|
57
59
|
const key = `cas-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
58
60
|
|
|
59
61
|
const setRes = await call('set', { key, value: 'v1' });
|
|
60
|
-
if (setRes.status === 404) return;
|
|
62
|
+
if (setRes.status === 404) return softSkip('blocked', 'precondition not met — `setRes.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
61
63
|
expect(setRes.status).toBe(200);
|
|
62
64
|
|
|
63
65
|
// Matching expect → swaps.
|
|
64
66
|
const okRes = await call('cas', { key, expect: 'v1', set: 'v2' });
|
|
65
|
-
expect(okRes.status, 'matching CAS MUST 200').toBe(200);
|
|
67
|
+
expect(okRes.status, req('openwop.it.kv-cas.cas-with-matching-expect-succeeds-stale-expect-fails-with-swapped-false', 'RFC 0015 §B', 'matching CAS MUST 200')).toBe(200);
|
|
66
68
|
const okBody = okRes.json as { swapped?: boolean };
|
|
67
|
-
expect(okBody.swapped, 'matching expect MUST swap').toBe(true);
|
|
69
|
+
expect(okBody.swapped, req('openwop.it.kv-cas.cas-with-matching-expect-succeeds-stale-expect-fails-with-swapped-false', 'RFC 0015 §B', 'matching expect MUST swap')).toBe(true);
|
|
68
70
|
|
|
69
71
|
// Stale expect → no swap.
|
|
70
72
|
const staleRes = await call('cas', { key, expect: 'v1', set: 'v3' });
|
|
71
|
-
expect(staleRes.status, 'stale CAS MUST 200 (CAS is non-throwing)').toBe(200);
|
|
73
|
+
expect(staleRes.status, req('openwop.it.kv-cas.cas-with-matching-expect-succeeds-stale-expect-fails-with-swapped-false', 'RFC 0015 §B', 'stale CAS MUST 200 (CAS is non-throwing)')).toBe(200);
|
|
72
74
|
const staleBody = staleRes.json as { swapped?: boolean; actual?: unknown };
|
|
73
|
-
expect(staleBody.swapped, 'stale expect MUST NOT swap').toBe(false);
|
|
74
|
-
expect(staleBody.actual, 'stale CAS MUST surface current value').toBe('v2');
|
|
75
|
+
expect(staleBody.swapped, req('openwop.it.kv-cas.cas-with-matching-expect-succeeds-stale-expect-fails-with-swapped-false', 'RFC 0015 §B', 'stale expect MUST NOT swap')).toBe(false);
|
|
76
|
+
expect(staleBody.actual, req('openwop.it.kv-cas.cas-with-matching-expect-succeeds-stale-expect-fails-with-swapped-false', 'RFC 0015 §B', 'stale CAS MUST surface current value')).toBe('v2');
|
|
75
77
|
});
|
|
76
78
|
});
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
import { describe, it, expect } from 'vitest';
|
|
20
20
|
import { driver } from '../lib/driver.js';
|
|
21
21
|
import { discoveryFamilies } from '../lib/discovery-capabilities.js';
|
|
22
|
+
import { req } from '../lib/requirement-ids.js';
|
|
23
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
22
24
|
|
|
23
25
|
interface DiscoveryDoc {
|
|
24
26
|
capabilities?: Record<string, unknown>;
|
|
@@ -39,10 +41,10 @@ async function call(tenantId: string, op: string, args: Record<string, unknown>)
|
|
|
39
41
|
describe('kv-cross-tenant-isolation: advertisement shape (RFC 0015)', () => {
|
|
40
42
|
it('capabilities.kvStorage is either absent or a well-formed object', async () => {
|
|
41
43
|
const cap = await readCap();
|
|
42
|
-
if (cap === null) return;
|
|
44
|
+
if (cap === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap === null` returned early');
|
|
43
45
|
expect(
|
|
44
46
|
typeof cap.supported,
|
|
45
|
-
|
|
47
|
+
req('openwop.it.kv-cross-tenant-isolation.capabilities-kvstorage-is-either-absent-or-a-well-formed-object',
|
|
46
48
|
'capabilities.schema.json §kvStorage',
|
|
47
49
|
'capabilities.kvStorage.supported MUST be a boolean when present',
|
|
48
50
|
),
|
|
@@ -53,18 +55,18 @@ describe('kv-cross-tenant-isolation: advertisement shape (RFC 0015)', () => {
|
|
|
53
55
|
describe('kv-cross-tenant-isolation: behavioral (RFC 0015 §B)', () => {
|
|
54
56
|
it('set under tenant A → get under tenant B with same key returns found:false', async () => {
|
|
55
57
|
const cap = await readCap();
|
|
56
|
-
if (!cap || cap.supported !== true) return;
|
|
58
|
+
if (!cap || cap.supported !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!cap || cap.supported !== true` returned early');
|
|
57
59
|
const key = `xtenant-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
58
60
|
const setRes = await call('tenant-a', 'set', { key, value: 'from-A' });
|
|
59
|
-
if (setRes.status === 404) return; // host doesn't expose test seam
|
|
60
|
-
expect(setRes.status,
|
|
61
|
+
if (setRes.status === 404) return softSkip('blocked', 'precondition not met — `setRes.status === 404` returned early (host doesn\'t expose test seam) (seam, prior step, or fixture unavailable)'); // host doesn't expose test seam
|
|
62
|
+
expect(setRes.status, req('openwop.it.kv-cross-tenant-isolation.set-under-tenant-a-get-under-tenant-b-with-same-key-returns-found-false', 'RFC 0015 §B', 'set MUST succeed')).toBe(200);
|
|
61
63
|
|
|
62
64
|
const getRes = await call('tenant-b', 'get', { key });
|
|
63
65
|
expect(getRes.status).toBe(200);
|
|
64
66
|
const body = getRes.json as { value?: unknown; found?: boolean };
|
|
65
67
|
expect(
|
|
66
68
|
body.found,
|
|
67
|
-
|
|
69
|
+
req('openwop.it.kv-cross-tenant-isolation.set-under-tenant-a-get-under-tenant-b-with-same-key-returns-found-false',
|
|
68
70
|
'SECURITY/invariants.yaml kv-cross-tenant-isolation',
|
|
69
71
|
'tenant B MUST NOT see tenant A value at the same key',
|
|
70
72
|
),
|
|
@@ -73,14 +75,14 @@ describe('kv-cross-tenant-isolation: behavioral (RFC 0015 §B)', () => {
|
|
|
73
75
|
|
|
74
76
|
it('same-tenant set→get round-trips the value', async () => {
|
|
75
77
|
const cap = await readCap();
|
|
76
|
-
if (!cap || cap.supported !== true) return;
|
|
78
|
+
if (!cap || cap.supported !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!cap || cap.supported !== true` returned early');
|
|
77
79
|
const key = `roundtrip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
78
80
|
const setRes = await call('tenant-a', 'set', { key, value: 'rt-A' });
|
|
79
|
-
if (setRes.status === 404) return;
|
|
81
|
+
if (setRes.status === 404) return softSkip('blocked', 'precondition not met — `setRes.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
80
82
|
expect(setRes.status).toBe(200);
|
|
81
83
|
const getRes = await call('tenant-a', 'get', { key });
|
|
82
84
|
const body = getRes.json as { value?: unknown; found?: boolean };
|
|
83
|
-
expect(body.found, 'same-tenant get MUST succeed').toBe(true);
|
|
84
|
-
expect(body.value, 'same-tenant get MUST return the stored value').toBe('rt-A');
|
|
85
|
+
expect(body.found, req('openwop.it.kv-cross-tenant-isolation.same-tenant-set-get-round-trips-the-value', 'RFC 0015 §B', 'same-tenant get MUST succeed')).toBe(true);
|
|
86
|
+
expect(body.value, req('openwop.it.kv-cross-tenant-isolation.same-tenant-set-get-round-trips-the-value', 'RFC 0015 §B', 'same-tenant get MUST return the stored value')).toBe('rt-A');
|
|
85
87
|
});
|
|
86
88
|
});
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
import { describe, it, expect } from 'vitest';
|
|
17
17
|
import { driver } from '../lib/driver.js';
|
|
18
18
|
import { discoveryFamilies } from '../lib/discovery-capabilities.js';
|
|
19
|
+
import { req } from '../lib/requirement-ids.js';
|
|
20
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
19
21
|
|
|
20
22
|
interface DiscoveryDoc {
|
|
21
23
|
capabilities?: Record<string, unknown>;
|
|
@@ -32,10 +34,10 @@ async function readCap(): Promise<Record<string, unknown> | null> {
|
|
|
32
34
|
describe('kv-ttl-expiry: advertisement shape (RFC 0015)', () => {
|
|
33
35
|
it('capabilities.kvStorage is either absent or a well-formed object', async () => {
|
|
34
36
|
const cap = await readCap();
|
|
35
|
-
if (cap === null) return; // host doesn't advertise — skip
|
|
37
|
+
if (cap === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `cap === null` returned early (host doesn\'t advertise — skip)'); // host doesn't advertise — skip
|
|
36
38
|
expect(
|
|
37
39
|
typeof cap.supported,
|
|
38
|
-
|
|
40
|
+
req('openwop.it.kv-ttl-expiry.capabilities-kvstorage-is-either-absent-or-a-well-formed-object',
|
|
39
41
|
'capabilities.schema.json §kvStorage',
|
|
40
42
|
'capabilities.kvStorage.supported MUST be a boolean when present',
|
|
41
43
|
),
|
|
@@ -50,7 +52,7 @@ async function call(op: string, args: Record<string, unknown>) {
|
|
|
50
52
|
describe('kv-ttl-expiry: behavioral (RFC 0015 §B point 3 — 1s TTL drift)', () => {
|
|
51
53
|
it('set with ttlSeconds=2 → get before expiry returns value; get after expiry returns found:false', async () => {
|
|
52
54
|
const probe = await call('get', { key: '__ttl-probe__' });
|
|
53
|
-
if (probe.status === 404) return; // host doesn't expose the seam
|
|
55
|
+
if (probe.status === 404) return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (host doesn\'t expose the seam) (seam, prior step, or fixture unavailable)'); // host doesn't expose the seam
|
|
54
56
|
const key = `ttl-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
55
57
|
const setRes = await call('set', { key, value: 'expires-soon', ttlSeconds: 2 });
|
|
56
58
|
expect(setRes.status).toBe(200);
|
|
@@ -61,7 +63,7 @@ describe('kv-ttl-expiry: behavioral (RFC 0015 §B point 3 — 1s TTL drift)', ()
|
|
|
61
63
|
const withinBody = within.json as { value?: unknown; found?: boolean };
|
|
62
64
|
expect(
|
|
63
65
|
withinBody.value,
|
|
64
|
-
|
|
66
|
+
req('openwop.it.kv-ttl-expiry.set-with-ttlseconds-2-get-before-expiry-returns-value-get-after-expiry-returns-f', 'RFC 0015 §B point 3', 'get within TTL window MUST return the stored value'),
|
|
65
67
|
).toBe('expires-soon');
|
|
66
68
|
expect(withinBody.found).toBe(true);
|
|
67
69
|
|
|
@@ -73,7 +75,7 @@ describe('kv-ttl-expiry: behavioral (RFC 0015 §B point 3 — 1s TTL drift)', ()
|
|
|
73
75
|
const afterBody = after.json as { value?: unknown; found?: boolean };
|
|
74
76
|
expect(
|
|
75
77
|
afterBody.found,
|
|
76
|
-
|
|
78
|
+
req('openwop.it.kv-ttl-expiry.set-with-ttlseconds-2-get-before-expiry-returns-value-get-after-expiry-returns-f', 'RFC 0015 §B point 3', 'get after TTL expiry MUST surface as found:false (≤1s drift)'),
|
|
77
79
|
).toBe(false);
|
|
78
80
|
});
|
|
79
81
|
});
|