@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
|
@@ -37,6 +37,7 @@ import { fileDisposition, deriveRequirementDispositions, requirementIdForFile, f
|
|
|
37
37
|
import { softSkip, softSkipDisposition, resetSoftSkips, UNCLASSIFIED_RETURN_DETAIL } from '../lib/soft-skip.js';
|
|
38
38
|
import { PROFILE_FLOOR_SCENARIOS } from '../lib/profiles.js';
|
|
39
39
|
import { requirementsFor, requirementIdForScenario, requirementIdForPrefix } from '../lib/requirement-registry.js';
|
|
40
|
+
import { req } from '../lib/requirement-ids.js';
|
|
40
41
|
|
|
41
42
|
describe('RFC 0148 §A (S6) — ledger file sink and reader', () => {
|
|
42
43
|
it('recordRequirement appends JSONL when OPENWOP_LEDGER_PATH is set; the reader merges and ranks conflicts least-certifiable-first', () => {
|
|
@@ -57,7 +58,7 @@ describe('RFC 0148 §A (S6) — ledger file sink and reader', () => {
|
|
|
57
58
|
expect(lines.length).toBe(4);
|
|
58
59
|
const merged = readLedgerFile(path);
|
|
59
60
|
expect(merged.map((e) => e.requirementId)).toEqual(['t.a', 't.b']);
|
|
60
|
-
expect(merged.find((e) => e.requirementId === 't.a')?.disposition, 'conflict resolves to the least certifiable (blocked over executed-pass)').toBe('blocked');
|
|
61
|
+
expect(merged.find((e) => e.requirementId === 't.a')?.disposition, req('openwop.it.runner-ledger.recordrequirement-appends-jsonl-when-openwop-ledger-path-is-set-the-reader-merge', 'RFC 0148 §A', 'conflict resolves to the least certifiable (blocked over executed-pass)')).toBe('blocked');
|
|
61
62
|
expect(merged.find((e) => e.requirementId === 't.b')?.disposition).toBe('inapplicable');
|
|
62
63
|
expect(readLedgerFile(join(dir, 'missing.jsonl'))).toEqual([]);
|
|
63
64
|
} finally {
|
|
@@ -71,7 +72,7 @@ describe('RFC 0148 §A (S6) — ledger file sink and reader', () => {
|
|
|
71
72
|
|
|
72
73
|
describe('RFC 0148 §A (S6) — fileDisposition folds a file honestly', () => {
|
|
73
74
|
it('fail beats pass; pass beats a gate reason; a gate reason beats blocked; nothing is blocked (unclassified)', () => {
|
|
74
|
-
expect(fileDisposition(['pass', 'fail', 'skip'], undefined).disposition).toBe('executed-fail');
|
|
75
|
+
expect(fileDisposition(['pass', 'fail', 'skip'], undefined).disposition, req('openwop.it.runner-ledger.fail-beats-pass-pass-beats-a-gate-reason-a-gate-reason-beats-blocked-nothing-is', 'RFC 0148 §A', 'fail beats pass; pass beats a gate reason; a gate reason beats blocked; nothing is blocked (unclassified)')).toBe('executed-fail');
|
|
75
76
|
expect(fileDisposition(['pass', 'skip'], 'inapplicable').disposition).toBe('executed-pass');
|
|
76
77
|
// real assertions + a gate reason → still an executed pass
|
|
77
78
|
expect(fileDisposition(['pass', 'skip'], 'inapplicable', 4).disposition).toBe('executed-pass');
|
|
@@ -90,7 +91,7 @@ describe('RFC 0148 §A (S6) — fileDisposition folds a file honestly', () => {
|
|
|
90
91
|
|
|
91
92
|
it('a floor file records under its §A floor id; any other file under openwop.scenario.*', () => {
|
|
92
93
|
const floor = [...floorScenarioFiles()];
|
|
93
|
-
expect(floor.length).toBeGreaterThan(0);
|
|
94
|
+
expect(floor.length, req('openwop.it.runner-ledger.a-floor-file-records-under-its-a-floor-id-any-other-file-under-openwop-scenario', 'RFC 0148 §A', 'a floor file records under its §A floor id; any other file under openwop.scenario.*')).toBeGreaterThan(0);
|
|
94
95
|
expect(requirementIdForFile(floor[0]!)).toBe(requirementIdForScenario(floor[0]!));
|
|
95
96
|
expect(requirementIdForFile('some-other.test.ts')).toBe('openwop.scenario.some-other');
|
|
96
97
|
});
|
|
@@ -119,7 +120,7 @@ describe('RFC 0148 §A (S6) — the runner derivation', () => {
|
|
|
119
120
|
|
|
120
121
|
it('ledger rows win, prefix requirements derive from matching files, and a fully recorded floor certifies', () => {
|
|
121
122
|
const d = deriveRequirementDispositions(reportAllPassed(), ledgerAllPassed(), [profile]);
|
|
122
|
-
expect(d.ledgerPresent).toBe(true);
|
|
123
|
+
expect(d.ledgerPresent, req('openwop.it.runner-ledger.ledger-rows-win-prefix-requirements-derive-from-matching-files-and-a-fully-recor', 'RFC 0148 §A', 'ledger rows win, prefix requirements derive from matching files, and a fully recorded floor certifies')).toBe(true);
|
|
123
124
|
expect(d.rejectUnclassified).toBe(false);
|
|
124
125
|
const v = d.verdicts.find((x) => x.profile === profile)!;
|
|
125
126
|
expect(v.certifiable, JSON.stringify(v)).toBe(true);
|
|
@@ -144,7 +145,7 @@ describe('RFC 0148 §A (S6) — the runner derivation', () => {
|
|
|
144
145
|
const row = d.requirements.find((r) => r.requirementId === requirementIdForScenario(files[0]!))!;
|
|
145
146
|
expect(row.disposition).toBe('executed-pass'); // vitest did say passed — the row is honest about the source
|
|
146
147
|
expect(row.detail).toMatch(/report-derived/);
|
|
147
|
-
expect(v.unclassified, 'no ledger entry ⇒ unclassified for a claimed floor').toContain(requirementIdForScenario(files[0]!));
|
|
148
|
+
expect(v.unclassified, req('openwop.it.runner-ledger.a-floor-file-that-vitest-passed-but-that-recorded-nothing-is-unclassified-when-a', 'RFC 0148 §A', 'no ledger entry ⇒ unclassified for a claimed floor')).toContain(requirementIdForScenario(files[0]!));
|
|
148
149
|
expect(v.certifiable).toBe(false);
|
|
149
150
|
expect(d.rejectUnclassified).toBe(true);
|
|
150
151
|
});
|
|
@@ -153,7 +154,7 @@ describe('RFC 0148 §A (S6) — the runner derivation', () => {
|
|
|
153
154
|
const l = ledgerAllPassed(5).map((e) => (e.requirementId === requirementIdForScenario(files[0]!) ? { ...e, assertionCount: 0 } : e));
|
|
154
155
|
const d = deriveRequirementDispositions(reportAllPassed(), l, [profile]);
|
|
155
156
|
const v = d.verdicts.find((x) => x.profile === profile)!;
|
|
156
|
-
expect(v.unclassified).toContain(requirementIdForScenario(files[0]!));
|
|
157
|
+
expect(v.unclassified, req('openwop.it.runner-ledger.a-vacuous-pass-executed-pass-with-assertioncount-0-on-a-claimed-floor-is-unclass', 'RFC 0148 §A', 'a VACUOUS pass (executed-pass with assertionCount 0) on a claimed floor is unclassified and REJECTS certification')).toContain(requirementIdForScenario(files[0]!));
|
|
157
158
|
expect(v.certifiable).toBe(false);
|
|
158
159
|
expect(d.rejectUnclassified).toBe(true);
|
|
159
160
|
});
|
|
@@ -163,7 +164,7 @@ describe('RFC 0148 §A (S6) — the runner derivation', () => {
|
|
|
163
164
|
const l = ledgerAllPassed().filter((e) => e.requirementId !== requirementIdForScenario(files[0]!));
|
|
164
165
|
const d = deriveRequirementDispositions(rep, l, [profile]);
|
|
165
166
|
const row = d.requirements.find((r) => r.requirementId === requirementIdForScenario(files[0]!))!;
|
|
166
|
-
expect(row.disposition).toBe('blocked');
|
|
167
|
+
expect(row.disposition, req('openwop.it.runner-ledger.a-skipped-file-with-no-ledger-entry-is-blocked-unclassified-and-it-never-reads-a', 'RFC 0148 §A', 'a skipped file with no ledger entry is blocked (unclassified) — and it never reads as skipped/inapplicable')).toBe('blocked');
|
|
167
168
|
expect(row.detail).toMatch(/unclassified/);
|
|
168
169
|
expect(d.verdicts[0]!.unclassified).toContain(requirementIdForScenario(files[0]!));
|
|
169
170
|
expect(d.rejectUnclassified).toBe(true);
|
|
@@ -173,7 +174,7 @@ describe('RFC 0148 §A (S6) — the runner derivation', () => {
|
|
|
173
174
|
it('a recorded inapplicable/skipped is certifiable and NOT unclassified; a recorded executed-fail blocks but is classified', () => {
|
|
174
175
|
const l = ledgerAllPassed().map((e) => (e.requirementId === requirementIdForScenario(files[0]!) ? { requirementId: e.requirementId, disposition: 'inapplicable' as const, detail: 'not advertised' } : e));
|
|
175
176
|
const d = deriveRequirementDispositions(reportAllPassed({ [files[0]!]: 'skipped' }), l, [profile]);
|
|
176
|
-
expect(d.verdicts[0]!.unclassified).toEqual([]);
|
|
177
|
+
expect(d.verdicts[0]!.unclassified, req('openwop.it.runner-ledger.a-recorded-inapplicable-skipped-is-certifiable-and-not-unclassified-a-recorded-e', 'RFC 0148 §A', 'a recorded inapplicable/skipped is certifiable and NOT unclassified; a recorded executed-fail blocks but is classified')).toEqual([]);
|
|
177
178
|
expect(d.verdicts[0]!.certifiable).toBe(true);
|
|
178
179
|
const l2 = ledgerAllPassed().map((e) => (e.requirementId === requirementIdForScenario(files[0]!) ? { requirementId: e.requirementId, disposition: 'executed-fail' as const, detail: 'assertion failed', assertionCount: 2 } : e));
|
|
179
180
|
const d2 = deriveRequirementDispositions(reportAllPassed({ [files[0]!]: 'failed' }), l2, [profile]);
|
|
@@ -185,7 +186,7 @@ describe('RFC 0148 §A (S6) — the runner derivation', () => {
|
|
|
185
186
|
|
|
186
187
|
it('a discoveryOnly profile certifies with an empty floor; an unknown profile does not', () => {
|
|
187
188
|
const d = deriveRequirementDispositions(new Map(), [], ['openwop-discovery-core', 'openwop-nope']);
|
|
188
|
-
expect(d.verdicts.find((v) => v.profile === 'openwop-discovery-core')?.certifiable).toBe(true);
|
|
189
|
+
expect(d.verdicts.find((v) => v.profile === 'openwop-discovery-core')?.certifiable, req('openwop.it.runner-ledger.a-discoveryonly-profile-certifies-with-an-empty-floor-an-unknown-profile-does-no', 'RFC 0148 §A', 'a discoveryOnly profile certifies with an empty floor; an unknown profile does not')).toBe(true);
|
|
189
190
|
expect(d.verdicts.find((v) => v.profile === 'openwop-nope')?.certifiable).toBe(false);
|
|
190
191
|
expect(requirementsFor('openwop-discovery-core')).toEqual([]);
|
|
191
192
|
});
|
|
@@ -193,7 +194,7 @@ describe('RFC 0148 §A (S6) — the runner derivation', () => {
|
|
|
193
194
|
it('a RUNTIME-DERIVED profile (openwop-node-packs) is HELD only when every floor row is a witnessed pass; otherwise it is not held — not rejected, not blocked', () => {
|
|
194
195
|
const np = 'openwop-node-packs';
|
|
195
196
|
const f = PROFILE_FLOOR_SCENARIOS[np]!;
|
|
196
|
-
expect(f.runtimeDerived, 'profiles.md: node-packs is "derivable from which scenarios pass"').toBe(true);
|
|
197
|
+
expect(f.runtimeDerived, req('openwop.it.runner-ledger.a-runtime-derived-profile-openwop-node-packs-is-held-only-when-every-floor-row-i', 'RFC 0148 §A', 'profiles.md: node-packs is "derivable from which scenarios pass"')).toBe(true);
|
|
197
198
|
const ids = f.required.map((x) => requirementIdForScenario(x));
|
|
198
199
|
const rep = new Map<string, 'passed' | 'failed' | 'skipped'>(f.required.map((x) => [x, 'passed' as const]));
|
|
199
200
|
// (a) held: every row a witnessed pass
|
|
@@ -207,7 +208,7 @@ describe('RFC 0148 §A (S6) — the runner derivation', () => {
|
|
|
207
208
|
const vn = notHeld.verdicts.find((v) => v.profile === np)!;
|
|
208
209
|
expect(vn.held).toBe(false);
|
|
209
210
|
expect(vn.certifiable).toBe(false);
|
|
210
|
-
expect(vn.unclassified, 'a profile the host does not hold is not a rejected claim').toEqual([]);
|
|
211
|
+
expect(vn.unclassified, req('openwop.it.runner-ledger.a-runtime-derived-profile-openwop-node-packs-is-held-only-when-every-floor-row-i', 'RFC 0148 §A', 'a profile the host does not hold is not a rejected claim')).toEqual([]);
|
|
211
212
|
expect([...vn.blocking].sort()).toEqual([...ids].sort());
|
|
212
213
|
expect(notHeld.rejectUnclassified).toBe(false);
|
|
213
214
|
// (c) a vacuous pass on a runtime-derived floor is still not a rejection — the emitter drops the claim
|
|
@@ -229,7 +230,7 @@ describe('RFC 0148 §A (S6) — the runner derivation', () => {
|
|
|
229
230
|
const l = ledgerAllPassed().map((e) => (e.requirementId === requirementIdForScenario(files[0]!) ? { requirementId: e.requirementId, disposition: 'blocked' as const, detail: UNCLASSIFIED_RETURN_DETAIL, assertionCount: 0 } : e));
|
|
230
231
|
const d = deriveRequirementDispositions(reportAllPassed(), l, [profile]);
|
|
231
232
|
const v = d.verdicts.find((x) => x.profile === profile)!;
|
|
232
|
-
expect(v.unclassified).toContain(requirementIdForScenario(files[0]!));
|
|
233
|
+
expect(v.unclassified, req('openwop.it.runner-ledger.the-runner-s-a-resolution-of-a-silent-zero-assertion-file-blocked-marker-is-stil', 'RFC 0148 §A', 'the runner\'s §A resolution of a silent zero-assertion file (blocked + marker) is STILL unclassified for a claimed floor')).toContain(requirementIdForScenario(files[0]!));
|
|
233
234
|
expect(d.rejectUnclassified).toBe(true);
|
|
234
235
|
// whereas a blocked row WITH a scenario-authored reason is classified (blocking, not unclassified)
|
|
235
236
|
const l2 = ledgerAllPassed().map((e) => (e.requirementId === requirementIdForScenario(files[0]!) ? { requirementId: e.requirementId, disposition: 'blocked' as const, detail: 'seam /v1/host/sample/x not mounted', assertionCount: 0 } : e));
|
|
@@ -240,7 +241,7 @@ describe('RFC 0148 §A (S6) — the runner derivation', () => {
|
|
|
240
241
|
|
|
241
242
|
it('with no ledger at all, every skipped file is blocked and the runner says so (the pre-S6 honest reading)', () => {
|
|
242
243
|
const d = deriveRequirementDispositions(reportAllPassed({ [files[0]!]: 'skipped' }), [], [profile]);
|
|
243
|
-
expect(d.ledgerPresent).toBe(false);
|
|
244
|
+
expect(d.ledgerPresent, req('openwop.it.runner-ledger.with-no-ledger-at-all-every-skipped-file-is-blocked-and-the-runner-says-so-the-p', 'RFC 0148 §A', 'with no ledger at all, every skipped file is blocked and the runner says so (the pre-S6 honest reading)')).toBe(false);
|
|
244
245
|
const row = d.requirements.find((r) => r.requirementId === requirementIdForScenario(files[0]!))!;
|
|
245
246
|
expect(row.disposition).toBe('blocked');
|
|
246
247
|
expect(row.detail).toMatch(/without a ledger/);
|
|
@@ -250,7 +251,7 @@ describe('RFC 0148 §A (S6) — the runner derivation', () => {
|
|
|
250
251
|
describe('RFC 0148 §A — softSkip notes the reason for an early return, per file', () => {
|
|
251
252
|
it('notes are keyed to the current test file, worst-first when mixed, and read back with joined reasons', () => {
|
|
252
253
|
resetSoftSkips();
|
|
253
|
-
expect(softSkip('inapplicable', 'host does not advertise X')).toBeUndefined();
|
|
254
|
+
expect(softSkip('inapplicable', 'host does not advertise X'), req('openwop.it.runner-ledger.notes-are-keyed-to-the-current-test-file-worst-first-when-mixed-and-read-back-wi', 'RFC 0148 §A', 'notes are keyed to the current test file, worst-first when mixed, and read back with joined reasons')).toBeUndefined();
|
|
254
255
|
softSkip('inapplicable', 'host does not advertise X'); // de-duplicated
|
|
255
256
|
let d = softSkipDisposition('runner-ledger.test.ts');
|
|
256
257
|
expect(d).toEqual({ kind: 'inapplicable', reason: 'host does not advertise X' });
|
|
@@ -268,12 +269,12 @@ describe('RFC 0148 §A — softSkip notes the reason for an early return, per fi
|
|
|
268
269
|
describe('RFC 0148 §A (S6) — the live sink, when this run was given one', () => {
|
|
269
270
|
it('files that finished before this one appear in the ledger file with an assertion count', () => {
|
|
270
271
|
const path = process.env['OPENWOP_LEDGER_PATH'];
|
|
271
|
-
if (!path || !existsSync(path)) return; // not a --certify run; nothing to inspect
|
|
272
|
+
if (!path || !existsSync(path)) return softSkip('blocked', 'precondition not met — `!path || !existsSync(path)` returned early (not a --certify run; nothing to inspect) (seam, prior step, or fixture unavailable)'); // not a --certify run; nothing to inspect
|
|
272
273
|
const entries = readLedgerFile(path);
|
|
273
274
|
// At least the in-memory ledger of THIS worker has file-level entries for
|
|
274
275
|
// earlier files (setup.ts afterAll), and each carries assertionCount.
|
|
275
276
|
const fileEntries = snapshot().filter((e) => e.requirementId.startsWith('openwop.scenario.') || e.requirementId.startsWith('openwop.floor.'));
|
|
276
|
-
for (const e of fileEntries) expect(typeof e.assertionCount).toBe('number');
|
|
277
|
+
for (const e of fileEntries) expect(typeof e.assertionCount, req('openwop.it.runner-ledger.files-that-finished-before-this-one-appear-in-the-ledger-file-with-an-assertion', 'RFC 0148 §A', 'files that finished before this one appear in the ledger file with an assertion count')).toBe('number');
|
|
277
278
|
expect(entries.length).toBeGreaterThanOrEqual(0);
|
|
278
279
|
});
|
|
279
280
|
});
|
|
@@ -13,6 +13,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
13
13
|
import { driver } from '../lib/driver.js';
|
|
14
14
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
15
15
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
16
|
+
import { req } from '../lib/requirement-ids.js';
|
|
16
17
|
|
|
17
18
|
const NOOP_WORKFLOW_ID = 'conformance-noop';
|
|
18
19
|
const SKIP_NO_NOOP = !isFixtureAdvertised(NOOP_WORKFLOW_ID);
|
|
@@ -21,17 +22,17 @@ describe.skipIf(SKIP_NO_NOOP)('run lifecycle: conformance-noop fixture', () => {
|
|
|
21
22
|
it('POST /v1/runs returns 201 with runId per rest-endpoints.md', async () => {
|
|
22
23
|
const res = await driver.post('/v1/runs', { workflowId: NOOP_WORKFLOW_ID });
|
|
23
24
|
|
|
24
|
-
expect(res.status,
|
|
25
|
+
expect(res.status, req('openwop.it.runs-lifecycle.post-v1-runs-returns-201-with-runid-per-rest-endpoints-md',
|
|
25
26
|
'rest-endpoints.md',
|
|
26
27
|
'POST /v1/runs MUST return 201 on accepted run',
|
|
27
28
|
)).toBe(201);
|
|
28
29
|
|
|
29
30
|
const body = res.json as { runId?: unknown; status?: unknown } | undefined;
|
|
30
|
-
expect(typeof body?.runId,
|
|
31
|
+
expect(typeof body?.runId, req('openwop.it.runs-lifecycle.post-v1-runs-returns-201-with-runid-per-rest-endpoints-md',
|
|
31
32
|
'rest-endpoints.md',
|
|
32
33
|
'POST /v1/runs response body MUST include `runId` string',
|
|
33
34
|
)).toBe('string');
|
|
34
|
-
expect(typeof body?.status,
|
|
35
|
+
expect(typeof body?.status, req('openwop.it.runs-lifecycle.post-v1-runs-returns-201-with-runid-per-rest-endpoints-md',
|
|
35
36
|
'rest-endpoints.md',
|
|
36
37
|
'POST /v1/runs response body MUST include `status` string',
|
|
37
38
|
)).toBe('string');
|
|
@@ -44,12 +45,12 @@ describe.skipIf(SKIP_NO_NOOP)('run lifecycle: conformance-noop fixture', () => {
|
|
|
44
45
|
|
|
45
46
|
const terminal = await pollUntilTerminal(runId);
|
|
46
47
|
|
|
47
|
-
expect(terminal.status,
|
|
48
|
+
expect(terminal.status, req('openwop.it.runs-lifecycle.reaches-terminal-completed-within-bounded-time-per-fixtures-md-noop-spec',
|
|
48
49
|
'fixtures.md conformance-noop §Terminal status',
|
|
49
50
|
'fixture MUST reach terminal status `completed`',
|
|
50
51
|
)).toBe('completed');
|
|
51
52
|
|
|
52
|
-
expect(terminal.runId,
|
|
53
|
+
expect(terminal.runId, req('openwop.it.runs-lifecycle.reaches-terminal-completed-within-bounded-time-per-fixtures-md-noop-spec',
|
|
53
54
|
'rest-endpoints.md RunSnapshot',
|
|
54
55
|
'GET /v1/runs/{runId} MUST echo runId',
|
|
55
56
|
)).toBe(runId);
|
|
@@ -59,7 +60,7 @@ describe.skipIf(SKIP_NO_NOOP)('run lifecycle: conformance-noop fixture', () => {
|
|
|
59
60
|
const res = await driver.get('/v1/runs/openwop-conformance-this-run-id-does-not-exist');
|
|
60
61
|
expect(
|
|
61
62
|
[403, 404].includes(res.status),
|
|
62
|
-
|
|
63
|
+
req('openwop.it.runs-lifecycle.get-v1-runs-nonexistentid-returns-404-or-403-per-rest-endpoints-md', 'rest-endpoints.md', 'unknown run MUST return 404 or 403'),
|
|
63
64
|
).toBe(true);
|
|
64
65
|
});
|
|
65
66
|
});
|
|
@@ -29,6 +29,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
29
29
|
import { driver } from '../lib/driver.js';
|
|
30
30
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
31
31
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
32
|
+
import { req } from '../lib/requirement-ids.js';
|
|
33
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
32
34
|
|
|
33
35
|
const CAPABILITY_MISSING_WORKFLOW_ID = 'conformance-capability-missing';
|
|
34
36
|
const SKIP_NO_FIXTURE = !isFixtureAdvertised(CAPABILITY_MISSING_WORKFLOW_ID);
|
|
@@ -37,7 +39,7 @@ describe('runtime-capabilities: /.well-known/openwop forward-compat shape', () =
|
|
|
37
39
|
it('IF runtimeCapabilities is present, it MUST be a string[] of unique non-empty entries', async () => {
|
|
38
40
|
const res = await driver.get('/.well-known/openwop', { authenticated: false });
|
|
39
41
|
|
|
40
|
-
expect(res.status,
|
|
42
|
+
expect(res.status, req('openwop.it.runtime-capabilities.if-runtimecapabilities-is-present-it-must-be-a-string-of-unique-non-empty-entrie',
|
|
41
43
|
'capabilities.md §2',
|
|
42
44
|
'discovery endpoint MUST return 200',
|
|
43
45
|
)).toBe(200);
|
|
@@ -50,28 +52,28 @@ describe('runtime-capabilities: /.well-known/openwop forward-compat shape', () =
|
|
|
50
52
|
// of v1 hosts will omit it. Assertion passes trivially; don't
|
|
51
53
|
// force a value on a host that doesn't advertise any.
|
|
52
54
|
expect(caps).toBeUndefined();
|
|
53
|
-
return;
|
|
55
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `caps === undefined` returned early');
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
expect(Array.isArray(caps),
|
|
58
|
+
expect(Array.isArray(caps), req('openwop.it.runtime-capabilities.if-runtimecapabilities-is-present-it-must-be-a-string-of-unique-non-empty-entrie',
|
|
57
59
|
'capabilities.md §"Runtime capabilities"',
|
|
58
60
|
'runtimeCapabilities MUST be an array when present',
|
|
59
61
|
)).toBe(true);
|
|
60
62
|
|
|
61
63
|
const arr = caps as unknown[];
|
|
62
64
|
for (const entry of arr) {
|
|
63
|
-
expect(typeof entry,
|
|
65
|
+
expect(typeof entry, req('openwop.it.runtime-capabilities.if-runtimecapabilities-is-present-it-must-be-a-string-of-unique-non-empty-entrie',
|
|
64
66
|
'capabilities.md §"Runtime capabilities"',
|
|
65
67
|
'every runtimeCapabilities entry MUST be a string',
|
|
66
68
|
)).toBe('string');
|
|
67
|
-
expect((entry as string).length,
|
|
69
|
+
expect((entry as string).length, req('openwop.it.runtime-capabilities.if-runtimecapabilities-is-present-it-must-be-a-string-of-unique-non-empty-entrie',
|
|
68
70
|
'capabilities.md §"Runtime capabilities"',
|
|
69
71
|
'every runtimeCapabilities entry MUST be non-empty',
|
|
70
72
|
)).toBeGreaterThan(0);
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
const unique = new Set(arr as string[]);
|
|
74
|
-
expect(unique.size,
|
|
76
|
+
expect(unique.size, req('openwop.it.runtime-capabilities.if-runtimecapabilities-is-present-it-must-be-a-string-of-unique-non-empty-entrie',
|
|
75
77
|
'capabilities.md §"Runtime capabilities"',
|
|
76
78
|
'runtimeCapabilities entries MUST be unique',
|
|
77
79
|
)).toBe(arr.length);
|
|
@@ -91,7 +93,7 @@ describe.skipIf(SKIP_NO_FIXTURE)('runtime-capabilities: dispatch refusal on unsa
|
|
|
91
93
|
const create = await driver.post('/v1/runs', {
|
|
92
94
|
workflowId: 'conformance-capability-missing',
|
|
93
95
|
});
|
|
94
|
-
expect(create.status,
|
|
96
|
+
expect(create.status, req('openwop.it.runtime-capabilities.terminates-the-run-with-error-code-capability-not-provided',
|
|
95
97
|
'capabilities.md §"Runtime capabilities"',
|
|
96
98
|
'POST /v1/runs MUST accept the run; refusal is engine-side at dispatch time, not request-validation',
|
|
97
99
|
)).toBe(201);
|
|
@@ -99,18 +101,18 @@ describe.skipIf(SKIP_NO_FIXTURE)('runtime-capabilities: dispatch refusal on unsa
|
|
|
99
101
|
|
|
100
102
|
const terminal = await pollUntilTerminal(runId);
|
|
101
103
|
|
|
102
|
-
expect(terminal.status,
|
|
104
|
+
expect(terminal.status, req('openwop.it.runtime-capabilities.terminates-the-run-with-error-code-capability-not-provided',
|
|
103
105
|
'capabilities.md §"Runtime capabilities"',
|
|
104
106
|
'a node with unsatisfied requires MUST cause the run to terminate as failed',
|
|
105
107
|
)).toBe('failed');
|
|
106
108
|
|
|
107
109
|
const error = (terminal as { error?: { code?: string; message?: string } }).error;
|
|
108
|
-
expect(error?.code,
|
|
110
|
+
expect(error?.code, req('openwop.it.runtime-capabilities.terminates-the-run-with-error-code-capability-not-provided',
|
|
109
111
|
'rest-endpoints.md §"Common error codes"',
|
|
110
112
|
'terminal RunSnapshot.error.code MUST be "capability_not_provided"',
|
|
111
113
|
)).toBe('capability_not_provided');
|
|
112
114
|
|
|
113
|
-
expect(error?.message,
|
|
115
|
+
expect(error?.message, req('openwop.it.runtime-capabilities.terminates-the-run-with-error-code-capability-not-provided',
|
|
114
116
|
'capabilities.md §"Runtime capabilities"',
|
|
115
117
|
'error.message MUST name the missing capability id verbatim so operators can act without grepping logs',
|
|
116
118
|
)).toContain('conformance.never-provided');
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
|
|
30
30
|
import { describe, it, expect } from 'vitest';
|
|
31
31
|
import { softSkip } from '../lib/soft-skip.js';
|
|
32
|
-
import { driver } from '../lib/driver.js';
|
|
33
32
|
import { installGate } from '../lib/runtimeRequires.js';
|
|
33
|
+
import { req } from '../lib/requirement-ids.js';
|
|
34
34
|
|
|
35
35
|
function manifest(requires: string[]) {
|
|
36
36
|
return {
|
|
@@ -48,11 +48,11 @@ describe('runtime-requires install gate (RFC 0076 §A)', () => {
|
|
|
48
48
|
if (res === null) return softSkip('blocked', 'seam absent — soft-skip');
|
|
49
49
|
expect(
|
|
50
50
|
res.status,
|
|
51
|
-
|
|
51
|
+
req('openwop.it.runtime-requires-install-gate.install-grant-requires-grant-set-install-succeeds', 'registry-operations.md §"Runtime-requirement install gate"', 'a pack whose runtime.requires are all grantable MUST install (no refusal)'),
|
|
52
52
|
).toBe(200);
|
|
53
53
|
expect(
|
|
54
54
|
res.body.outcome,
|
|
55
|
-
|
|
55
|
+
req('openwop.it.runtime-requires-install-gate.install-grant-requires-grant-set-install-succeeds', 'registry-operations.md §"Runtime-requirement install gate"', 'a granted install reports outcome:"installed"'),
|
|
56
56
|
).toBe('installed');
|
|
57
57
|
});
|
|
58
58
|
|
|
@@ -61,19 +61,19 @@ describe('runtime-requires install gate (RFC 0076 §A)', () => {
|
|
|
61
61
|
if (res === null) return softSkip('blocked', 'seam absent — soft-skip');
|
|
62
62
|
expect(
|
|
63
63
|
res.status,
|
|
64
|
-
|
|
64
|
+
req('openwop.it.runtime-requires-install-gate.install-refuse-an-ungrantable-primitive-pack-runtime-requirement-unmet', 'registry-operations.md §"Runtime-requirement install gate"', 'a pack requiring an ungranted primitive MUST be refused at install (not at first invocation)'),
|
|
65
65
|
).toBe(400);
|
|
66
66
|
expect(
|
|
67
67
|
res.body.error,
|
|
68
|
-
|
|
68
|
+
req('openwop.it.runtime-requires-install-gate.install-refuse-an-ungrantable-primitive-pack-runtime-requirement-unmet', 'registry-operations.md §"Runtime-requirement install gate"', 'the refusal MUST carry error code pack_runtime_requirement_unmet'),
|
|
69
69
|
).toBe('pack_runtime_requirement_unmet');
|
|
70
70
|
expect(
|
|
71
71
|
Array.isArray(res.body.unmet) && (res.body.unmet as unknown[]).includes('net.dns'),
|
|
72
|
-
|
|
72
|
+
req('openwop.it.runtime-requires-install-gate.install-refuse-an-ungrantable-primitive-pack-runtime-requirement-unmet', 'registry-operations.md §"Runtime-requirement install gate"', 'unmet[] MUST list the ungranted primitive(s) (capability_not_provided envelope)'),
|
|
73
73
|
).toBe(true);
|
|
74
74
|
expect(
|
|
75
75
|
typeof res.body.manifest === 'string' && (res.body.manifest as string).includes('vendor.example.http'),
|
|
76
|
-
|
|
76
|
+
req('openwop.it.runtime-requires-install-gate.install-refuse-an-ungrantable-primitive-pack-runtime-requirement-unmet', 'registry-operations.md §"Runtime-requirement install gate"', 'the refusal MUST name the offending manifest (name@version)'),
|
|
77
77
|
).toBe(true);
|
|
78
78
|
});
|
|
79
79
|
|
|
@@ -87,7 +87,7 @@ describe('runtime-requires install gate (RFC 0076 §A)', () => {
|
|
|
87
87
|
const projected = res.body.requiresProjected as unknown;
|
|
88
88
|
expect(
|
|
89
89
|
Array.isArray(projected) && ['net.dns', 'net.outbound'].every((t) => (projected as unknown[]).includes(t)),
|
|
90
|
-
|
|
90
|
+
req('openwop.it.runtime-requires-install-gate.non-sandbox-projection-a-non-gating-host-installs-and-projects-requires-a-should', 'node-packs.md §"Runtime platform requirements"', 'a non-gating host that projects SHOULD surface the declared runtime.requires[] on the inventory entry verbatim'),
|
|
91
91
|
).toBe(true);
|
|
92
92
|
});
|
|
93
93
|
});
|
|
@@ -37,6 +37,7 @@ import Ajv2020 from 'ajv/dist/2020.js';
|
|
|
37
37
|
import addFormats from 'ajv-formats';
|
|
38
38
|
import type { ErrorObject, ValidateFunction } from 'ajv';
|
|
39
39
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
40
|
+
import { req } from '../lib/requirement-ids.js';
|
|
40
41
|
|
|
41
42
|
const SCHEMA_PATH = join(SCHEMAS_DIR, 'node-pack-manifest.schema.json');
|
|
42
43
|
|
|
@@ -89,21 +90,21 @@ describe('category: runtime.requires vocabulary + shape (RFC 0076 §A)', () => {
|
|
|
89
90
|
const ok = validate(manifest(['net.dns', 'net.outbound']));
|
|
90
91
|
expect(
|
|
91
92
|
ok,
|
|
92
|
-
`node-packs.md §"Runtime platform requirements": a well-formed runtime.requires MUST validate. Errors: ${JSON.stringify(validate.errors)}
|
|
93
|
+
req('openwop.it.runtime-requires-shape.positive-a-manifest-declaring-valid-primitives-validates-cleanly', 'node-packs.md', `node-packs.md §"Runtime platform requirements": a well-formed runtime.requires MUST validate. Errors: ${JSON.stringify(validate.errors)}`),
|
|
93
94
|
).toBe(true);
|
|
94
95
|
});
|
|
95
96
|
|
|
96
97
|
it('positive: runtime.requires is OPTIONAL — a manifest omitting it validates (additive)', () => {
|
|
97
98
|
expect(
|
|
98
99
|
validate(manifest(undefined)),
|
|
99
|
-
'node-pack-manifest.schema.json: runtime.requires is additive/OPTIONAL — packs predating RFC 0076 validate unchanged',
|
|
100
|
+
req('openwop.it.runtime-requires-shape.positive-runtime-requires-is-optional-a-manifest-omitting-it-validates-additive', 'node-packs.md', 'node-pack-manifest.schema.json: runtime.requires is additive/OPTIONAL — packs predating RFC 0076 validate unchanged'),
|
|
100
101
|
).toBe(true);
|
|
101
102
|
});
|
|
102
103
|
|
|
103
104
|
it('positive: an empty requires[] validates (equivalent to omission per §A)', () => {
|
|
104
105
|
expect(
|
|
105
106
|
validate(manifest([])),
|
|
106
|
-
'node-packs.md §"Runtime platform requirements": runtime.requires:[] is valid and equivalent to omission',
|
|
107
|
+
req('openwop.it.runtime-requires-shape.positive-an-empty-requires-validates-equivalent-to-omission-per-a', 'node-packs.md', 'node-packs.md §"Runtime platform requirements": runtime.requires:[] is valid and equivalent to omission'),
|
|
107
108
|
).toBe(true);
|
|
108
109
|
});
|
|
109
110
|
|
|
@@ -111,7 +112,7 @@ describe('category: runtime.requires vocabulary + shape (RFC 0076 §A)', () => {
|
|
|
111
112
|
for (const token of VOCABULARY) {
|
|
112
113
|
expect(
|
|
113
114
|
validate(manifest([token])),
|
|
114
|
-
`node-pack-manifest.schema.json: "${token}" is in the RFC 0076 §A vocabulary. Errors: ${JSON.stringify(validate.errors)}
|
|
115
|
+
req('openwop.it.runtime-requires-shape.positive-every-vocabulary-token-individually-validates', 'node-packs.md', `node-pack-manifest.schema.json: "${token}" is in the RFC 0076 §A vocabulary. Errors: ${JSON.stringify(validate.errors)}`),
|
|
115
116
|
).toBe(true);
|
|
116
117
|
}
|
|
117
118
|
});
|
|
@@ -120,7 +121,7 @@ describe('category: runtime.requires vocabulary + shape (RFC 0076 §A)', () => {
|
|
|
120
121
|
const errs = errorsOn(manifest(['node:dns/promises']));
|
|
121
122
|
expect(
|
|
122
123
|
errs.some((e) => e.instancePath.includes('/runtime/requires')),
|
|
123
|
-
'node-packs.md §"Runtime platform requirements": raw language builtin names are NOT in the closed vocabulary — the abstract net.dns is the portable equivalent; the registry/host surfaces this as invalid_manifest',
|
|
124
|
+
req('openwop.it.runtime-requires-shape.negative-a-raw-builtin-name-node-dns-promises-is-rejected-invalid-manifest', 'node-packs.md', 'node-packs.md §"Runtime platform requirements": raw language builtin names are NOT in the closed vocabulary — the abstract net.dns is the portable equivalent; the registry/host surfaces this as invalid_manifest'),
|
|
124
125
|
).toBe(true);
|
|
125
126
|
});
|
|
126
127
|
|
|
@@ -128,7 +129,7 @@ describe('category: runtime.requires vocabulary + shape (RFC 0076 §A)', () => {
|
|
|
128
129
|
const errs = errorsOn(manifest(['net.dns', 'net.dns']));
|
|
129
130
|
expect(
|
|
130
131
|
errs.some((e) => e.keyword === 'uniqueItems'),
|
|
131
|
-
'node-pack-manifest.schema.json: runtime.requires has uniqueItems:true',
|
|
132
|
+
req('openwop.it.runtime-requires-shape.negative-a-duplicate-token-is-rejected-uniqueitems', 'node-packs.md', 'node-pack-manifest.schema.json: runtime.requires has uniqueItems:true'),
|
|
132
133
|
).toBe(true);
|
|
133
134
|
});
|
|
134
135
|
});
|
|
@@ -29,71 +29,72 @@
|
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
import { describe, it, expect } from 'vitest';
|
|
32
|
-
import { driver } from '../lib/driver.js';
|
|
33
32
|
import { isSafeFetchSupported, isToolHookAuditOn, safeFetch } from '../lib/safeFetch.js';
|
|
33
|
+
import { req } from '../lib/requirement-ids.js';
|
|
34
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
34
35
|
|
|
35
36
|
describe('safefetch-behavior (RFC 0076 §B / §host.http)', () => {
|
|
36
37
|
it('blocks a metadata-endpoint target (SSRF guard)', async () => {
|
|
37
|
-
if (!(await isSafeFetchSupported())) return; // capability absent — soft-skip
|
|
38
|
+
if (!(await isSafeFetchSupported())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSafeFetchSupported())` returned early (capability absent — soft-skip)'); // capability absent — soft-skip
|
|
38
39
|
const res = await safeFetch({ url: 'http://169.254.169.254/latest/meta-data/' });
|
|
39
|
-
if (res === null) return; // seam absent — soft-skip
|
|
40
|
+
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
|
|
40
41
|
expect(
|
|
41
42
|
res.outcome,
|
|
42
|
-
|
|
43
|
+
req('openwop.it.safefetch-behavior.blocks-a-metadata-endpoint-target-ssrf-guard', 'host-capabilities.md §host.http', 'safeFetch MUST NOT connect to a cloud-metadata address'),
|
|
43
44
|
).toBe('blocked');
|
|
44
45
|
expect(
|
|
45
46
|
res.blocked,
|
|
46
|
-
|
|
47
|
+
req('openwop.it.safefetch-behavior.blocks-a-metadata-endpoint-target-ssrf-guard', 'host-capabilities.md §host.http', 'a blocked SSRF target reports blocked:"ssrf" (http-client-ssrf-guard invariant)'),
|
|
47
48
|
).toBe('ssrf');
|
|
48
49
|
});
|
|
49
50
|
|
|
50
51
|
it('blocks a loopback target (SSRF guard)', async () => {
|
|
51
|
-
if (!(await isSafeFetchSupported())) return;
|
|
52
|
+
if (!(await isSafeFetchSupported())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSafeFetchSupported())` returned early');
|
|
52
53
|
const res = await safeFetch({ url: 'http://127.0.0.1:6379/' });
|
|
53
|
-
if (res === null) return;
|
|
54
|
+
if (res === null) return softSkip('blocked', 'precondition not met — `res === null` returned early (seam, prior step, or fixture unavailable)');
|
|
54
55
|
expect(
|
|
55
56
|
res.outcome,
|
|
56
|
-
|
|
57
|
+
req('openwop.it.safefetch-behavior.blocks-a-loopback-target-ssrf-guard', 'host-capabilities.md §host.http', 'safeFetch MUST NOT connect to loopback'),
|
|
57
58
|
).toBe('blocked');
|
|
58
59
|
});
|
|
59
60
|
|
|
60
61
|
it('blocks DNS-rebinding (resolved IP is pinned for the connection)', async () => {
|
|
61
|
-
if (!(await isSafeFetchSupported())) return;
|
|
62
|
+
if (!(await isSafeFetchSupported())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSafeFetchSupported())` returned early');
|
|
62
63
|
const res = await safeFetch({ url: 'http://example.com/', simulateRebindTo: '169.254.169.254' });
|
|
63
|
-
if (res === null) return;
|
|
64
|
+
if (res === null) return softSkip('blocked', 'precondition not met — `res === null` returned early (seam, prior step, or fixture unavailable)');
|
|
64
65
|
expect(
|
|
65
66
|
res.outcome,
|
|
66
|
-
|
|
67
|
+
req('openwop.it.safefetch-behavior.blocks-dns-rebinding-resolved-ip-is-pinned-for-the-connection', 'host-capabilities.md §host.http', 'a public name that re-resolves to a blocked address MUST be blocked (rebinding defeat)'),
|
|
67
68
|
).toBe('blocked');
|
|
68
69
|
});
|
|
69
70
|
|
|
70
71
|
it('refuses a Connection: upgrade request (no 101 socket-hijack escape)', async () => {
|
|
71
|
-
if (!(await isSafeFetchSupported())) return;
|
|
72
|
+
if (!(await isSafeFetchSupported())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSafeFetchSupported())` returned early');
|
|
72
73
|
const res = await safeFetch({ url: 'https://example.com/', init: { headers: { Connection: 'upgrade' } } });
|
|
73
|
-
if (res === null) return;
|
|
74
|
+
if (res === null) return softSkip('blocked', 'precondition not met — `res === null` returned early (seam, prior step, or fixture unavailable)');
|
|
74
75
|
expect(
|
|
75
76
|
res.outcome,
|
|
76
|
-
|
|
77
|
+
req('openwop.it.safefetch-behavior.refuses-a-connection-upgrade-request-no-101-socket-hijack-escape', 'host-capabilities.md §host.http', 'safeFetch MUST refuse a connection-upgrade attempt'),
|
|
77
78
|
).toBe('blocked');
|
|
78
79
|
expect(
|
|
79
80
|
res.blocked,
|
|
80
|
-
|
|
81
|
+
req('openwop.it.safefetch-behavior.refuses-a-connection-upgrade-request-no-101-socket-hijack-escape', 'host-capabilities.md §host.http', 'a refused upgrade reports blocked:"upgrade"'),
|
|
81
82
|
).toBe('upgrade');
|
|
82
83
|
});
|
|
83
84
|
|
|
84
85
|
it('emits the tool-hooks audit pair when prePostEvents is also advertised', async () => {
|
|
85
|
-
if (!(await isSafeFetchSupported())) return;
|
|
86
|
-
if (!(await isToolHookAuditOn())) return; // audit MUST applies only when both advertised
|
|
86
|
+
if (!(await isSafeFetchSupported())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSafeFetchSupported())` returned early');
|
|
87
|
+
if (!(await isToolHookAuditOn())) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isToolHookAuditOn())` returned early (audit MUST applies only when both advertised)'); // audit MUST applies only when both advertised
|
|
87
88
|
const res = await safeFetch({ url: 'https://example.com/' });
|
|
88
|
-
if (res === null) return;
|
|
89
|
-
if (res.outcome !== 'fetched') return; // only a completed call carries the pair
|
|
89
|
+
if (res === null) return softSkip('blocked', 'precondition not met — `res === null` returned early (seam, prior step, or fixture unavailable)');
|
|
90
|
+
if (res.outcome !== 'fetched') return softSkip('blocked', 'precondition not met — `res.outcome !== \'fetched\'` returned early (only a completed call carries the pair) (seam, prior step, or fixture unavailable)'); // only a completed call carries the pair
|
|
90
91
|
expect(
|
|
91
92
|
res.toolCalled !== undefined && res.toolReturned !== undefined,
|
|
92
|
-
|
|
93
|
+
req('openwop.it.safefetch-behavior.emits-the-tool-hooks-audit-pair-when-prepostevents-is-also-advertised', 'host-capabilities.md §host.http', 'when toolHooks.prePostEvents + safeFetch are both advertised, a safeFetch call MUST emit the agent.toolCalled/agent.toolReturned pair'),
|
|
93
94
|
).toBe(true);
|
|
94
95
|
expect(
|
|
95
96
|
(res.toolCalled as { transport?: string } | undefined)?.transport,
|
|
96
|
-
|
|
97
|
+
req('openwop.it.safefetch-behavior.emits-the-tool-hooks-audit-pair-when-prepostevents-is-also-advertised', 'host-capabilities.md §host.http', 'the audit pair carries transport:"http"'),
|
|
97
98
|
).toBe('http');
|
|
98
99
|
});
|
|
99
100
|
});
|
|
@@ -53,10 +53,11 @@
|
|
|
53
53
|
*/
|
|
54
54
|
|
|
55
55
|
import { describe, it, expect } from 'vitest';
|
|
56
|
-
import { driver } from '../lib/driver.js';
|
|
57
56
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
58
57
|
import { isSafeFetchLiveAuditAdvertised, safeFetchViaRun } from '../lib/safeFetch.js';
|
|
59
58
|
import { queryTestEvents } from '../lib/event-log-query.js';
|
|
59
|
+
import { req } from '../lib/requirement-ids.js';
|
|
60
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
60
61
|
|
|
61
62
|
const PROFILE = 'openwop-safefetch-live-audit';
|
|
62
63
|
const CITE = 'host-capabilities.md §host.http';
|
|
@@ -78,7 +79,7 @@ const FETCH_URL = 'https://example.com/';
|
|
|
78
79
|
* `false` (caller treats as host-pending soft-skip) only when the event-log
|
|
79
80
|
* query seam is unavailable; otherwise asserts and returns `true`.
|
|
80
81
|
*/
|
|
81
|
-
async function assertDurableHttpPair(runId: string, label: string): Promise<boolean> {
|
|
82
|
+
async function assertDurableHttpPair(requirementId: string, runId: string, label: string): Promise<boolean> {
|
|
82
83
|
const calledQ = await queryTestEvents(runId, { type: 'agent.toolCalled' });
|
|
83
84
|
const returnedQ = await queryTestEvents(runId, { type: 'agent.toolReturned' });
|
|
84
85
|
if (!calledQ.ok || !returnedQ.ok) {
|
|
@@ -91,7 +92,7 @@ async function assertDurableHttpPair(runId: string, label: string): Promise<bool
|
|
|
91
92
|
const httpCall = calledQ.events.find((e) => (e.payload as { transport?: string }).transport === 'http');
|
|
92
93
|
expect(
|
|
93
94
|
httpCall !== undefined,
|
|
94
|
-
|
|
95
|
+
req(requirementId,
|
|
95
96
|
CITE,
|
|
96
97
|
`(${label}) when toolHooks.prePostEvents + safeFetch are both advertised, a production ctx.http.safeFetch call MUST persist an agent.toolCalled with transport:"http" to the durable run event log (not just the seam echo), for EVERY invocation incl. blocked ones`,
|
|
97
98
|
),
|
|
@@ -101,14 +102,14 @@ async function assertDurableHttpPair(runId: string, label: string): Promise<bool
|
|
|
101
102
|
const callId = (httpCall.payload as { callId?: string }).callId;
|
|
102
103
|
expect(
|
|
103
104
|
typeof callId === 'string' && callId.length > 0,
|
|
104
|
-
|
|
105
|
+
req(requirementId, CITE, `(${label}) the persisted agent.toolCalled MUST carry the required callId (run-event-payloads.schema.json §agentToolCalled)`),
|
|
105
106
|
).toBe(true);
|
|
106
107
|
|
|
107
108
|
// The paired agent.toolReturned — matched by the required callId (RFC 0002 §B pairing).
|
|
108
109
|
const paired = returnedQ.events.find((e) => (e.payload as { callId?: string }).callId === callId);
|
|
109
110
|
expect(
|
|
110
111
|
paired !== undefined,
|
|
111
|
-
|
|
112
|
+
req(requirementId, CITE, `(${label}) the agent.toolCalled MUST be followed by a callId-paired agent.toolReturned in the durable log (no quiet bypass)`),
|
|
112
113
|
).toBe(true);
|
|
113
114
|
|
|
114
115
|
// Stricter, when the host surfaces causation: RFC 0002 §B says
|
|
@@ -117,7 +118,7 @@ async function assertDurableHttpPair(runId: string, label: string): Promise<bool
|
|
|
117
118
|
if (paired && typeof paired.causationId === 'string') {
|
|
118
119
|
expect(
|
|
119
120
|
paired.causationId,
|
|
120
|
-
|
|
121
|
+
req(requirementId, 'RFC 0002 §B', 'agent.toolReturned.causationId MUST equal the paired agent.toolCalled.eventId when surfaced'),
|
|
121
122
|
).toBe(httpCall.eventId);
|
|
122
123
|
}
|
|
123
124
|
return true;
|
|
@@ -135,7 +136,7 @@ describe('safefetch-live-audit (RFC 0076 §B / RFC 0064 §B — production path,
|
|
|
135
136
|
if (run === null) {
|
|
136
137
|
// eslint-disable-next-line no-console
|
|
137
138
|
console.warn(`[${PROFILE}] safe-fetch-run seam unwired (404); host-pending — skipping`);
|
|
138
|
-
return;
|
|
139
|
+
return softSkip('blocked', 'precondition not met — `run === null` returned early ([…] safe-fetch-run seam unwired (404); host-pending — skipping) (seam, prior step, or fixture unavailable)');
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
// The metadata IP MUST be refused by a conformant SSRF guard
|
|
@@ -144,9 +145,9 @@ describe('safefetch-live-audit (RFC 0076 §B / RFC 0064 §B — production path,
|
|
|
144
145
|
// exist — this is the egress-independent floor that makes the bar non-vacuous.
|
|
145
146
|
expect(
|
|
146
147
|
typeof run.runId === 'string' && (run.runId as string).length > 0,
|
|
147
|
-
|
|
148
|
+
req('openwop.it.safefetch-live-audit.a-blocked-real-run-safefetch-emits-the-durable-agent-toolcalled-agent-toolreturn', CITE, 'the safe-fetch-run seam MUST return the runId of the real run it executed the safeFetch in'),
|
|
148
149
|
).toBe(true);
|
|
149
|
-
await assertDurableHttpPair(run.runId as string, 'blocked');
|
|
150
|
+
await assertDurableHttpPair('openwop.it.safefetch-live-audit.a-blocked-real-run-safefetch-emits-the-durable-agent-toolcalled-agent-toolreturn', run.runId as string, 'blocked');
|
|
150
151
|
});
|
|
151
152
|
|
|
152
153
|
it('a FETCHED real-run safeFetch also emits the durable pair (success-path coverage — skipped without public egress)', async () => {
|
|
@@ -154,7 +155,7 @@ describe('safefetch-live-audit (RFC 0076 §B / RFC 0064 §B — production path,
|
|
|
154
155
|
if (!behaviorGate(PROFILE, advertised)) return;
|
|
155
156
|
|
|
156
157
|
const run = await safeFetchViaRun({ url: FETCH_URL });
|
|
157
|
-
if (run === null) return; // seam unwired — already warned by the floor test
|
|
158
|
+
if (run === null) return softSkip('blocked', 'precondition not met — `run === null` returned early (seam unwired — already warned by the floor test) (seam, prior step, or fixture unavailable)'); // seam unwired — already warned by the floor test
|
|
158
159
|
|
|
159
160
|
if (run.outcome !== 'fetched') {
|
|
160
161
|
// No public egress in this environment — the blocked-path floor already
|
|
@@ -164,12 +165,12 @@ describe('safefetch-live-audit (RFC 0076 §B / RFC 0064 §B — production path,
|
|
|
164
165
|
console.warn(
|
|
165
166
|
`[${PROFILE}] ${FETCH_URL} did not fetch (outcome=${run.outcome ?? 'n/a'}); no public egress — success-path coverage skipped (the blocked floor covers emission)`,
|
|
166
167
|
);
|
|
167
|
-
return;
|
|
168
|
+
return softSkip('blocked', 'precondition not met — `run.outcome !== \'fetched\'` returned early ([…] … did not fetch (outcome=…); no public egress — success-path coverage skipped (the blocked floor covers emission)) (seam, prior step, or fixture u…');
|
|
168
169
|
}
|
|
169
170
|
expect(
|
|
170
171
|
typeof run.runId === 'string' && (run.runId as string).length > 0,
|
|
171
|
-
|
|
172
|
+
req('openwop.it.safefetch-live-audit.a-fetched-real-run-safefetch-also-emits-the-durable-pair-success-path-coverage-s', CITE, 'the safe-fetch-run seam MUST return the runId of the real run it executed the fetch in'),
|
|
172
173
|
).toBe(true);
|
|
173
|
-
await assertDurableHttpPair(run.runId as string, 'fetched');
|
|
174
|
+
await assertDurableHttpPair('openwop.it.safefetch-live-audit.a-fetched-real-run-safefetch-also-emits-the-durable-pair-success-path-coverage-s', run.runId as string, 'fetched');
|
|
174
175
|
});
|
|
175
176
|
});
|