@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
|
@@ -35,6 +35,7 @@ import { mkdtempSync, rmSync } from 'node:fs';
|
|
|
35
35
|
import { tmpdir } from 'node:os';
|
|
36
36
|
import { join } from 'node:path';
|
|
37
37
|
import { spawnHost, type SpawnedHost } from '../lib/multiProcess.js';
|
|
38
|
+
import { req } from '../lib/requirement-ids.js';
|
|
38
39
|
|
|
39
40
|
// Default off: scenario must be opted in via env. The opt-in lists
|
|
40
41
|
// the host package dir relative to repo root that exposes the
|
|
@@ -193,7 +194,7 @@ describe.skipIf(!RUN_THIS_SCENARIO)(
|
|
|
193
194
|
15_000,
|
|
194
195
|
);
|
|
195
196
|
|
|
196
|
-
expect(terminal.status, 'orphaned run MUST resume to a terminal status under host B').toBe(
|
|
197
|
+
expect(terminal.status, req('openwop.it.staleClaim.process-b-picks-up-the-orphaned-run-after-process-a-dies-claim-expires', 'spec/v1/scale-profiles.md', 'orphaned run MUST resume to a terminal status under host B')).toBe(
|
|
197
198
|
'completed',
|
|
198
199
|
);
|
|
199
200
|
|
|
@@ -203,12 +204,12 @@ describe.skipIf(!RUN_THIS_SCENARIO)(
|
|
|
203
204
|
// SOMETHING that distinguishes resume from fresh start MUST
|
|
204
205
|
// exist in the event log).
|
|
205
206
|
const events = await fetchEvents(hostB.baseUrl, APIKEY_B, runId);
|
|
206
|
-
expect(Array.isArray(events.events), 'events poll MUST return an events array').toBe(true);
|
|
207
|
+
expect(Array.isArray(events.events), req('openwop.it.staleClaim.process-b-picks-up-the-orphaned-run-after-process-a-dies-claim-expires', 'spec/v1/scale-profiles.md', 'events poll MUST return an events array')).toBe(true);
|
|
207
208
|
if (events.events && events.events.length > 0) {
|
|
208
209
|
const types = events.events.map((e) => e.type);
|
|
209
210
|
expect(
|
|
210
211
|
types.includes('run.resumed') || types.includes('run.started'),
|
|
211
|
-
'event log MUST contain at least run.started; resume hosts SHOULD also emit run.resumed',
|
|
212
|
+
req('openwop.it.staleClaim.process-b-picks-up-the-orphaned-run-after-process-a-dies-claim-expires', 'spec/v1/scale-profiles.md', 'event log MUST contain at least run.started; resume hosts SHOULD also emit run.resumed'),
|
|
212
213
|
).toBe(true);
|
|
213
214
|
}
|
|
214
215
|
},
|
|
@@ -221,6 +222,6 @@ describe.skipIf(!RUN_THIS_SCENARIO)(
|
|
|
221
222
|
// when the scenario is gated off.
|
|
222
223
|
describe('staleClaim lib: spawnHost surface contract', () => {
|
|
223
224
|
it('spawnHost is exported and has the expected shape', async () => {
|
|
224
|
-
expect(typeof spawnHost).toBe('function');
|
|
225
|
+
expect(typeof spawnHost, req('openwop.it.staleClaim.spawnhost-is-exported-and-has-the-expected-shape', 'spec/v1/scale-profiles.md', 'spawnHost is exported and has the expected shape')).toBe('function');
|
|
225
226
|
});
|
|
226
227
|
});
|
|
@@ -22,6 +22,7 @@ import { driver } from '../lib/driver.js';
|
|
|
22
22
|
import { subscribe } from '../lib/sse.js';
|
|
23
23
|
import { 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-delay';
|
|
27
28
|
const SKIP_NO_FIXTURE = !isFixtureAdvertised(WORKFLOW_ID);
|
|
@@ -42,13 +43,13 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes-buffer: ?bufferMs= aggregation hi
|
|
|
42
43
|
{ timeoutMs: 30_000 },
|
|
43
44
|
);
|
|
44
45
|
|
|
45
|
-
expect(result.status,
|
|
46
|
+
expect(result.status, req('openwop.it.stream-modes-buffer.accepts-bufferms-in-range-and-emits-at-least-one-event-batch-frame',
|
|
46
47
|
'stream-modes.md §Aggregation hint',
|
|
47
48
|
'GET /v1/runs/{runId}/events with valid bufferMs MUST return 200 SSE',
|
|
48
49
|
)).toBe(200);
|
|
49
50
|
|
|
50
51
|
const batchEvents = result.events.filter((e) => e.event === 'batch');
|
|
51
|
-
expect(batchEvents.length,
|
|
52
|
+
expect(batchEvents.length, req('openwop.it.stream-modes-buffer.accepts-bufferms-in-range-and-emits-at-least-one-event-batch-frame',
|
|
52
53
|
'stream-modes.md §Aggregation hint',
|
|
53
54
|
'buffered mode MUST emit at least one `event: batch` SSE frame',
|
|
54
55
|
)).toBeGreaterThan(0);
|
|
@@ -56,7 +57,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes-buffer: ?bufferMs= aggregation hi
|
|
|
56
57
|
// Each batch's data is a JSON array of RunEventDoc.
|
|
57
58
|
for (const batch of batchEvents) {
|
|
58
59
|
const parsed = JSON.parse(batch.data);
|
|
59
|
-
expect(Array.isArray(parsed),
|
|
60
|
+
expect(Array.isArray(parsed), req('openwop.it.stream-modes-buffer.accepts-bufferms-in-range-and-emits-at-least-one-event-batch-frame',
|
|
60
61
|
'stream-modes.md §batch data shape',
|
|
61
62
|
'event: batch data MUST parse to a JSON array',
|
|
62
63
|
)).toBe(true);
|
|
@@ -77,7 +78,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes-buffer: ?bufferMs= aggregation hi
|
|
|
77
78
|
{ timeoutMs: 5_000 },
|
|
78
79
|
);
|
|
79
80
|
|
|
80
|
-
expect(result.status,
|
|
81
|
+
expect(result.status, req('openwop.it.stream-modes-buffer.rejects-out-of-range-bufferms-with-400-validation-error',
|
|
81
82
|
'stream-modes.md §Aggregation hint range',
|
|
82
83
|
'bufferMs > 5000 MUST return 400',
|
|
83
84
|
)).toBe(400);
|
|
@@ -114,7 +115,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes-buffer: ?bufferMs= aggregation hi
|
|
|
114
115
|
(e) => e.type === 'run.completed' || e.type === 'run.failed' || e.type === 'run.cancelled',
|
|
115
116
|
);
|
|
116
117
|
|
|
117
|
-
expect(hasTerminal,
|
|
118
|
+
expect(hasTerminal, req('openwop.it.stream-modes-buffer.forces-flush-on-terminal-run-completed-arrives-bundled-in-a-batch-before-the-tim',
|
|
118
119
|
'stream-modes.md §Aggregation hint — force-flush triggers',
|
|
119
120
|
'terminal events MUST be force-flushed; the stream MUST NOT close before delivering run.completed',
|
|
120
121
|
)).toBe(true);
|
|
@@ -124,7 +125,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes-buffer: ?bufferMs= aggregation hi
|
|
|
124
125
|
// bufferMs/2 as headroom for cold-start latency on the conformance
|
|
125
126
|
// server, but failing here proves the timer fired before terminal
|
|
126
127
|
// arrived (i.e., force-flush is broken).
|
|
127
|
-
expect(elapsedMs,
|
|
128
|
+
expect(elapsedMs, req('openwop.it.stream-modes-buffer.forces-flush-on-terminal-run-completed-arrives-bundled-in-a-batch-before-the-tim',
|
|
128
129
|
'stream-modes.md §Aggregation hint — force-flush is immediate',
|
|
129
130
|
`terminal SHOULD arrive in well under bufferMs (${BUFFER_MS}ms); observed ${elapsedMs}ms — if elapsed is close to bufferMs, force-flush is not firing`,
|
|
130
131
|
)).toBeLessThan(BUFFER_MS / 2);
|
|
@@ -140,7 +141,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes-buffer: ?bufferMs= aggregation hi
|
|
|
140
141
|
);
|
|
141
142
|
|
|
142
143
|
const batchEvents = result.events.filter((e) => e.event === 'batch');
|
|
143
|
-
expect(batchEvents.length,
|
|
144
|
+
expect(batchEvents.length, req('openwop.it.stream-modes-buffer.bufferms-0-behaves-identically-to-omitting-per-event-mode',
|
|
144
145
|
'stream-modes.md §Aggregation hint — bufferMs=0 sentinel',
|
|
145
146
|
'bufferMs=0 MUST behave identically to omitting (no batch frames)',
|
|
146
147
|
)).toBe(0);
|
|
@@ -21,6 +21,7 @@ import { driver } from '../lib/driver.js';
|
|
|
21
21
|
import { subscribe, type SseEvent } from '../lib/sse.js';
|
|
22
22
|
import { 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 WORKFLOW_ID = 'conformance-delay';
|
|
26
27
|
const SKIP_NO_FIXTURE = !isFixtureAdvertised(WORKFLOW_ID);
|
|
@@ -43,18 +44,18 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes-mixed: comma-separated subsets',
|
|
|
43
44
|
{ timeoutMs: 15_000 },
|
|
44
45
|
);
|
|
45
46
|
|
|
46
|
-
expect(result.status,
|
|
47
|
+
expect(result.status, req('openwop.it.stream-modes-mixed.accepts-streammode-updates-messages-and-emits-a-server-closed-stream',
|
|
47
48
|
'stream-modes.md §Mixed mode',
|
|
48
49
|
'streamMode=updates,messages MUST return 200',
|
|
49
50
|
)).toBe(200);
|
|
50
51
|
|
|
51
|
-
expect(result.closedBy,
|
|
52
|
+
expect(result.closedBy, req('openwop.it.stream-modes-mixed.accepts-streammode-updates-messages-and-emits-a-server-closed-stream',
|
|
52
53
|
'stream-modes.md §Mixed mode + §updates',
|
|
53
54
|
'server MUST close the stream on terminal run event',
|
|
54
55
|
)).toBe('server');
|
|
55
56
|
|
|
56
57
|
const types = eventTypes(result.events);
|
|
57
|
-
expect(types,
|
|
58
|
+
expect(types, req('openwop.it.stream-modes-mixed.accepts-streammode-updates-messages-and-emits-a-server-closed-stream',
|
|
58
59
|
'stream-modes.md §Mixed mode (union semantics)',
|
|
59
60
|
'mixed updates,messages MUST include run.completed (admitted by updates)',
|
|
60
61
|
)).toContain('run.completed');
|
|
@@ -70,7 +71,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes-mixed: comma-separated subsets',
|
|
|
70
71
|
const res = await driver.get(
|
|
71
72
|
`/v1/runs/${encodeURIComponent(runId)}/events?streamMode=values,updates`,
|
|
72
73
|
);
|
|
73
|
-
expect(res.status,
|
|
74
|
+
expect(res.status, req('openwop.it.stream-modes-mixed.rejects-streammode-values-updates-with-400-unsupported-stream-mode',
|
|
74
75
|
'stream-modes.md §Mixed mode',
|
|
75
76
|
'values combined with another mode MUST return 400',
|
|
76
77
|
)).toBe(400);
|
|
@@ -78,15 +79,15 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes-mixed: comma-separated subsets',
|
|
|
78
79
|
const body = res.json as
|
|
79
80
|
| { error?: string; message?: string; details?: { supported?: string[] } }
|
|
80
81
|
| undefined;
|
|
81
|
-
expect(body?.error,
|
|
82
|
+
expect(body?.error, req('openwop.it.stream-modes-mixed.rejects-streammode-values-updates-with-400-unsupported-stream-mode',
|
|
82
83
|
'stream-modes.md §Mode selection error envelope + error-envelope.schema.json',
|
|
83
84
|
'unsupported_stream_mode error envelope MUST carry an `error` string discriminator',
|
|
84
85
|
)).toBe('unsupported_stream_mode');
|
|
85
|
-
expect(typeof body?.message,
|
|
86
|
+
expect(typeof body?.message, req('openwop.it.stream-modes-mixed.rejects-streammode-values-updates-with-400-unsupported-stream-mode',
|
|
86
87
|
'error-envelope.schema.json',
|
|
87
88
|
'error envelope MUST carry a human-readable `message` string',
|
|
88
89
|
)).toBe('string');
|
|
89
|
-
expect(Array.isArray(body?.details?.supported),
|
|
90
|
+
expect(Array.isArray(body?.details?.supported), req('openwop.it.stream-modes-mixed.rejects-streammode-values-updates-with-400-unsupported-stream-mode',
|
|
90
91
|
'stream-modes.md §Mode selection error envelope',
|
|
91
92
|
'error body MUST carry `details.supported` array (NOT top-level — `details` is the canonical contextual-data slot per error-envelope.schema.json)',
|
|
92
93
|
)).toBe(true);
|
|
@@ -104,7 +105,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes-mixed: comma-separated subsets',
|
|
|
104
105
|
const res = await driver.get(
|
|
105
106
|
`/v1/runs/${encodeURIComponent(runId)}/events?streamMode=updates,bogus`,
|
|
106
107
|
);
|
|
107
|
-
expect(res.status,
|
|
108
|
+
expect(res.status, req('openwop.it.stream-modes-mixed.rejects-streammode-updates-bogus-one-bad-mode-fails-the-whole-list',
|
|
108
109
|
'stream-modes.md §Mixed mode + §Mode selection',
|
|
109
110
|
'partial-unknown lists MUST return 400',
|
|
110
111
|
)).toBe(400);
|
|
@@ -140,7 +141,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes-mixed: comma-separated subsets',
|
|
|
140
141
|
const mixedTypes = new Set(eventTypes(mixed.events));
|
|
141
142
|
|
|
142
143
|
for (const t of updatesTypes) {
|
|
143
|
-
expect(mixedTypes.has(t),
|
|
144
|
+
expect(mixedTypes.has(t), req('openwop.it.stream-modes-mixed.mixed-mode-union-updates-debug-sees-every-event-updates-sees',
|
|
144
145
|
'stream-modes.md §Mixed mode (union)',
|
|
145
146
|
`updates,debug MUST include every event type updates produces (missing: ${t})`,
|
|
146
147
|
)).toBe(true);
|
|
@@ -11,6 +11,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
11
11
|
import { driver } from '../lib/driver.js';
|
|
12
12
|
import { subscribe, type SseEvent } from '../lib/sse.js';
|
|
13
13
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
14
|
+
import { req } from '../lib/requirement-ids.js';
|
|
14
15
|
|
|
15
16
|
const WORKFLOW_ID = 'conformance-delay';
|
|
16
17
|
const SKIP_NO_FIXTURE = !isFixtureAdvertised(WORKFLOW_ID);
|
|
@@ -38,17 +39,17 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes: updates (default) closes on term
|
|
|
38
39
|
{ timeoutMs: 15_000 },
|
|
39
40
|
);
|
|
40
41
|
|
|
41
|
-
expect(closedBy,
|
|
42
|
+
expect(closedBy, req('openwop.it.stream-modes.emits-at-least-run-started-run-completed-and-server-closes-the-stream',
|
|
42
43
|
'stream-modes.md §updates',
|
|
43
44
|
'server MUST close the connection on terminal run event',
|
|
44
45
|
)).toBe('server');
|
|
45
46
|
|
|
46
47
|
const types = eventTypes(events);
|
|
47
|
-
expect(types,
|
|
48
|
+
expect(types, req('openwop.it.stream-modes.emits-at-least-run-started-run-completed-and-server-closes-the-stream',
|
|
48
49
|
'stream-modes.md §updates',
|
|
49
50
|
'updates stream MUST include run.started',
|
|
50
51
|
)).toContain('run.started');
|
|
51
|
-
expect(types,
|
|
52
|
+
expect(types, req('openwop.it.stream-modes.emits-at-least-run-started-run-completed-and-server-closes-the-stream',
|
|
52
53
|
'stream-modes.md §updates',
|
|
53
54
|
'updates stream MUST include run.completed for a successful run',
|
|
54
55
|
)).toContain('run.completed');
|
|
@@ -62,7 +63,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes: invalid streamMode is rejected',
|
|
|
62
63
|
`/v1/runs/${encodeURIComponent(runId)}/events?streamMode=does-not-exist`,
|
|
63
64
|
);
|
|
64
65
|
|
|
65
|
-
expect(res.status,
|
|
66
|
+
expect(res.status, req('openwop.it.stream-modes.returns-400-and-a-structured-error-body',
|
|
66
67
|
'stream-modes.md §Mode selection',
|
|
67
68
|
'unsupported streamMode MUST return 400',
|
|
68
69
|
)).toBe(400);
|
|
@@ -70,15 +71,15 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes: invalid streamMode is rejected',
|
|
|
70
71
|
const body = res.json as
|
|
71
72
|
| { error?: unknown; message?: unknown; details?: { supported?: unknown } }
|
|
72
73
|
| undefined;
|
|
73
|
-
expect(typeof body?.error,
|
|
74
|
+
expect(typeof body?.error, req('openwop.it.stream-modes.returns-400-and-a-structured-error-body',
|
|
74
75
|
'stream-modes.md §Mode selection + error-envelope.schema.json',
|
|
75
76
|
'unsupported_stream_mode error body MUST include `error` string discriminator',
|
|
76
77
|
)).toBe('string');
|
|
77
|
-
expect(typeof body?.message,
|
|
78
|
+
expect(typeof body?.message, req('openwop.it.stream-modes.returns-400-and-a-structured-error-body',
|
|
78
79
|
'error-envelope.schema.json',
|
|
79
80
|
'error envelope MUST include a human-readable `message` string',
|
|
80
81
|
)).toBe('string');
|
|
81
|
-
expect(Array.isArray(body?.details?.supported),
|
|
82
|
+
expect(Array.isArray(body?.details?.supported), req('openwop.it.stream-modes.returns-400-and-a-structured-error-body',
|
|
82
83
|
'stream-modes.md §Mode selection',
|
|
83
84
|
'error body MUST include `details.supported` array of mode names (under `details` per error-envelope.schema.json)',
|
|
84
85
|
)).toBe(true);
|
|
@@ -97,12 +98,12 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes: values mode is reachable + close
|
|
|
97
98
|
// spec gap S1, so we don't assert payload shape here. What's
|
|
98
99
|
// canonical: the connection MUST be reachable, MUST emit at least
|
|
99
100
|
// one event before terminal, AND the server MUST close on terminal.
|
|
100
|
-
expect(result.closedBy,
|
|
101
|
+
expect(result.closedBy, req('openwop.it.stream-modes.returns-200-emits-at-least-one-event-server-closes-per-stream-modes-md-values',
|
|
101
102
|
'stream-modes.md §values',
|
|
102
103
|
'server MUST close the connection on terminal run event',
|
|
103
104
|
)).toBe('server');
|
|
104
105
|
|
|
105
|
-
expect(result.events.length,
|
|
106
|
+
expect(result.events.length, req('openwop.it.stream-modes.returns-200-emits-at-least-one-event-server-closes-per-stream-modes-md-values',
|
|
106
107
|
'stream-modes.md §values',
|
|
107
108
|
'values mode MUST emit at least one event before terminal',
|
|
108
109
|
)).toBeGreaterThan(0);
|
|
@@ -126,12 +127,12 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-modes: debug emits at least as many eve
|
|
|
126
127
|
// Both runs are conformance-delay with the same input, so updates
|
|
127
128
|
// events (run.started, node.started not in updates per spec, node.completed,
|
|
128
129
|
// run.completed) should be a subset of debug events.
|
|
129
|
-
expect(debugResult.events.length,
|
|
130
|
+
expect(debugResult.events.length, req('openwop.it.stream-modes.debug-stream-is-a-superset-of-updates-per-stream-modes-md-mode-mapping',
|
|
130
131
|
'stream-modes.md mode-to-event mapping',
|
|
131
132
|
'debug stream event count MUST be >= updates stream event count',
|
|
132
133
|
)).toBeGreaterThanOrEqual(updatesResult.events.length);
|
|
133
134
|
|
|
134
|
-
expect(debugResult.closedBy,
|
|
135
|
+
expect(debugResult.closedBy, req('openwop.it.stream-modes.debug-stream-is-a-superset-of-updates-per-stream-modes-md-mode-mapping',
|
|
135
136
|
'stream-modes.md §debug',
|
|
136
137
|
'debug stream MUST close on terminal event',
|
|
137
138
|
)).toBe('server');
|
|
@@ -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('stream-subscribe-from-beginning: advertisement shape (RFC 0017)', () => {
|
|
33
35
|
it('capabilities.queueBus 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.stream-subscribe-from-beginning.capabilities-queuebus-is-either-absent-or-a-well-formed-object',
|
|
39
41
|
'capabilities.schema.json §queueBus',
|
|
40
42
|
'capabilities.queueBus.supported MUST be a boolean when present',
|
|
41
43
|
),
|
|
@@ -44,17 +46,17 @@ describe('stream-subscribe-from-beginning: advertisement shape (RFC 0017)', () =
|
|
|
44
46
|
|
|
45
47
|
it('stream.supported is a boolean when set', async () => {
|
|
46
48
|
const cap = await readCap();
|
|
47
|
-
if (!cap || cap.supported !== true) return;
|
|
49
|
+
if (!cap || cap.supported !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!cap || cap.supported !== true` returned early');
|
|
48
50
|
const subParts = ["stream","supported"];
|
|
49
51
|
let sub: unknown = cap;
|
|
50
52
|
for (const p of subParts) {
|
|
51
53
|
if (sub && typeof sub === 'object') sub = (sub as Record<string, unknown>)[p];
|
|
52
54
|
else { sub = undefined; break; }
|
|
53
55
|
}
|
|
54
|
-
if (sub === undefined) return; // optional sub-field
|
|
56
|
+
if (sub === undefined) return softSkip('blocked', 'precondition not met — `sub === undefined` returned early (optional sub-field) (seam, prior step, or fixture unavailable)'); // optional sub-field
|
|
55
57
|
expect(
|
|
56
58
|
typeof sub,
|
|
57
|
-
|
|
59
|
+
req('openwop.it.stream-subscribe-from-beginning.stream-supported-is-a-boolean-when-set',
|
|
58
60
|
'RFC 0017 §A',
|
|
59
61
|
'queueBus.stream.supported MUST be boolean when present',
|
|
60
62
|
),
|
|
@@ -69,7 +71,7 @@ async function call(op: string, args: Record<string, unknown>) {
|
|
|
69
71
|
describe('stream-subscribe-from-beginning: behavioral (RFC 0017 §A stream.fromBeginning)', () => {
|
|
70
72
|
it('streamPublish 5 records then streamSubscribe({fromBeginning:true}) MUST surface all 5 in the snapshot', async () => {
|
|
71
73
|
const probe = await call('streamSubscribe', { stream: '__probe__', fromBeginning: true });
|
|
72
|
-
if (probe.status === 404) return; // seam not exposed
|
|
74
|
+
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
|
|
73
75
|
const stream = `s-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
74
76
|
for (let i = 1; i <= 5; i++) {
|
|
75
77
|
const r = await call('streamPublish', { stream, record: { seq: i, value: `rec-${i}` } });
|
|
@@ -80,7 +82,7 @@ describe('stream-subscribe-from-beginning: behavioral (RFC 0017 §A stream.fromB
|
|
|
80
82
|
const body = sub.json as { records?: Array<{ payload?: { seq?: number } }>; fromBeginningSnapshot?: boolean };
|
|
81
83
|
expect(
|
|
82
84
|
Array.isArray(body.records) && body.records.length === 5,
|
|
83
|
-
|
|
85
|
+
req('openwop.it.stream-subscribe-from-beginning.streampublish-5-records-then-streamsubscribe-frombeginning-true-must-surface-all', 'RFC 0017 §A.stream.fromBeginning', 'subscribe with fromBeginning:true MUST return ALL records previously published on the stream'),
|
|
84
86
|
).toBe(true);
|
|
85
87
|
// Order MUST be preserved (publish-order = sequential on the same stream).
|
|
86
88
|
const seqs = body.records!.map((r) => r.payload?.seq);
|
|
@@ -90,14 +92,14 @@ describe('stream-subscribe-from-beginning: behavioral (RFC 0017 §A stream.fromB
|
|
|
90
92
|
|
|
91
93
|
it('streamSubscribe({fromBeginning:false}) MUST NOT include pre-subscribe records (live-tail semantics)', async () => {
|
|
92
94
|
const probe = await call('streamSubscribe', { stream: '__probe__', fromBeginning: true });
|
|
93
|
-
if (probe.status === 404) return;
|
|
95
|
+
if (probe.status === 404) return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
94
96
|
const stream = `s-live-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
95
97
|
await call('streamPublish', { stream, record: { v: 'before' } });
|
|
96
98
|
const sub = await call('streamSubscribe', { stream, fromBeginning: false });
|
|
97
99
|
const body = sub.json as { records?: unknown[]; fromBeginningSnapshot?: boolean };
|
|
98
100
|
expect(
|
|
99
101
|
Array.isArray(body.records) && body.records.length === 0,
|
|
100
|
-
|
|
102
|
+
req('openwop.it.stream-subscribe-from-beginning.streamsubscribe-frombeginning-false-must-not-include-pre-subscribe-records-live', 'RFC 0017 §A.stream.fromBeginning', 'subscribe with fromBeginning:false MUST omit pre-subscribe records'),
|
|
101
103
|
).toBe(true);
|
|
102
104
|
expect(body.fromBeginningSnapshot).toBe(false);
|
|
103
105
|
});
|
|
@@ -38,6 +38,7 @@ import { subscribe } from '../lib/sse.js';
|
|
|
38
38
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
39
39
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
40
40
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
41
|
+
import { req } from '../lib/requirement-ids.js';
|
|
41
42
|
|
|
42
43
|
const FIXTURE_ID = 'conformance-stream-text';
|
|
43
44
|
const SKIP_NO_FIXTURE = !isFixtureAdvertised(FIXTURE_ID);
|
|
@@ -105,7 +106,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-text-fixture: messages-mode fold throug
|
|
|
105
106
|
}
|
|
106
107
|
expect(
|
|
107
108
|
create.status,
|
|
108
|
-
|
|
109
|
+
req('openwop.it.stream-text-fixture.delivers-the-mocked-tokens-in-order-terminates-with-islast-meta-and-server-close',
|
|
109
110
|
'fixtures.md §conformance-stream-text',
|
|
110
111
|
'a test key MUST be able to start the fixture with configurable.mockProvider',
|
|
111
112
|
),
|
|
@@ -120,7 +121,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-text-fixture: messages-mode fold throug
|
|
|
120
121
|
// 3) server-closed, not timeout.
|
|
121
122
|
expect(
|
|
122
123
|
result.closedBy,
|
|
123
|
-
|
|
124
|
+
req('openwop.it.stream-text-fixture.delivers-the-mocked-tokens-in-order-terminates-with-islast-meta-and-server-close', 'stream-modes.md §messages', 'server MUST close the messages stream on terminal run status'),
|
|
124
125
|
).toBe('server');
|
|
125
126
|
|
|
126
127
|
// Collect ai.message.chunk payloads. Per stream-modes.md the SSE event
|
|
@@ -141,7 +142,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-text-fixture: messages-mode fold throug
|
|
|
141
142
|
|
|
142
143
|
expect(
|
|
143
144
|
chunks.length,
|
|
144
|
-
|
|
145
|
+
req('openwop.it.stream-text-fixture.delivers-the-mocked-tokens-in-order-terminates-with-islast-meta-and-server-close',
|
|
145
146
|
'stream-modes.md §messages',
|
|
146
147
|
'messages mode MUST emit ai.message.chunk SSE events for the streaming AI node',
|
|
147
148
|
),
|
|
@@ -150,7 +151,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-text-fixture: messages-mode fold throug
|
|
|
150
151
|
// 1) Token order matches the mock config exactly.
|
|
151
152
|
expect(
|
|
152
153
|
chunks.map((c) => c.chunk),
|
|
153
|
-
|
|
154
|
+
req('openwop.it.stream-text-fixture.delivers-the-mocked-tokens-in-order-terminates-with-islast-meta-and-server-close',
|
|
154
155
|
'fixtures.md §conformance-stream-text',
|
|
155
156
|
'chunk arrival order MUST match the mock token order',
|
|
156
157
|
),
|
|
@@ -160,32 +161,32 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-text-fixture: messages-mode fold throug
|
|
|
160
161
|
const last = chunks[chunks.length - 1]!;
|
|
161
162
|
expect(
|
|
162
163
|
last.isLast,
|
|
163
|
-
|
|
164
|
+
req('openwop.it.stream-text-fixture.delivers-the-mocked-tokens-in-order-terminates-with-islast-meta-and-server-close',
|
|
164
165
|
'stream-modes.md §messages + RFC 0094 §D',
|
|
165
166
|
'the final ai.message.chunk MUST carry isLast: true',
|
|
166
167
|
),
|
|
167
168
|
).toBe(true);
|
|
168
169
|
expect(
|
|
169
170
|
last.meta?.finishReason,
|
|
170
|
-
|
|
171
|
+
req('openwop.it.stream-text-fixture.delivers-the-mocked-tokens-in-order-terminates-with-islast-meta-and-server-close', 'stream-modes.md §messages (Tier 1)', 'final chunk meta.finishReason MUST be "stop"'),
|
|
171
172
|
).toBe('stop');
|
|
172
173
|
expect(
|
|
173
174
|
last.meta?.usage?.completionTokens,
|
|
174
|
-
|
|
175
|
+
req('openwop.it.stream-text-fixture.delivers-the-mocked-tokens-in-order-terminates-with-islast-meta-and-server-close', 'stream-modes.md §messages (Tier 1)', 'final chunk meta.usage.completionTokens MUST echo the mock usage'),
|
|
175
176
|
).toBe(4);
|
|
176
177
|
|
|
177
178
|
// Minimum compliant payload fields are present on every chunk.
|
|
178
179
|
for (const c of chunks) {
|
|
179
|
-
expect(typeof c.nodeId,
|
|
180
|
-
expect(typeof c.runId,
|
|
181
|
-
expect(typeof c.isLast,
|
|
180
|
+
expect(typeof c.nodeId, req('openwop.it.stream-text-fixture.delivers-the-mocked-tokens-in-order-terminates-with-islast-meta-and-server-close', 'run-event-payloads.schema.json §outputChunk', 'nodeId MUST be a string')).toBe('string');
|
|
181
|
+
expect(typeof c.runId, req('openwop.it.stream-text-fixture.delivers-the-mocked-tokens-in-order-terminates-with-islast-meta-and-server-close', 'run-event-payloads.schema.json §outputChunk', 'runId MUST be a string')).toBe('string');
|
|
182
|
+
expect(typeof c.isLast, req('openwop.it.stream-text-fixture.delivers-the-mocked-tokens-in-order-terminates-with-islast-meta-and-server-close', 'run-event-payloads.schema.json §outputChunk', 'isLast MUST be a boolean')).toBe('boolean');
|
|
182
183
|
}
|
|
183
184
|
|
|
184
185
|
// 4) Terminal completed.
|
|
185
186
|
const terminal = await pollUntilTerminal(runId);
|
|
186
187
|
expect(
|
|
187
188
|
terminal.status,
|
|
188
|
-
|
|
189
|
+
req('openwop.it.stream-text-fixture.delivers-the-mocked-tokens-in-order-terminates-with-islast-meta-and-server-close', 'fixtures.md §conformance-stream-text', 'the fixture run MUST reach terminal completed'),
|
|
189
190
|
).toBe('completed');
|
|
190
191
|
});
|
|
191
192
|
|
|
@@ -203,11 +204,11 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-text-fixture: messages-mode fold throug
|
|
|
203
204
|
}
|
|
204
205
|
expect(
|
|
205
206
|
create.status,
|
|
206
|
-
|
|
207
|
+
req('openwop.it.stream-text-fixture.an-unknown-mockprovider-id-is-rejected-with-400-unsupported-mock-provider', 'fixtures.md §conformance-stream-text', 'an unknown mockProvider.id MUST be rejected with 400'),
|
|
207
208
|
).toBe(400);
|
|
208
209
|
expect(
|
|
209
210
|
errCode(create.json),
|
|
210
|
-
|
|
211
|
+
req('openwop.it.stream-text-fixture.an-unknown-mockprovider-id-is-rejected-with-400-unsupported-mock-provider', 'fixtures.md §conformance-stream-text', 'the refusal MUST carry the canonical unsupported_mock_provider code'),
|
|
211
212
|
).toBe('unsupported_mock_provider');
|
|
212
213
|
});
|
|
213
214
|
});
|
|
@@ -21,6 +21,8 @@ import { driver } from '../lib/driver.js';
|
|
|
21
21
|
import { subscribe, type SseEvent } from '../lib/sse.js';
|
|
22
22
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
23
23
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
24
|
+
import { req } from '../lib/requirement-ids.js';
|
|
25
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
24
26
|
|
|
25
27
|
const WORKFLOW_ID = 'conformance-cancellable';
|
|
26
28
|
const SKIP_NO_FIXTURE = !isFixtureAdvertised(WORKFLOW_ID);
|
|
@@ -57,7 +59,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-reconnect: Last-Event-ID resume per spe
|
|
|
57
59
|
workflowId: WORKFLOW_ID,
|
|
58
60
|
inputs: { delayMs: 2000 },
|
|
59
61
|
});
|
|
60
|
-
if (create.status !== 201) return; // host doesn't seed cancellable fixture; skip-equivalent
|
|
62
|
+
if (create.status !== 201) return softSkip('blocked', 'precondition not met — `create.status !== 201` returned early (host doesn\'t seed cancellable fixture; skip-equivalent) (seam, prior step, or fixture unavailable)'); // host doesn't seed cancellable fixture; skip-equivalent
|
|
61
63
|
|
|
62
64
|
const runId = (create.json as { runId: string }).runId;
|
|
63
65
|
|
|
@@ -65,7 +67,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-reconnect: Last-Event-ID resume per spe
|
|
|
65
67
|
const firstHalf = await subscribe(`/v1/runs/${encodeURIComponent(runId)}/events`, {
|
|
66
68
|
timeoutMs: 1000, // disconnect after ~1s
|
|
67
69
|
});
|
|
68
|
-
expect(firstHalf.status,
|
|
70
|
+
expect(firstHalf.status, req('openwop.it.streamReconnect.reconnect-with-last-event-id-resumes-without-loss-or-duplication',
|
|
69
71
|
'spec/v1/stream-modes.md',
|
|
70
72
|
'SSE endpoint MUST return 200',
|
|
71
73
|
)).toBe(200);
|
|
@@ -82,7 +84,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-reconnect: Last-Event-ID resume per spe
|
|
|
82
84
|
// because the run already completed and the server closed before we
|
|
83
85
|
// got events. Skip the rest of this scenario; the host is fast enough
|
|
84
86
|
// that the reconnect path doesn't apply.
|
|
85
|
-
return;
|
|
87
|
+
return softSkip('blocked', 'precondition not met — `lastSeen < 0` returned early (First connection emitted no events with a parseable sequence — e.g. because the run already completed and the server closed before we got events. Skip the rest of …');
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
// Phase 3: reconnect with Last-Event-ID set to the last seq we saw.
|
|
@@ -91,7 +93,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-reconnect: Last-Event-ID resume per spe
|
|
|
91
93
|
timeoutMs: 5000,
|
|
92
94
|
lastEventId: String(lastSeen),
|
|
93
95
|
});
|
|
94
|
-
expect(resume.status,
|
|
96
|
+
expect(resume.status, req('openwop.it.streamReconnect.reconnect-with-last-event-id-resumes-without-loss-or-duplication',
|
|
95
97
|
'spec/v1/stream-modes.md §"Reconnection"',
|
|
96
98
|
'reconnection with Last-Event-ID MUST return 200',
|
|
97
99
|
)).toBe(200);
|
|
@@ -102,7 +104,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-reconnect: Last-Event-ID resume per spe
|
|
|
102
104
|
// Hosts MAY replay the boundary event (some impls do; spec is
|
|
103
105
|
// permissive). The strict assertion is "no event with seq <
|
|
104
106
|
// lastSeen-1" — i.e., no events from before the resume point.
|
|
105
|
-
expect(s,
|
|
107
|
+
expect(s, req('openwop.it.streamReconnect.reconnect-with-last-event-id-resumes-without-loss-or-duplication',
|
|
106
108
|
'spec/v1/stream-modes.md §"Reconnection"',
|
|
107
109
|
`resume MUST NOT yield events with seq < lastSeen-1; got ${s} after lastSeen=${lastSeen}`,
|
|
108
110
|
)).toBeGreaterThanOrEqual(lastSeen - 1);
|
|
@@ -120,21 +122,21 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-reconnect: Last-Event-ID resume per spe
|
|
|
120
122
|
// Quick run, observe terminal seq, then attempt a reconnect after
|
|
121
123
|
// terminal — server SHOULD close immediately with no events.
|
|
122
124
|
const create = await driver.post('/v1/runs', { workflowId: 'conformance-noop' });
|
|
123
|
-
if (create.status !== 201) return;
|
|
125
|
+
if (create.status !== 201) return softSkip('blocked', 'precondition not met — `create.status !== 201` returned early (seam, prior step, or fixture unavailable)');
|
|
124
126
|
const runId = (create.json as { runId: string }).runId;
|
|
125
127
|
|
|
126
128
|
const initial = await subscribe(`/v1/runs/${encodeURIComponent(runId)}/events`, {
|
|
127
129
|
timeoutMs: 5000,
|
|
128
130
|
});
|
|
129
|
-
if (initial.status !== 200 || initial.events.length === 0) return;
|
|
131
|
+
if (initial.status !== 200 || initial.events.length === 0) return softSkip('blocked', 'precondition not met — `initial.status !== 200 || initial.events.length === 0` returned early (seam, prior step, or fixture unavailable)');
|
|
130
132
|
|
|
131
133
|
const terminalEvent = initial.events.find(
|
|
132
134
|
(e) => TERMINAL_TYPES.has(e.event) && e.id !== null && Number.isFinite(Number(e.id)),
|
|
133
135
|
);
|
|
134
|
-
if (!terminalEvent || terminalEvent.id === null) return;
|
|
136
|
+
if (!terminalEvent || terminalEvent.id === null) return softSkip('blocked', 'precondition not met — `!terminalEvent || terminalEvent.id === null` returned early (seam, prior step, or fixture unavailable)');
|
|
135
137
|
|
|
136
138
|
const lastSeq = Number(terminalEvent.id);
|
|
137
|
-
if (!Number.isFinite(lastSeq)) return;
|
|
139
|
+
if (!Number.isFinite(lastSeq)) return softSkip('blocked', 'precondition not met — `!Number.isFinite(lastSeq)` returned early (seam, prior step, or fixture unavailable)');
|
|
138
140
|
|
|
139
141
|
const reconnect = await subscribe(`/v1/runs/${encodeURIComponent(runId)}/events`, {
|
|
140
142
|
timeoutMs: 5000,
|
|
@@ -144,7 +146,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-reconnect: Last-Event-ID resume per spe
|
|
|
144
146
|
// Reconnect MUST succeed (200) and SHOULD close quickly with no
|
|
145
147
|
// additional events beyond the terminal boundary. Permissive: the
|
|
146
148
|
// host MAY replay the terminal event itself.
|
|
147
|
-
expect(reconnect.status,
|
|
149
|
+
expect(reconnect.status, req('openwop.it.streamReconnect.reconnect-with-last-event-id-equal-to-terminal-seq-closes-immediately',
|
|
148
150
|
'spec/v1/stream-modes.md §"Reconnection"',
|
|
149
151
|
'reconnect after terminal MUST return 200',
|
|
150
152
|
)).toBe(200);
|
|
@@ -152,7 +154,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('stream-reconnect: Last-Event-ID resume per spe
|
|
|
152
154
|
const newSeqs = reconnect.events
|
|
153
155
|
.map(getSeq)
|
|
154
156
|
.filter((s): s is number => s !== null && s > lastSeq);
|
|
155
|
-
expect(newSeqs.length,
|
|
157
|
+
expect(newSeqs.length, req('openwop.it.streamReconnect.reconnect-with-last-event-id-equal-to-terminal-seq-closes-immediately',
|
|
156
158
|
'spec/v1/stream-modes.md §"Reconnection"',
|
|
157
159
|
'reconnect after terminal MUST NOT yield events with seq > lastSeq',
|
|
158
160
|
)).toBe(0);
|
|
@@ -30,6 +30,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
|
30
30
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
31
31
|
import { dispositionOf, resetLedger } from '../lib/requirement-ledger.js';
|
|
32
32
|
import { __resetEnvCacheForTests } from '../lib/env.js';
|
|
33
|
+
import { req } from '../lib/requirement-ids.js';
|
|
33
34
|
|
|
34
35
|
const ENV_KEYS = ['OPENWOP_REQUIRE_BEHAVIOR', 'OPENWOP_OPTED_OUT_PROFILES', 'OPENWOP_BASE_URL', 'OPENWOP_API_KEY'] as const;
|
|
35
36
|
let saved: Record<string, string | undefined> = {};
|
|
@@ -65,16 +66,16 @@ describe('RFC 0148 §B — advertise and opt-out are mutually exclusive', () =>
|
|
|
65
66
|
__resetEnvCacheForTests();
|
|
66
67
|
expect(
|
|
67
68
|
() => behaviorGate('openwop-audit-log-integrity', true),
|
|
68
|
-
'RFC 0148 §B: "A host MUST NOT both advertise and opt out of the same profile." ' +
|
|
69
|
+
req('openwop.it.strict-behavior-gate.advertising-and-opting-out-of-the-same-profile-fails', 'RFC 0148 §B', 'RFC 0148 §B: "A host MUST NOT both advertise and opt out of the same profile." ' +
|
|
69
70
|
'The two claims are opposite in kind — one says the host implements the profile, the ' +
|
|
70
71
|
'other says the operator declares it does not. A run where both hold has no defensible ' +
|
|
71
72
|
'reading, and resolving it in favour of advertisement extracts MORE certification claim ' +
|
|
72
|
-
'from a MORE contradictory input.',
|
|
73
|
+
'from a MORE contradictory input.'),
|
|
73
74
|
).toThrow(/MUST NOT both advertise and opt out/);
|
|
74
75
|
});
|
|
75
76
|
|
|
76
77
|
it('advertising alone proceeds', () => {
|
|
77
|
-
expect(behaviorGate('openwop-audit-log-integrity', true)).toBe(true);
|
|
78
|
+
expect(behaviorGate('openwop-audit-log-integrity', true), req('openwop.it.strict-behavior-gate.advertising-alone-proceeds', 'RFC 0148 §B', 'advertising alone proceeds')).toBe(true);
|
|
78
79
|
});
|
|
79
80
|
|
|
80
81
|
it('opting out alone skips, in strict mode too', () => {
|
|
@@ -82,13 +83,13 @@ describe('RFC 0148 §B — advertise and opt-out are mutually exclusive', () =>
|
|
|
82
83
|
__resetEnvCacheForTests();
|
|
83
84
|
process.env['OPENWOP_REQUIRE_BEHAVIOR'] = 'true';
|
|
84
85
|
__resetEnvCacheForTests();
|
|
85
|
-
expect(behaviorGate('openwop-audit-log-integrity', false)).toBe(false);
|
|
86
|
+
expect(behaviorGate('openwop-audit-log-integrity', false), req('openwop.it.strict-behavior-gate.opting-out-alone-skips-in-strict-mode-too', 'RFC 0148 §B', 'opting out alone skips, in strict mode too')).toBe(false);
|
|
86
87
|
});
|
|
87
88
|
|
|
88
89
|
it('strict mode fails an unadvertised, non-opted-out profile', () => {
|
|
89
90
|
process.env['OPENWOP_REQUIRE_BEHAVIOR'] = 'true';
|
|
90
91
|
__resetEnvCacheForTests();
|
|
91
|
-
expect(() => behaviorGate('openwop-audit-log-integrity', false)).toThrow();
|
|
92
|
+
expect(() => behaviorGate('openwop-audit-log-integrity', false), req('openwop.it.strict-behavior-gate.strict-mode-fails-an-unadvertised-non-opted-out-profile', 'RFC 0148 §B', 'strict mode fails an unadvertised, non-opted-out profile')).toThrow();
|
|
92
93
|
});
|
|
93
94
|
});
|
|
94
95
|
|
|
@@ -100,7 +101,7 @@ describe('RFC 0148 §B — gate decisions record a ledger disposition', () => {
|
|
|
100
101
|
process.env['OPENWOP_OPTED_OUT_PROFILES'] = 'openwop-audit-log-integrity';
|
|
101
102
|
__resetEnvCacheForTests();
|
|
102
103
|
behaviorGate('openwop-audit-log-integrity', false);
|
|
103
|
-
expect(dispositionOf('openwop.profile.openwop-audit-log-integrity')).toBe('skipped');
|
|
104
|
+
expect(dispositionOf('openwop.profile.openwop-audit-log-integrity'), req('openwop.it.strict-behavior-gate.an-honest-opt-out-records-skipped-not-silence', 'RFC 0148 §B', 'an honest opt-out records `skipped`, not silence')).toBe('skipped');
|
|
104
105
|
});
|
|
105
106
|
|
|
106
107
|
it('a default-mode soft-skip records `inapplicable`', () => {
|
|
@@ -109,12 +110,12 @@ describe('RFC 0148 §B — gate decisions record a ledger disposition', () => {
|
|
|
109
110
|
// statement from "we could not check", and it is certifiable where
|
|
110
111
|
// `blocked` is not.
|
|
111
112
|
behaviorGate('openwop-audit-log-integrity', false);
|
|
112
|
-
expect(dispositionOf('openwop.profile.openwop-audit-log-integrity')).toBe('inapplicable');
|
|
113
|
+
expect(dispositionOf('openwop.profile.openwop-audit-log-integrity'), req('openwop.it.strict-behavior-gate.a-default-mode-soft-skip-records-inapplicable', 'RFC 0148 §B', 'a default-mode soft-skip records `inapplicable`')).toBe('inapplicable');
|
|
113
114
|
});
|
|
114
115
|
|
|
115
116
|
it('an unexercised profile stays `blocked`', () => {
|
|
116
117
|
// Nothing called the gate at all. §A's default holds: silence is not a pass,
|
|
117
118
|
// and it is not certifiable either.
|
|
118
|
-
expect(dispositionOf('openwop.profile.openwop-never-touched')).toBe('blocked');
|
|
119
|
+
expect(dispositionOf('openwop.profile.openwop-never-touched'), req('openwop.it.strict-behavior-gate.an-unexercised-profile-stays-blocked', 'RFC 0148 §B', 'an unexercised profile stays `blocked`')).toBe('blocked');
|
|
119
120
|
});
|
|
120
121
|
});
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
import { describe, it, expect } from 'vitest';
|
|
16
16
|
import { softSkip } from '../lib/soft-skip.js';
|
|
17
|
-
import { driver } from '../lib/driver.js';
|
|
18
17
|
import { readSubRunAttestationCap, invokeSubRunAttest } from '../lib/subRunAttestation.js';
|
|
18
|
+
import { req } from '../lib/requirement-ids.js';
|
|
19
19
|
|
|
20
20
|
describe('subrun-approval-fail-closed (RFC 0063 §C)', () => {
|
|
21
21
|
it('no accept/edit-accept (terminated or expired) MUST NOT merge', async () => {
|
|
@@ -28,7 +28,7 @@ describe('subrun-approval-fail-closed (RFC 0063 §C)', () => {
|
|
|
28
28
|
if (res === null) return softSkip('blocked', 'seam absent — soft-skip');
|
|
29
29
|
expect(
|
|
30
30
|
res.merged,
|
|
31
|
-
|
|
31
|
+
req('openwop.it.subrun-approval-fail-closed.no-accept-edit-accept-terminated-or-expired-must-not-merge', 'RFC 0063 §C', 'an unresolved approval MUST fail closed — outputs MUST NOT be merged'),
|
|
32
32
|
).toBe(false);
|
|
33
33
|
});
|
|
34
34
|
});
|