@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
|
@@ -49,6 +49,7 @@ import { expandChain, expandChainWithCompensation, type WorkflowChain, type Work
|
|
|
49
49
|
import { capabilityFamily, discoveryFamilies } from '../lib/discovery-capabilities.js';
|
|
50
50
|
import { softSkip } from '../lib/soft-skip.js';
|
|
51
51
|
import { readErrorCode } from '../lib/error-envelope.js';
|
|
52
|
+
import { req } from '../lib/requirement-ids.js';
|
|
52
53
|
|
|
53
54
|
const PROFILE = 'workflowChainPacks.hostExpansionSeam';
|
|
54
55
|
const EXPAND_PATH = '/v1/host/sample/workflow-chain:expand';
|
|
@@ -106,7 +107,7 @@ describe('workflow-chain-host-expansion: live host wraps expansion algorithm cor
|
|
|
106
107
|
const caps = (discoveryFamilies(disco.json) as { workflowChainPacks?: ChainCaps }).workflowChainPacks;
|
|
107
108
|
expect(
|
|
108
109
|
caps,
|
|
109
|
-
|
|
110
|
+
req('openwop.it.workflow-chain-host-expansion.host-discovery-advertises-workflowchainpacks-hostexpansionseam-when-the-expand-s',
|
|
110
111
|
'capabilities.md §workflowChainPacks',
|
|
111
112
|
'a host serving the RFC 0013 host-expansion test seam MUST set `hostExpansionSeam: true` (and, being a chain-pack consumer, `supported: true`) in the discovery block',
|
|
112
113
|
),
|
|
@@ -133,7 +134,7 @@ describe('workflow-chain-host-expansion: live host wraps expansion algorithm cor
|
|
|
133
134
|
const probe = await driver.post(EXPAND_PATH, { packName: SAMPLE_PACK, chainId: CHAIN_1_NODE, parameters: {} });
|
|
134
135
|
expect(
|
|
135
136
|
probe.status,
|
|
136
|
-
|
|
137
|
+
req('openwop.it.workflow-chain-host-expansion.host-discovery-advertises-workflowchainpacks-hostexpansionseam-when-the-expand-s',
|
|
137
138
|
'capabilities.md §workflowChainPacks',
|
|
138
139
|
`a host advertising \`hostExpansionSeam: true\` MUST serve ${EXPAND_PATH}. Got ${probe.status} — ` +
|
|
139
140
|
'the discovery document promises a seam the host does not route. Advertising a capability ' +
|
|
@@ -167,7 +168,7 @@ describe('workflow-chain-host-expansion: live host wraps expansion algorithm cor
|
|
|
167
168
|
const [hm, hn] = body.packVersion.split('.').map(Number);
|
|
168
169
|
const [fm, fn] = PACK.version.split('.').map(Number);
|
|
169
170
|
const older = hm! < fm! || (hm === fm && hn! < fn!);
|
|
170
|
-
expect(older,
|
|
171
|
+
expect(older, req('openwop.it.workflow-chain-host-expansion.positive-1-node-chain-expansion-matches-the-reference-library-for-the-bundled-pa', 'workflow-chain-host-expansion', `host bundles workflow-chain-sample ${body.packVersion}; this suite's fixture is ${PACK.version} — a host pack NEWER than the suite is unaccounted for`)).toBe(true);
|
|
171
172
|
softSkip('blocked', `host bundles workflow-chain-sample ${body.packVersion} (suite fixture ${PACK.version}) — the RFC 0157 chains are absent there until its conformance pin catches up`);
|
|
172
173
|
}
|
|
173
174
|
expect(typeof body.expansionId).toBe('string');
|
|
@@ -188,16 +189,16 @@ describe('workflow-chain-host-expansion: live host wraps expansion algorithm cor
|
|
|
188
189
|
const ref = expected.nodes[0]!;
|
|
189
190
|
expect(
|
|
190
191
|
node.id,
|
|
191
|
-
|
|
192
|
+
req('openwop.it.workflow-chain-host-expansion.positive-1-node-chain-expansion-matches-the-reference-library-for-the-bundled-pa', 'workflow-chain-packs.md §Expansion semantics', 'host rewrites the node id exactly as the reference library (chainId dots → underscores + expansionId prefix)'),
|
|
192
193
|
).toBe(ref.id);
|
|
193
194
|
expect(node.typeId).toBe(ref.typeId);
|
|
194
195
|
expect(
|
|
195
196
|
node.config?.systemPrompt,
|
|
196
|
-
|
|
197
|
+
req('openwop.it.workflow-chain-host-expansion.positive-1-node-chain-expansion-matches-the-reference-library-for-the-bundled-pa', 'workflow-chain-packs.md §Expansion semantics', 'host performs the same literal {{params.*}} substitution as the reference library'),
|
|
197
198
|
).toBe((ref.config as { systemPrompt?: string } | undefined)?.systemPrompt);
|
|
198
199
|
expect(
|
|
199
200
|
node.capabilities,
|
|
200
|
-
|
|
201
|
+
req('openwop.it.workflow-chain-host-expansion.positive-1-node-chain-expansion-matches-the-reference-library-for-the-bundled-pa', 'workflow-chain-packs.md §Capability propagation', 'chain capabilities propagate to the expanded node'),
|
|
201
202
|
).toEqual(ref.capabilities);
|
|
202
203
|
});
|
|
203
204
|
|
|
@@ -222,14 +223,14 @@ describe('workflow-chain-host-expansion: live host wraps expansion algorithm cor
|
|
|
222
223
|
const refEdge = expected.edges[0]!;
|
|
223
224
|
expect(
|
|
224
225
|
{ from: edge.from, to: edge.to },
|
|
225
|
-
|
|
226
|
+
req('openwop.it.workflow-chain-host-expansion.positive-2-node-chain-matches-the-reference-library-edge-rewrite-capability-prop', 'workflow-chain-packs.md §Expansion semantics', 'host rewrites fragment-internal edge endpoints (port suffix preserved) exactly as the reference library'),
|
|
226
227
|
).toEqual({ from: refEdge.from, to: refEdge.to });
|
|
227
228
|
|
|
228
229
|
// side-effectful capability propagated to BOTH expanded nodes (per the ref).
|
|
229
230
|
const refCaps = expected.nodes.map((n) => n.capabilities);
|
|
230
231
|
expect(
|
|
231
232
|
body.nodes.map((n) => n.capabilities),
|
|
232
|
-
|
|
233
|
+
req('openwop.it.workflow-chain-host-expansion.positive-2-node-chain-matches-the-reference-library-edge-rewrite-capability-prop', 'workflow-chain-packs.md §Capability propagation', 'chain capability propagates uniformly to every expanded node'),
|
|
233
234
|
).toEqual(refCaps);
|
|
234
235
|
});
|
|
235
236
|
|
|
@@ -241,7 +242,7 @@ describe('workflow-chain-host-expansion: live host wraps expansion algorithm cor
|
|
|
241
242
|
chainId: 'whatever',
|
|
242
243
|
parameters: {},
|
|
243
244
|
});
|
|
244
|
-
expect(res.status).toBe(404);
|
|
245
|
+
expect(res.status, req('openwop.it.workflow-chain-host-expansion.negative-unknown-pack-returns-404-pack-not-found', 'RFC 0013', 'negative — unknown pack returns 404 pack_not_found')).toBe(404);
|
|
245
246
|
expect((res.json as { error: string }).error).toBe('pack_not_found');
|
|
246
247
|
});
|
|
247
248
|
|
|
@@ -253,7 +254,7 @@ describe('workflow-chain-host-expansion: live host wraps expansion algorithm cor
|
|
|
253
254
|
chainId: 'vendor.openwop.workflow-chain-sample.does-not-exist',
|
|
254
255
|
parameters: {},
|
|
255
256
|
});
|
|
256
|
-
expect(res.status).toBe(404);
|
|
257
|
+
expect(res.status, req('openwop.it.workflow-chain-host-expansion.negative-known-pack-but-unknown-chainid-returns-404-chain-not-found', 'RFC 0013', 'negative — known pack but unknown chainId returns 404 chain_not_found')).toBe(404);
|
|
257
258
|
expect((res.json as { error: string }).error).toBe('chain_not_found');
|
|
258
259
|
});
|
|
259
260
|
|
|
@@ -262,7 +263,7 @@ describe('workflow-chain-host-expansion: live host wraps expansion algorithm cor
|
|
|
262
263
|
|
|
263
264
|
// Missing chainId.
|
|
264
265
|
const res = await driver.post(EXPAND_PATH, { packName: SAMPLE_PACK, parameters: {} });
|
|
265
|
-
expect(res.status).toBe(422);
|
|
266
|
+
expect(res.status, req('openwop.it.workflow-chain-host-expansion.negative-malformed-request-body-returns-422-invalid-request', 'RFC 0013', 'negative — malformed request body returns 422 invalid_request')).toBe(422);
|
|
266
267
|
expect((res.json as { error: string }).error).toBe('invalid_request');
|
|
267
268
|
});
|
|
268
269
|
it('RFC 0157 — a compensating chain expands through the live path with `compensation` and `irreversibleEffect` carried verbatim onto the expanded nodes', async () => {
|
|
@@ -275,7 +276,7 @@ describe('workflow-chain-host-expansion: live host wraps expansion algorithm cor
|
|
|
275
276
|
// the chain does not exist there yet — unobservable, not a failure.
|
|
276
277
|
return softSkip('blocked', `host's bundled workflow-chain-sample pack predates 1.1.0 (chain ${CHAIN_COMP} answered chain_not_found) — the RFC 0157 carry is unobservable until the host's conformance pin reaches 1.133.0`);
|
|
277
278
|
}
|
|
278
|
-
expect(res.status,
|
|
279
|
+
expect(res.status, req('openwop.it.workflow-chain-host-expansion.rfc-0157-a-compensating-chain-expands-through-the-live-path-with-compensation-an', 'workflow-chain-packs.md §"Compensation (RFC 0157)"', 'a chain whose nodes declare an inverse action / irreversibleEffect (no policy) is acceptable on ANY host — the declaration describes, only the policy requests an unwind')).toBe(200);
|
|
279
280
|
const body = res.json as unknown as {
|
|
280
281
|
expansionId: string;
|
|
281
282
|
nodes: Array<{ id: string; compensation?: unknown; irreversibleEffect?: boolean }>;
|
|
@@ -288,15 +289,15 @@ describe('workflow-chain-host-expansion: live host wraps expansion algorithm cor
|
|
|
288
289
|
expect(body.nodes).toHaveLength(expected.nodes.length);
|
|
289
290
|
for (const ref of expected.nodes) {
|
|
290
291
|
const node = body.nodes.find((n) => n.id === ref.id);
|
|
291
|
-
expect(node,
|
|
292
|
+
expect(node, req('openwop.it.workflow-chain-host-expansion.rfc-0157-a-compensating-chain-expands-through-the-live-path-with-compensation-an', 'workflow-chain-packs.md §Expansion semantics', `expanded node ${ref.id} present`)).toBeDefined();
|
|
292
293
|
const refC = (ref as unknown as { compensation?: unknown }).compensation;
|
|
293
294
|
expect(
|
|
294
295
|
node?.compensation,
|
|
295
|
-
|
|
296
|
+
req('openwop.it.workflow-chain-host-expansion.rfc-0157-a-compensating-chain-expands-through-the-live-path-with-compensation-an', 'workflow-chain-packs.md §"Compensation (RFC 0157)" rules 5b/6b', `node ${ref.id}: the inverse-action declaration MUST survive expansion verbatim (params substituted, node-id refs re-prefixed) — a node-rebuilding allowlist that drops it silently loses the unwind`),
|
|
296
297
|
).toEqual(refC);
|
|
297
298
|
expect(
|
|
298
299
|
node?.irreversibleEffect,
|
|
299
|
-
|
|
300
|
+
req('openwop.it.workflow-chain-host-expansion.rfc-0157-a-compensating-chain-expands-through-the-live-path-with-compensation-an', 'workflow-chain-packs.md §"Compensation (RFC 0157)" rule 6c', `node ${ref.id}: irreversibleEffect MUST be copied unchanged`),
|
|
300
301
|
).toEqual((ref as unknown as { irreversibleEffect?: boolean }).irreversibleEffect);
|
|
301
302
|
}
|
|
302
303
|
// Non-vacuity: the fixture DOES declare both, so at least one node of each kind was compared.
|
|
@@ -318,11 +319,11 @@ describe('workflow-chain-host-expansion: live host wraps expansion algorithm cor
|
|
|
318
319
|
// MUST refuse a chain carrying a policy rather than accept a promise it will
|
|
319
320
|
// never honour. Accepting silently is the advertise-and-opt-out failure with
|
|
320
321
|
// the sign flipped.
|
|
321
|
-
expect(res.status >= 400,
|
|
322
|
-
expect(readErrorCode(res.json),
|
|
323
|
-
return;
|
|
322
|
+
expect(res.status >= 400, req('openwop.it.workflow-chain-host-expansion.rfc-0157-a-chain-carrying-an-unwind-policy-is-refused-with-capability-required-o', 'compensation.md §"Workflow policy"', 'a non-advertising host MUST refuse a chain carrying `compensation` (policy) — 4xx, not 200')).toBe(true);
|
|
323
|
+
expect(readErrorCode(res.json), req('openwop.it.workflow-chain-host-expansion.rfc-0157-a-chain-carrying-an-unwind-policy-is-refused-with-capability-required-o', 'capabilities.md §"Unsupported capability — refusal contract"', '`capability_required`')).toBe('capability_required');
|
|
324
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!advertises` returned early');
|
|
324
325
|
}
|
|
325
|
-
expect(res.status,
|
|
326
|
+
expect(res.status, req('openwop.it.workflow-chain-host-expansion.rfc-0157-a-chain-carrying-an-unwind-policy-is-refused-with-capability-required-o', 'workflow-chain-packs.md §"Compensation (RFC 0157)"', 'an advertising host expands the policy-carrying chain')).toBe(200);
|
|
326
327
|
const body = res.json as unknown as { expansionId: string; nodes: unknown[]; settings?: { compensation?: unknown } };
|
|
327
328
|
const expected = expandChainWithCompensation(chainById(CHAIN_COMP_POLICY) as unknown as WorkflowChainWithCompensation, {
|
|
328
329
|
expansionId: body.expansionId,
|
|
@@ -335,7 +336,7 @@ describe('workflow-chain-host-expansion: live host wraps expansion algorithm cor
|
|
|
335
336
|
// `settings` is host-optional, so the policy carry is asserted when visible
|
|
336
337
|
// and noted when not — never assumed.
|
|
337
338
|
if (body.settings?.compensation !== undefined) {
|
|
338
|
-
expect(body.settings.compensation,
|
|
339
|
+
expect(body.settings.compensation, req('openwop.it.workflow-chain-host-expansion.rfc-0157-a-chain-carrying-an-unwind-policy-is-refused-with-capability-required-o', 'workflow-chain-packs.md §"Compensation (RFC 0157)" rule 9b', 'the chain policy becomes settings.compensation, copied verbatim')).toEqual(expected.settingsCompensation);
|
|
339
340
|
} else {
|
|
340
341
|
softSkip('blocked', 'the expand seam does not echo `settings`, so the policy → settings.compensation carry (rule 9b) is unobservable through it; the node carry above was witnessed');
|
|
341
342
|
}
|
|
@@ -34,6 +34,8 @@ import Ajv2020 from 'ajv/dist/2020.js';
|
|
|
34
34
|
import addFormats from 'ajv-formats';
|
|
35
35
|
import type { ErrorObject } from 'ajv';
|
|
36
36
|
import { SCHEMAS_DIR, V1_DIR } from '../lib/paths.js';
|
|
37
|
+
import { req } from '../lib/requirement-ids.js';
|
|
38
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
37
39
|
|
|
38
40
|
const SCHEMA_PATH = join(SCHEMAS_DIR, 'workflow-chain-pack-manifest.schema.json');
|
|
39
41
|
// In-repo example pack — proves the schema validates a non-trivial
|
|
@@ -100,7 +102,7 @@ describe('category: workflow-chain-pack manifest validation', () => {
|
|
|
100
102
|
.join('\n');
|
|
101
103
|
expect(
|
|
102
104
|
ok,
|
|
103
|
-
`Positive sample MUST validate against workflow-chain-pack-manifest.schema.json — got:\n${errs}
|
|
105
|
+
req('openwop.it.workflow-chain-pack-manifest-validation.positive-a-valid-workflow-chain-pack-manifest-validates-cleanly', 'workflow-chain-packs.md', `Positive sample MUST validate against workflow-chain-pack-manifest.schema.json — got:\n${errs}`),
|
|
104
106
|
).toBe(true);
|
|
105
107
|
});
|
|
106
108
|
|
|
@@ -134,7 +136,7 @@ describe('category: workflow-chain-pack manifest validation', () => {
|
|
|
134
136
|
};
|
|
135
137
|
const ok = validate(conditionalChain);
|
|
136
138
|
const errs = (validate.errors ?? []).map((e: ErrorObject) => `${e.instancePath || '/'}: ${e.message}`).join('\n');
|
|
137
|
-
expect(ok, `Object-shaped edge condition MUST validate — got:\n${errs}`).toBe(true);
|
|
139
|
+
expect(ok, req('openwop.it.workflow-chain-pack-manifest-validation.positive-a-fragmentedge-condition-takes-the-top-level-edgecondition-shape-rfc-00', 'RFC 0013', `Object-shaped edge condition MUST validate — got:\n${errs}`)).toBe(true);
|
|
138
140
|
});
|
|
139
141
|
|
|
140
142
|
it('negative: a bare-string FragmentEdge condition is rejected (the pre-2026-07-03 shape)', () => {
|
|
@@ -160,7 +162,7 @@ describe('category: workflow-chain-pack manifest validation', () => {
|
|
|
160
162
|
},
|
|
161
163
|
],
|
|
162
164
|
};
|
|
163
|
-
expect(validate(stringCondition), 'A string edge condition MUST NOT validate under the corrected schema').toBe(false);
|
|
165
|
+
expect(validate(stringCondition), req('openwop.it.workflow-chain-pack-manifest-validation.negative-a-bare-string-fragmentedge-condition-is-rejected-the-pre-2026-07-03-sha', 'workflow-chain-packs.md', 'A string edge condition MUST NOT validate under the corrected schema')).toBe(false);
|
|
164
166
|
});
|
|
165
167
|
|
|
166
168
|
it('negative: manifest mixing chains[] AND nodes[] is rejected (pack_kind_invalid)', () => {
|
|
@@ -192,14 +194,14 @@ describe('category: workflow-chain-pack manifest validation', () => {
|
|
|
192
194
|
const ok = validate(manifest);
|
|
193
195
|
expect(
|
|
194
196
|
ok,
|
|
195
|
-
'Manifest with both nodes[] and chains[] MUST fail workflow-chain schema validation (pack_kind_invalid at the registry surface).',
|
|
197
|
+
req('openwop.it.workflow-chain-pack-manifest-validation.negative-manifest-mixing-chains-and-nodes-is-rejected-pack-kind-invalid', 'workflow-chain-packs.md', 'Manifest with both nodes[] and chains[] MUST fail workflow-chain schema validation (pack_kind_invalid at the registry surface).'),
|
|
196
198
|
).toBe(false);
|
|
197
199
|
const hasAdditionalPropertiesErr = (validate.errors ?? []).some(
|
|
198
200
|
(e: ErrorObject) => e.keyword === 'additionalProperties',
|
|
199
201
|
);
|
|
200
202
|
expect(
|
|
201
203
|
hasAdditionalPropertiesErr,
|
|
202
|
-
'Expected an `additionalProperties` violation flagging the unexpected `nodes` field.',
|
|
204
|
+
req('openwop.it.workflow-chain-pack-manifest-validation.negative-manifest-mixing-chains-and-nodes-is-rejected-pack-kind-invalid', 'workflow-chain-packs.md', 'Expected an `additionalProperties` violation flagging the unexpected `nodes` field.'),
|
|
203
205
|
).toBe(true);
|
|
204
206
|
});
|
|
205
207
|
|
|
@@ -228,7 +230,7 @@ describe('category: workflow-chain-pack manifest validation', () => {
|
|
|
228
230
|
const ok = validate(manifest);
|
|
229
231
|
expect(
|
|
230
232
|
ok,
|
|
231
|
-
'Manifest with invalid chainId MUST fail workflow-chain schema validation.',
|
|
233
|
+
req('openwop.it.workflow-chain-pack-manifest-validation.negative-chain-entry-with-invalid-chainid-is-rejected-pattern-violation', 'workflow-chain-packs.md', 'Manifest with invalid chainId MUST fail workflow-chain schema validation.'),
|
|
232
234
|
).toBe(false);
|
|
233
235
|
const hasPatternErr = (validate.errors ?? []).some(
|
|
234
236
|
(e: ErrorObject) =>
|
|
@@ -236,14 +238,14 @@ describe('category: workflow-chain-pack manifest validation', () => {
|
|
|
236
238
|
);
|
|
237
239
|
expect(
|
|
238
240
|
hasPatternErr,
|
|
239
|
-
'Expected a `pattern` violation on the chains[].chainId field.',
|
|
241
|
+
req('openwop.it.workflow-chain-pack-manifest-validation.negative-chain-entry-with-invalid-chainid-is-rejected-pattern-violation', 'workflow-chain-packs.md', 'Expected a `pattern` violation on the chains[].chainId field.'),
|
|
240
242
|
).toBe(true);
|
|
241
243
|
});
|
|
242
244
|
|
|
243
245
|
it('positive: the in-repo example pack at examples/packs/workflow-chain-sample/ validates against the schema', () => {
|
|
244
246
|
if (!EXAMPLE_PACK_PATH || !existsSync(EXAMPLE_PACK_PATH)) {
|
|
245
247
|
// Published-tarball layout doesn't ship examples/; skip cleanly.
|
|
246
|
-
return;
|
|
248
|
+
return softSkip('blocked', 'precondition not met — `!EXAMPLE_PACK_PATH || !existsSync(EXAMPLE_PACK_PATH)` returned early (Published-tarball layout doesn\'t ship examples/; skip cleanly.) (seam, prior step, or fixture unavailable)');
|
|
247
249
|
}
|
|
248
250
|
const manifest = JSON.parse(readFileSync(EXAMPLE_PACK_PATH, 'utf8'));
|
|
249
251
|
const ok = validate(manifest);
|
|
@@ -252,15 +254,15 @@ describe('category: workflow-chain-pack manifest validation', () => {
|
|
|
252
254
|
.join('\n');
|
|
253
255
|
expect(
|
|
254
256
|
ok,
|
|
255
|
-
`examples/packs/workflow-chain-sample/pack.json MUST validate against workflow-chain-pack-manifest.schema.json (closes RFC 0013 Phase 4 in-tree path). Errors:\n${errs}
|
|
257
|
+
req('openwop.it.workflow-chain-pack-manifest-validation.positive-the-in-repo-example-pack-at-examples-packs-workflow-chain-sample-valida', 'workflow-chain-packs.md', `examples/packs/workflow-chain-sample/pack.json MUST validate against workflow-chain-pack-manifest.schema.json (closes RFC 0013 Phase 4 in-tree path). Errors:\n${errs}`),
|
|
256
258
|
).toBe(true);
|
|
257
259
|
// Spot-check the structural claims the example README makes:
|
|
258
|
-
expect(manifest.kind, 'example pack MUST declare kind: "workflow-chain"').toBe(
|
|
260
|
+
expect(manifest.kind, req('openwop.it.workflow-chain-pack-manifest-validation.positive-the-in-repo-example-pack-at-examples-packs-workflow-chain-sample-valida', 'workflow-chain-packs.md', 'example pack MUST declare kind: "workflow-chain"')).toBe(
|
|
259
261
|
'workflow-chain',
|
|
260
262
|
);
|
|
261
263
|
expect(
|
|
262
264
|
Array.isArray(manifest.chains) && manifest.chains.length === 2,
|
|
263
|
-
'example pack MUST ship exactly 2 chains (1-node + 2-node shapes) per its README contract',
|
|
265
|
+
req('openwop.it.workflow-chain-pack-manifest-validation.positive-the-in-repo-example-pack-at-examples-packs-workflow-chain-sample-valida', 'workflow-chain-packs.md', 'example pack MUST ship exactly 2 chains (1-node + 2-node shapes) per its README contract'),
|
|
264
266
|
).toBe(true);
|
|
265
267
|
});
|
|
266
268
|
|
|
@@ -285,7 +287,7 @@ describe('category: workflow-chain-pack manifest validation', () => {
|
|
|
285
287
|
const ok = validate(manifest);
|
|
286
288
|
expect(
|
|
287
289
|
ok,
|
|
288
|
-
'Manifest without kind: "workflow-chain" MUST fail this schema (the other path is node-pack-manifest.schema.json).',
|
|
290
|
+
req('openwop.it.workflow-chain-pack-manifest-validation.negative-omitting-kind-field-rejects-kind-is-required', 'workflow-chain-packs.md', 'Manifest without kind: "workflow-chain" MUST fail this schema (the other path is node-pack-manifest.schema.json).'),
|
|
289
291
|
).toBe(false);
|
|
290
292
|
});
|
|
291
293
|
});
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
import { describe, it, expect } from 'vitest';
|
|
32
32
|
import { generateKeyPairSync, sign, verify } from 'node:crypto';
|
|
33
|
+
import { req } from '../lib/requirement-ids.js';
|
|
33
34
|
|
|
34
35
|
/** Canonical workflow-chain pack manifest used as the signing target.
|
|
35
36
|
* Mirrors the spec doc's Positive example, kept tight so the test
|
|
@@ -76,7 +77,7 @@ describe('category: workflow-chain pack signature — Ed25519 verification reuse
|
|
|
76
77
|
const ok = verify(null, manifest, publicKey, signature);
|
|
77
78
|
expect(
|
|
78
79
|
ok,
|
|
79
|
-
'Per workflow-chain-packs.md §"Expansion semantics" step 2: the signature recipe is IDENTICAL to node-packs (Ed25519 over pack.json bytes). A valid pair MUST verify with the canonical recipe.',
|
|
80
|
+
req('openwop.it.workflow-chain-pack-signature-verification.valid-manifest-valid-signature-must-verify-positive-path', 'workflow-chain-packs.md', 'Per workflow-chain-packs.md §"Expansion semantics" step 2: the signature recipe is IDENTICAL to node-packs (Ed25519 over pack.json bytes). A valid pair MUST verify with the canonical recipe.'),
|
|
80
81
|
).toBe(true);
|
|
81
82
|
});
|
|
82
83
|
|
|
@@ -92,7 +93,7 @@ describe('category: workflow-chain pack signature — Ed25519 verification reuse
|
|
|
92
93
|
const ok = verify(null, tampered, publicKey, signature);
|
|
93
94
|
expect(
|
|
94
95
|
ok,
|
|
95
|
-
'Per node-packs.md §Signing (reused unchanged for chain packs): Ed25519 verification MUST detect any byte-level tamper to the signed payload.',
|
|
96
|
+
req('openwop.it.workflow-chain-pack-signature-verification.tampered-manifest-must-fail-verification-sha-level-tamper-detection', 'workflow-chain-packs.md', 'Per node-packs.md §Signing (reused unchanged for chain packs): Ed25519 verification MUST detect any byte-level tamper to the signed payload.'),
|
|
96
97
|
).toBe(false);
|
|
97
98
|
});
|
|
98
99
|
|
|
@@ -108,7 +109,7 @@ describe('category: workflow-chain pack signature — Ed25519 verification reuse
|
|
|
108
109
|
const ok = verify(null, manifest, attackerPubKey, signature);
|
|
109
110
|
expect(
|
|
110
111
|
ok,
|
|
111
|
-
'Ed25519 verification MUST fail when the public key doesn\'t match the private key that produced the signature — the spec\'s trust model depends on this property.',
|
|
112
|
+
req('openwop.it.workflow-chain-pack-signature-verification.wrong-key-signature-must-fail-verification', 'workflow-chain-packs.md', 'Ed25519 verification MUST fail when the public key doesn\'t match the private key that produced the signature — the spec\'s trust model depends on this property.'),
|
|
112
113
|
).toBe(false);
|
|
113
114
|
});
|
|
114
115
|
|
|
@@ -130,7 +131,7 @@ describe('category: workflow-chain pack signature — Ed25519 verification reuse
|
|
|
130
131
|
};
|
|
131
132
|
expect(
|
|
132
133
|
signedManifest.signing.method,
|
|
133
|
-
'workflow-chain packs MUST accept the same signing block keys as node packs (publicKeyRef + signatureRef + method) per spec design.',
|
|
134
|
+
req('openwop.it.workflow-chain-pack-signature-verification.chain-pack-manifests-carry-the-same-signing-block-shape-as-node-packs', 'workflow-chain-packs.md', 'workflow-chain packs MUST accept the same signing block keys as node packs (publicKeyRef + signatureRef + method) per spec design.'),
|
|
134
135
|
).toBe('manual');
|
|
135
136
|
expect(signedManifest.signing.publicKeyRef).toBe('keys/2026-05.pem');
|
|
136
137
|
expect(signedManifest.signing.signatureRef).toBe('pack.json.sig');
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
ChainUnresolvableTypeIdError,
|
|
37
37
|
type WorkflowChain,
|
|
38
38
|
} from '../lib/workflow-chain-expansion.js';
|
|
39
|
+
import { req } from '../lib/requirement-ids.js';
|
|
39
40
|
|
|
40
41
|
const CHAIN_WITH_UNKNOWN_TYPEID: WorkflowChain = {
|
|
41
42
|
chainId: 'vendor.acme.someChain',
|
|
@@ -66,7 +67,7 @@ describe('category: workflow-chain expansion — unresolvable typeId rejection',
|
|
|
66
67
|
params: {},
|
|
67
68
|
isTypeIdResolvable: isKnown,
|
|
68
69
|
}),
|
|
69
|
-
'Per spec §"Expansion semantics" step 3: hosts MUST reject expansion with `chain_unresolvable_typeid` when any fragment node\'s typeId can\'t be resolved by the destination host.',
|
|
70
|
+
req('openwop.it.workflow-chain-unresolvable-typeid.throws-chainunresolvabletypeiderror-when-chain-references-an-unknown-typeid', 'workflow-chain-packs.md', 'Per spec §"Expansion semantics" step 3: hosts MUST reject expansion with `chain_unresolvable_typeid` when any fragment node\'s typeId can\'t be resolved by the destination host.'),
|
|
70
71
|
).toThrow(ChainUnresolvableTypeIdError);
|
|
71
72
|
});
|
|
72
73
|
|
|
@@ -79,21 +80,21 @@ describe('category: workflow-chain expansion — unresolvable typeId rejection',
|
|
|
79
80
|
});
|
|
80
81
|
expect.fail('expandChain MUST have thrown ChainUnresolvableTypeIdError');
|
|
81
82
|
} catch (err) {
|
|
82
|
-
expect(err, 'expected ChainUnresolvableTypeIdError').toBeInstanceOf(
|
|
83
|
+
expect(err, req('openwop.it.workflow-chain-unresolvable-typeid.error-carries-the-offending-typeid-and-the-chainid-in-details', 'workflow-chain-packs.md', 'expected ChainUnresolvableTypeIdError')).toBeInstanceOf(
|
|
83
84
|
ChainUnresolvableTypeIdError,
|
|
84
85
|
);
|
|
85
86
|
const e = err as ChainUnresolvableTypeIdError;
|
|
86
87
|
expect(
|
|
87
88
|
e.code,
|
|
88
|
-
'Per spec §"Error codes": the wire-level error code MUST be `chain_unresolvable_typeid`.',
|
|
89
|
+
req('openwop.it.workflow-chain-unresolvable-typeid.error-carries-the-offending-typeid-and-the-chainid-in-details', 'workflow-chain-packs.md', 'Per spec §"Error codes": the wire-level error code MUST be `chain_unresolvable_typeid`.'),
|
|
89
90
|
).toBe('chain_unresolvable_typeid');
|
|
90
91
|
expect(
|
|
91
92
|
e.typeId,
|
|
92
|
-
'The error MUST surface the offending typeId so the host editor can render an actionable diagnostic.',
|
|
93
|
+
req('openwop.it.workflow-chain-unresolvable-typeid.error-carries-the-offending-typeid-and-the-chainid-in-details', 'workflow-chain-packs.md', 'The error MUST surface the offending typeId so the host editor can render an actionable diagnostic.'),
|
|
93
94
|
).toBe('made.up.foo');
|
|
94
95
|
expect(
|
|
95
96
|
e.chainId,
|
|
96
|
-
'The error MUST surface the chainId so the host editor knows which chain-pack tile triggered the rejection.',
|
|
97
|
+
req('openwop.it.workflow-chain-unresolvable-typeid.error-carries-the-offending-typeid-and-the-chainid-in-details', 'workflow-chain-packs.md', 'The error MUST surface the chainId so the host editor knows which chain-pack tile triggered the rejection.'),
|
|
97
98
|
).toBe('vendor.acme.someChain');
|
|
98
99
|
}
|
|
99
100
|
});
|
|
@@ -115,7 +116,7 @@ describe('category: workflow-chain expansion — unresolvable typeId rejection',
|
|
|
115
116
|
}
|
|
116
117
|
expect(
|
|
117
118
|
threw,
|
|
118
|
-
'Expansion MUST throw before producing any output — host editors rely on the "no partial expansion" guarantee to keep parent workflows clean on rejection.',
|
|
119
|
+
req('openwop.it.workflow-chain-unresolvable-typeid.rejection-happens-before-any-id-rewriting-or-placeholder-substitution', 'workflow-chain-packs.md', 'Expansion MUST throw before producing any output — host editors rely on the "no partial expansion" guarantee to keep parent workflows clean on rejection.'),
|
|
119
120
|
).toBe(true);
|
|
120
121
|
});
|
|
121
122
|
|
|
@@ -134,7 +135,7 @@ describe('category: workflow-chain expansion — unresolvable typeId rejection',
|
|
|
134
135
|
params: {},
|
|
135
136
|
isTypeIdResolvable: isKnown,
|
|
136
137
|
}),
|
|
137
|
-
'Sanity: when every typeId resolves, expansion MUST proceed without throwing.',
|
|
138
|
+
req('openwop.it.workflow-chain-unresolvable-typeid.accepts-the-chain-when-every-typeid-is-resolvable', 'workflow-chain-packs.md', 'Sanity: when every typeId resolves, expansion MUST proceed without throwing.'),
|
|
138
139
|
).not.toThrow();
|
|
139
140
|
});
|
|
140
141
|
|
|
@@ -163,7 +164,7 @@ describe('category: workflow-chain expansion — unresolvable typeId rejection',
|
|
|
163
164
|
const e = err as ChainUnresolvableTypeIdError;
|
|
164
165
|
expect(
|
|
165
166
|
e.typeId,
|
|
166
|
-
'When multiple nodes have unknown typeIds, the throw MUST identify the first unknown one encountered in declaration order.',
|
|
167
|
+
req('openwop.it.workflow-chain-unresolvable-typeid.throws-on-the-first-unknown-typeid-encountered-fail-fast', 'workflow-chain-packs.md', 'When multiple nodes have unknown typeIds, the throw MUST identify the first unknown one encountered in declaration order.'),
|
|
167
168
|
).toBe('made.up.foo');
|
|
168
169
|
}
|
|
169
170
|
});
|
|
@@ -23,6 +23,7 @@ import addFormats from 'ajv-formats';
|
|
|
23
23
|
import { readFileSync, readdirSync } from 'node:fs';
|
|
24
24
|
import { join } from 'node:path';
|
|
25
25
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
26
|
+
import { req } from '../lib/requirement-ids.js';
|
|
26
27
|
|
|
27
28
|
function compileWorkflowDefinition(): ReturnType<Ajv2020['compile']> {
|
|
28
29
|
const ajv = new Ajv2020({ strict: false, allErrors: true });
|
|
@@ -78,7 +79,7 @@ describe('workflow-primary-output-annotation: outputRole shape (RFC 0065)', () =
|
|
|
78
79
|
baseNode('b'),
|
|
79
80
|
]);
|
|
80
81
|
const ok = validate(def);
|
|
81
|
-
expect(ok, JSON.stringify(validate.errors, null, 2)).toBe(true);
|
|
82
|
+
expect(ok, req('openwop.it.workflow-primary-output-annotation.accepts-a-workflow-with-one-node-declaring-outputrole-primary', 'RFC 0065', JSON.stringify(validate.errors, null, 2))).toBe(true);
|
|
82
83
|
});
|
|
83
84
|
|
|
84
85
|
it('accepts primary AND secondary annotations on different nodes', () => {
|
|
@@ -88,7 +89,7 @@ describe('workflow-primary-output-annotation: outputRole shape (RFC 0065)', () =
|
|
|
88
89
|
baseNode('c'),
|
|
89
90
|
]);
|
|
90
91
|
const ok = validate(def);
|
|
91
|
-
expect(ok, JSON.stringify(validate.errors, null, 2)).toBe(true);
|
|
92
|
+
expect(ok, req('openwop.it.workflow-primary-output-annotation.accepts-primary-and-secondary-annotations-on-different-nodes', 'RFC 0065', JSON.stringify(validate.errors, null, 2))).toBe(true);
|
|
92
93
|
});
|
|
93
94
|
|
|
94
95
|
it('accepts a workflow with the field absent (additive promise)', () => {
|
|
@@ -97,7 +98,7 @@ describe('workflow-primary-output-annotation: outputRole shape (RFC 0065)', () =
|
|
|
97
98
|
baseNode('b'),
|
|
98
99
|
]);
|
|
99
100
|
const ok = validate(def);
|
|
100
|
-
expect(ok, JSON.stringify(validate.errors, null, 2)).toBe(true);
|
|
101
|
+
expect(ok, req('openwop.it.workflow-primary-output-annotation.accepts-a-workflow-with-the-field-absent-additive-promise', 'RFC 0065', JSON.stringify(validate.errors, null, 2))).toBe(true);
|
|
101
102
|
});
|
|
102
103
|
|
|
103
104
|
it('rejects an unknown outputRole enum value', () => {
|
|
@@ -105,7 +106,7 @@ describe('workflow-primary-output-annotation: outputRole shape (RFC 0065)', () =
|
|
|
105
106
|
baseNode('a', { outputRole: 'tertiary' }),
|
|
106
107
|
]);
|
|
107
108
|
const ok = validate(def);
|
|
108
|
-
expect(ok).toBe(false);
|
|
109
|
+
expect(ok, req('openwop.it.workflow-primary-output-annotation.rejects-an-unknown-outputrole-enum-value', 'RFC 0065', 'rejects an unknown outputRole enum value')).toBe(false);
|
|
109
110
|
expect(validate.errors).toBeTruthy();
|
|
110
111
|
});
|
|
111
112
|
|
|
@@ -114,7 +115,7 @@ describe('workflow-primary-output-annotation: outputRole shape (RFC 0065)', () =
|
|
|
114
115
|
baseNode('a', { outputRole: 1 }),
|
|
115
116
|
]);
|
|
116
117
|
const ok = validate(def);
|
|
117
|
-
expect(ok).toBe(false);
|
|
118
|
+
expect(ok, req('openwop.it.workflow-primary-output-annotation.rejects-outputrole-set-to-a-non-string', 'RFC 0065', 'rejects outputRole set to a non-string')).toBe(false);
|
|
118
119
|
});
|
|
119
120
|
|
|
120
121
|
it('permits multiple nodes declaring outputRole="primary" (tooling decides)', () => {
|
|
@@ -127,6 +128,6 @@ describe('workflow-primary-output-annotation: outputRole shape (RFC 0065)', () =
|
|
|
127
128
|
baseNode('b', { outputRole: 'primary' }),
|
|
128
129
|
]);
|
|
129
130
|
const ok = validate(def);
|
|
130
|
-
expect(ok, JSON.stringify(validate.errors, null, 2)).toBe(true);
|
|
131
|
+
expect(ok, req('openwop.it.workflow-primary-output-annotation.permits-multiple-nodes-declaring-outputrole-primary-tooling-decides', 'RFC 0065', JSON.stringify(validate.errors, null, 2))).toBe(true);
|
|
131
132
|
});
|
|
132
133
|
});
|
|
@@ -48,8 +48,8 @@ import { behaviorGate } from '../lib/behavior-gate.js';
|
|
|
48
48
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
49
49
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
50
50
|
import { driver } from '../lib/driver.js';
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
52
|
+
import { req } from '../lib/requirement-ids.js';
|
|
53
53
|
const WORKFLOW_DEF = join(SCHEMAS_DIR, 'workflow-definition.schema.json');
|
|
54
54
|
// S38 (2026-08-17): `spec/` is NOT in the published package (`files`), so a path built
|
|
55
55
|
// from SCHEMAS_DIR/../spec ENOENTs for every npm consumer — five always-on legs reddened
|
|
@@ -83,11 +83,11 @@ describe('workflow-variable-format §A: corpus (RFC 0136, always-on)', () => {
|
|
|
83
83
|
const wv = defs.WorkflowVariable;
|
|
84
84
|
const props = wv.properties as Record<string, Record<string, unknown>>;
|
|
85
85
|
|
|
86
|
-
expect(props.format,
|
|
87
|
-
expect(props.format.type,
|
|
86
|
+
expect(props.format, req('openwop.it.workflow-variable-format.a1-workflowvariable-declares-format-as-an-advisory-string', '§WorkflowVariable', '`format` is declared')).toBeDefined();
|
|
87
|
+
expect(props.format.type, req('openwop.it.workflow-variable-format.a1-workflowvariable-declares-format-as-an-advisory-string', '§WorkflowVariable', '`format` is a string')).toBe('string');
|
|
88
88
|
expect(
|
|
89
89
|
(wv.required as string[] | undefined)?.includes('format') ?? false,
|
|
90
|
-
|
|
90
|
+
req('openwop.it.workflow-variable-format.a1-workflowvariable-declares-format-as-an-advisory-string', '§WorkflowVariable', '`format` is OPTIONAL — additive per COMPATIBILITY.md §2.1'),
|
|
91
91
|
).toBe(false);
|
|
92
92
|
});
|
|
93
93
|
|
|
@@ -98,7 +98,7 @@ describe('workflow-variable-format §A: corpus (RFC 0136, always-on)', () => {
|
|
|
98
98
|
const recognised = { name: 'recipientEmail', type: 'string', format: 'email' };
|
|
99
99
|
expect(
|
|
100
100
|
validate(recognised),
|
|
101
|
-
|
|
101
|
+
req('openwop.it.workflow-variable-format.a2-an-unrecognised-format-validates-requirement-2-unknown-plain-text-never-an-er', '§WorkflowVariable', `recognised format validates: ${JSON.stringify(validate.errors)}`),
|
|
102
102
|
).toBe(true);
|
|
103
103
|
|
|
104
104
|
// OUTSIDE the table. This is the whole point: the property must NOT be an enum, or a
|
|
@@ -107,7 +107,7 @@ describe('workflow-variable-format §A: corpus (RFC 0136, always-on)', () => {
|
|
|
107
107
|
const unrecognised = { name: 'ipAddress', type: 'string', format: 'vendor.acme.ipv4-or-hostname' };
|
|
108
108
|
expect(
|
|
109
109
|
validate(unrecognised),
|
|
110
|
-
|
|
110
|
+
req('openwop.it.workflow-variable-format.a2-an-unrecognised-format-validates-requirement-2-unknown-plain-text-never-an-er', '§WorkflowVariable', `unrecognised format validates (RFC 0136 req 2): ${JSON.stringify(validate.errors)}`),
|
|
111
111
|
).toBe(true);
|
|
112
112
|
});
|
|
113
113
|
|
|
@@ -116,21 +116,21 @@ describe('workflow-variable-format §A: corpus (RFC 0136, always-on)', () => {
|
|
|
116
116
|
const both = { name: 'notifyAddress', type: 'string', format: 'email', sensitive: true };
|
|
117
117
|
expect(
|
|
118
118
|
validate(both),
|
|
119
|
-
|
|
119
|
+
req('openwop.it.workflow-variable-format.a3-format-and-sensitive-compose-on-one-variable-no-interaction', '§WorkflowVariable', `format + sensitive compose: ${JSON.stringify(validate.errors)}`),
|
|
120
120
|
).toBe(true);
|
|
121
121
|
});
|
|
122
122
|
|
|
123
123
|
it.skipIf(CHAIN_DOC === null)('A4 — chain-pack spec documents deferred-mode `format` propagation as mode-scoped', () => {
|
|
124
124
|
const doc = readFileSync(CHAIN_DOC as string, 'utf8');
|
|
125
125
|
const step1 = doc.slice(doc.indexOf('Materialize parameters as variables'));
|
|
126
|
-
expect(step1.length > 0,
|
|
126
|
+
expect(step1.length > 0, req('openwop.it.workflow-variable-format.a4-chain-pack-spec-documents-deferred-mode-format-propagation-as-mode-scoped', '§Deferred-parameter expansion', 'step 1 present')).toBe(true);
|
|
127
127
|
expect(
|
|
128
128
|
/`format`/.test(step1.slice(0, 1400)),
|
|
129
|
-
|
|
129
|
+
req('openwop.it.workflow-variable-format.a4-chain-pack-spec-documents-deferred-mode-format-propagation-as-mode-scoped', '§Deferred-parameter expansion', 'step 1 copy-list names `format`'),
|
|
130
130
|
).toBe(true);
|
|
131
131
|
expect(
|
|
132
132
|
/this mode only|mode only/i.test(step1.slice(0, 1400)),
|
|
133
|
-
|
|
133
|
+
req('openwop.it.workflow-variable-format.a4-chain-pack-spec-documents-deferred-mode-format-propagation-as-mode-scoped', '§Deferred-parameter expansion', 'the `format` MUST is scoped to deferred mode, not universal'),
|
|
134
134
|
).toBe(true);
|
|
135
135
|
});
|
|
136
136
|
|
|
@@ -138,7 +138,7 @@ describe('workflow-variable-format §A: corpus (RFC 0136, always-on)', () => {
|
|
|
138
138
|
const rfc = readFileSync(RFC_DOC as string, 'utf8');
|
|
139
139
|
expect(
|
|
140
140
|
/configurableSchema/.test(rfc),
|
|
141
|
-
|
|
141
|
+
req('openwop.it.workflow-variable-format.a5-the-rfc-forbids-format-participating-in-a-configurable-validation-decision', 'RFC 0136', 'names the configurableSchema propagation path'),
|
|
142
142
|
).toBe(true);
|
|
143
143
|
// The trap: run-options.md §1 makes validating `configurable` against
|
|
144
144
|
// `configurableSchema` a MUST with `validation_error` on failure. A format-asserting
|
|
@@ -147,7 +147,7 @@ describe('workflow-variable-format §A: corpus (RFC 0136, always-on)', () => {
|
|
|
147
147
|
// closes it: annotation permitted, assertion forbidden.
|
|
148
148
|
expect(
|
|
149
149
|
/requirement 3[\s\S]{0,600}(back door|surface-independent)/i.test(rfc),
|
|
150
|
-
|
|
150
|
+
req('openwop.it.workflow-variable-format.a5-the-rfc-forbids-format-participating-in-a-configurable-validation-decision', 'RFC 0136 req 8', 'states requirement 3 is surface-independent'),
|
|
151
151
|
).toBe(true);
|
|
152
152
|
});
|
|
153
153
|
});
|
|
@@ -171,7 +171,7 @@ describe('workflow-variable-format §B: host behaviour (RFC 0136, capability-gat
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
it('B1 — deferred expansion mints `format` onto the WorkflowVariable: string copied (req 7), unknown copied (req 2), non-string omits it (req 1)', async () => {
|
|
174
|
-
if (!(await deferredParamsGateOpen())) return;
|
|
174
|
+
if (!(await deferredParamsGateOpen())) return softSkip('blocked', 'precondition not met — `!(await deferredParamsGateOpen())` returned early (seam, prior step, or fixture unavailable)');
|
|
175
175
|
|
|
176
176
|
// Drive the RFC 0124 deferred-expand seam with a chain whose parameters exercise all
|
|
177
177
|
// three propagation rules in one expansion; assert the minted variables[] carry (or omit)
|
|
@@ -190,15 +190,15 @@ describe('workflow-variable-format §B: host behaviour (RFC 0136, capability-gat
|
|
|
190
190
|
});
|
|
191
191
|
// A host advertising deferredParameters but not yet serving the variables[]-returning
|
|
192
192
|
// seam extension soft-skips (404), as does a host with the seam disabled in this boot.
|
|
193
|
-
if (res.status === 404) return;
|
|
194
|
-
expect(res.status,
|
|
193
|
+
if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (A host advertising deferredParameters but not yet serving the variables[]-returning seam extension soft-skips (404), as does a host with the seam disabled in…');
|
|
194
|
+
expect(res.status, req('openwop.it.workflow-variable-format.b1-deferred-expansion-mints-format-onto-the-workflowvariable-string-copied-req-7', '§Deferred-parameter expansion', 'the deferred-expand seam returns 200')).toBe(200);
|
|
195
195
|
const variables = (res.json as { variables?: Array<{ name: string; type?: string; format?: string }> }).variables;
|
|
196
|
-
if (!Array.isArray(variables)) return; // seam present but not returning variables[] yet — soft-skip
|
|
196
|
+
if (!Array.isArray(variables)) return softSkip('blocked', 'precondition not met — `!Array.isArray(variables)` returned early (seam present but not returning variables[] yet — soft-skip) (seam, prior step, or fixture unavailable)'); // seam present but not returning variables[] yet — soft-skip
|
|
197
197
|
const fmt = (n: string): string | undefined => variables.find((v) => v.name === n)?.format;
|
|
198
198
|
|
|
199
|
-
expect(fmt('email'),
|
|
200
|
-
expect(fmt('note'),
|
|
201
|
-
expect(fmt('count'),
|
|
199
|
+
expect(fmt('email'), req('openwop.it.workflow-variable-format.b1-deferred-expansion-mints-format-onto-the-workflowvariable-string-copied-req-7', '§WorkflowVariable', 'req 7: a string param\'s recognised `format` is minted verbatim')).toBe('email');
|
|
200
|
+
expect(fmt('note'), req('openwop.it.workflow-variable-format.b1-deferred-expansion-mints-format-onto-the-workflowvariable-string-copied-req-7', '§WorkflowVariable', 'req 2: an UNRECOGNISED `format` is minted verbatim, never dropped')).toBe('vendor.acme.freeform');
|
|
201
|
+
expect(fmt('count'), req('openwop.it.workflow-variable-format.b1-deferred-expansion-mints-format-onto-the-workflowvariable-string-copied-req-7', '§WorkflowVariable', 'req 1: a NON-STRING param mints no `format`')).toBeUndefined();
|
|
202
202
|
});
|
|
203
203
|
|
|
204
204
|
const B2_FIXTURE = 'conformance-workflow-variable-format-advisory';
|
|
@@ -210,13 +210,13 @@ describe('workflow-variable-format §B: host behaviour (RFC 0136, capability-gat
|
|
|
210
210
|
// endpoint, since registration is "POST /v1/workflows or equivalent" (capabilities.md)
|
|
211
211
|
// and the create path is not a mandated portable surface. Gate on fixture advertisement:
|
|
212
212
|
// req 3 is universal (not deferred-mode-specific), so it rides the host loading the fixture.
|
|
213
|
-
if (!isFixtureAdvertised(B2_FIXTURE)) return;
|
|
213
|
+
if (!isFixtureAdvertised(B2_FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(B2_FIXTURE)` returned early (Portable pre-registered-workflow pattern (cf. agentPackHandoffSchemaValidation): the fixture `conformance-wor…');
|
|
214
214
|
|
|
215
215
|
const run = await driver.post('/v1/runs', { workflowId: B2_FIXTURE });
|
|
216
|
-
if (run.status === 404) return; // no run surface — soft-skip
|
|
216
|
+
if (run.status === 404) return softSkip('blocked', 'precondition not met — `run.status === 404` returned early (no run surface — soft-skip) (seam, prior step, or fixture unavailable)'); // no run surface — soft-skip
|
|
217
217
|
expect(
|
|
218
218
|
[200, 201],
|
|
219
|
-
|
|
219
|
+
req('openwop.it.workflow-variable-format.b2-a-run-whose-variable-value-does-not-match-its-declared-format-is-accepted-and', 'POST /v1/runs', 'req 3: a run whose variable value violates its advisory `format` is accepted, not rejected'),
|
|
220
220
|
).toContain(run.status);
|
|
221
221
|
const runId = (run.json as { runId: string }).runId;
|
|
222
222
|
|
|
@@ -232,7 +232,7 @@ describe('workflow-variable-format §B: host behaviour (RFC 0136, capability-gat
|
|
|
232
232
|
}
|
|
233
233
|
expect(
|
|
234
234
|
snap?.status,
|
|
235
|
-
|
|
235
|
+
req('openwop.it.workflow-variable-format.b2-a-run-whose-variable-value-does-not-match-its-declared-format-is-accepted-and', '§WorkflowVariable', 'req 3: the run COMPLETES — `format` is advisory, a value mismatch MUST NOT fail the run'),
|
|
236
236
|
).toBe('completed');
|
|
237
237
|
});
|
|
238
238
|
});
|