@openwop/openwop-conformance 1.163.0 → 2.0.0-rc.1
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 +262 -14
- package/dist/lib/certification-bundle-v3.js +142 -0
- package/dist/lib/paths.js +25 -3
- package/dist/lib/requirement-ids.js +57 -4
- package/dist/lib/requirement-ledger.js +1 -0
- package/dist/lib/scenario-disposition.js +6 -1
- package/dist/lib/spec-coherence.js +13 -32
- package/dist/spec-artifacts.lock.json +5 -0
- package/package.json +11 -33
- package/requirements.json +22892 -11701
- package/scenario-majors.json +1493 -0
- package/schemas/CORPUS-STAMP.json +137 -5
- package/src/cli.ts +236 -19
- package/src/global-setup.ts +3 -2
- package/src/lib/certification-bundle-v3.ts +175 -0
- package/src/lib/corpus-stamp.ts +29 -1
- package/src/lib/driver.ts +9 -1
- package/src/lib/era2-seed.ts +197 -0
- package/src/lib/paths.ts +25 -3
- package/src/lib/requirement-ids.ts +54 -4
- package/src/lib/requirement-ledger.ts +7 -1
- package/src/lib/scenario-disposition.ts +6 -1
- package/src/lib/seams.ts +45 -0
- package/src/lib/spec-coherence.ts +15 -32
- package/src/lib/v2.ts +62 -0
- 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/v2-approver-enforced.test.ts +97 -0
- package/src/scenarios/v2-assurance-downgrade-audited.test.ts +75 -0
- package/src/scenarios/v2-bundle-v3-signed.test.ts +96 -0
- package/src/scenarios/v2-capabilities-root-closed.test.ts +63 -0
- package/src/scenarios/v2-capability-record-shape.test.ts +104 -0
- package/src/scenarios/v2-chain-pin-exact.test.ts +118 -0
- package/src/scenarios/v2-coherence-not-in-bundle.test.ts +98 -0
- package/src/scenarios/v2-compensation-read-projection.test.ts +85 -0
- package/src/scenarios/v2-configurable-closed.test.ts +67 -0
- package/src/scenarios/v2-dual-stack-negotiation.test.ts +118 -0
- package/src/scenarios/v2-effect-identity-business-key.test.ts +132 -0
- package/src/scenarios/v2-effect-seam-manifest.test.ts +163 -0
- package/src/scenarios/v2-enum-growth-rule.test.ts +69 -0
- package/src/scenarios/v2-era-key.test.ts +86 -0
- package/src/scenarios/v2-error-registry.test.ts +89 -0
- package/src/scenarios/v2-event-type-closed.test.ts +97 -0
- package/src/scenarios/v2-fork-a-v1-run.test.ts +109 -0
- package/src/scenarios/v2-form-when-reuses-edge-conditions.test.ts +70 -0
- package/src/scenarios/v2-header-scheme.test.ts +67 -0
- package/src/scenarios/v2-id-grammar.test.ts +77 -0
- package/src/scenarios/v2-idempotency-key-grammar.test.ts +65 -0
- package/src/scenarios/v2-interrupt-token-scheme.test.ts +55 -0
- package/src/scenarios/v2-lane-issuer-advertised.test.ts +62 -0
- package/src/scenarios/v2-legacy-profiles-absent.test.ts +78 -0
- package/src/scenarios/v2-manifest-ceiling-refused.test.ts +130 -0
- package/src/scenarios/v2-manifest-hatch-carried.test.ts +120 -0
- package/src/scenarios/v2-min-client-version.test.ts +48 -0
- package/src/scenarios/v2-minimum-version-refused.test.ts +141 -0
- package/src/scenarios/v2-mrtr-rounds-ceiling.test.ts +95 -0
- package/src/scenarios/v2-negotiation-authenticated.test.ts +125 -0
- package/src/scenarios/v2-negotiation-decided-emitted.test.ts +149 -0
- package/src/scenarios/v2-no-transport-advertisement.test.ts +57 -0
- package/src/scenarios/v2-owner-subject-required.test.ts +72 -0
- package/src/scenarios/v2-pack-isolation.test.ts +147 -0
- package/src/scenarios/v2-payload-registry-closed.test.ts +110 -0
- package/src/scenarios/v2-peer-dependency-declared.test.ts +132 -0
- package/src/scenarios/v2-pinned-run-disposition.test.ts +124 -0
- package/src/scenarios/v2-poll-cursor-v2.test.ts +114 -0
- package/src/scenarios/v2-preferred-version-default.test.ts +83 -0
- package/src/scenarios/v2-profiles-derived-only.test.ts +70 -0
- package/src/scenarios/v2-provider-conflict.test.ts +115 -0
- package/src/scenarios/v2-refresh-sla.test.ts +78 -0
- package/src/scenarios/v2-relaxation-recorded.test.ts +134 -0
- package/src/scenarios/v2-revocation-honored.test.ts +66 -0
- package/src/scenarios/v2-subject-link-record.test.ts +102 -0
- package/src/scenarios/v2-unmapped-type-refused.test.ts +95 -0
- package/src/scenarios/v2-v1-events-translated.test.ts +105 -0
- package/src/scenarios/v2-v1-signed-webhook-accepted.test.ts +109 -0
- package/src/scenarios/v2-webhook-durable-delivery.test.ts +238 -0
- package/src/scenarios/v2-well-known-one-resource.test.ts +129 -0
- 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 +44 -6
- 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 -4160
- 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
|
@@ -24,13 +24,14 @@
|
|
|
24
24
|
|
|
25
25
|
import { describe, it, expect } from 'vitest';
|
|
26
26
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
27
|
-
import { driver } from '../lib/driver.js';
|
|
28
27
|
import {
|
|
29
28
|
isAnonymousActorAdvertised,
|
|
30
29
|
anonDispatch,
|
|
31
30
|
anonToolCatalog,
|
|
32
31
|
readAnonymousActorCap,
|
|
33
32
|
} from '../lib/anonymousActor.js';
|
|
33
|
+
import { req } from '../lib/requirement-ids.js';
|
|
34
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
34
35
|
|
|
35
36
|
const PROFILE = 'openwop-anonymous-actor';
|
|
36
37
|
|
|
@@ -41,36 +42,36 @@ describe('anonymous-actor-default-deny (RFC 0132 §C.1)', () => {
|
|
|
41
42
|
it('the anon tool catalog returns only the explicit surface grant — never a default baseline', async () => {
|
|
42
43
|
if (!behaviorGate(PROFILE, await isAnonymousActorAdvertised())) return;
|
|
43
44
|
const cat = await anonToolCatalog();
|
|
44
|
-
if (cat.status === 404 || cat.status === 405) return; // seam unwired — soft-skip
|
|
45
|
+
if (cat.status === 404 || cat.status === 405) return softSkip('blocked', 'precondition not met — `cat.status === 404 || cat.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
45
46
|
expect(
|
|
46
47
|
cat.status,
|
|
47
|
-
|
|
48
|
+
req('openwop.it.anonymous-actor-default-deny.the-anon-tool-catalog-returns-only-the-explicit-surface-grant-never-a-default-ba', 'RFC 0132 §C.1', 'the anon tool-catalog read MUST resolve (scoped to the anon principal)'),
|
|
48
49
|
).toBe(200);
|
|
49
50
|
// Default-deny: the ungranted baseline tool MUST NOT appear in the anon catalog.
|
|
50
51
|
const names = cat.tools.map((t) => t.name);
|
|
51
52
|
expect(
|
|
52
53
|
names,
|
|
53
|
-
|
|
54
|
+
req('openwop.it.anonymous-actor-default-deny.the-anon-tool-catalog-returns-only-the-explicit-surface-grant-never-a-default-ba', 'RFC 0132 §C.1', 'a default-on baseline tool MUST NOT appear in the anon grant'),
|
|
54
55
|
).not.toContain(UNGRANTED_TOOL);
|
|
55
56
|
});
|
|
56
57
|
|
|
57
58
|
it('calling a non-granted tool denies with reason "anon-not-granted" and does not dispatch', async () => {
|
|
58
59
|
if (!behaviorGate(PROFILE, await isAnonymousActorAdvertised())) return;
|
|
59
60
|
const res = await anonDispatch({ tool: UNGRANTED_TOOL });
|
|
60
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
|
|
61
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
61
62
|
const decided = res.json?.authorizationDecided?.payload;
|
|
62
63
|
expect(
|
|
63
64
|
decided?.allowed,
|
|
64
|
-
|
|
65
|
+
req('openwop.it.anonymous-actor-default-deny.calling-a-non-granted-tool-denies-with-reason-anon-not-granted-and-does-not-disp', 'RFC 0132 §C.1', 'a non-granted anon tool MUST be denied (allowed:false), never default-allowed'),
|
|
65
66
|
).toBe(false);
|
|
66
67
|
expect(
|
|
67
68
|
decided?.reason,
|
|
68
|
-
|
|
69
|
+
req('openwop.it.anonymous-actor-default-deny.calling-a-non-granted-tool-denies-with-reason-anon-not-granted-and-does-not-disp', 'RFC 0132 §C.1', 'a default-deny denial carries the machine reason "anon-not-granted"'),
|
|
69
70
|
).toBe('anon-not-granted');
|
|
70
71
|
// No dispatch — the seam MUST NOT return a tool result for a denied call.
|
|
71
72
|
expect(
|
|
72
73
|
res.json?.result,
|
|
73
|
-
|
|
74
|
+
req('openwop.it.anonymous-actor-default-deny.calling-a-non-granted-tool-denies-with-reason-anon-not-granted-and-does-not-disp', 'RFC 0132 §C.1', 'a denied anon tool MUST NOT dispatch (no result)'),
|
|
74
75
|
).toBeUndefined();
|
|
75
76
|
});
|
|
76
77
|
|
|
@@ -80,7 +81,7 @@ describe('anonymous-actor-default-deny (RFC 0132 §C.1)', () => {
|
|
|
80
81
|
if ((cap?.tiers ?? []).includes('bounded-write-egress')) {
|
|
81
82
|
expect(
|
|
82
83
|
(cap?.writeEgressControls ?? []).length,
|
|
83
|
-
|
|
84
|
+
req('openwop.it.anonymous-actor-default-deny.a-host-that-advertises-bounded-write-egress-also-advertises-a-mandatory-control', 'RFC 0132 §B.2', 'bounded-write-egress MUST advertise ≥1 writeEgressControl'),
|
|
84
85
|
).toBeGreaterThan(0);
|
|
85
86
|
}
|
|
86
87
|
});
|
|
@@ -21,8 +21,9 @@
|
|
|
21
21
|
|
|
22
22
|
import { describe, it, expect } from 'vitest';
|
|
23
23
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
24
|
-
import { driver } from '../lib/driver.js';
|
|
25
24
|
import { readAnonymousActorCap, anonDispatch } from '../lib/anonymousActor.js';
|
|
25
|
+
import { req } from '../lib/requirement-ids.js';
|
|
26
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
26
27
|
|
|
27
28
|
const PROFILE = 'openwop-anonymous-actor';
|
|
28
29
|
|
|
@@ -36,19 +37,19 @@ describe('anonymous-actor-egress-guarded (RFC 0132 §C.3)', () => {
|
|
|
36
37
|
tool: 'http.fetch',
|
|
37
38
|
destination: 'https://attacker.example/exfil',
|
|
38
39
|
});
|
|
39
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
|
|
40
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
40
41
|
const egress = res.json?.egressDecided;
|
|
41
42
|
expect(
|
|
42
43
|
egress,
|
|
43
|
-
|
|
44
|
+
req('openwop.it.anonymous-actor-egress-guarded.an-out-of-audience-anon-egress-is-denied-downgraded-and-attaches-no-credential', 'RFC 0132 §C.3', 'an anon egress MUST ride the RFC 0079 egress-decision path'),
|
|
44
45
|
).toBeDefined();
|
|
45
46
|
expect(
|
|
46
47
|
['denied', 'downgraded'],
|
|
47
|
-
|
|
48
|
+
req('openwop.it.anonymous-actor-egress-guarded.an-out-of-audience-anon-egress-is-denied-downgraded-and-attaches-no-credential', 'RFC 0132 §C.3', 'an out-of-audience anon egress MUST be denied or downgraded — never allowed-with-credential'),
|
|
48
49
|
).toContain(egress?.decision);
|
|
49
50
|
expect(
|
|
50
51
|
egress?.credentialAttached === true,
|
|
51
|
-
|
|
52
|
+
req('openwop.it.anonymous-actor-egress-guarded.an-out-of-audience-anon-egress-is-denied-downgraded-and-attaches-no-credential', 'SECURITY anon-actor-egress-ssrf-guarded', 'no tenant/host credential MUST attach out-of-audience'),
|
|
52
53
|
).toBe(false);
|
|
53
54
|
});
|
|
54
55
|
});
|
|
@@ -19,8 +19,9 @@
|
|
|
19
19
|
|
|
20
20
|
import { describe, it, expect } from 'vitest';
|
|
21
21
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
22
|
-
import { driver } from '../lib/driver.js';
|
|
23
22
|
import { isAnonymousActorAdvertised, anonDispatch } from '../lib/anonymousActor.js';
|
|
23
|
+
import { req } from '../lib/requirement-ids.js';
|
|
24
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
24
25
|
|
|
25
26
|
const PROFILE = 'openwop-anonymous-actor';
|
|
26
27
|
|
|
@@ -32,11 +33,11 @@ describe('anonymous-actor-no-secret-reach (RFC 0132 §C.2/§C.3)', () => {
|
|
|
32
33
|
it('a read-tier anon tool call never surfaces a planted BYOK canary', async () => {
|
|
33
34
|
if (!behaviorGate(PROFILE, await isAnonymousActorAdvertised())) return;
|
|
34
35
|
const res = await anonDispatch({ tool: 'catalog.read', args: { probeSecrets: true } });
|
|
35
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
|
|
36
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
36
37
|
const serialized = JSON.stringify(res.json ?? {});
|
|
37
38
|
expect(
|
|
38
39
|
serialized.includes(BYOK_CANARY),
|
|
39
|
-
|
|
40
|
+
req('openwop.it.anonymous-actor-no-secret-reach.a-read-tier-anon-tool-call-never-surfaces-a-planted-byok-canary', 'SECURITY anon-actor-no-secret-reach', 'an anon tool call MUST NOT surface secret/BYOK material'),
|
|
40
41
|
).toBe(false);
|
|
41
42
|
});
|
|
42
43
|
|
|
@@ -50,7 +51,7 @@ describe('anonymous-actor-no-secret-reach (RFC 0132 §C.2/§C.3)', () => {
|
|
|
50
51
|
// one non-conformant behavior is resolving/returning the caller's tenant.
|
|
51
52
|
const CROSS = 'other-tenant';
|
|
52
53
|
const res = await anonDispatch({ tool: 'catalog.read', args: { tenant: CROSS } });
|
|
53
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
|
|
54
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
54
55
|
const decided = res.json?.authorizationDecided?.payload;
|
|
55
56
|
const deniedByDecision = decided?.allowed === false;
|
|
56
57
|
const deniedByStatus = res.status === 403 || res.status === 404;
|
|
@@ -61,7 +62,7 @@ describe('anonymous-actor-no-secret-reach (RFC 0132 §C.2/§C.3)', () => {
|
|
|
61
62
|
typeof resolvedTenant === 'string' && resolvedTenant.length > 0 && resolvedTenant !== CROSS;
|
|
62
63
|
expect(
|
|
63
64
|
deniedByDecision || deniedByStatus || neutralizedToSurface,
|
|
64
|
-
|
|
65
|
+
req('openwop.it.anonymous-actor-no-secret-reach.a-cross-tenant-read-fails-closed-the-caller-supplied-tenant-is-never-resolved',
|
|
65
66
|
'SECURITY anon-actor-no-secret-reach (CTI-1 / RFC 0132 §A.2)',
|
|
66
67
|
'a cross-tenant anon read MUST fail closed — denied, or the caller-supplied tenant ignored (never resolved to the caller’s tenant)',
|
|
67
68
|
),
|
|
@@ -71,7 +72,7 @@ describe('anonymous-actor-no-secret-reach (RFC 0132 §C.2/§C.3)', () => {
|
|
|
71
72
|
if (deniedByDecision || deniedByStatus) {
|
|
72
73
|
expect(
|
|
73
74
|
res.json?.result,
|
|
74
|
-
|
|
75
|
+
req('openwop.it.anonymous-actor-no-secret-reach.a-cross-tenant-read-fails-closed-the-caller-supplied-tenant-is-never-resolved', 'SECURITY anon-actor-no-secret-reach (CTI-1)', 'a denied cross-tenant anon read MUST NOT return data'),
|
|
75
76
|
).toBeUndefined();
|
|
76
77
|
}
|
|
77
78
|
});
|
|
@@ -34,9 +34,7 @@ import { join } from 'node:path';
|
|
|
34
34
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
35
35
|
import addFormats from 'ajv-formats';
|
|
36
36
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
37
|
-
|
|
38
|
-
/** Server-free assertion-message helper (mirrors driver.describe without OPENWOP_BASE_URL). */
|
|
39
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
37
|
+
import { req } from '../lib/requirement-ids.js';
|
|
40
38
|
|
|
41
39
|
function loadSchema(name: string): Record<string, unknown> {
|
|
42
40
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
|
|
@@ -50,12 +48,12 @@ describe('anonymous-actor-shape: capability advertisement (RFC 0132 §B, server-
|
|
|
50
48
|
it('the capabilities schema declares anonymousActor with its sub-flags', () => {
|
|
51
49
|
expect(
|
|
52
50
|
anon,
|
|
53
|
-
|
|
51
|
+
req('openwop.it.anonymous-actor-shape.the-capabilities-schema-declares-anonymousactor-with-its-sub-flags', 'capabilities.md §anonymousActor', 'capabilities.anonymousActor MUST be declared'),
|
|
54
52
|
).toBeDefined();
|
|
55
53
|
for (const flag of ['supported', 'tiers', 'writeEgressControls', 'failClosed']) {
|
|
56
54
|
expect(
|
|
57
55
|
anon?.properties?.[flag],
|
|
58
|
-
|
|
56
|
+
req('openwop.it.anonymous-actor-shape.the-capabilities-schema-declares-anonymousactor-with-its-sub-flags', 'capabilities.md §anonymousActor', `anonymousActor.${flag} MUST be declared`),
|
|
59
57
|
).toBeDefined();
|
|
60
58
|
}
|
|
61
59
|
});
|
|
@@ -68,7 +66,7 @@ describe('anonymous-actor-shape: capability advertisement (RFC 0132 §B, server-
|
|
|
68
66
|
// Positive — read-only tier.
|
|
69
67
|
expect(
|
|
70
68
|
validate({ supported: true, tiers: ['read'], failClosed: true }),
|
|
71
|
-
|
|
69
|
+
req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B', 'a conforming read-tier advert MUST validate'),
|
|
72
70
|
).toBe(true);
|
|
73
71
|
// Positive — bounded-write-egress tier with a mandatory control.
|
|
74
72
|
expect(
|
|
@@ -78,33 +76,33 @@ describe('anonymous-actor-shape: capability advertisement (RFC 0132 §B, server-
|
|
|
78
76
|
writeEgressControls: ['hitl'],
|
|
79
77
|
failClosed: true,
|
|
80
78
|
}),
|
|
81
|
-
|
|
79
|
+
req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B', 'a bounded-write-egress advert with a control MUST validate'),
|
|
82
80
|
).toBe(true);
|
|
83
81
|
|
|
84
82
|
// Negative — empty tiers (minItems: 1).
|
|
85
83
|
expect(
|
|
86
84
|
validate({ supported: true, tiers: [] }),
|
|
87
|
-
|
|
85
|
+
req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B', 'tiers: [] MUST be rejected (minItems)'),
|
|
88
86
|
).toBe(false);
|
|
89
87
|
// Negative — write tier without a control (the §B.2 conditional-MUST).
|
|
90
88
|
expect(
|
|
91
89
|
validate({ supported: true, tiers: ['bounded-write-egress'] }),
|
|
92
|
-
|
|
90
|
+
req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B.2', 'bounded-write-egress without writeEgressControls MUST be rejected'),
|
|
93
91
|
).toBe(false);
|
|
94
92
|
// Negative — controls advertised without the write tier (§B.2 else branch).
|
|
95
93
|
expect(
|
|
96
94
|
validate({ supported: true, tiers: ['read'], writeEgressControls: ['hitl'] }),
|
|
97
|
-
|
|
95
|
+
req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B.2', 'writeEgressControls without the write tier MUST be rejected'),
|
|
98
96
|
).toBe(false);
|
|
99
97
|
// Negative — supported: false (the block is omitted when unsupported, const: true).
|
|
100
98
|
expect(
|
|
101
99
|
validate({ supported: false, tiers: ['read'] }),
|
|
102
|
-
|
|
100
|
+
req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B', 'supported: false MUST be rejected (const: true — omit the block instead)'),
|
|
103
101
|
).toBe(false);
|
|
104
102
|
// Negative — unknown property (additionalProperties: false).
|
|
105
103
|
expect(
|
|
106
104
|
validate({ supported: true, tiers: ['read'], surface: 'wgt_abc' }),
|
|
107
|
-
|
|
105
|
+
req('openwop.it.anonymous-actor-shape.validates-conforming-read-bounded-write-egress-adverts-and-rejects-the-negatives', 'RFC 0132 §B', 'an unknown property MUST be rejected (additionalProperties: false)'),
|
|
108
106
|
).toBe(false);
|
|
109
107
|
});
|
|
110
108
|
});
|
|
@@ -122,16 +120,16 @@ describe('anonymous-actor-shape: owner.principalKind (RFC 0132 §A, server-free)
|
|
|
122
120
|
const validate = ajv.compile(owner);
|
|
123
121
|
expect(
|
|
124
122
|
validate({ tenant: 'acme', principal: 'anon:sess-3f9c', principalKind: 'anonymous' }),
|
|
125
|
-
|
|
123
|
+
req('openwop.it.anonymous-actor-shape.run-snapshot-owner-accepts-principalkind-anonymous-and-rejects-guest', 'RFC 0132 §A', 'owner.principalKind "anonymous" MUST validate'),
|
|
126
124
|
).toBe(true);
|
|
127
125
|
// Absent principalKind is today's RFC 0048 behavior — still valid.
|
|
128
126
|
expect(
|
|
129
127
|
validate({ tenant: 'acme', principal: 'u_42' }),
|
|
130
|
-
|
|
128
|
+
req('openwop.it.anonymous-actor-shape.run-snapshot-owner-accepts-principalkind-anonymous-and-rejects-guest', 'RFC 0048', 'owner without principalKind MUST still validate'),
|
|
131
129
|
).toBe(true);
|
|
132
130
|
expect(
|
|
133
131
|
validate({ tenant: 'acme', principalKind: 'guest' }),
|
|
134
|
-
|
|
132
|
+
req('openwop.it.anonymous-actor-shape.run-snapshot-owner-accepts-principalkind-anonymous-and-rejects-guest', 'RFC 0132 §A', 'owner.principalKind "guest" MUST be rejected (enum)'),
|
|
135
133
|
).toBe(false);
|
|
136
134
|
});
|
|
137
135
|
});
|
|
@@ -145,7 +143,7 @@ describe('anonymous-actor-shape: audit reuses authorization.decided (RFC 0132 §
|
|
|
145
143
|
const decided = ajv.getSchema('payloads#/$defs/authorizationDecided');
|
|
146
144
|
|
|
147
145
|
it('a content-free anon grant + deny record validates against the existing $def', () => {
|
|
148
|
-
expect(decided, 'the authorizationDecided $def MUST exist (no new event minted)').toBeTruthy();
|
|
146
|
+
expect(decided, req('openwop.it.anonymous-actor-shape.a-content-free-anon-grant-deny-record-validates-against-the-existing-def', 'RFC 0132', 'the authorizationDecided $def MUST exist (no new event minted)')).toBeTruthy();
|
|
149
147
|
// Grant — the §G positive example.
|
|
150
148
|
expect(
|
|
151
149
|
decided!({
|
|
@@ -155,7 +153,7 @@ describe('anonymous-actor-shape: audit reuses authorization.decided (RFC 0132 §
|
|
|
155
153
|
allowed: true,
|
|
156
154
|
reason: 'anon-granted',
|
|
157
155
|
}),
|
|
158
|
-
|
|
156
|
+
req('openwop.it.anonymous-actor-shape.a-content-free-anon-grant-deny-record-validates-against-the-existing-def', 'RFC 0132 §D', 'a conforming anon grant record MUST validate'),
|
|
159
157
|
).toBe(true);
|
|
160
158
|
// Deny — a not-granted tool.
|
|
161
159
|
expect(
|
|
@@ -166,13 +164,13 @@ describe('anonymous-actor-shape: audit reuses authorization.decided (RFC 0132 §
|
|
|
166
164
|
allowed: false,
|
|
167
165
|
reason: 'anon-not-granted',
|
|
168
166
|
}),
|
|
169
|
-
|
|
167
|
+
req('openwop.it.anonymous-actor-shape.a-content-free-anon-grant-deny-record-validates-against-the-existing-def', 'RFC 0132 §D', 'a conforming anon deny record MUST validate'),
|
|
170
168
|
).toBe(true);
|
|
171
169
|
});
|
|
172
170
|
|
|
173
171
|
it('authorization.decided is in the RunEventType enum (the reused audit event)', () => {
|
|
174
172
|
const runEvent = loadSchema('run-event.schema.json');
|
|
175
173
|
const enumVals = (runEvent.$defs as Record<string, { enum?: string[] }>).RunEventType?.enum ?? [];
|
|
176
|
-
expect(enumVals).toContain('authorization.decided');
|
|
174
|
+
expect(enumVals, req('openwop.it.anonymous-actor-shape.authorization-decided-is-in-the-runeventtype-enum-the-reused-audit-event', 'RFC 0132', 'authorization.decided is in the RunEventType enum (the reused audit event)')).toContain('authorization.decided');
|
|
177
175
|
});
|
|
178
176
|
});
|
|
@@ -22,8 +22,9 @@
|
|
|
22
22
|
|
|
23
23
|
import { describe, it, expect } from 'vitest';
|
|
24
24
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
25
|
-
import { driver } from '../lib/driver.js';
|
|
26
25
|
import { readAnonymousActorCap, anonDispatch } from '../lib/anonymousActor.js';
|
|
26
|
+
import { req } from '../lib/requirement-ids.js';
|
|
27
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
27
28
|
|
|
28
29
|
const PROFILE = 'openwop-anonymous-actor';
|
|
29
30
|
|
|
@@ -34,7 +35,7 @@ describe('anonymous-actor-write-gated (RFC 0132 §C.3)', () => {
|
|
|
34
35
|
if (!behaviorGate(PROFILE, cap?.supported === true && supportsWrite)) return;
|
|
35
36
|
|
|
36
37
|
const res = await anonDispatch({ tool: 'lead.capture', args: { email: 'visitor@example.com' } });
|
|
37
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
|
|
38
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
38
39
|
|
|
39
40
|
const controls = cap?.writeEgressControls ?? [];
|
|
40
41
|
const decided = res.json?.authorizationDecided?.payload;
|
|
@@ -44,11 +45,11 @@ describe('anonymous-actor-write-gated (RFC 0132 §C.3)', () => {
|
|
|
44
45
|
// With a HITL control the write MUST suspend on an approval interrupt before durable write.
|
|
45
46
|
expect(
|
|
46
47
|
suspended,
|
|
47
|
-
|
|
48
|
+
req('openwop.it.anonymous-actor-write-gated.an-anon-bounded-write-tool-is-gated-denied-when-ungated-or-suspends-on-an-approv', 'RFC 0132 §C.3 (hitl)', 'an anon bounded-write MUST suspend on an approval interrupt before the write'),
|
|
48
49
|
).toBe(true);
|
|
49
50
|
expect(
|
|
50
51
|
res.json?.result,
|
|
51
|
-
|
|
52
|
+
req('openwop.it.anonymous-actor-write-gated.an-anon-bounded-write-tool-is-gated-denied-when-ungated-or-suspends-on-an-approv', 'RFC 0132 §C.3 (hitl)', 'no durable write result before the approval resolves'),
|
|
52
53
|
).toBeUndefined();
|
|
53
54
|
} else {
|
|
54
55
|
// With only a rate-limit/session-cap control, the seam surfaces a gate; an
|
|
@@ -56,7 +57,7 @@ describe('anonymous-actor-write-gated (RFC 0132 §C.3)', () => {
|
|
|
56
57
|
const gatedOrDenied = suspended || decided?.allowed === false || res.status === 429;
|
|
57
58
|
expect(
|
|
58
59
|
gatedOrDenied,
|
|
59
|
-
|
|
60
|
+
req('openwop.it.anonymous-actor-write-gated.an-anon-bounded-write-tool-is-gated-denied-when-ungated-or-suspends-on-an-approv', 'SECURITY anon-actor-write-egress-gated', 'an anon bounded-write MUST be gated — an ungated write MUST be denied'),
|
|
60
61
|
).toBe(true);
|
|
61
62
|
}
|
|
62
63
|
});
|
|
@@ -67,16 +68,16 @@ describe('anonymous-actor-write-gated (RFC 0132 §C.3)', () => {
|
|
|
67
68
|
if (!behaviorGate(PROFILE, cap?.supported === true && supportsWrite)) return;
|
|
68
69
|
// Probe a surface deliberately configured with a write grant but no control.
|
|
69
70
|
const res = await anonDispatch({ tool: 'lead.capture', surface: 'sample-uncontrolled-surface' });
|
|
70
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired / surface absent — soft-skip
|
|
71
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired / surface absent — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired / surface absent — soft-skip
|
|
71
72
|
const decided = res.json?.authorizationDecided?.payload;
|
|
72
73
|
expect(
|
|
73
74
|
decided?.allowed === false || res.status === 403 || res.status === 429,
|
|
74
|
-
|
|
75
|
+
req('openwop.it.anonymous-actor-write-gated.an-anon-write-with-no-resolvable-control-is-denied-with-a-machine-reason', 'SECURITY anon-actor-write-egress-gated', 'an anon write with no resolvable control MUST be denied'),
|
|
75
76
|
).toBe(true);
|
|
76
77
|
if (decided && decided.allowed === false) {
|
|
77
78
|
expect(
|
|
78
79
|
decided.reason,
|
|
79
|
-
|
|
80
|
+
req('openwop.it.anonymous-actor-write-gated.an-anon-write-with-no-resolvable-control-is-denied-with-a-machine-reason', 'RFC 0132 §C.3', 'an ungated anon write denial carries a machine reason'),
|
|
80
81
|
).toBe('anon-write-ungated');
|
|
81
82
|
}
|
|
82
83
|
});
|
|
@@ -18,6 +18,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
18
18
|
import { driver } from '../lib/driver.js';
|
|
19
19
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
20
20
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
21
|
+
import { req } from '../lib/requirement-ids.js';
|
|
22
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
21
23
|
|
|
22
24
|
const CANDIDATES = ['conformance-append-ordering', 'conformance-multi-node'] as const;
|
|
23
25
|
const FIXTURE = CANDIDATES.find((id) => isFixtureAdvertised(id)) ?? null;
|
|
@@ -71,13 +73,13 @@ describe.skipIf(SKIP)('append-ordering: folded channel reflects event sequence',
|
|
|
71
73
|
console.warn(
|
|
72
74
|
'[append-ordering] fixture emitted no channel.written events; skipping ordering assertions',
|
|
73
75
|
);
|
|
74
|
-
return;
|
|
76
|
+
return softSkip('blocked', 'precondition not met — `byChannel.size === 0` returned early ([append-ordering] fixture emitted no channel.written events; skipping ordering assertions) (seam, prior step, or fixture unavailable)');
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
// For each channel, sequence MUST be strictly increasing within the run.
|
|
78
80
|
for (const [channel, writes] of byChannel) {
|
|
79
81
|
for (let i = 1; i < writes.length; i++) {
|
|
80
|
-
expect(writes[i].sequence,
|
|
82
|
+
expect(writes[i].sequence, req('openwop.it.append-ordering.append-reducer-channels-project-entries-in-event-sequence-order',
|
|
81
83
|
'channels-and-reducers.md §"Append ordering"',
|
|
82
84
|
`channel '${channel}': channel.written events MUST appear in sequence order in the event log`,
|
|
83
85
|
)).toBeGreaterThan(writes[i - 1].sequence);
|
|
@@ -101,7 +103,7 @@ describe.skipIf(SKIP)('append-ordering: folded channel reflects event sequence',
|
|
|
101
103
|
// Property: every cross-engine event carries BOTH sourceEngineId
|
|
102
104
|
// AND sourceRunId (per channel-written-payload.schema.json).
|
|
103
105
|
for (const e of crossEngineWrites) {
|
|
104
|
-
expect(typeof e.payload?.sourceRunId,
|
|
106
|
+
expect(typeof e.payload?.sourceRunId, req('openwop.it.append-ordering.append-reducer-channels-project-entries-in-event-sequence-order',
|
|
105
107
|
'channel-written-payload.schema.json',
|
|
106
108
|
'cross-engine writes MUST carry sourceRunId alongside sourceEngineId',
|
|
107
109
|
)).toBe('string');
|
|
@@ -110,7 +112,7 @@ describe.skipIf(SKIP)('append-ordering: folded channel reflects event sequence',
|
|
|
110
112
|
const seen = new Set<string>();
|
|
111
113
|
for (const e of allWrites) {
|
|
112
114
|
const key = `${e.sequence}::${e.eventId ?? ''}`;
|
|
113
|
-
expect(seen.has(key),
|
|
115
|
+
expect(seen.has(key), req('openwop.it.append-ordering.append-reducer-channels-project-entries-in-event-sequence-order',
|
|
114
116
|
'channels-and-reducers.md §"Tie-breaking when sequences collide"',
|
|
115
117
|
`(sequence, eventId) MUST be unique across the run — duplicate found: ${key}`,
|
|
116
118
|
)).toBe(false);
|
|
@@ -125,7 +127,7 @@ describe.skipIf(SKIP)('append-ordering: folded channel reflects event sequence',
|
|
|
125
127
|
for (const [channel, writes] of byChannel) {
|
|
126
128
|
const projected = (channels as Record<string, unknown>)[channel];
|
|
127
129
|
if (Array.isArray(projected)) {
|
|
128
|
-
expect(projected.length,
|
|
130
|
+
expect(projected.length, req('openwop.it.append-ordering.append-reducer-channels-project-entries-in-event-sequence-order',
|
|
129
131
|
'channels-and-reducers.md §"Append ordering"',
|
|
130
132
|
`channel '${channel}': projected array length MUST equal #channel.written events`,
|
|
131
133
|
)).toBe(writes.length);
|
|
@@ -22,6 +22,7 @@ import { readFileSync } from 'node:fs';
|
|
|
22
22
|
import { join } from 'node:path';
|
|
23
23
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
24
24
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
25
|
+
import { req } from '../lib/requirement-ids.js';
|
|
25
26
|
|
|
26
27
|
interface PayloadsSchema {
|
|
27
28
|
$schema: string;
|
|
@@ -41,7 +42,7 @@ describe('category: approval-gate governance events (RFC 0051 §B)', () => {
|
|
|
41
42
|
it('approval.granted requires gateId + principal; quorumProgress optional', () => {
|
|
42
43
|
const v = compile('approvalGranted');
|
|
43
44
|
expect(v({ gateId: 'g1', principal: 'user_1' }), JSON.stringify(v.errors)).toBe(true);
|
|
44
|
-
expect(v({ gateId: 'g1', principal: 'user_1', quorumProgress: { granted: 1, required: 2 } })).toBe(true);
|
|
45
|
+
expect(v({ gateId: 'g1', principal: 'user_1', quorumProgress: { granted: 1, required: 2 } }), req('openwop.it.approval-gate-events.approval-granted-requires-gateid-principal-quorumprogress-optional', 'RFC 0051 §B', 'approval.granted requires gateId + principal; quorumProgress optional')).toBe(true);
|
|
45
46
|
expect(v({ gateId: 'g1' })).toBe(false); // missing principal
|
|
46
47
|
expect(v({ gateId: 'g1', principal: 'user_1', role: 'admin' })).toBe(false); // unknown prop
|
|
47
48
|
});
|
|
@@ -49,13 +50,13 @@ describe('category: approval-gate governance events (RFC 0051 §B)', () => {
|
|
|
49
50
|
it('approval.rejected requires gateId + principal; reason optional', () => {
|
|
50
51
|
const v = compile('approvalRejected');
|
|
51
52
|
expect(v({ gateId: 'g1', principal: 'user_1' }), JSON.stringify(v.errors)).toBe(true);
|
|
52
|
-
expect(v({ gateId: 'g1', principal: 'user_1', reason: 'incomplete' })).toBe(true);
|
|
53
|
+
expect(v({ gateId: 'g1', principal: 'user_1', reason: 'incomplete' }), req('openwop.it.approval-gate-events.approval-rejected-requires-gateid-principal-reason-optional', 'RFC 0051 §B', 'approval.rejected requires gateId + principal; reason optional')).toBe(true);
|
|
53
54
|
expect(v({ principal: 'user_1' })).toBe(false); // missing gateId
|
|
54
55
|
});
|
|
55
56
|
|
|
56
57
|
it('approval.overridden requires gateId + principal + reason (audit breadcrumb)', () => {
|
|
57
58
|
const v = compile('approvalOverridden');
|
|
58
59
|
expect(v({ gateId: 'g1', principal: 'owner_1', reason: 'emergency publish' }), JSON.stringify(v.errors)).toBe(true);
|
|
59
|
-
expect(v({ gateId: 'g1', principal: 'owner_1' })).toBe(false); // reason MUST be present
|
|
60
|
+
expect(v({ gateId: 'g1', principal: 'owner_1' }), req('openwop.it.approval-gate-events.approval-overridden-requires-gateid-principal-reason-audit-breadcrumb', 'RFC 0051 §B', 'approval.overridden requires gateId + principal + reason (audit breadcrumb)')).toBe(false); // reason MUST be present
|
|
60
61
|
});
|
|
61
62
|
});
|
|
@@ -24,6 +24,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
24
24
|
import { softSkip } from '../lib/soft-skip.js';
|
|
25
25
|
import { driver } from '../lib/driver.js';
|
|
26
26
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
27
|
+
import { req } from '../lib/requirement-ids.js';
|
|
27
28
|
|
|
28
29
|
async function authorizationSupported(): Promise<boolean> {
|
|
29
30
|
// Root-first per RFC 0073 (`capabilities.authorization` is the deprecated wrapper shape).
|
|
@@ -42,7 +43,7 @@ describe('approval-gate-flow: role-gated, audited approval (RFC 0051 §A)', () =
|
|
|
42
43
|
const body = res.json as { released?: boolean } | undefined;
|
|
43
44
|
expect(
|
|
44
45
|
body?.released,
|
|
45
|
-
|
|
46
|
+
req('openwop.it.approval-gate-flow.an-unauthorized-principal-does-not-release-the-gate-fail-closed', 'RFC 0051 §A', 'an unauthorized principal MUST NOT release the gate (fail-closed)'),
|
|
46
47
|
).toBe(false);
|
|
47
48
|
});
|
|
48
49
|
|
|
@@ -57,11 +58,11 @@ describe('approval-gate-flow: role-gated, audited approval (RFC 0051 §A)', () =
|
|
|
57
58
|
const body = res.json as { event?: { type?: string; payload?: { reason?: string } } } | undefined;
|
|
58
59
|
expect(
|
|
59
60
|
body?.event?.type,
|
|
60
|
-
|
|
61
|
+
req('openwop.it.approval-gate-flow.the-override-path-emits-an-audited-approval-overridden-with-a-reason', 'RFC 0051 §B', 'taking the override path MUST emit approval.overridden'),
|
|
61
62
|
).toBe('approval.overridden');
|
|
62
63
|
expect(
|
|
63
64
|
typeof body?.event?.payload?.reason === 'string' && body.event.payload.reason.length > 0,
|
|
64
|
-
|
|
65
|
+
req('openwop.it.approval-gate-flow.the-override-path-emits-an-audited-approval-overridden-with-a-reason', 'RFC 0051 §B', 'approval.overridden MUST carry a non-empty reason (the audit breadcrumb)'),
|
|
65
66
|
).toBe(true);
|
|
66
67
|
});
|
|
67
68
|
});
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
|
|
44
44
|
import { describe, it, expect } from 'vitest';
|
|
45
45
|
import { driver } from '../lib/driver.js';
|
|
46
|
+
import { req } from '../lib/requirement-ids.js';
|
|
46
47
|
|
|
47
48
|
const CANONICAL_ACTIONS = ['accept', 'reject', 'refine', 'edit-accept', 'ask'] as const;
|
|
48
49
|
const CANONICAL_EVENT_ACTIONS = ['accept', 'reject', 'refine', 'edit-accept', 'timeout'] as const;
|
|
@@ -73,7 +74,7 @@ describe('approval-payload: vocabulary discovery contract', () => {
|
|
|
73
74
|
// is distinguishable from the canonical form.
|
|
74
75
|
const legacyHits = text.match(/"edit"/g) ?? [];
|
|
75
76
|
|
|
76
|
-
expect(legacyHits.length,
|
|
77
|
+
expect(legacyHits.length, req('openwop.it.approval-payload.host-capability-declaration-does-not-regress-on-the-legacy-edit-form-7-drift-pin',
|
|
77
78
|
'interrupt.md §"`ApprovalResume`"',
|
|
78
79
|
'capability declaration MUST NOT contain the legacy `"edit"` action token (use `"edit-accept"` per spec)',
|
|
79
80
|
)).toBe(0);
|
|
@@ -84,7 +85,7 @@ describe('approval-payload: vocabulary discovery contract', () => {
|
|
|
84
85
|
// accept/reject/refine/edit-accept/ask. Per-host advertisement is
|
|
85
86
|
// optional; this test pins the vocabulary itself for future
|
|
86
87
|
// scenarios that gate on it.
|
|
87
|
-
expect(CANONICAL_ACTIONS).toHaveLength(5);
|
|
88
|
+
expect(CANONICAL_ACTIONS, req('openwop.it.approval-payload.canonical-action-vocabulary-is-documented-in-spec-assertion-free-reference', 'capabilities.md', 'canonical action vocabulary is documented in spec (assertion-free reference)')).toHaveLength(5);
|
|
88
89
|
expect(new Set(CANONICAL_ACTIONS)).toEqual(
|
|
89
90
|
new Set(['accept', 'reject', 'refine', 'edit-accept', 'ask']),
|
|
90
91
|
);
|
|
@@ -98,7 +99,7 @@ describe('approval-payload: vocabulary discovery contract', () => {
|
|
|
98
99
|
// but isn't a resume action (clients can't submit a timeout).
|
|
99
100
|
//
|
|
100
101
|
// Pin this asymmetry so it doesn't drift.
|
|
101
|
-
expect(CANONICAL_EVENT_ACTIONS).toHaveLength(5);
|
|
102
|
+
expect(CANONICAL_EVENT_ACTIONS, req('openwop.it.approval-payload.canonical-event-action-vocabulary-differs-from-resume-timeout-instead-of-ask', 'capabilities.md', 'canonical event action vocabulary differs from resume (timeout instead of ask)')).toHaveLength(5);
|
|
102
103
|
expect(new Set(CANONICAL_EVENT_ACTIONS)).toEqual(
|
|
103
104
|
new Set(['accept', 'reject', 'refine', 'edit-accept', 'timeout']),
|
|
104
105
|
);
|
|
@@ -112,7 +113,7 @@ describe('approval-payload: vocabulary discovery contract', () => {
|
|
|
112
113
|
// The 3 documented scopes `whole/section/items` MUST be a stable
|
|
113
114
|
// set in v1.x. Adding a scope is additive (clients tolerating
|
|
114
115
|
// unknown values) but semantic changes need a spec discussion.
|
|
115
|
-
expect(CANONICAL_REFINE_SCOPES).toHaveLength(3);
|
|
116
|
+
expect(CANONICAL_REFINE_SCOPES, req('openwop.it.approval-payload.refinefeedback-scope-vocabulary-pin-7-audit-a-5-prereq', 'capabilities.md', 'refineFeedback scope vocabulary pin (§7 audit, A.5 prereq)')).toHaveLength(3);
|
|
116
117
|
expect(new Set(CANONICAL_REFINE_SCOPES)).toEqual(
|
|
117
118
|
new Set(['whole', 'section', 'items']),
|
|
118
119
|
);
|
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
|
|
21
21
|
import { describe, it, expect } from 'vitest';
|
|
22
22
|
|
|
23
|
-
import { driver } from '../lib/driver.js';
|
|
24
23
|
import { loadEnv } from '../lib/env.js';
|
|
24
|
+
import { req } from '../lib/requirement-ids.js';
|
|
25
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
25
26
|
|
|
26
27
|
describe('artifact-auth: unauthenticated artifact requests are rejected', () => {
|
|
27
28
|
it('GET /v1/runs/{runId}/artifacts/{artifactId} without Authorization returns 401', async () => {
|
|
@@ -31,12 +32,12 @@ describe('artifact-auth: unauthenticated artifact requests are rejected', () =>
|
|
|
31
32
|
const res = await fetch(
|
|
32
33
|
`${env.baseUrl}/v1/runs/openwop-conformance-noauth-run/artifacts/openwop-conformance-noauth-artifact`,
|
|
33
34
|
);
|
|
34
|
-
expect(res.status,
|
|
35
|
+
expect(res.status, req('openwop.it.artifact-auth.get-v1-runs-runid-artifacts-artifactid-without-authorization-returns-401',
|
|
35
36
|
'rest-endpoints.md GET /v1/runs/{runId}/artifacts/{artifactId}',
|
|
36
37
|
'artifact endpoint MUST reject unauthenticated requests with 401 (never 200, never redirect)',
|
|
37
38
|
)).toBe(401);
|
|
38
|
-
expect(res.status, 'redirect MUST NOT be used to handle missing auth on artifact endpoint').not.toBe(301);
|
|
39
|
-
expect(res.status, 'redirect MUST NOT be used to handle missing auth on artifact endpoint').not.toBe(302);
|
|
39
|
+
expect(res.status, req('openwop.it.artifact-auth.get-v1-runs-runid-artifacts-artifactid-without-authorization-returns-401', 'rest-endpoints.md GET /v1/runs/{runId}/artifacts/{artifactId}', 'redirect MUST NOT be used to handle missing auth on artifact endpoint')).not.toBe(301);
|
|
40
|
+
expect(res.status, req('openwop.it.artifact-auth.get-v1-runs-runid-artifacts-artifactid-without-authorization-returns-401', 'rest-endpoints.md GET /v1/runs/{runId}/artifacts/{artifactId}', 'redirect MUST NOT be used to handle missing auth on artifact endpoint')).not.toBe(302);
|
|
40
41
|
expect(res.status).not.toBe(200);
|
|
41
42
|
|
|
42
43
|
// Canonical error envelope: error: 'unauthenticated', message: string.
|
|
@@ -46,10 +47,10 @@ describe('artifact-auth: unauthenticated artifact requests are rejected', () =>
|
|
|
46
47
|
} catch {
|
|
47
48
|
// Some hosts return a bare 401 with no body — acceptable. Skip the
|
|
48
49
|
// envelope-shape assertion in that case.
|
|
49
|
-
return;
|
|
50
|
+
return softSkip('blocked', 'precondition not met — an earlier step threw (Some hosts return a bare 401 with no body — acceptable. Skip the envelope-shape assertion in that case.) (seam, prior step, or fixture unavailable)');
|
|
50
51
|
}
|
|
51
52
|
if (typeof body.error === 'string') {
|
|
52
|
-
expect(body.error,
|
|
53
|
+
expect(body.error, req('openwop.it.artifact-auth.get-v1-runs-runid-artifacts-artifactid-without-authorization-returns-401',
|
|
53
54
|
'auth.md §"Error envelope"',
|
|
54
55
|
'401 envelope SHOULD carry `error: "unauthenticated"` for missing-auth on artifact endpoint',
|
|
55
56
|
)).toBe('unauthenticated');
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import { describe, it, expect } from 'vitest';
|
|
29
|
-
import { driver } from '../lib/driver.js';
|
|
30
29
|
import { behaviorGate, behaviorGatePresent } from '../lib/behavior-gate.js';
|
|
31
30
|
|
|
32
31
|
const PROFILE = 'openwop-artifact-type-packs';
|
|
@@ -37,6 +36,7 @@ import {
|
|
|
37
36
|
produceArtifact,
|
|
38
37
|
sampleArtifactTypePack,
|
|
39
38
|
} from '../lib/artifactTypes.js';
|
|
39
|
+
import { req } from '../lib/requirement-ids.js';
|
|
40
40
|
|
|
41
41
|
describe('artifact-type-pack-install: registered artifacts are schema-validated (RFC 0071)', () => {
|
|
42
42
|
it('a conforming payload yields artifact.created { registered: true }', async () => {
|
|
@@ -47,24 +47,24 @@ describe('artifact-type-pack-install: registered artifacts are schema-validated
|
|
|
47
47
|
if (!behaviorGatePresent(PROFILE, installed)) return; // seam absent: skip default, FAIL strict
|
|
48
48
|
expect(
|
|
49
49
|
installed.status >= 200 && installed.status < 300,
|
|
50
|
-
|
|
50
|
+
req('openwop.it.artifact-type-pack-install.a-conforming-payload-yields-artifact-created-registered-true', 'artifact-type-packs.md §"Pack kind"', 'a valid artifact-type pack MUST install cleanly'),
|
|
51
51
|
).toBe(true);
|
|
52
52
|
|
|
53
53
|
const produced = await produceArtifact(artifactTypeId, { title: 'Hello', body: 'World' });
|
|
54
54
|
if (!behaviorGatePresent(PROFILE, produced)) return; // seam absent: skip default, FAIL strict
|
|
55
55
|
expect(
|
|
56
56
|
produced.json['registered'],
|
|
57
|
-
|
|
57
|
+
req('openwop.it.artifact-type-pack-install.a-conforming-payload-yields-artifact-created-registered-true', 'artifact-type-packs.md §"Binding the existing artifact surfaces"', 'a payload matching a registered artifactTypeId MUST be marked registered'),
|
|
58
58
|
).toBe(true);
|
|
59
59
|
expect(
|
|
60
60
|
produced.json['validated'],
|
|
61
|
-
|
|
61
|
+
req('openwop.it.artifact-type-pack-install.a-conforming-payload-yields-artifact-created-registered-true', 'artifact-type-packs.md', 'the host MUST validate the payload against the pack schema before emitting artifact.created'),
|
|
62
62
|
).toBe(true);
|
|
63
63
|
const evt = produced.json['artifactCreated'] as { registered?: unknown } | undefined;
|
|
64
64
|
if (evt && 'registered' in evt) {
|
|
65
65
|
expect(
|
|
66
66
|
evt.registered,
|
|
67
|
-
|
|
67
|
+
req('openwop.it.artifact-type-pack-install.a-conforming-payload-yields-artifact-created-registered-true', 'run-event-payloads.schema.json §artifactCreated', 'artifact.created.registered MUST be true for a validated registered artifact'),
|
|
68
68
|
).toBe(true);
|
|
69
69
|
}
|
|
70
70
|
});
|
|
@@ -83,7 +83,7 @@ describe('artifact-type-pack-install: registered artifacts are schema-validated
|
|
|
83
83
|
produced.json['stored'] === false;
|
|
84
84
|
expect(
|
|
85
85
|
rejected,
|
|
86
|
-
|
|
86
|
+
req('openwop.it.artifact-type-pack-install.a-schema-violating-payload-is-rejected-not-stored-as-a-validated-registered-arti', 'artifact-type-packs.md §"Binding the existing artifact surfaces"', 'a payload that fails the pack schema MUST NOT be emitted as a validated registered artifact'),
|
|
87
87
|
).toBe(true);
|
|
88
88
|
});
|
|
89
89
|
});
|