@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
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
import { describe, it, expect } from 'vitest';
|
|
21
21
|
import { softSkip } from '../lib/soft-skip.js';
|
|
22
22
|
import { driver } from '../lib/driver.js';
|
|
23
|
+
import { req } from '../lib/requirement-ids.js';
|
|
23
24
|
|
|
24
25
|
interface CommitmentCaps {
|
|
25
26
|
agents?: { commitments?: { supported?: boolean } };
|
|
@@ -54,14 +55,14 @@ describe('commitment-fired: fire contract (RFC 0068 §C, capability-gated)', ()
|
|
|
54
55
|
});
|
|
55
56
|
if (res.status === 404 || res.status === 501) return softSkip('blocked', 'seam not wired — soft-skip');
|
|
56
57
|
|
|
57
|
-
expect(res.status,
|
|
58
|
+
expect(res.status, req('openwop.it.commitment-fired.a-fired-commitment-emits-a-content-free-event-with-memory-provenance-exactly-onc', 'RFC 0068 §C', 'an advertised commitment seam MUST succeed')).toBe(200);
|
|
58
59
|
const r = res.json as FireResult;
|
|
59
60
|
|
|
60
61
|
// §C — required identifiers.
|
|
61
|
-
expect(r.event?.commitmentId,
|
|
62
|
+
expect(r.event?.commitmentId, req('openwop.it.commitment-fired.a-fired-commitment-emits-a-content-free-event-with-memory-provenance-exactly-onc', 'RFC 0068 §C', 'commitment.fired MUST carry commitmentId')).toBeTruthy();
|
|
62
63
|
expect(
|
|
63
64
|
r.event?.memoryRef,
|
|
64
|
-
|
|
65
|
+
req('openwop.it.commitment-fired.a-fired-commitment-emits-a-content-free-event-with-memory-provenance-exactly-onc', 'RFC 0068 §C.1', 'commitment.fired MUST carry the source memoryRef (CTI-1 provenance)'),
|
|
65
66
|
).toBeTruthy();
|
|
66
67
|
|
|
67
68
|
// §C.3 — content-free: the inferred intention text MUST NOT appear on the event.
|
|
@@ -69,7 +70,7 @@ describe('commitment-fired: fire contract (RFC 0068 §C, capability-gated)', ()
|
|
|
69
70
|
const serialized = JSON.stringify(r.event ?? {});
|
|
70
71
|
expect(
|
|
71
72
|
serialized.includes(r.intentionCanary),
|
|
72
|
-
|
|
73
|
+
req('openwop.it.commitment-fired.a-fired-commitment-emits-a-content-free-event-with-memory-provenance-exactly-onc', 'RFC 0068 §C.3', 'the inferred intention text MUST NOT appear on the commitment.fired payload'),
|
|
73
74
|
).toBe(false);
|
|
74
75
|
}
|
|
75
76
|
|
|
@@ -77,7 +78,7 @@ describe('commitment-fired: fire contract (RFC 0068 §C, capability-gated)', ()
|
|
|
77
78
|
if (typeof r.fireCount === 'number') {
|
|
78
79
|
expect(
|
|
79
80
|
r.fireCount,
|
|
80
|
-
|
|
81
|
+
req('openwop.it.commitment-fired.a-fired-commitment-emits-a-content-free-event-with-memory-provenance-exactly-onc', 'RFC 0068 §C.2', 'a commitment MUST fire at most once per satisfied condition'),
|
|
81
82
|
).toBeLessThanOrEqual(1);
|
|
82
83
|
}
|
|
83
84
|
});
|
|
@@ -44,6 +44,7 @@ import { softSkip } from '../lib/soft-skip.js';
|
|
|
44
44
|
import { driver } from '../lib/driver.js';
|
|
45
45
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
46
46
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
47
|
+
import { req } from '../lib/requirement-ids.js';
|
|
47
48
|
|
|
48
49
|
const PROFILE = 'openwop-compensation';
|
|
49
50
|
|
|
@@ -76,7 +77,7 @@ describe('RFC 0151 §C — compensation lifecycle (capability-gated behavior)',
|
|
|
76
77
|
const caps = capabilityFamily<CompensationCaps>(disco.json, 'compensation');
|
|
77
78
|
expect(
|
|
78
79
|
caps?.orderingModels ?? [],
|
|
79
|
-
|
|
80
|
+
req('openwop.it.compensation-behavior.a-host-advertising-compensation-implements-reverse-completion',
|
|
80
81
|
'RFCS/0151-compensation-and-partial-failure-profile.md §A',
|
|
81
82
|
'an advertising host MUST implement `reverse-completion`; `dependency-graph` is optional',
|
|
82
83
|
),
|
|
@@ -93,23 +94,23 @@ describe('RFC 0151 §C — compensation lifecycle (capability-gated behavior)',
|
|
|
93
94
|
if (seam.status === 404) {
|
|
94
95
|
expect(
|
|
95
96
|
seam.status,
|
|
96
|
-
|
|
97
|
+
req('openwop.it.compensation-behavior.the-plan-is-persisted-before-the-first-inverse-action',
|
|
97
98
|
'RFCS/0151 §C',
|
|
98
99
|
'a host advertising `compensation` MUST expose the unwind sample seam so plan-before-effect ' +
|
|
99
100
|
'ordering can be witnessed; without it the requirement is unobservable and the profile ' +
|
|
100
101
|
'cannot be certified (RFC 0148 §A: unobservable resolves to `blocked`, not to a pass)',
|
|
101
102
|
),
|
|
102
103
|
).not.toBe(404);
|
|
103
|
-
return;
|
|
104
|
+
return softSkip('blocked', 'precondition not met — `seam.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
104
105
|
}
|
|
105
106
|
const events = (seam.json as { events?: { type: string }[] }).events ?? [];
|
|
106
107
|
const requestedAt = events.findIndex((e) => e.type === 'compensation.requested');
|
|
107
108
|
const startedAt = events.findIndex((e) => e.type === 'compensation.started');
|
|
108
|
-
expect(requestedAt,
|
|
109
|
-
expect(startedAt,
|
|
109
|
+
expect(requestedAt, req('openwop.it.compensation-behavior.the-plan-is-persisted-before-the-first-inverse-action', 'RFCS/0151 §D', '`compensation.requested` MUST be emitted')).toBeGreaterThanOrEqual(0);
|
|
110
|
+
expect(startedAt, req('openwop.it.compensation-behavior.the-plan-is-persisted-before-the-first-inverse-action', 'RFCS/0151 §D', '`compensation.started` MUST be emitted')).toBeGreaterThanOrEqual(0);
|
|
110
111
|
expect(
|
|
111
112
|
requestedAt,
|
|
112
|
-
|
|
113
|
+
req('openwop.it.compensation-behavior.the-plan-is-persisted-before-the-first-inverse-action',
|
|
113
114
|
'RFCS/0151 §C',
|
|
114
115
|
'the plan MUST be persisted BEFORE the first inverse action executes',
|
|
115
116
|
),
|
|
@@ -119,12 +120,12 @@ describe('RFC 0151 §C — compensation lifecycle (capability-gated behavior)',
|
|
|
119
120
|
it('inverse actions run in descending forward-completion order', async () => {
|
|
120
121
|
if (!behaviorGate(PROFILE, await advertised())) return;
|
|
121
122
|
const seam = await driver.post('/v1/host/sample/test/compensation/unwind', { nodes: 3 });
|
|
122
|
-
if (seam.status === 404) return; // covered by the seam assertion above
|
|
123
|
+
if (seam.status === 404) return softSkip('blocked', 'precondition not met — `seam.status === 404` returned early (covered by the seam assertion above) (seam, prior step, or fixture unavailable)'); // covered by the seam assertion above
|
|
123
124
|
const order = (seam.json as { compensatedOrder?: number[] }).compensatedOrder ?? [];
|
|
124
125
|
const descending = [...order].sort((a, b) => b - a);
|
|
125
126
|
expect(
|
|
126
127
|
order,
|
|
127
|
-
|
|
128
|
+
req('openwop.it.compensation-behavior.inverse-actions-run-in-descending-forward-completion-order',
|
|
128
129
|
'RFCS/0151 §C',
|
|
129
130
|
'`reverse-completion` orders compensations by DESCENDING durable forward-completion ' +
|
|
130
131
|
'sequence. Compensating forward can release a resource a later inverse action still needs.',
|
|
@@ -135,10 +136,10 @@ describe('RFC 0151 §C — compensation lifecycle (capability-gated behavior)',
|
|
|
135
136
|
it('replay does not re-fire inverse effects', async () => {
|
|
136
137
|
if (!behaviorGate(PROFILE, await advertised())) return;
|
|
137
138
|
const seam = await driver.post('/v1/host/sample/test/compensation/replay', {});
|
|
138
|
-
if (seam.status === 404) return;
|
|
139
|
+
if (seam.status === 404) return softSkip('blocked', 'precondition not met — `seam.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
139
140
|
expect(
|
|
140
141
|
(seam.json as { refiredEffects?: number }).refiredEffects ?? 0,
|
|
141
|
-
|
|
142
|
+
req('openwop.it.compensation-behavior.replay-does-not-re-fire-inverse-effects',
|
|
142
143
|
'RFCS/0151 §F',
|
|
143
144
|
'replay defaults MUST use recorded compensation outcomes and MUST NOT re-fire inverse ' +
|
|
144
145
|
'effects — a replay that re-executes them turns a recovery into a second outage',
|
|
@@ -149,22 +150,22 @@ describe('RFC 0151 §C — compensation lifecycle (capability-gated behavior)',
|
|
|
149
150
|
it('the run snapshot carries the §D rollup, and it agrees with the events', async () => {
|
|
150
151
|
if (!behaviorGate(PROFILE, await advertised())) return;
|
|
151
152
|
const seam = await driver.post('/v1/host/sample/test/compensation/unwind', {});
|
|
152
|
-
if (seam.status === 404) return; // covered by the seam assertion above
|
|
153
|
+
if (seam.status === 404) return softSkip('blocked', 'precondition not met — `seam.status === 404` returned early (covered by the seam assertion above) (seam, prior step, or fixture unavailable)'); // covered by the seam assertion above
|
|
153
154
|
const body = seam.json as { runId?: string; events?: { type: string }[] };
|
|
154
155
|
expect(
|
|
155
156
|
typeof body.runId === 'string' && body.runId.length > 0,
|
|
156
|
-
|
|
157
|
+
req('openwop.it.compensation-behavior.the-run-snapshot-carries-the-d-rollup-and-it-agrees-with-the-events',
|
|
157
158
|
'host-sample-test-seams.md §21',
|
|
158
159
|
'the unwind seam MUST return the `runId` it created — without it the §D rollup has no ' +
|
|
159
160
|
'black-box path to a witness and lands as another shape-only claim',
|
|
160
161
|
),
|
|
161
162
|
).toBe(true);
|
|
162
163
|
const snap = await driver.get(`/v1/runs/${encodeURIComponent(body.runId ?? '')}`);
|
|
163
|
-
expect(snap.status,
|
|
164
|
+
expect(snap.status, req('openwop.it.compensation-behavior.the-run-snapshot-carries-the-d-rollup-and-it-agrees-with-the-events', 'rest-endpoints.md', 'GET /v1/runs/{runId} for the seam run')).toBe(200);
|
|
164
165
|
const status = (snap.json as { compensationStatus?: unknown }).compensationStatus;
|
|
165
166
|
expect(
|
|
166
167
|
status,
|
|
167
|
-
|
|
168
|
+
req('openwop.it.compensation-behavior.the-run-snapshot-carries-the-d-rollup-and-it-agrees-with-the-events',
|
|
168
169
|
'compensation.md §"Run rollup: compensationStatus"',
|
|
169
170
|
'a host that advertises `compensation` MUST include `compensationStatus` on every snapshot ' +
|
|
170
171
|
'(`none` when idle) — presence is the wire witness of the advert',
|
|
@@ -187,17 +188,17 @@ describe('RFC 0151 §C — compensation lifecycle (capability-gated behavior)',
|
|
|
187
188
|
if (expected === null) {
|
|
188
189
|
expect(
|
|
189
190
|
['running', 'partial', 'failed'].includes(String(status)),
|
|
190
|
-
|
|
191
|
+
req('openwop.it.compensation-behavior.the-run-snapshot-carries-the-d-rollup-and-it-agrees-with-the-events',
|
|
191
192
|
'compensation.md §"Run rollup: compensationStatus"',
|
|
192
193
|
'an unwind that started and did not complete MUST report `running`, `partial`, or `failed` ' +
|
|
193
194
|
`— got ${String(status)}`,
|
|
194
195
|
),
|
|
195
196
|
).toBe(true);
|
|
196
|
-
return;
|
|
197
|
+
return softSkip('blocked', 'precondition not met — `expected === null` returned early (seam, prior step, or fixture unavailable)');
|
|
197
198
|
}
|
|
198
199
|
expect(
|
|
199
200
|
status,
|
|
200
|
-
|
|
201
|
+
req('openwop.it.compensation-behavior.the-run-snapshot-carries-the-d-rollup-and-it-agrees-with-the-events',
|
|
201
202
|
'compensation.md §"Run rollup: compensationStatus"',
|
|
202
203
|
`the rollup MUST be the deterministic fold of the compensation.* events — events ${JSON.stringify(types)} fold to \`${expected}\``,
|
|
203
204
|
),
|
|
@@ -207,7 +208,7 @@ describe('RFC 0151 §C — compensation lifecycle (capability-gated behavior)',
|
|
|
207
208
|
it('compensation events carry no provider bodies or credentials', async () => {
|
|
208
209
|
if (!behaviorGate(PROFILE, await advertised())) return;
|
|
209
210
|
const seam = await driver.post('/v1/host/sample/test/compensation/unwind', {});
|
|
210
|
-
if (seam.status === 404) return;
|
|
211
|
+
if (seam.status === 404) return softSkip('blocked', 'precondition not met — `seam.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
211
212
|
const events = (seam.json as { events?: Record<string, unknown>[] }).events ?? [];
|
|
212
213
|
for (const e of events) {
|
|
213
214
|
if (!COMPENSATION_EVENTS.includes(e['type'] as (typeof COMPENSATION_EVENTS)[number])) continue;
|
|
@@ -215,7 +216,7 @@ describe('RFC 0151 §C — compensation lifecycle (capability-gated behavior)',
|
|
|
215
216
|
for (const forbidden of ['-----BEGIN', 'Bearer ', 'sk-', 'authorization', 'providerResponse']) {
|
|
216
217
|
expect(
|
|
217
218
|
serialized.toLowerCase().includes(forbidden.toLowerCase()),
|
|
218
|
-
|
|
219
|
+
req('openwop.it.compensation-behavior.compensation-events-carry-no-provider-bodies-or-credentials',
|
|
219
220
|
'RFCS/0151 §D + §G',
|
|
220
221
|
'compensation event payloads carry opaque IDs and closed reason codes — never provider ' +
|
|
221
222
|
'bodies or credentials. These land in the DURABLE log, which is the least revocable ' +
|
|
@@ -257,7 +258,7 @@ describe('RFC 0151 §C — compensation lifecycle (capability-gated behavior)',
|
|
|
257
258
|
const body = seam.json as { runId?: string; events?: { type: string }[] };
|
|
258
259
|
expect(
|
|
259
260
|
typeof body.runId === 'string' && body.runId.length > 0,
|
|
260
|
-
|
|
261
|
+
req('openwop.it.compensation-behavior.a-healthy-run-that-declares-a-compensator-reports-compensationstatus-none-sp-11a', 'host-sample-test-seams.md §21', '`fail: false` MUST still return the runId it created'),
|
|
261
262
|
).toBe(true);
|
|
262
263
|
|
|
263
264
|
// Positive control: the seam really did run a healthy compensator-declaring
|
|
@@ -274,12 +275,12 @@ describe('RFC 0151 §C — compensation lifecycle (capability-gated behavior)',
|
|
|
274
275
|
}
|
|
275
276
|
|
|
276
277
|
const snap = await driver.get(`/v1/runs/${encodeURIComponent(body.runId ?? '')}`);
|
|
277
|
-
expect(snap.status,
|
|
278
|
+
expect(snap.status, req('openwop.it.compensation-behavior.a-healthy-run-that-declares-a-compensator-reports-compensationstatus-none-sp-11a', 'rest-endpoints.md', 'GET /v1/runs/{runId} for the healthy seam run')).toBe(200);
|
|
278
279
|
const snapshot = snap.json as { status?: unknown; compensationStatus?: unknown };
|
|
279
280
|
|
|
280
281
|
expect(
|
|
281
282
|
snapshot.compensationStatus,
|
|
282
|
-
|
|
283
|
+
req('openwop.it.compensation-behavior.a-healthy-run-that-declares-a-compensator-reports-compensationstatus-none-sp-11a',
|
|
283
284
|
'compensation.md §"Run rollup: compensationStatus"',
|
|
284
285
|
'a host advertising `compensation` MUST carry the field on EVERY snapshot — presence is the wire witness of the advert',
|
|
285
286
|
),
|
|
@@ -287,7 +288,7 @@ describe('RFC 0151 §C — compensation lifecycle (capability-gated behavior)',
|
|
|
287
288
|
|
|
288
289
|
expect(
|
|
289
290
|
snapshot.compensationStatus,
|
|
290
|
-
|
|
291
|
+
req('openwop.it.compensation-behavior.a-healthy-run-that-declares-a-compensator-reports-compensationstatus-none-sp-11a',
|
|
291
292
|
'compensation.md §"Run rollup: compensationStatus"',
|
|
292
293
|
'a run that completed while declaring compensable nodes has nothing to unwind: no `compensation.requested` ' +
|
|
293
294
|
'was recorded, so the fold is `none`. `pending` here means the rollup was derived from obligation ROWS ' +
|
|
@@ -55,6 +55,7 @@ import { behaviorGate } from '../lib/behavior-gate.js';
|
|
|
55
55
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
56
56
|
import { seamAbsent, softSkip } from '../lib/soft-skip.js';
|
|
57
57
|
import { readErrorCode, readRetriable } from '../lib/error-envelope.js';
|
|
58
|
+
import { req } from '../lib/requirement-ids.js';
|
|
58
59
|
|
|
59
60
|
const PROFILE = 'openwop-compensation';
|
|
60
61
|
const UNWIND = '/v1/host/sample/test/compensation/unwind';
|
|
@@ -112,29 +113,29 @@ describe('RFC 0151 §C — inverse-action identity is retry-stable (capability-g
|
|
|
112
113
|
it('a transient inverse failure retries under the SAME identity and the same downstream key', async () => {
|
|
113
114
|
if (!behaviorGate(PROFILE, await advertised())) return;
|
|
114
115
|
const r = await post(UNWIND, { nodes: 2, failFirstInverseAttempts: 2 });
|
|
115
|
-
if (r === null) return;
|
|
116
|
-
expect(r.status,
|
|
116
|
+
if (r === null) return softSkip('blocked', 'precondition not met — `r === null` returned early (seam, prior step, or fixture unavailable)');
|
|
117
|
+
expect(r.status, req('openwop.it.compensation-recovery.a-transient-inverse-failure-retries-under-the-same-identity-and-the-same-downstr', 'spec/v1/host-sample-test-seams.md §21', 'unwind seam answers 200')).toBe(200);
|
|
117
118
|
const body = r.json as UnwindResponse;
|
|
118
119
|
if (!Array.isArray(body.inverseActions)) return extensionAbsent('`unwind` did not return `inverseActions[]`');
|
|
119
120
|
// Under reverse-completion the first inverse to run is the highest forward ordinal (2).
|
|
120
121
|
const first = body.inverseActions.find((a) => a.ordinal === 2) ?? body.inverseActions[0];
|
|
121
|
-
expect(first,
|
|
122
|
+
expect(first, req('openwop.it.compensation-recovery.a-transient-inverse-failure-retries-under-the-same-identity-and-the-same-downstr', 'spec/v1/host-sample-test-seams.md §21', 'one entry per plan entry')).toBeDefined();
|
|
122
123
|
const a = first as InverseAction;
|
|
123
|
-
expect(a.attempts,
|
|
124
|
-
expect(a.outcome,
|
|
125
|
-
expect(Array.isArray(a.downstreamKeys) ? a.downstreamKeys.length : -1,
|
|
124
|
+
expect(a.attempts, req('openwop.it.compensation-recovery.a-transient-inverse-failure-retries-under-the-same-identity-and-the-same-downstr', 'RFCS/0151 §C', 'two transient failures then success is THREE attempts of one obligation')).toBe(3);
|
|
125
|
+
expect(a.outcome, req('openwop.it.compensation-recovery.a-transient-inverse-failure-retries-under-the-same-identity-and-the-same-downstr', 'RFCS/0151 §C', 'a transient failure MUST be retryable — the action completes')).toBe('completed');
|
|
126
|
+
expect(Array.isArray(a.downstreamKeys) ? a.downstreamKeys.length : -1, req('openwop.it.compensation-recovery.a-transient-inverse-failure-retries-under-the-same-identity-and-the-same-downstr', 'spec/v1/host-sample-test-seams.md §21', '`downstreamKeys` has one entry per attempt')).toBe(3);
|
|
126
127
|
const distinct = new Set(a.downstreamKeys ?? []);
|
|
127
128
|
expect(
|
|
128
129
|
distinct.size,
|
|
129
|
-
|
|
130
|
+
req('openwop.it.compensation-recovery.a-transient-inverse-failure-retries-under-the-same-identity-and-the-same-downstr',
|
|
130
131
|
'RFCS/0151 §C + RFC 0150 §B',
|
|
131
132
|
'a retry re-presents the SAME inverse-action identity as its idempotency key — a second key at the downstream is a second obligation (two refunds). `attempt` is outside the identity for exactly this reason.',
|
|
132
133
|
),
|
|
133
134
|
).toBe(1);
|
|
134
135
|
// One obligation per ordinal: no duplicate plan entries.
|
|
135
136
|
const ordinals = body.inverseActions.map((x) => x.ordinal);
|
|
136
|
-
expect(new Set(ordinals).size,
|
|
137
|
-
expect(typeof a.effectId === 'string' && a.effectId.length > 0,
|
|
137
|
+
expect(new Set(ordinals).size, req('openwop.it.compensation-recovery.a-transient-inverse-failure-retries-under-the-same-identity-and-the-same-downstr', 'RFCS/0151 §C', 'exactly one plan entry per forward ordinal')).toBe(ordinals.length);
|
|
138
|
+
expect(typeof a.effectId === 'string' && a.effectId.length > 0, req('openwop.it.compensation-recovery.a-transient-inverse-failure-retries-under-the-same-identity-and-the-same-downstr', 'RFCS/0151 §D', 'the identity is carried as an opaque `effectId`')).toBe(true);
|
|
138
139
|
});
|
|
139
140
|
});
|
|
140
141
|
|
|
@@ -142,7 +143,7 @@ describe('RFC 0151 §E — operator authority is bound to the plan\'s tenant (ca
|
|
|
142
143
|
it('cross-tenant is neutralized (404), same-tenant non-operator is refused and audited (403), the operator acts (200)', async () => {
|
|
143
144
|
if (!behaviorGate(PROFILE, await advertised())) return;
|
|
144
145
|
const held = await post(UNWIND, { nodes: 2, hold: true });
|
|
145
|
-
if (held === null) return;
|
|
146
|
+
if (held === null) return softSkip('blocked', 'precondition not met — `held === null` returned early (seam, prior step, or fixture unavailable)');
|
|
146
147
|
const body = held.json as UnwindResponse;
|
|
147
148
|
const runId = body.runId;
|
|
148
149
|
if (typeof runId !== 'string') return extensionAbsent('`unwind` did not return a `runId`');
|
|
@@ -151,8 +152,8 @@ describe('RFC 0151 §E — operator authority is bound to the plan\'s tenant (ca
|
|
|
151
152
|
if (heldEntry === undefined && !manual) return extensionAbsent('`unwind` with `hold: true` produced neither a held plan entry nor `compensation.manual_intervention_required`');
|
|
152
153
|
// Snapshot must read `manual` while held (§D fold).
|
|
153
154
|
const snap = await driver.get(`/v1/runs/${encodeURIComponent(runId)}`);
|
|
154
|
-
expect(snap.status,
|
|
155
|
-
expect((snap.json as { compensationStatus?: string }).compensationStatus,
|
|
155
|
+
expect(snap.status, req('openwop.it.compensation-recovery.cross-tenant-is-neutralized-404-same-tenant-non-operator-is-refused-and-audited', 'rest-endpoints.md', 'GET /v1/runs/{runId} for the held run')).toBe(200);
|
|
156
|
+
expect((snap.json as { compensationStatus?: string }).compensationStatus, req('openwop.it.compensation-recovery.cross-tenant-is-neutralized-404-same-tenant-non-operator-is-refused-and-audited', 'spec/v1/compensation.md §D', 'a held plan reads `manual`')).toBe('manual');
|
|
156
157
|
|
|
157
158
|
const op = async (actor: Record<string, unknown>) =>
|
|
158
159
|
driver.post(OPERATOR, { runId, action: 'retry', actor });
|
|
@@ -161,28 +162,28 @@ describe('RFC 0151 §E — operator authority is bound to the plan\'s tenant (ca
|
|
|
161
162
|
if (cross.status === 404 && readErrorCode(cross.json) === undefined && (cross.json === null || cross.json === undefined || Object.keys(cross.json as object).length === 0)) {
|
|
162
163
|
return extensionAbsent(`${OPERATOR} answered a bodiless 404 — the operator seam is absent (an absent seam and a neutralized cross-tenant plan must be told apart by the envelope: RFC 0132 §A.2 answers \`not_found\` in the canonical envelope)`);
|
|
163
164
|
}
|
|
164
|
-
expect(cross.status,
|
|
165
|
-
expect(readErrorCode(cross.json),
|
|
165
|
+
expect(cross.status, req('openwop.it.compensation-recovery.cross-tenant-is-neutralized-404-same-tenant-non-operator-is-refused-and-audited', 'RFCS/0151 §E + RFC 0132 §A.2', 'an actor from another tenant MUST NOT learn the plan exists — 404, not 403')).toBe(404);
|
|
166
|
+
expect(readErrorCode(cross.json), req('openwop.it.compensation-recovery.cross-tenant-is-neutralized-404-same-tenant-non-operator-is-refused-and-audited', 'rest-endpoints.md §Error codes', '`not_found` — do not leak existence')).toBe('not_found');
|
|
166
167
|
// 2. Same tenant, no operator authority: refused AND audited.
|
|
167
168
|
const plain = await op({ tenantId: 'default', principalId: 'conformance-bystander', operator: false });
|
|
168
|
-
expect(plain.status,
|
|
169
|
-
expect(readErrorCode(plain.json),
|
|
170
|
-
expect(readRetriable(plain.json),
|
|
169
|
+
expect(plain.status, req('openwop.it.compensation-recovery.cross-tenant-is-neutralized-404-same-tenant-non-operator-is-refused-and-audited', 'RFCS/0151 §E', 'a principal without operator authority in the plan\'s tenant is refused')).toBe(403);
|
|
170
|
+
expect(readErrorCode(plain.json), req('openwop.it.compensation-recovery.cross-tenant-is-neutralized-404-same-tenant-non-operator-is-refused-and-audited', 'rest-endpoints.md §Error codes', '`forbidden`')).toBe('forbidden');
|
|
171
|
+
expect(readRetriable(plain.json), req('openwop.it.compensation-recovery.cross-tenant-is-neutralized-404-same-tenant-non-operator-is-refused-and-audited', 'spec/v1/host-sample-test-seams.md §21', 'a refused override is not retriable')).toBe(false);
|
|
171
172
|
// 3. The operator: acts, audited, rollup moves off `manual`.
|
|
172
173
|
const ok = await op({ tenantId: 'default', principalId: 'conformance-operator', operator: true });
|
|
173
|
-
expect(ok.status,
|
|
174
|
+
expect(ok.status, req('openwop.it.compensation-recovery.cross-tenant-is-neutralized-404-same-tenant-non-operator-is-refused-and-audited', 'RFCS/0151 §E', 'an authorized operator MAY retry a held inverse action')).toBe(200);
|
|
174
175
|
const res = ok.json as { compensationStatus?: string; audited?: boolean; planVersion?: unknown };
|
|
175
|
-
expect(res.audited,
|
|
176
|
+
expect(res.audited, req('openwop.it.compensation-recovery.cross-tenant-is-neutralized-404-same-tenant-non-operator-is-refused-and-audited', 'RFCS/0151 §E', 'every override MUST be audited (`authorization.decided`)')).toBe(true);
|
|
176
177
|
expect(
|
|
177
178
|
['completed', 'partial', 'failed', 'running'],
|
|
178
|
-
|
|
179
|
+
req('openwop.it.compensation-recovery.cross-tenant-is-neutralized-404-same-tenant-non-operator-is-refused-and-audited', 'spec/v1/compensation.md §D', 'after the operator retries, the rollup is whatever the recorded outcomes yield — never still `manual` for a resolved hold'),
|
|
179
180
|
).toContain(res.compensationStatus);
|
|
180
181
|
// The refusal in step 2 was audited too: the plan's events carry a reason.
|
|
181
182
|
const after = await driver.get(`/v1/runs/${encodeURIComponent(runId)}/events`);
|
|
182
183
|
if (after.status === 200) {
|
|
183
184
|
const evs = (after.json as { events?: Array<{ type?: string; payload?: Record<string, unknown> }> }).events ?? [];
|
|
184
185
|
const decided = evs.filter((e) => e.type === 'authorization.decided');
|
|
185
|
-
expect(decided.length,
|
|
186
|
+
expect(decided.length, req('openwop.it.compensation-recovery.cross-tenant-is-neutralized-404-same-tenant-non-operator-is-refused-and-audited', 'RFCS/0151 §E + RFC 0049', 'both the refusal and the override are `authorization.decided` records')).toBeGreaterThanOrEqual(2);
|
|
186
187
|
}
|
|
187
188
|
});
|
|
188
189
|
});
|
|
@@ -191,23 +192,23 @@ describe('RFC 0151 §B/§F — inverse inputs are recorded facts, not re-derived
|
|
|
191
192
|
it('a replay executes the same inverse actions with the same inputs, and re-fires nothing', async () => {
|
|
192
193
|
if (!behaviorGate(PROFILE, await advertised())) return;
|
|
193
194
|
const r = await post(REPLAY, {});
|
|
194
|
-
if (r === null) return;
|
|
195
|
-
expect(r.status,
|
|
195
|
+
if (r === null) return softSkip('blocked', 'precondition not met — `r === null` returned early (seam, prior step, or fixture unavailable)');
|
|
196
|
+
expect(r.status, req('openwop.it.compensation-recovery.a-replay-executes-the-same-inverse-actions-with-the-same-inputs-and-re-fires-not', 'spec/v1/host-sample-test-seams.md §21', 'replay seam answers 200')).toBe(200);
|
|
196
197
|
const body = r.json as ReplayResponse;
|
|
197
198
|
if (!Array.isArray(body.source) || !Array.isArray(body.replayed)) return extensionAbsent('`replay` did not return `source[]` / `replayed[]`');
|
|
198
|
-
expect(body.refiredEffects,
|
|
199
|
-
expect(body.source.length,
|
|
199
|
+
expect(body.refiredEffects, req('openwop.it.compensation-recovery.a-replay-executes-the-same-inverse-actions-with-the-same-inputs-and-re-fires-not', 'RFCS/0151 §F', 'replay MUST NOT re-fire inverse effects')).toBe(0);
|
|
200
|
+
expect(body.source.length, req('openwop.it.compensation-recovery.a-replay-executes-the-same-inverse-actions-with-the-same-inputs-and-re-fires-not', 'spec/v1/host-sample-test-seams.md §21', 'the source run had inverse actions to compare')).toBeGreaterThan(0);
|
|
200
201
|
const norm = (xs: readonly InverseAction[]) =>
|
|
201
202
|
[...xs].sort((a, b) => (a.ordinal ?? 0) - (b.ordinal ?? 0)).map((x) => ({ ordinal: x.ordinal, effectId: x.effectId, input: x.input }));
|
|
202
203
|
expect(
|
|
203
204
|
norm(body.replayed),
|
|
204
|
-
|
|
205
|
+
req('openwop.it.compensation-recovery.a-replay-executes-the-same-inverse-actions-with-the-same-inputs-and-re-fires-not',
|
|
205
206
|
'RFCS/0151 §B + §F',
|
|
206
207
|
'a replay uses the RECORDED inverse inputs and identities — an inverse built from a re-derived (prompt / model / live) value is not the inverse of what was actually done',
|
|
207
208
|
),
|
|
208
209
|
).toEqual(norm(body.source));
|
|
209
210
|
for (const x of body.source) {
|
|
210
|
-
expect(x.input,
|
|
211
|
+
expect(x.input, req('openwop.it.compensation-recovery.a-replay-executes-the-same-inverse-actions-with-the-same-inputs-and-re-fires-not', 'RFCS/0151 §B', 'each inverse action executed with a recorded input')).toBeDefined();
|
|
211
212
|
}
|
|
212
213
|
});
|
|
213
214
|
});
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
import { describe, it, expect } from 'vitest';
|
|
19
19
|
import { driver } from '../lib/driver.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
|
// Re-use `conformance-identity` if it advertises a configurableSchema;
|
|
23
25
|
// otherwise the scenario soft-skips. Hosts adopting v1.1 SHOULD seed a
|
|
@@ -44,13 +46,13 @@ describe('configurable-schema: per-workflow schema enforced', () => {
|
|
|
44
46
|
console.warn(
|
|
45
47
|
'[configurable-schema] no advertised fixture declares configurableSchema; skipping',
|
|
46
48
|
);
|
|
47
|
-
return;
|
|
49
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!fixture` returned early ([configurable-schema] no advertised fixture declares configurableSchema; skipping)');
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
const manifest = await driver.get(`/v1/workflows/${encodeURIComponent(fixture)}`);
|
|
51
53
|
const schema = (manifest.json as { configurableSchema?: Record<string, unknown> })
|
|
52
54
|
.configurableSchema;
|
|
53
|
-
expect(schema,
|
|
55
|
+
expect(schema, req('openwop.it.configurable-schema.manifest-surfaces-configurableschema-mismatched-configurable-is-rejected',
|
|
54
56
|
'run-options.md §"Per-workflow configurableSchema"',
|
|
55
57
|
'GET /v1/workflows/{workflowId} MUST surface configurableSchema when the workflow declares one',
|
|
56
58
|
)).toBeDefined();
|
|
@@ -64,7 +66,7 @@ describe('configurable-schema: per-workflow schema enforced', () => {
|
|
|
64
66
|
});
|
|
65
67
|
expect(
|
|
66
68
|
[400, 422].includes(create.status),
|
|
67
|
-
|
|
69
|
+
req('openwop.it.configurable-schema.manifest-surfaces-configurableschema-mismatched-configurable-is-rejected',
|
|
68
70
|
'run-options.md §"Per-workflow configurableSchema"',
|
|
69
71
|
'configurable that violates the workflow schema MUST be rejected with 400/422',
|
|
70
72
|
),
|
|
@@ -76,12 +78,12 @@ describe('configurable-schema: per-workflow schema enforced', () => {
|
|
|
76
78
|
|
|
77
79
|
it('configurable overlay matching configurableSchema is accepted', async () => {
|
|
78
80
|
const fixture = await pickFixture();
|
|
79
|
-
if (!fixture) return; // covered by skip warning above
|
|
81
|
+
if (!fixture) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!fixture` returned early (covered by skip warning above)'); // covered by skip warning above
|
|
80
82
|
|
|
81
83
|
const manifest = await driver.get(`/v1/workflows/${encodeURIComponent(fixture)}`);
|
|
82
84
|
const schema = (manifest.json as { configurableSchema?: Record<string, unknown> })
|
|
83
85
|
.configurableSchema;
|
|
84
|
-
if (!schema) return;
|
|
86
|
+
if (!schema) return softSkip('blocked', 'precondition not met — `!schema` returned early (seam, prior step, or fixture unavailable)');
|
|
85
87
|
|
|
86
88
|
// Build a minimal valid overlay derived from the schema's first
|
|
87
89
|
// `properties.*` entry. This stays generic across fixtures: we pick
|
|
@@ -106,7 +108,7 @@ describe('configurable-schema: per-workflow schema enforced', () => {
|
|
|
106
108
|
workflowId: fixture,
|
|
107
109
|
configurable: overlay,
|
|
108
110
|
});
|
|
109
|
-
expect(create.status,
|
|
111
|
+
expect(create.status, req('openwop.it.configurable-schema.configurable-overlay-matching-configurableschema-is-accepted',
|
|
110
112
|
'run-options.md §"Per-workflow configurableSchema"',
|
|
111
113
|
'configurable matching the declared schema MUST be accepted (201)',
|
|
112
114
|
)).toBe(201);
|
|
@@ -27,6 +27,7 @@ import { behaviorGate, behaviorGatePresent } from '../lib/behavior-gate.js';
|
|
|
27
27
|
import { seamAbsent, softSkipDisposition, resetSoftSkips } from '../lib/soft-skip.js';
|
|
28
28
|
import { dispositionOf, resetLedger, suspendSinkForFixtures } from '../lib/requirement-ledger.js';
|
|
29
29
|
import { __resetEnvCacheForTests } from '../lib/env.js';
|
|
30
|
+
import { req } from '../lib/requirement-ids.js';
|
|
30
31
|
|
|
31
32
|
export const HOST_CALLBACK_NOT_REQUIRED = 'server-free: toggles OPENWOP_REQUIRE_BEHAVIOR in-process and pins the advertised-missing-seam rule';
|
|
32
33
|
|
|
@@ -71,7 +72,7 @@ describe('RFC 0148 §B — conformance-advertised-seam-required', () => {
|
|
|
71
72
|
delete process.env['OPENWOP_REQUIRE_BEHAVIOR'];
|
|
72
73
|
__resetEnvCacheForTests();
|
|
73
74
|
delete process.env['OPENWOP_OPTED_OUT_PROFILES'];
|
|
74
|
-
expect(behaviorGate(PROFILE, false)).toBe(false);
|
|
75
|
+
expect(behaviorGate(PROFILE, false), req('openwop.it.conformance-advertised-seam-required.default-mode-an-unadvertised-profile-records-inapplicable-and-gates-false-never', 'RFC 0148 §B', 'default mode: an unadvertised profile records inapplicable and gates false — never a pass')).toBe(false);
|
|
75
76
|
expect(dispositionOf(`openwop.profile.${PROFILE}`)).toBe('inapplicable');
|
|
76
77
|
});
|
|
77
78
|
|
|
@@ -79,14 +80,14 @@ describe('RFC 0148 §B — conformance-advertised-seam-required', () => {
|
|
|
79
80
|
process.env['OPENWOP_REQUIRE_BEHAVIOR'] = 'true';
|
|
80
81
|
__resetEnvCacheForTests();
|
|
81
82
|
delete process.env['OPENWOP_OPTED_OUT_PROFILES'];
|
|
82
|
-
expect(() => behaviorGate(PROFILE, false)).toThrow(/OPENWOP_REQUIRE_BEHAVIOR=true/);
|
|
83
|
+
expect(() => behaviorGate(PROFILE, false), req('openwop.it.conformance-advertised-seam-required.strict-mode-an-unadvertised-un-opted-out-profile-fails-advertise-or-opt-out-expl', 'RFC 0148 §B', 'strict mode: an unadvertised, un-opted-out profile FAILS (advertise or opt out explicitly)')).toThrow(/OPENWOP_REQUIRE_BEHAVIOR=true/);
|
|
83
84
|
});
|
|
84
85
|
|
|
85
86
|
it('strict mode: an advertised capability whose seam helper returned nothing FAILS (behaviorGatePresent)', () => {
|
|
86
87
|
process.env['OPENWOP_REQUIRE_BEHAVIOR'] = 'true';
|
|
87
88
|
__resetEnvCacheForTests();
|
|
88
89
|
delete process.env['OPENWOP_OPTED_OUT_PROFILES'];
|
|
89
|
-
expect(() => behaviorGatePresent(PROFILE, null)).toThrow();
|
|
90
|
+
expect(() => behaviorGatePresent(PROFILE, null), req('openwop.it.conformance-advertised-seam-required.strict-mode-an-advertised-capability-whose-seam-helper-returned-nothing-fails-be', 'RFC 0148 §B', 'strict mode: an advertised capability whose seam helper returned nothing FAILS (behaviorGatePresent)')).toThrow();
|
|
90
91
|
// and with a value present it narrows and passes through
|
|
91
92
|
delete process.env['OPENWOP_REQUIRE_BEHAVIOR'];
|
|
92
93
|
__resetEnvCacheForTests();
|
|
@@ -96,7 +97,7 @@ describe('RFC 0148 §B — conformance-advertised-seam-required', () => {
|
|
|
96
97
|
it('strict mode: seamAbsent (advertised, seam answered 404/403 mid-scenario) FAILS; default mode notes blocked with the reason', () => {
|
|
97
98
|
process.env['OPENWOP_REQUIRE_BEHAVIOR'] = 'true';
|
|
98
99
|
__resetEnvCacheForTests();
|
|
99
|
-
expect(() => seamAbsent('host advertises X but /v1/host/sample/x answered 404')).toThrow(/RFC 0148 §B/);
|
|
100
|
+
expect(() => seamAbsent('host advertises X but /v1/host/sample/x answered 404'), req('openwop.it.conformance-advertised-seam-required.strict-mode-seamabsent-advertised-seam-answered-404-403-mid-scenario-fails-defau', 'RFC 0148 §B', 'strict mode: seamAbsent (advertised, seam answered 404/403 mid-scenario) FAILS; default mode notes blocked with the reason')).toThrow(/RFC 0148 §B/);
|
|
100
101
|
delete process.env['OPENWOP_REQUIRE_BEHAVIOR'];
|
|
101
102
|
__resetEnvCacheForTests();
|
|
102
103
|
resetSoftSkips();
|
|
@@ -111,7 +112,7 @@ describe('RFC 0148 §B — conformance-advertised-seam-required', () => {
|
|
|
111
112
|
__resetEnvCacheForTests();
|
|
112
113
|
process.env['OPENWOP_OPTED_OUT_PROFILES'] = PROFILE;
|
|
113
114
|
__resetEnvCacheForTests();
|
|
114
|
-
expect(behaviorGate(PROFILE, false)).toBe(false);
|
|
115
|
+
expect(behaviorGate(PROFILE, false), req('openwop.it.conformance-advertised-seam-required.the-sanctioned-escape-is-an-explicit-opt-out-which-records-skipped-with-the-reas', 'RFC 0148 §B', 'the sanctioned escape is an explicit opt-out, which records skipped with the reason — still never a pass')).toBe(false);
|
|
115
116
|
expect(dispositionOf(`openwop.profile.${PROFILE}`)).toBe('skipped');
|
|
116
117
|
});
|
|
117
118
|
});
|
|
@@ -27,6 +27,7 @@ import { UNCLASSIFIED_RETURN_DETAIL } from '../lib/soft-skip.js';
|
|
|
27
27
|
import { PROFILE_FLOOR_SCENARIOS } from '../lib/profiles.js';
|
|
28
28
|
import { requirementIdForScenario, requirementsFor } from '../lib/requirement-registry.js';
|
|
29
29
|
import { verifyBundleV2, type BundleV2Requirement } from '../lib/certification-bundle-verify.js';
|
|
30
|
+
import { req } from '../lib/requirement-ids.js';
|
|
30
31
|
|
|
31
32
|
export const HOST_CALLBACK_NOT_REQUIRED = 'server-free: pins the runner/emitter/verifier rule that an early return is never a pass';
|
|
32
33
|
|
|
@@ -34,19 +35,19 @@ const HEX = 'c'.repeat(64);
|
|
|
34
35
|
|
|
35
36
|
describe('RFC 0148 §A — conformance-execution-witness: the runner record', () => {
|
|
36
37
|
it('a witnessed pass is executed-pass; a failed test is executed-fail regardless of count', () => {
|
|
37
|
-
expect(resolveFileRecord(['pass', 'pass'], undefined, 7, null).disposition).toBe('executed-pass');
|
|
38
|
+
expect(resolveFileRecord(['pass', 'pass'], undefined, 7, null).disposition, req('openwop.it.conformance-execution-witness.a-witnessed-pass-is-executed-pass-a-failed-test-is-executed-fail-regardless-of-c', 'RFC 0148 §A', 'a witnessed pass is executed-pass; a failed test is executed-fail regardless of count')).toBe('executed-pass');
|
|
38
39
|
expect(resolveFileRecord(['pass', 'fail'], undefined, 7, null).disposition).toBe('executed-fail');
|
|
39
40
|
expect(resolveFileRecord(['fail'], undefined, 0, null).disposition).toBe('executed-fail');
|
|
40
41
|
});
|
|
41
42
|
|
|
42
43
|
it('a zero-assertion pass with NO reason is BLOCKED with the marker — never a pass', () => {
|
|
43
44
|
const r = resolveFileRecord(['pass', 'pass'], undefined, 0, null);
|
|
44
|
-
expect(r.disposition).toBe('blocked');
|
|
45
|
+
expect(r.disposition, req('openwop.it.conformance-execution-witness.a-zero-assertion-pass-with-no-reason-is-blocked-with-the-marker-never-a-pass', 'RFC 0148 §A', 'a zero-assertion pass with NO reason is BLOCKED with the marker — never a pass')).toBe('blocked');
|
|
45
46
|
expect(r.detail).toBe(UNCLASSIFIED_RETURN_DETAIL);
|
|
46
47
|
});
|
|
47
48
|
|
|
48
49
|
it('a zero-assertion pass takes the noted reason (softSkip / seamAbsent) — inapplicable, skipped, or blocked with the text', () => {
|
|
49
|
-
expect(resolveFileRecord(['pass'], undefined, 0, { kind: 'inapplicable', reason: 'host does not advertise X' })).toEqual({ disposition: 'inapplicable', detail: 'host does not advertise X' });
|
|
50
|
+
expect(resolveFileRecord(['pass'], undefined, 0, { kind: 'inapplicable', reason: 'host does not advertise X' }), req('openwop.it.conformance-execution-witness.a-zero-assertion-pass-takes-the-noted-reason-softskip-seamabsent-inapplicable-sk', 'RFC 0148 §A', 'a zero-assertion pass takes the noted reason (softSkip / seamAbsent) — inapplicable, skipped, or blocked with the text')).toEqual({ disposition: 'inapplicable', detail: 'host does not advertise X' });
|
|
50
51
|
expect(resolveFileRecord(['pass'], undefined, 0, { kind: 'blocked', reason: 'seam answered 404' })).toEqual({ disposition: 'blocked', detail: 'seam answered 404' });
|
|
51
52
|
expect(resolveFileRecord(['pass'], undefined, 0, { kind: 'skipped', reason: 'operator opt-out' })).toEqual({ disposition: 'skipped', detail: 'operator opt-out' });
|
|
52
53
|
});
|
|
@@ -59,7 +60,7 @@ describe('RFC 0148 §A — conformance-execution-witness: the runner record', ()
|
|
|
59
60
|
// from a positive control or a setup precondition. Measured on this exact
|
|
60
61
|
// function by a tier-1 host writing a scenario whose first leg is a control.
|
|
61
62
|
const one = resolveFileRecord(['pass'], undefined, 1, { kind: 'blocked', reason: 'fork returned 501' });
|
|
62
|
-
expect(one.disposition).toBe('executed-pass');
|
|
63
|
+
expect(one.disposition, req('openwop.it.conformance-execution-witness.gap-g8-a-file-that-asserted-then-soft-skipped-keeps-the-note-as-a-partial-witnes', 'RFC 0148 §A', 'gap G8 — a file that asserted then soft-skipped keeps the note as a partial-witness detail, and its disposition does NOT move')).toBe('executed-pass');
|
|
63
64
|
expect(one.detail).toBe('partial-witness: blocked: fork returned 501');
|
|
64
65
|
|
|
65
66
|
const many = resolveFileRecord(['pass', 'pass'], undefined, 4, { kind: 'inapplicable', reason: 'fork unsupported' });
|
|
@@ -83,7 +84,7 @@ describe('RFC 0148 §A — conformance-execution-witness: the runner record', ()
|
|
|
83
84
|
it('every test ctx.skip()ped takes the noted reason when one was written before the skip, else stays the blocked marker', () => {
|
|
84
85
|
// `ctx.skip()` throws — a `softSkip(...)` AFTER it is dead code. Seven files
|
|
85
86
|
// carried exactly that dead note and reported as unclassified for a suite minor.
|
|
86
|
-
expect(resolveFileRecord(['skip', 'skip'], undefined, 0, { kind: 'inapplicable', reason: 'sandbox not advertised' })).toEqual({ disposition: 'inapplicable', detail: 'sandbox not advertised' });
|
|
87
|
+
expect(resolveFileRecord(['skip', 'skip'], undefined, 0, { kind: 'inapplicable', reason: 'sandbox not advertised' }), req('openwop.it.conformance-execution-witness.every-test-ctx-skip-ped-takes-the-noted-reason-when-one-was-written-before-the-s', 'RFC 0148 §A', 'every test ctx.skip()ped takes the noted reason when one was written before the skip, else stays the blocked marker')).toEqual({ disposition: 'inapplicable', detail: 'sandbox not advertised' });
|
|
87
88
|
expect(resolveFileRecord(['skip'], undefined, 0, { kind: 'blocked', reason: 'simulator seam 404' })).toEqual({ disposition: 'blocked', detail: 'simulator seam 404' });
|
|
88
89
|
const bare = resolveFileRecord(['skip', 'skip'], undefined, 0, null);
|
|
89
90
|
expect(bare.disposition).toBe('blocked');
|
|
@@ -93,7 +94,7 @@ describe('RFC 0148 §A — conformance-execution-witness: the runner record', ()
|
|
|
93
94
|
});
|
|
94
95
|
|
|
95
96
|
it('a behaviorGate reason resolves a zero-assertion pass to inapplicable/skipped; a note outranks nothing but never a witnessed pass', () => {
|
|
96
|
-
expect(resolveFileRecord(['pass'], 'inapplicable', 0, null).disposition).toBe('inapplicable');
|
|
97
|
+
expect(resolveFileRecord(['pass'], 'inapplicable', 0, null).disposition, req('openwop.it.conformance-execution-witness.a-behaviorgate-reason-resolves-a-zero-assertion-pass-to-inapplicable-skipped-a-n', 'RFC 0148 §A', 'a behaviorGate reason resolves a zero-assertion pass to inapplicable/skipped; a note outranks nothing but never a witnessed pass')).toBe('inapplicable');
|
|
97
98
|
expect(resolveFileRecord(['pass'], 'skipped', 0, null).disposition).toBe('skipped');
|
|
98
99
|
// real assertions ran: the note is irrelevant, the file passed
|
|
99
100
|
expect(resolveFileRecord(['pass'], undefined, 3, { kind: 'blocked', reason: 'irrelevant' }).disposition).toBe('executed-pass');
|
|
@@ -115,7 +116,7 @@ describe('RFC 0148 §A — conformance-execution-witness: emitter and verifier r
|
|
|
115
116
|
rep.set('interrupt-alpha.test.ts', 'passed');
|
|
116
117
|
const d = deriveRequirementDispositions(rep, ledger, [profile], {});
|
|
117
118
|
const v = d.verdicts.find((x) => x.profile === profile)!;
|
|
118
|
-
expect(v.unclassified).toEqual([requirementIdForScenario(files[0]!)]);
|
|
119
|
+
expect(v.unclassified, req('openwop.it.conformance-execution-witness.the-emitter-derivation-marks-a-marker-blocked-floor-row-unclassified-and-rejects', 'RFC 0148 §A', 'the emitter derivation marks a marker-blocked floor row unclassified and rejects the claim')).toEqual([requirementIdForScenario(files[0]!)]);
|
|
119
120
|
expect(d.rejectUnclassified).toBe(true);
|
|
120
121
|
// and the same file with a witnessed pass certifies
|
|
121
122
|
ledger[0] = { requirementId: requirementIdForScenario(files[0]!), disposition: 'executed-pass' as const, assertionCount: 3 };
|
|
@@ -134,7 +135,7 @@ describe('RFC 0148 §A — conformance-execution-witness: emitter and verifier r
|
|
|
134
135
|
results: { totals: { executedPass: rs.filter((r) => r.disposition === 'executed-pass').length, executedFail: 0, skipped: 0, inapplicable: 0, blocked: rs.filter((r) => r.disposition === 'blocked').length }, requirements: rs },
|
|
135
136
|
scenarioManifestSha256: HEX, targetConfigurationSha256: HEX,
|
|
136
137
|
});
|
|
137
|
-
expect(verifyBundleV2(bundle(rows)).certified).toBe(true);
|
|
138
|
+
expect(verifyBundleV2(bundle(rows)).certified, req('openwop.it.conformance-execution-witness.the-consumer-verifier-rejects-a-bundle-whose-required-row-is-a-vacuous-pass-or-t', 'RFC 0148 §A', 'the consumer verifier rejects a bundle whose required row is a vacuous pass or the runner marker, and accepts the witnessed twin')).toBe(true);
|
|
138
139
|
const vacuous = rows.map((r, i) => (i === 0 ? { ...r, assertionCount: 0 } : r));
|
|
139
140
|
expect(verifyBundleV2(bundle(vacuous)).evidenceValid).toBe(false);
|
|
140
141
|
const marker = rows.map((r, i) => (i === 0 ? { requirementId: r.requirementId, scenarioId: r.scenarioId, disposition: 'blocked', detail: UNCLASSIFIED_RETURN_DETAIL, assertionCount: 0 } : r));
|