@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
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
verifyProfileRequirements,
|
|
35
35
|
} from '../lib/requirement-ledger.js';
|
|
36
36
|
import { allRequirements, requirementsFor } from '../lib/requirement-registry.js';
|
|
37
|
+
import { req } from '../lib/requirement-ids.js';
|
|
37
38
|
|
|
38
39
|
// This file records REAL requirement ids as fixtures; keep them out of a live
|
|
39
40
|
// --certify ledger sink (they would out-vote genuine dispositions).
|
|
@@ -47,7 +48,7 @@ describe('RFC 0148 §A — requirement execution ledger', () => {
|
|
|
47
48
|
beforeEach(() => resetLedger());
|
|
48
49
|
|
|
49
50
|
it('the disposition vocabulary is exactly the five §A names', () => {
|
|
50
|
-
expect([...DISPOSITIONS].sort()).toEqual(
|
|
51
|
+
expect([...DISPOSITIONS].sort(), req('openwop.it.requirement-ledger.the-disposition-vocabulary-is-exactly-the-five-a-names', 'RFC 0148 §A', 'the disposition vocabulary is exactly the five §A names')).toEqual(
|
|
51
52
|
['blocked', 'executed-fail', 'executed-pass', 'inapplicable', 'skipped'].sort(),
|
|
52
53
|
);
|
|
53
54
|
});
|
|
@@ -58,7 +59,7 @@ describe('RFC 0148 §A — requirement execution ledger', () => {
|
|
|
58
59
|
// is "not exercised". Every vacuity in this corpus reached `pass` this way.
|
|
59
60
|
expect(
|
|
60
61
|
dispositionOf('openwop.never.recorded'),
|
|
61
|
-
'RFC 0148 §A: silence is evidence of nothing. An absent disposition MUST NOT read as executed-pass.',
|
|
62
|
+
req('openwop.it.requirement-ledger.an-unrecorded-requirement-resolves-to-blocked-not-to-a-pass', 'RFC 0148 §A', 'RFC 0148 §A: silence is evidence of nothing. An absent disposition MUST NOT read as executed-pass.'),
|
|
62
63
|
).toBe('blocked');
|
|
63
64
|
expect(entryOf('openwop.never.recorded').detail).toMatch(/not exercised/);
|
|
64
65
|
});
|
|
@@ -66,7 +67,7 @@ describe('RFC 0148 §A — requirement execution ledger', () => {
|
|
|
66
67
|
it('blocked is not certifiable, while skipped and inapplicable are', () => {
|
|
67
68
|
// "We could not check" and "we checked and it holds" are the two states this
|
|
68
69
|
// whole program exists to stop conflating.
|
|
69
|
-
expect(CERTIFIABLE).not.toContain('blocked');
|
|
70
|
+
expect(CERTIFIABLE, req('openwop.it.requirement-ledger.blocked-is-not-certifiable-while-skipped-and-inapplicable-are', 'RFC 0148 §A', 'blocked is not certifiable, while skipped and inapplicable are')).not.toContain('blocked');
|
|
70
71
|
expect(CERTIFIABLE).not.toContain('executed-fail');
|
|
71
72
|
expect([...CERTIFIABLE].sort()).toEqual(['executed-pass', 'inapplicable', 'skipped']);
|
|
72
73
|
});
|
|
@@ -78,7 +79,7 @@ describe('RFC 0148 §A — requirement execution ledger', () => {
|
|
|
78
79
|
const verdict = verifyProfileRequirements('openwop-example', []);
|
|
79
80
|
expect(
|
|
80
81
|
verdict.certifiable,
|
|
81
|
-
'RFC 0148 §C / gap G6: an empty requirement set is exactly the vacuity that started this program.',
|
|
82
|
+
req('openwop.it.requirement-ledger.a-profile-with-no-requirements-is-not-certifiable', 'RFC 0148 §A', 'RFC 0148 §C / gap G6: an empty requirement set is exactly the vacuity that started this program.'),
|
|
82
83
|
).toBe(false);
|
|
83
84
|
});
|
|
84
85
|
|
|
@@ -86,20 +87,20 @@ describe('RFC 0148 §A — requirement execution ledger', () => {
|
|
|
86
87
|
recordRequirement('openwop.a', 'executed-pass');
|
|
87
88
|
recordRequirement('openwop.b', 'skipped', 'profile not advertised; operator opted out');
|
|
88
89
|
const verdict = verifyProfileRequirements('openwop-example', ['openwop.a', 'openwop.b', 'openwop.c']);
|
|
89
|
-
expect(verdict.certifiable, 'RFC 0148 §A: a blocked requirement invalidates the claim').toBe(false);
|
|
90
|
+
expect(verdict.certifiable, req('openwop.it.requirement-ledger.one-blocked-requirement-invalidates-the-whole-profile', 'RFC 0148 §A', 'RFC 0148 §A: a blocked requirement invalidates the claim')).toBe(false);
|
|
90
91
|
expect(verdict.blocking.map((b) => b.requirementId)).toEqual(['openwop.c']);
|
|
91
92
|
});
|
|
92
93
|
|
|
93
94
|
it('a fully-exercised profile certifies', () => {
|
|
94
95
|
recordRequirement('openwop.a', 'executed-pass');
|
|
95
96
|
recordRequirement('openwop.b', 'inapplicable', 'host advertises no streaming surface');
|
|
96
|
-
expect(verifyProfileRequirements('openwop-example', ['openwop.a', 'openwop.b']).certifiable).toBe(true);
|
|
97
|
+
expect(verifyProfileRequirements('openwop-example', ['openwop.a', 'openwop.b']).certifiable, req('openwop.it.requirement-ledger.a-fully-exercised-profile-certifies', 'RFC 0148 §A', 'a fully-exercised profile certifies')).toBe(true);
|
|
97
98
|
});
|
|
98
99
|
|
|
99
100
|
it('a non-pass disposition without a reason is rejected', () => {
|
|
100
101
|
// A `blocked` with no explanation is an outcome nobody can act on, and it is
|
|
101
102
|
// the shape a lazily-instrumented scenario would emit by default.
|
|
102
|
-
expect(() => recordRequirement('openwop.a', 'blocked')).toThrow(/without a reason/);
|
|
103
|
+
expect(() => recordRequirement('openwop.a', 'blocked'), req('openwop.it.requirement-ledger.a-non-pass-disposition-without-a-reason-is-rejected', 'RFC 0148 §A', 'a non-pass disposition without a reason is rejected')).toThrow(/without a reason/);
|
|
103
104
|
expect(() => recordRequirement('openwop.b', 'skipped', ' ')).toThrow(/without a reason/);
|
|
104
105
|
// A pass needs no prose — the assertion itself is the evidence.
|
|
105
106
|
expect(() => recordRequirement('openwop.c', 'executed-pass')).not.toThrow();
|
|
@@ -109,14 +110,14 @@ describe('RFC 0148 §A — requirement execution ledger', () => {
|
|
|
109
110
|
// Silent overwrite would let a later soft-skip bury an earlier real failure
|
|
110
111
|
// — the same conflation running the other direction.
|
|
111
112
|
recordRequirement('openwop.a', 'executed-fail', 'assertion failed against target');
|
|
112
|
-
expect(() => recordRequirement('openwop.a', 'executed-pass')).toThrow(/already recorded/);
|
|
113
|
+
expect(() => recordRequirement('openwop.a', 'executed-pass'), req('openwop.it.requirement-ledger.contradictory-dispositions-for-one-requirement-throw-rather-than-last-write-wins', 'RFC 0148 §A', 'contradictory dispositions for one requirement throw rather than last-write-wins')).toThrow(/already recorded/);
|
|
113
114
|
expect(dispositionOf('openwop.a')).toBe('executed-fail');
|
|
114
115
|
});
|
|
115
116
|
|
|
116
117
|
it('re-recording the same disposition is idempotent', () => {
|
|
117
118
|
recordRequirement('openwop.a', 'executed-pass');
|
|
118
119
|
recordRequirement('openwop.a', 'executed-pass');
|
|
119
|
-
expect(snapshot()).toHaveLength(1);
|
|
120
|
+
expect(snapshot(), req('openwop.it.requirement-ledger.re-recording-the-same-disposition-is-idempotent', 'RFC 0148 §A', 're-recording the same disposition is idempotent')).toHaveLength(1);
|
|
120
121
|
});
|
|
121
122
|
});
|
|
122
123
|
|
|
@@ -125,7 +126,7 @@ describe('RFC 0148 §A — the registry binds to the certification floor', () =>
|
|
|
125
126
|
|
|
126
127
|
it('every profile with a runtime floor yields requirement IDs', () => {
|
|
127
128
|
// Guard: an empty registry would make the legs below vacuous.
|
|
128
|
-
expect(allRequirements().length, 'the floor MUST produce requirement IDs').toBeGreaterThan(10);
|
|
129
|
+
expect(allRequirements().length, req('openwop.it.requirement-ledger.every-profile-with-a-runtime-floor-yields-requirement-ids', 'RFC 0148 §A', 'the floor MUST produce requirement IDs')).toBeGreaterThan(10);
|
|
129
130
|
expect(allRequirements()).toContain('openwop.floor.runs-lifecycle');
|
|
130
131
|
expect(allRequirements()).toContain('openwop.floor.any.interrupt-');
|
|
131
132
|
});
|
|
@@ -137,7 +138,7 @@ describe('RFC 0148 §A — the registry binds to the certification floor', () =>
|
|
|
137
138
|
// the caller to decide; an empty array would silently certify.
|
|
138
139
|
expect(
|
|
139
140
|
requirementsFor('openwop-replay-fork'),
|
|
140
|
-
'RFC 0148 §C: unevaluable MUST be distinguishable from empty-by-design',
|
|
141
|
+
req('openwop.it.requirement-ledger.an-unwritten-floor-returns-null-not-an-empty-list-and-so-does-a-conditional-floo', 'RFC 0148 §A', 'RFC 0148 §C: unevaluable MUST be distinguishable from empty-by-design'),
|
|
141
142
|
).toBeNull();
|
|
142
143
|
expect(requirementsFor('openwop-does-not-exist')).toBeNull();
|
|
143
144
|
// With the document, the advertised branch is required
|
|
@@ -151,7 +152,7 @@ describe('RFC 0148 §A — the registry binds to the certification floor', () =>
|
|
|
151
152
|
expect(allRequirements()).toContain('openwop.floor.replay-fork');
|
|
152
153
|
// the six formerly-unprovable profiles now have floors
|
|
153
154
|
for (const p of ['openwop-interrupts', 'openwop-secrets', 'openwop-provider-policy', 'openwop-memory', 'openwop-trigger-bridge']) {
|
|
154
|
-
expect((requirementsFor(p) ?? []).length, `${p} floor MUST be non-empty`).toBeGreaterThan(0);
|
|
155
|
+
expect((requirementsFor(p) ?? []).length, req('openwop.it.requirement-ledger.an-unwritten-floor-returns-null-not-an-empty-list-and-so-does-a-conditional-floo', 'RFC 0148 §A', `${p} floor MUST be non-empty`)).toBeGreaterThan(0);
|
|
155
156
|
}
|
|
156
157
|
});
|
|
157
158
|
|
|
@@ -161,13 +162,13 @@ describe('RFC 0148 §A — the registry binds to the certification floor', () =>
|
|
|
161
162
|
expect(requirementsFor('openwop-core')).toEqual([]);
|
|
162
163
|
expect(
|
|
163
164
|
verifyProfileRequirements('openwop-core', requirementsFor('openwop-core') ?? []).certifiable,
|
|
164
|
-
'RFC 0155 §A: an `openwop-core` badge is a statement about a document, not a running system',
|
|
165
|
+
req('openwop.it.requirement-ledger.a-discovery-only-profile-returns-an-empty-list-which-does-not-certify', 'RFC 0148 §A', 'RFC 0155 §A: an `openwop-core` badge is a statement about a document, not a running system'),
|
|
165
166
|
).toBe(false);
|
|
166
167
|
});
|
|
167
168
|
|
|
168
169
|
it('the real core-standard floor does not certify until every requirement is exercised', () => {
|
|
169
170
|
const ids = requirementsFor('openwop-core-standard');
|
|
170
|
-
expect(ids).not.toBeNull();
|
|
171
|
+
expect(ids, req('openwop.it.requirement-ledger.the-real-core-standard-floor-does-not-certify-until-every-requirement-is-exercis', 'RFC 0148 §A', 'the real core-standard floor does not certify until every requirement is exercised')).not.toBeNull();
|
|
171
172
|
const requirements = ids as readonly string[];
|
|
172
173
|
// Nothing recorded yet: every requirement is blocked, so the profile fails.
|
|
173
174
|
expect(verifyProfileRequirements('openwop-core-standard', requirements).blocking).toHaveLength(
|
|
@@ -36,6 +36,7 @@ import { mkdtempSync, rmSync } from 'node:fs';
|
|
|
36
36
|
import { tmpdir } from 'node:os';
|
|
37
37
|
import { join } from 'node:path';
|
|
38
38
|
import { spawnHost, type SpawnedHost } from '../lib/multiProcess.js';
|
|
39
|
+
import { req } from '../lib/requirement-ids.js';
|
|
39
40
|
|
|
40
41
|
const HOST_PACKAGE_DIR =
|
|
41
42
|
process.env.OPENWOP_RESTART_DURING_RUN_HOST_DIR ?? 'examples/hosts/sqlite';
|
|
@@ -160,11 +161,11 @@ describe.skipIf(!RUN_THIS_SCENARIO)(
|
|
|
160
161
|
|
|
161
162
|
expect(
|
|
162
163
|
terminal,
|
|
163
|
-
'run MUST reach terminal status on the restarted host within 30s',
|
|
164
|
+
req('openwop.it.restart-during-run.mid-run-sigkill-on-the-host-process-restart-at-the-same-port-db-resumes-to-termi', 'spec/v1/scale-profiles.md', 'run MUST reach terminal status on the restarted host within 30s'),
|
|
164
165
|
).not.toBeNull();
|
|
165
166
|
expect(
|
|
166
167
|
terminal?.status,
|
|
167
|
-
'restart-recovery MUST drive the run to a non-pending terminal status',
|
|
168
|
+
req('openwop.it.restart-during-run.mid-run-sigkill-on-the-host-process-restart-at-the-same-port-db-resumes-to-termi', 'spec/v1/scale-profiles.md', 'restart-recovery MUST drive the run to a non-pending terminal status'),
|
|
168
169
|
).toMatch(/^(completed|failed|cancelled)$/);
|
|
169
170
|
// For the cancellable fixture, completed is the expected
|
|
170
171
|
// outcome — the workflow just exits the delay node and finishes.
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { describe, it, expect } from 'vitest';
|
|
11
11
|
import { driver } from '../lib/driver.js';
|
|
12
12
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
13
|
+
import { req } from '../lib/requirement-ids.js';
|
|
13
14
|
|
|
14
15
|
interface ErrorEnvelope {
|
|
15
16
|
error?: unknown;
|
|
@@ -21,28 +22,28 @@ interface ErrorEnvelope {
|
|
|
21
22
|
const NOOP_WORKFLOW_ID = 'conformance-noop';
|
|
22
23
|
const SKIP_NO_NOOP = !isFixtureAdvertised(NOOP_WORKFLOW_ID);
|
|
23
24
|
|
|
24
|
-
function assertCanonicalErrorEnvelope(body: unknown, specSection: string): void {
|
|
25
|
-
expect(typeof body,
|
|
25
|
+
function assertCanonicalErrorEnvelope(requirementId: string, body: unknown, specSection: string): void {
|
|
26
|
+
expect(typeof body, req(requirementId, specSection, 'error response MUST be a JSON object')).toBe(
|
|
26
27
|
'object',
|
|
27
28
|
);
|
|
28
|
-
expect(body,
|
|
29
|
+
expect(body, req(requirementId, specSection, 'error response MUST NOT be null')).not.toBeNull();
|
|
29
30
|
|
|
30
31
|
const env = body as ErrorEnvelope;
|
|
31
|
-
expect(typeof env.error,
|
|
32
|
+
expect(typeof env.error, req(requirementId,
|
|
32
33
|
specSection,
|
|
33
34
|
'error envelope MUST include machine-readable string `error`',
|
|
34
35
|
)).toBe('string');
|
|
35
|
-
expect(typeof env.message,
|
|
36
|
+
expect(typeof env.message, req(requirementId,
|
|
36
37
|
specSection,
|
|
37
38
|
'error envelope MUST include human-readable string `message`',
|
|
38
39
|
)).toBe('string');
|
|
39
40
|
|
|
40
41
|
if (env.details !== undefined) {
|
|
41
|
-
expect(typeof env.details,
|
|
42
|
+
expect(typeof env.details, req(requirementId,
|
|
42
43
|
specSection,
|
|
43
44
|
'error envelope `details`, when present, MUST be an object',
|
|
44
45
|
)).toBe('object');
|
|
45
|
-
expect(env.details,
|
|
46
|
+
expect(env.details, req(requirementId,
|
|
46
47
|
specSection,
|
|
47
48
|
'error envelope `details`, when present, MUST NOT be null',
|
|
48
49
|
)).not.toBeNull();
|
|
@@ -50,7 +51,7 @@ function assertCanonicalErrorEnvelope(body: unknown, specSection: string): void
|
|
|
50
51
|
|
|
51
52
|
const allowedKeys = new Set(['error', 'message', 'details']);
|
|
52
53
|
const extras = Object.keys(env).filter((key) => !allowedKeys.has(key));
|
|
53
|
-
expect(extras,
|
|
54
|
+
expect(extras, req(requirementId,
|
|
54
55
|
'schemas/error-envelope.schema.json',
|
|
55
56
|
'error envelope MUST NOT contain top-level keys outside {error,message,details}',
|
|
56
57
|
)).toEqual([]);
|
|
@@ -60,17 +61,17 @@ describe.skipIf(SKIP_NO_NOOP)('route coverage: GET /v1/workflows/{workflowId}',
|
|
|
60
61
|
it('returns the seeded workflow definition for an advertised fixture workflow', async () => {
|
|
61
62
|
const res = await driver.get(`/v1/workflows/${encodeURIComponent(NOOP_WORKFLOW_ID)}`);
|
|
62
63
|
|
|
63
|
-
expect(res.status,
|
|
64
|
+
expect(res.status, req('openwop.it.route-coverage.returns-the-seeded-workflow-definition-for-an-advertised-fixture-workflow',
|
|
64
65
|
'api/openapi.yaml operationId=getWorkflow',
|
|
65
66
|
'GET /v1/workflows/{workflowId} MUST return 200 for a known workflow',
|
|
66
67
|
)).toBe(200);
|
|
67
68
|
|
|
68
69
|
const body = res.json as { id?: unknown; nodes?: unknown } | undefined;
|
|
69
|
-
expect(body?.id,
|
|
70
|
+
expect(body?.id, req('openwop.it.route-coverage.returns-the-seeded-workflow-definition-for-an-advertised-fixture-workflow',
|
|
70
71
|
'schemas/workflow-definition.schema.json',
|
|
71
72
|
'workflow definition MUST echo its id',
|
|
72
73
|
)).toBe(NOOP_WORKFLOW_ID);
|
|
73
|
-
expect(Array.isArray(body?.nodes),
|
|
74
|
+
expect(Array.isArray(body?.nodes), req('openwop.it.route-coverage.returns-the-seeded-workflow-definition-for-an-advertised-fixture-workflow',
|
|
74
75
|
'schemas/workflow-definition.schema.json',
|
|
75
76
|
'workflow definition MUST include a nodes array',
|
|
76
77
|
)).toBe(true);
|
|
@@ -81,11 +82,11 @@ describe('route coverage: negative operation probes', () => {
|
|
|
81
82
|
it('GET /v1/workflows/{unknownWorkflowId} returns a canonical 404 or 403 envelope', async () => {
|
|
82
83
|
const res = await driver.get('/v1/workflows/openwop-conformance-missing-workflow');
|
|
83
84
|
|
|
84
|
-
expect([403, 404].includes(res.status),
|
|
85
|
+
expect([403, 404].includes(res.status), req('openwop.it.route-coverage.get-v1-workflows-unknownworkflowid-returns-a-canonical-404-or-403-envelope',
|
|
85
86
|
'api/openapi.yaml operationId=getWorkflow',
|
|
86
87
|
'unknown workflow MUST return 404 or 403 if existence is protected',
|
|
87
88
|
)).toBe(true);
|
|
88
|
-
assertCanonicalErrorEnvelope(res.json, 'rest-endpoints.md error envelope');
|
|
89
|
+
assertCanonicalErrorEnvelope('openwop.it.route-coverage.get-v1-workflows-unknownworkflowid-returns-a-canonical-404-or-403-envelope', res.json, 'rest-endpoints.md error envelope');
|
|
89
90
|
});
|
|
90
91
|
|
|
91
92
|
it('GET /v1/runs/{runId}/artifacts/{artifactId} for an unknown artifact returns a canonical 404 or 403 envelope', async () => {
|
|
@@ -93,11 +94,11 @@ describe('route coverage: negative operation probes', () => {
|
|
|
93
94
|
'/v1/runs/openwop-conformance-missing-run/artifacts/openwop-conformance-missing-artifact',
|
|
94
95
|
);
|
|
95
96
|
|
|
96
|
-
expect([403, 404].includes(res.status),
|
|
97
|
+
expect([403, 404].includes(res.status), req('openwop.it.route-coverage.get-v1-runs-runid-artifacts-artifactid-for-an-unknown-artifact-returns-a-canonic',
|
|
97
98
|
'api/openapi.yaml operationId=getArtifact',
|
|
98
99
|
'unknown artifact MUST return 404 or 403 if existence is protected',
|
|
99
100
|
)).toBe(true);
|
|
100
|
-
assertCanonicalErrorEnvelope(res.json, 'rest-endpoints.md error envelope');
|
|
101
|
+
assertCanonicalErrorEnvelope('openwop.it.route-coverage.get-v1-runs-runid-artifacts-artifactid-for-an-unknown-artifact-returns-a-canonic', res.json, 'rest-endpoints.md error envelope');
|
|
101
102
|
});
|
|
102
103
|
|
|
103
104
|
it('POST /v1/webhooks with an invalid URL returns a canonical validation envelope', async () => {
|
|
@@ -106,24 +107,24 @@ describe('route coverage: negative operation probes', () => {
|
|
|
106
107
|
events: ['run.completed'],
|
|
107
108
|
});
|
|
108
109
|
|
|
109
|
-
expect(res.status,
|
|
110
|
+
expect(res.status, req('openwop.it.route-coverage.post-v1-webhooks-with-an-invalid-url-returns-a-canonical-validation-envelope',
|
|
110
111
|
'api/openapi.yaml operationId=registerWebhook',
|
|
111
112
|
'invalid webhook registration MUST return a 4xx validation response',
|
|
112
113
|
)).toBeGreaterThanOrEqual(400);
|
|
113
114
|
expect(res.status).toBeLessThan(500);
|
|
114
|
-
assertCanonicalErrorEnvelope(res.json, 'rest-endpoints.md error envelope');
|
|
115
|
+
assertCanonicalErrorEnvelope('openwop.it.route-coverage.post-v1-webhooks-with-an-invalid-url-returns-a-canonical-validation-envelope', res.json, 'rest-endpoints.md error envelope');
|
|
115
116
|
});
|
|
116
117
|
|
|
117
118
|
it('DELETE /v1/webhooks/{webhookId} for an unknown subscription returns 204, 404, or 403', async () => {
|
|
118
119
|
const res = await driver.delete('/v1/webhooks/openwop-conformance-missing-webhook');
|
|
119
120
|
|
|
120
|
-
expect([204, 403, 404].includes(res.status),
|
|
121
|
+
expect([204, 403, 404].includes(res.status), req('openwop.it.route-coverage.delete-v1-webhooks-webhookid-for-an-unknown-subscription-returns-204-404-or-403',
|
|
121
122
|
'api/openapi.yaml operationId=unregisterWebhook',
|
|
122
123
|
'unknown webhook unregister MUST be idempotent 204 or return 404/403',
|
|
123
124
|
)).toBe(true);
|
|
124
125
|
|
|
125
126
|
if (res.status !== 204) {
|
|
126
|
-
assertCanonicalErrorEnvelope(res.json, 'rest-endpoints.md error envelope');
|
|
127
|
+
assertCanonicalErrorEnvelope('openwop.it.route-coverage.delete-v1-webhooks-webhookid-for-an-unknown-subscription-returns-204-404-or-403', res.json, 'rest-endpoints.md error envelope');
|
|
127
128
|
}
|
|
128
129
|
});
|
|
129
130
|
});
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
|
|
24
24
|
import { describe, it, expect } from 'vitest';
|
|
25
25
|
import { driver } from '../lib/driver.js';
|
|
26
|
+
import { req } from '../lib/requirement-ids.js';
|
|
27
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
26
28
|
|
|
27
29
|
const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
|
|
28
30
|
|
|
@@ -59,41 +61,41 @@ async function settle(runId: string): Promise<void> {
|
|
|
59
61
|
describe.skipIf(HTTP_SKIP)('run-diff: GET /v1/runs/{runId}:diff (RFC 0054)', () => {
|
|
60
62
|
it('diffing a run against itself ⇒ divergedAtSeq null + empty eventDiffs', async (ctx) => {
|
|
61
63
|
const runId = await createRun(FIXTURE_A);
|
|
62
|
-
if (!runId) { ctx.skip(); return; }
|
|
64
|
+
if (!runId) { ctx.skip(); return softSkip('blocked', 'precondition not met — `!runId` returned early (seam, prior step, or fixture unavailable)'); }
|
|
63
65
|
await settle(runId);
|
|
64
66
|
|
|
65
67
|
const res = await driver.get(`/v1/runs/${encodeURIComponent(runId)}:diff?against=${encodeURIComponent(runId)}`);
|
|
66
|
-
if (res.status === 404) { ctx.skip(); return; } // endpoint not implemented
|
|
67
|
-
expect(res.status,
|
|
68
|
+
if (res.status === 404) { ctx.skip(); return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (endpoint not implemented) (seam, prior step, or fixture unavailable)'); } // endpoint not implemented
|
|
69
|
+
expect(res.status, req('openwop.it.run-diff.diffing-a-run-against-itself-divergedatseq-null-empty-eventdiffs', 'spec/v1/rest-endpoints.md §:diff', 'self-diff MUST return 200')).toBe(200);
|
|
68
70
|
|
|
69
71
|
const body = res.json as DiffResponse;
|
|
70
72
|
expect(body.a).toBe(runId);
|
|
71
73
|
expect(body.b).toBe(runId);
|
|
72
74
|
expect(
|
|
73
75
|
body.divergedAtSeq,
|
|
74
|
-
|
|
76
|
+
req('openwop.it.run-diff.diffing-a-run-against-itself-divergedatseq-null-empty-eventdiffs', 'RFCS/0054 §C', 'identical logs MUST yield divergedAtSeq: null'),
|
|
75
77
|
).toBeNull();
|
|
76
|
-
expect(body.eventDiffs, 'identical logs MUST yield an empty eventDiffs array').toEqual([]);
|
|
78
|
+
expect(body.eventDiffs, req('openwop.it.run-diff.diffing-a-run-against-itself-divergedatseq-null-empty-eventdiffs', 'RFCS/0054 §C', 'identical logs MUST yield an empty eventDiffs array')).toEqual([]);
|
|
77
79
|
});
|
|
78
80
|
|
|
79
81
|
it('diffing two structurally-different runs ⇒ non-null divergedAtSeq aligned to eventDiffs[0]', async (ctx) => {
|
|
80
82
|
const [ra, rb] = await Promise.all([createRun(FIXTURE_A), createRun(FIXTURE_B)]);
|
|
81
|
-
if (!ra || !rb) { ctx.skip(); return; }
|
|
83
|
+
if (!ra || !rb) { ctx.skip(); return softSkip('blocked', 'precondition not met — `!ra || !rb` returned early (seam, prior step, or fixture unavailable)'); }
|
|
82
84
|
await Promise.all([settle(ra), settle(rb)]);
|
|
83
85
|
|
|
84
86
|
const res = await driver.get(`/v1/runs/${encodeURIComponent(ra)}:diff?against=${encodeURIComponent(rb)}`);
|
|
85
|
-
if (res.status === 404) { ctx.skip(); return; }
|
|
87
|
+
if (res.status === 404) { ctx.skip(); return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam, prior step, or fixture unavailable)'); }
|
|
86
88
|
expect(res.status).toBe(200);
|
|
87
89
|
|
|
88
90
|
const body = res.json as DiffResponse;
|
|
89
91
|
expect(
|
|
90
92
|
typeof body.divergedAtSeq === 'number' && body.divergedAtSeq >= 0,
|
|
91
|
-
|
|
93
|
+
req('openwop.it.run-diff.diffing-two-structurally-different-runs-non-null-divergedatseq-aligned-to-eventd', 'RFCS/0054 §C', 'structurally-different runs MUST report a non-null integer divergedAtSeq'),
|
|
92
94
|
).toBe(true);
|
|
93
|
-
expect(body.eventDiffs.length, 'divergent runs MUST report at least one eventDiff').toBeGreaterThan(0);
|
|
95
|
+
expect(body.eventDiffs.length, req('openwop.it.run-diff.diffing-two-structurally-different-runs-non-null-divergedatseq-aligned-to-eventd', 'RFCS/0054 §C', 'divergent runs MUST report at least one eventDiff')).toBeGreaterThan(0);
|
|
94
96
|
expect(
|
|
95
97
|
body.eventDiffs[0]?.seq,
|
|
96
|
-
|
|
98
|
+
req('openwop.it.run-diff.diffing-two-structurally-different-runs-non-null-divergedatseq-aligned-to-eventd', 'RFCS/0054 §C', 'eventDiffs MUST begin at divergedAtSeq'),
|
|
97
99
|
).toBe(body.divergedAtSeq);
|
|
98
100
|
for (const d of body.eventDiffs) {
|
|
99
101
|
expect(['added', 'removed', 'changed']).toContain(d.op);
|
|
@@ -102,42 +104,42 @@ describe.skipIf(HTTP_SKIP)('run-diff: GET /v1/runs/{runId}:diff (RFC 0054)', ()
|
|
|
102
104
|
|
|
103
105
|
it('response conforms to run-diff-response.schema.json and stateDiff is redaction-safe', async (ctx) => {
|
|
104
106
|
const [ra, rb] = await Promise.all([createRun(FIXTURE_A), createRun(FIXTURE_B)]);
|
|
105
|
-
if (!ra || !rb) { ctx.skip(); return; }
|
|
107
|
+
if (!ra || !rb) { ctx.skip(); return softSkip('blocked', 'precondition not met — `!ra || !rb` returned early (seam, prior step, or fixture unavailable)'); }
|
|
106
108
|
await Promise.all([settle(ra), settle(rb)]);
|
|
107
109
|
|
|
108
110
|
const res = await driver.get(`/v1/runs/${encodeURIComponent(ra)}:diff?against=${encodeURIComponent(rb)}`);
|
|
109
|
-
if (res.status === 404) { ctx.skip(); return; }
|
|
111
|
+
if (res.status === 404) { ctx.skip(); return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam, prior step, or fixture unavailable)'); }
|
|
110
112
|
expect(res.status).toBe(200);
|
|
111
113
|
|
|
112
114
|
const body = res.json as DiffResponse;
|
|
113
|
-
expect(typeof body.a === 'string' && typeof body.b === 'string', 'a + b MUST be strings').toBe(true);
|
|
114
|
-
expect(Array.isArray(body.eventDiffs), 'eventDiffs MUST be an array').toBe(true);
|
|
115
|
-
expect(body.stateDiff !== null && typeof body.stateDiff === 'object', 'stateDiff MUST be an object').toBe(true);
|
|
115
|
+
expect(typeof body.a === 'string' && typeof body.b === 'string', req('openwop.it.run-diff.response-conforms-to-run-diff-response-schema-json-and-statediff-is-redaction-sa', 'RFCS/0054 §B', 'a + b MUST be strings')).toBe(true);
|
|
116
|
+
expect(Array.isArray(body.eventDiffs), req('openwop.it.run-diff.response-conforms-to-run-diff-response-schema-json-and-statediff-is-redaction-sa', 'RFCS/0054 §B', 'eventDiffs MUST be an array')).toBe(true);
|
|
117
|
+
expect(body.stateDiff !== null && typeof body.stateDiff === 'object', req('openwop.it.run-diff.response-conforms-to-run-diff-response-schema-json-and-statediff-is-redaction-sa', 'RFCS/0054 §B', 'stateDiff MUST be an object')).toBe(true);
|
|
116
118
|
// Redaction-safe: no credential-shaped material leaks into the diff.
|
|
117
119
|
const serialized = JSON.stringify(body.stateDiff);
|
|
118
120
|
expect(
|
|
119
121
|
/sk-|api[_-]?key|secret|bearer\s/i.test(serialized),
|
|
120
|
-
|
|
122
|
+
req('openwop.it.run-diff.response-conforms-to-run-diff-response-schema-json-and-statediff-is-redaction-sa', 'RFCS/0054 §B', 'stateDiff MUST be redaction-safe — no credential material'),
|
|
121
123
|
).toBe(false);
|
|
122
124
|
});
|
|
123
125
|
|
|
124
126
|
it('missing `against` ⇒ 400; nonexistent `against` ⇒ 404 (access boundary)', async (ctx) => {
|
|
125
127
|
const runId = await createRun(FIXTURE_A);
|
|
126
|
-
if (!runId) { ctx.skip(); return; }
|
|
128
|
+
if (!runId) { ctx.skip(); return softSkip('blocked', 'precondition not met — `!runId` returned early (seam, prior step, or fixture unavailable)'); }
|
|
127
129
|
|
|
128
130
|
const probe = await driver.get(`/v1/runs/${encodeURIComponent(runId)}:diff?against=${encodeURIComponent(runId)}`);
|
|
129
|
-
if (probe.status === 404) { ctx.skip(); return; } // endpoint not implemented at all
|
|
131
|
+
if (probe.status === 404) { ctx.skip(); return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (endpoint not implemented at all) (seam, prior step, or fixture unavailable)'); } // endpoint not implemented at all
|
|
130
132
|
|
|
131
133
|
const missing = await driver.get(`/v1/runs/${encodeURIComponent(runId)}:diff`);
|
|
132
134
|
expect(
|
|
133
135
|
missing.status,
|
|
134
|
-
|
|
136
|
+
req('openwop.it.run-diff.missing-against-400-nonexistent-against-404-access-boundary', 'api/openapi.yaml §diffRun', 'missing required `against` query param MUST return 400'),
|
|
135
137
|
).toBe(400);
|
|
136
138
|
|
|
137
139
|
const nonexistent = await driver.get(`/v1/runs/${encodeURIComponent(runId)}:diff?against=does-not-exist-${Date.now()}`);
|
|
138
140
|
expect(
|
|
139
141
|
nonexistent.status,
|
|
140
|
-
|
|
142
|
+
req('openwop.it.run-diff.missing-against-400-nonexistent-against-404-access-boundary', 'RFCS/0054 §A', 'diffing against a run the caller cannot read/that does not exist MUST NOT return 200'),
|
|
141
143
|
).toBe(404);
|
|
142
144
|
});
|
|
143
145
|
});
|
|
@@ -32,6 +32,8 @@ import { driver } from '../lib/driver.js';
|
|
|
32
32
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
33
33
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
34
34
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
35
|
+
import { req } from '../lib/requirement-ids.js';
|
|
36
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
35
37
|
|
|
36
38
|
interface DiscoveryLimits {
|
|
37
39
|
maxRunDurationMs?: number;
|
|
@@ -59,10 +61,10 @@ async function readLimits(): Promise<DiscoveryLimits | null> {
|
|
|
59
61
|
describe('run-execution-bounds-shape: advertisement shape (RFC 0058)', () => {
|
|
60
62
|
it('maxRunDurationMs is an integer >= 1000 when present', async () => {
|
|
61
63
|
const limits = await readLimits();
|
|
62
|
-
if (limits?.maxRunDurationMs === undefined) return; // not advertised
|
|
64
|
+
if (limits?.maxRunDurationMs === undefined) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `limits?.maxRunDurationMs === undefined` returned early (not advertised)'); // not advertised
|
|
63
65
|
expect(
|
|
64
66
|
Number.isInteger(limits.maxRunDurationMs) && limits.maxRunDurationMs >= 1000,
|
|
65
|
-
|
|
67
|
+
req('openwop.it.run-execution-bounds-shape.maxrundurationms-is-an-integer-1000-when-present',
|
|
66
68
|
'capabilities.schema.json §limits.maxRunDurationMs',
|
|
67
69
|
`capabilities.limits.maxRunDurationMs MUST be an integer >= 1000, got: ${limits.maxRunDurationMs}`,
|
|
68
70
|
),
|
|
@@ -71,10 +73,10 @@ describe('run-execution-bounds-shape: advertisement shape (RFC 0058)', () => {
|
|
|
71
73
|
|
|
72
74
|
it('maxLoopIterations is an integer >= 1 when present', async () => {
|
|
73
75
|
const limits = await readLimits();
|
|
74
|
-
if (limits?.maxLoopIterations === undefined) return; // not advertised
|
|
76
|
+
if (limits?.maxLoopIterations === undefined) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `limits?.maxLoopIterations === undefined` returned early (not advertised)'); // not advertised
|
|
75
77
|
expect(
|
|
76
78
|
Number.isInteger(limits.maxLoopIterations) && limits.maxLoopIterations >= 1,
|
|
77
|
-
|
|
79
|
+
req('openwop.it.run-execution-bounds-shape.maxloopiterations-is-an-integer-1-when-present',
|
|
78
80
|
'capabilities.schema.json §limits.maxLoopIterations',
|
|
79
81
|
`capabilities.limits.maxLoopIterations MUST be an integer >= 1, got: ${limits.maxLoopIterations}`,
|
|
80
82
|
),
|
|
@@ -92,18 +94,18 @@ describe.skipIf(SKIP_TIMEOUT)('run-execution-bounds: run-duration breach (RFC 00
|
|
|
92
94
|
workflowId: TIMEOUT_FIXTURE,
|
|
93
95
|
configurable: { runTimeoutMs: 1000 },
|
|
94
96
|
});
|
|
95
|
-
expect(create.status,
|
|
97
|
+
expect(create.status, req('openwop.it.run-execution-bounds-shape.a-run-with-runtimeoutms-below-its-real-duration-fails-with-run-timeout-cap-breac',
|
|
96
98
|
'rest-endpoints.md POST /v1/runs',
|
|
97
99
|
'run creation MUST accept a runTimeoutMs override',
|
|
98
100
|
)).toBe(201);
|
|
99
101
|
const runId = (create.json as { runId: string }).runId;
|
|
100
102
|
|
|
101
103
|
const terminal = await pollUntilTerminal(runId);
|
|
102
|
-
expect(terminal.status,
|
|
104
|
+
expect(terminal.status, req('openwop.it.run-execution-bounds-shape.a-run-with-runtimeoutms-below-its-real-duration-fails-with-run-timeout-cap-breac',
|
|
103
105
|
'run-options.md §runTimeoutMs',
|
|
104
106
|
'a run exceeding its runTimeoutMs MUST reach terminal `failed`',
|
|
105
107
|
)).toBe('failed');
|
|
106
|
-
expect(terminal.error?.code,
|
|
108
|
+
expect(terminal.error?.code, req('openwop.it.run-execution-bounds-shape.a-run-with-runtimeoutms-below-its-real-duration-fails-with-run-timeout-cap-breac',
|
|
107
109
|
'rest-endpoints.md §run_timeout',
|
|
108
110
|
'RunSnapshot.error.code MUST equal "run_timeout" on wall-clock timeout',
|
|
109
111
|
)).toBe('run_timeout');
|
|
@@ -113,12 +115,12 @@ describe.skipIf(SKIP_TIMEOUT)('run-execution-bounds: run-duration breach (RFC 00
|
|
|
113
115
|
);
|
|
114
116
|
const events = (eventsRes.json as { events?: RunEvent[] } | undefined)?.events ?? [];
|
|
115
117
|
const breach = events.find((e) => e.type === 'cap.breached');
|
|
116
|
-
expect(breach,
|
|
118
|
+
expect(breach, req('openwop.it.run-execution-bounds-shape.a-run-with-runtimeoutms-below-its-real-duration-fails-with-run-timeout-cap-breac',
|
|
117
119
|
'capabilities.md §Engine-enforced limits',
|
|
118
120
|
'a cap.breached event MUST be emitted on run-duration breach',
|
|
119
121
|
)).toBeDefined();
|
|
120
122
|
const payload = breach!.payload as { kind?: string; limit?: number; observed?: number } | undefined;
|
|
121
|
-
expect(payload?.kind,
|
|
123
|
+
expect(payload?.kind, req('openwop.it.run-execution-bounds-shape.a-run-with-runtimeoutms-below-its-real-duration-fails-with-run-timeout-cap-breac',
|
|
122
124
|
'run-event-payloads.schema.json §capBreached.kind',
|
|
123
125
|
'cap.breached payload MUST carry kind="run-duration"',
|
|
124
126
|
)).toBe('run-duration');
|
|
@@ -132,14 +134,14 @@ describe.skipIf(SKIP_TIMEOUT)('run-execution-bounds: run-duration breach (RFC 00
|
|
|
132
134
|
// not report the observed values.
|
|
133
135
|
expect(
|
|
134
136
|
typeof payload?.observed,
|
|
135
|
-
|
|
137
|
+
req('openwop.it.run-execution-bounds-shape.a-run-with-runtimeoutms-below-its-real-duration-fails-with-run-timeout-cap-breac',
|
|
136
138
|
'run-event-payloads.schema.json §capBreached.observed',
|
|
137
139
|
`cap.breached MUST carry a numeric \`observed\`; got ${JSON.stringify(payload?.observed)}`,
|
|
138
140
|
),
|
|
139
141
|
).toBe('number');
|
|
140
142
|
expect(
|
|
141
143
|
typeof payload?.limit,
|
|
142
|
-
|
|
144
|
+
req('openwop.it.run-execution-bounds-shape.a-run-with-runtimeoutms-below-its-real-duration-fails-with-run-timeout-cap-breac',
|
|
143
145
|
'run-event-payloads.schema.json §capBreached.limit',
|
|
144
146
|
`cap.breached MUST carry a numeric \`limit\`; got ${JSON.stringify(payload?.limit)}`,
|
|
145
147
|
),
|
|
@@ -159,7 +161,7 @@ describe.skipIf(SKIP_TIMEOUT)('run-execution-bounds: run-duration breach (RFC 00
|
|
|
159
161
|
const { observed = NaN, limit = NaN } = payload ?? {};
|
|
160
162
|
expect(
|
|
161
163
|
observed > limit,
|
|
162
|
-
|
|
164
|
+
req('openwop.it.run-execution-bounds-shape.a-run-with-runtimeoutms-below-its-real-duration-fails-with-run-timeout-cap-breac',
|
|
163
165
|
'run-event-payloads.schema.json §capBreached.observed',
|
|
164
166
|
`observed (elapsedMs) MUST be strictly greater than limit (resolved timeout). ` +
|
|
165
167
|
`Got observed=${observed}, limit=${limit}` +
|
|
@@ -40,6 +40,8 @@ import { driver } from '../lib/driver.js';
|
|
|
40
40
|
import { loadEnv } from '../lib/env.js';
|
|
41
41
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
42
42
|
import { pollUntilStatus, pollUntilTerminal } from '../lib/polling.js';
|
|
43
|
+
import { req } from '../lib/requirement-ids.js';
|
|
44
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
43
45
|
|
|
44
46
|
const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
|
|
45
47
|
const APPROVAL_FIXTURE = 'conformance-approval';
|
|
@@ -80,7 +82,7 @@ describe.skipIf(HTTP_SKIP)('run-transport-economy: conditional GET on run reads
|
|
|
80
82
|
const caps = await readRestTransport();
|
|
81
83
|
if (caps?.conditionalRunGet !== true) {
|
|
82
84
|
ctx.skip(); // host does not advertise restTransport.conditionalRunGet
|
|
83
|
-
return;
|
|
85
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `caps?.conditionalRunGet !== true` returned early');
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
// Use the approval fixture: it parks at a stable `waiting-approval` state,
|
|
@@ -88,7 +90,7 @@ describe.skipIf(HTTP_SKIP)('run-transport-economy: conditional GET on run reads
|
|
|
88
90
|
const create = await driver.post('/v1/runs', { workflowId: APPROVAL_FIXTURE });
|
|
89
91
|
if (create.status === 404 || create.status === 422) {
|
|
90
92
|
ctx.skip(); // fixture not advertised by this host
|
|
91
|
-
return;
|
|
93
|
+
return softSkip('blocked', 'precondition not met — `create.status === 404 || create.status === 422` returned early (seam, prior step, or fixture unavailable)');
|
|
92
94
|
}
|
|
93
95
|
expect(create.status).toBe(201);
|
|
94
96
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -100,7 +102,7 @@ describe.skipIf(HTTP_SKIP)('run-transport-economy: conditional GET on run reads
|
|
|
100
102
|
const etagSuspended = etagOf(suspendedRead);
|
|
101
103
|
expect(
|
|
102
104
|
etagSuspended,
|
|
103
|
-
|
|
105
|
+
req('openwop.it.run-transport-economy.emits-a-sequence-derived-strong-etag-honors-if-none-match-with-304-and-rotates-t',
|
|
104
106
|
'rest-endpoints.md §GET /v1/runs/{runId} conditional read (RFC 0115)',
|
|
105
107
|
'a host advertising restTransport.conditionalRunGet MUST return a strong ETag on the 200',
|
|
106
108
|
),
|
|
@@ -112,14 +114,14 @@ describe.skipIf(HTTP_SKIP)('run-transport-economy: conditional GET on run reads
|
|
|
112
114
|
});
|
|
113
115
|
expect(
|
|
114
116
|
revalidate.status,
|
|
115
|
-
|
|
117
|
+
req('openwop.it.run-transport-economy.emits-a-sequence-derived-strong-etag-honors-if-none-match-with-304-and-rotates-t',
|
|
116
118
|
'rest-endpoints.md §GET /v1/runs/{runId} conditional read (RFC 0115)',
|
|
117
119
|
'If-None-Match matching the current ETag MUST return 304 Not Modified',
|
|
118
120
|
),
|
|
119
121
|
).toBe(304);
|
|
120
122
|
expect(
|
|
121
123
|
revalidate.text,
|
|
122
|
-
|
|
124
|
+
req('openwop.it.run-transport-economy.emits-a-sequence-derived-strong-etag-honors-if-none-match-with-304-and-rotates-t',
|
|
123
125
|
'rest-endpoints.md §GET /v1/runs/{runId} conditional read (RFC 0115)',
|
|
124
126
|
'304 Not Modified MUST carry no body',
|
|
125
127
|
),
|
|
@@ -141,7 +143,7 @@ describe.skipIf(HTTP_SKIP)('run-transport-economy: conditional GET on run reads
|
|
|
141
143
|
expect(etagCompleted).toBeTruthy();
|
|
142
144
|
expect(
|
|
143
145
|
etagCompleted,
|
|
144
|
-
|
|
146
|
+
req('openwop.it.run-transport-economy.emits-a-sequence-derived-strong-etag-honors-if-none-match-with-304-and-rotates-t',
|
|
145
147
|
'rest-endpoints.md §GET /v1/runs/{runId} conditional read (RFC 0115)',
|
|
146
148
|
'the ETag MUST change once the run advances (it is derived from the latest event-log sequence); a stable ETag across an observable transition would leave a 304 stale',
|
|
147
149
|
),
|
|
@@ -153,7 +155,7 @@ describe.skipIf(HTTP_SKIP)('run-transport-economy: conditional GET on run reads
|
|
|
153
155
|
});
|
|
154
156
|
expect(
|
|
155
157
|
revalidateTerminal.status,
|
|
156
|
-
|
|
158
|
+
req('openwop.it.run-transport-economy.emits-a-sequence-derived-strong-etag-honors-if-none-match-with-304-and-rotates-t',
|
|
157
159
|
'rest-endpoints.md §GET /v1/runs/{runId} conditional read (RFC 0115)',
|
|
158
160
|
'the terminal ETag MUST be stable — If-None-Match against it returns 304',
|
|
159
161
|
),
|
|
@@ -167,14 +169,14 @@ describe.skipIf(HTTP_SKIP)('run-transport-economy: Content-Encoding round-trips
|
|
|
167
169
|
const encodings = advertisedEncodings(caps);
|
|
168
170
|
if (encodings.length === 0) {
|
|
169
171
|
ctx.skip(); // host advertises no run-read content encodings
|
|
170
|
-
return;
|
|
172
|
+
return softSkip('blocked', 'precondition not met — `encodings.length === 0` returned early (seam, prior step, or fixture unavailable)');
|
|
171
173
|
}
|
|
172
174
|
|
|
173
175
|
// A terminal run gives a stable body to compare encodings against.
|
|
174
176
|
const create = await driver.post('/v1/runs', { workflowId: NOOP_FIXTURE });
|
|
175
177
|
if (create.status === 404 || create.status === 422) {
|
|
176
178
|
ctx.skip();
|
|
177
|
-
return;
|
|
179
|
+
return softSkip('blocked', 'precondition not met — `create.status === 404 || create.status === 422` returned early (seam, prior step, or fixture unavailable)');
|
|
178
180
|
}
|
|
179
181
|
expect(create.status).toBe(201);
|
|
180
182
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -219,7 +221,7 @@ describe.skipIf(HTTP_SKIP)('run-transport-economy: Content-Encoding round-trips
|
|
|
219
221
|
const contentEncoding = res.headers.get('content-encoding');
|
|
220
222
|
expect(
|
|
221
223
|
contentEncoding,
|
|
222
|
-
|
|
224
|
+
req('openwop.it.run-transport-economy.each-advertised-contentencodings-value-decodes-to-the-identity-body-byte-for-byt',
|
|
223
225
|
'rest-endpoints.md §GET /v1/runs/{runId} conditional read + Content-Encoding (RFC 0115)',
|
|
224
226
|
`a host advertising restTransport.contentEncodings:["...","${enc}"] MUST set Content-Encoding: ${enc} when that encoding is requested`,
|
|
225
227
|
),
|
|
@@ -249,7 +251,7 @@ describe.skipIf(HTTP_SKIP)('run-transport-economy: Content-Encoding round-trips
|
|
|
249
251
|
}
|
|
250
252
|
expect(
|
|
251
253
|
decoded.equals(identityBytes),
|
|
252
|
-
|
|
254
|
+
req('openwop.it.run-transport-economy.each-advertised-contentencodings-value-decodes-to-the-identity-body-byte-for-byt',
|
|
253
255
|
'rest-endpoints.md §GET /v1/runs/{runId} conditional read + Content-Encoding (RFC 0115)',
|
|
254
256
|
`the ${enc}-decoded body MUST be byte-identical to the identity body (Content-Encoding MUST NOT alter decoded bytes or semantics)`,
|
|
255
257
|
),
|