@openwop/openwop-conformance 1.163.0 → 2.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/dist/cli.js +262 -14
- package/dist/lib/certification-bundle-v3.js +142 -0
- package/dist/lib/paths.js +25 -3
- package/dist/lib/requirement-ids.js +57 -4
- package/dist/lib/requirement-ledger.js +1 -0
- package/dist/lib/scenario-disposition.js +6 -1
- package/dist/lib/spec-coherence.js +13 -32
- package/dist/spec-artifacts.lock.json +5 -0
- package/package.json +11 -33
- package/requirements.json +22892 -11701
- package/scenario-majors.json +1493 -0
- package/schemas/CORPUS-STAMP.json +137 -5
- package/src/cli.ts +236 -19
- package/src/global-setup.ts +3 -2
- package/src/lib/certification-bundle-v3.ts +175 -0
- package/src/lib/corpus-stamp.ts +29 -1
- package/src/lib/driver.ts +9 -1
- package/src/lib/era2-seed.ts +197 -0
- package/src/lib/paths.ts +25 -3
- package/src/lib/requirement-ids.ts +54 -4
- package/src/lib/requirement-ledger.ts +7 -1
- package/src/lib/scenario-disposition.ts +6 -1
- package/src/lib/seams.ts +45 -0
- package/src/lib/spec-coherence.ts +15 -32
- package/src/lib/v2.ts +62 -0
- package/src/scenarios/a2a-1-0-agent-card.test.ts +17 -16
- package/src/scenarios/a2a-1-0-task-roundtrip.test.ts +11 -10
- package/src/scenarios/a2a-card-runtime-consistency.test.ts +18 -17
- package/src/scenarios/a2a-peer-authority.test.ts +7 -6
- package/src/scenarios/a2a-task-roundtrip.test.ts +30 -29
- package/src/scenarios/a2a-version-negotiation.test.ts +16 -14
- package/src/scenarios/a2ui-surface-degrades.test.ts +5 -3
- package/src/scenarios/a2ui-surface-delta-transport.test.ts +35 -33
- package/src/scenarios/a2ui-surface-replay.test.ts +6 -4
- package/src/scenarios/a2ui-surface-shape.test.ts +11 -10
- package/src/scenarios/a2ui-surface-version-refusal.test.ts +6 -4
- package/src/scenarios/a2ui-untrusted-blocks-approval.test.ts +5 -3
- package/src/scenarios/agent-capability-degraded-projection.test.ts +12 -11
- package/src/scenarios/agent-channel-dispatch.test.ts +18 -16
- package/src/scenarios/agent-deployment-lifecycle.test.ts +20 -19
- package/src/scenarios/agent-deployment-shape.test.ts +22 -24
- package/src/scenarios/agent-eval-run.test.ts +19 -18
- package/src/scenarios/agent-eval-suite-shape.test.ts +20 -22
- package/src/scenarios/agent-live-allowlist-enforced.test.ts +6 -5
- package/src/scenarios/agent-live-invocation-bracket.test.ts +13 -12
- package/src/scenarios/agent-live-runtime-shape.test.ts +10 -12
- package/src/scenarios/agent-live-structured-output.test.ts +7 -6
- package/src/scenarios/agent-loop-iteration-monotonic.test.ts +3 -3
- package/src/scenarios/agent-loop-stateful-resume.test.ts +2 -2
- package/src/scenarios/agent-loop-version5-shape.test.ts +4 -4
- package/src/scenarios/agent-loop-workspace-snapshot.test.ts +3 -3
- package/src/scenarios/agent-manifest-role-profile.test.ts +9 -9
- package/src/scenarios/agent-manifest-runtime.test.ts +13 -12
- package/src/scenarios/agent-org-chart-scoping.test.ts +14 -13
- package/src/scenarios/agent-org-chart-shape.test.ts +11 -13
- package/src/scenarios/agent-platform-aggregate-evidence.test.ts +5 -4
- package/src/scenarios/agent-platform-profile.test.ts +18 -19
- package/src/scenarios/agent-requires-capabilities-shape.test.ts +4 -4
- package/src/scenarios/agent-roster-attribution.test.ts +18 -17
- package/src/scenarios/agent-roster-shape.test.ts +17 -19
- package/src/scenarios/agent-verifier-shape.test.ts +11 -11
- package/src/scenarios/agentConfidenceEscalation.test.ts +2 -1
- package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +6 -5
- package/src/scenarios/agentMemoryRedactionContract.test.ts +2 -1
- package/src/scenarios/agentMemoryRoundTrip.test.ts +2 -1
- package/src/scenarios/agentMemoryTtlExpiry.test.ts +4 -3
- package/src/scenarios/agentMessageReducer.test.ts +2 -1
- package/src/scenarios/agentMetadata.test.ts +2 -1
- package/src/scenarios/agentPackCatalog.test.ts +23 -21
- package/src/scenarios/agentPackExport.test.ts +4 -2
- package/src/scenarios/agentPackHandoffSchemaValidation.test.ts +5 -4
- package/src/scenarios/agentPackInstall.test.ts +4 -2
- package/src/scenarios/agentPackProvenance.test.ts +2 -1
- package/src/scenarios/agentReasoningEvents.test.ts +3 -2
- package/src/scenarios/agentReasoningStreaming.test.ts +11 -10
- package/src/scenarios/agents-run-tool-allowlist.test.ts +14 -12
- package/src/scenarios/ai-envelope-shape.test.ts +31 -29
- package/src/scenarios/aiEnvelope.capBreached.test.ts +26 -24
- package/src/scenarios/aiEnvelope.contractRefusal.test.ts +36 -34
- package/src/scenarios/aiEnvelope.correlationReplay.test.ts +17 -15
- package/src/scenarios/aiEnvelope.redaction.test.ts +22 -20
- package/src/scenarios/aiEnvelope.schemaDrift.test.ts +18 -16
- package/src/scenarios/aiEnvelope.trustBoundaryPropagation.test.ts +38 -36
- package/src/scenarios/aiEnvelope.universalKinds.test.ts +31 -29
- package/src/scenarios/aiproviders-input-shape.test.ts +5 -5
- package/src/scenarios/aiproviders-realtimevoice-shape.test.ts +14 -16
- package/src/scenarios/aiproviders-selfhosted-honesty.test.ts +6 -4
- package/src/scenarios/aiproviders-selfhosted-shape.test.ts +15 -17
- package/src/scenarios/aiproviders-speechsynth-shape.test.ts +6 -8
- package/src/scenarios/aiproviders-subscription-scope.test.ts +7 -8
- package/src/scenarios/anonymous-actor-audit-opaque.test.ts +9 -8
- package/src/scenarios/anonymous-actor-default-deny.test.ts +10 -9
- package/src/scenarios/anonymous-actor-egress-guarded.test.ts +6 -5
- package/src/scenarios/anonymous-actor-no-secret-reach.test.ts +7 -6
- package/src/scenarios/anonymous-actor-shape.test.ts +17 -19
- package/src/scenarios/anonymous-actor-write-gated.test.ts +9 -8
- package/src/scenarios/append-ordering.test.ts +7 -5
- package/src/scenarios/approval-gate-events.test.ts +4 -3
- package/src/scenarios/approval-gate-flow.test.ts +4 -3
- package/src/scenarios/approval-payload.test.ts +5 -4
- package/src/scenarios/artifact-auth.test.ts +7 -6
- package/src/scenarios/artifact-type-pack-install.test.ts +6 -6
- package/src/scenarios/artifact-type-pack-manifest-validation.test.ts +9 -8
- package/src/scenarios/artifact-type-registration-source.test.ts +20 -20
- package/src/scenarios/artifact-type-store-emission.test.ts +15 -18
- package/src/scenarios/artifact-type-store-without-render.test.ts +4 -4
- package/src/scenarios/audit-log-integrity.test.ts +8 -6
- package/src/scenarios/auth-api-key-rotation.test.ts +11 -9
- package/src/scenarios/auth-mtls.test.ts +22 -20
- package/src/scenarios/auth-oauth2-client-credentials.test.ts +16 -14
- package/src/scenarios/auth-oidc-user-bearer.test.ts +26 -24
- package/src/scenarios/auth-saml-profile.test.ts +14 -12
- package/src/scenarios/auth-scim-profile.test.ts +3 -2
- package/src/scenarios/auth-subject-link-key-class.test.ts +8 -7
- package/src/scenarios/auth-subject-link.test.ts +11 -10
- package/src/scenarios/auth.test.ts +5 -4
- package/src/scenarios/authorization-fail-closed.test.ts +3 -2
- package/src/scenarios/authorization-roles-shape.test.ts +9 -7
- package/src/scenarios/blob-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/blob-presign-expiry.test.ts +12 -10
- package/src/scenarios/blob-roundtrip.test.ts +0 -0
- package/src/scenarios/budget-enforcement.test.ts +15 -14
- package/src/scenarios/budget-policy-shape.test.ts +18 -19
- package/src/scenarios/bulk-cancel.test.ts +9 -8
- package/src/scenarios/byok-auth-modes.test.ts +13 -14
- package/src/scenarios/byok-roundtrip.test.ts +12 -10
- package/src/scenarios/cache-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/cache-ttl-expiry.test.ts +7 -5
- package/src/scenarios/callai-multimodal.test.ts +5 -3
- package/src/scenarios/cancellation.test.ts +6 -5
- package/src/scenarios/cap-breach.test.ts +15 -14
- package/src/scenarios/capability-arm-witness.test.ts +5 -3
- package/src/scenarios/certification-bundle-non-vacuous.test.ts +20 -19
- package/src/scenarios/certification-bundle-redaction.test.ts +11 -10
- package/src/scenarios/certification-bundle-v2.test.ts +11 -10
- package/src/scenarios/chain-compensation-expansion.test.ts +14 -13
- package/src/scenarios/chain-produced-var-roundtrip.test.ts +13 -14
- package/src/scenarios/chain-subchain-cycle-rejected.test.ts +9 -10
- package/src/scenarios/chain-subchain-fanout.test.ts +8 -9
- package/src/scenarios/chain-subchain-sibling.test.ts +14 -16
- package/src/scenarios/channel-presence-behavioral.test.ts +9 -7
- package/src/scenarios/channel-presence-shape.test.ts +11 -12
- package/src/scenarios/channel-ttl.test.ts +6 -5
- package/src/scenarios/chat-card-pack-execution.test.ts +4 -4
- package/src/scenarios/chat-card-pack-manifest-validation.test.ts +9 -8
- package/src/scenarios/commitment-fired.test.ts +6 -5
- package/src/scenarios/compensation-behavior.test.ts +24 -23
- package/src/scenarios/compensation-recovery.test.ts +28 -27
- package/src/scenarios/configurable-schema.test.ts +8 -6
- package/src/scenarios/conformance-advertised-seam-required.test.ts +6 -5
- package/src/scenarios/conformance-execution-witness.test.ts +9 -8
- package/src/scenarios/connection-pack-apihosts.test.ts +19 -17
- package/src/scenarios/connection-pack-manifest-valid.test.ts +12 -11
- package/src/scenarios/connection-pack-no-credential-material.test.ts +8 -6
- package/src/scenarios/connection-pack-reach-exclusive.test.ts +5 -4
- package/src/scenarios/connection-pack-write-reconsent.test.ts +6 -4
- package/src/scenarios/connection-provider-resolution.test.ts +18 -16
- package/src/scenarios/connector-manifest-validity.test.ts +8 -7
- package/src/scenarios/context-budget-transcript-bound.test.ts +17 -15
- package/src/scenarios/context-summarization-replay.test.ts +14 -12
- package/src/scenarios/contract-provenance.test.ts +18 -18
- package/src/scenarios/conversation-turn-model-provenance-shape.test.ts +14 -15
- package/src/scenarios/conversationCapabilityNegotiation.test.ts +2 -1
- package/src/scenarios/conversationLifecycle.test.ts +2 -1
- package/src/scenarios/conversationReplayDeterminism.test.ts +4 -2
- package/src/scenarios/conversationVsLegacySuspend.test.ts +2 -1
- package/src/scenarios/core-standard-profile.test.ts +8 -9
- package/src/scenarios/cost-attribution.test.ts +23 -21
- package/src/scenarios/credential-payload-redaction.test.ts +4 -3
- package/src/scenarios/credentials-capability-shape.test.ts +5 -4
- package/src/scenarios/cross-engine-append-behavior.test.ts +11 -10
- package/src/scenarios/cross-engine-append-ordering.test.ts +4 -3
- package/src/scenarios/cross-host-ancestry-endpoint.test.ts +11 -10
- package/src/scenarios/cross-host-causation-shape.test.ts +7 -6
- package/src/scenarios/cross-workspace-isolation.test.ts +4 -3
- package/src/scenarios/data-residency-admission.test.ts +15 -14
- package/src/scenarios/deadletter-capability-shape.test.ts +6 -4
- package/src/scenarios/deadletter-retry-exhaustion.test.ts +4 -3
- package/src/scenarios/debug-bundle-truncation.test.ts +9 -7
- package/src/scenarios/debugBundle.test.ts +25 -23
- package/src/scenarios/discovery-families-view.test.ts +5 -4
- package/src/scenarios/discovery.test.ts +30 -29
- package/src/scenarios/dispatch-cross-worker-handoff.test.ts +10 -8
- package/src/scenarios/dispatch-fanout-parallel.test.ts +24 -22
- package/src/scenarios/dispatch-input-mapping.test.ts +12 -10
- package/src/scenarios/dispatch-output-mapping.test.ts +9 -7
- package/src/scenarios/dispatch-per-item-input.test.ts +22 -20
- package/src/scenarios/dispatchLoop.test.ts +3 -2
- package/src/scenarios/distillation-index-roundtrip.test.ts +3 -3
- package/src/scenarios/distillation-secret-carryforward.test.ts +2 -2
- package/src/scenarios/distillation-shape.test.ts +4 -4
- package/src/scenarios/distillation-stable-archive.test.ts +6 -6
- package/src/scenarios/distillation-token-budget.test.ts +5 -5
- package/src/scenarios/durability-poison-exhaustion.test.ts +7 -6
- package/src/scenarios/egress-audience-binding.test.ts +11 -10
- package/src/scenarios/egress-decision-content-free.test.ts +6 -5
- package/src/scenarios/egress-provenance-shape.test.ts +17 -18
- package/src/scenarios/envelope-completion-distinguishes-truncation.test.ts +27 -25
- package/src/scenarios/envelope-nl-to-format-engaged.test.ts +19 -17
- package/src/scenarios/envelope-reasoning-secret-redaction.test.ts +28 -26
- package/src/scenarios/envelope-reasoning-shape.test.ts +19 -17
- package/src/scenarios/envelope-recovery-applied.test.ts +24 -22
- package/src/scenarios/envelope-refusal-shape.test.ts +31 -29
- package/src/scenarios/envelope-rendering-hint.test.ts +5 -4
- package/src/scenarios/envelope-retry-attempted.test.ts +33 -31
- package/src/scenarios/envelope-retry-exhausted.test.ts +22 -20
- package/src/scenarios/envelope-tier-one-subset-static.test.ts +9 -7
- package/src/scenarios/envelope-truncated.test.ts +19 -17
- package/src/scenarios/envelope-truncation-cap-exhaustion.test.ts +19 -17
- package/src/scenarios/envelope-variant-discriminator-static.test.ts +4 -3
- package/src/scenarios/errors.test.ts +20 -19
- package/src/scenarios/eventOrdering.test.ts +16 -14
- package/src/scenarios/exec-not-protocol-tier.test.ts +5 -7
- package/src/scenarios/experimental-tier-shape.test.ts +17 -15
- package/src/scenarios/export-bundle-portability.test.ts +17 -17
- package/src/scenarios/failure-path.test.ts +7 -6
- package/src/scenarios/feedback-capability-shape.test.ts +4 -3
- package/src/scenarios/feedback-correction-redaction.test.ts +2 -1
- package/src/scenarios/feedback-cross-tenant-isolation.test.ts +2 -1
- package/src/scenarios/feedback-fork-not-copied.test.ts +3 -2
- package/src/scenarios/feedback-on-terminal-run.test.ts +3 -2
- package/src/scenarios/feedback-record-and-list.test.ts +2 -1
- package/src/scenarios/feedback-unsupported-501.test.ts +2 -1
- package/src/scenarios/fixtures-gating.test.ts +29 -28
- package/src/scenarios/fixtures-valid.test.ts +16 -15
- package/src/scenarios/form-content-instantiation.test.ts +15 -14
- package/src/scenarios/frontend-plugin-packs.test.ts +29 -27
- package/src/scenarios/fs-path-traversal.test.ts +14 -12
- package/src/scenarios/goal-standing-continuation.test.ts +21 -21
- package/src/scenarios/grpc-transport.test.ts +9 -7
- package/src/scenarios/heartbeat-capability-shape.test.ts +3 -3
- package/src/scenarios/heartbeat-fires-once-per-tick.test.ts +2 -2
- package/src/scenarios/heartbeat-idempotent-no-spam.test.ts +3 -3
- package/src/scenarios/heartbeat-runtime-bound.test.ts +2 -2
- package/src/scenarios/highConcurrency.test.ts +14 -13
- package/src/scenarios/host-callback-declaration.test.ts +8 -7
- package/src/scenarios/http-client-ssrf.test.ts +7 -5
- package/src/scenarios/i18n-negotiation.test.ts +14 -12
- package/src/scenarios/idempotency-concurrent-claim.test.ts +14 -13
- package/src/scenarios/idempotency-key-determinism.test.ts +20 -18
- package/src/scenarios/idempotency.test.ts +8 -7
- package/src/scenarios/idempotencyRetry.test.ts +13 -11
- package/src/scenarios/identity-owner-shape.test.ts +6 -5
- package/src/scenarios/identity-passthrough.test.ts +4 -3
- package/src/scenarios/interrupt-approval.test.ts +6 -5
- package/src/scenarios/interrupt-approver-routing.test.ts +15 -15
- package/src/scenarios/interrupt-auth-required-resume.test.ts +5 -3
- package/src/scenarios/interrupt-clarification.test.ts +4 -3
- package/src/scenarios/interrupt-external-event-correlation.test.ts +6 -5
- package/src/scenarios/interrupt-parent-child-cascade.test.ts +4 -3
- package/src/scenarios/interrupt-quorum-resolution.test.ts +5 -4
- package/src/scenarios/interrupt-token-matrix.test.ts +8 -6
- package/src/scenarios/interruptRace.test.ts +12 -10
- package/src/scenarios/kv-atomic-increment.test.ts +11 -9
- package/src/scenarios/kv-cas.test.ts +14 -12
- package/src/scenarios/kv-cross-tenant-isolation.test.ts +12 -10
- package/src/scenarios/kv-ttl-expiry.test.ts +7 -5
- package/src/scenarios/localized-content-delivery.test.ts +24 -26
- package/src/scenarios/maliciousManifest.test.ts +10 -8
- package/src/scenarios/mcp-2026-07-28-discover.test.ts +15 -14
- package/src/scenarios/mcp-cache-tenant-scope.test.ts +6 -5
- package/src/scenarios/mcp-current-auth-boundary.test.ts +7 -6
- package/src/scenarios/mcp-discoverability.test.ts +9 -7
- package/src/scenarios/mcp-extension-opacity.test.ts +7 -6
- package/src/scenarios/mcp-mrtr-roundtrip.test.ts +18 -17
- package/src/scenarios/mcp-server-elicitation-bridge.test.ts +13 -12
- package/src/scenarios/mcp-server-prompt-roundtrip.test.ts +11 -10
- package/src/scenarios/mcp-server-resource-roundtrip.test.ts +11 -10
- package/src/scenarios/mcp-server-sampling-bridge.test.ts +12 -11
- package/src/scenarios/mcp-server-tool-roundtrip.test.ts +14 -13
- package/src/scenarios/mcp-server-untrusted-args.test.ts +14 -13
- package/src/scenarios/mcp-stateless-request.test.ts +11 -10
- package/src/scenarios/mcp-tool-roundtrip.test.ts +4 -3
- package/src/scenarios/mcp-toolcall-redaction.test.ts +4 -3
- package/src/scenarios/mcp-version-negotiation.test.ts +15 -14
- package/src/scenarios/media-url-inline-cap.test.ts +21 -19
- package/src/scenarios/memory-attribution-emits-on-write.test.ts +5 -5
- package/src/scenarios/memory-attribution-no-content.test.ts +4 -4
- package/src/scenarios/memory-attribution-replay-stable.test.ts +4 -3
- package/src/scenarios/memory-attribution-shape.test.ts +4 -3
- package/src/scenarios/memory-attribution-tenant-scoped.test.ts +3 -3
- package/src/scenarios/memory-capability-model-shape.test.ts +14 -15
- package/src/scenarios/memory-compaction-event-emitted.test.ts +7 -6
- package/src/scenarios/memory-compaction-provenance-tag.test.ts +3 -2
- package/src/scenarios/memory-compaction-sr1-carry-forward.test.ts +7 -6
- package/src/scenarios/memory-consolidation-idempotent.test.ts +5 -4
- package/src/scenarios/memory-consolidation-shape.test.ts +9 -11
- package/src/scenarios/memory-degraded-projection.test.ts +10 -9
- package/src/scenarios/memory-injection-budget.test.ts +15 -14
- package/src/scenarios/metric-emission.test.ts +4 -3
- package/src/scenarios/model-capability-insufficient.test.ts +19 -17
- package/src/scenarios/model-capability-substituted.test.ts +20 -18
- package/src/scenarios/multi-agent-confidence-escalation.test.ts +12 -11
- package/src/scenarios/multi-agent-handoff-state-machine.test.ts +10 -9
- package/src/scenarios/multi-agent-memory-lifecycle.test.ts +11 -10
- package/src/scenarios/multi-node-ordering.test.ts +4 -3
- package/src/scenarios/multi-party-conversation-behavioral.test.ts +6 -6
- package/src/scenarios/multi-party-conversation-shape.test.ts +21 -23
- package/src/scenarios/multi-region-idempotency-behavior.test.ts +16 -15
- package/src/scenarios/multi-region-idempotency.test.ts +6 -5
- package/src/scenarios/node-failed-payload-shape.test.ts +5 -4
- package/src/scenarios/node-module-required-capabilities-shape.test.ts +10 -8
- package/src/scenarios/oauth-authorization-code-roundtrip.test.ts +5 -4
- package/src/scenarios/oauth-capability-shape.test.ts +5 -4
- package/src/scenarios/oauth-connector-redaction.test.ts +4 -3
- package/src/scenarios/openapi-resolved-paths.test.ts +6 -5
- package/src/scenarios/orchestratorConservativePath.test.ts +2 -1
- package/src/scenarios/orchestratorDispatch.test.ts +2 -1
- package/src/scenarios/orchestratorTermination.test.ts +2 -1
- package/src/scenarios/org-position-no-authority-escalation.test.ts +2 -2
- package/src/scenarios/otel-collector-canary-inspection.test.ts +8 -7
- package/src/scenarios/otel-emission-grpc.test.ts +3 -2
- package/src/scenarios/otel-emission.test.ts +6 -5
- package/src/scenarios/otel-trace-propagation-subworkflow.test.ts +7 -6
- package/src/scenarios/otel-trace-propagation.test.ts +2 -1
- package/src/scenarios/owner-subject-echo.test.ts +13 -12
- package/src/scenarios/owner-subject-shape.test.ts +11 -10
- package/src/scenarios/pack-manifest-extension-opacity.test.ts +8 -8
- package/src/scenarios/pack-registry-isolation.test.ts +4 -3
- package/src/scenarios/pack-registry-publish.test.ts +65 -63
- package/src/scenarios/pack-registry.test.ts +38 -36
- package/src/scenarios/pause-resume.test.ts +18 -16
- package/src/scenarios/policies.test.ts +20 -18
- package/src/scenarios/production-backpressure.test.ts +18 -16
- package/src/scenarios/production-retention-expiry.test.ts +8 -6
- package/src/scenarios/profile-discovery-core-alias.test.ts +13 -11
- package/src/scenarios/profileDerivation.test.ts +34 -33
- package/src/scenarios/prompt-all-four-kinds-events.test.ts +11 -9
- package/src/scenarios/prompt-composed-secret-redaction.test.ts +11 -9
- package/src/scenarios/prompt-composed-trust-marker.test.ts +10 -8
- package/src/scenarios/prompt-end-to-end-events.test.ts +17 -15
- package/src/scenarios/prompt-list-and-fetch.test.ts +16 -14
- package/src/scenarios/prompt-mutable-lifecycle.test.ts +13 -11
- package/src/scenarios/prompt-mutation-workspace-membership-enforced.test.ts +6 -4
- package/src/scenarios/prompt-pack-install.test.ts +14 -12
- package/src/scenarios/prompt-prefix-cache.test.ts +19 -17
- package/src/scenarios/prompt-read-workspace-membership-enforced.test.ts +11 -9
- package/src/scenarios/prompt-render-deterministic.test.ts +19 -17
- package/src/scenarios/prompt-resolution-chain-agent-intrinsic.test.ts +7 -5
- package/src/scenarios/prompt-resolution-chain-event.test.ts +12 -10
- package/src/scenarios/prompt-resolution-chain-fallback-cascade.test.ts +10 -8
- package/src/scenarios/prompt-resolution-chain-node-wins.test.ts +8 -6
- package/src/scenarios/prompt-template-shape.test.ts +30 -28
- package/src/scenarios/proposal-reviewable-learning.test.ts +19 -19
- package/src/scenarios/protocol-versions-array.test.ts +10 -9
- package/src/scenarios/provider-usage.test.ts +17 -15
- package/src/scenarios/providerPolicyEnforcement.test.ts +16 -14
- package/src/scenarios/purpose-propagation.test.ts +23 -21
- package/src/scenarios/queue-ack-nack-dlq.test.ts +12 -10
- package/src/scenarios/queue-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/queue-publish-consume-roundtrip.test.ts +10 -8
- package/src/scenarios/rate-limit-envelope.test.ts +6 -5
- package/src/scenarios/redaction.test.ts +13 -11
- package/src/scenarios/redactionAdversarial.test.ts +11 -9
- package/src/scenarios/registry-public.test.ts +12 -11
- package/src/scenarios/replay-divergence-at-refusal.test.ts +16 -15
- package/src/scenarios/replay-fanout-suppression.test.ts +16 -15
- package/src/scenarios/replay-fork-arbitrary.test.ts +24 -23
- package/src/scenarios/replay-fork.test.ts +20 -19
- package/src/scenarios/replay-llm-cache-key-portable.test.ts +11 -9
- package/src/scenarios/replay-llm-cache-key.test.ts +22 -21
- package/src/scenarios/replay-observable-sequence-determinism.test.ts +4 -3
- package/src/scenarios/replay-retention-expiry.test.ts +11 -9
- package/src/scenarios/replay-side-effect-suppression.test.ts +21 -19
- package/src/scenarios/replayDeterminism.test.ts +13 -12
- package/src/scenarios/requirement-ledger.test.ts +15 -14
- package/src/scenarios/restart-during-run.test.ts +3 -2
- package/src/scenarios/route-coverage.test.ts +20 -19
- package/src/scenarios/run-diff.test.ts +22 -20
- package/src/scenarios/run-execution-bounds-shape.test.ts +14 -12
- package/src/scenarios/run-transport-economy.test.ts +13 -11
- package/src/scenarios/runner-ledger.test.ts +17 -16
- package/src/scenarios/runs-lifecycle.test.ts +7 -6
- package/src/scenarios/runtime-capabilities.test.ts +12 -10
- package/src/scenarios/runtime-requires-install-gate.test.ts +8 -8
- package/src/scenarios/runtime-requires-shape.test.ts +7 -6
- package/src/scenarios/safefetch-behavior.test.ts +22 -21
- package/src/scenarios/safefetch-live-audit.test.ts +14 -13
- package/src/scenarios/sandbox-memory-cap.test.ts +2 -1
- package/src/scenarios/sandbox-mvp-behavior.test.ts +24 -23
- package/src/scenarios/sandbox-no-host-fs-escape.test.ts +3 -2
- package/src/scenarios/sandbox-timeout-cap.test.ts +2 -1
- package/src/scenarios/sandbox-wasm-isolation.test.ts +14 -15
- package/src/scenarios/sandbox-wasm-timeout.test.ts +5 -6
- package/src/scenarios/scheduling-capability-shape.test.ts +8 -6
- package/src/scenarios/scheduling-cron-fires-once.test.ts +8 -6
- package/src/scenarios/search-bm25-roundtrip.test.ts +8 -6
- package/src/scenarios/secret-leakage-otel-attribute.test.ts +22 -20
- package/src/scenarios/self-hosted-runner.test.ts +26 -24
- package/src/scenarios/semantic-digest-vectors.test.ts +8 -7
- package/src/scenarios/speech-synthesis-roundtrip.test.ts +9 -7
- package/src/scenarios/speech-synthesis-unadvertised.test.ts +5 -3
- package/src/scenarios/sql-injection-rejection.test.ts +8 -6
- package/src/scenarios/sql-transaction-atomicity.test.ts +10 -8
- package/src/scenarios/staleClaim.test.ts +5 -4
- package/src/scenarios/stream-modes-buffer.test.ts +8 -7
- package/src/scenarios/stream-modes-mixed.test.ts +10 -9
- package/src/scenarios/stream-modes.test.ts +12 -11
- package/src/scenarios/stream-subscribe-from-beginning.test.ts +11 -9
- package/src/scenarios/stream-text-fixture.test.ts +14 -13
- package/src/scenarios/streamReconnect.test.ts +13 -11
- package/src/scenarios/strict-behavior-gate.test.ts +9 -8
- package/src/scenarios/subrun-approval-fail-closed.test.ts +2 -2
- package/src/scenarios/subrun-approval-gate.test.ts +3 -3
- package/src/scenarios/subrun-attestation-shape.test.ts +2 -2
- package/src/scenarios/subrun-checksum-stable.test.ts +4 -4
- package/src/scenarios/subworkflow-input-mapping.test.ts +16 -14
- package/src/scenarios/subworkflow.test.ts +10 -9
- package/src/scenarios/table-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/table-cursor-pagination.test.ts +8 -6
- package/src/scenarios/table-schema-enforcement.test.ts +7 -5
- package/src/scenarios/test-seam-unauthenticated.test.ts +2 -2
- package/src/scenarios/tool-catalog-compact-projection.test.ts +10 -8
- package/src/scenarios/tool-catalog-projection.test.ts +11 -9
- package/src/scenarios/tool-descriptor-shape.test.ts +14 -15
- package/src/scenarios/tool-hooks-authorization-fail-closed.test.ts +3 -3
- package/src/scenarios/tool-hooks-content-free.test.ts +4 -4
- package/src/scenarios/tool-hooks-failure-honesty.test.ts +4 -4
- package/src/scenarios/tool-hooks-rate-limit.test.ts +2 -2
- package/src/scenarios/tool-hooks-secret-redaction.test.ts +2 -2
- package/src/scenarios/tool-hooks-shape.test.ts +3 -3
- package/src/scenarios/tool-session-lifecycle.test.ts +14 -13
- package/src/scenarios/trigger-bridge-delivery.test.ts +14 -14
- package/src/scenarios/trigger-bridge-shape.test.ts +19 -20
- package/src/scenarios/trigger-ingestion.test.ts +19 -17
- package/src/scenarios/trigger-stream-cdc-sources.test.ts +16 -14
- package/src/scenarios/v2-approver-enforced.test.ts +97 -0
- package/src/scenarios/v2-assurance-downgrade-audited.test.ts +75 -0
- package/src/scenarios/v2-bundle-v3-signed.test.ts +96 -0
- package/src/scenarios/v2-capabilities-root-closed.test.ts +63 -0
- package/src/scenarios/v2-capability-record-shape.test.ts +104 -0
- package/src/scenarios/v2-chain-pin-exact.test.ts +118 -0
- package/src/scenarios/v2-coherence-not-in-bundle.test.ts +98 -0
- package/src/scenarios/v2-compensation-read-projection.test.ts +85 -0
- package/src/scenarios/v2-configurable-closed.test.ts +67 -0
- package/src/scenarios/v2-dual-stack-negotiation.test.ts +118 -0
- package/src/scenarios/v2-effect-identity-business-key.test.ts +132 -0
- package/src/scenarios/v2-effect-seam-manifest.test.ts +163 -0
- package/src/scenarios/v2-enum-growth-rule.test.ts +69 -0
- package/src/scenarios/v2-era-key.test.ts +86 -0
- package/src/scenarios/v2-error-registry.test.ts +89 -0
- package/src/scenarios/v2-event-type-closed.test.ts +97 -0
- package/src/scenarios/v2-fork-a-v1-run.test.ts +109 -0
- package/src/scenarios/v2-form-when-reuses-edge-conditions.test.ts +70 -0
- package/src/scenarios/v2-header-scheme.test.ts +67 -0
- package/src/scenarios/v2-id-grammar.test.ts +77 -0
- package/src/scenarios/v2-idempotency-key-grammar.test.ts +65 -0
- package/src/scenarios/v2-interrupt-token-scheme.test.ts +55 -0
- package/src/scenarios/v2-lane-issuer-advertised.test.ts +62 -0
- package/src/scenarios/v2-legacy-profiles-absent.test.ts +78 -0
- package/src/scenarios/v2-manifest-ceiling-refused.test.ts +130 -0
- package/src/scenarios/v2-manifest-hatch-carried.test.ts +120 -0
- package/src/scenarios/v2-min-client-version.test.ts +48 -0
- package/src/scenarios/v2-minimum-version-refused.test.ts +141 -0
- package/src/scenarios/v2-mrtr-rounds-ceiling.test.ts +95 -0
- package/src/scenarios/v2-negotiation-authenticated.test.ts +125 -0
- package/src/scenarios/v2-negotiation-decided-emitted.test.ts +149 -0
- package/src/scenarios/v2-no-transport-advertisement.test.ts +57 -0
- package/src/scenarios/v2-owner-subject-required.test.ts +72 -0
- package/src/scenarios/v2-pack-isolation.test.ts +147 -0
- package/src/scenarios/v2-payload-registry-closed.test.ts +110 -0
- package/src/scenarios/v2-peer-dependency-declared.test.ts +132 -0
- package/src/scenarios/v2-pinned-run-disposition.test.ts +124 -0
- package/src/scenarios/v2-poll-cursor-v2.test.ts +114 -0
- package/src/scenarios/v2-preferred-version-default.test.ts +83 -0
- package/src/scenarios/v2-profiles-derived-only.test.ts +70 -0
- package/src/scenarios/v2-provider-conflict.test.ts +115 -0
- package/src/scenarios/v2-refresh-sla.test.ts +78 -0
- package/src/scenarios/v2-relaxation-recorded.test.ts +134 -0
- package/src/scenarios/v2-revocation-honored.test.ts +66 -0
- package/src/scenarios/v2-subject-link-record.test.ts +102 -0
- package/src/scenarios/v2-unmapped-type-refused.test.ts +95 -0
- package/src/scenarios/v2-v1-events-translated.test.ts +105 -0
- package/src/scenarios/v2-v1-signed-webhook-accepted.test.ts +109 -0
- package/src/scenarios/v2-webhook-durable-delivery.test.ts +238 -0
- package/src/scenarios/v2-well-known-one-resource.test.ts +129 -0
- package/src/scenarios/vector-knn-roundtrip.test.ts +9 -7
- package/src/scenarios/verifier-gating.test.ts +8 -6
- package/src/scenarios/version-fold.test.ts +9 -8
- package/src/scenarios/version-negotiation.test.ts +12 -11
- package/src/scenarios/voice-bargein-no-partial-leak.test.ts +8 -6
- package/src/scenarios/voice-event-payloads-shape.test.ts +10 -11
- package/src/scenarios/voice-interim-not-durable.test.ts +7 -5
- package/src/scenarios/voice-streamref-tenant-bound.test.ts +5 -3
- package/src/scenarios/voice-synthesis-streaming.test.ts +11 -9
- package/src/scenarios/voice-transcription-streaming.test.ts +8 -6
- package/src/scenarios/voice-transcription-unadvertised.test.ts +5 -3
- package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +3 -2
- package/src/scenarios/wasm-pack-invoke-completed.test.ts +3 -2
- package/src/scenarios/wasm-pack-invoke-suspended.test.ts +3 -2
- package/src/scenarios/wasm-pack-load.test.ts +4 -3
- package/src/scenarios/wasm-pack-memory-cap.test.ts +5 -4
- package/src/scenarios/wasm-pack-replay-determinism.test.ts +2 -1
- package/src/scenarios/webhook-negative.test.ts +11 -9
- package/src/scenarios/webhook-receiver-adversarial.test.ts +8 -7
- package/src/scenarios/webhook-sig-algorithm.test.ts +5 -3
- package/src/scenarios/webhook-signed-delivery.test.ts +16 -15
- package/src/scenarios/webhook-tenant-isolation.test.ts +16 -14
- package/src/scenarios/workflow-chain-deferred-parameters.test.ts +28 -30
- package/src/scenarios/workflow-chain-expansion.test.ts +21 -20
- package/src/scenarios/workflow-chain-host-expansion.test.ts +21 -20
- package/src/scenarios/workflow-chain-pack-manifest-validation.test.ts +14 -12
- package/src/scenarios/workflow-chain-pack-signature-verification.test.ts +5 -4
- package/src/scenarios/workflow-chain-unresolvable-typeid.test.ts +9 -8
- package/src/scenarios/workflow-primary-output-annotation.test.ts +7 -6
- package/src/scenarios/workflow-variable-format.test.ts +24 -24
- package/src/scenarios/workload-identity-behavior.test.ts +15 -13
- package/src/scenarios/workload-identity-chain-bounds.test.ts +18 -17
- package/src/scenarios/workspace-behavior.test.ts +19 -18
- package/src/scenarios/workspace-capability-shape.test.ts +3 -2
- package/src/scenarios/workspace-cross-tenant-isolation-blackbox.test.ts +6 -5
- package/src/scenarios/workspace-cross-tenant-isolation.test.ts +7 -6
- package/src/scenarios/x-openwop-form-pack-manifest.test.ts +8 -7
- package/src/setup.ts +44 -6
- package/vitest.config.ts +5 -1
- package/api/.redocly.lint-ignore.yaml +0 -22
- package/api/asyncapi.yaml +0 -834
- package/api/grpc/openwop.proto +0 -251
- package/api/openapi.yaml +0 -2693
- package/api/redocly.yaml +0 -23
- package/schemas/README.md +0 -144
- package/schemas/a2a-task-state.schema.json +0 -78
- package/schemas/a2ui-surface-delta-frame.schema.json +0 -48
- package/schemas/agent-deployment-transition.schema.json +0 -49
- package/schemas/agent-deployment.schema.json +0 -54
- package/schemas/agent-eval-suite.schema.json +0 -140
- package/schemas/agent-inventory-response.schema.json +0 -115
- package/schemas/agent-manifest.schema.json +0 -156
- package/schemas/agent-org-chart.schema.json +0 -82
- package/schemas/agent-ref.schema.json +0 -63
- package/schemas/agent-roster-entry.schema.json +0 -81
- package/schemas/agent-roster-response.schema.json +0 -21
- package/schemas/ai-envelope.schema.json +0 -134
- package/schemas/annotation-create.schema.json +0 -37
- package/schemas/annotation.schema.json +0 -56
- package/schemas/artifact-type-pack-manifest.schema.json +0 -244
- package/schemas/audit-verify-result.schema.json +0 -90
- package/schemas/budget-policy.schema.json +0 -18
- package/schemas/capabilities.schema.json +0 -4160
- package/schemas/certification-bundle-v2.schema.json +0 -118
- package/schemas/channel-presence-payload.schema.json +0 -41
- package/schemas/channel-written-payload.schema.json +0 -55
- package/schemas/chat-card-pack-manifest.schema.json +0 -253
- package/schemas/compact-tool-descriptor.schema.json +0 -51
- package/schemas/compensation-policy.schema.json +0 -64
- package/schemas/conformance-certification-bundle.schema.json +0 -86
- package/schemas/connection-pack-manifest.schema.json +0 -353
- package/schemas/conversation-event.schema.json +0 -168
- package/schemas/conversation-turn.schema.json +0 -117
- package/schemas/core-conformance-mock-agent-config.schema.json +0 -152
- package/schemas/credential-provenance.schema.json +0 -18
- package/schemas/credential-reference.schema.json +0 -21
- package/schemas/debug-bundle.schema.json +0 -196
- package/schemas/dispatch-config.schema.json +0 -112
- package/schemas/envelopes/clarification.request.schema.json +0 -52
- package/schemas/envelopes/error.schema.json +0 -30
- package/schemas/envelopes/media.audio.schema.json +0 -38
- package/schemas/envelopes/media.file.schema.json +0 -37
- package/schemas/envelopes/media.image.schema.json +0 -33
- package/schemas/envelopes/schema.request.schema.json +0 -26
- package/schemas/envelopes/schema.response.schema.json +0 -22
- package/schemas/envelopes/ui.a2ui-surface.schema.json +0 -154
- package/schemas/error-envelope.schema.json +0 -25
- package/schemas/eval-summary.schema.json +0 -92
- package/schemas/export-bundle.schema.json +0 -66
- package/schemas/form-content-pack-manifest.schema.json +0 -243
- package/schemas/frontend-plugin-manifest.schema.json +0 -109
- package/schemas/goal.schema.json +0 -104
- package/schemas/heartbeat-evaluated.schema.json +0 -14
- package/schemas/heartbeat-state-changed.schema.json +0 -14
- package/schemas/localized-content-language-settings.schema.json +0 -26
- package/schemas/localized-content-page-response.schema.json +0 -60
- package/schemas/localized-content-page.schema.json +0 -62
- package/schemas/localized-content-section.schema.json +0 -51
- package/schemas/memory-entry.schema.json +0 -36
- package/schemas/memory-list-options.schema.json +0 -37
- package/schemas/node-pack-manifest.schema.json +0 -654
- package/schemas/orchestrator-decision.schema.json +0 -90
- package/schemas/org-chart-responsibility-view.schema.json +0 -26
- package/schemas/pack-lockfile.schema.json +0 -108
- package/schemas/prompt-kind.schema.json +0 -8
- package/schemas/prompt-pack-manifest.schema.json +0 -117
- package/schemas/prompt-ref.schema.json +0 -40
- package/schemas/prompt-template.schema.json +0 -149
- package/schemas/proposal.schema.json +0 -84
- package/schemas/registry-version-manifest.schema.json +0 -208
- package/schemas/residency.schema.json +0 -16
- package/schemas/run-ancestry-response.schema.json +0 -54
- package/schemas/run-diff-response.schema.json +0 -64
- package/schemas/run-event-payloads.schema.json +0 -4452
- package/schemas/run-event.schema.json +0 -211
- package/schemas/run-options.schema.json +0 -80
- package/schemas/run-orchestrator-decided-event.schema.json +0 -20
- package/schemas/run-snapshot.schema.json +0 -148
- package/schemas/security-advisory.schema.json +0 -109
- package/schemas/self-hosted-runner-dispatch-frame.schema.json +0 -79
- package/schemas/self-hosted-runner-registration.schema.json +0 -53
- package/schemas/self-hosted-runner-result-frame.schema.json +0 -38
- package/schemas/subject.schema.json +0 -56
- package/schemas/suspend-request.schema.json +0 -207
- package/schemas/tool-descriptor.schema.json +0 -63
- package/schemas/trigger-event.schema.json +0 -222
- package/schemas/trigger-subscription-registration.schema.json +0 -67
- package/schemas/trigger-subscription.schema.json +0 -26
- package/schemas/ui-plugin-message.schema.json +0 -90
- package/schemas/workflow-chain-pack-manifest.schema.json +0 -615
- package/schemas/workflow-definition.schema.json +0 -850
- package/schemas/workload-identity.schema.json +0 -79
- package/schemas/workspace-file-create.schema.json +0 -20
- package/schemas/workspace-file.schema.json +0 -39
|
@@ -50,6 +50,8 @@ import {
|
|
|
50
50
|
type WorkflowChain,
|
|
51
51
|
type DeferredExpansionContext,
|
|
52
52
|
} from '../lib/workflow-chain-expansion.js';
|
|
53
|
+
import { req } from '../lib/requirement-ids.js';
|
|
54
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
53
55
|
|
|
54
56
|
const CAPS = join(SCHEMAS_DIR, 'capabilities.schema.json');
|
|
55
57
|
const MANIFEST = join(SCHEMAS_DIR, 'workflow-chain-pack-manifest.schema.json');
|
|
@@ -59,10 +61,6 @@ const MANIFEST = join(SCHEMAS_DIR, 'workflow-chain-pack-manifest.schema.json');
|
|
|
59
61
|
// repo-layout only: `null` in the published layout and skipped, never thrown.
|
|
60
62
|
const CHAIN_DOC: string | null = V1_DIR === null ? null : join(V1_DIR, 'workflow-chain-packs.md');
|
|
61
63
|
|
|
62
|
-
/** Spec-cited assertion message. Unlike `driver.describe`, this does NOT load
|
|
63
|
-
* env, so it is safe in always-on server-free legs (no OPENWOP_BASE_URL). */
|
|
64
|
-
const cite = (section: string, requirement: string): string => `${section} — ${requirement}`;
|
|
65
|
-
|
|
66
64
|
const resolvable = () => true;
|
|
67
65
|
const HOST_FULL = { promptVariableSource: true, secretsSupported: true };
|
|
68
66
|
|
|
@@ -112,13 +110,13 @@ describe('workflow-chain-deferred: non-sensitive deferral (server-free, RFC 0124
|
|
|
112
110
|
|
|
113
111
|
expect(
|
|
114
112
|
hasResidualToken({ nodes: out.nodes, edges: out.edges }),
|
|
115
|
-
|
|
113
|
+
req('openwop.it.workflow-chain-deferred-parameters.materializes-variables-with-defaultvalue-type-and-leaves-zero-params-tokens-r3', 'workflow-chain-packs.md §Deferred-parameter expansion', 'the persisted fragment MUST contain zero {{params.*}} tokens (R3 portability)'),
|
|
116
114
|
).toBe(false);
|
|
117
115
|
|
|
118
116
|
const topicVar = out.variables.find((v) => v.name === 'topic');
|
|
119
|
-
expect(topicVar, 'topic MUST be materialized as a top-level variable').toBeDefined();
|
|
120
|
-
expect(topicVar!.defaultValue, 'author input becomes defaultValue').toBe('sales');
|
|
121
|
-
expect(topicVar!.type, 'type copied from the parameter schema').toBe('string');
|
|
117
|
+
expect(topicVar, req('openwop.it.workflow-chain-deferred-parameters.materializes-variables-with-defaultvalue-type-and-leaves-zero-params-tokens-r3', 'workflow-chain-packs.md', 'topic MUST be materialized as a top-level variable')).toBeDefined();
|
|
118
|
+
expect(topicVar!.defaultValue, req('openwop.it.workflow-chain-deferred-parameters.materializes-variables-with-defaultvalue-type-and-leaves-zero-params-tokens-r3', 'workflow-chain-packs.md', 'author input becomes defaultValue')).toBe('sales');
|
|
119
|
+
expect(topicVar!.type, req('openwop.it.workflow-chain-deferred-parameters.materializes-variables-with-defaultvalue-type-and-leaves-zero-params-tokens-r3', 'workflow-chain-packs.md', 'type copied from the parameter schema')).toBe('string');
|
|
122
120
|
|
|
123
121
|
// prompt token → {{varName}} + source:"variable"
|
|
124
122
|
expect((out.nodes[0].config as { systemPrompt: string }).systemPrompt).toBe('Hello {{topic}}');
|
|
@@ -133,7 +131,7 @@ describe('workflow-chain-deferred: non-sensitive deferral (server-free, RFC 0124
|
|
|
133
131
|
const out = expandChainDeferred(c, deferCtx({ count: 5 }, c.parameters as never));
|
|
134
132
|
expect(
|
|
135
133
|
out.nodes[0].inputs!.limit,
|
|
136
|
-
|
|
134
|
+
req('openwop.it.workflow-chain-deferred-parameters.a-whole-value-input-token-becomes-a-variable-sourced-portvalue-wcp2-raw-typed', 'workflow-chain-packs.md §Deferred-parameter expansion', 'a whole-value {{params.x}} input rewrites to a variable-sourced PortValue, not a stringified token'),
|
|
137
135
|
).toEqual({ source: 'variable', variable: 'count' });
|
|
138
136
|
});
|
|
139
137
|
|
|
@@ -145,7 +143,7 @@ describe('workflow-chain-deferred: non-sensitive deferral (server-free, RFC 0124
|
|
|
145
143
|
const out = expandChainDeferred(c, deferCtx({ topic: 't' }, c.parameters as never));
|
|
146
144
|
expect(
|
|
147
145
|
out.configurableSchema.properties.topic,
|
|
148
|
-
|
|
146
|
+
req('openwop.it.workflow-chain-deferred-parameters.the-bare-param-name-is-the-override-key-in-the-auto-generated-configurableschema', 'workflow-chain-packs.md §Override key + variable naming', 'the bare parameter name is the normative override key'),
|
|
149
147
|
).toEqual({ type: 'string' });
|
|
150
148
|
});
|
|
151
149
|
});
|
|
@@ -162,14 +160,14 @@ describe('workflow-chain-deferred: §Security sensitive-parameter MUSTs (server-
|
|
|
162
160
|
|
|
163
161
|
expect(
|
|
164
162
|
out.promptVariables.find((p) => p.name === 'apiKey')?.source,
|
|
165
|
-
|
|
163
|
+
req('openwop.it.workflow-chain-deferred-parameters.a-sensitive-param-in-a-prompt-body-materializes-as-source-secret-no-plaintext-de', 'workflow-chain-packs.md §Security', 'a sensitive prompt-body param MUST bind source:"secret" (not source:"variable")'),
|
|
166
164
|
).toBe('secret');
|
|
167
165
|
// The plaintext secret MUST appear NOWHERE — not as a variable defaultValue, not in the fragment.
|
|
168
166
|
expect(
|
|
169
167
|
JSON.stringify(out).includes('sk-PLAINTEXT-SECRET'),
|
|
170
|
-
|
|
168
|
+
req('openwop.it.workflow-chain-deferred-parameters.a-sensitive-param-in-a-prompt-body-materializes-as-source-secret-no-plaintext-de', 'workflow-chain-packs.md §Security', 'the sensitive value MUST NOT be materialized into variables[] or the persisted fragment (never bagged, SR-1)'),
|
|
171
169
|
).toBe(false);
|
|
172
|
-
expect(out.variables.find((v) => v.name === 'apiKey'), 'sensitive param is NOT a plaintext top-level variable').toBeUndefined();
|
|
170
|
+
expect(out.variables.find((v) => v.name === 'apiKey'), req('openwop.it.workflow-chain-deferred-parameters.a-sensitive-param-in-a-prompt-body-materializes-as-source-secret-no-plaintext-de', 'workflow-chain-packs.md', 'sensitive param is NOT a plaintext top-level variable')).toBeUndefined();
|
|
173
171
|
});
|
|
174
172
|
|
|
175
173
|
it('a sensitive param in a whole-value node.input FAILS CLOSED (sensitive_param_not_deferrable, 422)', () => {
|
|
@@ -183,10 +181,10 @@ describe('workflow-chain-deferred: §Security sensitive-parameter MUSTs (server-
|
|
|
183
181
|
} catch (e) {
|
|
184
182
|
err = e;
|
|
185
183
|
}
|
|
186
|
-
expect(err, 'a sensitive whole-value input MUST throw, not defer').toBeInstanceOf(SensitiveParamNotDeferrableError);
|
|
184
|
+
expect(err, req('openwop.it.workflow-chain-deferred-parameters.a-sensitive-param-in-a-whole-value-node-input-fails-closed-sensitive-param-not-d', 'workflow-chain-packs.md', 'a sensitive whole-value input MUST throw, not defer')).toBeInstanceOf(SensitiveParamNotDeferrableError);
|
|
187
185
|
expect(
|
|
188
186
|
(err as SensitiveParamNotDeferrableError).code,
|
|
189
|
-
|
|
187
|
+
req('openwop.it.workflow-chain-deferred-parameters.a-sensitive-param-in-a-whole-value-node-input-fails-closed-sensitive-param-not-d', 'workflow-chain-packs.md §Security', 'a sensitive param outside a prompt body MUST fail closed with sensitive_param_not_deferrable'),
|
|
190
188
|
).toBe('sensitive_param_not_deferrable');
|
|
191
189
|
expect((err as SensitiveParamNotDeferrableError).httpStatus).toBe(422);
|
|
192
190
|
});
|
|
@@ -204,7 +202,7 @@ describe('workflow-chain-deferred: §Security sensitive-parameter MUSTs (server-
|
|
|
204
202
|
}
|
|
205
203
|
expect(
|
|
206
204
|
err,
|
|
207
|
-
|
|
205
|
+
req('openwop.it.workflow-chain-deferred-parameters.a-sensitive-prompt-body-param-on-a-host-without-secrets-support-fails-closed-422', 'workflow-chain-packs.md §Security', 'a host lacking capabilities.secrets MUST NOT plaintext-defer a sensitive param — fail closed'),
|
|
208
206
|
).toBeInstanceOf(SensitiveParamNotDeferrableError);
|
|
209
207
|
});
|
|
210
208
|
|
|
@@ -219,7 +217,7 @@ describe('workflow-chain-deferred: §Security sensitive-parameter MUSTs (server-
|
|
|
219
217
|
);
|
|
220
218
|
// Fallback resolves the token at expansion time — no {{topic}} slot, value inlined, still zero {{params.*}}.
|
|
221
219
|
expect((out.nodes[0].config as { systemPrompt: string }).systemPrompt).toBe('Hi sales');
|
|
222
|
-
expect(hasResidualToken({ nodes: out.nodes }), 'no residual {{params.*}} even on the fallback path').toBe(false);
|
|
220
|
+
expect(hasResidualToken({ nodes: out.nodes }), req('openwop.it.workflow-chain-deferred-parameters.negative-capability-no-prompts-variable-source-prompt-token-falls-back-to-expans', 'workflow-chain-packs.md', 'no residual {{params.*}} even on the fallback path')).toBe(false);
|
|
223
221
|
});
|
|
224
222
|
});
|
|
225
223
|
|
|
@@ -227,29 +225,29 @@ describe('workflow-chain-deferred: schema + spec surface (always-on, server-free
|
|
|
227
225
|
it('capabilities.schema.json §workflowChainPacks.deferredParameters requires supported:boolean', () => {
|
|
228
226
|
const raw = readFileSync(CAPS, 'utf8');
|
|
229
227
|
// The deferredParameters block MUST parse as part of the capabilities schema.
|
|
230
|
-
expect(() => JSON.parse(raw), 'capabilities.schema.json MUST be valid JSON').not.toThrow();
|
|
231
|
-
expect(raw.includes('"deferredParameters"'), 'the deferredParameters capability block MUST exist').toBe(true);
|
|
228
|
+
expect(() => JSON.parse(raw), req('openwop.it.workflow-chain-deferred-parameters.capabilities-schema-json-workflowchainpacks-deferredparameters-requires-supporte', 'workflow-chain-packs.md', 'capabilities.schema.json MUST be valid JSON')).not.toThrow();
|
|
229
|
+
expect(raw.includes('"deferredParameters"'), req('openwop.it.workflow-chain-deferred-parameters.capabilities-schema-json-workflowchainpacks-deferredparameters-requires-supporte', 'workflow-chain-packs.md', 'the deferredParameters capability block MUST exist')).toBe(true);
|
|
232
230
|
expect(
|
|
233
231
|
raw.includes('sensitive_param_not_deferrable'),
|
|
234
|
-
|
|
232
|
+
req('openwop.it.workflow-chain-deferred-parameters.capabilities-schema-json-workflowchainpacks-deferredparameters-requires-supporte', 'capabilities.schema.json §deferredParameters', 'the capability description MUST reference the fail-closed sensitive rule'),
|
|
235
233
|
).toBe(true);
|
|
236
234
|
});
|
|
237
235
|
|
|
238
236
|
it('workflow-chain-pack-manifest.schema.json documents x-openwop-sensitive with the source:"secret" MUST', () => {
|
|
239
237
|
const raw = readFileSync(MANIFEST, 'utf8');
|
|
240
|
-
expect(raw.includes('x-openwop-sensitive'), 'the manifest schema MUST recognize x-openwop-sensitive').toBe(true);
|
|
238
|
+
expect(raw.includes('x-openwop-sensitive'), req('openwop.it.workflow-chain-deferred-parameters.workflow-chain-pack-manifest-schema-json-documents-x-openwop-sensitive-with-the', 'workflow-chain-packs.md', 'the manifest schema MUST recognize x-openwop-sensitive')).toBe(true);
|
|
241
239
|
expect(
|
|
242
240
|
raw.includes('source:"secret"') || raw.includes('source:\\"secret\\"'),
|
|
243
|
-
|
|
241
|
+
req('openwop.it.workflow-chain-deferred-parameters.workflow-chain-pack-manifest-schema-json-documents-x-openwop-sensitive-with-the', 'workflow-chain-pack-manifest.schema.json', 'the x-openwop-sensitive description MUST reflect the amended source:"secret" MUST, not the stale plaintext-variable wording'),
|
|
244
242
|
).toBe(true);
|
|
245
243
|
});
|
|
246
244
|
|
|
247
245
|
it.skipIf(CHAIN_DOC === null)('the spec pins the error code + the per-run credentialRef (not plaintext) supply shape', () => {
|
|
248
246
|
const spec = readFileSync(CHAIN_DOC as string, 'utf8');
|
|
249
|
-
expect(spec.includes('sensitive_param_not_deferrable'), 'error code MUST be documented').toBe(true);
|
|
247
|
+
expect(spec.includes('sensitive_param_not_deferrable'), req('openwop.it.workflow-chain-deferred-parameters.the-spec-pins-the-error-code-the-per-run-credentialref-not-plaintext-supply-shap', 'workflow-chain-packs.md', 'error code MUST be documented')).toBe(true);
|
|
250
248
|
expect(
|
|
251
249
|
spec.includes('credentialRef'),
|
|
252
|
-
|
|
250
|
+
req('openwop.it.workflow-chain-deferred-parameters.the-spec-pins-the-error-code-the-per-run-credentialref-not-plaintext-supply-shap', 'workflow-chain-packs.md §Security', 'per-run supply of a sensitive param MUST be a credentialRef, not plaintext'),
|
|
253
251
|
).toBe(true);
|
|
254
252
|
});
|
|
255
253
|
});
|
|
@@ -265,20 +263,20 @@ describe('workflow-chain-deferred: host behavior (capability-gated, RFC 0124)',
|
|
|
265
263
|
override: { topic: 'run-topic' },
|
|
266
264
|
fork: true,
|
|
267
265
|
});
|
|
268
|
-
if (res.status === 404 || res.status === 403) return; // seam unwired — soft-skip
|
|
266
|
+
if (res.status === 404 || res.status === 403) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 403` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
269
267
|
|
|
270
268
|
const body = res.json as { resolved?: string; forkResolved?: string; contentTrust?: string } | undefined;
|
|
271
269
|
expect(
|
|
272
270
|
body?.resolved,
|
|
273
|
-
|
|
271
|
+
req('openwop.it.workflow-chain-deferred-parameters.a-deferred-round-trip-bare-param-configurable-override-changes-the-value-fork-re', 'workflow-chain-packs.md §Deferred-parameter expansion', 'a bare-param configurable override rebinds the resolved value'),
|
|
274
272
|
).toBe('run-topic');
|
|
275
273
|
expect(
|
|
276
274
|
body?.forkResolved,
|
|
277
|
-
|
|
275
|
+
req('openwop.it.workflow-chain-deferred-parameters.a-deferred-round-trip-bare-param-configurable-override-changes-the-value-fork-re', 'replay.md §Determinism', ':fork replays the same bound value (RunSnapshot.variables byte-equivalence, R4)'),
|
|
278
276
|
).toBe('run-topic');
|
|
279
277
|
expect(
|
|
280
278
|
body?.contentTrust,
|
|
281
|
-
|
|
279
|
+
req('openwop.it.workflow-chain-deferred-parameters.a-deferred-round-trip-bare-param-configurable-override-changes-the-value-fork-re', 'workflow-chain-packs.md §Deferred-parameter expansion step 4', 'a deferred-variable prompt binding composes contentTrust:"untrusted" (R1)'),
|
|
282
280
|
).toBe('untrusted');
|
|
283
281
|
});
|
|
284
282
|
|
|
@@ -291,12 +289,12 @@ describe('workflow-chain-deferred: host behavior (capability-gated, RFC 0124)',
|
|
|
291
289
|
sensitiveParam: 'apiKey',
|
|
292
290
|
credentialRef: 'cred-123',
|
|
293
291
|
});
|
|
294
|
-
if (res.status === 404 || res.status === 403) return; // seam unwired — soft-skip
|
|
292
|
+
if (res.status === 404 || res.status === 403) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 403` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
295
293
|
|
|
296
294
|
const body = res.json as { composed?: string } | undefined;
|
|
297
295
|
expect(
|
|
298
296
|
body?.composed?.includes('[REDACTED'),
|
|
299
|
-
|
|
297
|
+
req('openwop.it.workflow-chain-deferred-parameters.a-sensitive-param-composes-as-redacted-credentialref-and-the-plaintext-appears-n', 'workflow-chain-packs.md §Security', 'a source:"secret" sensitive var MUST redact to [REDACTED:<credentialRef>] in prompt.composed'),
|
|
300
298
|
).toBe(true);
|
|
301
299
|
});
|
|
302
300
|
});
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
import { describe, it, expect } from 'vitest';
|
|
36
36
|
import { expandChain, type WorkflowChain } from '../lib/workflow-chain-expansion.js';
|
|
37
|
+
import { req } from '../lib/requirement-ids.js';
|
|
37
38
|
|
|
38
39
|
/** Index helper that narrows away the `T | undefined` from `noUncheckedIndexedAccess`
|
|
39
40
|
* with an actionable error message — replaces `arr[i]!` non-null assertions so a
|
|
@@ -95,7 +96,7 @@ describe('category: workflow-chain expansion — placeholder substitution', () =
|
|
|
95
96
|
const config = at(fragment.nodes, 0, 'fragment.nodes').config as { systemPrompt: string };
|
|
96
97
|
expect(
|
|
97
98
|
config.systemPrompt,
|
|
98
|
-
'Per workflow-chain-packs.md §"Parameter substitution": placeholders MUST be substituted literally at expansion time.',
|
|
99
|
+
req('openwop.it.workflow-chain-expansion.substitutes-params-x-placeholders-literally-in-config-strings', 'workflow-chain-packs.md', 'Per workflow-chain-packs.md §"Parameter substitution": placeholders MUST be substituted literally at expansion time.'),
|
|
99
100
|
).toBe('Write a PRD for: AI-powered toaster\nAudience: restaurant chains');
|
|
100
101
|
});
|
|
101
102
|
|
|
@@ -130,11 +131,11 @@ describe('category: workflow-chain expansion — placeholder substitution', () =
|
|
|
130
131
|
};
|
|
131
132
|
expect(
|
|
132
133
|
config.outer.middle.message,
|
|
133
|
-
'Recursive substitution MUST walk into nested objects (per spec §Parameter substitution: "Substitution MUST recurse into nested string values within `config` and `inputs`").',
|
|
134
|
+
req('openwop.it.workflow-chain-expansion.recurses-into-nested-object-structures-in-config', 'workflow-chain-packs.md', 'Recursive substitution MUST walk into nested objects (per spec §Parameter substitution: "Substitution MUST recurse into nested string values within `config` and `inputs`").'),
|
|
134
135
|
).toBe('Hello world');
|
|
135
136
|
expect(
|
|
136
137
|
config.outer.middle.tags,
|
|
137
|
-
'Recursive substitution MUST also walk into array elements when those elements are strings.',
|
|
138
|
+
req('openwop.it.workflow-chain-expansion.recurses-into-nested-object-structures-in-config', 'workflow-chain-packs.md', 'Recursive substitution MUST also walk into array elements when those elements are strings.'),
|
|
138
139
|
).toEqual(['static', 'world-tag']);
|
|
139
140
|
});
|
|
140
141
|
|
|
@@ -156,7 +157,7 @@ describe('category: workflow-chain expansion — placeholder substitution', () =
|
|
|
156
157
|
{ expansionId: 'x', params: {}, isTypeIdResolvable: RESOLVE_ALL },
|
|
157
158
|
);
|
|
158
159
|
const config = at(fragment.nodes, 0, 'fragment.nodes').config as { literal: string; empty: string };
|
|
159
|
-
expect(config.literal).toBe('no placeholders here');
|
|
160
|
+
expect(config.literal, req('openwop.it.workflow-chain-expansion.leaves-non-placeholder-strings-untouched', 'workflow-chain-packs.md', 'leaves non-placeholder strings untouched')).toBe('no placeholders here');
|
|
160
161
|
expect(config.empty).toBe('');
|
|
161
162
|
});
|
|
162
163
|
|
|
@@ -178,7 +179,7 @@ describe('category: workflow-chain expansion — placeholder substitution', () =
|
|
|
178
179
|
{ expansionId: 'x', params: { who: 'Ada' }, isTypeIdResolvable: RESOLVE_ALL },
|
|
179
180
|
);
|
|
180
181
|
const config = at(fragment.nodes, 0, 'fragment.nodes').config as { a: string; b: string; joined: string };
|
|
181
|
-
expect(config.a).toBe('Ada');
|
|
182
|
+
expect(config.a, req('openwop.it.workflow-chain-expansion.substitutes-the-same-placeholder-name-in-multiple-positions', 'workflow-chain-packs.md', 'substitutes the same placeholder name in multiple positions')).toBe('Ada');
|
|
182
183
|
expect(config.b).toBe('Ada');
|
|
183
184
|
expect(config.joined).toBe('hi Ada, hi again Ada');
|
|
184
185
|
});
|
|
@@ -231,7 +232,7 @@ describe('category: workflow-chain expansion — whole-value typed resolution',
|
|
|
231
232
|
};
|
|
232
233
|
expect(
|
|
233
234
|
config.retryPolicy,
|
|
234
|
-
'Per workflow-chain-packs.md §"Parameter substitution": a value that is EXACTLY one `{{params.x}}` token MUST resolve to the raw typed value — an object param MUST NOT be stringified to "[object Object]".',
|
|
235
|
+
req('openwop.it.workflow-chain-expansion.resolves-a-whole-value-params-x-token-to-the-raw-typed-value-object-array-number', 'workflow-chain-packs.md', 'Per workflow-chain-packs.md §"Parameter substitution": a value that is EXACTLY one `{{params.x}}` token MUST resolve to the raw typed value — an object param MUST NOT be stringified to "[object Object]".'),
|
|
235
236
|
).toEqual({ attempts: 3, backoff: 'exponential' });
|
|
236
237
|
expect(config.allowlist).toEqual(['a', 'b']);
|
|
237
238
|
expect(config.maxTokens).toBe(4096);
|
|
@@ -255,7 +256,7 @@ describe('category: workflow-chain expansion — whole-value typed resolution',
|
|
|
255
256
|
const config = at(fragment.nodes, 0, 'fragment.nodes').config as { label: string };
|
|
256
257
|
expect(
|
|
257
258
|
config.label,
|
|
258
|
-
'A token embedded in surrounding text MUST do literal string substitution (the numeric param coerces to its string form).',
|
|
259
|
+
req('openwop.it.workflow-chain-expansion.does-literal-string-coercion-for-an-embedded-token-in-surrounding-text', 'workflow-chain-packs.md', 'A token embedded in surrounding text MUST do literal string substitution (the numeric param coerces to its string form).'),
|
|
259
260
|
).toBe('items: 42');
|
|
260
261
|
});
|
|
261
262
|
});
|
|
@@ -290,7 +291,7 @@ describe('category: workflow-chain expansion — inputs preservation', () => {
|
|
|
290
291
|
};
|
|
291
292
|
expect(
|
|
292
293
|
inputs.prompt,
|
|
293
|
-
'Per workflow-chain-packs.md §"Parameter substitution": expansion MUST preserve a present `node.inputs` (PortValue references) verbatim — only `{{params.*}}` tokens inside its string leaves are substituted.',
|
|
294
|
+
req('openwop.it.workflow-chain-expansion.preserves-a-present-node-inputs-portvalue-references-through-expansion', 'workflow-chain-packs.md', 'Per workflow-chain-packs.md §"Parameter substitution": expansion MUST preserve a present `node.inputs` (PortValue references) verbatim — only `{{params.*}}` tokens inside its string leaves are substituted.'),
|
|
294
295
|
).toEqual({ sourceNodeId: 'upstream', sourcePort: 'text' });
|
|
295
296
|
expect(inputs.seed).toBe('xyz');
|
|
296
297
|
});
|
|
@@ -310,7 +311,7 @@ describe('category: workflow-chain expansion — node id collision avoidance', (
|
|
|
310
311
|
});
|
|
311
312
|
expect(
|
|
312
313
|
at(first.nodes, 0, 'first.nodes').id,
|
|
313
|
-
'Per spec §"Expansion semantics" step 6: each expansion MUST use a unique `expansionId` prefix so multi-expansion DAGs do not collide.',
|
|
314
|
+
req('openwop.it.workflow-chain-expansion.same-chain-expanded-twice-in-one-parent-workflow-produces-non-colliding-node-ids', 'workflow-chain-packs.md', 'Per spec §"Expansion semantics" step 6: each expansion MUST use a unique `expansionId` prefix so multi-expansion DAGs do not collide.'),
|
|
314
315
|
).not.toBe(at(second.nodes, 0, 'second.nodes').id);
|
|
315
316
|
expect(at(first.nodes, 0, 'first.nodes').id).toBe('vendor_acme_generatePRD_a8f3_prd-call');
|
|
316
317
|
expect(at(second.nodes, 0, 'second.nodes').id).toBe('vendor_acme_generatePRD_b9e4_prd-call');
|
|
@@ -324,7 +325,7 @@ describe('category: workflow-chain expansion — node id collision avoidance', (
|
|
|
324
325
|
});
|
|
325
326
|
expect(
|
|
326
327
|
at(fragment.nodes, 0, 'fragment.nodes').id,
|
|
327
|
-
'chainId `vendor.acme.generatePRD` MUST be slugged (dots → underscores) so the resulting id is safe to use in storage backends that reserve `.` for hierarchical keys.',
|
|
328
|
+
req('openwop.it.workflow-chain-expansion.rewritten-ids-replace-dots-in-chainid-with-underscores-for-storage-key-safety', 'workflow-chain-packs.md', 'chainId `vendor.acme.generatePRD` MUST be slugged (dots → underscores) so the resulting id is safe to use in storage backends that reserve `.` for hierarchical keys.'),
|
|
328
329
|
).toMatch(/^vendor_acme_generatePRD_/);
|
|
329
330
|
expect(at(fragment.nodes, 0, 'fragment.nodes').id).not.toMatch(/\./);
|
|
330
331
|
});
|
|
@@ -337,7 +338,7 @@ describe('category: workflow-chain expansion — node id collision avoidance', (
|
|
|
337
338
|
});
|
|
338
339
|
expect(
|
|
339
340
|
fragment.idMap.get('prd-call'),
|
|
340
|
-
'The expansion contract MUST surface the original-id → rewritten-id map so the caller can route adjacent parent-workflow edges to the right expanded node.',
|
|
341
|
+
req('openwop.it.workflow-chain-expansion.exposes-idmap-so-the-caller-can-wire-parent-workflow-edges-into-the-expansion', 'workflow-chain-packs.md', 'The expansion contract MUST surface the original-id → rewritten-id map so the caller can route adjacent parent-workflow edges to the right expanded node.'),
|
|
341
342
|
).toBe('vendor_acme_generatePRD_a8f3_prd-call');
|
|
342
343
|
});
|
|
343
344
|
});
|
|
@@ -367,7 +368,7 @@ describe('category: workflow-chain expansion — edge rewriting', () => {
|
|
|
367
368
|
expect(fragment.edges).toHaveLength(1);
|
|
368
369
|
expect(
|
|
369
370
|
at(fragment.edges, 0, 'fragment.edges').from,
|
|
370
|
-
'Edge `from` ids that match fragment node ids MUST be rewritten with the same prefix as the nodes themselves.',
|
|
371
|
+
req('openwop.it.workflow-chain-expansion.rewrites-from-and-to-ids-that-reference-fragment-nodes', 'workflow-chain-packs.md', 'Edge `from` ids that match fragment node ids MUST be rewritten with the same prefix as the nodes themselves.'),
|
|
371
372
|
).toBe('vendor_acme_twoStep_e1_first');
|
|
372
373
|
expect(at(fragment.edges, 0, 'fragment.edges').to).toBe('vendor_acme_twoStep_e1_second');
|
|
373
374
|
});
|
|
@@ -387,7 +388,7 @@ describe('category: workflow-chain expansion — edge rewriting', () => {
|
|
|
387
388
|
});
|
|
388
389
|
expect(
|
|
389
390
|
at(fragment.edges, 0, 'fragment.edges').from,
|
|
390
|
-
'Port-name suffix (after the `.`) MUST be preserved verbatim during id rewriting.',
|
|
391
|
+
req('openwop.it.workflow-chain-expansion.preserves-port-name-suffix-nodeid-portname-when-rewriting-edge-refs', 'workflow-chain-packs.md', 'Port-name suffix (after the `.`) MUST be preserved verbatim during id rewriting.'),
|
|
391
392
|
).toBe('vendor_acme_twoStep_e2_first.out');
|
|
392
393
|
expect(at(fragment.edges, 0, 'fragment.edges').to).toBe('vendor_acme_twoStep_e2_second.in');
|
|
393
394
|
});
|
|
@@ -413,7 +414,7 @@ describe('category: workflow-chain expansion — edge rewriting', () => {
|
|
|
413
414
|
});
|
|
414
415
|
expect(
|
|
415
416
|
at(fragment.edges, 0, 'fragment.edges').from,
|
|
416
|
-
'Edge refs to nodes OUTSIDE the fragment MUST pass through unchanged so the parent host can wire adjacent edges post-splice.',
|
|
417
|
+
req('openwop.it.workflow-chain-expansion.leaves-edge-refs-alone-when-they-don-t-match-a-fragment-node-id', 'workflow-chain-packs.md', 'Edge refs to nodes OUTSIDE the fragment MUST pass through unchanged so the parent host can wire adjacent edges post-splice.'),
|
|
417
418
|
).toBe('parent-upstream');
|
|
418
419
|
expect(at(fragment.edges, 0, 'fragment.edges').to).toBe('vendor_acme_twoStep_e3_first');
|
|
419
420
|
expect(at(fragment.edges, 1, 'fragment.edges').from).toBe('vendor_acme_twoStep_e3_second');
|
|
@@ -449,11 +450,11 @@ describe('category: workflow-chain expansion — edge rewriting', () => {
|
|
|
449
450
|
expect(edge.to).toBe('vendor_acme_twoStep_e4_second');
|
|
450
451
|
expect(
|
|
451
452
|
edge.condition,
|
|
452
|
-
'Per RFC 0013 amendment (2026-07-03): a present `FragmentEdge.condition` MUST be carried onto the expanded WorkflowEdge (not dropped at expansion).',
|
|
453
|
+
req('openwop.it.workflow-chain-expansion.preserves-edge-condition-and-triggerrule-onto-the-expanded-workflowedge-rfc-0013', 'RFC 0013', 'Per RFC 0013 amendment (2026-07-03): a present `FragmentEdge.condition` MUST be carried onto the expanded WorkflowEdge (not dropped at expansion).'),
|
|
453
454
|
).toEqual({ type: 'equals', left: 'status', right: 'ok' });
|
|
454
455
|
expect(
|
|
455
456
|
edge.triggerRule,
|
|
456
|
-
'Per RFC 0125 / workflow-chain-packs.md §"Expansion semantics": expansion MUST preserve `FragmentEdge.triggerRule` onto the resulting WorkflowEdge so the scheduler honors the fan-in / error-routing rule.',
|
|
457
|
+
req('openwop.it.workflow-chain-expansion.preserves-edge-condition-and-triggerrule-onto-the-expanded-workflowedge-rfc-0013', 'RFC 0013', 'Per RFC 0125 / workflow-chain-packs.md §"Expansion semantics": expansion MUST preserve `FragmentEdge.triggerRule` onto the resulting WorkflowEdge so the scheduler honors the fan-in / error-routing rule.'),
|
|
457
458
|
).toBe('all_complete');
|
|
458
459
|
});
|
|
459
460
|
|
|
@@ -465,7 +466,7 @@ describe('category: workflow-chain expansion — edge rewriting', () => {
|
|
|
465
466
|
});
|
|
466
467
|
expect(
|
|
467
468
|
at(fragment.edges, 0, 'fragment.edges').triggerRule,
|
|
468
|
-
'When a fragment edge declares no `triggerRule`, the expanded edge MUST NOT carry one — omission is identical to the `all_success` default (preserves wire-shape minimality + prior behavior).',
|
|
469
|
+
req('openwop.it.workflow-chain-expansion.omits-triggerrule-on-the-expanded-edge-when-the-fragment-edge-declares-none-defa', 'workflow-chain-packs.md', 'When a fragment edge declares no `triggerRule`, the expanded edge MUST NOT carry one — omission is identical to the `all_success` default (preserves wire-shape minimality + prior behavior).'),
|
|
469
470
|
).toBeUndefined();
|
|
470
471
|
});
|
|
471
472
|
});
|
|
@@ -491,7 +492,7 @@ describe('category: workflow-chain expansion — capability propagation', () =>
|
|
|
491
492
|
for (const node of fragment.nodes) {
|
|
492
493
|
expect(
|
|
493
494
|
node.capabilities,
|
|
494
|
-
'Per spec §"Expansion semantics" step 8: chain-level `capabilities[]` MUST be copied to every expanded node so existing capability gates (side-effect, streamable) apply uniformly.',
|
|
495
|
+
req('openwop.it.workflow-chain-expansion.copies-chain-capabilities-into-every-expanded-node-s-capabilities', 'workflow-chain-packs.md', 'Per spec §"Expansion semantics" step 8: chain-level `capabilities[]` MUST be copied to every expanded node so existing capability gates (side-effect, streamable) apply uniformly.'),
|
|
495
496
|
).toEqual(['side-effectful', 'cacheable']);
|
|
496
497
|
}
|
|
497
498
|
});
|
|
@@ -504,7 +505,7 @@ describe('category: workflow-chain expansion — capability propagation', () =>
|
|
|
504
505
|
});
|
|
505
506
|
expect(
|
|
506
507
|
at(fragment.nodes, 0, 'fragment.nodes').capabilities,
|
|
507
|
-
'When the chain declares no `capabilities[]`, expanded nodes MUST NOT carry an empty array (preserves wire-shape minimality).',
|
|
508
|
+
req('openwop.it.workflow-chain-expansion.omits-capabilities-on-expanded-nodes-when-the-chain-declares-none', 'workflow-chain-packs.md', 'When the chain declares no `capabilities[]`, expanded nodes MUST NOT carry an empty array (preserves wire-shape minimality).'),
|
|
508
509
|
).toBeUndefined();
|
|
509
510
|
});
|
|
510
511
|
});
|
|
@@ -519,7 +520,7 @@ describe('category: workflow-chain expansion — runtime-invariance contract', (
|
|
|
519
520
|
for (const node of fragment.nodes) {
|
|
520
521
|
expect(
|
|
521
522
|
node.typeId,
|
|
522
|
-
'Per workflow-chain-packs.md §"What hosts dispatch": dispatching runtimes see only concrete `core.*`/published-vendor typeIds — the chain reference MUST NOT be preserved at runtime.',
|
|
523
|
+
req('openwop.it.workflow-chain-expansion.expanded-fragment-carries-only-concrete-typeids-no-chain-reference-survives', 'workflow-chain-packs.md', 'Per workflow-chain-packs.md §"What hosts dispatch": dispatching runtimes see only concrete `core.*`/published-vendor typeIds — the chain reference MUST NOT be preserved at runtime.'),
|
|
523
524
|
).not.toMatch(/^vendor\.acme\.generatePRD$/);
|
|
524
525
|
expect(node.typeId).toBe('core.ai.callPrompt');
|
|
525
526
|
}
|
|
@@ -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
|
}
|