@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
|
@@ -60,6 +60,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
60
60
|
import { driver } from '../lib/driver.js';
|
|
61
61
|
import { semanticRequestDigestV2, callCacheKeySeam as callSeam } from '../lib/llm-cache-key-recipe.js';
|
|
62
62
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
63
|
+
import { req } from '../lib/requirement-ids.js';
|
|
64
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
63
65
|
|
|
64
66
|
const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
|
|
65
67
|
|
|
@@ -99,12 +101,12 @@ describe.skipIf(HTTP_SKIP)('replay-llm-cache-key-portable: intra-host reproducib
|
|
|
99
101
|
const result = await callSeam(input);
|
|
100
102
|
if (result.status === 404) {
|
|
101
103
|
ctx.skip(); // host doesn't expose the test seam
|
|
102
|
-
return;
|
|
104
|
+
return softSkip('blocked', 'precondition not met — `result.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
103
105
|
}
|
|
104
106
|
expect(result.status).toBe(200);
|
|
105
107
|
expect(
|
|
106
108
|
result.cacheKey,
|
|
107
|
-
|
|
109
|
+
req('openwop.it.replay-llm-cache-key-portable.host-cache-key-must-equal-locally-recomputed-sha-256-over-canonical-json-reprodu',
|
|
108
110
|
'SECURITY/invariants.yaml §replay-llm-cache-key-portable + replay.md §B',
|
|
109
111
|
'host cache key MUST be reproducible offline from the recipe alone — no host-internal state',
|
|
110
112
|
),
|
|
@@ -121,12 +123,12 @@ describe.skipIf(HTTP_SKIP)('replay-llm-cache-key-portable: intra-host reproducib
|
|
|
121
123
|
const a = await callSeam(input);
|
|
122
124
|
if (a.status === 404) {
|
|
123
125
|
ctx.skip(); // host doesn't expose the test seam
|
|
124
|
-
return;
|
|
126
|
+
return softSkip('blocked', 'precondition not met — `a.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
125
127
|
}
|
|
126
128
|
const b = await callSeam(input);
|
|
127
129
|
expect(
|
|
128
130
|
a.cacheKey,
|
|
129
|
-
|
|
131
|
+
req('openwop.it.replay-llm-cache-key-portable.two-identical-probes-must-yield-byte-identical-keys-intra-host-determinism',
|
|
130
132
|
'SECURITY/invariants.yaml §replay-llm-cache-key-portable',
|
|
131
133
|
'two byte-identical recipe inputs MUST yield byte-identical keys (no per-request entropy)',
|
|
132
134
|
),
|
|
@@ -145,7 +147,7 @@ describe.skipIf(HTTP_SKIP)('replay-llm-cache-key-portable: non-recipe-field inva
|
|
|
145
147
|
const baseResult = await callSeam(base);
|
|
146
148
|
if (baseResult.status === 404) {
|
|
147
149
|
ctx.skip(); // host doesn't expose the test seam
|
|
148
|
-
return;
|
|
150
|
+
return softSkip('blocked', 'precondition not met — `baseResult.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
149
151
|
}
|
|
150
152
|
|
|
151
153
|
// The security boundary: ANY of these fields leaking into the key
|
|
@@ -163,7 +165,7 @@ describe.skipIf(HTTP_SKIP)('replay-llm-cache-key-portable: non-recipe-field inva
|
|
|
163
165
|
const pollutedResult = await callSeam(polluted);
|
|
164
166
|
expect(
|
|
165
167
|
pollutedResult.cacheKey,
|
|
166
|
-
|
|
168
|
+
req('openwop.it.replay-llm-cache-key-portable.transport-only-fields-request-id-trace-context-tenant-id-must-not-influence-the',
|
|
167
169
|
'SECURITY/invariants.yaml §replay-llm-cache-key-portable + replay.md §A',
|
|
168
170
|
'transport-only fields (request id, trace context, tenant id, run id, timeout) MUST NOT influence the cache key — leaking them defeats the portability invariant',
|
|
169
171
|
),
|
|
@@ -178,13 +180,13 @@ describe.skipIf(HTTP_SKIP)('replay-llm-cache-key-portable: Phase 4 advertisement
|
|
|
178
180
|
const version = em?.version;
|
|
179
181
|
if (typeof version !== 'number' || version < 4) {
|
|
180
182
|
ctx.skip(); // pre-Phase-4 or no multiAgent advertisement
|
|
181
|
-
return;
|
|
183
|
+
return softSkip('blocked', 'precondition not met — `typeof version !== \'number\' || version < 4` returned early (seam, prior step, or fixture unavailable)');
|
|
182
184
|
}
|
|
183
185
|
|
|
184
186
|
const recipe = em?.replayDeterminism?.llmCacheKeyRecipe;
|
|
185
187
|
expect(
|
|
186
188
|
typeof recipe === 'string',
|
|
187
|
-
|
|
189
|
+
req('openwop.it.replay-llm-cache-key-portable.hosts-advertising-version-4-must-advertise-replaydeterminism-llmcachekeyrecipe',
|
|
188
190
|
'RFCS/0041-multi-agent-replay-under-nondeterminism.md §D',
|
|
189
191
|
'Phase 4 host MUST advertise replayDeterminism.llmCacheKeyRecipe (`spec-rfc-0041` or `x-host-<host>-<recipe>`)',
|
|
190
192
|
),
|
|
@@ -195,7 +197,7 @@ describe.skipIf(HTTP_SKIP)('replay-llm-cache-key-portable: Phase 4 advertisement
|
|
|
195
197
|
const vendor = /^x-host-[a-z][a-z0-9-]*-[a-z][a-z0-9-]*$/.test(r);
|
|
196
198
|
expect(
|
|
197
199
|
canonical || vendor,
|
|
198
|
-
|
|
200
|
+
req('openwop.it.replay-llm-cache-key-portable.hosts-advertising-version-4-must-advertise-replaydeterminism-llmcachekeyrecipe',
|
|
199
201
|
'schemas/capabilities.schema.json §replayDeterminism.llmCacheKeyRecipe',
|
|
200
202
|
'llmCacheKeyRecipe MUST be `spec-rfc-0041` OR match `^x-host-<host>-<recipe>$` per host-extensions.md',
|
|
201
203
|
),
|
|
@@ -28,8 +28,9 @@
|
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
import { describe, it, expect } from 'vitest';
|
|
31
|
-
import { driver } from '../lib/driver.js';
|
|
32
31
|
import { semanticRequestDigestV2, callCacheKeySeam as callSeam } from '../lib/llm-cache-key-recipe.js';
|
|
32
|
+
import { req } from '../lib/requirement-ids.js';
|
|
33
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
33
34
|
|
|
34
35
|
describe('replay-llm-cache-key: SHA-256-over-JCS recipe, v2 (replay.md §B, RFC 0150 §C)', () => {
|
|
35
36
|
it('host cache key MUST equal the locally-recomputed v2 semantic-request digest', async () => {
|
|
@@ -43,11 +44,11 @@ describe('replay-llm-cache-key: SHA-256-over-JCS recipe, v2 (replay.md §B, RFC
|
|
|
43
44
|
temperature: 0.7,
|
|
44
45
|
};
|
|
45
46
|
const result = await callSeam(input);
|
|
46
|
-
if (result.status === 404) return; // seam not exposed
|
|
47
|
+
if (result.status === 404) return softSkip('blocked', 'precondition not met — `result.status === 404` returned early (seam not exposed) (seam, prior step, or fixture unavailable)'); // seam not exposed
|
|
47
48
|
expect(result.status).toBe(200);
|
|
48
49
|
expect(
|
|
49
50
|
result.cacheKey,
|
|
50
|
-
|
|
51
|
+
req('openwop.it.replay-llm-cache-key.host-cache-key-must-equal-the-locally-recomputed-v2-semantic-request-digest',
|
|
51
52
|
'replay.md §B (RFC 0150 §C)',
|
|
52
53
|
'host cache key MUST be lowercase-hex SHA-256 of the JCS-canonical v2 semantic request ' +
|
|
53
54
|
'(stamped `recipe: "openwop-semantic-request-v2"`); a v1 key here is a host that has ' +
|
|
@@ -62,8 +63,8 @@ describe('replay-llm-cache-key: SHA-256-over-JCS recipe, v2 (replay.md §B, RFC
|
|
|
62
63
|
model: 'gpt-4',
|
|
63
64
|
messages: [{ role: 'user', content: 'hi' }],
|
|
64
65
|
});
|
|
65
|
-
if (result.status === 404) return;
|
|
66
|
-
expect(result.cacheKey).toMatch(/^[0-9a-f]{64}$/);
|
|
66
|
+
if (result.status === 404) return softSkip('blocked', 'precondition not met — `result.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
67
|
+
expect(result.cacheKey, req('openwop.it.replay-llm-cache-key.cache-key-must-be-64-lowercase-hex-characters-sha-256-output-shape', 'replay.md §"LLM cache-key recipe', 'cache key MUST be 64 lowercase-hex characters (SHA-256 output shape)')).toMatch(/^[0-9a-f]{64}$/);
|
|
67
68
|
});
|
|
68
69
|
});
|
|
69
70
|
|
|
@@ -76,7 +77,7 @@ describe('replay-llm-cache-key: transport-only fields are EXCLUDED (replay.md §
|
|
|
76
77
|
temperature: 0.5,
|
|
77
78
|
};
|
|
78
79
|
const baseResult = await callSeam(base);
|
|
79
|
-
if (baseResult.status === 404) return;
|
|
80
|
+
if (baseResult.status === 404) return softSkip('blocked', 'precondition not met — `baseResult.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
80
81
|
|
|
81
82
|
// Transport / bookkeeping only. None of these can change what the model
|
|
82
83
|
// returns, so none may change the key. (`max_tokens`, `stop`, and `seed`
|
|
@@ -95,7 +96,7 @@ describe('replay-llm-cache-key: transport-only fields are EXCLUDED (replay.md §
|
|
|
95
96
|
const noisyResult = await callSeam(noisy);
|
|
96
97
|
expect(
|
|
97
98
|
noisyResult.cacheKey,
|
|
98
|
-
|
|
99
|
+
req('openwop.it.replay-llm-cache-key.stream-metadata-user-request-ids-trace-context-must-not-influence-the-cache-key',
|
|
99
100
|
'replay.md §A (v2)',
|
|
100
101
|
'cache key MUST be invariant under transport-only fields (stream, metadata, user, request ids, trace context, tenant, timeout)',
|
|
101
102
|
),
|
|
@@ -111,11 +112,11 @@ describe('replay-llm-cache-key: transport-only fields are EXCLUDED (replay.md §
|
|
|
111
112
|
};
|
|
112
113
|
const hotInput = { ...baseInput, temperature: 1.0 };
|
|
113
114
|
const baseResult = await callSeam(baseInput);
|
|
114
|
-
if (baseResult.status === 404) return;
|
|
115
|
+
if (baseResult.status === 404) return softSkip('blocked', 'precondition not met — `baseResult.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
115
116
|
const hotResult = await callSeam(hotInput);
|
|
116
117
|
expect(
|
|
117
118
|
baseResult.cacheKey === hotResult.cacheKey,
|
|
118
|
-
|
|
119
|
+
req('openwop.it.replay-llm-cache-key.changing-a-recipe-field-temperature-must-yield-a-different-cache-key', 'replay.md §A', 'changing a recipe field MUST yield a different cache key (no false collisions)'),
|
|
119
120
|
).toBe(false);
|
|
120
121
|
});
|
|
121
122
|
});
|
|
@@ -139,11 +140,11 @@ describe('replay-llm-cache-key: outcome-affecting fields are INCLUDED (RFC 0150
|
|
|
139
140
|
['maxOutputTokens', { maxOutputTokens: 100 }, { maxOutputTokens: 1000 }],
|
|
140
141
|
])('%s DOES change the cache key', async (_field, a, b) => {
|
|
141
142
|
const ra = await callSeam({ ...base, ...a });
|
|
142
|
-
if (ra.status === 404) return;
|
|
143
|
+
if (ra.status === 404) return softSkip('blocked', 'precondition not met — `ra.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
143
144
|
const rb = await callSeam({ ...base, ...b });
|
|
144
145
|
expect(
|
|
145
146
|
ra.cacheKey === rb.cacheKey,
|
|
146
|
-
|
|
147
|
+
req('openwop.it.replay-llm-cache-key.s-does-change-the-cache-key',
|
|
147
148
|
'replay.md §A (RFC 0150 §C)',
|
|
148
149
|
`\`${_field}\` decides the completion, so two requests differing only in it MUST NOT share a cache key — ` +
|
|
149
150
|
'sharing one returns a response the caller never asked for, deterministically',
|
|
@@ -151,10 +152,10 @@ describe('replay-llm-cache-key: outcome-affecting fields are INCLUDED (RFC 0150
|
|
|
151
152
|
).toBe(false);
|
|
152
153
|
// And each side MUST equal the locally-recomputed v2 digest, so a host cannot
|
|
153
154
|
// pass this leg by salting the key with something arbitrary.
|
|
154
|
-
expect(ra.cacheKey,
|
|
155
|
+
expect(ra.cacheKey, req('openwop.it.replay-llm-cache-key.s-does-change-the-cache-key', 'replay.md §B (v2)', 'key MUST equal the v2 digest')).toBe(
|
|
155
156
|
semanticRequestDigestV2({ ...base, ...a }),
|
|
156
157
|
);
|
|
157
|
-
expect(rb.cacheKey,
|
|
158
|
+
expect(rb.cacheKey, req('openwop.it.replay-llm-cache-key.s-does-change-the-cache-key', 'replay.md §B (v2)', 'key MUST equal the v2 digest')).toBe(
|
|
158
159
|
semanticRequestDigestV2({ ...base, ...b }),
|
|
159
160
|
);
|
|
160
161
|
});
|
|
@@ -163,17 +164,17 @@ describe('replay-llm-cache-key: outcome-affecting fields are INCLUDED (RFC 0150
|
|
|
163
164
|
const withOpts = { ...base, providerOptions: { 'vendor.openai.logitBias': { '50256': -100 } } };
|
|
164
165
|
const withOtherOpts = { ...base, providerOptions: { 'vendor.openai.logitBias': { '50256': 100 } } };
|
|
165
166
|
const r1 = await callSeam(withOpts);
|
|
166
|
-
if (r1.status === 404) return;
|
|
167
|
+
if (r1.status === 404) return softSkip('blocked', 'precondition not met — `r1.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
167
168
|
const r2 = await callSeam(withOtherOpts);
|
|
168
169
|
expect(
|
|
169
170
|
r1.cacheKey === r2.cacheKey,
|
|
170
|
-
|
|
171
|
+
req('openwop.it.replay-llm-cache-key.provideroptions-are-carried-into-the-key-not-dropped',
|
|
171
172
|
'replay.md §B step 1 (RFC 0150 §C)',
|
|
172
173
|
'an unknown provider option MUST be carried into `providerOptions` before hashing — a dropped option ' +
|
|
173
174
|
'that alters output is indistinguishable from one that was never set',
|
|
174
175
|
),
|
|
175
176
|
).toBe(false);
|
|
176
|
-
expect(r1.cacheKey,
|
|
177
|
+
expect(r1.cacheKey, req('openwop.it.replay-llm-cache-key.provideroptions-are-carried-into-the-key-not-dropped', 'replay.md §B (v2)', 'key MUST equal the v2 digest')).toBe(
|
|
177
178
|
semanticRequestDigestV2(withOpts),
|
|
178
179
|
);
|
|
179
180
|
});
|
|
@@ -182,7 +183,7 @@ describe('replay-llm-cache-key: outcome-affecting fields are INCLUDED (RFC 0150
|
|
|
182
183
|
describe('replay-llm-cache-key: cross-host parity (replay.md §D)', () => {
|
|
183
184
|
it('two hosts compute the same cache key for the same input (when OPENWOP_BASE_URL_B is configured)', async () => {
|
|
184
185
|
const otherBaseUrl = process.env['OPENWOP_BASE_URL_B'];
|
|
185
|
-
if (!otherBaseUrl) return; // operator-supplied second host not configured
|
|
186
|
+
if (!otherBaseUrl) return softSkip('blocked', 'precondition not met — `!otherBaseUrl` returned early (operator-supplied second host not configured) (seam, prior step, or fixture unavailable)'); // operator-supplied second host not configured
|
|
186
187
|
const input = {
|
|
187
188
|
provider: 'openai',
|
|
188
189
|
model: 'gpt-4',
|
|
@@ -191,19 +192,19 @@ describe('replay-llm-cache-key: cross-host parity (replay.md §D)', () => {
|
|
|
191
192
|
seed: 7,
|
|
192
193
|
};
|
|
193
194
|
const resA = await callSeam(input);
|
|
194
|
-
if (resA.status === 404) return;
|
|
195
|
+
if (resA.status === 404) return softSkip('blocked', 'precondition not met — `resA.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
195
196
|
const resB = await fetch(`${otherBaseUrl.replace(/\/$/, '')}/v1/host/sample/test/llm-cache-key`, {
|
|
196
197
|
method: 'POST',
|
|
197
198
|
headers: { 'content-type': 'application/json' },
|
|
198
199
|
body: JSON.stringify(input),
|
|
199
200
|
});
|
|
200
|
-
if (resB.status === 404) return;
|
|
201
|
+
if (resB.status === 404) return softSkip('blocked', 'precondition not met — `resB.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
201
202
|
const jsonB = (await resB.json()) as { cacheKey?: string };
|
|
202
203
|
expect(
|
|
203
204
|
jsonB.cacheKey,
|
|
204
|
-
|
|
205
|
+
req('openwop.it.replay-llm-cache-key.two-hosts-compute-the-same-cache-key-for-the-same-input-when-openwop-base-url-b', 'replay.md §D', 'two compliant hosts MUST compute byte-identical cache keys for the same recipe input'),
|
|
205
206
|
).toBe(resA.cacheKey);
|
|
206
|
-
expect(resA.cacheKey,
|
|
207
|
+
expect(resA.cacheKey, req('openwop.it.replay-llm-cache-key.two-hosts-compute-the-same-cache-key-for-the-same-input-when-openwop-base-url-b', 'replay.md §B (v2)', 'and both MUST equal the v2 digest')).toBe(
|
|
207
208
|
semanticRequestDigestV2(input),
|
|
208
209
|
);
|
|
209
210
|
});
|
|
@@ -44,6 +44,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
44
44
|
import { softSkip, seamAbsent } from '../lib/soft-skip.js';
|
|
45
45
|
import { driver } from '../lib/driver.js';
|
|
46
46
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
47
|
+
import { req } from '../lib/requirement-ids.js';
|
|
47
48
|
|
|
48
49
|
const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
|
|
49
50
|
const FIXTURE = 'conformance-phase4-nondet-tool';
|
|
@@ -186,7 +187,7 @@ describe.skipIf(HTTP_SKIP)(
|
|
|
186
187
|
const replayNorm = replayEvents.map(stripVolatile);
|
|
187
188
|
expect(
|
|
188
189
|
replayNorm,
|
|
189
|
-
|
|
190
|
+
req('openwop.it.replay-observable-sequence-determinism.original-and-replay-event-log-prefixes-must-be-byte-equivalent-modulo-per-event',
|
|
190
191
|
'RFCS/0041-multi-agent-replay-under-nondeterminism.md §C',
|
|
191
192
|
'a mode:replay fork MUST reproduce the original observable event-log sequence byte-for-byte modulo volatile per-event fields (eventId/ULID entropy, per-region observedAt clock)',
|
|
192
193
|
),
|
|
@@ -211,7 +212,7 @@ describe.skipIf(HTTP_SKIP)(
|
|
|
211
212
|
const sourceNodeEvents = sourceEvents.filter((e) => e.nodeId === NONDET_NODE_ID).map(stripVolatile);
|
|
212
213
|
expect(
|
|
213
214
|
sourceNodeEvents.length,
|
|
214
|
-
|
|
215
|
+
req('openwop.it.replay-observable-sequence-determinism.replay-of-a-nondeterministic-tool-node-reproduces-the-original-observable-result',
|
|
215
216
|
'RFCS/0041-multi-agent-replay-under-nondeterminism.md §C',
|
|
216
217
|
`the fixture's nondeterministic node \`${NONDET_NODE_ID}\` MUST emit at least one observable event`,
|
|
217
218
|
),
|
|
@@ -229,7 +230,7 @@ describe.skipIf(HTTP_SKIP)(
|
|
|
229
230
|
|
|
230
231
|
expect(
|
|
231
232
|
replayNodeEvents,
|
|
232
|
-
|
|
233
|
+
req('openwop.it.replay-observable-sequence-determinism.replay-of-a-nondeterministic-tool-node-reproduces-the-original-observable-result',
|
|
233
234
|
'RFCS/0041-multi-agent-replay-under-nondeterminism.md §C',
|
|
234
235
|
'the nondeterministic tool node MUST replay its ORIGINAL observable result (cached event-log entry) rather than re-executing — bit-equivalent re-execution would require unbounded caching, rejected per RFC 0041 §"Alternatives considered" #2',
|
|
235
236
|
),
|
|
@@ -33,6 +33,8 @@
|
|
|
33
33
|
import { describe, it, expect } from 'vitest';
|
|
34
34
|
import { driver } from '../lib/driver.js';
|
|
35
35
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
36
|
+
import { req } from '../lib/requirement-ids.js';
|
|
37
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
36
38
|
|
|
37
39
|
interface ReplayRetentionCaps {
|
|
38
40
|
windowSeconds?: number;
|
|
@@ -66,14 +68,14 @@ describe('replay-retention-expiry: capability shape', () => {
|
|
|
66
68
|
return;
|
|
67
69
|
}
|
|
68
70
|
|
|
69
|
-
expect(replay?.supported,
|
|
71
|
+
expect(replay?.supported, req('openwop.it.replay-retention-expiry.host-advertising-replay-surfaces-well-formed-retention-metadata-when-present',
|
|
70
72
|
'replay.md §"Retention and garbage collection"',
|
|
71
73
|
'replay.supported MUST be true when the host claims the openwop-replay-fork profile',
|
|
72
74
|
)).toBe(true);
|
|
73
75
|
|
|
74
76
|
expect(
|
|
75
77
|
Array.isArray(replay?.modes) && (replay?.modes?.length ?? 0) > 0,
|
|
76
|
-
|
|
78
|
+
req('openwop.it.replay-retention-expiry.host-advertising-replay-surfaces-well-formed-retention-metadata-when-present',
|
|
77
79
|
'profiles.md §`openwop-replay-fork`',
|
|
78
80
|
'replay.modes MUST be a non-empty array',
|
|
79
81
|
),
|
|
@@ -86,7 +88,7 @@ describe('replay-retention-expiry: capability shape', () => {
|
|
|
86
88
|
expect(
|
|
87
89
|
Number.isInteger(replay.retention.windowSeconds) &&
|
|
88
90
|
replay.retention.windowSeconds >= 0,
|
|
89
|
-
|
|
91
|
+
req('openwop.it.replay-retention-expiry.host-advertising-replay-surfaces-well-formed-retention-metadata-when-present',
|
|
90
92
|
'replay.md §"Retention and garbage collection"',
|
|
91
93
|
'replay.retention.windowSeconds MUST be a non-negative integer when advertised',
|
|
92
94
|
),
|
|
@@ -109,7 +111,7 @@ describe('replay-retention-expiry: 410/422 on expired-range fork', () => {
|
|
|
109
111
|
console.warn(
|
|
110
112
|
'[replay-retention-expiry] OPENWOP_TEST_EXPIRED_REPLAY_RUN_ID not supplied; skipping envelope assertion (operator must produce a known-expired run id and pass it via env)',
|
|
111
113
|
);
|
|
112
|
-
return;
|
|
114
|
+
return softSkip('blocked', 'precondition not met — `!expiredRunId` returned early ([replay-retention-expiry] OPENWOP_TEST_EXPIRED_REPLAY_RUN_ID not supplied; skipping envelope assertion (operator must produce a known-expired run id and pass it v…');
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
const fromSeqEnv = process.env.OPENWOP_TEST_EXPIRED_REPLAY_FROM_SEQ;
|
|
@@ -119,7 +121,7 @@ describe('replay-retention-expiry: 410/422 on expired-range fork', () => {
|
|
|
119
121
|
console.warn(
|
|
120
122
|
`[replay-retention-expiry] OPENWOP_TEST_EXPIRED_REPLAY_FROM_SEQ=${String(fromSeqEnv)} is not a non-negative integer; skipping`,
|
|
121
123
|
);
|
|
122
|
-
return;
|
|
124
|
+
return softSkip('blocked', 'precondition not met — `!Number.isFinite(fromSeq) || fromSeq < 0` returned early ([replay-retention-expiry] OPENWOP_TEST_EXPIRED_REPLAY_FROM_SEQ=… is not a non-negative integer; skipping) (seam, prior step, or fixture…');
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
// Pick a mode the host advertises. Per replay.md the envelope
|
|
@@ -134,7 +136,7 @@ describe('replay-retention-expiry: 410/422 on expired-range fork', () => {
|
|
|
134
136
|
|
|
135
137
|
expect(
|
|
136
138
|
res.status === 410 || res.status === 422,
|
|
137
|
-
|
|
139
|
+
req('openwop.it.replay-retention-expiry.post-v1-runs-expiredrunid-fork-returns-410-or-422-with-canonical-envelope',
|
|
138
140
|
'replay.md §"Retention and garbage collection"',
|
|
139
141
|
'fork against expired event range MUST return 410 Gone or 422 Unprocessable Entity',
|
|
140
142
|
),
|
|
@@ -150,7 +152,7 @@ describe('replay-retention-expiry: 410/422 on expired-range fork', () => {
|
|
|
150
152
|
};
|
|
151
153
|
};
|
|
152
154
|
|
|
153
|
-
expect(typeof body.error,
|
|
155
|
+
expect(typeof body.error, req('openwop.it.replay-retention-expiry.post-v1-runs-expiredrunid-fork-returns-410-or-422-with-canonical-envelope',
|
|
154
156
|
'replay.md §"Retention and garbage collection"',
|
|
155
157
|
'expired-fork response MUST use the canonical error envelope ({error, message, details?})',
|
|
156
158
|
)).toBe('string');
|
|
@@ -162,14 +164,14 @@ describe('replay-retention-expiry: 410/422 on expired-range fork', () => {
|
|
|
162
164
|
// details.{sourceRunId, fromSeq, retentionBoundary} are SHOULD —
|
|
163
165
|
// soft-check when present, MUST NOT mismatch when present.
|
|
164
166
|
if (body.details?.sourceRunId !== undefined) {
|
|
165
|
-
expect(body.details.sourceRunId,
|
|
167
|
+
expect(body.details.sourceRunId, req('openwop.it.replay-retention-expiry.post-v1-runs-expiredrunid-fork-returns-410-or-422-with-canonical-envelope',
|
|
166
168
|
'replay.md §"Retention and garbage collection"',
|
|
167
169
|
'details.sourceRunId (when present) MUST match the runId in the request path',
|
|
168
170
|
)).toBe(expiredRunId);
|
|
169
171
|
}
|
|
170
172
|
|
|
171
173
|
if (body.details?.fromSeq !== undefined) {
|
|
172
|
-
expect(body.details.fromSeq,
|
|
174
|
+
expect(body.details.fromSeq, req('openwop.it.replay-retention-expiry.post-v1-runs-expiredrunid-fork-returns-410-or-422-with-canonical-envelope',
|
|
173
175
|
'replay.md §"Retention and garbage collection"',
|
|
174
176
|
'details.fromSeq (when present) MUST match the fromSeq supplied in the request body',
|
|
175
177
|
)).toBe(fromSeq);
|
|
@@ -54,6 +54,8 @@ import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
|
54
54
|
import { readFileSync } from 'node:fs';
|
|
55
55
|
import { join } from 'node:path';
|
|
56
56
|
import { V1_DIR } from '../lib/paths.js';
|
|
57
|
+
import { req } from '../lib/requirement-ids.js';
|
|
58
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
57
59
|
|
|
58
60
|
const WORKFLOW_ID = 'conformance-replay-side-effect';
|
|
59
61
|
const EFFECT_NODE = 'effect';
|
|
@@ -98,10 +100,10 @@ function errorCode(e: RawEvent): string | undefined {
|
|
|
98
100
|
describe('replay-side-effect-suppression: capability shape (RFC 0140 §A)', () => {
|
|
99
101
|
it('sideEffectSuppression, when present, is one of the two defined values', async () => {
|
|
100
102
|
const replay = await fetchReplay();
|
|
101
|
-
if (replay?.sideEffectSuppression === undefined) return; // absent ⇒ "none"; nothing to check
|
|
103
|
+
if (replay?.sideEffectSuppression === undefined) return softSkip('blocked', 'precondition not met — `replay?.sideEffectSuppression === undefined` returned early (absent ⇒ "none"; nothing to check) (seam, prior step, or fixture unavailable)'); // absent ⇒ "none"; nothing to check
|
|
102
104
|
expect(
|
|
103
105
|
replay.sideEffectSuppression,
|
|
104
|
-
|
|
106
|
+
req('openwop.it.replay-side-effect-suppression.sideeffectsuppression-when-present-is-one-of-the-two-defined-values',
|
|
105
107
|
'capabilities.schema.json §replay.sideEffectSuppression',
|
|
106
108
|
'sideEffectSuppression MUST be "recorded-outcome" or "none" when advertised',
|
|
107
109
|
),
|
|
@@ -132,11 +134,11 @@ describe.skipIf(SKIP_NO_FIXTURE)('replay-side-effect-suppression: a replay does
|
|
|
132
134
|
/* already recorded */
|
|
133
135
|
}
|
|
134
136
|
ctx.skip();
|
|
135
|
-
return;
|
|
137
|
+
return softSkip('blocked', 'precondition not met — `replay?.sideEffectSuppression !== \'recorded-outcome\'` returned early (seam, prior step, or fixture unavailable)');
|
|
136
138
|
}
|
|
137
139
|
if (!replay.modes?.includes('replay')) {
|
|
138
140
|
ctx.skip(); // suppression is scoped to replay mode
|
|
139
|
-
return;
|
|
141
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!replay.modes?.includes(\'replay\')` returned early');
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
// 1. Start the run with a delay long enough to cancel mid-flight, so the
|
|
@@ -151,13 +153,13 @@ describe.skipIf(SKIP_NO_FIXTURE)('replay-side-effect-suppression: a replay does
|
|
|
151
153
|
workflowId: WORKFLOW_ID,
|
|
152
154
|
inputs: { delayMs: 5_000 },
|
|
153
155
|
});
|
|
154
|
-
expect(create.status, `Failed to start ${WORKFLOW_ID}`).toBe(201);
|
|
156
|
+
expect(create.status, req('openwop.it.replay-side-effect-suppression.fails-a-side-effecting-node-closed-with-replay-source-missing-when-the-source-ne', 'replay.md §"Side-effect suppression in replay" requirement 1', `Failed to start ${WORKFLOW_ID}`)).toBe(201);
|
|
155
157
|
const sourceRunId = (create.json as { runId: string }).runId;
|
|
156
158
|
|
|
157
159
|
const cancel = await driver.post(`/v1/runs/${encodeURIComponent(sourceRunId)}/cancel`, {});
|
|
158
160
|
expect(
|
|
159
161
|
[200, 202, 204].includes(cancel.status),
|
|
160
|
-
`cancel returned ${cancel.status}
|
|
162
|
+
req('openwop.it.replay-side-effect-suppression.fails-a-side-effecting-node-closed-with-replay-source-missing-when-the-source-ne', 'replay.md §"Side-effect suppression in replay" requirement 1', `cancel returned ${cancel.status}`),
|
|
161
163
|
).toBe(true);
|
|
162
164
|
await pollUntilTerminal(sourceRunId, { timeoutMs: 15_000 });
|
|
163
165
|
|
|
@@ -167,7 +169,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('replay-side-effect-suppression: a replay does
|
|
|
167
169
|
const sourceEvents = await readEvents(sourceRunId);
|
|
168
170
|
expect(
|
|
169
171
|
sourceEvents.some((e) => e.type === 'node.completed' && e.nodeId === EFFECT_NODE),
|
|
170
|
-
'precondition: the cancelled source run must NOT have completed the effect node',
|
|
172
|
+
req('openwop.it.replay-side-effect-suppression.fails-a-side-effecting-node-closed-with-replay-source-missing-when-the-source-ne', 'replay.md §"Side-effect suppression in replay" requirement 1', 'precondition: the cancelled source run must NOT have completed the effect node'),
|
|
171
173
|
).toBe(false);
|
|
172
174
|
|
|
173
175
|
// 2. Fork it in replay mode.
|
|
@@ -176,9 +178,9 @@ describe.skipIf(SKIP_NO_FIXTURE)('replay-side-effect-suppression: a replay does
|
|
|
176
178
|
});
|
|
177
179
|
if (forkDeclined(fork.status, 'side-effect-suppression replay fork')) {
|
|
178
180
|
ctx.skip();
|
|
179
|
-
return;
|
|
181
|
+
return softSkip('blocked', 'precondition not met — `forkDeclined(fork.status, \'side-effect-suppression replay fork\')` returned early (seam, prior step, or fixture unavailable)');
|
|
180
182
|
}
|
|
181
|
-
expect(fork.status, 'fork should be accepted').toBe(201);
|
|
183
|
+
expect(fork.status, req('openwop.it.replay-side-effect-suppression.fails-a-side-effecting-node-closed-with-replay-source-missing-when-the-source-ne', 'replay.md §"Side-effect suppression in replay" requirement 1', 'fork should be accepted')).toBe(201);
|
|
182
184
|
const forkRunId = (fork.json as { runId: string }).runId;
|
|
183
185
|
await pollUntilTerminal(forkRunId, { timeoutMs: 45_000 }); // re-runs the 5s delay before reaching `effect`
|
|
184
186
|
|
|
@@ -190,7 +192,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('replay-side-effect-suppression: a replay does
|
|
|
190
192
|
|
|
191
193
|
expect(
|
|
192
194
|
effectEvents.some((e) => e.type === 'node.completed'),
|
|
193
|
-
|
|
195
|
+
req('openwop.it.replay-side-effect-suppression.fails-a-side-effecting-node-closed-with-replay-source-missing-when-the-source-ne',
|
|
194
196
|
'replay.md §"Side-effect suppression in replay" requirement 1',
|
|
195
197
|
'the side-effecting node MUST NOT complete during a replay — completing it means the effect was performed',
|
|
196
198
|
),
|
|
@@ -198,7 +200,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('replay-side-effect-suppression: a replay does
|
|
|
198
200
|
|
|
199
201
|
expect(
|
|
200
202
|
effectEvents.some((e) => e.type === 'node.failed' && errorCode(e) === 'replay_source_missing'),
|
|
201
|
-
|
|
203
|
+
req('openwop.it.replay-side-effect-suppression.fails-a-side-effecting-node-closed-with-replay-source-missing-when-the-source-ne',
|
|
202
204
|
'replay.md §"Side-effect suppression in replay" requirement 3',
|
|
203
205
|
'a side-effecting node with no recorded source outcome MUST fail closed with replay_source_missing',
|
|
204
206
|
),
|
|
@@ -240,9 +242,9 @@ describe.skipIf(V1_DIR === null)('replay.md req 4 — the manifest role is a cla
|
|
|
240
242
|
// binds a value nothing can declare — a rule with no reachable trigger.
|
|
241
243
|
expect(
|
|
242
244
|
manifest().includes('side-effect'),
|
|
243
|
-
'node-pack-manifest.schema.json §role MUST still describe the `side-effect` value that ' +
|
|
245
|
+
req('openwop.it.replay-side-effect-suppression.the-manifest-still-offers-the-role-value-the-requirement-binds', 'RFC 0140', 'node-pack-manifest.schema.json §role MUST still describe the `side-effect` value that ' +
|
|
244
246
|
'replay.md requirement 4 makes binding. A requirement whose trigger no longer exists is ' +
|
|
245
|
-
'not enforcement, it is decoration.',
|
|
247
|
+
'not enforcement, it is decoration.'),
|
|
246
248
|
).toBe(true);
|
|
247
249
|
});
|
|
248
250
|
|
|
@@ -250,9 +252,9 @@ describe.skipIf(V1_DIR === null)('replay.md req 4 — the manifest role is a cla
|
|
|
250
252
|
const doc = replay();
|
|
251
253
|
expect(
|
|
252
254
|
/MUST\*{0,2} be treated as side-effecting/.test(doc) && doc.includes('node-pack-manifest.schema.json'),
|
|
253
|
-
'replay.md §"Requirements when a host declares `sideEffectSuppression: \\"recorded-outcome\\"`" ' +
|
|
255
|
+
req('openwop.it.replay-side-effect-suppression.replay-md-binds-that-declaration-naming-the-schema-it-comes-from', 'replay.md', 'replay.md §"Requirements when a host declares `sideEffectSuppression: \\"recorded-outcome\\"`" ' +
|
|
254
256
|
'MUST bind a manifest-declared `role: "side-effect"` to the suppression obligation, and MUST ' +
|
|
255
|
-
'name the schema the declaration comes from so a reader can find it.',
|
|
257
|
+
'name the schema the declaration comes from so a reader can find it.'),
|
|
256
258
|
).toBe(true);
|
|
257
259
|
});
|
|
258
260
|
|
|
@@ -267,10 +269,10 @@ describe.skipIf(V1_DIR === null)('replay.md req 4 — the manifest role is a cla
|
|
|
267
269
|
const doc = replay();
|
|
268
270
|
expect(
|
|
269
271
|
doc.includes('backstop, not a discharge'),
|
|
270
|
-
'replay.md requirement 4 MUST state that a guarded-seam throw is a backstop rather than a ' +
|
|
272
|
+
req('openwop.it.replay-side-effect-suppression.a-throw-is-named-a-backstop-not-a-discharge', 'RFC 0140', 'replay.md requirement 4 MUST state that a guarded-seam throw is a backstop rather than a ' +
|
|
271
273
|
'discharge. It satisfies requirement 1 (no effect) and not requirement 2 (resolve the ' +
|
|
272
274
|
'recorded outcome), and a host reading the floor as "make it throw" ships a replay that ' +
|
|
273
|
-
'fails where this section says it must succeed.',
|
|
275
|
+
'fails where this section says it must succeed.'),
|
|
274
276
|
).toBe(true);
|
|
275
277
|
expect(
|
|
276
278
|
/safe and\s+non-conformant/.test(doc),
|
|
@@ -285,9 +287,9 @@ describe.skipIf(V1_DIR === null)('replay.md req 4 — the manifest role is a cla
|
|
|
285
287
|
// manifest is wrong." The requirement is one-directional and says so.
|
|
286
288
|
expect(
|
|
287
289
|
replay().includes('MUST NOT classify fewer'),
|
|
288
|
-
'replay.md requirement 4 MUST state the direction: a host classifier may treat MORE nodes as ' +
|
|
290
|
+
req('openwop.it.replay-side-effect-suppression.the-floor-direction-is-stated-not-left-to-inference', 'RFC 0140', 'replay.md requirement 4 MUST state the direction: a host classifier may treat MORE nodes as ' +
|
|
289
291
|
'side-effecting and never fewer. Without the direction, a host that trusts its own list over ' +
|
|
290
|
-
'the manifest can read the clause as permission to disagree.',
|
|
292
|
+
'the manifest can read the clause as permission to disagree.'),
|
|
291
293
|
).toBe(true);
|
|
292
294
|
});
|
|
293
295
|
});
|
|
@@ -22,6 +22,7 @@ import { softSkip } from '../lib/soft-skip.js';
|
|
|
22
22
|
import { forkDeclined } from '../lib/fork-availability.js';
|
|
23
23
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
24
24
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
25
|
+
import { req } from '../lib/requirement-ids.js';
|
|
25
26
|
|
|
26
27
|
const NOOP_WORKFLOW_ID = 'conformance-noop';
|
|
27
28
|
const SKIP_NO_NOOP = !isFixtureAdvertised(NOOP_WORKFLOW_ID);
|
|
@@ -73,13 +74,13 @@ describe('replay-determinism: openwop-replay-fork profile gate', () => {
|
|
|
73
74
|
const replay = await fetchReplayCapability();
|
|
74
75
|
if (replay === null || replay.supported !== true) return softSkip('inapplicable', 'host does not advertise `replay.supported: true` — the replay contract does not apply to it');
|
|
75
76
|
|
|
76
|
-
expect(Array.isArray(replay.modes),
|
|
77
|
+
expect(Array.isArray(replay.modes), req('openwop.it.replayDeterminism.host-advertising-replay-supported-must-also-advertise-replay-modes',
|
|
77
78
|
'spec/v1/replay.md',
|
|
78
79
|
'host advertising replay.supported MUST advertise replay.modes as an array',
|
|
79
80
|
)).toBe(true);
|
|
80
81
|
if (Array.isArray(replay.modes)) {
|
|
81
82
|
for (const m of replay.modes) {
|
|
82
|
-
expect(typeof m,
|
|
83
|
+
expect(typeof m, req('openwop.it.replayDeterminism.host-advertising-replay-supported-must-also-advertise-replay-modes',
|
|
83
84
|
'spec/v1/replay.md',
|
|
84
85
|
'each replay.modes entry MUST be a string',
|
|
85
86
|
)).toBe('string');
|
|
@@ -98,7 +99,7 @@ describe.skipIf(SKIP_NO_NOOP)('replay-determinism: same fromSeq + same workflow
|
|
|
98
99
|
|
|
99
100
|
// Phase 1: complete an original run.
|
|
100
101
|
const create = await driver.post('/v1/runs', { workflowId: NOOP_WORKFLOW_ID });
|
|
101
|
-
if (create.status !== 201) return;
|
|
102
|
+
if (create.status !== 201) return softSkip('blocked', 'precondition not met — `create.status !== 201` returned early (seam, prior step, or fixture unavailable)');
|
|
102
103
|
const originalRunId = (create.json as { runId: string }).runId;
|
|
103
104
|
await pollUntilTerminal(originalRunId, { timeoutMs: 10_000 });
|
|
104
105
|
|
|
@@ -107,8 +108,8 @@ describe.skipIf(SKIP_NO_NOOP)('replay-determinism: same fromSeq + same workflow
|
|
|
107
108
|
mode: 'replay',
|
|
108
109
|
fromSeq: 0,
|
|
109
110
|
});
|
|
110
|
-
if (forkDeclined(fork1.status, 'determinism fork 1')) return;
|
|
111
|
-
expect(fork1.status,
|
|
111
|
+
if (forkDeclined(fork1.status, 'determinism fork 1')) return softSkip('blocked', 'precondition not met — `forkDeclined(fork1.status, \'determinism fork 1\')` returned early (seam, prior step, or fixture unavailable)');
|
|
112
|
+
expect(fork1.status, req('openwop.it.replayDeterminism.two-replay-forks-of-the-same-point-produce-structurally-identical-event-lists',
|
|
112
113
|
'spec/v1/replay.md',
|
|
113
114
|
'POST /v1/runs/{runId}:fork with mode=replay MUST return 201',
|
|
114
115
|
)).toBe(201);
|
|
@@ -120,7 +121,7 @@ describe.skipIf(SKIP_NO_NOOP)('replay-determinism: same fromSeq + same workflow
|
|
|
120
121
|
mode: 'replay',
|
|
121
122
|
fromSeq: 0,
|
|
122
123
|
});
|
|
123
|
-
if (forkDeclined(fork2.status, 'determinism fork 2')) return;
|
|
124
|
+
if (forkDeclined(fork2.status, 'determinism fork 2')) return softSkip('blocked', 'precondition not met — `forkDeclined(fork2.status, \'determinism fork 2\')` returned early (seam, prior step, or fixture unavailable)');
|
|
124
125
|
expect(fork2.status).toBe(201);
|
|
125
126
|
const fork2Id = (fork2.json as { runId: string }).runId;
|
|
126
127
|
await pollUntilTerminal(fork2Id, { timeoutMs: 10_000 });
|
|
@@ -128,21 +129,21 @@ describe.skipIf(SKIP_NO_NOOP)('replay-determinism: same fromSeq + same workflow
|
|
|
128
129
|
// Phase 4: fetch both fork event streams.
|
|
129
130
|
const fork1Events = await driver.get(`/v1/runs/${encodeURIComponent(fork1Id)}/events/poll`);
|
|
130
131
|
const fork2Events = await driver.get(`/v1/runs/${encodeURIComponent(fork2Id)}/events/poll`);
|
|
131
|
-
if (fork1Events.status !== 200 || fork2Events.status !== 200) return;
|
|
132
|
+
if (fork1Events.status !== 200 || fork2Events.status !== 200) return softSkip('blocked', 'precondition not met — `fork1Events.status !== 200 || fork2Events.status !== 200` returned early (seam, prior step, or fixture unavailable)');
|
|
132
133
|
|
|
133
134
|
const fork1Body = fork1Events.json as { events?: RawEvent[] };
|
|
134
135
|
const fork2Body = fork2Events.json as { events?: RawEvent[] };
|
|
135
|
-
if (!fork1Body.events || !fork2Body.events) return;
|
|
136
|
+
if (!fork1Body.events || !fork2Body.events) return softSkip('blocked', 'precondition not met — `!fork1Body.events || !fork2Body.events` returned early (seam, prior step, or fixture unavailable)');
|
|
136
137
|
|
|
137
138
|
// Phase 5: assert structural identity (modulo timestamps + IDs).
|
|
138
|
-
expect(fork1Body.events.length,
|
|
139
|
+
expect(fork1Body.events.length, req('openwop.it.replayDeterminism.two-replay-forks-of-the-same-point-produce-structurally-identical-event-lists',
|
|
139
140
|
'spec/v1/replay.md §"Replay determinism"',
|
|
140
141
|
'two replay forks MUST produce the same number of events',
|
|
141
142
|
)).toBe(fork2Body.events.length);
|
|
142
143
|
|
|
143
144
|
const shape1 = structuralShape(fork1Body.events);
|
|
144
145
|
const shape2 = structuralShape(fork2Body.events);
|
|
145
|
-
expect(shape1,
|
|
146
|
+
expect(shape1, req('openwop.it.replayDeterminism.two-replay-forks-of-the-same-point-produce-structurally-identical-event-lists',
|
|
146
147
|
'spec/v1/replay.md §"Replay determinism"',
|
|
147
148
|
'event sequence (type/nodeId/data) MUST be identical across two replay forks of the same point',
|
|
148
149
|
)).toEqual(shape2);
|
|
@@ -155,14 +156,14 @@ describe.skipIf(SKIP_NO_NOOP)('replay-determinism: branch-mode is permitted to d
|
|
|
155
156
|
it('branch mode does NOT need to produce identical event sequences (negative-control)', async () => {
|
|
156
157
|
const replay = await fetchReplayCapability();
|
|
157
158
|
if (replay === null || replay.supported !== true) return softSkip('inapplicable', 'host does not advertise `replay.supported: true` — the replay contract does not apply to it');
|
|
158
|
-
if (!Array.isArray(replay.modes) || !replay.modes.includes('branch')) return;
|
|
159
|
+
if (!Array.isArray(replay.modes) || !replay.modes.includes('branch')) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!Array.isArray(replay.modes) || !replay.modes.includes(\'branch\')` returned early');
|
|
159
160
|
|
|
160
161
|
// Self-test on the spec interpretation: branch and replay are
|
|
161
162
|
// SEMANTICALLY DIFFERENT modes per spec/v1/replay.md. Branch may
|
|
162
163
|
// diverge by design (variable overlay, runOptionsOverlay). This
|
|
163
164
|
// assertion just pins the interpretation; no actual round-trip
|
|
164
165
|
// needed.
|
|
165
|
-
expect((replay.modes as string[]).includes('branch'),
|
|
166
|
+
expect((replay.modes as string[]).includes('branch'), req('openwop.it.replayDeterminism.branch-mode-does-not-need-to-produce-identical-event-sequences-negative-control',
|
|
166
167
|
'spec/v1/replay.md',
|
|
167
168
|
'branch mode is documented; this self-test ensures the suite does not assume branch determinism',
|
|
168
169
|
)).toBe(true);
|