@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
|
@@ -26,9 +26,10 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import { describe, it, expect } from 'vitest';
|
|
29
|
-
import { driver } from '../lib/driver.js';
|
|
30
29
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
31
30
|
import { readManifestRuntimeCap, listManifestAgents } from '../lib/agentRuntime.js';
|
|
31
|
+
import { req } from '../lib/requirement-ids.js';
|
|
32
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
32
33
|
|
|
33
34
|
interface InventoryEntry {
|
|
34
35
|
agentId?: string;
|
|
@@ -43,13 +44,13 @@ describe('agent-capability-degraded-projection (RFC 0092 §B)', () => {
|
|
|
43
44
|
if (!behaviorGate('openwop-agent-capability-degraded', mr?.supported === true)) return;
|
|
44
45
|
|
|
45
46
|
const inv = await listManifestAgents();
|
|
46
|
-
if (inv === null) return; // cap advertised but /v1/agents unserved — soft-skip
|
|
47
|
+
if (inv === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `inv === null` returned early (cap advertised but /v1/agents unserved — soft-skip)'); // cap advertised but /v1/agents unserved — soft-skip
|
|
47
48
|
const agents = (inv.agents ?? []) as InventoryEntry[];
|
|
48
49
|
|
|
49
50
|
// Non-vacuity: an advertising + serving host MUST expose its inventory.
|
|
50
51
|
expect(
|
|
51
52
|
agents.length >= 1,
|
|
52
|
-
|
|
53
|
+
req('openwop.it.agent-capability-degraded-projection.surfaces-unmet-requirescapabilities-in-degraded-and-nothing-on-the-rest', 'RFC 0072 §A', 'GET /v1/agents MUST return the installed manifest agents'),
|
|
53
54
|
).toBe(true);
|
|
54
55
|
|
|
55
56
|
// §B well-formedness on EVERY entry: degraded[], when present, is a unique
|
|
@@ -59,18 +60,18 @@ describe('agent-capability-degraded-projection (RFC 0092 §B)', () => {
|
|
|
59
60
|
if (d !== undefined) {
|
|
60
61
|
expect(
|
|
61
62
|
Array.isArray(d),
|
|
62
|
-
|
|
63
|
+
req('openwop.it.agent-capability-degraded-projection.surfaces-unmet-requirescapabilities-in-degraded-and-nothing-on-the-rest', 'agent-inventory-response.schema.json', `degraded MUST be an array when present (agent ${a.agentId})`),
|
|
63
64
|
).toBe(true);
|
|
64
65
|
if (Array.isArray(d)) {
|
|
65
66
|
for (const k of d) {
|
|
66
67
|
expect(
|
|
67
68
|
typeof k === 'string' && k.length > 0,
|
|
68
|
-
|
|
69
|
+
req('openwop.it.agent-capability-degraded-projection.surfaces-unmet-requirescapabilities-in-degraded-and-nothing-on-the-rest', 'RFC 0072 §C', `degraded[] members MUST be non-empty strings (agent ${a.agentId}, got ${String(k)})`),
|
|
69
70
|
).toBe(true);
|
|
70
71
|
}
|
|
71
72
|
expect(
|
|
72
73
|
new Set(d as string[]).size === d.length,
|
|
73
|
-
|
|
74
|
+
req('openwop.it.agent-capability-degraded-projection.surfaces-unmet-requirescapabilities-in-degraded-and-nothing-on-the-rest', 'RFC 0092 §B', `degraded[] MUST be unique (agent ${a.agentId})`),
|
|
74
75
|
).toBe(true);
|
|
75
76
|
}
|
|
76
77
|
}
|
|
@@ -82,23 +83,23 @@ describe('agent-capability-degraded-projection (RFC 0092 §B)', () => {
|
|
|
82
83
|
const target = agents.find((a) => a.agentId === degradedId);
|
|
83
84
|
expect(
|
|
84
85
|
target !== undefined,
|
|
85
|
-
|
|
86
|
+
req('openwop.it.agent-capability-degraded-projection.surfaces-unmet-requirescapabilities-in-degraded-and-nothing-on-the-rest', 'RFC 0092 §B', `OPENWOP_DEGRADED_CAPABILITY_AGENT_ID=${degradedId} MUST appear in the inventory`),
|
|
86
87
|
).toBe(true);
|
|
87
88
|
if (target) {
|
|
88
89
|
const d = target.degraded;
|
|
89
90
|
expect(
|
|
90
91
|
Array.isArray(d) && d.length >= 1,
|
|
91
|
-
|
|
92
|
+
req('openwop.it.agent-capability-degraded-projection.surfaces-unmet-requirescapabilities-in-degraded-and-nothing-on-the-rest', 'RFC 0092 §B', 'the named capability-degraded agent MUST carry a non-empty degraded[]'),
|
|
92
93
|
).toBe(true);
|
|
93
94
|
// When the entry also exposes requiresCapabilities, every degraded key
|
|
94
95
|
// MUST be one the agent actually requires (the projection is a subset of
|
|
95
96
|
// requirements, never invented).
|
|
96
97
|
if (Array.isArray(d) && Array.isArray(target.requiresCapabilities)) {
|
|
97
|
-
const
|
|
98
|
+
const required = new Set(target.requiresCapabilities as unknown[]);
|
|
98
99
|
for (const k of d) {
|
|
99
100
|
expect(
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
required.has(k),
|
|
102
|
+
req('openwop.it.agent-capability-degraded-projection.surfaces-unmet-requirescapabilities-in-degraded-and-nothing-on-the-rest', 'RFC 0092 §B', `a degraded[] key MUST be one the agent requires (got ${String(k)})`),
|
|
102
103
|
).toBe(true);
|
|
103
104
|
}
|
|
104
105
|
}
|
|
@@ -43,6 +43,8 @@ import { pollUntilTerminal } from '../lib/polling.js';
|
|
|
43
43
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
44
44
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
45
45
|
import { readDeploymentCap, driveDeploymentTransition } from '../lib/agentDeployment.js';
|
|
46
|
+
import { req } from '../lib/requirement-ids.js';
|
|
47
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
46
48
|
|
|
47
49
|
const FIXTURE_ID = 'conformance-agent-channel-dispatch';
|
|
48
50
|
const BOUND_CHANNEL = 'stable';
|
|
@@ -93,7 +95,7 @@ async function startChannelRun(): Promise<string> {
|
|
|
93
95
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE_ID });
|
|
94
96
|
expect(
|
|
95
97
|
create.status,
|
|
96
|
-
|
|
98
|
+
req('openwop.it.agent-channel-dispatch.resolves-records-the-channel-pin-on-a-real-run-and-re-reads-it-on-replay-never-r',
|
|
97
99
|
'agent-deployment.md §B',
|
|
98
100
|
`a host advertising agents.deployment + the ${FIXTURE_ID} fixture MUST accept a channel-bound run (201)`,
|
|
99
101
|
),
|
|
@@ -111,12 +113,12 @@ describe.skipIf(HTTP_SKIP)('agent-channel-dispatch (RFC 0082 §B): production ru
|
|
|
111
113
|
// Host advertises agents.deployment but hasn't seeded the channel-bound
|
|
112
114
|
// fixture — a host-config precondition, not a conformance failure.
|
|
113
115
|
ctx.skip();
|
|
114
|
-
return;
|
|
116
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE_ID)` returned early');
|
|
115
117
|
}
|
|
116
118
|
const modes = await fetchReplayModes();
|
|
117
119
|
if (!modes.includes('replay')) {
|
|
118
120
|
ctx.skip();
|
|
119
|
-
return;
|
|
121
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!modes.includes(\'replay\')` returned early');
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
// ---- Leg 1: production-path channel resolution + recorded pin (§B) ----
|
|
@@ -124,14 +126,14 @@ describe.skipIf(HTTP_SKIP)('agent-channel-dispatch (RFC 0082 §B): production ru
|
|
|
124
126
|
const started = await firstInvocationStarted(sourceRunId);
|
|
125
127
|
expect(
|
|
126
128
|
started !== null,
|
|
127
|
-
|
|
129
|
+
req('openwop.it.agent-channel-dispatch.resolves-records-the-channel-pin-on-a-real-run-and-re-reads-it-on-replay-never-r',
|
|
128
130
|
'agent-deployment.md §B',
|
|
129
131
|
'a @channel-bound run MUST emit agent.invocation.started',
|
|
130
132
|
),
|
|
131
133
|
).toBe(true);
|
|
132
134
|
expect(
|
|
133
135
|
started!.payload.resolvedChannel,
|
|
134
|
-
|
|
136
|
+
req('openwop.it.agent-channel-dispatch.resolves-records-the-channel-pin-on-a-real-run-and-re-reads-it-on-replay-never-r',
|
|
135
137
|
'agent-deployment.md §B',
|
|
136
138
|
`agent.invocation.started MUST carry the bound channel as resolvedChannel ("${BOUND_CHANNEL}")`,
|
|
137
139
|
),
|
|
@@ -139,7 +141,7 @@ describe.skipIf(HTTP_SKIP)('agent-channel-dispatch (RFC 0082 §B): production ru
|
|
|
139
141
|
const pinnedVersion = started!.payload.resolvedAgentVersion;
|
|
140
142
|
expect(
|
|
141
143
|
typeof pinnedVersion === 'string' && (pinnedVersion as string).length > 0,
|
|
142
|
-
|
|
144
|
+
req('openwop.it.agent-channel-dispatch.resolves-records-the-channel-pin-on-a-real-run-and-re-reads-it-on-replay-never-r',
|
|
143
145
|
'agent-deployment.md §B',
|
|
144
146
|
'a @channel-bound run MUST record a concrete resolvedAgentVersion (the recorded fact a replay re-reads, RFC 0077)',
|
|
145
147
|
),
|
|
@@ -152,22 +154,22 @@ describe.skipIf(HTTP_SKIP)('agent-channel-dispatch (RFC 0082 §B): production ru
|
|
|
152
154
|
);
|
|
153
155
|
if (forkDeclined(fork1.status, 'channel-dispatch replay fork 1')) {
|
|
154
156
|
ctx.skip();
|
|
155
|
-
return;
|
|
157
|
+
return softSkip('blocked', 'precondition not met — `forkDeclined(fork1.status, \'channel-dispatch replay fork 1\')` returned early (seam, prior step, or fixture unavailable)');
|
|
156
158
|
}
|
|
157
159
|
expect(
|
|
158
160
|
fork1.status,
|
|
159
|
-
|
|
161
|
+
req('openwop.it.agent-channel-dispatch.resolves-records-the-channel-pin-on-a-real-run-and-re-reads-it-on-replay-never-r', 'rest-endpoints.md POST /v1/runs/{runId}:fork', 'replay fork MUST return 201'),
|
|
160
162
|
).toBe(201);
|
|
161
163
|
const fork1RunId = (fork1.json as { runId: string }).runId;
|
|
162
164
|
await pollUntilTerminal(fork1RunId, { timeoutMs: 15_000 });
|
|
163
165
|
const fork1Started = await firstInvocationStarted(fork1RunId);
|
|
164
166
|
expect(
|
|
165
167
|
fork1Started !== null,
|
|
166
|
-
|
|
168
|
+
req('openwop.it.agent-channel-dispatch.resolves-records-the-channel-pin-on-a-real-run-and-re-reads-it-on-replay-never-r', 'agent-deployment.md §B', 'a replay fork MUST re-emit agent.invocation.started'),
|
|
167
169
|
).toBe(true);
|
|
168
170
|
expect(
|
|
169
171
|
fork1Started!.payload.resolvedAgentVersion,
|
|
170
|
-
|
|
172
|
+
req('openwop.it.agent-channel-dispatch.resolves-records-the-channel-pin-on-a-real-run-and-re-reads-it-on-replay-never-r',
|
|
171
173
|
'agent-deployment.md §B',
|
|
172
174
|
'a replay MUST re-read the recorded resolvedAgentVersion (NOT re-resolve the channel)',
|
|
173
175
|
),
|
|
@@ -196,7 +198,7 @@ describe.skipIf(HTTP_SKIP)('agent-channel-dispatch (RFC 0082 §B): production ru
|
|
|
196
198
|
// evidence; the cross-move proof needs the seam. Honest skip of Leg 3.
|
|
197
199
|
// eslint-disable-next-line no-console
|
|
198
200
|
console.warn('[agent-channel-dispatch] deployment seam absent — skipping the channel-move non-re-resolution leg (Leg 3)');
|
|
199
|
-
return;
|
|
201
|
+
return softSkip('blocked', 'precondition not met — `moved === null` returned early ([agent-channel-dispatch] deployment seam absent — skipping the channel-move non-re-resolution leg (Leg 3)) (seam, prior step, or fixture unavailable)');
|
|
200
202
|
}
|
|
201
203
|
// Confirm the move is OBSERVABLE: a fresh channel-bound run must now resolve
|
|
202
204
|
// to a DIFFERENT version. If it doesn't (canary split, no-op promote), we
|
|
@@ -207,7 +209,7 @@ describe.skipIf(HTTP_SKIP)('agent-channel-dispatch (RFC 0082 §B): production ru
|
|
|
207
209
|
if (typeof movedVersion !== 'string' || movedVersion === pinnedVersion) {
|
|
208
210
|
// eslint-disable-next-line no-console
|
|
209
211
|
console.warn('[agent-channel-dispatch] channel did not observably move — skipping Leg 3 strict assertion');
|
|
210
|
-
return;
|
|
212
|
+
return softSkip('blocked', 'precondition not met — `typeof movedVersion !== \'string\' || movedVersion === pinnedVersion` returned early ([agent-channel-dispatch] channel did not observably move — skipping Leg 3 strict assertion) (seam, prior step…');
|
|
211
213
|
}
|
|
212
214
|
const fork2 = await driver.post(
|
|
213
215
|
`/v1/runs/${encodeURIComponent(sourceRunId)}:fork`,
|
|
@@ -215,25 +217,25 @@ describe.skipIf(HTTP_SKIP)('agent-channel-dispatch (RFC 0082 §B): production ru
|
|
|
215
217
|
);
|
|
216
218
|
if (forkDeclined(fork2.status, 'channel-dispatch replay fork 2')) {
|
|
217
219
|
ctx.skip();
|
|
218
|
-
return;
|
|
220
|
+
return softSkip('blocked', 'precondition not met — `forkDeclined(fork2.status, \'channel-dispatch replay fork 2\')` returned early (seam, prior step, or fixture unavailable)');
|
|
219
221
|
}
|
|
220
222
|
expect(
|
|
221
223
|
fork2.status,
|
|
222
|
-
|
|
224
|
+
req('openwop.it.agent-channel-dispatch.resolves-records-the-channel-pin-on-a-real-run-and-re-reads-it-on-replay-never-r', 'rest-endpoints.md POST /v1/runs/{runId}:fork', 'replay fork MUST return 201'),
|
|
223
225
|
).toBe(201);
|
|
224
226
|
const fork2RunId = (fork2.json as { runId: string }).runId;
|
|
225
227
|
await pollUntilTerminal(fork2RunId, { timeoutMs: 15_000 });
|
|
226
228
|
const fork2Started = await firstInvocationStarted(fork2RunId);
|
|
227
229
|
expect(
|
|
228
230
|
fork2Started?.payload.resolvedAgentVersion,
|
|
229
|
-
|
|
231
|
+
req('openwop.it.agent-channel-dispatch.resolves-records-the-channel-pin-on-a-real-run-and-re-reads-it-on-replay-never-r',
|
|
230
232
|
'agent-deployment.md §B',
|
|
231
233
|
'after the channel moves, a replay of the original run MUST still carry the ORIGINAL pin — never re-resolving the moved channel',
|
|
232
234
|
),
|
|
233
235
|
).toBe(pinnedVersion);
|
|
234
236
|
expect(
|
|
235
237
|
fork2Started?.payload.resolvedAgentVersion,
|
|
236
|
-
|
|
238
|
+
req('openwop.it.agent-channel-dispatch.resolves-records-the-channel-pin-on-a-real-run-and-re-reads-it-on-replay-never-r',
|
|
237
239
|
'agent-deployment.md §B',
|
|
238
240
|
'a replay MUST NOT resolve to the post-move version (proves the recorded fact is re-read, not re-resolved)',
|
|
239
241
|
),
|
|
@@ -33,7 +33,6 @@ import { readFileSync } from 'node:fs';
|
|
|
33
33
|
import { join } from 'node:path';
|
|
34
34
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
35
35
|
import addFormats from 'ajv-formats';
|
|
36
|
-
import { driver } from '../lib/driver.js';
|
|
37
36
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
38
37
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
39
38
|
import {
|
|
@@ -43,6 +42,8 @@ import {
|
|
|
43
42
|
DEPLOYMENT_CONTENT_FORBIDDEN,
|
|
44
43
|
} from '../lib/agentDeployment.js';
|
|
45
44
|
import { queryTestEvents, requireEvents, isEventLogSeamAvailable, resetTestSeam } from '../lib/event-log-query.js';
|
|
45
|
+
import { req } from '../lib/requirement-ids.js';
|
|
46
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
46
47
|
|
|
47
48
|
function loadSchema(name: string): Record<string, unknown> {
|
|
48
49
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
|
|
@@ -52,7 +53,7 @@ function expectContentFree(payload: Record<string, unknown>, where: string): voi
|
|
|
52
53
|
for (const f of DEPLOYMENT_CONTENT_FORBIDDEN) {
|
|
53
54
|
expect(
|
|
54
55
|
!(f in payload),
|
|
55
|
-
|
|
56
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'RFC 0082 §D (deployment-event-no-content-leak)', `${where} MUST be content-free (no ${f})`),
|
|
56
57
|
).toBe(true);
|
|
57
58
|
}
|
|
58
59
|
}
|
|
@@ -61,7 +62,7 @@ describe('agent-deployment-lifecycle (RFC 0082 §B/§E)', () => {
|
|
|
61
62
|
it('promotes via the eval+RBAC+approval gate, fails closed without scope/eval, and pins the channel version', async () => {
|
|
62
63
|
const cap = await readDeploymentCap();
|
|
63
64
|
if (!behaviorGate('openwop-deployment-lifecycle', cap?.supported === true)) return;
|
|
64
|
-
if (!(await isEventLogSeamAvailable())) return; // event-log seam absent — soft-skip
|
|
65
|
+
if (!(await isEventLogSeamAvailable())) return softSkip('blocked', 'precondition not met — `!(await isEventLogSeamAvailable())` returned early (event-log seam absent — soft-skip) (seam, prior step, or fixture unavailable)'); // event-log seam absent — soft-skip
|
|
65
66
|
|
|
66
67
|
const ajv = new Ajv2020({ strict: false, allErrors: true });
|
|
67
68
|
addFormats(ajv);
|
|
@@ -69,22 +70,22 @@ describe('agent-deployment-lifecycle (RFC 0082 §B/§E)', () => {
|
|
|
69
70
|
|
|
70
71
|
// ---- Leg 1: eval+RBAC+approval-gated promotion (§E) ------------------
|
|
71
72
|
const promote = await driveDeploymentTransition({ scenario: 'promote' });
|
|
72
|
-
if (promote === null) return; // deployment seam unwired — soft-skip the whole behavioral suite
|
|
73
|
+
if (promote === null) return softSkip('blocked', 'precondition not met — `promote === null` returned early (deployment seam unwired — soft-skip the whole behavioral suite) (seam, prior step, or fixture unavailable)'); // deployment seam unwired — soft-skip the whole behavioral suite
|
|
73
74
|
|
|
74
75
|
// The host has ADVERTISED agents.deployment AND wired the seam — missing
|
|
75
76
|
// evidence is a FAILURE, not a soft-skip. A successful promote MUST return
|
|
76
77
|
// a runId + a schema-valid record + emit ≥1 content-free deployment.promoted.
|
|
77
78
|
expect(
|
|
78
79
|
typeof promote.runId === 'string' && (promote.runId as string).length > 0,
|
|
79
|
-
|
|
80
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'agent-deployment.md §E', 'a wired promote MUST return the runId'),
|
|
80
81
|
).toBe(true);
|
|
81
82
|
expect(
|
|
82
83
|
promote.record !== undefined && promote.record !== null,
|
|
83
|
-
|
|
84
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'agent-deployment.md §E', 'a successful promote MUST return the deployment record'),
|
|
84
85
|
).toBe(true);
|
|
85
86
|
expect(
|
|
86
87
|
validateRecord(promote.record),
|
|
87
|
-
|
|
88
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'agent-deployment.schema.json', `a promoted deployment record MUST validate (${ajv.errorsText(validateRecord.errors)})`),
|
|
88
89
|
).toBe(true);
|
|
89
90
|
|
|
90
91
|
const promotedEvents = requireEvents(
|
|
@@ -93,17 +94,17 @@ describe('agent-deployment-lifecycle (RFC 0082 §B/§E)', () => {
|
|
|
93
94
|
);
|
|
94
95
|
expect(
|
|
95
96
|
promotedEvents.length >= 1,
|
|
96
|
-
|
|
97
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'agent-deployment.md §E', 'a successful promote MUST emit at least one deployment.promoted'),
|
|
97
98
|
).toBe(true);
|
|
98
99
|
for (const e of promotedEvents) {
|
|
99
100
|
expectContentFree(e.payload, 'deployment.promoted');
|
|
100
101
|
expect(
|
|
101
102
|
typeof e.payload.toState === 'string' && DEPLOYMENT_STATES.includes(e.payload.toState as string),
|
|
102
|
-
|
|
103
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'run-event-payloads.schema.json#/$defs/deploymentPromoted', 'toState MUST be in the seven-state vocabulary'),
|
|
103
104
|
).toBe(true);
|
|
104
105
|
expect(
|
|
105
106
|
typeof e.payload.toVersion === 'string' && (e.payload.toVersion as string).length > 0,
|
|
106
|
-
|
|
107
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'agent-deployment.md §D', 'deployment.promoted MUST carry the promoted toVersion'),
|
|
107
108
|
).toBe(true);
|
|
108
109
|
}
|
|
109
110
|
|
|
@@ -111,11 +112,11 @@ describe('agent-deployment-lifecycle (RFC 0082 §B/§E)', () => {
|
|
|
111
112
|
const unauth = await driveDeploymentTransition({ scenario: 'unauthorized' });
|
|
112
113
|
expect(
|
|
113
114
|
unauth !== null && typeof unauth.runId === 'string' && (unauth.runId as string).length > 0,
|
|
114
|
-
|
|
115
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'agent-deployment.md §E-1', 'the unauthorized scenario MUST return a runId to evidence the fail-closed denial'),
|
|
115
116
|
).toBe(true);
|
|
116
117
|
expect(
|
|
117
118
|
unauth!.allowed !== true,
|
|
118
|
-
|
|
119
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'agent-deployment.md §E-1', 'a principal without deploy:promote MUST be denied (fail-closed)'),
|
|
119
120
|
).toBe(true);
|
|
120
121
|
const unauthPromoted = requireEvents(
|
|
121
122
|
await queryTestEvents(unauth!.runId as string, { type: 'deployment.promoted' }),
|
|
@@ -123,18 +124,18 @@ describe('agent-deployment-lifecycle (RFC 0082 §B/§E)', () => {
|
|
|
123
124
|
);
|
|
124
125
|
expect(
|
|
125
126
|
unauthPromoted.length === 0,
|
|
126
|
-
|
|
127
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'SECURITY invariant deployment-promotion-fail-closed', 'a denied transition MUST emit NO deployment.promoted'),
|
|
127
128
|
).toBe(true);
|
|
128
129
|
|
|
129
130
|
// ---- Leg 3: eval-gate-unmet denial (§E-3) ----------------------------
|
|
130
131
|
const evalUnmet = await driveDeploymentTransition({ scenario: 'eval-gate-unmet' });
|
|
131
132
|
expect(
|
|
132
133
|
evalUnmet !== null && typeof evalUnmet.runId === 'string' && (evalUnmet.runId as string).length > 0,
|
|
133
|
-
|
|
134
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'agent-deployment.md §E-3', 'the eval-gate-unmet scenario MUST return a runId to evidence the denial'),
|
|
134
135
|
).toBe(true);
|
|
135
136
|
expect(
|
|
136
137
|
evalUnmet!.error === 'eval_gate_unmet' || evalUnmet!.allowed !== true,
|
|
137
|
-
|
|
138
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'agent-deployment.md §E-3', 'a promote whose eval evidence has passed:false MUST be denied (eval_gate_unmet)'),
|
|
138
139
|
).toBe(true);
|
|
139
140
|
const evalUnmetPromoted = requireEvents(
|
|
140
141
|
await queryTestEvents(evalUnmet!.runId as string, { type: 'deployment.promoted' }),
|
|
@@ -142,14 +143,14 @@ describe('agent-deployment-lifecycle (RFC 0082 §B/§E)', () => {
|
|
|
142
143
|
);
|
|
143
144
|
expect(
|
|
144
145
|
evalUnmetPromoted.length === 0,
|
|
145
|
-
|
|
146
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'agent-deployment.md §E-3', 'an unmet eval gate MUST emit NO deployment.promoted'),
|
|
146
147
|
).toBe(true);
|
|
147
148
|
|
|
148
149
|
// ---- Leg 4: channel-resolution pin (§B) ------------------------------
|
|
149
150
|
const pin = await driveDeploymentTransition({ scenario: 'channel-pin', channel: 'stable' });
|
|
150
151
|
expect(
|
|
151
152
|
pin !== null && typeof pin.runId === 'string' && (pin.runId as string).length > 0,
|
|
152
|
-
|
|
153
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'agent-deployment.md §B', 'the channel-pin scenario MUST return a runId'),
|
|
153
154
|
).toBe(true);
|
|
154
155
|
const invEvents = requireEvents(
|
|
155
156
|
await queryTestEvents(pin!.runId as string, { type: 'agent.invocation.started' }),
|
|
@@ -157,12 +158,12 @@ describe('agent-deployment-lifecycle (RFC 0082 §B/§E)', () => {
|
|
|
157
158
|
);
|
|
158
159
|
expect(
|
|
159
160
|
invEvents.length >= 1,
|
|
160
|
-
|
|
161
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'agent-deployment.md §B', 'a @channel-bound run MUST emit agent.invocation.started'),
|
|
161
162
|
).toBe(true);
|
|
162
163
|
const startedInv = invEvents.sort((a, b) => a.sequence - b.sequence)[0]!;
|
|
163
164
|
expect(
|
|
164
165
|
typeof startedInv.payload.resolvedAgentVersion === 'string' && (startedInv.payload.resolvedAgentVersion as string).length > 0,
|
|
165
|
-
|
|
166
|
+
req('openwop.it.agent-deployment-lifecycle.promotes-via-the-eval-rbac-approval-gate-fails-closed-without-scope-eval-and-pin', 'agent-deployment.md §B', 'a @channel-bound run MUST record resolvedAgentVersion on agent.invocation.started (the recorded fact a replay re-reads)'),
|
|
166
167
|
).toBe(true);
|
|
167
168
|
|
|
168
169
|
await resetTestSeam();
|
|
@@ -38,9 +38,7 @@ import { join } from 'node:path';
|
|
|
38
38
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
39
39
|
import addFormats from 'ajv-formats';
|
|
40
40
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
41
|
-
|
|
42
|
-
/** Server-free assertion-message helper. */
|
|
43
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
41
|
+
import { req } from '../lib/requirement-ids.js';
|
|
44
42
|
|
|
45
43
|
function loadSchema(name: string): Record<string, unknown> {
|
|
46
44
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
|
|
@@ -51,11 +49,11 @@ describe('agent-deployment-shape: capability advertisement (RFC 0082, server-fre
|
|
|
51
49
|
const caps = loadSchema('capabilities.schema.json');
|
|
52
50
|
const agents = (caps.properties as Record<string, { properties?: Record<string, { properties?: Record<string, unknown> }> }>).agents;
|
|
53
51
|
const deployment = agents?.properties?.deployment;
|
|
54
|
-
expect(deployment,
|
|
52
|
+
expect(deployment, req('openwop.it.agent-deployment-shape.the-capabilities-schema-declares-agents-deployment-with-its-sub-flags', 'capabilities.md §agents', 'agents.deployment MUST be declared')).toBeDefined();
|
|
55
53
|
for (const flag of ['supported', 'channels', 'canary', 'rollback', 'states']) {
|
|
56
54
|
expect(
|
|
57
55
|
deployment?.properties?.[flag],
|
|
58
|
-
|
|
56
|
+
req('openwop.it.agent-deployment-shape.the-capabilities-schema-declares-agents-deployment-with-its-sub-flags', 'agent-deployment.md §F', `agents.deployment.${flag} MUST be declared`),
|
|
59
57
|
).toBeDefined();
|
|
60
58
|
}
|
|
61
59
|
});
|
|
@@ -69,16 +67,16 @@ describe('agent-deployment-shape: deployment record + AgentRef binding (RFC 0082
|
|
|
69
67
|
|
|
70
68
|
it('AgentDeployment validates a conforming record and rejects a bad state / out-of-range canary', () => {
|
|
71
69
|
const good = { agentId: 'core.openwop.agents.support-resolver', version: '2.4.0', state: 'active', canaryPercent: 10, channels: ['stable'] };
|
|
72
|
-
expect(record(good),
|
|
73
|
-
expect(record({ ...good, state: 'live' }),
|
|
74
|
-
expect(record({ ...good, canaryPercent: 150 }),
|
|
70
|
+
expect(record(good), req('openwop.it.agent-deployment-shape.agentdeployment-validates-a-conforming-record-and-rejects-a-bad-state-out-of-ran', 'RFC 0082 §C', 'a conforming deployment record MUST validate')).toBe(true);
|
|
71
|
+
expect(record({ ...good, state: 'live' }), req('openwop.it.agent-deployment-shape.agentdeployment-validates-a-conforming-record-and-rejects-a-bad-state-out-of-ran', 'RFC 0082 §C', 'an out-of-enum state MUST be rejected')).toBe(false);
|
|
72
|
+
expect(record({ ...good, canaryPercent: 150 }), req('openwop.it.agent-deployment-shape.agentdeployment-validates-a-conforming-record-and-rejects-a-bad-state-out-of-ran', 'RFC 0082 §C', 'canaryPercent > 100 MUST be rejected')).toBe(false);
|
|
75
73
|
});
|
|
76
74
|
|
|
77
75
|
it('AgentRef channel XOR version: each alone and neither validate; both is rejected (RFC 0082 §A)', () => {
|
|
78
|
-
expect(agentRef({ agentId: 'core.x.y.z', version: '1.0.0' }),
|
|
79
|
-
expect(agentRef({ agentId: 'core.x.y.z', channel: 'stable' }),
|
|
80
|
-
expect(agentRef({ agentId: 'core.x.y.z' }),
|
|
81
|
-
expect(agentRef({ agentId: 'core.x.y.z', version: '1.0.0', channel: 'stable' }),
|
|
76
|
+
expect(agentRef({ agentId: 'core.x.y.z', version: '1.0.0' }), req('openwop.it.agent-deployment-shape.agentref-channel-xor-version-each-alone-and-neither-validate-both-is-rejected-rf', 'RFC 0082 §A', 'version-only AgentRef MUST validate')).toBe(true);
|
|
77
|
+
expect(agentRef({ agentId: 'core.x.y.z', channel: 'stable' }), req('openwop.it.agent-deployment-shape.agentref-channel-xor-version-each-alone-and-neither-validate-both-is-rejected-rf', 'RFC 0082 §A', 'channel-only AgentRef MUST validate')).toBe(true);
|
|
78
|
+
expect(agentRef({ agentId: 'core.x.y.z' }), req('openwop.it.agent-deployment-shape.agentref-channel-xor-version-each-alone-and-neither-validate-both-is-rejected-rf', 'RFC 0082 §A', 'a ref with neither version nor channel MUST validate (host default)')).toBe(true);
|
|
79
|
+
expect(agentRef({ agentId: 'core.x.y.z', version: '1.0.0', channel: 'stable' }), req('openwop.it.agent-deployment-shape.agentref-channel-xor-version-each-alone-and-neither-validate-both-is-rejected-rf', 'RFC 0082 §A', 'a ref with BOTH version and channel MUST be rejected')).toBe(false);
|
|
82
80
|
});
|
|
83
81
|
});
|
|
84
82
|
|
|
@@ -94,29 +92,29 @@ describe('agent-deployment-shape: deployment.* event payloads (RFC 0082, server-
|
|
|
94
92
|
const stateChanged = ajv.getSchema('payloads#/$defs/deploymentStateChanged');
|
|
95
93
|
|
|
96
94
|
it('deployment.promoted validates a content-free promotion record and requires toVersion + toState', () => {
|
|
97
|
-
expect(promoted, 'the deploymentPromoted $def MUST exist').toBeTruthy();
|
|
95
|
+
expect(promoted, req('openwop.it.agent-deployment-shape.deployment-promoted-validates-a-content-free-promotion-record-and-requires-tover', 'RFC 0082', 'the deploymentPromoted $def MUST exist')).toBeTruthy();
|
|
98
96
|
expect(
|
|
99
97
|
promoted!({ agentId: 'core.openwop.agents.support-resolver', toVersion: '2.4.0', toState: 'active', channel: 'stable', canaryPercent: 10, evalRunId: 'run_abc' }),
|
|
100
|
-
|
|
98
|
+
req('openwop.it.agent-deployment-shape.deployment-promoted-validates-a-content-free-promotion-record-and-requires-tover', 'RFC 0082 §D', 'a conforming deployment.promoted payload MUST validate'),
|
|
101
99
|
).toBe(true);
|
|
102
|
-
expect(promoted!({ agentId: 'a' }),
|
|
100
|
+
expect(promoted!({ agentId: 'a' }), req('openwop.it.agent-deployment-shape.deployment-promoted-validates-a-content-free-promotion-record-and-requires-tover', 'RFC 0082 §D', 'deployment.promoted without toVersion/toState MUST be rejected')).toBe(false);
|
|
103
101
|
});
|
|
104
102
|
|
|
105
103
|
it('deployment.rolled-back / canary.adjusted / state.changed validate conforming records', () => {
|
|
106
|
-
expect(rolledBack!({ agentId: 'a', fromVersion: '2.4.0', toVersion: '2.3.1', rollbackPointer: '2.3.1' }),
|
|
107
|
-
expect(canary!({ agentId: 'a', version: '2.4.0', fromPercent: 10, toPercent: 50 }),
|
|
108
|
-
expect(stateChanged!({ agentId: 'a', version: '2.4.0', fromState: 'active', toState: 'paused' }),
|
|
109
|
-
expect(stateChanged!({ agentId: 'a', version: '2.4.0', fromState: 'active', toState: 'live' }),
|
|
104
|
+
expect(rolledBack!({ agentId: 'a', fromVersion: '2.4.0', toVersion: '2.3.1', rollbackPointer: '2.3.1' }), req('openwop.it.agent-deployment-shape.deployment-rolled-back-canary-adjusted-state-changed-validate-conforming-records', 'RFC 0082 §D', 'a conforming deployment.rolled-back MUST validate')).toBe(true);
|
|
105
|
+
expect(canary!({ agentId: 'a', version: '2.4.0', fromPercent: 10, toPercent: 50 }), req('openwop.it.agent-deployment-shape.deployment-rolled-back-canary-adjusted-state-changed-validate-conforming-records', 'RFC 0082 §D', 'a conforming deployment.canary.adjusted MUST validate')).toBe(true);
|
|
106
|
+
expect(stateChanged!({ agentId: 'a', version: '2.4.0', fromState: 'active', toState: 'paused' }), req('openwop.it.agent-deployment-shape.deployment-rolled-back-canary-adjusted-state-changed-validate-conforming-records', 'RFC 0082 §D', 'a conforming deployment.state.changed MUST validate')).toBe(true);
|
|
107
|
+
expect(stateChanged!({ agentId: 'a', version: '2.4.0', fromState: 'active', toState: 'live' }), req('openwop.it.agent-deployment-shape.deployment-rolled-back-canary-adjusted-state-changed-validate-conforming-records', 'RFC 0082 §D', 'an out-of-enum toState MUST be rejected')).toBe(false);
|
|
110
108
|
});
|
|
111
109
|
|
|
112
110
|
it('deployment.* events are content-free — a manifest body and a prompt are rejected (deployment-event-no-content-leak)', () => {
|
|
113
111
|
expect(
|
|
114
112
|
promoted!({ agentId: 'a', toVersion: '2.4.0', toState: 'active', manifestBody: '{...}' }),
|
|
115
|
-
|
|
113
|
+
req('openwop.it.agent-deployment-shape.deployment-events-are-content-free-a-manifest-body-and-a-prompt-are-rejected-dep', 'SECURITY invariant deployment-event-no-content-leak', 'a deployment.promoted MUST NOT carry a manifest body'),
|
|
116
114
|
).toBe(false);
|
|
117
115
|
expect(
|
|
118
116
|
stateChanged!({ agentId: 'a', version: '2.4.0', fromState: 'active', toState: 'paused', prompt: 'system: …' }),
|
|
119
|
-
|
|
117
|
+
req('openwop.it.agent-deployment-shape.deployment-events-are-content-free-a-manifest-body-and-a-prompt-are-rejected-dep', 'SECURITY invariant deployment-event-no-content-leak', 'a deployment.state.changed MUST NOT carry prompt content'),
|
|
120
118
|
).toBe(false);
|
|
121
119
|
});
|
|
122
120
|
});
|
|
@@ -125,15 +123,15 @@ describe('agent-deployment-shape: §B recorded-fact pin + enum (RFC 0082, server
|
|
|
125
123
|
it('agent.invocation.started carries the additive recorded-fact resolvedAgentVersion / resolvedChannel', () => {
|
|
126
124
|
const payloads = loadSchema('run-event-payloads.schema.json');
|
|
127
125
|
const started = ((payloads.$defs as Record<string, { properties?: Record<string, unknown> }>).agentInvocationStarted)?.properties ?? {};
|
|
128
|
-
expect(started.resolvedAgentVersion,
|
|
129
|
-
expect(started.resolvedChannel,
|
|
126
|
+
expect(started.resolvedAgentVersion, req('openwop.it.agent-deployment-shape.agent-invocation-started-carries-the-additive-recorded-fact-resolvedagentversion', 'RFC 0082 §B', 'agent.invocation.started.resolvedAgentVersion MUST be declared (the channel pin)')).toBeDefined();
|
|
127
|
+
expect(started.resolvedChannel, req('openwop.it.agent-deployment-shape.agent-invocation-started-carries-the-additive-recorded-fact-resolvedagentversion', 'RFC 0082 §B', 'agent.invocation.started.resolvedChannel MUST be declared')).toBeDefined();
|
|
130
128
|
});
|
|
131
129
|
|
|
132
130
|
it('all four deployment event names appear in the RunEventType enum', () => {
|
|
133
131
|
const runEvent = loadSchema('run-event.schema.json');
|
|
134
132
|
const enumVals = (runEvent.$defs as Record<string, { enum?: string[] }>).RunEventType?.enum ?? [];
|
|
135
133
|
for (const e of ['deployment.promoted', 'deployment.rolled-back', 'deployment.canary.adjusted', 'deployment.state.changed']) {
|
|
136
|
-
expect(enumVals).toContain(e);
|
|
134
|
+
expect(enumVals, req('openwop.it.agent-deployment-shape.all-four-deployment-event-names-appear-in-the-runeventtype-enum', 'RFC 0082', 'all four deployment event names appear in the RunEventType enum')).toContain(e);
|
|
137
135
|
}
|
|
138
136
|
});
|
|
139
137
|
});
|
|
@@ -29,7 +29,6 @@ import { readFileSync } from 'node:fs';
|
|
|
29
29
|
import { join } from 'node:path';
|
|
30
30
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
31
31
|
import addFormats from 'ajv-formats';
|
|
32
|
-
import { driver } from '../lib/driver.js';
|
|
33
32
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
34
33
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
35
34
|
import {
|
|
@@ -39,6 +38,8 @@ import {
|
|
|
39
38
|
EVAL_CONTENT_FORBIDDEN,
|
|
40
39
|
} from '../lib/agentEval.js';
|
|
41
40
|
import { queryTestEvents, requireEvents, isEventLogSeamAvailable, resetTestSeam } from '../lib/event-log-query.js';
|
|
41
|
+
import { req } from '../lib/requirement-ids.js';
|
|
42
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
42
43
|
|
|
43
44
|
function loadSchema(name: string): Record<string, unknown> {
|
|
44
45
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
|
|
@@ -48,7 +49,7 @@ function expectContentFree(payload: Record<string, unknown>, where: string): voi
|
|
|
48
49
|
for (const f of EVAL_CONTENT_FORBIDDEN) {
|
|
49
50
|
expect(
|
|
50
51
|
!(f in payload),
|
|
51
|
-
|
|
52
|
+
req('openwop.it.agent-eval-run.emits-eval-started-per-task-eval-scored-eval-completed-and-serves-a-content-free', 'RFC 0081 §C (eval-summary-no-content-leak)', `${where} MUST be content-free (no ${f})`),
|
|
52
53
|
).toBe(true);
|
|
53
54
|
}
|
|
54
55
|
}
|
|
@@ -57,10 +58,10 @@ describe('agent-eval-run (RFC 0081 §B/§C)', () => {
|
|
|
57
58
|
it('emits eval.started → per-task eval.scored → eval.completed and serves a content-free EvalSummary', async () => {
|
|
58
59
|
const cap = await readEvalSuiteCap();
|
|
59
60
|
if (!behaviorGate('openwop-eval-run', cap?.supported === true)) return;
|
|
60
|
-
if (!(await isEventLogSeamAvailable())) return; // event-log seam absent — soft-skip
|
|
61
|
+
if (!(await isEventLogSeamAvailable())) return softSkip('blocked', 'precondition not met — `!(await isEventLogSeamAvailable())` returned early (event-log seam absent — soft-skip) (seam, prior step, or fixture unavailable)'); // event-log seam absent — soft-skip
|
|
61
62
|
|
|
62
63
|
const run = await driveEvalRun({ modes: ['golden'] });
|
|
63
|
-
if (run === null) return; // eval-run seam unwired — soft-skip the whole behavioral suite
|
|
64
|
+
if (run === null) return softSkip('blocked', 'precondition not met — `run === null` returned early (eval-run seam unwired — soft-skip the whole behavioral suite) (seam, prior step, or fixture unavailable)'); // eval-run seam unwired — soft-skip the whole behavioral suite
|
|
64
65
|
|
|
65
66
|
// From here the host has ADVERTISED agents.evalSuite AND wired the eval-run
|
|
66
67
|
// seam — missing evidence is a FAILURE, not a soft-skip. A host claiming the
|
|
@@ -68,7 +69,7 @@ describe('agent-eval-run (RFC 0081 §B/§C)', () => {
|
|
|
68
69
|
// normative EvalSummary, or it is advertising a capability it doesn't deliver.
|
|
69
70
|
expect(
|
|
70
71
|
typeof run.runId === 'string' && run.runId.length > 0,
|
|
71
|
-
|
|
72
|
+
req('openwop.it.agent-eval-run.emits-eval-started-per-task-eval-scored-eval-completed-and-serves-a-content-free', 'agent-evaluation.md §B', 'a wired eval-run seam MUST return the projected runId'),
|
|
72
73
|
).toBe(true);
|
|
73
74
|
const runId = run.runId as string;
|
|
74
75
|
|
|
@@ -87,15 +88,15 @@ describe('agent-eval-run (RFC 0081 §B/§C)', () => {
|
|
|
87
88
|
// ≥1 eval.scored — a wired eval run MUST emit the full sequence.
|
|
88
89
|
expect(
|
|
89
90
|
startedEvents.length === 1,
|
|
90
|
-
|
|
91
|
+
req('openwop.it.agent-eval-run.emits-eval-started-per-task-eval-scored-eval-completed-and-serves-a-content-free', 'agent-evaluation.md §C', 'an eval run MUST emit exactly one eval.started'),
|
|
91
92
|
).toBe(true);
|
|
92
93
|
expect(
|
|
93
94
|
scoredEvents.length >= 1,
|
|
94
|
-
|
|
95
|
+
req('openwop.it.agent-eval-run.emits-eval-started-per-task-eval-scored-eval-completed-and-serves-a-content-free', 'agent-evaluation.md §C', 'an eval run MUST emit at least one eval.scored'),
|
|
95
96
|
).toBe(true);
|
|
96
97
|
expect(
|
|
97
98
|
completedEvents.length === 1,
|
|
98
|
-
|
|
99
|
+
req('openwop.it.agent-eval-run.emits-eval-started-per-task-eval-scored-eval-completed-and-serves-a-content-free', 'agent-evaluation.md §C', 'an eval run MUST emit exactly one eval.completed'),
|
|
99
100
|
).toBe(true);
|
|
100
101
|
const started = startedEvents[0]!;
|
|
101
102
|
const completed = completedEvents[0]!;
|
|
@@ -104,22 +105,22 @@ describe('agent-eval-run (RFC 0081 §B/§C)', () => {
|
|
|
104
105
|
for (const s of scoredEvents) {
|
|
105
106
|
expect(
|
|
106
107
|
started.sequence < s.sequence,
|
|
107
|
-
|
|
108
|
+
req('openwop.it.agent-eval-run.emits-eval-started-per-task-eval-scored-eval-completed-and-serves-a-content-free', 'agent-evaluation.md §C', 'eval.started MUST precede every eval.scored'),
|
|
108
109
|
).toBe(true);
|
|
109
110
|
expect(
|
|
110
111
|
s.sequence < completed.sequence,
|
|
111
|
-
|
|
112
|
+
req('openwop.it.agent-eval-run.emits-eval-started-per-task-eval-scored-eval-completed-and-serves-a-content-free', 'agent-evaluation.md §C', 'every eval.scored MUST precede eval.completed'),
|
|
112
113
|
).toBe(true);
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
// One eval.scored per task (count == eval.completed.taskCount).
|
|
116
117
|
expect(
|
|
117
118
|
typeof completed.payload.taskCount === 'number',
|
|
118
|
-
|
|
119
|
+
req('openwop.it.agent-eval-run.emits-eval-started-per-task-eval-scored-eval-completed-and-serves-a-content-free', 'run-event-payloads.schema.json#/$defs/evalCompleted', 'eval.completed MUST carry a numeric taskCount'),
|
|
119
120
|
).toBe(true);
|
|
120
121
|
expect(
|
|
121
122
|
scoredEvents.length === completed.payload.taskCount,
|
|
122
|
-
|
|
123
|
+
req('openwop.it.agent-eval-run.emits-eval-started-per-task-eval-scored-eval-completed-and-serves-a-content-free', 'agent-evaluation.md §C', 'one eval.scored per task (count == eval.completed.taskCount)'),
|
|
123
124
|
).toBe(true);
|
|
124
125
|
|
|
125
126
|
// Content-free (§C / eval-summary-no-content-leak) + score ∈ 0..1, passed boolean.
|
|
@@ -129,11 +130,11 @@ describe('agent-eval-run (RFC 0081 §B/§C)', () => {
|
|
|
129
130
|
expectContentFree(s.payload, 'eval.scored');
|
|
130
131
|
expect(
|
|
131
132
|
typeof s.payload.score === 'number' && (s.payload.score as number) >= 0 && (s.payload.score as number) <= 1,
|
|
132
|
-
|
|
133
|
+
req('openwop.it.agent-eval-run.emits-eval-started-per-task-eval-scored-eval-completed-and-serves-a-content-free', 'run-event-payloads.schema.json#/$defs/evalScored', 'eval.scored.score MUST be in 0..1'),
|
|
133
134
|
).toBe(true);
|
|
134
135
|
expect(
|
|
135
136
|
typeof s.payload.passed === 'boolean',
|
|
136
|
-
|
|
137
|
+
req('openwop.it.agent-eval-run.emits-eval-started-per-task-eval-scored-eval-completed-and-serves-a-content-free', 'run-event-payloads.schema.json#/$defs/evalScored', 'eval.scored.passed MUST be a boolean'),
|
|
137
138
|
).toBe(true);
|
|
138
139
|
}
|
|
139
140
|
|
|
@@ -141,7 +142,7 @@ describe('agent-eval-run (RFC 0081 §B/§C)', () => {
|
|
|
141
142
|
const { status, summary } = await getEvalSummary(runId);
|
|
142
143
|
expect(
|
|
143
144
|
status === 200 && summary !== undefined,
|
|
144
|
-
|
|
145
|
+
req('openwop.it.agent-eval-run.emits-eval-started-per-task-eval-scored-eval-completed-and-serves-a-content-free', 'agent-evaluation.md §C', `GET /v1/runs/{runId}/eval-summary MUST serve a 200 EvalSummary for a completed eval run (got ${status})`),
|
|
145
146
|
).toBe(true);
|
|
146
147
|
const sum = summary as Record<string, unknown>;
|
|
147
148
|
const ajv = new Ajv2020({ strict: false, allErrors: true });
|
|
@@ -149,7 +150,7 @@ describe('agent-eval-run (RFC 0081 §B/§C)', () => {
|
|
|
149
150
|
const validate = ajv.compile(loadSchema('eval-summary.schema.json'));
|
|
150
151
|
expect(
|
|
151
152
|
validate(sum),
|
|
152
|
-
|
|
153
|
+
req('openwop.it.agent-eval-run.emits-eval-started-per-task-eval-scored-eval-completed-and-serves-a-content-free', 'eval-summary.schema.json', `EvalSummary MUST be schema-valid (${ajv.errorsText(validate.errors)})`),
|
|
153
154
|
).toBe(true);
|
|
154
155
|
|
|
155
156
|
const tasks = (sum.tasks as Array<Record<string, unknown>> | undefined) ?? [];
|
|
@@ -157,11 +158,11 @@ describe('agent-eval-run (RFC 0081 §B/§C)', () => {
|
|
|
157
158
|
const taskCount = sum.taskCount as number | undefined;
|
|
158
159
|
expect(
|
|
159
160
|
typeof passedCount === 'number' && typeof taskCount === 'number',
|
|
160
|
-
|
|
161
|
+
req('openwop.it.agent-eval-run.emits-eval-started-per-task-eval-scored-eval-completed-and-serves-a-content-free', 'eval-summary.schema.json', 'EvalSummary MUST carry numeric passedCount + taskCount'),
|
|
161
162
|
).toBe(true);
|
|
162
163
|
expect(
|
|
163
164
|
(passedCount as number) <= (taskCount as number),
|
|
164
|
-
|
|
165
|
+
req('openwop.it.agent-eval-run.emits-eval-started-per-task-eval-scored-eval-completed-and-serves-a-content-free', 'agent-evaluation.md §C', 'EvalSummary.passedCount MUST NOT exceed taskCount'),
|
|
165
166
|
).toBe(true);
|
|
166
167
|
for (const t of tasks) {
|
|
167
168
|
expectContentFree(t, 'EvalSummary.tasks[]');
|