@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/CHANGELOG.md +15 -0
- package/README.md +227 -608
- package/coverage.md +118 -90
- package/dist/spec-artifacts.lock.json +2 -2
- package/fixtures/conformance-wasm-pack-abi-mismatch.json +41 -0
- package/fixtures.md +79 -56
- package/package.json +3 -3
- package/requirements.json +262 -249
- package/scenario-majors.json +5 -2
- package/schemas/CORPUS-STAMP.json +16 -16
- package/src/lib/effect-receiver.ts +12 -1
- package/src/lib/wasm-abi-reject-witness.ts +66 -0
- package/src/scenarios/frontend-plugin-packs.test.ts +10 -167
- package/src/scenarios/v2-wasm-abi-reject.test.ts +35 -0
package/fixtures.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenWOP Conformance Suite — Fixture Workflow Contract
|
|
2
2
|
|
|
3
|
-
>
|
|
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
|
|
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
|
-
|
|
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 /
|
|
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 /
|
|
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 /
|
|
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 /
|
|
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 /
|
|
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 /
|
|
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
|
-
|
|
|
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
|
|
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
|
|
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 /
|
|
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 `/
|
|
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 /
|
|
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
|
|
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 /
|
|
325
|
+
2. Client posts `POST /runs/{runId}/cancel` (`cancelRun`).
|
|
325
326
|
3. Server emits `run.cancelled` within 5s.
|
|
326
|
-
4. Subsequent `GET /
|
|
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 `/
|
|
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
|
|
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 `/
|
|
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 /
|
|
410
|
-
- **Assert** `GET /
|
|
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 `/
|
|
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 `/
|
|
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 `/
|
|
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 `/
|
|
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 `/
|
|
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 `/
|
|
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 `/
|
|
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 /
|
|
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
|
-
|
|
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
|
|
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
|
|
794
|
+
fixtures.md this file
|
|
794
795
|
fixtures/
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
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`.
|
|
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.
|
|
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` —
|
|
987
|
-
-
|
|
988
|
-
- `../
|
|
989
|
-
-
|
|
990
|
-
-
|
|
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.
|
|
4
|
-
"description": "
|
|
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.
|
|
59
|
+
"@openwop/spec-artifacts": "2.45.25"
|
|
60
60
|
}
|
|
61
61
|
}
|