@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
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
|
|
13
13
|
import { describe, it, expect } from 'vitest';
|
|
14
14
|
import { softSkip } from '../lib/soft-skip.js';
|
|
15
|
-
import { driver } from '../lib/driver.js';
|
|
16
15
|
import { readDistillationCap, invokeDistill } from '../lib/distillation.js';
|
|
16
|
+
import { req } from '../lib/requirement-ids.js';
|
|
17
17
|
|
|
18
18
|
describe('distillation-token-budget (RFC 0062 §B)', () => {
|
|
19
19
|
it('within budget tokensUsed ≤ tokenBudget; an un-meetable budget fails atomically', async () => {
|
|
@@ -24,11 +24,11 @@ describe('distillation-token-budget (RFC 0062 §B)', () => {
|
|
|
24
24
|
const dist = ok.body.event?.distillation ?? {};
|
|
25
25
|
expect(
|
|
26
26
|
typeof dist.tokenBudget === 'number' && typeof dist.tokensUsed === 'number',
|
|
27
|
-
|
|
27
|
+
req('openwop.it.distillation-token-budget.within-budget-tokensused-tokenbudget-an-un-meetable-budget-fails-atomically', 'RFC 0062 §B', 'memory.compacted MUST carry distillation.tokenBudget + tokensUsed on a budgeted run'),
|
|
28
28
|
).toBe(true);
|
|
29
29
|
expect(
|
|
30
30
|
(dist.tokensUsed as number) <= (dist.tokenBudget as number),
|
|
31
|
-
|
|
31
|
+
req('openwop.it.distillation-token-budget.within-budget-tokensused-tokenbudget-an-un-meetable-budget-fails-atomically', 'RFC 0062 §B', 'a successful distillation MUST consume ≤ its tokenBudget'),
|
|
32
32
|
).toBe(true);
|
|
33
33
|
|
|
34
34
|
// A budget too small to distill the corpus MUST fail closed, no partial archive.
|
|
@@ -36,11 +36,11 @@ describe('distillation-token-budget (RFC 0062 §B)', () => {
|
|
|
36
36
|
if (tooSmall === null) return softSkip('blocked', 'precondition not met — `tooSmall === null` returned early (seam, prior step, or fixture unavailable)');
|
|
37
37
|
expect(
|
|
38
38
|
tooSmall.status >= 400 && tooSmall.body.error === 'token_budget_exceeded',
|
|
39
|
-
|
|
39
|
+
req('openwop.it.distillation-token-budget.within-budget-tokensused-tokenbudget-an-un-meetable-budget-fails-atomically', 'RFC 0062 §B', 'an un-meetable budget MUST fail with token_budget_exceeded'),
|
|
40
40
|
).toBe(true);
|
|
41
41
|
expect(
|
|
42
42
|
tooSmall.body.archiveChecksum,
|
|
43
|
-
|
|
43
|
+
req('openwop.it.distillation-token-budget.within-budget-tokensused-tokenbudget-an-un-meetable-budget-fails-atomically', 'RFC 0062 §B', 'a token_budget_exceeded run MUST write no partial archive (atomic)'),
|
|
44
44
|
).toBeUndefined();
|
|
45
45
|
});
|
|
46
46
|
});
|
|
@@ -51,6 +51,7 @@ import { softSkip } from '../lib/soft-skip.js';
|
|
|
51
51
|
import { queryTestEvents, requireEvents } from '../lib/event-log-query.js';
|
|
52
52
|
import { recordRequirement } from '../lib/requirement-ledger.js';
|
|
53
53
|
import { requirementIdForFile } from '../lib/scenario-disposition.js';
|
|
54
|
+
import { req } from '../lib/requirement-ids.js';
|
|
54
55
|
|
|
55
56
|
const WORKFLOW_ID = 'conformance-failure';
|
|
56
57
|
const FILE = 'durability-poison-exhaustion.test.ts';
|
|
@@ -90,7 +91,7 @@ if (SKIP_NO_FIXTURE) {
|
|
|
90
91
|
describe.skipIf(SKIP_NO_FIXTURE)('RFC 0158 §C.8 — poison work terminates within a bounded number of attempts', () => {
|
|
91
92
|
it('reaches terminal AND stops being retried, asserted on the log rather than on the status alone', async () => {
|
|
92
93
|
const create = await driver.post('/v1/runs', { workflowId: WORKFLOW_ID });
|
|
93
|
-
expect(create.status,
|
|
94
|
+
expect(create.status, req('openwop.it.durability-poison-exhaustion.reaches-terminal-and-stops-being-retried-asserted-on-the-log-rather-than-on-the',
|
|
94
95
|
'rest-endpoints.md',
|
|
95
96
|
'POST /v1/runs MUST return 201 even for work that fails at runtime',
|
|
96
97
|
)).toBe(201);
|
|
@@ -98,7 +99,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('RFC 0158 §C.8 — poison work terminates with
|
|
|
98
99
|
|
|
99
100
|
// ── First clause: a terminal, OPERATOR-VISIBLE state ─────────────────────
|
|
100
101
|
const terminal = await pollUntilTerminal(runId);
|
|
101
|
-
expect(terminal.status,
|
|
102
|
+
expect(terminal.status, req('openwop.it.durability-poison-exhaustion.reaches-terminal-and-stops-being-retried-asserted-on-the-log-rather-than-on-the',
|
|
102
103
|
'RFC 0158 §C.8',
|
|
103
104
|
'deterministically failing work MUST reach a terminal, operator-visible state',
|
|
104
105
|
)).toBe('failed');
|
|
@@ -118,13 +119,13 @@ describe.skipIf(SKIP_NO_FIXTURE)('RFC 0158 §C.8 — poison work terminates with
|
|
|
118
119
|
// Non-vacuity: the failure must actually be ON the log. Without this, a host
|
|
119
120
|
// returning an empty array would sail through every count comparison below,
|
|
120
121
|
// since 0 === 0 after any wait.
|
|
121
|
-
expect(events.some((e) => e.type === 'node.failed'),
|
|
122
|
+
expect(events.some((e) => e.type === 'node.failed'), req('openwop.it.durability-poison-exhaustion.reaches-terminal-and-stops-being-retried-asserted-on-the-log-rather-than-on-the',
|
|
122
123
|
'RFC 0158 §C.8',
|
|
123
124
|
'the run log MUST record the deterministic failure — an empty log makes every attempt count vacuous',
|
|
124
125
|
)).toBe(true);
|
|
125
126
|
|
|
126
127
|
const attemptsBefore = events.filter((e) => ATTEMPT_TYPES.has(e.type)).length;
|
|
127
|
-
expect(attemptsBefore,
|
|
128
|
+
expect(attemptsBefore, req('openwop.it.durability-poison-exhaustion.reaches-terminal-and-stops-being-retried-asserted-on-the-log-rather-than-on-the',
|
|
128
129
|
'RFC 0158 §C.8',
|
|
129
130
|
'at least one attempt MUST be recorded — zero attempts means nothing was delivered',
|
|
130
131
|
)).toBeGreaterThan(0);
|
|
@@ -135,7 +136,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('RFC 0158 §C.8 — poison work terminates with
|
|
|
135
136
|
const after = requireEvents(second, 'RFC 0158 §C.8 attempt counting (post-terminal)');
|
|
136
137
|
const attemptsAfter = after.filter((e) => ATTEMPT_TYPES.has(e.type)).length;
|
|
137
138
|
|
|
138
|
-
expect(attemptsAfter,
|
|
139
|
+
expect(attemptsAfter, req('openwop.it.durability-poison-exhaustion.reaches-terminal-and-stops-being-retried-asserted-on-the-log-rather-than-on-the',
|
|
139
140
|
'RFC 0158 §C.8',
|
|
140
141
|
'attempts MUST NOT continue after the run reports terminal — a host still redelivering records more',
|
|
141
142
|
)).toBe(attemptsBefore);
|
|
@@ -143,7 +144,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('RFC 0158 §C.8 — poison work terminates with
|
|
|
143
144
|
// ── Optional ceiling, only when the operator supplies the declared bound ─
|
|
144
145
|
const bound = declaredAttemptBound();
|
|
145
146
|
if (bound !== null) {
|
|
146
|
-
expect(attemptsAfter,
|
|
147
|
+
expect(attemptsAfter, req('openwop.it.durability-poison-exhaustion.reaches-terminal-and-stops-being-retried-asserted-on-the-log-rather-than-on-the',
|
|
147
148
|
'RFC 0158 §C.8 + §B.5',
|
|
148
149
|
'total attempts MUST NOT exceed the operator-declared attempt bound',
|
|
149
150
|
)).toBeLessThanOrEqual(bound);
|
|
@@ -28,9 +28,10 @@
|
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
import { describe, it, expect } from 'vitest';
|
|
31
|
-
import { driver } from '../lib/driver.js';
|
|
32
31
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
33
32
|
import { readEgressPolicyCap, driveEgress, EGRESS_DECISIONS, EGRESS_REASONS } from '../lib/egressPolicy.js';
|
|
33
|
+
import { req } from '../lib/requirement-ids.js';
|
|
34
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
34
35
|
|
|
35
36
|
describe('egress-audience-binding (RFC 0079 §C)', () => {
|
|
36
37
|
it('denies/downgrades an out-of-audience egress without attaching the credential, and fails closed on unevaluable provenance', async () => {
|
|
@@ -39,22 +40,22 @@ describe('egress-audience-binding (RFC 0079 §C)', () => {
|
|
|
39
40
|
|
|
40
41
|
// ---- Leg 1: out-of-audience — deny|downgrade + credential NOT attached --
|
|
41
42
|
const oob = await driveEgress({ scenario: 'out-of-audience' });
|
|
42
|
-
if (oob === null) return; // egress seam absent — soft-skip the whole behavior
|
|
43
|
+
if (oob === null) return softSkip('blocked', 'precondition not met — `oob === null` returned early (egress seam absent — soft-skip the whole behavior) (seam, prior step, or fixture unavailable)'); // egress seam absent — soft-skip the whole behavior
|
|
43
44
|
expect(
|
|
44
45
|
oob.decision === 'denied' || oob.decision === 'downgraded',
|
|
45
|
-
|
|
46
|
+
req('openwop.it.egress-audience-binding.denies-downgrades-an-out-of-audience-egress-without-attaching-the-credential-and', 'host-capabilities.md §"Credential provenance + egress policy"', 'an out-of-audience egress MUST be denied or downgraded'),
|
|
46
47
|
).toBe(true);
|
|
47
48
|
expect(
|
|
48
49
|
typeof oob.decision === 'string' && EGRESS_DECISIONS.includes(oob.decision),
|
|
49
|
-
|
|
50
|
+
req('openwop.it.egress-audience-binding.denies-downgrades-an-out-of-audience-egress-without-attaching-the-credential-and', 'run-event-payloads.schema.json#egressDecided', 'decision MUST be in the closed enum'),
|
|
50
51
|
).toBe(true);
|
|
51
52
|
expect(
|
|
52
53
|
oob.reason === 'out-of-audience',
|
|
53
|
-
|
|
54
|
+
req('openwop.it.egress-audience-binding.denies-downgrades-an-out-of-audience-egress-without-attaching-the-credential-and', 'RFC 0079 §C', 'an out-of-audience denial MUST carry reason "out-of-audience"'),
|
|
54
55
|
).toBe(true);
|
|
55
56
|
expect(
|
|
56
57
|
oob.credentialAttached !== true,
|
|
57
|
-
|
|
58
|
+
req('openwop.it.egress-audience-binding.denies-downgrades-an-out-of-audience-egress-without-attaching-the-credential-and', 'SECURITY/invariants.yaml egress-credential-audience-bound', 'the host MUST NOT attach a credential whose audience excludes the destination (confused-deputy)'),
|
|
58
59
|
).toBe(true);
|
|
59
60
|
|
|
60
61
|
// ---- Leg 2: provenance-unevaluable — fail closed (deny) ----------------
|
|
@@ -62,19 +63,19 @@ describe('egress-audience-binding (RFC 0079 §C)', () => {
|
|
|
62
63
|
if (uneval !== null) {
|
|
63
64
|
expect(
|
|
64
65
|
uneval.decision === 'denied',
|
|
65
|
-
|
|
66
|
+
req('openwop.it.egress-audience-binding.denies-downgrades-an-out-of-audience-egress-without-attaching-the-credential-and', 'RFC 0079 §C', 'an egress with unevaluable provenance MUST fail closed (denied)'),
|
|
66
67
|
).toBe(true);
|
|
67
68
|
expect(
|
|
68
69
|
uneval.reason === 'provenance-unevaluable',
|
|
69
|
-
|
|
70
|
+
req('openwop.it.egress-audience-binding.denies-downgrades-an-out-of-audience-egress-without-attaching-the-credential-and', 'RFC 0079 §C', 'a provenance-unevaluable denial MUST carry reason "provenance-unevaluable"'),
|
|
70
71
|
).toBe(true);
|
|
71
72
|
expect(
|
|
72
73
|
typeof uneval.reason === 'string' && EGRESS_REASONS.includes(uneval.reason),
|
|
73
|
-
|
|
74
|
+
req('openwop.it.egress-audience-binding.denies-downgrades-an-out-of-audience-egress-without-attaching-the-credential-and', 'run-event-payloads.schema.json#egressDecided', 'reason MUST be in the closed enum'),
|
|
74
75
|
).toBe(true);
|
|
75
76
|
expect(
|
|
76
77
|
uneval.credentialAttached !== true,
|
|
77
|
-
|
|
78
|
+
req('openwop.it.egress-audience-binding.denies-downgrades-an-out-of-audience-egress-without-attaching-the-credential-and', 'SECURITY/invariants.yaml egress-credential-audience-bound', 'a fail-closed egress MUST NOT attach the credential'),
|
|
78
79
|
).toBe(true);
|
|
79
80
|
}
|
|
80
81
|
});
|
|
@@ -20,9 +20,10 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { describe, it, expect } from 'vitest';
|
|
23
|
-
import { driver } from '../lib/driver.js';
|
|
24
23
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
25
24
|
import { readEgressPolicyCap, driveEgress, EGRESS_REASONS, EGRESS_CONTENT_FORBIDDEN } from '../lib/egressPolicy.js';
|
|
25
|
+
import { req } from '../lib/requirement-ids.js';
|
|
26
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
26
27
|
|
|
27
28
|
describe('egress-decision-content-free (RFC 0079 §F / SR-1)', () => {
|
|
28
29
|
it('never leaks the credential value or the blocked destination into the egress.decided payload', async () => {
|
|
@@ -30,19 +31,19 @@ describe('egress-decision-content-free (RFC 0079 §F / SR-1)', () => {
|
|
|
30
31
|
if (!behaviorGate('openwop-egress-decision-content-free', cap?.supported === true)) return;
|
|
31
32
|
|
|
32
33
|
const res = await driveEgress({ scenario: 'canary' });
|
|
33
|
-
if (res === null) return; // seam absent — soft-skip
|
|
34
|
+
if (res === null) return softSkip('blocked', 'precondition not met — `res === null` returned early (seam absent — soft-skip) (seam, prior step, or fixture unavailable)'); // seam absent — soft-skip
|
|
34
35
|
|
|
35
36
|
// The canary sentinel MUST NOT appear anywhere observable.
|
|
36
37
|
expect(
|
|
37
38
|
res.canaryLeaked !== true,
|
|
38
|
-
|
|
39
|
+
req('openwop.it.egress-decision-content-free.never-leaks-the-credential-value-or-the-blocked-destination-into-the-egress-deci', 'RFC 0079 §F (SR-1)', 'the credential value (canary) MUST NOT leak into any observable surface'),
|
|
39
40
|
).toBe(true);
|
|
40
41
|
|
|
41
42
|
// No forbidden content keys on the decision payload.
|
|
42
43
|
for (const forbidden of EGRESS_CONTENT_FORBIDDEN) {
|
|
43
44
|
expect(
|
|
44
45
|
!(forbidden in res),
|
|
45
|
-
|
|
46
|
+
req('openwop.it.egress-decision-content-free.never-leaks-the-credential-value-or-the-blocked-destination-into-the-egress-deci', 'RFC 0079 §F (SR-1)', `egress.decided MUST be content-free (no ${forbidden})`),
|
|
46
47
|
).toBe(true);
|
|
47
48
|
}
|
|
48
49
|
|
|
@@ -50,7 +51,7 @@ describe('egress-decision-content-free (RFC 0079 §F / SR-1)', () => {
|
|
|
50
51
|
if (res.reason !== undefined) {
|
|
51
52
|
expect(
|
|
52
53
|
typeof res.reason === 'string' && EGRESS_REASONS.includes(res.reason),
|
|
53
|
-
|
|
54
|
+
req('openwop.it.egress-decision-content-free.never-leaks-the-credential-value-or-the-blocked-destination-into-the-egress-deci', 'run-event-payloads.schema.json#egressDecided', 'reason MUST be in the closed enum (no free-form spill)'),
|
|
54
55
|
).toBe(true);
|
|
55
56
|
}
|
|
56
57
|
});
|
|
@@ -40,8 +40,7 @@ import { join } from 'node:path';
|
|
|
40
40
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
41
41
|
import addFormats from 'ajv-formats';
|
|
42
42
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
43
|
-
|
|
44
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
43
|
+
import { req } from '../lib/requirement-ids.js';
|
|
45
44
|
|
|
46
45
|
function loadSchema(name: string): Record<string, unknown> {
|
|
47
46
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
|
|
@@ -58,20 +57,20 @@ describe('egress-provenance-shape: CredentialProvenance (RFC 0079 §A, server-fr
|
|
|
58
57
|
it('a conforming descriptor validates', () => {
|
|
59
58
|
expect(
|
|
60
59
|
validate({ credentialId: 'cred-stripe-1', issuer: 'host', audiences: ['api.stripe.com'], expiresAt: '2026-12-01T00:00:00Z', scopes: ['egress:stripe:charge'], redactionPolicy: 'always' }),
|
|
61
|
-
|
|
60
|
+
req('openwop.it.egress-provenance-shape.a-conforming-descriptor-validates', 'host-capabilities.md §"Credential provenance + egress policy"', 'a conforming CredentialProvenance MUST validate'),
|
|
62
61
|
).toBe(true);
|
|
63
62
|
});
|
|
64
63
|
|
|
65
64
|
it('audiences: [] is rejected (minItems:1 — a credential needs ≥1 audience to bind)', () => {
|
|
66
|
-
expect(validate({ credentialId: 'c', issuer: 'host', audiences: [] }),
|
|
65
|
+
expect(validate({ credentialId: 'c', issuer: 'host', audiences: [] }), req('openwop.it.egress-provenance-shape.audiences-is-rejected-minitems-1-a-credential-needs-1-audience-to-bind', 'RFC 0079 §A', 'audiences MUST have ≥1 entry')).toBe(false);
|
|
67
66
|
});
|
|
68
67
|
|
|
69
68
|
it('a missing REQUIRED credentialId is rejected', () => {
|
|
70
|
-
expect(validate({ issuer: 'host', audiences: ['a'] }),
|
|
69
|
+
expect(validate({ issuer: 'host', audiences: ['a'] }), req('openwop.it.egress-provenance-shape.a-missing-required-credentialid-is-rejected', 'RFC 0079 §A', 'credentialId is REQUIRED')).toBe(false);
|
|
71
70
|
});
|
|
72
71
|
|
|
73
72
|
it('an unknown property is rejected (additionalProperties:false)', () => {
|
|
74
|
-
expect(validate({ credentialId: 'c', issuer: 'host', audiences: ['a'], secretValue: 'sk-live-xxx' }),
|
|
73
|
+
expect(validate({ credentialId: 'c', issuer: 'host', audiences: ['a'], secretValue: 'sk-live-xxx' }), req('openwop.it.egress-provenance-shape.an-unknown-property-is-rejected-additionalproperties-false', 'RFC 0079 §A', 'CredentialProvenance MUST be additionalProperties:false')).toBe(false);
|
|
75
74
|
});
|
|
76
75
|
|
|
77
76
|
it('declares no secret-value property (egress-decision-no-secret-leak)', () => {
|
|
@@ -79,7 +78,7 @@ describe('egress-provenance-shape: CredentialProvenance (RFC 0079 §A, server-fr
|
|
|
79
78
|
for (const p of props) {
|
|
80
79
|
expect(
|
|
81
80
|
SECRET_PROP_NAMES.includes(p.toLowerCase()),
|
|
82
|
-
|
|
81
|
+
req('openwop.it.egress-provenance-shape.declares-no-secret-value-property-egress-decision-no-secret-leak', 'SECURITY invariant egress-decision-no-secret-leak', `CredentialProvenance MUST NOT declare a secret-bearing property (${p})`),
|
|
83
82
|
).toBe(false);
|
|
84
83
|
}
|
|
85
84
|
});
|
|
@@ -95,32 +94,32 @@ describe('egress-provenance-shape: egress.decided event (RFC 0079 §B, server-fr
|
|
|
95
94
|
} as Record<string, unknown>);
|
|
96
95
|
|
|
97
96
|
it('validates a content-free decision and enforces the decision enum + required fields', () => {
|
|
98
|
-
expect(validate({ decision: 'allowed', destination: 'api.stripe.com', credentialId: 'cred-stripe-1', reason: 'ok' }),
|
|
99
|
-
expect(validate({ decision: 'denied', destination: 'attacker.example', credentialId: 'cred-stripe-1', reason: 'out-of-audience' }),
|
|
100
|
-
expect(validate({ decision: 'ok', destination: 'a' }),
|
|
101
|
-
expect(validate({ destination: 'a' }),
|
|
102
|
-
expect(validate({ decision: 'allowed' }),
|
|
97
|
+
expect(validate({ decision: 'allowed', destination: 'api.stripe.com', credentialId: 'cred-stripe-1', reason: 'ok' }), req('openwop.it.egress-provenance-shape.validates-a-content-free-decision-and-enforces-the-decision-enum-required-fields', 'RFC 0079 §B', 'a conforming egress.decided MUST validate')).toBe(true);
|
|
98
|
+
expect(validate({ decision: 'denied', destination: 'attacker.example', credentialId: 'cred-stripe-1', reason: 'out-of-audience' }), req('openwop.it.egress-provenance-shape.validates-a-content-free-decision-and-enforces-the-decision-enum-required-fields', 'RFC 0079 §B', 'a denied decision MUST validate')).toBe(true);
|
|
99
|
+
expect(validate({ decision: 'ok', destination: 'a' }), req('openwop.it.egress-provenance-shape.validates-a-content-free-decision-and-enforces-the-decision-enum-required-fields', 'RFC 0079 §B', 'the canonical allow value is "allowed", not "ok"')).toBe(false);
|
|
100
|
+
expect(validate({ destination: 'a' }), req('openwop.it.egress-provenance-shape.validates-a-content-free-decision-and-enforces-the-decision-enum-required-fields', 'RFC 0079 §B', 'decision is REQUIRED')).toBe(false);
|
|
101
|
+
expect(validate({ decision: 'allowed' }), req('openwop.it.egress-provenance-shape.validates-a-content-free-decision-and-enforces-the-decision-enum-required-fields', 'RFC 0079 §B', 'destination is REQUIRED')).toBe(false);
|
|
103
102
|
});
|
|
104
103
|
|
|
105
104
|
it('reason is a CLOSED enum — a free-form / URL-bearing reason is rejected (egress-decision-no-secret-leak)', () => {
|
|
106
|
-
expect(validate({ decision: 'denied', destination: 'attacker.example', reason: 'out-of-audience' }),
|
|
105
|
+
expect(validate({ decision: 'denied', destination: 'attacker.example', reason: 'out-of-audience' }), req('openwop.it.egress-provenance-shape.reason-is-a-closed-enum-a-free-form-url-bearing-reason-is-rejected-egress-decisi', 'egress-decision-no-secret-leak', 'a conventional reason code MUST validate')).toBe(true);
|
|
107
106
|
expect(
|
|
108
107
|
validate({ decision: 'denied', destination: 'attacker.example', reason: 'https://attacker.example/leak?token=sk-live-xxx' }),
|
|
109
|
-
|
|
108
|
+
req('openwop.it.egress-provenance-shape.reason-is-a-closed-enum-a-free-form-url-bearing-reason-is-rejected-egress-decisi', 'egress-decision-no-secret-leak', 'a free-form / URL-bearing reason MUST be rejected (it would defeat the content-free guarantee)'),
|
|
110
109
|
).toBe(false);
|
|
111
110
|
});
|
|
112
111
|
|
|
113
112
|
it('the egressDecided $def declares no secret-value property', () => {
|
|
114
113
|
const def = ((payloads.$defs as Record<string, { properties?: Record<string, unknown> }>).egressDecided.properties) ?? {};
|
|
115
114
|
for (const p of Object.keys(def)) {
|
|
116
|
-
expect(SECRET_PROP_NAMES.includes(p.toLowerCase()),
|
|
115
|
+
expect(SECRET_PROP_NAMES.includes(p.toLowerCase()), req('openwop.it.egress-provenance-shape.the-egressdecided-def-declares-no-secret-value-property', 'egress-decision-no-secret-leak', `egress.decided MUST NOT declare a secret-bearing property (${p})`)).toBe(false);
|
|
117
116
|
}
|
|
118
117
|
});
|
|
119
118
|
|
|
120
119
|
it('egress.decided is in the RunEventType enum', () => {
|
|
121
120
|
const runEvent = loadSchema('run-event.schema.json');
|
|
122
121
|
const enumVals = ((runEvent.$defs as Record<string, { enum?: string[] }>).RunEventType?.enum) ?? [];
|
|
123
|
-
expect(enumVals.includes('egress.decided'),
|
|
122
|
+
expect(enumVals.includes('egress.decided'), req('openwop.it.egress-provenance-shape.egress-decided-is-in-the-runeventtype-enum', 'run-event.schema.json', 'egress.decided MUST be in the RunEventType enum')).toBe(true);
|
|
124
123
|
});
|
|
125
124
|
});
|
|
126
125
|
|
|
@@ -129,9 +128,9 @@ describe('egress-provenance-shape: capability advertisement (RFC 0079 §D, serve
|
|
|
129
128
|
const caps = loadSchema('capabilities.schema.json');
|
|
130
129
|
const httpClient = (caps.properties as Record<string, { properties?: Record<string, { properties?: Record<string, unknown> }> }>).httpClient;
|
|
131
130
|
const egressPolicy = httpClient?.properties?.egressPolicy;
|
|
132
|
-
expect(egressPolicy,
|
|
131
|
+
expect(egressPolicy, req('openwop.it.egress-provenance-shape.capabilities-httpclient-egresspolicy-is-declared-with-its-sub-flags', 'capabilities.md §httpClient', 'httpClient.egressPolicy MUST be declared')).toBeDefined();
|
|
133
132
|
for (const flag of ['supported', 'decisions']) {
|
|
134
|
-
expect(egressPolicy?.properties?.[flag],
|
|
133
|
+
expect(egressPolicy?.properties?.[flag], req('openwop.it.egress-provenance-shape.capabilities-httpclient-egresspolicy-is-declared-with-its-sub-flags', 'host-capabilities.md §"Credential provenance + egress policy"', `egressPolicy.${flag} MUST be declared`)).toBeDefined();
|
|
135
134
|
}
|
|
136
135
|
});
|
|
137
136
|
});
|
|
@@ -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
|
const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
|
|
37
39
|
const NODE_ID = 'structured-call';
|
|
@@ -91,18 +93,18 @@ async function lastBudget(): Promise<number | null> {
|
|
|
91
93
|
describe.skipIf(HTTP_SKIP)('envelope-completion-distinguishes-truncation: advertisement shape (RFC 0033 §E)', () => {
|
|
92
94
|
it('capabilities.envelopes.reliability.completion (when present) conforms to RFC 0033 §E', async () => {
|
|
93
95
|
const d = await readDiscovery();
|
|
94
|
-
if (d === null) return;
|
|
96
|
+
if (d === null) return softSkip('blocked', 'precondition not met — `d === null` returned early (seam, prior step, or fixture unavailable)');
|
|
95
97
|
const completion = capabilityFamily<{ reasoning?: Record<string, unknown>; tierOneSubsetCompliance?: unknown; reliability?: { completion?: Record<string, unknown> } & Record<string, unknown> }>(d, 'envelopes')?.reliability?.completion;
|
|
96
|
-
if (completion === undefined) return;
|
|
98
|
+
if (completion === undefined) return softSkip('blocked', 'precondition not met — `completion === undefined` returned early (seam, prior step, or fixture unavailable)');
|
|
97
99
|
expect(
|
|
98
100
|
typeof completion.distinguishesTruncation,
|
|
99
|
-
|
|
101
|
+
req('openwop.it.envelope-completion-distinguishes-truncation.capabilities-envelopes-reliability-completion-when-present-conforms-to-rfc-0033', 'RFCS/0033-envelope-completion-contract.md §E', 'completion.distinguishesTruncation MUST be boolean when block is advertised'),
|
|
100
102
|
).toBe('boolean');
|
|
101
103
|
if (completion.truncationBudgetMultiplier !== undefined) {
|
|
102
104
|
const n = completion.truncationBudgetMultiplier as number;
|
|
103
105
|
expect(
|
|
104
106
|
typeof n === 'number' && n >= 1 && n <= 8,
|
|
105
|
-
|
|
107
|
+
req('openwop.it.envelope-completion-distinguishes-truncation.capabilities-envelopes-reliability-completion-when-present-conforms-to-rfc-0033', 'RFCS/0033-envelope-completion-contract.md §E', 'truncationBudgetMultiplier MUST be a number in [1, 8] (default 2)'),
|
|
106
108
|
).toBe(true);
|
|
107
109
|
}
|
|
108
110
|
});
|
|
@@ -113,24 +115,24 @@ const SCHEMA_VIOLATION_FIXTURE = 'conformance-envelope-retry-attempted';
|
|
|
113
115
|
|
|
114
116
|
describe.skipIf(HTTP_SKIP)('envelope-completion-distinguishes-truncation: truncation path (RFC 0033 §B)', () => {
|
|
115
117
|
it('truncation: emits envelope.truncated + envelope.retry.attempted with reason: "truncation"', async () => {
|
|
116
|
-
if (!isFixtureAdvertised(TRUNCATED_FIXTURE)) return;
|
|
118
|
+
if (!isFixtureAdvertised(TRUNCATED_FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(TRUNCATED_FIXTURE)` returned early');
|
|
117
119
|
const d = await readDiscovery();
|
|
118
|
-
if (capabilityFamily<{ reasoning?: Record<string, unknown>; tierOneSubsetCompliance?: unknown; reliability?: { completion?: Record<string, unknown> } & Record<string, unknown> }>(d, 'envelopes')?.reliability?.completion?.distinguishesTruncation !== true) return;
|
|
120
|
+
if (capabilityFamily<{ reasoning?: Record<string, unknown>; tierOneSubsetCompliance?: unknown; reliability?: { completion?: Record<string, unknown> } & Record<string, unknown> }>(d, 'envelopes')?.reliability?.completion?.distinguishesTruncation !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `capabilityFamily<{ reasoning?: Record<string, unknown>; tierOneSubsetCompliance?: unknown; reliability?: { completion?: Record<string, unknown> } & Record<stri…');
|
|
119
121
|
const seed = await programMock([
|
|
120
122
|
{ stopReason: 'max_tokens', content: '{"partial' },
|
|
121
123
|
{ stopReason: 'end_turn', content: '{"valid":true}' },
|
|
122
124
|
]);
|
|
123
|
-
if (seed.status === 404) return;
|
|
125
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
124
126
|
|
|
125
127
|
const result = await startRunAndRead(TRUNCATED_FIXTURE);
|
|
126
|
-
if (result === null) return;
|
|
128
|
+
if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
|
|
127
129
|
const truncated = result.events.find((e) => e.type === 'envelope.truncated');
|
|
128
|
-
expect(truncated, 'envelope.truncated MUST fire on the truncation path').toBeDefined();
|
|
130
|
+
expect(truncated, req('openwop.it.envelope-completion-distinguishes-truncation.truncation-emits-envelope-truncated-envelope-retry-attempted-with-reason-truncat', 'RFCS/0033-envelope-completion-contract.md §B', 'envelope.truncated MUST fire on the truncation path')).toBeDefined();
|
|
129
131
|
const retry = result.events.find((e) => e.type === 'envelope.retry.attempted');
|
|
130
|
-
expect(retry, 'envelope.retry.attempted MUST fire between attempts').toBeDefined();
|
|
132
|
+
expect(retry, req('openwop.it.envelope-completion-distinguishes-truncation.truncation-emits-envelope-truncated-envelope-retry-attempted-with-reason-truncat', 'RFCS/0033-envelope-completion-contract.md §B', 'envelope.retry.attempted MUST fire between attempts')).toBeDefined();
|
|
131
133
|
expect(
|
|
132
134
|
retry!.payload?.reason,
|
|
133
|
-
|
|
135
|
+
req('openwop.it.envelope-completion-distinguishes-truncation.truncation-emits-envelope-truncated-envelope-retry-attempted-with-reason-truncat',
|
|
134
136
|
'RFCS/0033-envelope-completion-contract.md §B',
|
|
135
137
|
'truncation-routed retry MUST carry reason: "truncation" (distinct from schema-violation per RFC 0033 §A precedence rule)',
|
|
136
138
|
),
|
|
@@ -138,21 +140,21 @@ describe.skipIf(HTTP_SKIP)('envelope-completion-distinguishes-truncation: trunca
|
|
|
138
140
|
});
|
|
139
141
|
|
|
140
142
|
it('truncation: retry budget strictly greater than initial (RFC 0033 §B truncationBudgetMultiplier)', async () => {
|
|
141
|
-
if (!isFixtureAdvertised(TRUNCATED_FIXTURE)) return;
|
|
143
|
+
if (!isFixtureAdvertised(TRUNCATED_FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(TRUNCATED_FIXTURE)` returned early');
|
|
142
144
|
const d = await readDiscovery();
|
|
143
|
-
if (capabilityFamily<{ reasoning?: Record<string, unknown>; tierOneSubsetCompliance?: unknown; reliability?: { completion?: Record<string, unknown> } & Record<string, unknown> }>(d, 'envelopes')?.reliability?.completion?.distinguishesTruncation !== true) return;
|
|
145
|
+
if (capabilityFamily<{ reasoning?: Record<string, unknown>; tierOneSubsetCompliance?: unknown; reliability?: { completion?: Record<string, unknown> } & Record<string, unknown> }>(d, 'envelopes')?.reliability?.completion?.distinguishesTruncation !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `capabilityFamily<{ reasoning?: Record<string, unknown>; tierOneSubsetCompliance?: unknown; reliability?: { completion?: Record<string, unknown> } & Record<stri…');
|
|
144
146
|
const seed = await programMock([
|
|
145
147
|
{ stopReason: 'max_tokens', content: '{"partial' },
|
|
146
148
|
{ stopReason: 'end_turn', content: '{"valid":true}' },
|
|
147
149
|
]);
|
|
148
|
-
if (seed.status === 404) return;
|
|
150
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
149
151
|
|
|
150
152
|
await startRunAndRead(TRUNCATED_FIXTURE);
|
|
151
153
|
const budget = await lastBudget();
|
|
152
|
-
if (budget === null) return;
|
|
154
|
+
if (budget === null) return softSkip('blocked', 'precondition not met — `budget === null` returned early (seam, prior step, or fixture unavailable)');
|
|
153
155
|
expect(
|
|
154
156
|
budget,
|
|
155
|
-
|
|
157
|
+
req('openwop.it.envelope-completion-distinguishes-truncation.truncation-retry-budget-strictly-greater-than-initial-rfc-0033-b-truncationbudge',
|
|
156
158
|
'RFCS/0033-envelope-completion-contract.md §B',
|
|
157
159
|
'truncation retry MUST multiply maxTokens by truncationBudgetMultiplier — final budget > initial 50 fixture value',
|
|
158
160
|
),
|
|
@@ -162,19 +164,19 @@ describe.skipIf(HTTP_SKIP)('envelope-completion-distinguishes-truncation: trunca
|
|
|
162
164
|
|
|
163
165
|
describe.skipIf(HTTP_SKIP)('envelope-completion-distinguishes-truncation: schema-violation path (RFC 0033 §C)', () => {
|
|
164
166
|
it('schema-violation: NO envelope.truncated; envelope.retry.attempted reason ∈ {schema-violation, parse-error}', async () => {
|
|
165
|
-
if (!isFixtureAdvertised(SCHEMA_VIOLATION_FIXTURE)) return;
|
|
167
|
+
if (!isFixtureAdvertised(SCHEMA_VIOLATION_FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(SCHEMA_VIOLATION_FIXTURE)` returned early');
|
|
166
168
|
const seed = await programMock([
|
|
167
169
|
{ content: 'not valid json' },
|
|
168
170
|
{ content: '{"valid":true}' },
|
|
169
171
|
]);
|
|
170
|
-
if (seed.status === 404) return;
|
|
172
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
171
173
|
|
|
172
174
|
const result = await startRunAndRead(SCHEMA_VIOLATION_FIXTURE);
|
|
173
|
-
if (result === null) return;
|
|
175
|
+
if (result === null) return softSkip('blocked', 'precondition not met — `result === null` returned early (seam, prior step, or fixture unavailable)');
|
|
174
176
|
const truncated = result.events.find((e) => e.type === 'envelope.truncated');
|
|
175
177
|
expect(
|
|
176
178
|
truncated,
|
|
177
|
-
|
|
179
|
+
req('openwop.it.envelope-completion-distinguishes-truncation.schema-violation-no-envelope-truncated-envelope-retry-attempted-reason-schema-vi',
|
|
178
180
|
'RFCS/0033-envelope-completion-contract.md §C',
|
|
179
181
|
'schema-violation path MUST NOT emit envelope.truncated (truncation and schema-violation are distinct paths per RFC 0033 §A)',
|
|
180
182
|
),
|
|
@@ -184,7 +186,7 @@ describe.skipIf(HTTP_SKIP)('envelope-completion-distinguishes-truncation: schema
|
|
|
184
186
|
const reason = retry!.payload?.reason as string | undefined;
|
|
185
187
|
expect(
|
|
186
188
|
reason === 'schema-violation' || reason === 'parse-error',
|
|
187
|
-
|
|
189
|
+
req('openwop.it.envelope-completion-distinguishes-truncation.schema-violation-no-envelope-truncated-envelope-retry-attempted-reason-schema-vi',
|
|
188
190
|
'RFCS/0033-envelope-completion-contract.md §C',
|
|
189
191
|
'schema-violation-routed retry MUST carry reason ∈ {schema-violation, parse-error}; truncation reason is reserved for the budget-doubling path',
|
|
190
192
|
),
|
|
@@ -192,16 +194,16 @@ describe.skipIf(HTTP_SKIP)('envelope-completion-distinguishes-truncation: schema
|
|
|
192
194
|
});
|
|
193
195
|
|
|
194
196
|
it('schema-violation: retry budget UNCHANGED from initial (no budget multiplication on this path)', async () => {
|
|
195
|
-
if (!isFixtureAdvertised(SCHEMA_VIOLATION_FIXTURE)) return;
|
|
197
|
+
if (!isFixtureAdvertised(SCHEMA_VIOLATION_FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(SCHEMA_VIOLATION_FIXTURE)` returned early');
|
|
196
198
|
const seed = await programMock([
|
|
197
199
|
{ content: 'not valid json' },
|
|
198
200
|
{ content: '{"valid":true}' },
|
|
199
201
|
]);
|
|
200
|
-
if (seed.status === 404) return;
|
|
202
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
201
203
|
|
|
202
204
|
await startRunAndRead(SCHEMA_VIOLATION_FIXTURE);
|
|
203
205
|
const budget = await lastBudget();
|
|
204
|
-
if (budget === null) return;
|
|
206
|
+
if (budget === null) return softSkip('blocked', 'precondition not met — `budget === null` returned early (seam, prior step, or fixture unavailable)');
|
|
205
207
|
// The schema-violation fixture doesn't set maxTokens explicitly →
|
|
206
208
|
// budget snapshots whatever the host's default is on each call.
|
|
207
209
|
// The KEY invariant: the retry call's budget MUST NOT be multiplied
|
|
@@ -214,7 +216,7 @@ describe.skipIf(HTTP_SKIP)('envelope-completion-distinguishes-truncation: schema
|
|
|
214
216
|
if (budget !== null) {
|
|
215
217
|
expect(
|
|
216
218
|
budget,
|
|
217
|
-
|
|
219
|
+
req('openwop.it.envelope-completion-distinguishes-truncation.schema-violation-retry-budget-unchanged-from-initial-no-budget-multiplication-on',
|
|
218
220
|
'RFCS/0033-envelope-completion-contract.md §C',
|
|
219
221
|
'schema-violation retry MUST NOT multiply maxTokens — budget stays at the original value (host default)',
|
|
220
222
|
),
|
|
@@ -28,6 +28,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
28
28
|
import { driver } from '../lib/driver.js';
|
|
29
29
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
30
30
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
31
|
+
import { req } from '../lib/requirement-ids.js';
|
|
32
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
31
33
|
|
|
32
34
|
const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
|
|
33
35
|
const FIXTURE = 'conformance-envelope-nl-to-format-engaged';
|
|
@@ -69,17 +71,17 @@ const NL_THEN_COERCED_PROGRAM = [
|
|
|
69
71
|
|
|
70
72
|
describe.skipIf(HTTP_SKIP)('envelope-nl-to-format-engaged: runtime behavior (RFC 0032 §B.5 MAY)', () => {
|
|
71
73
|
it('when retry exhaustion triggers the NL-to-Format fallback, exactly one `envelope.nlToFormat.engaged` event fires', async () => {
|
|
72
|
-
if (!isFixtureAdvertised(FIXTURE)) return;
|
|
74
|
+
if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
73
75
|
const seed = await programMock(NL_THEN_COERCED_PROGRAM);
|
|
74
|
-
if (seed.status === 404) return;
|
|
76
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
75
77
|
expect(seed.status).toBe(200);
|
|
76
78
|
|
|
77
79
|
const events = await runAndReadEvents();
|
|
78
|
-
if (events === null) return;
|
|
80
|
+
if (events === null) return softSkip('blocked', 'precondition not met — `events === null` returned early (seam, prior step, or fixture unavailable)');
|
|
79
81
|
const engagements = events.filter((e) => e.type === 'envelope.nlToFormat.engaged');
|
|
80
82
|
expect(
|
|
81
83
|
engagements.length,
|
|
82
|
-
|
|
84
|
+
req('openwop.it.envelope-nl-to-format-engaged.when-retry-exhaustion-triggers-the-nl-to-format-fallback-exactly-one-envelope-nl',
|
|
83
85
|
'RFCS/0032-envelope-reliability-events.md §B.5',
|
|
84
86
|
'exactly one envelope.nlToFormat.engaged event MUST fire when the host detects NL-shape responses after retry exhaustion',
|
|
85
87
|
),
|
|
@@ -87,17 +89,17 @@ describe.skipIf(HTTP_SKIP)('envelope-nl-to-format-engaged: runtime behavior (RFC
|
|
|
87
89
|
});
|
|
88
90
|
|
|
89
91
|
it('`originalEnvelopeType` carries the envelope kind the original attempt targeted', async () => {
|
|
90
|
-
if (!isFixtureAdvertised(FIXTURE)) return;
|
|
92
|
+
if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
91
93
|
const seed = await programMock(NL_THEN_COERCED_PROGRAM);
|
|
92
|
-
if (seed.status === 404) return;
|
|
94
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
93
95
|
|
|
94
96
|
const events = await runAndReadEvents();
|
|
95
|
-
if (events === null) return;
|
|
97
|
+
if (events === null) return softSkip('blocked', 'precondition not met — `events === null` returned early (seam, prior step, or fixture unavailable)');
|
|
96
98
|
const engagement = events.find((e) => e.type === 'envelope.nlToFormat.engaged');
|
|
97
99
|
expect(engagement).toBeDefined();
|
|
98
100
|
expect(
|
|
99
101
|
typeof engagement!.payload?.originalEnvelopeType,
|
|
100
|
-
|
|
102
|
+
req('openwop.it.envelope-nl-to-format-engaged.originalenvelopetype-carries-the-envelope-kind-the-original-attempt-targeted',
|
|
101
103
|
'RFCS/0032-envelope-reliability-events.md §B.5',
|
|
102
104
|
'originalEnvelopeType MUST be present and string-typed — derived from the response-schema or wrapping metadata',
|
|
103
105
|
),
|
|
@@ -106,19 +108,19 @@ describe.skipIf(HTTP_SKIP)('envelope-nl-to-format-engaged: runtime behavior (RFC
|
|
|
106
108
|
});
|
|
107
109
|
|
|
108
110
|
it('`fallbackCalls >= 1` reports the number of secondary LLM calls used to reformat free-form output into the envelope schema', async () => {
|
|
109
|
-
if (!isFixtureAdvertised(FIXTURE)) return;
|
|
111
|
+
if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
110
112
|
const seed = await programMock(NL_THEN_COERCED_PROGRAM);
|
|
111
|
-
if (seed.status === 404) return;
|
|
113
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
112
114
|
|
|
113
115
|
const events = await runAndReadEvents();
|
|
114
|
-
if (events === null) return;
|
|
116
|
+
if (events === null) return softSkip('blocked', 'precondition not met — `events === null` returned early (seam, prior step, or fixture unavailable)');
|
|
115
117
|
const engagement = events.find((e) => e.type === 'envelope.nlToFormat.engaged');
|
|
116
118
|
expect(engagement).toBeDefined();
|
|
117
119
|
const fallbackCalls = engagement!.payload?.fallbackCalls;
|
|
118
120
|
expect(typeof fallbackCalls).toBe('number');
|
|
119
121
|
expect(
|
|
120
122
|
fallbackCalls as number,
|
|
121
|
-
|
|
123
|
+
req('openwop.it.envelope-nl-to-format-engaged.fallbackcalls-1-reports-the-number-of-secondary-llm-calls-used-to-reformat-free',
|
|
122
124
|
'RFCS/0032-envelope-reliability-events.md §B.5',
|
|
123
125
|
'fallbackCalls MUST be >= 1 — the fallback fired at least one secondary call to reformat the free-form output',
|
|
124
126
|
),
|
|
@@ -126,16 +128,16 @@ describe.skipIf(HTTP_SKIP)('envelope-nl-to-format-engaged: runtime behavior (RFC
|
|
|
126
128
|
});
|
|
127
129
|
|
|
128
130
|
it('the eventual envelope acceptance (when fallback succeeds) records normally via downstream RunEventDoc', async () => {
|
|
129
|
-
if (!isFixtureAdvertised(FIXTURE)) return;
|
|
131
|
+
if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
130
132
|
const seed = await programMock(NL_THEN_COERCED_PROGRAM);
|
|
131
|
-
if (seed.status === 404) return;
|
|
133
|
+
if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
132
134
|
|
|
133
135
|
const events = await runAndReadEvents();
|
|
134
|
-
if (events === null) return;
|
|
136
|
+
if (events === null) return softSkip('blocked', 'precondition not met — `events === null` returned early (seam, prior step, or fixture unavailable)');
|
|
135
137
|
const nodeCompleted = events.find((e) => e.type === 'node.completed' && e.nodeId === NODE_ID);
|
|
136
138
|
expect(
|
|
137
139
|
nodeCompleted,
|
|
138
|
-
|
|
140
|
+
req('openwop.it.envelope-nl-to-format-engaged.the-eventual-envelope-acceptance-when-fallback-succeeds-records-normally-via-dow',
|
|
139
141
|
'RFCS/0032-envelope-reliability-events.md §B.5',
|
|
140
142
|
'NL-to-Format fallback success MUST reach node.completed — the coerced envelope flows downstream like any other accepted envelope',
|
|
141
143
|
),
|
|
@@ -143,7 +145,7 @@ describe.skipIf(HTTP_SKIP)('envelope-nl-to-format-engaged: runtime behavior (RFC
|
|
|
143
145
|
const completedPayload = JSON.stringify(nodeCompleted?.payload ?? {});
|
|
144
146
|
expect(
|
|
145
147
|
completedPayload.includes('coerced-ok'),
|
|
146
|
-
|
|
148
|
+
req('openwop.it.envelope-nl-to-format-engaged.the-eventual-envelope-acceptance-when-fallback-succeeds-records-normally-via-dow',
|
|
147
149
|
'RFCS/0032-envelope-reliability-events.md §B.5',
|
|
148
150
|
'the coerced structured data from the secondary call MUST flow to the downstream RunEventDoc',
|
|
149
151
|
),
|