@openwop/openwop-conformance 1.162.0 → 2.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/dist/cli.js +54 -1
- package/dist/lib/paths.js +25 -3
- package/dist/lib/requirement-ids.js +34 -4
- package/dist/lib/scenario-disposition.js +1 -0
- package/dist/lib/spec-coherence.js +13 -32
- package/dist/spec-artifacts.lock.json +5 -0
- package/package.json +11 -33
- package/requirements.json +20303 -11809
- package/scenario-majors.json +1343 -0
- package/schemas/CORPUS-STAMP.json +138 -6
- package/src/cli.ts +47 -1
- package/src/global-setup.ts +3 -2
- package/src/lib/corpus-stamp.ts +29 -1
- package/src/lib/paths.ts +25 -3
- package/src/lib/requirement-ids.ts +33 -4
- package/src/lib/scenario-disposition.ts +1 -0
- package/src/lib/spec-coherence.ts +15 -32
- package/src/scenarios/a2a-1-0-agent-card.test.ts +17 -16
- package/src/scenarios/a2a-1-0-task-roundtrip.test.ts +11 -10
- package/src/scenarios/a2a-card-runtime-consistency.test.ts +18 -17
- package/src/scenarios/a2a-peer-authority.test.ts +7 -6
- package/src/scenarios/a2a-task-roundtrip.test.ts +30 -29
- package/src/scenarios/a2a-version-negotiation.test.ts +16 -14
- package/src/scenarios/a2ui-surface-degrades.test.ts +5 -3
- package/src/scenarios/a2ui-surface-delta-transport.test.ts +35 -33
- package/src/scenarios/a2ui-surface-replay.test.ts +6 -4
- package/src/scenarios/a2ui-surface-shape.test.ts +11 -10
- package/src/scenarios/a2ui-surface-version-refusal.test.ts +6 -4
- package/src/scenarios/a2ui-untrusted-blocks-approval.test.ts +5 -3
- package/src/scenarios/agent-capability-degraded-projection.test.ts +12 -11
- package/src/scenarios/agent-channel-dispatch.test.ts +18 -16
- package/src/scenarios/agent-deployment-lifecycle.test.ts +20 -19
- package/src/scenarios/agent-deployment-shape.test.ts +22 -24
- package/src/scenarios/agent-eval-run.test.ts +19 -18
- package/src/scenarios/agent-eval-suite-shape.test.ts +20 -22
- package/src/scenarios/agent-live-allowlist-enforced.test.ts +6 -5
- package/src/scenarios/agent-live-invocation-bracket.test.ts +13 -12
- package/src/scenarios/agent-live-runtime-shape.test.ts +10 -12
- package/src/scenarios/agent-live-structured-output.test.ts +7 -6
- package/src/scenarios/agent-loop-iteration-monotonic.test.ts +3 -3
- package/src/scenarios/agent-loop-stateful-resume.test.ts +2 -2
- package/src/scenarios/agent-loop-version5-shape.test.ts +4 -4
- package/src/scenarios/agent-loop-workspace-snapshot.test.ts +3 -3
- package/src/scenarios/agent-manifest-role-profile.test.ts +9 -9
- package/src/scenarios/agent-manifest-runtime.test.ts +13 -12
- package/src/scenarios/agent-org-chart-scoping.test.ts +14 -13
- package/src/scenarios/agent-org-chart-shape.test.ts +11 -13
- package/src/scenarios/agent-platform-aggregate-evidence.test.ts +5 -4
- package/src/scenarios/agent-platform-profile.test.ts +18 -19
- package/src/scenarios/agent-requires-capabilities-shape.test.ts +4 -4
- package/src/scenarios/agent-roster-attribution.test.ts +18 -17
- package/src/scenarios/agent-roster-shape.test.ts +17 -19
- package/src/scenarios/agent-verifier-shape.test.ts +11 -11
- package/src/scenarios/agentConfidenceEscalation.test.ts +2 -1
- package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +6 -5
- package/src/scenarios/agentMemoryRedactionContract.test.ts +2 -1
- package/src/scenarios/agentMemoryRoundTrip.test.ts +2 -1
- package/src/scenarios/agentMemoryTtlExpiry.test.ts +4 -3
- package/src/scenarios/agentMessageReducer.test.ts +2 -1
- package/src/scenarios/agentMetadata.test.ts +2 -1
- package/src/scenarios/agentPackCatalog.test.ts +23 -21
- package/src/scenarios/agentPackExport.test.ts +4 -2
- package/src/scenarios/agentPackHandoffSchemaValidation.test.ts +5 -4
- package/src/scenarios/agentPackInstall.test.ts +4 -2
- package/src/scenarios/agentPackProvenance.test.ts +2 -1
- package/src/scenarios/agentReasoningEvents.test.ts +3 -2
- package/src/scenarios/agentReasoningStreaming.test.ts +11 -10
- package/src/scenarios/agents-run-tool-allowlist.test.ts +14 -12
- package/src/scenarios/ai-envelope-shape.test.ts +31 -29
- package/src/scenarios/aiEnvelope.capBreached.test.ts +26 -24
- package/src/scenarios/aiEnvelope.contractRefusal.test.ts +36 -34
- package/src/scenarios/aiEnvelope.correlationReplay.test.ts +17 -15
- package/src/scenarios/aiEnvelope.redaction.test.ts +22 -20
- package/src/scenarios/aiEnvelope.schemaDrift.test.ts +18 -16
- package/src/scenarios/aiEnvelope.trustBoundaryPropagation.test.ts +38 -36
- package/src/scenarios/aiEnvelope.universalKinds.test.ts +31 -29
- package/src/scenarios/aiproviders-input-shape.test.ts +5 -5
- package/src/scenarios/aiproviders-realtimevoice-shape.test.ts +14 -16
- package/src/scenarios/aiproviders-selfhosted-honesty.test.ts +6 -4
- package/src/scenarios/aiproviders-selfhosted-shape.test.ts +15 -17
- package/src/scenarios/aiproviders-speechsynth-shape.test.ts +6 -8
- package/src/scenarios/aiproviders-subscription-scope.test.ts +7 -8
- package/src/scenarios/anonymous-actor-audit-opaque.test.ts +9 -8
- package/src/scenarios/anonymous-actor-default-deny.test.ts +10 -9
- package/src/scenarios/anonymous-actor-egress-guarded.test.ts +6 -5
- package/src/scenarios/anonymous-actor-no-secret-reach.test.ts +7 -6
- package/src/scenarios/anonymous-actor-shape.test.ts +17 -19
- package/src/scenarios/anonymous-actor-write-gated.test.ts +9 -8
- package/src/scenarios/append-ordering.test.ts +7 -5
- package/src/scenarios/approval-gate-events.test.ts +4 -3
- package/src/scenarios/approval-gate-flow.test.ts +4 -3
- package/src/scenarios/approval-payload.test.ts +5 -4
- package/src/scenarios/artifact-auth.test.ts +7 -6
- package/src/scenarios/artifact-type-pack-install.test.ts +6 -6
- package/src/scenarios/artifact-type-pack-manifest-validation.test.ts +9 -8
- package/src/scenarios/artifact-type-registration-source.test.ts +20 -20
- package/src/scenarios/artifact-type-store-emission.test.ts +15 -18
- package/src/scenarios/artifact-type-store-without-render.test.ts +4 -4
- package/src/scenarios/audit-log-integrity.test.ts +8 -6
- package/src/scenarios/auth-api-key-rotation.test.ts +11 -9
- package/src/scenarios/auth-mtls.test.ts +22 -20
- package/src/scenarios/auth-oauth2-client-credentials.test.ts +16 -14
- package/src/scenarios/auth-oidc-user-bearer.test.ts +26 -24
- package/src/scenarios/auth-saml-profile.test.ts +14 -12
- package/src/scenarios/auth-scim-profile.test.ts +3 -2
- package/src/scenarios/auth-subject-link-key-class.test.ts +8 -7
- package/src/scenarios/auth-subject-link.test.ts +11 -10
- package/src/scenarios/auth.test.ts +5 -4
- package/src/scenarios/authorization-fail-closed.test.ts +3 -2
- package/src/scenarios/authorization-roles-shape.test.ts +9 -7
- package/src/scenarios/blob-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/blob-presign-expiry.test.ts +12 -10
- package/src/scenarios/blob-roundtrip.test.ts +0 -0
- package/src/scenarios/budget-enforcement.test.ts +15 -14
- package/src/scenarios/budget-policy-shape.test.ts +18 -19
- package/src/scenarios/bulk-cancel.test.ts +9 -8
- package/src/scenarios/byok-auth-modes.test.ts +13 -14
- package/src/scenarios/byok-roundtrip.test.ts +12 -10
- package/src/scenarios/cache-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/cache-ttl-expiry.test.ts +7 -5
- package/src/scenarios/callai-multimodal.test.ts +5 -3
- package/src/scenarios/cancellation.test.ts +6 -5
- package/src/scenarios/cap-breach.test.ts +15 -14
- package/src/scenarios/capability-arm-witness.test.ts +5 -3
- package/src/scenarios/certification-bundle-non-vacuous.test.ts +20 -19
- package/src/scenarios/certification-bundle-redaction.test.ts +11 -10
- package/src/scenarios/certification-bundle-v2.test.ts +11 -10
- package/src/scenarios/chain-compensation-expansion.test.ts +14 -13
- package/src/scenarios/chain-produced-var-roundtrip.test.ts +13 -14
- package/src/scenarios/chain-subchain-cycle-rejected.test.ts +9 -10
- package/src/scenarios/chain-subchain-fanout.test.ts +8 -9
- package/src/scenarios/chain-subchain-sibling.test.ts +14 -16
- package/src/scenarios/channel-presence-behavioral.test.ts +9 -7
- package/src/scenarios/channel-presence-shape.test.ts +11 -12
- package/src/scenarios/channel-ttl.test.ts +6 -5
- package/src/scenarios/chat-card-pack-execution.test.ts +4 -4
- package/src/scenarios/chat-card-pack-manifest-validation.test.ts +9 -8
- package/src/scenarios/commitment-fired.test.ts +6 -5
- package/src/scenarios/compensation-behavior.test.ts +24 -23
- package/src/scenarios/compensation-recovery.test.ts +28 -27
- package/src/scenarios/configurable-schema.test.ts +8 -6
- package/src/scenarios/conformance-advertised-seam-required.test.ts +6 -5
- package/src/scenarios/conformance-execution-witness.test.ts +9 -8
- package/src/scenarios/connection-pack-apihosts.test.ts +19 -17
- package/src/scenarios/connection-pack-manifest-valid.test.ts +12 -11
- package/src/scenarios/connection-pack-no-credential-material.test.ts +8 -6
- package/src/scenarios/connection-pack-reach-exclusive.test.ts +5 -4
- package/src/scenarios/connection-pack-write-reconsent.test.ts +6 -4
- package/src/scenarios/connection-provider-resolution.test.ts +18 -16
- package/src/scenarios/connector-manifest-validity.test.ts +8 -7
- package/src/scenarios/context-budget-transcript-bound.test.ts +17 -15
- package/src/scenarios/context-summarization-replay.test.ts +14 -12
- package/src/scenarios/contract-provenance.test.ts +18 -18
- package/src/scenarios/conversation-turn-model-provenance-shape.test.ts +14 -15
- package/src/scenarios/conversationCapabilityNegotiation.test.ts +2 -1
- package/src/scenarios/conversationLifecycle.test.ts +2 -1
- package/src/scenarios/conversationReplayDeterminism.test.ts +4 -2
- package/src/scenarios/conversationVsLegacySuspend.test.ts +2 -1
- package/src/scenarios/core-standard-profile.test.ts +8 -9
- package/src/scenarios/cost-attribution.test.ts +23 -21
- package/src/scenarios/credential-payload-redaction.test.ts +4 -3
- package/src/scenarios/credentials-capability-shape.test.ts +5 -4
- package/src/scenarios/cross-engine-append-behavior.test.ts +11 -10
- package/src/scenarios/cross-engine-append-ordering.test.ts +4 -3
- package/src/scenarios/cross-host-ancestry-endpoint.test.ts +11 -10
- package/src/scenarios/cross-host-causation-shape.test.ts +7 -6
- package/src/scenarios/cross-workspace-isolation.test.ts +4 -3
- package/src/scenarios/data-residency-admission.test.ts +15 -14
- package/src/scenarios/deadletter-capability-shape.test.ts +6 -4
- package/src/scenarios/deadletter-retry-exhaustion.test.ts +4 -3
- package/src/scenarios/debug-bundle-truncation.test.ts +9 -7
- package/src/scenarios/debugBundle.test.ts +25 -23
- package/src/scenarios/discovery-families-view.test.ts +5 -4
- package/src/scenarios/discovery.test.ts +30 -29
- package/src/scenarios/dispatch-cross-worker-handoff.test.ts +10 -8
- package/src/scenarios/dispatch-fanout-parallel.test.ts +24 -22
- package/src/scenarios/dispatch-input-mapping.test.ts +12 -10
- package/src/scenarios/dispatch-output-mapping.test.ts +9 -7
- package/src/scenarios/dispatch-per-item-input.test.ts +22 -20
- package/src/scenarios/dispatchLoop.test.ts +3 -2
- package/src/scenarios/distillation-index-roundtrip.test.ts +3 -3
- package/src/scenarios/distillation-secret-carryforward.test.ts +2 -2
- package/src/scenarios/distillation-shape.test.ts +4 -4
- package/src/scenarios/distillation-stable-archive.test.ts +6 -6
- package/src/scenarios/distillation-token-budget.test.ts +5 -5
- package/src/scenarios/durability-poison-exhaustion.test.ts +7 -6
- package/src/scenarios/egress-audience-binding.test.ts +11 -10
- package/src/scenarios/egress-decision-content-free.test.ts +6 -5
- package/src/scenarios/egress-provenance-shape.test.ts +17 -18
- package/src/scenarios/envelope-completion-distinguishes-truncation.test.ts +27 -25
- package/src/scenarios/envelope-nl-to-format-engaged.test.ts +19 -17
- package/src/scenarios/envelope-reasoning-secret-redaction.test.ts +28 -26
- package/src/scenarios/envelope-reasoning-shape.test.ts +19 -17
- package/src/scenarios/envelope-recovery-applied.test.ts +24 -22
- package/src/scenarios/envelope-refusal-shape.test.ts +31 -29
- package/src/scenarios/envelope-rendering-hint.test.ts +5 -4
- package/src/scenarios/envelope-retry-attempted.test.ts +33 -31
- package/src/scenarios/envelope-retry-exhausted.test.ts +22 -20
- package/src/scenarios/envelope-tier-one-subset-static.test.ts +9 -7
- package/src/scenarios/envelope-truncated.test.ts +19 -17
- package/src/scenarios/envelope-truncation-cap-exhaustion.test.ts +19 -17
- package/src/scenarios/envelope-variant-discriminator-static.test.ts +4 -3
- package/src/scenarios/errors.test.ts +20 -19
- package/src/scenarios/eventOrdering.test.ts +16 -14
- package/src/scenarios/exec-not-protocol-tier.test.ts +5 -7
- package/src/scenarios/experimental-tier-shape.test.ts +17 -15
- package/src/scenarios/export-bundle-portability.test.ts +17 -17
- package/src/scenarios/failure-path.test.ts +7 -6
- package/src/scenarios/feedback-capability-shape.test.ts +4 -3
- package/src/scenarios/feedback-correction-redaction.test.ts +2 -1
- package/src/scenarios/feedback-cross-tenant-isolation.test.ts +2 -1
- package/src/scenarios/feedback-fork-not-copied.test.ts +3 -2
- package/src/scenarios/feedback-on-terminal-run.test.ts +3 -2
- package/src/scenarios/feedback-record-and-list.test.ts +2 -1
- package/src/scenarios/feedback-unsupported-501.test.ts +2 -1
- package/src/scenarios/fixtures-gating.test.ts +29 -28
- package/src/scenarios/fixtures-valid.test.ts +16 -15
- package/src/scenarios/form-content-instantiation.test.ts +15 -14
- package/src/scenarios/frontend-plugin-packs.test.ts +29 -27
- package/src/scenarios/fs-path-traversal.test.ts +14 -12
- package/src/scenarios/goal-standing-continuation.test.ts +21 -21
- package/src/scenarios/grpc-transport.test.ts +9 -7
- package/src/scenarios/heartbeat-capability-shape.test.ts +3 -3
- package/src/scenarios/heartbeat-fires-once-per-tick.test.ts +2 -2
- package/src/scenarios/heartbeat-idempotent-no-spam.test.ts +3 -3
- package/src/scenarios/heartbeat-runtime-bound.test.ts +2 -2
- package/src/scenarios/highConcurrency.test.ts +14 -13
- package/src/scenarios/host-callback-declaration.test.ts +8 -7
- package/src/scenarios/http-client-ssrf.test.ts +7 -5
- package/src/scenarios/i18n-negotiation.test.ts +14 -12
- package/src/scenarios/idempotency-concurrent-claim.test.ts +14 -13
- package/src/scenarios/idempotency-key-determinism.test.ts +20 -18
- package/src/scenarios/idempotency.test.ts +8 -7
- package/src/scenarios/idempotencyRetry.test.ts +13 -11
- package/src/scenarios/identity-owner-shape.test.ts +6 -5
- package/src/scenarios/identity-passthrough.test.ts +4 -3
- package/src/scenarios/interrupt-approval.test.ts +6 -5
- package/src/scenarios/interrupt-approver-routing.test.ts +15 -15
- package/src/scenarios/interrupt-auth-required-resume.test.ts +5 -3
- package/src/scenarios/interrupt-clarification.test.ts +4 -3
- package/src/scenarios/interrupt-external-event-correlation.test.ts +6 -5
- package/src/scenarios/interrupt-parent-child-cascade.test.ts +4 -3
- package/src/scenarios/interrupt-quorum-resolution.test.ts +5 -4
- package/src/scenarios/interrupt-token-matrix.test.ts +8 -6
- package/src/scenarios/interruptRace.test.ts +12 -10
- package/src/scenarios/kv-atomic-increment.test.ts +11 -9
- package/src/scenarios/kv-cas.test.ts +14 -12
- package/src/scenarios/kv-cross-tenant-isolation.test.ts +12 -10
- package/src/scenarios/kv-ttl-expiry.test.ts +7 -5
- package/src/scenarios/localized-content-delivery.test.ts +24 -26
- package/src/scenarios/maliciousManifest.test.ts +10 -8
- package/src/scenarios/mcp-2026-07-28-discover.test.ts +15 -14
- package/src/scenarios/mcp-cache-tenant-scope.test.ts +6 -5
- package/src/scenarios/mcp-current-auth-boundary.test.ts +7 -6
- package/src/scenarios/mcp-discoverability.test.ts +9 -7
- package/src/scenarios/mcp-extension-opacity.test.ts +7 -6
- package/src/scenarios/mcp-mrtr-roundtrip.test.ts +18 -17
- package/src/scenarios/mcp-server-elicitation-bridge.test.ts +13 -12
- package/src/scenarios/mcp-server-prompt-roundtrip.test.ts +11 -10
- package/src/scenarios/mcp-server-resource-roundtrip.test.ts +11 -10
- package/src/scenarios/mcp-server-sampling-bridge.test.ts +12 -11
- package/src/scenarios/mcp-server-tool-roundtrip.test.ts +14 -13
- package/src/scenarios/mcp-server-untrusted-args.test.ts +14 -13
- package/src/scenarios/mcp-stateless-request.test.ts +11 -10
- package/src/scenarios/mcp-tool-roundtrip.test.ts +4 -3
- package/src/scenarios/mcp-toolcall-redaction.test.ts +4 -3
- package/src/scenarios/mcp-version-negotiation.test.ts +15 -14
- package/src/scenarios/media-url-inline-cap.test.ts +21 -19
- package/src/scenarios/memory-attribution-emits-on-write.test.ts +5 -5
- package/src/scenarios/memory-attribution-no-content.test.ts +4 -4
- package/src/scenarios/memory-attribution-replay-stable.test.ts +4 -3
- package/src/scenarios/memory-attribution-shape.test.ts +4 -3
- package/src/scenarios/memory-attribution-tenant-scoped.test.ts +3 -3
- package/src/scenarios/memory-capability-model-shape.test.ts +14 -15
- package/src/scenarios/memory-compaction-event-emitted.test.ts +7 -6
- package/src/scenarios/memory-compaction-provenance-tag.test.ts +3 -2
- package/src/scenarios/memory-compaction-sr1-carry-forward.test.ts +7 -6
- package/src/scenarios/memory-consolidation-idempotent.test.ts +5 -4
- package/src/scenarios/memory-consolidation-shape.test.ts +9 -11
- package/src/scenarios/memory-degraded-projection.test.ts +10 -9
- package/src/scenarios/memory-injection-budget.test.ts +15 -14
- package/src/scenarios/metric-emission.test.ts +4 -3
- package/src/scenarios/model-capability-insufficient.test.ts +19 -17
- package/src/scenarios/model-capability-substituted.test.ts +20 -18
- package/src/scenarios/multi-agent-confidence-escalation.test.ts +12 -11
- package/src/scenarios/multi-agent-handoff-state-machine.test.ts +10 -9
- package/src/scenarios/multi-agent-memory-lifecycle.test.ts +11 -10
- package/src/scenarios/multi-node-ordering.test.ts +4 -3
- package/src/scenarios/multi-party-conversation-behavioral.test.ts +6 -6
- package/src/scenarios/multi-party-conversation-shape.test.ts +21 -23
- package/src/scenarios/multi-region-idempotency-behavior.test.ts +16 -15
- package/src/scenarios/multi-region-idempotency.test.ts +6 -5
- package/src/scenarios/node-failed-payload-shape.test.ts +5 -4
- package/src/scenarios/node-module-required-capabilities-shape.test.ts +10 -8
- package/src/scenarios/oauth-authorization-code-roundtrip.test.ts +5 -4
- package/src/scenarios/oauth-capability-shape.test.ts +5 -4
- package/src/scenarios/oauth-connector-redaction.test.ts +4 -3
- package/src/scenarios/openapi-resolved-paths.test.ts +6 -5
- package/src/scenarios/orchestratorConservativePath.test.ts +2 -1
- package/src/scenarios/orchestratorDispatch.test.ts +2 -1
- package/src/scenarios/orchestratorTermination.test.ts +2 -1
- package/src/scenarios/org-position-no-authority-escalation.test.ts +2 -2
- package/src/scenarios/otel-collector-canary-inspection.test.ts +8 -7
- package/src/scenarios/otel-emission-grpc.test.ts +3 -2
- package/src/scenarios/otel-emission.test.ts +6 -5
- package/src/scenarios/otel-trace-propagation-subworkflow.test.ts +7 -6
- package/src/scenarios/otel-trace-propagation.test.ts +2 -1
- package/src/scenarios/owner-subject-echo.test.ts +13 -12
- package/src/scenarios/owner-subject-shape.test.ts +11 -10
- package/src/scenarios/pack-manifest-extension-opacity.test.ts +8 -8
- package/src/scenarios/pack-registry-isolation.test.ts +4 -3
- package/src/scenarios/pack-registry-publish.test.ts +65 -63
- package/src/scenarios/pack-registry.test.ts +38 -36
- package/src/scenarios/pause-resume.test.ts +18 -16
- package/src/scenarios/policies.test.ts +20 -18
- package/src/scenarios/production-backpressure.test.ts +18 -16
- package/src/scenarios/production-retention-expiry.test.ts +8 -6
- package/src/scenarios/profile-discovery-core-alias.test.ts +13 -11
- package/src/scenarios/profileDerivation.test.ts +34 -33
- package/src/scenarios/prompt-all-four-kinds-events.test.ts +11 -9
- package/src/scenarios/prompt-composed-secret-redaction.test.ts +11 -9
- package/src/scenarios/prompt-composed-trust-marker.test.ts +10 -8
- package/src/scenarios/prompt-end-to-end-events.test.ts +17 -15
- package/src/scenarios/prompt-list-and-fetch.test.ts +16 -14
- package/src/scenarios/prompt-mutable-lifecycle.test.ts +13 -11
- package/src/scenarios/prompt-mutation-workspace-membership-enforced.test.ts +6 -4
- package/src/scenarios/prompt-pack-install.test.ts +14 -12
- package/src/scenarios/prompt-prefix-cache.test.ts +19 -17
- package/src/scenarios/prompt-read-workspace-membership-enforced.test.ts +11 -9
- package/src/scenarios/prompt-render-deterministic.test.ts +19 -17
- package/src/scenarios/prompt-resolution-chain-agent-intrinsic.test.ts +7 -5
- package/src/scenarios/prompt-resolution-chain-event.test.ts +12 -10
- package/src/scenarios/prompt-resolution-chain-fallback-cascade.test.ts +10 -8
- package/src/scenarios/prompt-resolution-chain-node-wins.test.ts +8 -6
- package/src/scenarios/prompt-template-shape.test.ts +30 -28
- package/src/scenarios/proposal-reviewable-learning.test.ts +19 -19
- package/src/scenarios/protocol-versions-array.test.ts +10 -9
- package/src/scenarios/provider-usage.test.ts +17 -15
- package/src/scenarios/providerPolicyEnforcement.test.ts +16 -14
- package/src/scenarios/purpose-propagation.test.ts +23 -21
- package/src/scenarios/queue-ack-nack-dlq.test.ts +12 -10
- package/src/scenarios/queue-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/queue-publish-consume-roundtrip.test.ts +10 -8
- package/src/scenarios/rate-limit-envelope.test.ts +6 -5
- package/src/scenarios/redaction.test.ts +13 -11
- package/src/scenarios/redactionAdversarial.test.ts +11 -9
- package/src/scenarios/registry-public.test.ts +12 -11
- package/src/scenarios/replay-divergence-at-refusal.test.ts +16 -15
- package/src/scenarios/replay-fanout-suppression.test.ts +16 -15
- package/src/scenarios/replay-fork-arbitrary.test.ts +24 -23
- package/src/scenarios/replay-fork.test.ts +20 -19
- package/src/scenarios/replay-llm-cache-key-portable.test.ts +11 -9
- package/src/scenarios/replay-llm-cache-key.test.ts +22 -21
- package/src/scenarios/replay-observable-sequence-determinism.test.ts +4 -3
- package/src/scenarios/replay-retention-expiry.test.ts +11 -9
- package/src/scenarios/replay-side-effect-suppression.test.ts +21 -19
- package/src/scenarios/replayDeterminism.test.ts +13 -12
- package/src/scenarios/requirement-ledger.test.ts +15 -14
- package/src/scenarios/restart-during-run.test.ts +3 -2
- package/src/scenarios/route-coverage.test.ts +20 -19
- package/src/scenarios/run-diff.test.ts +22 -20
- package/src/scenarios/run-execution-bounds-shape.test.ts +14 -12
- package/src/scenarios/run-transport-economy.test.ts +13 -11
- package/src/scenarios/runner-ledger.test.ts +17 -16
- package/src/scenarios/runs-lifecycle.test.ts +7 -6
- package/src/scenarios/runtime-capabilities.test.ts +12 -10
- package/src/scenarios/runtime-requires-install-gate.test.ts +8 -8
- package/src/scenarios/runtime-requires-shape.test.ts +7 -6
- package/src/scenarios/safefetch-behavior.test.ts +22 -21
- package/src/scenarios/safefetch-live-audit.test.ts +14 -13
- package/src/scenarios/sandbox-memory-cap.test.ts +2 -1
- package/src/scenarios/sandbox-mvp-behavior.test.ts +24 -23
- package/src/scenarios/sandbox-no-host-fs-escape.test.ts +3 -2
- package/src/scenarios/sandbox-timeout-cap.test.ts +2 -1
- package/src/scenarios/sandbox-wasm-isolation.test.ts +14 -15
- package/src/scenarios/sandbox-wasm-timeout.test.ts +5 -6
- package/src/scenarios/scheduling-capability-shape.test.ts +8 -6
- package/src/scenarios/scheduling-cron-fires-once.test.ts +8 -6
- package/src/scenarios/search-bm25-roundtrip.test.ts +8 -6
- package/src/scenarios/secret-leakage-otel-attribute.test.ts +22 -20
- package/src/scenarios/self-hosted-runner.test.ts +26 -24
- package/src/scenarios/semantic-digest-vectors.test.ts +8 -7
- package/src/scenarios/speech-synthesis-roundtrip.test.ts +9 -7
- package/src/scenarios/speech-synthesis-unadvertised.test.ts +5 -3
- package/src/scenarios/sql-injection-rejection.test.ts +8 -6
- package/src/scenarios/sql-transaction-atomicity.test.ts +10 -8
- package/src/scenarios/staleClaim.test.ts +5 -4
- package/src/scenarios/stream-modes-buffer.test.ts +8 -7
- package/src/scenarios/stream-modes-mixed.test.ts +10 -9
- package/src/scenarios/stream-modes.test.ts +12 -11
- package/src/scenarios/stream-subscribe-from-beginning.test.ts +11 -9
- package/src/scenarios/stream-text-fixture.test.ts +14 -13
- package/src/scenarios/streamReconnect.test.ts +13 -11
- package/src/scenarios/strict-behavior-gate.test.ts +9 -8
- package/src/scenarios/subrun-approval-fail-closed.test.ts +2 -2
- package/src/scenarios/subrun-approval-gate.test.ts +3 -3
- package/src/scenarios/subrun-attestation-shape.test.ts +2 -2
- package/src/scenarios/subrun-checksum-stable.test.ts +4 -4
- package/src/scenarios/subworkflow-input-mapping.test.ts +16 -14
- package/src/scenarios/subworkflow.test.ts +10 -9
- package/src/scenarios/table-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/table-cursor-pagination.test.ts +8 -6
- package/src/scenarios/table-schema-enforcement.test.ts +7 -5
- package/src/scenarios/test-seam-unauthenticated.test.ts +2 -2
- package/src/scenarios/tool-catalog-compact-projection.test.ts +10 -8
- package/src/scenarios/tool-catalog-projection.test.ts +11 -9
- package/src/scenarios/tool-descriptor-shape.test.ts +14 -15
- package/src/scenarios/tool-hooks-authorization-fail-closed.test.ts +3 -3
- package/src/scenarios/tool-hooks-content-free.test.ts +4 -4
- package/src/scenarios/tool-hooks-failure-honesty.test.ts +4 -4
- package/src/scenarios/tool-hooks-rate-limit.test.ts +2 -2
- package/src/scenarios/tool-hooks-secret-redaction.test.ts +2 -2
- package/src/scenarios/tool-hooks-shape.test.ts +3 -3
- package/src/scenarios/tool-session-lifecycle.test.ts +14 -13
- package/src/scenarios/trigger-bridge-delivery.test.ts +14 -14
- package/src/scenarios/trigger-bridge-shape.test.ts +19 -20
- package/src/scenarios/trigger-ingestion.test.ts +19 -17
- package/src/scenarios/trigger-stream-cdc-sources.test.ts +16 -14
- package/src/scenarios/vector-knn-roundtrip.test.ts +9 -7
- package/src/scenarios/verifier-gating.test.ts +8 -6
- package/src/scenarios/version-fold.test.ts +9 -8
- package/src/scenarios/version-negotiation.test.ts +12 -11
- package/src/scenarios/voice-bargein-no-partial-leak.test.ts +8 -6
- package/src/scenarios/voice-event-payloads-shape.test.ts +10 -11
- package/src/scenarios/voice-interim-not-durable.test.ts +7 -5
- package/src/scenarios/voice-streamref-tenant-bound.test.ts +5 -3
- package/src/scenarios/voice-synthesis-streaming.test.ts +11 -9
- package/src/scenarios/voice-transcription-streaming.test.ts +8 -6
- package/src/scenarios/voice-transcription-unadvertised.test.ts +5 -3
- package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +3 -2
- package/src/scenarios/wasm-pack-invoke-completed.test.ts +3 -2
- package/src/scenarios/wasm-pack-invoke-suspended.test.ts +3 -2
- package/src/scenarios/wasm-pack-load.test.ts +4 -3
- package/src/scenarios/wasm-pack-memory-cap.test.ts +5 -4
- package/src/scenarios/wasm-pack-replay-determinism.test.ts +2 -1
- package/src/scenarios/webhook-negative.test.ts +11 -9
- package/src/scenarios/webhook-receiver-adversarial.test.ts +8 -7
- package/src/scenarios/webhook-sig-algorithm.test.ts +5 -3
- package/src/scenarios/webhook-signed-delivery.test.ts +16 -15
- package/src/scenarios/webhook-tenant-isolation.test.ts +16 -14
- package/src/scenarios/workflow-chain-deferred-parameters.test.ts +28 -30
- package/src/scenarios/workflow-chain-expansion.test.ts +21 -20
- package/src/scenarios/workflow-chain-host-expansion.test.ts +21 -20
- package/src/scenarios/workflow-chain-pack-manifest-validation.test.ts +14 -12
- package/src/scenarios/workflow-chain-pack-signature-verification.test.ts +5 -4
- package/src/scenarios/workflow-chain-unresolvable-typeid.test.ts +9 -8
- package/src/scenarios/workflow-primary-output-annotation.test.ts +7 -6
- package/src/scenarios/workflow-variable-format.test.ts +24 -24
- package/src/scenarios/workload-identity-behavior.test.ts +15 -13
- package/src/scenarios/workload-identity-chain-bounds.test.ts +18 -17
- package/src/scenarios/workspace-behavior.test.ts +19 -18
- package/src/scenarios/workspace-capability-shape.test.ts +3 -2
- package/src/scenarios/workspace-cross-tenant-isolation-blackbox.test.ts +6 -5
- package/src/scenarios/workspace-cross-tenant-isolation.test.ts +7 -6
- package/src/scenarios/x-openwop-form-pack-manifest.test.ts +8 -7
- package/src/setup.ts +4 -2
- package/vitest.config.ts +5 -1
- package/api/.redocly.lint-ignore.yaml +0 -22
- package/api/asyncapi.yaml +0 -834
- package/api/grpc/openwop.proto +0 -251
- package/api/openapi.yaml +0 -2693
- package/api/redocly.yaml +0 -23
- package/schemas/README.md +0 -144
- package/schemas/a2a-task-state.schema.json +0 -78
- package/schemas/a2ui-surface-delta-frame.schema.json +0 -48
- package/schemas/agent-deployment-transition.schema.json +0 -49
- package/schemas/agent-deployment.schema.json +0 -54
- package/schemas/agent-eval-suite.schema.json +0 -140
- package/schemas/agent-inventory-response.schema.json +0 -115
- package/schemas/agent-manifest.schema.json +0 -156
- package/schemas/agent-org-chart.schema.json +0 -82
- package/schemas/agent-ref.schema.json +0 -63
- package/schemas/agent-roster-entry.schema.json +0 -81
- package/schemas/agent-roster-response.schema.json +0 -21
- package/schemas/ai-envelope.schema.json +0 -134
- package/schemas/annotation-create.schema.json +0 -37
- package/schemas/annotation.schema.json +0 -56
- package/schemas/artifact-type-pack-manifest.schema.json +0 -244
- package/schemas/audit-verify-result.schema.json +0 -90
- package/schemas/budget-policy.schema.json +0 -18
- package/schemas/capabilities.schema.json +0 -4124
- package/schemas/certification-bundle-v2.schema.json +0 -118
- package/schemas/channel-presence-payload.schema.json +0 -41
- package/schemas/channel-written-payload.schema.json +0 -55
- package/schemas/chat-card-pack-manifest.schema.json +0 -253
- package/schemas/compact-tool-descriptor.schema.json +0 -51
- package/schemas/compensation-policy.schema.json +0 -64
- package/schemas/conformance-certification-bundle.schema.json +0 -86
- package/schemas/connection-pack-manifest.schema.json +0 -353
- package/schemas/conversation-event.schema.json +0 -168
- package/schemas/conversation-turn.schema.json +0 -117
- package/schemas/core-conformance-mock-agent-config.schema.json +0 -152
- package/schemas/credential-provenance.schema.json +0 -18
- package/schemas/credential-reference.schema.json +0 -21
- package/schemas/debug-bundle.schema.json +0 -196
- package/schemas/dispatch-config.schema.json +0 -112
- package/schemas/envelopes/clarification.request.schema.json +0 -52
- package/schemas/envelopes/error.schema.json +0 -30
- package/schemas/envelopes/media.audio.schema.json +0 -38
- package/schemas/envelopes/media.file.schema.json +0 -37
- package/schemas/envelopes/media.image.schema.json +0 -33
- package/schemas/envelopes/schema.request.schema.json +0 -26
- package/schemas/envelopes/schema.response.schema.json +0 -22
- package/schemas/envelopes/ui.a2ui-surface.schema.json +0 -154
- package/schemas/error-envelope.schema.json +0 -25
- package/schemas/eval-summary.schema.json +0 -92
- package/schemas/export-bundle.schema.json +0 -66
- package/schemas/form-content-pack-manifest.schema.json +0 -243
- package/schemas/frontend-plugin-manifest.schema.json +0 -109
- package/schemas/goal.schema.json +0 -104
- package/schemas/heartbeat-evaluated.schema.json +0 -14
- package/schemas/heartbeat-state-changed.schema.json +0 -14
- package/schemas/localized-content-language-settings.schema.json +0 -26
- package/schemas/localized-content-page-response.schema.json +0 -60
- package/schemas/localized-content-page.schema.json +0 -62
- package/schemas/localized-content-section.schema.json +0 -51
- package/schemas/memory-entry.schema.json +0 -36
- package/schemas/memory-list-options.schema.json +0 -37
- package/schemas/node-pack-manifest.schema.json +0 -654
- package/schemas/orchestrator-decision.schema.json +0 -90
- package/schemas/org-chart-responsibility-view.schema.json +0 -26
- package/schemas/pack-lockfile.schema.json +0 -108
- package/schemas/prompt-kind.schema.json +0 -8
- package/schemas/prompt-pack-manifest.schema.json +0 -117
- package/schemas/prompt-ref.schema.json +0 -40
- package/schemas/prompt-template.schema.json +0 -149
- package/schemas/proposal.schema.json +0 -84
- package/schemas/registry-version-manifest.schema.json +0 -208
- package/schemas/residency.schema.json +0 -16
- package/schemas/run-ancestry-response.schema.json +0 -54
- package/schemas/run-diff-response.schema.json +0 -64
- package/schemas/run-event-payloads.schema.json +0 -4452
- package/schemas/run-event.schema.json +0 -211
- package/schemas/run-options.schema.json +0 -80
- package/schemas/run-orchestrator-decided-event.schema.json +0 -20
- package/schemas/run-snapshot.schema.json +0 -148
- package/schemas/security-advisory.schema.json +0 -109
- package/schemas/self-hosted-runner-dispatch-frame.schema.json +0 -79
- package/schemas/self-hosted-runner-registration.schema.json +0 -53
- package/schemas/self-hosted-runner-result-frame.schema.json +0 -38
- package/schemas/subject.schema.json +0 -56
- package/schemas/suspend-request.schema.json +0 -207
- package/schemas/tool-descriptor.schema.json +0 -63
- package/schemas/trigger-event.schema.json +0 -222
- package/schemas/trigger-subscription-registration.schema.json +0 -67
- package/schemas/trigger-subscription.schema.json +0 -26
- package/schemas/ui-plugin-message.schema.json +0 -90
- package/schemas/workflow-chain-pack-manifest.schema.json +0 -615
- package/schemas/workflow-definition.schema.json +0 -850
- package/schemas/workload-identity.schema.json +0 -79
- package/schemas/workspace-file-create.schema.json +0 -20
- package/schemas/workspace-file.schema.json +0 -39
|
@@ -20,6 +20,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
20
20
|
import { driver } from '../lib/driver.js';
|
|
21
21
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
22
22
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
23
|
+
import { req } from '../lib/requirement-ids.js';
|
|
24
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
23
25
|
|
|
24
26
|
const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
|
|
25
27
|
const FIXTURE = 'conformance-envelope-truncated';
|
|
@@ -56,20 +58,20 @@ async function lastBudget(): Promise<number | null> {
|
|
|
56
58
|
|
|
57
59
|
describe.skipIf(HTTP_SKIP)('envelope-truncated: runtime behavior (RFC 0032 §B.4 + RFC 0033 §B)', () => {
|
|
58
60
|
it('when mock returns stopReason: max_tokens on attempt 1, exactly one envelope.truncated event fires with stopReason: max_tokens', async () => {
|
|
59
|
-
if (!isFixtureAdvertised(FIXTURE)) return;
|
|
61
|
+
if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
60
62
|
const seed = await programMock([
|
|
61
63
|
{ stopReason: 'max_tokens', content: '{"valid":' },
|
|
62
64
|
{ stopReason: 'end_turn', content: '{"valid":true}' },
|
|
63
65
|
]);
|
|
64
|
-
if (seed.status === 404) return;
|
|
66
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
65
67
|
expect(seed.status).toBe(200);
|
|
66
68
|
|
|
67
69
|
const result = await startRunAndRead();
|
|
68
|
-
if (result === null) return;
|
|
70
|
+
if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
|
|
69
71
|
const truncated = result.events.filter((e) => e.type === 'envelope.truncated');
|
|
70
72
|
expect(
|
|
71
73
|
truncated.length,
|
|
72
|
-
|
|
74
|
+
req('openwop.it.envelope-truncated.when-mock-returns-stopreason-max-tokens-on-attempt-1-exactly-one-envelope-trunca',
|
|
73
75
|
'RFCS/0032-envelope-reliability-events.md §B.4',
|
|
74
76
|
'exactly one envelope.truncated event MUST fire when the provider returns finishReason corresponding to truncation',
|
|
75
77
|
),
|
|
@@ -78,17 +80,17 @@ describe.skipIf(HTTP_SKIP)('envelope-truncated: runtime behavior (RFC 0032 §B.4
|
|
|
78
80
|
});
|
|
79
81
|
|
|
80
82
|
it('payload includes nodeId + provider + model + partialPayloadAvailable boolean', async () => {
|
|
81
|
-
if (!isFixtureAdvertised(FIXTURE)) return;
|
|
83
|
+
if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
82
84
|
const seed = await programMock([
|
|
83
85
|
{ stopReason: 'max_tokens', content: '{"partial' },
|
|
84
86
|
{ stopReason: 'end_turn', content: '{"valid":true}' },
|
|
85
87
|
]);
|
|
86
|
-
if (seed.status === 404) return;
|
|
88
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
87
89
|
|
|
88
90
|
const result = await startRunAndRead();
|
|
89
|
-
if (result === null) return;
|
|
91
|
+
if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
|
|
90
92
|
const truncated = result.events.find((e) => e.type === 'envelope.truncated');
|
|
91
|
-
expect(truncated).toBeDefined();
|
|
93
|
+
expect(truncated, req('openwop.it.envelope-truncated.payload-includes-nodeid-provider-model-partialpayloadavailable-boolean', 'RFC 0032 §B.4', 'payload includes nodeId + provider + model + partialPayloadAvailable boolean')).toBeDefined();
|
|
92
94
|
const payload = truncated!.payload ?? {};
|
|
93
95
|
expect(payload.nodeId).toBe(NODE_ID);
|
|
94
96
|
expect(payload.provider).toBe('mock');
|
|
@@ -97,24 +99,24 @@ describe.skipIf(HTTP_SKIP)('envelope-truncated: runtime behavior (RFC 0032 §B.4
|
|
|
97
99
|
});
|
|
98
100
|
|
|
99
101
|
it('retry attempt receives a maxTokens value strictly greater than the previous attempt (RFC 0033 §B truncationBudgetMultiplier)', async () => {
|
|
100
|
-
if (!isFixtureAdvertised(FIXTURE)) return;
|
|
102
|
+
if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
101
103
|
const seed = await programMock([
|
|
102
104
|
{ stopReason: 'max_tokens', content: '{"partial' },
|
|
103
105
|
{ stopReason: 'end_turn', content: '{"valid":true}' },
|
|
104
106
|
]);
|
|
105
|
-
if (seed.status === 404) return;
|
|
107
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
106
108
|
|
|
107
109
|
const result = await startRunAndRead();
|
|
108
|
-
if (result === null) return;
|
|
110
|
+
if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
|
|
109
111
|
// After the run, the mock's most-recent budget is the SECOND (retry)
|
|
110
112
|
// attempt's maxTokens. Per RFC 0033 §B, this MUST exceed the fixture's
|
|
111
113
|
// initial maxTokens (50). The host's default multiplier is 2 — so the
|
|
112
114
|
// retry should see 100.
|
|
113
115
|
const budget = await lastBudget();
|
|
114
|
-
if (budget === null) return; // host doesn't expose the seam
|
|
116
|
+
if (budget === null) return softSkip('blocked', 'precondition not met — `budget === null` returned early (host doesn\'t expose the seam) (seam, prior step, or fixture unavailable)'); // host doesn't expose the seam
|
|
115
117
|
expect(
|
|
116
118
|
budget,
|
|
117
|
-
|
|
119
|
+
req('openwop.it.envelope-truncated.retry-attempt-receives-a-maxtokens-value-strictly-greater-than-the-previous-atte',
|
|
118
120
|
'RFCS/0033-envelope-completion-contract.md §B',
|
|
119
121
|
'truncation retry MUST issue with a strictly-increased maxTokens budget (host multiplies by capabilities.envelopes.reliability.completion.truncationBudgetMultiplier)',
|
|
120
122
|
),
|
|
@@ -122,15 +124,15 @@ describe.skipIf(HTTP_SKIP)('envelope-truncated: runtime behavior (RFC 0032 §B.4
|
|
|
122
124
|
});
|
|
123
125
|
|
|
124
126
|
it('run terminates `completed` after the second attempt succeeds', async () => {
|
|
125
|
-
if (!isFixtureAdvertised(FIXTURE)) return;
|
|
127
|
+
if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
126
128
|
const seed = await programMock([
|
|
127
129
|
{ stopReason: 'max_tokens', content: '{"partial' },
|
|
128
130
|
{ stopReason: 'end_turn', content: '{"valid":true}' },
|
|
129
131
|
]);
|
|
130
|
-
if (seed.status === 404) return;
|
|
132
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
131
133
|
|
|
132
134
|
const result = await startRunAndRead();
|
|
133
|
-
if (result === null) return;
|
|
134
|
-
expect((result.terminal as { status?: string }).status).toBe('completed');
|
|
135
|
+
if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
|
|
136
|
+
expect((result.terminal as { status?: string }).status, req('openwop.it.envelope-truncated.run-terminates-completed-after-the-second-attempt-succeeds', 'RFC 0032 §B.4', 'run terminates `completed` after the second attempt succeeds')).toBe('completed');
|
|
135
137
|
});
|
|
136
138
|
});
|
|
@@ -19,6 +19,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
19
19
|
import { driver } from '../lib/driver.js';
|
|
20
20
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
21
21
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
22
|
+
import { req } from '../lib/requirement-ids.js';
|
|
23
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
22
24
|
|
|
23
25
|
const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
|
|
24
26
|
const FIXTURE = 'conformance-envelope-truncation-cap-exhaustion';
|
|
@@ -57,24 +59,24 @@ const PERPETUAL_TRUNCATION = Array.from({ length: 16 }, () => ({
|
|
|
57
59
|
|
|
58
60
|
describe.skipIf(HTTP_SKIP)('envelope-truncation-cap-exhaustion: DoS-bound retry budget (RFC 0033 §B + §F)', () => {
|
|
59
61
|
it('perpetual truncation → emits exactly one envelope.retry.exhausted with finalReason: "truncation"', async () => {
|
|
60
|
-
if (!isFixtureAdvertised(FIXTURE)) return;
|
|
62
|
+
if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
61
63
|
const seed = await programMock(PERPETUAL_TRUNCATION);
|
|
62
|
-
if (seed.status === 404) return;
|
|
64
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
63
65
|
expect(seed.status).toBe(200);
|
|
64
66
|
|
|
65
67
|
const result = await startRunAndRead();
|
|
66
|
-
if (result === null) return;
|
|
68
|
+
if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
|
|
67
69
|
const exhausted = result.events.filter((e) => e.type === 'envelope.retry.exhausted');
|
|
68
70
|
expect(
|
|
69
71
|
exhausted.length,
|
|
70
|
-
|
|
72
|
+
req('openwop.it.envelope-truncation-cap-exhaustion.perpetual-truncation-emits-exactly-one-envelope-retry-exhausted-with-finalreason',
|
|
71
73
|
'RFCS/0032-envelope-reliability-events.md §B.2',
|
|
72
74
|
'exactly one envelope.retry.exhausted event MUST fire when the truncation-retry budget is exhausted',
|
|
73
75
|
),
|
|
74
76
|
).toBe(1);
|
|
75
77
|
expect(
|
|
76
78
|
exhausted[0]!.payload?.finalReason,
|
|
77
|
-
|
|
79
|
+
req('openwop.it.envelope-truncation-cap-exhaustion.perpetual-truncation-emits-exactly-one-envelope-retry-exhausted-with-finalreason',
|
|
78
80
|
'RFCS/0033-envelope-completion-contract.md §B',
|
|
79
81
|
'finalReason MUST be "truncation" when the host exhausts truncation retries (distinguished from schema-violation per RFC 0033 §A)',
|
|
80
82
|
),
|
|
@@ -82,16 +84,16 @@ describe.skipIf(HTTP_SKIP)('envelope-truncation-cap-exhaustion: DoS-bound retry
|
|
|
82
84
|
});
|
|
83
85
|
|
|
84
86
|
it('node fails with RunSnapshot.error.code: "envelope_truncation_unrecoverable" per RFC 0033 §F', async () => {
|
|
85
|
-
if (!isFixtureAdvertised(FIXTURE)) return;
|
|
87
|
+
if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
86
88
|
const seed = await programMock(PERPETUAL_TRUNCATION);
|
|
87
|
-
if (seed.status === 404) return;
|
|
89
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
88
90
|
|
|
89
91
|
const result = await startRunAndRead();
|
|
90
|
-
if (result === null) return;
|
|
92
|
+
if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
|
|
91
93
|
const code = (result.terminal as { error?: { code?: string } }).error?.code;
|
|
92
94
|
expect(
|
|
93
95
|
code,
|
|
94
|
-
|
|
96
|
+
req('openwop.it.envelope-truncation-cap-exhaustion.node-fails-with-runsnapshot-error-code-envelope-truncation-unrecoverable-per-rfc',
|
|
95
97
|
'RFCS/0033-envelope-completion-contract.md §F',
|
|
96
98
|
'truncation-retry-exhaustion MUST surface as RunSnapshot.error.code = envelope_truncation_unrecoverable (distinct from envelope_invalid which surfaces schema-violation-exhaustion)',
|
|
97
99
|
),
|
|
@@ -99,18 +101,18 @@ describe.skipIf(HTTP_SKIP)('envelope-truncation-cap-exhaustion: DoS-bound retry
|
|
|
99
101
|
});
|
|
100
102
|
|
|
101
103
|
it('total LLM calls bounded by maxRetryAttempts (DoS-bound — no infinite loop)', async () => {
|
|
102
|
-
if (!isFixtureAdvertised(FIXTURE)) return;
|
|
104
|
+
if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
103
105
|
const seed = await programMock(PERPETUAL_TRUNCATION);
|
|
104
|
-
if (seed.status === 404) return;
|
|
106
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
105
107
|
|
|
106
108
|
const result = await startRunAndRead();
|
|
107
|
-
if (result === null) return;
|
|
109
|
+
if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
|
|
108
110
|
// Count envelope.truncated events as a proxy for LLM-call count
|
|
109
111
|
// (each truncated attempt emits one).
|
|
110
112
|
const truncatedCount = result.events.filter((e) => e.type === 'envelope.truncated').length;
|
|
111
113
|
expect(
|
|
112
114
|
truncatedCount,
|
|
113
|
-
|
|
115
|
+
req('openwop.it.envelope-truncation-cap-exhaustion.total-llm-calls-bounded-by-maxretryattempts-dos-bound-no-infinite-loop',
|
|
114
116
|
'RFCS/0033-envelope-completion-contract.md §B',
|
|
115
117
|
'truncation-retry count MUST be bounded — host cannot loop indefinitely doubling budget; expected upper-bound matches advertised maxRetryAttempts',
|
|
116
118
|
),
|
|
@@ -123,19 +125,19 @@ describe.skipIf(HTTP_SKIP)('envelope-truncation-cap-exhaustion: DoS-bound retry
|
|
|
123
125
|
});
|
|
124
126
|
|
|
125
127
|
it('envelope.retry.exhausted is emitted BEFORE node.failed (cause precedes effect)', async () => {
|
|
126
|
-
if (!isFixtureAdvertised(FIXTURE)) return;
|
|
128
|
+
if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
127
129
|
const seed = await programMock(PERPETUAL_TRUNCATION);
|
|
128
|
-
if (seed.status === 404) return;
|
|
130
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
129
131
|
|
|
130
132
|
const result = await startRunAndRead();
|
|
131
|
-
if (result === null) return;
|
|
133
|
+
if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
|
|
132
134
|
const exhaustedIdx = result.events.findIndex((e) => e.type === 'envelope.retry.exhausted');
|
|
133
135
|
const failedIdx = result.events.findIndex((e) => e.type === 'node.failed');
|
|
134
136
|
expect(exhaustedIdx).toBeGreaterThanOrEqual(0);
|
|
135
137
|
expect(failedIdx).toBeGreaterThanOrEqual(0);
|
|
136
138
|
expect(
|
|
137
139
|
exhaustedIdx < failedIdx,
|
|
138
|
-
|
|
140
|
+
req('openwop.it.envelope-truncation-cap-exhaustion.envelope-retry-exhausted-is-emitted-before-node-failed-cause-precedes-effect',
|
|
139
141
|
'RFCS/0032-envelope-reliability-events.md §B.2',
|
|
140
142
|
'envelope.retry.exhausted MUST be emitted BEFORE node.failed',
|
|
141
143
|
),
|
|
@@ -21,6 +21,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
21
21
|
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
|
22
22
|
import { join } from 'node:path';
|
|
23
23
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
24
|
+
import { req } from '../lib/requirement-ids.js';
|
|
24
25
|
|
|
25
26
|
interface DiscriminatorViolation {
|
|
26
27
|
path: string;
|
|
@@ -124,7 +125,7 @@ describe('envelope-variant-discriminator-static (RFC 0031 §A)', () => {
|
|
|
124
125
|
it('local envelope-schema directory is discoverable', () => {
|
|
125
126
|
expect(
|
|
126
127
|
schemas.length,
|
|
127
|
-
'schemas/envelopes/*.schema.json MUST contain at least the four universal-kind schemas',
|
|
128
|
+
req('openwop.it.envelope-variant-discriminator-static.local-envelope-schema-directory-is-discoverable', 'RFC 0031 §A', 'schemas/envelopes/*.schema.json MUST contain at least the four universal-kind schemas'),
|
|
128
129
|
).toBeGreaterThanOrEqual(4);
|
|
129
130
|
});
|
|
130
131
|
|
|
@@ -135,7 +136,7 @@ describe('envelope-variant-discriminator-static (RFC 0031 §A)', () => {
|
|
|
135
136
|
walkForOneOf(schema, '#', violations);
|
|
136
137
|
expect(
|
|
137
138
|
violations,
|
|
138
|
-
`${kind}.schema.json contains \`oneOf\` — REFORMULATE as \`anyOf\` + single-string-enum discriminator per RFC 0031 §A. Violations: ${JSON.stringify(violations, null, 2)}
|
|
139
|
+
req('openwop.it.envelope-variant-discriminator-static.schema-json-must-not-contain-oneof-at-any-nesting-depth-gemini-silently-drops-it', 'RFC 0031 §A', `${kind}.schema.json contains \`oneOf\` — REFORMULATE as \`anyOf\` + single-string-enum discriminator per RFC 0031 §A. Violations: ${JSON.stringify(violations, null, 2)}`),
|
|
139
140
|
).toEqual([]);
|
|
140
141
|
});
|
|
141
142
|
|
|
@@ -145,7 +146,7 @@ describe('envelope-variant-discriminator-static (RFC 0031 §A)', () => {
|
|
|
145
146
|
walkForAnyOfDiscriminators(schema, '#', violations);
|
|
146
147
|
expect(
|
|
147
148
|
violations,
|
|
148
|
-
`${kind}.schema.json \`anyOf\` discriminator violations: ${JSON.stringify(violations, null, 2)}
|
|
149
|
+
req('openwop.it.envelope-variant-discriminator-static.schema-json-every-anyof-branch-declares-a-single-string-enum-discriminator-in-re', 'RFC 0031 §A', `${kind}.schema.json \`anyOf\` discriminator violations: ${JSON.stringify(violations, null, 2)}`),
|
|
149
150
|
).toEqual([]);
|
|
150
151
|
});
|
|
151
152
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { describe, it, expect } from 'vitest';
|
|
10
10
|
import { driver } from '../lib/driver.js';
|
|
11
|
+
import { req } from '../lib/requirement-ids.js';
|
|
11
12
|
|
|
12
13
|
interface ErrorEnvelope {
|
|
13
14
|
error: unknown;
|
|
@@ -16,28 +17,28 @@ interface ErrorEnvelope {
|
|
|
16
17
|
[key: string]: unknown;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
function assertErrorEnvelope(body: unknown, specSection: string): void {
|
|
20
|
-
expect(typeof body,
|
|
20
|
+
function assertErrorEnvelope(requirementId: string, body: unknown, specSection: string): void {
|
|
21
|
+
expect(typeof body, req(requirementId, specSection, 'error response MUST be a JSON object')).toBe(
|
|
21
22
|
'object',
|
|
22
23
|
);
|
|
23
24
|
const env = body as ErrorEnvelope;
|
|
24
25
|
|
|
25
|
-
expect(typeof env.error,
|
|
26
|
+
expect(typeof env.error, req(requirementId,
|
|
26
27
|
specSection,
|
|
27
28
|
'error envelope MUST include `error` (machine-readable string)',
|
|
28
29
|
)).toBe('string');
|
|
29
30
|
|
|
30
|
-
expect(typeof env.message,
|
|
31
|
+
expect(typeof env.message, req(requirementId,
|
|
31
32
|
specSection,
|
|
32
33
|
'error envelope MUST include `message` (human-readable string)',
|
|
33
34
|
)).toBe('string');
|
|
34
35
|
|
|
35
36
|
if (env.details !== undefined) {
|
|
36
|
-
expect(typeof env.details,
|
|
37
|
+
expect(typeof env.details, req(requirementId,
|
|
37
38
|
specSection,
|
|
38
39
|
'error envelope `details` (when present) MUST be a JSON object',
|
|
39
40
|
)).toBe('object');
|
|
40
|
-
expect(env.details,
|
|
41
|
+
expect(env.details, req(requirementId,
|
|
41
42
|
specSection,
|
|
42
43
|
'error envelope `details` (when present) MUST NOT be null',
|
|
43
44
|
)).not.toBeNull();
|
|
@@ -51,7 +52,7 @@ function assertErrorEnvelope(body: unknown, specSection: string): void {
|
|
|
51
52
|
const extraneousKeys = Object.keys(env as Record<string, unknown>).filter(
|
|
52
53
|
(k) => !allowedKeys.has(k),
|
|
53
54
|
);
|
|
54
|
-
expect(extraneousKeys,
|
|
55
|
+
expect(extraneousKeys, req(requirementId,
|
|
55
56
|
'schemas/error-envelope.schema.json (additionalProperties:false)',
|
|
56
57
|
`error envelope MUST NOT have keys outside {error, message, details}; extraneous: [${extraneousKeys.join(', ')}]`,
|
|
57
58
|
)).toEqual([]);
|
|
@@ -64,7 +65,7 @@ function assertErrorEnvelope(body: unknown, specSection: string): void {
|
|
|
64
65
|
* RECOMMENDED, not REQUIRED — hosts that don't emit trace IDs are still
|
|
65
66
|
* conformant. This helper just pins the placement.
|
|
66
67
|
*/
|
|
67
|
-
function assertCorrelationIdShape(body: unknown, specSection: string): void {
|
|
68
|
+
function assertCorrelationIdShape(requirementId: string, body: unknown, specSection: string): void {
|
|
68
69
|
const env = body as ErrorEnvelope;
|
|
69
70
|
// Top-level correlationId would be a spec violation; the assertErrorEnvelope
|
|
70
71
|
// additionalProperties check above catches it. This helper additionally
|
|
@@ -72,11 +73,11 @@ function assertCorrelationIdShape(body: unknown, specSection: string): void {
|
|
|
72
73
|
if (env.details && typeof env.details === 'object') {
|
|
73
74
|
const det = env.details as Record<string, unknown>;
|
|
74
75
|
if (det.correlationId !== undefined) {
|
|
75
|
-
expect(typeof det.correlationId,
|
|
76
|
+
expect(typeof det.correlationId, req(requirementId,
|
|
76
77
|
specSection,
|
|
77
78
|
'when present, details.correlationId MUST be a string',
|
|
78
79
|
)).toBe('string');
|
|
79
|
-
expect((det.correlationId as string).length,
|
|
80
|
+
expect((det.correlationId as string).length, req(requirementId,
|
|
80
81
|
specSection,
|
|
81
82
|
'details.correlationId MUST be a non-empty string',
|
|
82
83
|
)).toBeGreaterThan(0);
|
|
@@ -90,11 +91,11 @@ describe('errors: 404 envelope', () => {
|
|
|
90
91
|
|
|
91
92
|
expect(
|
|
92
93
|
[403, 404].includes(res.status),
|
|
93
|
-
|
|
94
|
+
req('openwop.it.errors.get-v1-runs-nonexistentid-returns-canonical-envelope', 'rest-endpoints.md', 'unknown run MUST return 404 (or 403 if leaking existence is forbidden)'),
|
|
94
95
|
).toBe(true);
|
|
95
96
|
|
|
96
|
-
assertErrorEnvelope(res.json, 'rest-endpoints.md error envelope');
|
|
97
|
-
assertCorrelationIdShape(res.json, 'rest-endpoints.md §error-envelope');
|
|
97
|
+
assertErrorEnvelope('openwop.it.errors.get-v1-runs-nonexistentid-returns-canonical-envelope', res.json, 'rest-endpoints.md error envelope');
|
|
98
|
+
assertCorrelationIdShape('openwop.it.errors.get-v1-runs-nonexistentid-returns-canonical-envelope', res.json, 'rest-endpoints.md §error-envelope');
|
|
98
99
|
});
|
|
99
100
|
});
|
|
100
101
|
|
|
@@ -107,12 +108,12 @@ describe('errors: 400 envelope (validation)', () => {
|
|
|
107
108
|
// not the status code.
|
|
108
109
|
expect(
|
|
109
110
|
res.status,
|
|
110
|
-
|
|
111
|
+
req('openwop.it.errors.post-v1-runs-with-empty-body-returns-canonical-envelope', 'rest-endpoints.md', 'malformed POST /v1/runs MUST return 4xx'),
|
|
111
112
|
).toBeGreaterThanOrEqual(400);
|
|
112
113
|
expect(res.status).toBeLessThan(500);
|
|
113
114
|
|
|
114
|
-
assertErrorEnvelope(res.json, 'rest-endpoints.md error envelope');
|
|
115
|
-
assertCorrelationIdShape(res.json, 'rest-endpoints.md §error-envelope');
|
|
115
|
+
assertErrorEnvelope('openwop.it.errors.post-v1-runs-with-empty-body-returns-canonical-envelope', res.json, 'rest-endpoints.md error envelope');
|
|
116
|
+
assertCorrelationIdShape('openwop.it.errors.post-v1-runs-with-empty-body-returns-canonical-envelope', res.json, 'rest-endpoints.md §error-envelope');
|
|
116
117
|
});
|
|
117
118
|
});
|
|
118
119
|
|
|
@@ -131,12 +132,12 @@ describe('errors: details.correlationId placement convention', () => {
|
|
|
131
132
|
const res = await driver.get('/v1/runs/openwop-conformance-correlation-probe-' + Date.now());
|
|
132
133
|
expect(
|
|
133
134
|
[400, 403, 404].includes(res.status),
|
|
134
|
-
|
|
135
|
+
req('openwop.it.errors.correlationid-when-emitted-on-5xx-must-be-in-details-never-top-level', 'rest-endpoints.md', 'unknown run returns 4xx envelope'),
|
|
135
136
|
).toBe(true);
|
|
136
137
|
const body = res.json as Record<string, unknown>;
|
|
137
|
-
expect(body,
|
|
138
|
+
expect(body, req('openwop.it.errors.correlationid-when-emitted-on-5xx-must-be-in-details-never-top-level', 'rest-endpoints.md', 'response MUST be a JSON object')).toBeDefined();
|
|
138
139
|
// The structural constraint: no top-level correlationId.
|
|
139
|
-
expect(body.correlationId,
|
|
140
|
+
expect(body.correlationId, req('openwop.it.errors.correlationid-when-emitted-on-5xx-must-be-in-details-never-top-level',
|
|
140
141
|
'spec/v1/rest-endpoints.md §error-envelope (correlationId convention)',
|
|
141
142
|
'correlationId MUST NOT appear at the top level of the error envelope; canonical home is `details.correlationId`',
|
|
142
143
|
)).toBeUndefined();
|
|
@@ -19,6 +19,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
19
19
|
import { driver } from '../lib/driver.js';
|
|
20
20
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
21
21
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
22
|
+
import { req } from '../lib/requirement-ids.js';
|
|
23
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
22
24
|
|
|
23
25
|
const NOOP_WORKFLOW_ID = 'conformance-noop';
|
|
24
26
|
const SKIP_NO_NOOP = !isFixtureAdvertised(NOOP_WORKFLOW_ID);
|
|
@@ -48,18 +50,18 @@ describe.skipIf(SKIP_NO_NOOP)('event-ordering: polling returns events in monoton
|
|
|
48
50
|
await pollUntilTerminal(runId);
|
|
49
51
|
|
|
50
52
|
const res = await driver.get(`/v1/runs/${encodeURIComponent(runId)}/events/poll`);
|
|
51
|
-
if (res.status !== 200) return;
|
|
53
|
+
if (res.status !== 200) return softSkip('blocked', 'precondition not met — `res.status !== 200` returned early (seam, prior step, or fixture unavailable)');
|
|
52
54
|
|
|
53
55
|
const body = res.json as { events?: RawEvent[] } | undefined;
|
|
54
|
-
if (!body?.events) return;
|
|
55
|
-
if (body.events.length < 2) return; // single-event runs have no ordering to verify
|
|
56
|
+
if (!body?.events) return softSkip('blocked', 'precondition not met — `!body?.events` returned early (seam, prior step, or fixture unavailable)');
|
|
57
|
+
if (body.events.length < 2) return softSkip('blocked', 'precondition not met — `body.events.length < 2` returned early (single-event runs have no ordering to verify) (seam, prior step, or fixture unavailable)'); // single-event runs have no ordering to verify
|
|
56
58
|
|
|
57
59
|
const seqs = body.events.map(getSeq);
|
|
58
60
|
for (let i = 1; i < seqs.length; i++) {
|
|
59
61
|
const curr = seqs[i];
|
|
60
62
|
const prev = seqs[i - 1];
|
|
61
63
|
if (curr === null || prev === null) continue; // host without seq fields
|
|
62
|
-
expect(curr,
|
|
64
|
+
expect(curr, req('openwop.it.eventOrdering.events-from-a-single-poll-have-non-decreasing-sequence-numbers',
|
|
63
65
|
'observability.md §"Event ordering"',
|
|
64
66
|
`event[${i}].sequence (${curr}) MUST be >= event[${i - 1}].sequence (${prev})`,
|
|
65
67
|
)).toBeGreaterThanOrEqual(prev);
|
|
@@ -74,13 +76,13 @@ describe.skipIf(SKIP_NO_NOOP)('event-ordering: polling returns events in monoton
|
|
|
74
76
|
|
|
75
77
|
const a = await driver.get(`/v1/runs/${encodeURIComponent(runId)}/events/poll`);
|
|
76
78
|
const b = await driver.get(`/v1/runs/${encodeURIComponent(runId)}/events/poll`);
|
|
77
|
-
if (a.status !== 200 || b.status !== 200) return;
|
|
79
|
+
if (a.status !== 200 || b.status !== 200) return softSkip('blocked', 'precondition not met — `a.status !== 200 || b.status !== 200` returned early (seam, prior step, or fixture unavailable)');
|
|
78
80
|
|
|
79
81
|
const aBody = a.json as { events?: RawEvent[] } | undefined;
|
|
80
82
|
const bBody = b.json as { events?: RawEvent[] } | undefined;
|
|
81
|
-
if (!aBody?.events || !bBody?.events) return;
|
|
83
|
+
if (!aBody?.events || !bBody?.events) return softSkip('blocked', 'precondition not met — `!aBody?.events || !bBody?.events` returned early (seam, prior step, or fixture unavailable)');
|
|
82
84
|
|
|
83
|
-
expect(aBody.events.length,
|
|
85
|
+
expect(aBody.events.length, req('openwop.it.eventOrdering.repeated-polls-of-a-terminal-run-yield-identical-event-sequences',
|
|
84
86
|
'observability.md',
|
|
85
87
|
'repeated polls of terminal run MUST return same number of events',
|
|
86
88
|
)).toBe(bBody.events.length);
|
|
@@ -89,7 +91,7 @@ describe.skipIf(SKIP_NO_NOOP)('event-ordering: polling returns events in monoton
|
|
|
89
91
|
const aSeq = getSeq(aBody.events[i]!);
|
|
90
92
|
const bSeq = getSeq(bBody.events[i]!);
|
|
91
93
|
if (aSeq === null || bSeq === null) continue;
|
|
92
|
-
expect(aSeq,
|
|
94
|
+
expect(aSeq, req('openwop.it.eventOrdering.repeated-polls-of-a-terminal-run-yield-identical-event-sequences',
|
|
93
95
|
'observability.md',
|
|
94
96
|
`event[${i}] sequence MUST be stable across repeated polls`,
|
|
95
97
|
)).toBe(bSeq);
|
|
@@ -105,15 +107,15 @@ describe.skipIf(SKIP_NO_NOOP)('event-ordering: terminal run has at most one term
|
|
|
105
107
|
await pollUntilTerminal(runId);
|
|
106
108
|
|
|
107
109
|
const res = await driver.get(`/v1/runs/${encodeURIComponent(runId)}/events/poll`);
|
|
108
|
-
if (res.status !== 200) return;
|
|
110
|
+
if (res.status !== 200) return softSkip('blocked', 'precondition not met — `res.status !== 200` returned early (seam, prior step, or fixture unavailable)');
|
|
109
111
|
|
|
110
112
|
const body = res.json as { events?: RawEvent[] } | undefined;
|
|
111
|
-
if (!body?.events) return;
|
|
113
|
+
if (!body?.events) return softSkip('blocked', 'precondition not met — `!body?.events` returned early (seam, prior step, or fixture unavailable)');
|
|
112
114
|
|
|
113
115
|
const TERMINAL_TYPES = new Set(['run.completed', 'run.failed', 'run.cancelled']);
|
|
114
116
|
const terminalCount = body.events.filter((e) => typeof e.type === 'string' && TERMINAL_TYPES.has(e.type)).length;
|
|
115
117
|
|
|
116
|
-
expect(terminalCount,
|
|
118
|
+
expect(terminalCount, req('openwop.it.eventOrdering.event-stream-contains-exactly-one-of-run-completed-run-failed-run-cancelled',
|
|
117
119
|
'observability.md §"Run lifecycle events"',
|
|
118
120
|
'a run MUST emit exactly one terminal event (run.completed / run.failed / run.cancelled)',
|
|
119
121
|
)).toBe(1);
|
|
@@ -126,16 +128,16 @@ describe.skipIf(SKIP_NO_NOOP)('event-ordering: terminal run has at most one term
|
|
|
126
128
|
await pollUntilTerminal(runId);
|
|
127
129
|
|
|
128
130
|
const res = await driver.get(`/v1/runs/${encodeURIComponent(runId)}/events/poll`);
|
|
129
|
-
if (res.status !== 200) return;
|
|
131
|
+
if (res.status !== 200) return softSkip('blocked', 'precondition not met — `res.status !== 200` returned early (seam, prior step, or fixture unavailable)');
|
|
130
132
|
|
|
131
133
|
const body = res.json as { events?: RawEvent[] } | undefined;
|
|
132
|
-
if (!body?.events || body.events.length === 0) return;
|
|
134
|
+
if (!body?.events || body.events.length === 0) return softSkip('blocked', 'precondition not met — `!body?.events || body.events.length === 0` returned early (seam, prior step, or fixture unavailable)');
|
|
133
135
|
|
|
134
136
|
const TERMINAL_TYPES = new Set(['run.completed', 'run.failed', 'run.cancelled']);
|
|
135
137
|
const lastEvent = body.events[body.events.length - 1]!;
|
|
136
138
|
expect(
|
|
137
139
|
typeof lastEvent.type === 'string' && TERMINAL_TYPES.has(lastEvent.type),
|
|
138
|
-
|
|
140
|
+
req('openwop.it.eventOrdering.the-terminal-event-is-the-last-event-in-the-stream',
|
|
139
141
|
'observability.md §"Run lifecycle events"',
|
|
140
142
|
'terminal event MUST be the last event in the stream — no events after a terminal type',
|
|
141
143
|
),
|
|
@@ -26,9 +26,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
26
26
|
import { readFileSync, readdirSync } from 'node:fs';
|
|
27
27
|
import { join } from 'node:path';
|
|
28
28
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
29
|
-
|
|
30
|
-
/** Server-free assertion-message helper (mirrors driver.describe's "spec — requirement" shape without requiring OPENWOP_BASE_URL). */
|
|
31
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
29
|
+
import { req } from '../lib/requirement-ids.js';
|
|
32
30
|
|
|
33
31
|
/**
|
|
34
32
|
* Closed denylist of exec-class identifier *segments* (whole tokens). The
|
|
@@ -79,7 +77,7 @@ describe('exec-not-protocol-tier: no exec-class primitive in the protocol corpus
|
|
|
79
77
|
}
|
|
80
78
|
expect(
|
|
81
79
|
offenders,
|
|
82
|
-
|
|
80
|
+
req('openwop.it.exec-not-protocol-tier.no-protocol-owned-core-openwop-identifier-denotes-arbitrary-command-execution',
|
|
83
81
|
'host-extensions.md §exec-class tools',
|
|
84
82
|
'the protocol corpus MUST NOT define a core.*/openwop.* exec-class identifier',
|
|
85
83
|
),
|
|
@@ -105,7 +103,7 @@ describe('exec-not-protocol-tier: no exec-class primitive in the protocol corpus
|
|
|
105
103
|
walkProps(caps, 'capabilities');
|
|
106
104
|
expect(
|
|
107
105
|
offenders,
|
|
108
|
-
|
|
106
|
+
req('openwop.it.exec-not-protocol-tier.no-capabilities-schema-json-property-name-denotes-arbitrary-command-execution', 'host-extensions.md §exec-class tools', 'capabilities.schema.json MUST NOT declare an exec-class capability flag'),
|
|
109
107
|
).toEqual([]);
|
|
110
108
|
});
|
|
111
109
|
|
|
@@ -120,12 +118,12 @@ describe('exec-not-protocol-tier: no exec-class primitive in the protocol corpus
|
|
|
120
118
|
});
|
|
121
119
|
expect(
|
|
122
120
|
offenders,
|
|
123
|
-
|
|
121
|
+
req('openwop.it.exec-not-protocol-tier.the-canonical-runeventtype-vocabulary-contains-no-exec-class-event', 'host-extensions.md §exec-class tools', 'no RunEventType MUST denote arbitrary command execution'),
|
|
124
122
|
).toEqual([]);
|
|
125
123
|
});
|
|
126
124
|
|
|
127
125
|
it('positive control: a vendor / x-host exec identifier is allowed (host-extension namespace)', () => {
|
|
128
|
-
expect(isExecClass('vendor.acme.exec')).toBe(false);
|
|
126
|
+
expect(isExecClass('vendor.acme.exec'), req('openwop.it.exec-not-protocol-tier.positive-control-a-vendor-x-host-exec-identifier-is-allowed-host-extension-names', 'RFC 0069', 'positive control: a vendor / x-host exec identifier is allowed (host-extension namespace)')).toBe(false);
|
|
129
127
|
expect(isExecClass('x-host-acme-exec')).toBe(false);
|
|
130
128
|
expect(isExecClass('private.host.shell')).toBe(false);
|
|
131
129
|
// And the denylist actually fires on a protocol-owned id:
|
|
@@ -31,6 +31,8 @@ import { driver } from '../lib/driver.js';
|
|
|
31
31
|
import { experimentalGate } from '../lib/behavior-gate.js';
|
|
32
32
|
import { __resetEnvCacheForTests } from '../lib/env.js';
|
|
33
33
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
34
|
+
import { req } from '../lib/requirement-ids.js';
|
|
35
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
34
36
|
|
|
35
37
|
const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
|
|
36
38
|
|
|
@@ -62,15 +64,15 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §A schema discipline (RFC
|
|
|
62
64
|
const em = capabilityFamily<{ executionModel?: { [k: string]: unknown; crossHostCausation?: Record<string, unknown>; replayDeterminism?: Record<string, unknown> } }>(d, 'multiAgent')?.executionModel;
|
|
63
65
|
if (em === undefined) {
|
|
64
66
|
ctx.skip();
|
|
65
|
-
return;
|
|
67
|
+
return softSkip('blocked', 'precondition not met — `em === undefined` returned early (seam, prior step, or fixture unavailable)');
|
|
66
68
|
}
|
|
67
69
|
if (em.tier === undefined) {
|
|
68
70
|
ctx.skip(); // tier is optional with default 'stable'
|
|
69
|
-
return;
|
|
71
|
+
return softSkip('blocked', 'precondition not met — `em.tier === undefined` returned early (seam, prior step, or fixture unavailable)');
|
|
70
72
|
}
|
|
71
73
|
expect(
|
|
72
74
|
em.tier === 'stable' || em.tier === 'experimental',
|
|
73
|
-
|
|
75
|
+
req('openwop.it.experimental-tier-shape.multiagent-executionmodel-tier-when-present-must-be-one-of-stable-experimental',
|
|
74
76
|
'RFCS/0042-experimental-capability-tier.md §A',
|
|
75
77
|
'multiAgent.executionModel.tier MUST be one of the canonical enum values',
|
|
76
78
|
),
|
|
@@ -82,12 +84,12 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §A schema discipline (RFC
|
|
|
82
84
|
const em = capabilityFamily<{ executionModel?: { [k: string]: unknown; crossHostCausation?: Record<string, unknown>; replayDeterminism?: Record<string, unknown> } }>(d, 'multiAgent')?.executionModel;
|
|
83
85
|
if (em === undefined || em.tier !== 'experimental') {
|
|
84
86
|
ctx.skip();
|
|
85
|
-
return;
|
|
87
|
+
return softSkip('blocked', 'precondition not met — `em === undefined || em.tier !== \'experimental\'` returned early (seam, prior step, or fixture unavailable)');
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
expect(
|
|
89
91
|
typeof em.experimentalUntil,
|
|
90
|
-
|
|
92
|
+
req('openwop.it.experimental-tier-shape.when-tier-experimental-experimentaluntil-must-be-present-valid-date',
|
|
91
93
|
'RFCS/0042-experimental-capability-tier.md §B',
|
|
92
94
|
'when tier is "experimental", experimentalUntil MUST be present (the §B sunset-rule contract)',
|
|
93
95
|
),
|
|
@@ -96,7 +98,7 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §A schema discipline (RFC
|
|
|
96
98
|
const dateStr = em.experimentalUntil as string;
|
|
97
99
|
expect(
|
|
98
100
|
/^\d{4}-\d{2}-\d{2}$/.test(dateStr),
|
|
99
|
-
|
|
101
|
+
req('openwop.it.experimental-tier-shape.when-tier-experimental-experimentaluntil-must-be-present-valid-date',
|
|
100
102
|
'RFCS/0042-experimental-capability-tier.md §B',
|
|
101
103
|
'experimentalUntil MUST match YYYY-MM-DD',
|
|
102
104
|
),
|
|
@@ -105,7 +107,7 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §A schema discipline (RFC
|
|
|
105
107
|
const parsed = new Date(dateStr + 'T00:00:00Z');
|
|
106
108
|
expect(
|
|
107
109
|
!Number.isNaN(parsed.getTime()),
|
|
108
|
-
|
|
110
|
+
req('openwop.it.experimental-tier-shape.when-tier-experimental-experimentaluntil-must-be-present-valid-date',
|
|
109
111
|
'RFCS/0042-experimental-capability-tier.md §B',
|
|
110
112
|
'experimentalUntil MUST parse as a valid ISO-8601 date',
|
|
111
113
|
),
|
|
@@ -117,18 +119,18 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §A schema discipline (RFC
|
|
|
117
119
|
const em = capabilityFamily<{ executionModel?: { [k: string]: unknown; crossHostCausation?: Record<string, unknown>; replayDeterminism?: Record<string, unknown> } }>(d, 'multiAgent')?.executionModel;
|
|
118
120
|
if (em === undefined || em.tier !== 'experimental') {
|
|
119
121
|
ctx.skip();
|
|
120
|
-
return;
|
|
122
|
+
return softSkip('blocked', 'precondition not met — `em === undefined || em.tier !== \'experimental\'` returned early (seam, prior step, or fixture unavailable)');
|
|
121
123
|
}
|
|
122
124
|
if (typeof em.experimentalUntil !== 'string') {
|
|
123
125
|
ctx.skip(); // shape probe above will fail; don't double-fail
|
|
124
|
-
return;
|
|
126
|
+
return softSkip('blocked', 'precondition not met — `typeof em.experimentalUntil !== \'string\'` returned early (seam, prior step, or fixture unavailable)');
|
|
125
127
|
}
|
|
126
128
|
const target = new Date((em.experimentalUntil as string) + 'T00:00:00Z').getTime();
|
|
127
129
|
const now = Date.now();
|
|
128
130
|
const daysAhead = (target - now) / (1000 * 60 * 60 * 24);
|
|
129
131
|
expect(
|
|
130
132
|
daysAhead <= 365,
|
|
131
|
-
|
|
133
|
+
req('openwop.it.experimental-tier-shape.experimentaluntil-must-be-365-days-in-the-future-sunset-bound',
|
|
132
134
|
'RFCS/0042-experimental-capability-tier.md §B',
|
|
133
135
|
`experimentalUntil MUST be ≤ 365 days from now (got ${Math.floor(daysAhead)} days; advertised ${em.experimentalUntil})`,
|
|
134
136
|
),
|
|
@@ -140,17 +142,17 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §A schema discipline (RFC
|
|
|
140
142
|
const em = capabilityFamily<{ executionModel?: { [k: string]: unknown; crossHostCausation?: Record<string, unknown>; replayDeterminism?: Record<string, unknown> } }>(d, 'multiAgent')?.executionModel;
|
|
141
143
|
if (em === undefined || em.tier !== 'experimental') {
|
|
142
144
|
ctx.skip();
|
|
143
|
-
return;
|
|
145
|
+
return softSkip('blocked', 'precondition not met — `em === undefined || em.tier !== \'experimental\'` returned early (seam, prior step, or fixture unavailable)');
|
|
144
146
|
}
|
|
145
147
|
if (typeof em.experimentalUntil !== 'string') {
|
|
146
148
|
ctx.skip();
|
|
147
|
-
return;
|
|
149
|
+
return softSkip('blocked', 'precondition not met — `typeof em.experimentalUntil !== \'string\'` returned early (seam, prior step, or fixture unavailable)');
|
|
148
150
|
}
|
|
149
151
|
const target = new Date((em.experimentalUntil as string) + 'T00:00:00Z').getTime();
|
|
150
152
|
const now = Date.now();
|
|
151
153
|
expect(
|
|
152
154
|
target >= now,
|
|
153
|
-
|
|
155
|
+
req('openwop.it.experimental-tier-shape.sunset-detection-experimentaluntil-in-the-past-is-non-conformant',
|
|
154
156
|
'RFCS/0042-experimental-capability-tier.md §B',
|
|
155
157
|
`experimentalUntil MUST NOT be in the past (advertised ${em.experimentalUntil}; host MUST either flip tier to stable, retract the advertisement, or re-advertise with a future date + open deprecation RFC)`,
|
|
156
158
|
),
|
|
@@ -168,7 +170,7 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §D experimentalGate helper
|
|
|
168
170
|
const result = experimentalGate('test-profile', true, 'experimental', '2027-05-22');
|
|
169
171
|
expect(
|
|
170
172
|
result,
|
|
171
|
-
|
|
173
|
+
req('openwop.it.experimental-tier-shape.experimentalgate-returns-false-for-tier-experimental-without-openwop-require-exp',
|
|
172
174
|
'RFCS/0042-experimental-capability-tier.md §D',
|
|
173
175
|
'default mode + tier="experimental" MUST soft-skip — helper returns false',
|
|
174
176
|
),
|
|
@@ -189,7 +191,7 @@ describe.skipIf(HTTP_SKIP)('experimental-tier-shape: §D experimentalGate helper
|
|
|
189
191
|
__resetEnvCacheForTests();
|
|
190
192
|
try {
|
|
191
193
|
// Stable + advertised → proceed.
|
|
192
|
-
expect(experimentalGate('test-stable', true, 'stable')).toBe(true);
|
|
194
|
+
expect(experimentalGate('test-stable', true, 'stable'), req('openwop.it.experimental-tier-shape.experimentalgate-routes-through-behaviorgate-when-tier-undefined-or-stable', 'RFC 0042 §A', 'experimentalGate routes through behaviorGate when tier === undefined or "stable"')).toBe(true);
|
|
193
195
|
expect(experimentalGate('test-stable-undef', true, undefined)).toBe(true);
|
|
194
196
|
// Stable + NOT advertised, default mode → skip (returns false, no throw).
|
|
195
197
|
expect(experimentalGate('test-not-adv', false, 'stable')).toBe(false);
|