@openwop/openwop-conformance 2.45.23 → 2.45.25

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/fixtures.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # OpenWOP Conformance Suite — Fixture Workflow Contract
2
2
 
3
- > **Status: FINAL v1 (2026-05-10).** Defines the standardized fixture workflows every OpenWOP-compliant server MUST seed before the conformance suite can exercise run-lifecycle, idempotency, stream-mode, interrupt, and replay scenarios. Stable surface for external review. Keywords MUST, SHOULD, MAY follow [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119). Status legend per `../spec/v1/auth.md`.
3
+ > Defines the fixture workflows a host seeds so the conformance suite can exercise run lifecycle, idempotency, streaming, interrupts, replay and the other fixture-gated scenarios. RFC 2119 keywords (MUST, SHOULD, MAY) apply to hosts that seed these fixtures. Paths are written in v2 form (unversioned, [`spec/v2/core/versioning.md`](../spec/v2/core/versioning.md)); a v1 host serves the same operations under `/v1/`. `/v1/host/sample/...` paths below are the v1 host test seams; the v2 seams are in [`api/seams-v2.yaml`](../api/seams-v2.yaml).
4
4
 
5
5
  ---
6
6
 
@@ -8,16 +8,16 @@
8
8
 
9
9
  Run-lifecycle conformance tests need a stable target — a workflow whose `workflowId`, expected events, and terminal status are agreed in advance. Without this, every implementation defines its own test workflows and the conformance suite can't run cross-implementation.
10
10
 
11
- This document defines a small set of fixture workflows whose canonical definitions live alongside the conformance suite (`fixtures/*.json`). An OpenWOP-compliant server MUST seed these fixtures into its workflow store before running the conformance suite against itself.
11
+ This document defines the fixture workflows whose canonical definitions live alongside the conformance suite (`fixtures/*.json`). A host MUST seed the fixtures its advertised scenarios need into its workflow store before running the suite against itself.
12
12
 
13
13
  ---
14
14
 
15
15
  ## Seeding contract
16
16
 
17
- An OpenWOP-compliant server MUST:
17
+ A host that seeds the fixtures MUST:
18
18
 
19
19
  1. Accept the canonical JSON fixture definitions in `fixtures/*.json` verbatim (they validate against `../schemas/workflow-definition.schema.json`).
20
- 2. Persist each fixture under its declared `id` so that subsequent `GET /v1/workflows/{id}` returns the seeded definition.
20
+ 2. Persist each fixture under its declared `id` so that a subsequent `GET /workflows/{workflowId}` (`getWorkflow`) returns the seeded definition.
21
21
  3. Treat seeding as idempotent — running the seeder repeatedly MUST NOT produce duplicate runs, error states, or version drift.
22
22
  4. Expose the seeded fixtures to runs created with the conformance suite's API key.
23
23
 
@@ -36,7 +36,7 @@ Servers MUST NOT require fixtures to be re-uploaded on every conformance run —
36
36
  All fixtures MUST advertise:
37
37
 
38
38
  - **`workflowId`** — exact string clients use to start runs
39
- - **Trigger** — must be `manual` so the conformance suite can call `POST /v1/runs` without channel-specific setup
39
+ - **Trigger** — must be `manual` so the conformance suite can call `POST /runs` (`createRun`) without channel-specific setup
40
40
  - **Inputs** — schema declared via `variables[]`
41
41
  - **Expected behavior** — terminal status, expected event types, timing bounds
42
42
 
@@ -81,13 +81,13 @@ All fixtures MUST advertise:
81
81
  | Agent Pack Export | `conformance-agent-pack-export` | Phase 2 — workspace agents project to AgentManifest at `GET /v1/packs/export` | `completed` | ≤ 5s |
82
82
  | Agent Pack Provenance | `conformance-agent-pack-provenance` | Phase 2 — `sourceManifestId` provenance round-trip | `completed` | ≤ 10s |
83
83
  | Agent Pack Handoff Schema Validation | `conformance-agent-pack-handoff-schema-validation` | Phase 2 / HV-1 — host validates dispatch payloads against `handoff.taskSchemaRef` AND return payloads against `handoff.returnSchemaRef` per RFC 0003 §D. Three branches: valid-task → `completed`; invalid-task → `failed` with structured violation; mock-return-violation → violation surfaced before persistence. | varies by scenario | ≤ 5s |
84
- | WorkflowVariable format advisory | `conformance-workflow-variable-format-advisory` | RFC 0136 req 3 — a variable declares `format: "email"` with an off-format `defaultValue`; `format` is advisory, so the run MUST complete (a value/format mismatch MUST NOT fail the run). Run via `POST /v1/runs` (portable pre-registered pattern). `workflow-variable-format.test.ts` leg B2. | `completed` | ≤ 5s |
84
+ | WorkflowVariable format advisory | `conformance-workflow-variable-format-advisory` | RFC 0136 req 3 — a variable declares `format: "email"` with an off-format `defaultValue`; `format` is advisory, so the run MUST complete (a value/format mismatch MUST NOT fail the run). Run via `POST /runs` (portable pre-registered pattern). `workflow-variable-format.test.ts` leg B2. | `completed` | ≤ 5s |
85
85
  | Dispatch Input Mapping | `conformance-dispatch-input-mapping` | RFC 0022 §A / HVMAP-1a — host honors `inputMapping` on `core.dispatch`. Capability-gated on `capabilities.agents.dispatchMapping`. | `completed` | ≤ 5s |
86
86
  | Dispatch Output Mapping | `conformance-dispatch-output-mapping` | RFC 0022 §A / HVMAP-1b — host harvests child variables via `outputMapping` on `core.dispatch`. Capability-gated on `capabilities.agents.dispatchMapping`. | `completed` | ≤ 5s |
87
87
  | Dispatch Cross-Worker Handoff | `conformance-dispatch-cross-worker-handoff` | RFC 0022 §A / HVMAP-1c — sequential fan-out: child-a writes via `perWorkerOutputMappings`, child-b reads via `perWorkerInputMappings`. Capability-gated on `capabilities.agents.dispatchMapping`. | `completed` | ≤ 10s |
88
88
  | subWorkflow Input Mapping | `conformance-subworkflow-input-mapping` | RFC 0022 §B / HVMAP-2 — host honors `inputMapping` on `core.subWorkflow`; overrides matching `defaultValue` declarations on the child. Capability-gated on `capabilities.subWorkflow.inputMapping`. | `completed` | ≤ 10s |
89
- | subWorkflow Input Mapping (child) | `conformance-subworkflow-input-mapping-child` | RFC 0022 §B / HVMAP-2 — child workflow for the input-mapping scenario. Declares `receivedPrdId.defaultValue='baked-in'`; parent's `inputMapping` MUST override that default. Single noop node; final variables read via `GET /v1/runs/{runId}` for the assertion. | `completed` | ≤ 5s |
90
- | Dispatch Input Mapping (child) | `conformance-dispatch-input-mapping-child` | RFC 0022 §A / HVMAP-1a — child workflow for the dispatch input-mapping scenario. Single noop node; the scenario reads this child's `inputs_json` via `GET /v1/runs/{childRunId}` and asserts `inputs.childGreeting === 'Alice'`. | `completed` | ≤ 5s |
89
+ | subWorkflow Input Mapping (child) | `conformance-subworkflow-input-mapping-child` | RFC 0022 §B / HVMAP-2 — child workflow for the input-mapping scenario. Declares `receivedPrdId.defaultValue='baked-in'`; parent's `inputMapping` MUST override that default. Single noop node; final variables read via `GET /runs/{runId}` for the assertion. | `completed` | ≤ 5s |
90
+ | Dispatch Input Mapping (child) | `conformance-dispatch-input-mapping-child` | RFC 0022 §A / HVMAP-1a — child workflow for the dispatch input-mapping scenario. Single noop node; the scenario reads this child's `inputs_json` via `GET /runs/{childRunId}` and asserts `inputs.childGreeting === 'Alice'`. | `completed` | ≤ 5s |
91
91
  | Dispatch Output Mapping (child) | `conformance-dispatch-output-mapping-child` | RFC 0022 §A / HVMAP-1b — child workflow for the dispatch output-mapping scenario. Declares `childOutcome.defaultValue='done'`; on terminal, parent's `outputMapping` harvests `childOutcome → parentResult`. | `completed` | ≤ 5s |
92
92
  | Dispatch Cross-Worker Handoff (child-a) | `conformance-dispatch-cross-worker-handoff-child-a` | RFC 0022 §A / HVMAP-1c — first child of the cross-worker-handoff scenario. Declares `output.defaultValue='hello'`; on terminal, parent's `perWorkerOutputMappings.child-a` harvests `output → sharedVar`. | `completed` | ≤ 5s |
93
93
  | Dispatch Cross-Worker Handoff (child-b) | `conformance-dispatch-cross-worker-handoff-child-b` | RFC 0022 §A + §D / HVMAP-1c — second child of the cross-worker-handoff scenario. Sequential fan-out — runs after child-a; receives parent's `sharedVar` via `perWorkerInputMappings.child-b` onto its `input` input. Scenario reads child-b's `inputs_json` to assert `inputs.input === 'hello'`. | `completed` | ≤ 5s |
@@ -97,7 +97,7 @@ All fixtures MUST advertise:
97
97
  | subWorkflow Mid-Run Mutation (child) | `conformance-subworkflow-mid-run-mutation-child` | RFC 0022 §B / HVMAP-2-no-midrun-propagation — child workflow with a `core.approvalGate` that suspends so the parent can mutate its variable bag mid-run. Declares `receivedPrdId.defaultValue='baked-in'` (overridden at dispatch by inputMapping). | `completed` | ≤ 30s |
98
98
  | Dispatch Per-Worker Mapping Override | `conformance-dispatch-per-worker-override` | RFC 0022 §A / HVMAP-1c-override — parent with BOTH a default `inputMapping` (`{ input: 'defaultX' }`) AND `perWorkerInputMappings.child-b: { input: 'sharedVar' }`. Verifies `effectiveInputMapping` precedence per §A: child-a receives the default, child-b receives the override. Reuses `conformance-dispatch-cross-worker-handoff-child-a` + `-child-b`. | `completed` | ≤ 30s |
99
99
  | Dispatch deterministic-fail child | `conformance-dispatch-deterministic-fail-child` | RFC 0022 §B / HVMAP-1b-failed — child workflow that ALWAYS terminates `failed` via `core.fail`. Used by `conformance-dispatch-output-mapping` to verify the parent's `outputMapping` is SKIPPED when the child fails terminally. | `failed` | ≤ 5s |
100
- | Dispatch cancellable child | `conformance-dispatch-cancellable-child` | RFC 0022 §B / HVMAP-1b-cancelled — child workflow with a long `core.delay` so the test cancels it externally via `POST /v1/runs/{childRunId}/cancel`. Verifies the parent's `outputMapping` is SKIPPED when the child terminates `cancelled`. | `cancelled` | ≤ 60s |
100
+ | Dispatch cancellable child | `conformance-dispatch-cancellable-child` | RFC 0022 §B / HVMAP-1b-cancelled — child workflow with a long `core.delay` so the test cancels it externally via `POST /runs/{childRunId}/cancel`. Verifies the parent's `outputMapping` is SKIPPED when the child terminates `cancelled`. | `cancelled` | ≤ 60s |
101
101
  | Multi-Agent Handoff (parent) | `conformance-multi-agent-handoff` | RFC 0037 (`version: 1`) — exercises the planner→worker handoff state machine. Supervisor decides one `next-worker`, dispatch spawns the child, harvests outputMapping. Conformance reads the event log for the 4 `core.workflowChain.event` transition records in causation-chained order (`dispatch.began → dispatch.succeeded → child.completed → output.harvested`). Capability-gated on `capabilities.multiAgent.executionModel.supported`. | `completed` | ≤ 30s |
102
102
  | Multi-Agent Handoff (child) | `conformance-multi-agent-handoff-child` | RFC 0037 (`version: 1`) — child for `conformance-multi-agent-handoff`. Declares `childOutcome.defaultValue='handoff-complete'`; the parent's outputMapping harvests it onto `parentResult`, triggering the `output.harvested` transition event. | `completed` | ≤ 5s |
103
103
  | Multi-Agent Confidence Escalation | `conformance-multi-agent-confidence-escalation` | RFC 0039 §A (`version: 2`) — exercises the confidence-floor escalation contract. Supervisor's `mockDispatchPlan` carries ONE decision with `confidence: 0.3` (below the 0.5 spec floor). The host MUST emit `core.workflowChain.confidence-escalated` AND suspend with a clarification interrupt BEFORE any dispatch.began fires; conformance asserts zero `core.workflowChain.event` records (no dispatch). Capability-gated on `capabilities.multiAgent.executionModel.version >= 2`. | `waiting-clarification` | ≤ 30s |
@@ -126,7 +126,8 @@ All fixtures MUST advertise:
126
126
  | A2A Task Roundtrip | `conformance-a2a-task-roundtrip` | Track 6 — host consumes the conformance suite's synthetic A2A peer; covers drift points #3 (`AUTH_REQUIRED`) and #4 (`REJECTED`). Node `core.conformance.a2a-invoke` is a **conformance-RESERVED** typeId (renamed 2026-08-16 from `core.a2a.invoke`, a multi-segment `core.*` id no host shipped and the corpus never defined): a host that consumes A2A peers MUST map it to its A2A bridge node (a host-extension node — `a2a-integration.md` §"State projection"); a host that does not consume A2A MUST NOT advertise this fixture | `failed` or `waiting-input` (per `driftScenario` input) | ≤ 30s |
127
127
  | WASM Pack Roundtrip | `conformance-wasm-pack-roundtrip` | RFC 0008 — invokes `vendor.openwop.rust-hello.greet` (loaded WASM pack); exercises required exports + at least one import | `completed` | ≤ 10s |
128
128
  | WASM Pack Memory-Cap Breach | `conformance-wasm-pack-memory-cap-breach` | RFC 0008 §K — invokes the deliberately-misbehaving `vendor.openwop.misbehaving.memory-bomb` pack (allocates 1 GiB beyond the host's `memoryPagesMax`). Host MUST emit `cap.breached` with `kind: "wasm-memory"` and drive the run to terminal `failed`. Misbehaving pack lives at `openwop-examples:examples/packs/rust-misbehaving-memory/` (repo-qualified per the 2026-06 monorepo split — the `openwop-examples` sibling repo) and is fixture-only (NOT signed for registry publication). | `failed` (with `cap.breached`) | ≤ 10s |
129
- | Configurable Schema | `conformance-configurable-schema` | Track 13 — workflow declares `configurableSchema` (`additionalProperties: false`, `recursionLimit: integer ≥ 1`). Suite verifies `GET /v1/workflows/{id}` surfaces the schema AND `POST /v1/runs` with a mismatched `configurable` returns `validation_error`. | `completed` (with accepted overlay) | ≤ 5s |
129
+ | WASM Pack ABI Mismatch | `conformance-wasm-pack-abi-mismatch` | node-pack-runtimes.md §WASM — invokes `vendor.openwop.misbehaving.abi-bomb`, whose module reports `openwop_abi_version()` 999. Advertising the fixture says the operator offered `fixtures/wasm-packs/misbehaving-abi/` to the loader; a conforming host rejected it at load, so `loadedPacks` (when advertised) omits it and the node never runs. Fixture-only (NOT signed for registry publication). | refused at creation (4xx), or `failed` / `cancelled` without `node.completed` for `abi` | ≤ 10s |
130
+ | Configurable Schema | `conformance-configurable-schema` | Track 13 — workflow declares `configurableSchema` (`additionalProperties: false`, `recursionLimit: integer ≥ 1`). Suite verifies `GET /workflows/{id}` surfaces the schema AND `POST /runs` with a mismatched `configurable` returns `validation_error`. | `completed` (with accepted overlay) | ≤ 5s |
130
131
  | Smoke — BYOK Roundtrip | `openwop-smoke-byok-roundtrip` | End-to-end BYOK secret-resolution smoke. Single `conformance.secret.echo` node fetches the host-provisioned canary secret `openwop-conformance-canary-secret`, emits SHA-256 hex + byte length to variables — never the raw value. Spec: `run-options.md` §"Credential references" + `auth.md` §"Secret resolution" + `observability.md` §"Redaction". | `completed` | ≤ 10s |
131
132
  | Secrets — Run Witness | `openwop-secrets-run-witness` | RFC 0229 §C — one `core.secret.witness` node. The client supplies a value with the run (`runSecrets`, a `run:` ref) and its SHA-256 as an input; the node outputs only `{ matched }`. It can confirm only a value the caller already holds, so it is safe to advertise in production. Advertised by every host that advertises `secrets.runSecrets`. | `completed` (`matched: true` or `false`); `failed` (`credential_forbidden` / `credential_not_found`) on the scope legs | ≤ 10s |
132
133
  | Secret — resolve then fail | `conformance-secret-resolve-then-fail` | `host-services.md` §`secrets` — resolves the byok canary exactly as `openwop-smoke-byok-roundtrip`, then `core.fail` fails the run; the canary MUST appear on no surface of the failed run | `failed` | ≤ 10s |
@@ -145,7 +146,7 @@ All fixtures MUST advertise:
145
146
  | Phase 4 Replay Divergence | `conformance-phase4-replay-divergence` | RFC 0041 §B — single `core.ai.structuredOutput` node against mock provider. Conformance scenario pre-seeds a 2-entry program via the existing mock-AI program seam: entry [0] returns a valid envelope (original run consumes); entry [1] returns `stopReason: 'safety'` + `refusalText` (`:fork mode: replay` consumes). Phase 4 hosts advertising `multiAgent.executionModel.replayDeterminism.refusalDivergenceEmission: true` MUST emit `replay.divergedAtRefusal` + fail replay with `error.code: 'replay_diverged_at_refusal'`. Silent substitution is non-conformant. Pairs with `replay-divergence-at-refusal.test.ts`. | original: `completed`; replay: `failed` (`error.code='replay_diverged_at_refusal'`) | ≤ 10s |
146
147
  | Phase 4 Nondeterministic Tool | `conformance-phase4-nondet-tool` | RFC 0041 §C — two-node workflow (`core.noop` proxied as a nondeterministic tool → `core.ai.structuredOutput`). Used by `replay-observable-sequence-determinism.test.ts` to verify that across original + replay runs, the observable `RunEventDoc` sequence prefix is identical up to and including the nondeterministic-tool node's `node.completed` event. The host's replay path MUST replay the original event log entries (rather than re-executing the tool) for nodes whose `core.tool.*` config carries `nondeterministic: true`. Phase 4 hosts advertising `multiAgent.executionModel.replayDeterminism.supported: true` honor this contract. | original + replay: `completed`; observable prefixes equal up to the nondet boundary | ≤ 10s |
147
148
 
148
- The `messages`-mode stream fixture (AI token streaming) is covered by the deterministic mock-provider surface in `spec/v1/run-options.md`. Hosts that do not advertise `Capabilities.testing.mockProviders` skip-equivalent on those scenarios.
149
+ The `messages`-mode stream fixture (AI token streaming) is covered by the deterministic mock-provider surface (`spec/v1/run-options.md` at v1; `testing.mockProviders` in [`spec/v2/core/runs.md`](../spec/v2/core/runs.md) at v2). Hosts that do not advertise `Capabilities.testing.mockProviders` skip-equivalent on those scenarios.
149
150
 
150
151
  ---
151
152
 
@@ -161,7 +162,7 @@ The `messages`-mode stream fixture (AI token streaming) is covered by the determ
161
162
 
162
163
  ### `conformance-noop`
163
164
 
164
- - **Purpose**: cheapest run-lifecycle test. Used by the conformance suite's `runs.test.ts` to verify create/read/terminal-event/cleanup work end-to-end.
165
+ - **Purpose**: cheapest run-lifecycle test. Used by `runs-lifecycle.test.ts` (v1) and `v2-created-run-readable.test.ts` (v2), and by most run-surface scenarios, to verify create, read and the terminal event end-to-end.
165
166
  - **Inputs**: none.
166
167
  - **Expected events** (in order, `updates` mode):
167
168
  1. `run.started`
@@ -183,7 +184,7 @@ The `messages`-mode stream fixture (AI token streaming) is covered by the determ
183
184
  - **Purpose**: verify the engine handles in-flight runs (status transitions over time, SSE keep-alives, poll fallback).
184
185
  - **Inputs**:
185
186
  - `delayMs` (integer, required, 0 ≤ value ≤ 30000) — server MUST sleep for this duration before completing.
186
- - **Expected behavior**: `GET /v1/runs/{runId}` MUST return `status: "running"` while the delay is in flight; `status: "completed"` after.
187
+ - **Expected behavior**: `GET /runs/{runId}` MUST return `status: "running"` while the delay is in flight; `status: "completed"` after.
187
188
  - **Terminal status**: `completed`.
188
189
 
189
190
  ### `conformance-failure`
@@ -205,7 +206,7 @@ The `messages`-mode stream fixture (AI token streaming) is covered by the determ
205
206
  1. Run starts and reaches an `approvalGate` node that calls `ctx.interrupt({kind: 'approval', ...})`.
206
207
  2. Server emits `interrupt.requested` (and SHOULD also emit `approval.requested` for back-compat).
207
208
  3. Run status MUST be `waiting-approval`.
208
- 4. After client POSTs `{action: 'accept'}` to `/v1/runs/{runId}/interrupt`, server emits `approval.received` and resumes.
209
+ 4. After the client POSTs `{action: 'accept'}` to `/runs/{runId}/interrupts/{nodeId}` (`resolveInterruptByRun`), server emits `approval.received` and resumes.
209
210
  5. Run reaches `completed`.
210
211
  - **Terminal status (after accept)**: `completed`.
211
212
  - **Terminal status (after reject, major 2)**: `failed`, with `error.code` `approval_rejected` on the snapshot and on `run.failed`, and `failedNodeId: "gate"`. The fixture has no edges, so nothing routes the failure (`spec/v2/core/interrupt.md` §Rejection, RFC 0223; `v2-approval-reject-disposition.test.ts`).
@@ -310,20 +311,20 @@ The `messages`-mode stream fixture (AI token streaming) is covered by the determ
310
311
  - **Inputs**:
311
312
  - `nonce` (string, required) — caller-supplied; server MUST NOT use this for any side effect, only for de-duplication semantics tests.
312
313
  - **Expected behavior**:
313
- - `POST /v1/runs` with the same `Idempotency-Key` and same body twice → second response MUST replay the first (`openwop-Idempotent-Replay: true` header) and MUST NOT create a second run.
314
+ - `POST /runs` with the same `Idempotency-Key` and same body twice → second response MUST replay the first (`openwop-Idempotent-Replay: true` header) and MUST NOT create a second run.
314
315
  - Same `Idempotency-Key` with a different body → 409.
315
316
  - **Terminal status**: `completed`.
316
317
 
317
318
  ### `conformance-cancellable`
318
319
 
319
- - **Purpose**: verify `:cancel` mid-run.
320
+ - **Purpose**: verify cancellation mid-run.
320
321
  - **Inputs**:
321
322
  - `delayMs` (integer, required, 1 ≤ value ≤ 60000) — wait long enough for the conformance test to issue cancel.
322
323
  - **Expected behavior**:
323
324
  1. Run reaches `running`.
324
- 2. Client posts `POST /v1/runs/{runId}:cancel`.
325
+ 2. Client posts `POST /runs/{runId}/cancel` (`cancelRun`).
325
326
  3. Server emits `run.cancelled` within 5s.
326
- 4. Subsequent `GET /v1/runs/{runId}` MUST return `status: "cancelled"`.
327
+ 4. Subsequent `GET /runs/{runId}` MUST return `status: "cancelled"`.
327
328
 
328
329
  ### `conformance-replay-ordinal-loop`
329
330
 
@@ -374,7 +375,7 @@ The `messages`-mode stream fixture (AI token streaming) is covered by the determ
374
375
  - **Topology**: two nodes: `core.orchestrator.supervisor` and `core.dispatch`, looping back to each other.
375
376
  - **Inputs**: none.
376
377
  - **Conformance test driver**:
377
- 1. POST `/v1/runs` with `{workflowId: "conformance-dispatch-loop"}`.
378
+ 1. POST `/runs` with `{workflowId: "conformance-dispatch-loop"}`.
378
379
  2. The orchestrator node must emit `runOrchestrator.decided` with `next-worker` then `terminate`.
379
380
  3. Poll until terminal.
380
381
  4. **Assert** terminal status is `completed`.
@@ -396,18 +397,18 @@ The `messages`-mode stream fixture (AI token streaming) is covered by the determ
396
397
 
397
398
  ## `conformance-version-fold` (closes F5)
398
399
 
399
- - **Consuming scenario**: `conformance/src/scenarios/version-fold.test.ts` (added 2026-06-11; previously this fixture had no consuming scenario).
400
+ - **Consuming scenario**: `conformance/src/scenarios/version-fold.test.ts`. **v1 only**: `X-Force-Engine-Version` is not a v2 header ([`spec/v2/core/headers.md`](../spec/v2/core/headers.md)).
400
401
  - **Purpose**: verify forward-compat fold-best-effort tolerance across the spec's engine-version cross-version interop matrix (`version-negotiation.md` §Cross-version interop matrix). Uses the test-keys-only `X-Force-Engine-Version` header to drive the same workflow at three different engine versions from a single deployed server — no multi-version fleet needed.
401
402
  - **Fixture topology**: a single `core.noop` node. The workflow itself is trivial; the test exercises the server's READ path (projection, event-log fold) under each forced engine version.
402
403
  - **Inputs**: none.
403
404
  - **Conformance test driver**:
404
405
  1. Read the server's `Capabilities.testing.forceEngineVersionRange = { min, max }`.
405
406
  2. For each version `v` in `[min, current, max]` (deduped):
406
- - POST `/v1/runs` with body `{workflowId: "conformance-version-fold"}` AND header `X-Force-Engine-Version: v`. Use a test API key.
407
+ - POST `/runs` with body `{workflowId: "conformance-version-fold"}` AND header `X-Force-Engine-Version: v`. Use a test API key.
407
408
  - Poll until terminal.
408
409
  - **Assert** terminal status is `completed`.
409
- - **Assert** `GET /v1/runs/{runId}` returns a valid `RunSnapshot` (the projection tolerates the version mismatch via fold-best-effort).
410
- - **Assert** `GET /v1/runs/{runId}/events/poll?lastSequence=0&timeout=1` returns a non-empty `events[]` array (event log is readable).
410
+ - **Assert** `GET /runs/{runId}` returns a valid `RunSnapshot` (the projection tolerates the version mismatch via fold-best-effort).
411
+ - **Assert** `GET /runs/{runId}/events/poll?lastSequence=0&timeout=1` returns a non-empty `events[]` array (event log is readable).
411
412
  - **Negative paths**:
412
413
  - Same fixture with a production API key returns `403 force_engine_version_forbidden`.
413
414
  - Same fixture with `X-Force-Engine-Version: <out-of-range>` returns `400 unsupported_force_engine_version`.
@@ -424,7 +425,7 @@ This fixture closes F5 without requiring any new server-side test infrastructure
424
425
  - **Fixture topology**: a single `core.ai.callPrompt` (or similar AI-bearing typeId) node. The node's actual prompt content is irrelevant — the conformance driver intercepts the AI dispatch via `configurable.mockProvider`.
425
426
  - **Inputs**: none.
426
427
  - **Conformance test driver**:
427
- 1. POST `/v1/runs` with body:
428
+ 1. POST `/runs` with body:
428
429
 
429
430
  ```jsonc
430
431
  {
@@ -445,7 +446,7 @@ This fixture closes F5 without requiring any new server-side test infrastructure
445
446
 
446
447
  Use a test API key (server returns 403 on production keys per `run-options.md` §Authorization).
447
448
 
448
- 2. Subscribe to `/v1/runs/{runId}/events?streamMode=messages`.
449
+ 2. Subscribe to `/runs/{runId}/events?streamMode=messages`.
449
450
  3. **Assert** chunk arrival order: `["Hello", " ", "world", "!"]` — same order as `tokens`.
450
451
  4. **Assert** the final chunk has `isLast: true`, `meta.finishReason === "stop"`, `meta.usage.completionTokens === 4`.
451
452
  5. **Assert** SSE stream closes on terminal — server-closed, not timeout.
@@ -495,7 +496,7 @@ The fixture JSONs and matching `subworkflow.test.ts` are part of the current con
495
496
  - **Topology**: 10 sequential `core.noop` nodes (`a → b → c → … → j`). A run completes naturally if no per-run override is supplied. With `configurable.recursionLimit = 5`, the run MUST trip after the 5th node.
496
497
  - **Inputs**: none.
497
498
  - **Conformance test driver**:
498
- 1. POST `/v1/runs` with `{workflowId: "conformance-cap-breach", configurable: {recursionLimit: 5}}`.
499
+ 1. POST `/runs` with `{workflowId: "conformance-cap-breach", configurable: {recursionLimit: 5}}`.
499
500
  2. Server SHOULD validate `recursionLimit ≤ Capabilities.limits.maxNodeExecutions`. If `maxNodeExecutions` is `100` (default), `5` is fine.
500
501
  3. Poll until terminal.
501
502
  4. **Assert** terminal status is `failed`.
@@ -520,7 +521,7 @@ Both items are tracked as v1.x conformance expansion work.
520
521
  - **Topology**: single `core.delay` node that sleeps `input.delayMs` (default `30000`) — far longer than the small `runTimeoutMs` the test supplies.
521
522
  - **Inputs**: `delayMs` (number, default `30000`).
522
523
  - **Conformance test driver**:
523
- 1. POST `/v1/runs` with `{workflowId: "conformance-run-duration-breach", configurable: {runTimeoutMs: 1000}}`.
524
+ 1. POST `/runs` with `{workflowId: "conformance-run-duration-breach", configurable: {runTimeoutMs: 1000}}`.
524
525
  2. Poll until terminal.
525
526
  3. **Assert** terminal status is `failed`.
526
527
  4. **Assert** `RunSnapshot.error.code === "run_timeout"`.
@@ -538,7 +539,7 @@ This fixture is unblocked when a host exposes a wall-clock deadline enforcer adv
538
539
  - **Purpose**: verify the BYOK secret-resolution roundtrip end-to-end. A host that advertises `capabilities.secrets.supported: true` MUST resolve the canary `openwop-conformance-canary-secret` via its `SecretResolver` and surface only the SHA-256 hex + byte length on every observable channel (variables, events, debug bundle, logs). The raw value MUST NOT leak per `observability.md` §"Redaction" + `threat-model-secret-leakage.md` §SR-1.
539
540
  - **Topology**: single node `resolve-secret` with `typeId: conformance.secret.echo`, `config.secretId: "openwop-conformance-canary-secret"`. Resolves the canary, hashes it, and writes `{secretSha256, secretLength}` to the run's `variables.resolve-secret`.
540
541
  - **Conformance test driver**:
541
- 1. POST `/v1/runs` with `{workflowId: "openwop-smoke-byok-roundtrip"}`.
542
+ 1. POST `/runs` with `{workflowId: "openwop-smoke-byok-roundtrip"}`.
542
543
  2. Poll until terminal.
543
544
  3. **Assert** terminal status is `completed`.
544
545
  4. **Assert** `variables['resolve-secret'].secretSha256` matches `^[0-9a-f]{64}$`.
@@ -615,7 +616,7 @@ Hosts that don't ship a BYOK SecretResolver MAY return `404` / `422` on the star
615
616
  - **Topology**: 4 sequential `core.channelWrite` nodes targeting channel `events` with `ttlMs: 200`, separated by a `core.delay` of 300ms between writes 3 and 4. The 4th write fires after the TTL window has elapsed.
616
617
  - **Inputs**: none. Each node carries a static `value` in its `config` (`a`, `b`, `c`, `d` respectively).
617
618
  - **Conformance test driver**:
618
- 1. POST `/v1/runs` with `{workflowId: "conformance-channel-ttl"}`.
619
+ 1. POST `/runs` with `{workflowId: "conformance-channel-ttl"}`.
619
620
  2. Poll until terminal.
620
621
  3. **Assert** terminal status is `completed`.
621
622
  4. **Assert** `RunSnapshot.variables.events.length === 1` — the 3 priors aged out at the 4th write.
@@ -632,9 +633,9 @@ Hosts that don't ship a BYOK SecretResolver MAY return `404` / `422` on the star
632
633
  - **Topology**: a single `core.identity` node whose `agent` binding is `{ "agentId": "core.conformance.channel-agent", "channel": "stable" }` (no `version`). The host MUST have an active deployment of `core.conformance.channel-agent` on the `stable` channel.
633
634
  - **Inputs**: none. Trigger `manual`.
634
635
  - **Conformance test driver**:
635
- 1. POST `/v1/runs` with `{workflowId: "conformance-agent-channel-dispatch"}`; poll until terminal.
636
+ 1. POST `/runs` with `{workflowId: "conformance-agent-channel-dispatch"}`; poll until terminal.
636
637
  2. **Assert** the first `agent.invocation.started` carries `resolvedChannel: "stable"` and a concrete non-empty `resolvedAgentVersion` (the recorded fact, RFC 0077).
637
- 3. **Replay** via `POST /v1/runs/{runId}:fork {mode:"replay"}`; **assert** the fork's `agent.invocation.started` re-reads the SAME `resolvedAgentVersion`.
638
+ 3. **Replay** via `POST /runs/{runId}:fork {mode:"replay"}`; **assert** the fork's `agent.invocation.started` re-reads the SAME `resolvedAgentVersion`.
638
639
  4. **(Seam-guarded)** Move the `stable` channel via the deployment seam; **assert** a replay of the original run STILL carries the original pin — never re-resolving the moved channel.
639
640
 
640
641
  ---
@@ -685,7 +686,7 @@ The fixtures reference these typeIds:
685
686
  | `conformance.artifact.emit` | artifact-emit | Opt-in (RFC 0205). Produce one artifact of `config.artifactType` with payload `config.data`; emit `artifact.created` naming it; readable through `getArtifact`. |
686
687
  | `conformance.oauth.use` | credential | Opt-in (RFC 0199). Resolve the credential `config.auth` names for the run's Subject, refreshing host-side; no credential ⇒ a `credential` interrupt (with the facet) or a failed node. Never carries token material on any surface. |
687
688
 
688
- An OpenWOP-compliant server's NodeModule registry MUST include implementations for all six core typeIds before seeding fixtures. The `conformance.requiresMissing` fixture node is opt-in — see the row above.
689
+ A host's NodeModule registry MUST include implementations for all six core typeIds before seeding fixtures. The `conformance.requiresMissing` fixture node is opt-in — see the row above.
689
690
 
690
691
  ---
691
692
 
@@ -782,7 +783,7 @@ A host MUST NOT advertise the fixture unless it advertises `httpClient.safeFetch
782
783
 
783
784
  ## Versioning
784
785
 
785
- Each fixture's JSON has its own `version` field. The OpenWOP v1.0 conformance suite targets fixture version 1.0. Fixture spec breaking changes MUST bump the major; the suite MUST refuse to run against an unrecognized fixture version with a clear error message.
786
+ Each fixture's JSON has its own `version` field. The suite targets fixture version 1.x. Fixture spec breaking changes MUST bump the major; the suite MUST refuse to run against an unrecognized fixture version with a clear error message.
786
787
 
787
788
  ---
788
789
 
@@ -790,26 +791,24 @@ Each fixture's JSON has its own `version` field. The OpenWOP v1.0 conformance su
790
791
 
791
792
  ```text
792
793
  conformance/
793
- fixtures.md — this file
794
+ fixtures.md this file
794
795
  fixtures/
795
- conformance-noop.json
796
- conformance-identity.json
797
- conformance-delay.json
798
- conformance-failure.json
799
- conformance-approval.json
800
- conformance-approval-refine.json
801
- conformance-approval-edit-accept.json
802
- conformance-approval-reject-routed.json
803
- conformance-approval-timeout.json
804
- conformance-approval-timeout-approve.json
805
- conformance-approval-reject-loopback.json
806
- conformance-clarification.json
807
- conformance-multi-node.json
808
- conformance-idempotent.json
809
- conformance-cancellable.json
796
+ *.json workflow fixtures (WorkflowDefinition), seeded by the host
797
+ a2ui-v09/ A2UI v0.9 surface payloads (not seeded)
798
+ connection-packs/ connection-pack manifests
799
+ frontend-plugin-packs/ front-end plugin packs
800
+ interrupt-payloads/ InterruptPayload documents (not seeded)
801
+ node-pack-runtime/ node-pack runtime fixtures
802
+ oauth-providers/ synthetic OAuth provider definitions
803
+ pack-manifests/ pack manifests
804
+ prompt-templates/ prompt templates
805
+ trigger-events/ trigger-event payloads
806
+ upstream/ upstream schemas vendored byte for byte (a2a-v1.0.1/, a2ui-v0.9/)
807
+ wasm-packs/ installable WASM node packs
808
+ wasm-sandbox/ WASM modules for the server-free sandbox probe (not seeded)
810
809
  ```
811
810
 
812
- Each JSON is a valid `WorkflowDefinition` per `../schemas/workflow-definition.schema.json`. Servers MUST treat them as opaque blobs to seed verbatim — do not transform field names or strip fields.
811
+ Each top-level JSON is a valid `WorkflowDefinition` per `../schemas/workflow-definition.schema.json`, checked by `fixtures-valid.test.ts`. Hosts MUST seed them verbatim, as opaque blobs: do not transform field names or strip fields. The sub-directories are described in the sections below.
813
812
 
814
813
  ---
815
814
 
@@ -843,7 +842,25 @@ The `fixtures/wasm-packs/` sub-directory holds two node packs (`language: "wasm"
843
842
  | Pack | Node | Purpose |
844
843
  | --- | --- | --- |
845
844
  | `misbehaving-memory` (`vendor.openwop.misbehaving`) | `vendor.openwop.misbehaving.memory-bomb` | Grows linear memory 64 MiB at a time until the host refuses, then traps. A host advertising `nodePackRuntimes.wasm.maxMemoryBytes` MUST emit `cap.breached` `kind: "wasm-memory"`. Drives `conformance-wasm-pack-memory-cap-breach` and `v2-wasm-memory-cap`. |
846
- | `misbehaving-abi` (`vendor.openwop.misbehaving-abi`) | `vendor.openwop.misbehaving.abi-bomb` | Reports `openwop_abi_version` 999, which no host lists, so a conforming host refuses it at load. Rejection at load is not yet observable over the protocol (TODO §8), so no v2 leg reads it. |
845
+ | `misbehaving-abi` (`vendor.openwop.misbehaving-abi`) | `vendor.openwop.misbehaving.abi-bomb` | Reports `openwop_abi_version` 999, which no host lists, so a conforming host refuses it at load. Drives `conformance-wasm-pack-abi-mismatch` and `v2-wasm-abi-reject`, which read the refusal from what it prevents: the pack in `nodePackRuntimes.wasm.loadedPacks`, and its node completing. |
846
+
847
+ ---
848
+
849
+ ## WASM sandbox probe fixtures
850
+
851
+ The `fixtures/wasm-sandbox/` sub-directory holds small WebAssembly modules, each a hand-written text module (`<name>.wat`) with its built `<name>.wasm`. They are NOT packs and are NOT seeded into a server: the server-free probe in `src/lib/wasm-sandbox-probe.ts` loads them directly to witness the sandbox isolation invariants (RFC 0035 §B; `SECURITY/invariants.yaml` `node-pack-sandbox-*`). Escape attempts and the capability gate are checked by inspecting each module's declared imports before instantiation; the memory bound by instantiating under a capped memory; isolated context by instantiating twice.
852
+
853
+ | Module | Expect | Consumed by |
854
+ | --- | --- | --- |
855
+ | `well-behaved-echo` | runs and returns its input | `sandbox-wasm-isolation.test.ts` |
856
+ | `well-behaved-host-fetch` | callable when `host-fetch` is granted; denied without the grant | `sandbox-wasm-isolation.test.ts` |
857
+ | `misbehaving-fs`, `misbehaving-env`, `misbehaving-network`, `misbehaving-process` | `sandbox_escape_attempt` with the matching escape kind | `sandbox-wasm-isolation.test.ts` |
858
+ | `misbehaving-capability-gate` | `sandbox_capability_denied`, naming the capability | `sandbox-wasm-isolation.test.ts` |
859
+ | `misbehaving-memory` | `sandbox_memory_exceeded` on access past the bound | `sandbox-wasm-isolation.test.ts` |
860
+ | `isolation-global` | no state shared between two instances | `sandbox-wasm-isolation.test.ts` |
861
+ | `misbehaving-timeout` | killed with `sandbox_timeout` by a worker-thread kill timer | `sandbox-wasm-timeout.test.ts` |
862
+
863
+ Both scenarios run at major 1 only and need no host.
847
864
 
848
865
  ---
849
866
 
@@ -979,12 +996,18 @@ The `fixtures/a2ui-v09/` sub-directory (suite 2.36.0) holds `ui.a2ui-surface` pa
979
996
 
980
997
  The `fixtures/upstream/a2ui-v0.9/` sub-directory vendors `server_to_client.json`, `catalogs/basic/catalog.json` and `common_types.json` from a2ui.org byte for byte, pinned by SHA-256 (RFC 0209 §References; its `README.md` records the URLs, fetch date and the Apache-2.0 notice). They are read only by the corpus gate.
981
998
 
999
+ ## Upstream A2A v1.0.1 schema source (RFC 0205)
1000
+
1001
+ The `fixtures/upstream/a2a-v1.0.1/` sub-directory vendors `a2a.proto` from the A2A repository at tag `v1.0.1`, byte for byte, pinned by SHA-256 (`e195bf96ab630c69797851970203e1b2b6b19528f2e9803b7d904b91a5104016`). A2A publishes no normative JSON Schema of its own, so `schemas/v2/part.schema.json` and `schemas/v2/artifact.schema.json` are transcribed from this file. The corpus gate `src/coherence/a2a-parts-schemas.test.ts` checks the digest and compares each schema's member names with the fields of `message Part` and `message Artifact`, so a transcription slip fails the gate. Its `README.md` records the source URL, fetch date and the Apache-2.0 notice. Do not edit it; moving to a later A2A minor re-vendors the file and re-transcribes both schemas. It is read only by the corpus gate and is not seeded.
1002
+
982
1003
  ---
983
1004
 
984
1005
  ## References
985
1006
 
986
- - `README.md` — conformance suite operator docs
987
- - `../schemas/workflow-definition.schema.json` — every fixture validates against this
988
- - `../rest-endpoints.md` — endpoint contracts the fixtures exercise
989
- - `../interrupt.md` — HITL primitive used by approval + clarification fixtures
990
- - `../idempotency.md` — semantics the idempotent fixture exercises
1007
+ - [`README.md`](./README.md) — running the suite
1008
+ - [`coverage.md`](./coverage.md) — which scenarios consume which fixtures
1009
+ - `../schemas/workflow-definition.schema.json` — every top-level fixture validates against this
1010
+ - [`spec/v2/core/runs.md`](../spec/v2/core/runs.md) — the run operations the fixtures exercise
1011
+ - [`spec/v2/core/interrupt.md`](../spec/v2/core/interrupt.md) — approval, clarification and credential interrupts
1012
+ - [`spec/v2/core/idempotency.md`](../spec/v2/core/idempotency.md) — the semantics the idempotent fixture exercises
1013
+ - [`spec/v2/core/replay.md`](../spec/v2/core/replay.md) — replay and fork, used by the replay fixtures
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "2.45.23",
4
- "description": "Production-ready black-box conformance suite for OpenWOP v1.0 compliant servers.",
3
+ "version": "2.45.25",
4
+ "description": "Black-box conformance suite for OpenWOP hosts (v2 current; v1 scenarios retained for the frozen v1 tree).",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/openwop/openwop.git",
@@ -56,6 +56,6 @@
56
56
  "@openwop/spec-artifacts": "file:../spec-artifacts"
57
57
  },
58
58
  "peerDependencies": {
59
- "@openwop/spec-artifacts": "2.45.23"
59
+ "@openwop/spec-artifacts": "2.45.25"
60
60
  }
61
61
  }