@openwop/openwop-conformance 2.1.5 → 2.1.7

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 CHANGED
@@ -1,5 +1,45 @@
1
1
  # `@openwop/openwop-conformance` Changelog
2
2
 
3
+ ## [2.1.7] — 2026-09-13 — nine fixtures shared one programmable node id
4
+
5
+ The mock-AI program seam is keyed by `nodeId` alone — no run, no workflow, no
6
+ tenant. `host-sample-test-seams.md` §5 stated the isolation that relies on:
7
+ *"each conformance scenario uses a unique fixture (and therefore unique
8
+ `nodeId`)"*. The corpus did not honour it. Nine fixtures declared a node called
9
+ `structured-call`, and vitest runs scenario files in parallel, so any two of
10
+ them could overwrite each other's program mid-run.
11
+
12
+ That is the long-standing `replay-observable-sequence-determinism` flake.
13
+ That scenario never programs the mock: it runs `conformance-phase4-nondet-tool`
14
+ and inherits whatever the last writer left. When an envelope scenario had just
15
+ staged `{ stopReason: 'safety' }`, the replay fixture's own node refused, the
16
+ source run reached `failed`, and the scenario reported
17
+
18
+ expected 'failed' to be 'completed'
19
+
20
+ — an assertion about replay determinism failing for a reason that has nothing
21
+ to do with replay. It was green in isolation, red under the full gate, and did
22
+ not track load, because contention was never the variable; overlap was.
23
+
24
+ Each of the nine nodes is now named for its fixture
25
+ (`refusal-structured-call`, `nondet-structured-call`, and so on), which is what
26
+ §5 always claimed. `conformance/scripts/check-mock-ai-node-ids-unique.mjs`
27
+ holds the line: every fixture node dispatching to the mock provider must be
28
+ owned by exactly one fixture.
29
+
30
+ **Hosts: re-register the conformance fixtures when you pin 2.1.7.** The node
31
+ ids inside those nine workflow definitions changed. A host still serving the
32
+ old definitions will program a node its fixture no longer has, and the envelope
33
+ scenarios will fail loudly rather than silently — but they will fail.
34
+
35
+ ## [2.1.6] — 2026-09-13 — the unknown-run probe is tenant-bound
36
+
37
+ `v2-compensation-read-projection` asserted `404 not_found` for an unknown run
38
+ while probing with a BARE id, which `identity.md` §5 requires a v1-retired host
39
+ to refuse `400 validation_error`. The probe now forms a tenant-bound id from a
40
+ run it creates, so it tests existence rather than spelling and passes on
41
+ overlap and retired hosts alike. No other scenario changed.
42
+
3
43
  ## [2.1.5] — 2026-09-12 — v1 errata only
4
44
 
5
45
  No scenario changed. The contract peer ships `spec/v1/deprecations.json`, whose
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "package": "@openwop/spec-artifacts",
3
- "version": "2.1.5",
4
- "stampSha256": "88b9aea840128edb93799af8942d5fe7f8711ed36746f44c971602a4b47b5aea"
3
+ "version": "2.1.7",
4
+ "stampSha256": "5594e445dafa6f889a005c00556c2ae241f7a591f45e08eee0b126736bfb04d3"
5
5
  }
@@ -5,7 +5,7 @@
5
5
  "description": "Drives `core.ai.structuredOutput` against the conformance-only `mock` provider. The conformance scenario POSTs a 3-entry program: all three attempts return natural-language prose (no JSON sigil at the start). The host's `dispatchStructured` retry loop exhausts on parse-error, detects the NL shape, and fires ONE additional dispatch with a corrective coercion fragment — emitting `envelope.nlToFormat.engaged { originalEnvelopeType, fallbackCalls: 1 }` BEFORE the secondary call. The pre-seeded 4th program entry returns valid JSON, the schema validates, and the run terminates `completed`.",
6
6
  "nodes": [
7
7
  {
8
- "id": "structured-call",
8
+ "id": "nl-to-format-structured-call",
9
9
  "typeId": "core.ai.structuredOutput",
10
10
  "name": "Structured output via mock provider (NL responses)",
11
11
  "position": { "x": 0, "y": 0 },
@@ -2,10 +2,10 @@
2
2
  "id": "conformance-envelope-recovery-applied",
3
3
  "name": "Conformance: envelope.recovery.applied (RFC 0032 §B.6)",
4
4
  "version": "1.0",
5
- "description": "Drives `core.ai.structuredOutput` against the conformance-only `mock` provider. The conformance scenario POSTs a 1-entry program to `/v1/host/sample/test/mock-ai/program` (keyed by `nodeId: 'structured-call'`) BEFORE starting the run: the mock returns a markdown-fenced JSON envelope (e.g., ```json\\n{\"result\":\"ok\"}\\n```). The host's `dispatchStructured` lenient-parse fallback strips the fence via `tryLenientParse(text)`, emits exactly one `envelope.recovery.applied` with `path: 'markdown-fence'`, and accepts the parsed value WITHOUT counting against the retry budget per RFC 0033 §D. Run terminates `completed`.",
5
+ "description": "Drives `core.ai.structuredOutput` against the conformance-only `mock` provider. The conformance scenario POSTs a 1-entry program to `/v1/host/sample/test/mock-ai/program` (keyed by `nodeId: 'recovery-applied-structured-call'`) BEFORE starting the run: the mock returns a markdown-fenced JSON envelope (e.g., ```json\\n{\"result\":\"ok\"}\\n```). The host's `dispatchStructured` lenient-parse fallback strips the fence via `tryLenientParse(text)`, emits exactly one `envelope.recovery.applied` with `path: 'markdown-fence'`, and accepts the parsed value WITHOUT counting against the retry budget per RFC 0033 §D. Run terminates `completed`.",
6
6
  "nodes": [
7
7
  {
8
- "id": "structured-call",
8
+ "id": "recovery-applied-structured-call",
9
9
  "typeId": "core.ai.structuredOutput",
10
10
  "name": "Structured output via mock provider (markdown-fenced)",
11
11
  "position": { "x": 0, "y": 0 },
@@ -5,7 +5,7 @@
5
5
  "description": "Single `core.ai.structuredOutput` node against the conformance `mock` provider with a pre-seeded program returning `stopReason: 'safety'` + `refusalText: '...'` on attempt 1. Host's `dispatchStructured()` MUST: (a) emit exactly one `envelope.refusal` event with the canonical payload shape; (b) NOT retry (RFC 0032 §B.3 + RFC 0033 §D — refusal is terminal); (c) fail the node with `error.code: 'envelope_refused_by_provider'` per RFC 0033 §F; (d) NOT echo the refusal text in `RunSnapshot.error.message` (SECURITY invariant `envelope-refusal-no-prompt-leak` — refusal text lives only on the event-log entry, scrubbed via the existing SR-1 redaction harness).",
6
6
  "nodes": [
7
7
  {
8
- "id": "structured-call",
8
+ "id": "refusal-structured-call",
9
9
  "typeId": "core.ai.structuredOutput",
10
10
  "name": "Structured output via mock provider (refusal)",
11
11
  "position": { "x": 0, "y": 0 },
@@ -2,10 +2,10 @@
2
2
  "id": "conformance-envelope-retry-attempted",
3
3
  "name": "Conformance: envelope.retry.attempted (RFC 0032 §B.1)",
4
4
  "version": "1.0",
5
- "description": "Drives `core.ai.structuredOutput` against the conformance-only `mock` provider. The conformance scenario POSTs a 2-entry program to `/v1/host/sample/test/mock-ai/program` (keyed by `nodeId: 'structured-call'`) BEFORE starting the run: attempt 1 returns invalid JSON, attempt 2 returns a valid envelope. The host's `dispatchStructured` retry loop MUST emit exactly one `envelope.retry.attempted` event with `attempt: 2` between the two provider calls (RFC 0032 §B.1). The run terminates `completed` after the second attempt succeeds.",
5
+ "description": "Drives `core.ai.structuredOutput` against the conformance-only `mock` provider. The conformance scenario POSTs a 2-entry program to `/v1/host/sample/test/mock-ai/program` (keyed by `nodeId: 'retry-attempted-structured-call'`) BEFORE starting the run: attempt 1 returns invalid JSON, attempt 2 returns a valid envelope. The host's `dispatchStructured` retry loop MUST emit exactly one `envelope.retry.attempted` event with `attempt: 2` between the two provider calls (RFC 0032 §B.1). The run terminates `completed` after the second attempt succeeds.",
6
6
  "nodes": [
7
7
  {
8
- "id": "structured-call",
8
+ "id": "retry-attempted-structured-call",
9
9
  "typeId": "core.ai.structuredOutput",
10
10
  "name": "Structured output via mock provider",
11
11
  "position": { "x": 0, "y": 0 },
@@ -5,7 +5,7 @@
5
5
  "description": "Drives `core.ai.structuredOutput` against the conformance `mock` provider with a program that returns invalid JSON on EVERY attempt. The host's `dispatchStructured` retry loop MUST exhaust its budget and emit exactly one `envelope.retry.exhausted` event with `finalReason: 'schema-violation'` (or `'parse-error'` per RFC 0032 §B.1 reason enum). The node MUST fail with `error.code: 'envelope_payload_invalid'` (existing RFC 0021 code per RFC 0033 §C). Pairs with `envelope-retry-exhausted.test.ts`.",
6
6
  "nodes": [
7
7
  {
8
- "id": "structured-call",
8
+ "id": "retry-exhausted-structured-call",
9
9
  "typeId": "core.ai.structuredOutput",
10
10
  "name": "Structured output via mock provider (always invalid)",
11
11
  "position": { "x": 0, "y": 0 },
@@ -5,7 +5,7 @@
5
5
  "description": "Drives `core.ai.structuredOutput` against the conformance `mock` provider with a program: attempt 1 returns `stopReason: 'max_tokens'` (truncation); attempt 2 returns a valid envelope. The host's `dispatchStructured` retry loop MUST: (a) emit exactly one `envelope.truncated` event with `stopReason: 'max_tokens'`; (b) retry with an INCREASED output budget per RFC 0033 §B (the host's `truncationBudgetMultiplier` — default 2×); (c) NOT inject the corrective schema fragment on the truncation retry (truncation is an output-size problem, not a schema problem). Eventually completes after attempt 2 succeeds. Pairs with `envelope-truncated.test.ts`.",
6
6
  "nodes": [
7
7
  {
8
- "id": "structured-call",
8
+ "id": "truncated-structured-call",
9
9
  "typeId": "core.ai.structuredOutput",
10
10
  "name": "Structured output (truncation then success)",
11
11
  "position": { "x": 0, "y": 0 },
@@ -5,7 +5,7 @@
5
5
  "description": "Drives `core.ai.structuredOutput` against the conformance `mock` provider with a program that returns `stopReason: 'max_tokens'` on EVERY attempt. The host's `dispatchStructured` retry loop MUST: (a) emit `envelope.truncated` on each attempt (or at least the first one — RFC 0032 §B.4 is per-attempt); (b) double the budget each retry (RFC 0033 §B); (c) exhaust retries after `maxRetryAttempts` (default 3); (d) emit exactly one `envelope.retry.exhausted` with `finalReason: 'truncation'`; (e) emit `cap.breached` with `kind: 'schema'`; (f) fail the node with `error.code: 'envelope_truncation_unrecoverable'` per RFC 0033 §F. The run does NOT exceed `maxRetryAttempts` total LLM calls — DoS-bound assertion. Pairs with `envelope-truncation-cap-exhaustion.test.ts`.",
6
6
  "nodes": [
7
7
  {
8
- "id": "structured-call",
8
+ "id": "truncation-cap-structured-call",
9
9
  "typeId": "core.ai.structuredOutput",
10
10
  "name": "Structured output (perpetual truncation → cap exhaustion)",
11
11
  "position": { "x": 0, "y": 0 },
@@ -16,7 +16,7 @@
16
16
  "inputs": {}
17
17
  },
18
18
  {
19
- "id": "structured-call",
19
+ "id": "nondet-structured-call",
20
20
  "typeId": "core.ai.structuredOutput",
21
21
  "name": "Structured output via mock provider (consumes nondet-tool result)",
22
22
  "position": { "x": 200, "y": 0 },
@@ -40,7 +40,7 @@
40
40
  }
41
41
  ],
42
42
  "edges": [
43
- { "id": "e1", "sourceNodeId": "nondet-tool", "targetNodeId": "structured-call" }
43
+ { "id": "e1", "sourceNodeId": "nondet-tool", "targetNodeId": "nondet-structured-call" }
44
44
  ],
45
45
  "triggers": [
46
46
  { "id": "manual", "type": "manual", "enabled": true }
@@ -2,10 +2,10 @@
2
2
  "id": "conformance-phase4-replay-divergence",
3
3
  "name": "Conformance: RFC 0041 §B replay-divergence-at-refusal (Phase 4)",
4
4
  "version": "1.0",
5
- "description": "Single `core.ai.structuredOutput` node against the conformance `mock` provider. Conformance scenario `replay-divergence-at-refusal.test.ts` pre-seeds the mock with a two-entry program via `POST /v1/host/sample/test/mock-ai/program` keyed on the structured-call nodeId: entry [0] returns a valid envelope (consumed by the original run); entry [1] returns `stopReason: 'safety'` + `refusalText` (consumed by the `:fork mode: replay`). Phase 4 hosts advertising `multiAgent.executionModel.replayDeterminism.refusalDivergenceEmission: true` MUST detect the divergence at replay time, emit a `replay.divergedAtRefusal` event with `originalEnvelopeKind: 'valid'` + `replayEnvelopeKind: 'refusal'`, and fail the replay with HTTP `422` + `error.code: 'replay_diverged_at_refusal'` per `spec/v1/rest-endpoints.md §\"Common error codes\"`. Silent substitution of the refusal for the original envelope is non-conformant.",
5
+ "description": "Single `core.ai.structuredOutput` node against the conformance `mock` provider. Conformance scenario `replay-divergence-at-refusal.test.ts` pre-seeds the mock with a two-entry program via `POST /v1/host/sample/test/mock-ai/program` keyed on the divergence-structured-call nodeId: entry [0] returns a valid envelope (consumed by the original run); entry [1] returns `stopReason: 'safety'` + `refusalText` (consumed by the `:fork mode: replay`). Phase 4 hosts advertising `multiAgent.executionModel.replayDeterminism.refusalDivergenceEmission: true` MUST detect the divergence at replay time, emit a `replay.divergedAtRefusal` event with `originalEnvelopeKind: 'valid'` + `replayEnvelopeKind: 'refusal'`, and fail the replay with HTTP `422` + `error.code: 'replay_diverged_at_refusal'` per `spec/v1/rest-endpoints.md §\"Common error codes\"`. Silent substitution of the refusal for the original envelope is non-conformant.",
6
6
  "nodes": [
7
7
  {
8
- "id": "structured-call",
8
+ "id": "divergence-structured-call",
9
9
  "typeId": "core.ai.structuredOutput",
10
10
  "name": "Structured output via mock provider (Phase 4 replay-divergence probe)",
11
11
  "position": { "x": 0, "y": 0 },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "2.1.5",
3
+ "version": "2.1.7",
4
4
  "description": "Production-ready black-box conformance suite for OpenWOP v1.0 compliant servers.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -56,6 +56,6 @@
56
56
  "@openwop/spec-artifacts": "file:../spec-artifacts"
57
57
  },
58
58
  "peerDependencies": {
59
- "@openwop/spec-artifacts": "2.1.5"
59
+ "@openwop/spec-artifacts": "2.1.7"
60
60
  }
61
61
  }
package/requirements.json CHANGED
@@ -11218,7 +11218,7 @@
11218
11218
  {
11219
11219
  "id": "openwop.it.envelope-completion-distinguishes-truncation.capabilities-envelopes-reliability-completion-when-present-conforms-to-rfc-0033",
11220
11220
  "file": "envelope-completion-distinguishes-truncation.test.ts",
11221
- "line": 94,
11221
+ "line": 103,
11222
11222
  "title": "capabilities.envelopes.reliability.completion (when present) conforms to RFC 0033 §E",
11223
11223
  "explicitId": "openwop.it.envelope-completion-distinguishes-truncation.capabilities-envelopes-reliability-completion-when-present-conforms-to-rfc-0033",
11224
11224
  "citations": [
@@ -11235,7 +11235,7 @@
11235
11235
  {
11236
11236
  "id": "openwop.it.envelope-completion-distinguishes-truncation.truncation-emits-envelope-truncated-envelope-retry-attempted-with-reason-truncat",
11237
11237
  "file": "envelope-completion-distinguishes-truncation.test.ts",
11238
- "line": 117,
11238
+ "line": 126,
11239
11239
  "title": "truncation: emits envelope.truncated + envelope.retry.attempted with reason: \"truncation\"",
11240
11240
  "explicitId": "openwop.it.envelope-completion-distinguishes-truncation.truncation-emits-envelope-truncated-envelope-retry-attempted-with-reason-truncat",
11241
11241
  "citations": [
@@ -11256,7 +11256,7 @@
11256
11256
  {
11257
11257
  "id": "openwop.it.envelope-completion-distinguishes-truncation.truncation-retry-budget-strictly-greater-than-initial-rfc-0033-b-truncationbudge",
11258
11258
  "file": "envelope-completion-distinguishes-truncation.test.ts",
11259
- "line": 142,
11259
+ "line": 151,
11260
11260
  "title": "truncation: retry budget strictly greater than initial (RFC 0033 §B truncationBudgetMultiplier)",
11261
11261
  "explicitId": "openwop.it.envelope-completion-distinguishes-truncation.truncation-retry-budget-strictly-greater-than-initial-rfc-0033-b-truncationbudge",
11262
11262
  "citations": [
@@ -11269,7 +11269,7 @@
11269
11269
  {
11270
11270
  "id": "openwop.it.envelope-completion-distinguishes-truncation.schema-violation-no-envelope-truncated-envelope-retry-attempted-reason-schema-vi",
11271
11271
  "file": "envelope-completion-distinguishes-truncation.test.ts",
11272
- "line": 166,
11272
+ "line": 175,
11273
11273
  "title": "schema-violation: NO envelope.truncated; envelope.retry.attempted reason ∈ {schema-violation, parse-error}",
11274
11274
  "explicitId": "openwop.it.envelope-completion-distinguishes-truncation.schema-violation-no-envelope-truncated-envelope-retry-attempted-reason-schema-vi",
11275
11275
  "citations": [
@@ -11286,7 +11286,7 @@
11286
11286
  {
11287
11287
  "id": "openwop.it.envelope-completion-distinguishes-truncation.schema-violation-retry-budget-unchanged-from-initial-no-budget-multiplication-on",
11288
11288
  "file": "envelope-completion-distinguishes-truncation.test.ts",
11289
- "line": 196,
11289
+ "line": 205,
11290
11290
  "title": "schema-violation: retry budget UNCHANGED from initial (no budget multiplication on this path)",
11291
11291
  "explicitId": "openwop.it.envelope-completion-distinguishes-truncation.schema-violation-retry-budget-unchanged-from-initial-no-budget-multiplication-on",
11292
11292
  "citations": [
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "_comment": "Provenance of @openwop/spec-artifacts (RFC 0168 §D.2). files: SHA-256 per file; the conformance suite compares the installed peer against dist/spec-artifacts.lock.json at start.",
3
3
  "package": "@openwop/spec-artifacts",
4
- "version": "2.1.5",
5
- "corpusTag": "v2.1.5",
4
+ "version": "2.1.7",
5
+ "corpusTag": "v2.1.7",
6
6
  "files": {
7
7
  "api/.redocly.lint-ignore.yaml": "bf5a8350b88a72fa43f59605ed8d903ed24b6cfccda5e45509c9f6ed9ee4e712",
8
8
  "api/asyncapi.yaml": "d5ecb9ee6114582be3b1f662c84bfac9ae96dae7bacb853e461168f70a8e1c7d",
9
9
  "api/grpc/openwop.proto": "c3e72bb17cba514ee98feb6434e6c9b6ea6795bfd086489ec69fd882dd1ad977",
10
10
  "api/openapi.yaml": "39081c59fb696159806b0f2f9a42e7e9ff830d622fcf2ad4159b21357580a955",
11
11
  "api/redocly.yaml": "b0604c89b2ca6d5076ec25725c539dad44a741a811fe524439ee6daef8baa09f",
12
- "api/seams-v2.yaml": "190ca6cf1b7a62af8c32eac1fdaa469c26132f0e8770b4ce4755f32b615907d6",
13
- "api/v2/asyncapi.yaml": "be1ef37248284355d61aa7c81ef91632bd74ff4af26707014c9bd9a171ea1644",
14
- "api/v2/openapi.yaml": "350a1737db4b157fc0e3a9c1e429598ef66531c2f5c2bf06f293e81101ad932c",
12
+ "api/seams-v2.yaml": "a848487c183fff6a6f9117932245c2c7765b5b7da8cad1d0a6d02e669ef1b40c",
13
+ "api/v2/asyncapi.yaml": "66440a6d3f136c5d12c9ebe4dad80e993f7d8fcf5c6203a5694f06ce45f977df",
14
+ "api/v2/openapi.yaml": "0ca4bdcc6572f427369e1d55b7d3e981668cc424f4bc84f314d6d25170964d51",
15
15
  "api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
16
16
  "schemas/README.md": "0c0b737ffcf8f30e7d2809cec8a498232de710f41443212922ad8337cdde0b51",
17
17
  "schemas/a2a-task-state.schema.json": "c9365918f993f943b4b619d42551eb066a1ed33a08d895d51b395432a5b1f1bc",
@@ -200,7 +200,7 @@
200
200
  "schemas/workspace-file.schema.json": "464de85c2a068243084ee9c1d969bc7cd5d8f7948574e58450d6493c38a0e1e4",
201
201
  "spec/v1/alias-detectors.json": "fee4594ef49953953ffcd0b3813300067d16b3e65ebff2aac722034ac9b3f545",
202
202
  "spec/v1/capability-declaration-classes.json": "e7729aed5c4b4e1dd02abab0530f14cc95f5d4070fe51fb139e7f5cccefa00c6",
203
- "spec/v1/core-standard-manifest.json": "6be5bd4339d5b8dfc1a5286d7e00ee51be23d69695f55d550584cf222cc753aa",
203
+ "spec/v1/core-standard-manifest.json": "de593dd613393b6d93d90c8e76032466aaa68379a53eaf274fcd921264e51ead",
204
204
  "spec/v1/deprecations.json": "a2022c2d6eb501bf406b5d75fe2d431a3fe0158e8509125d2453985eb1bf511a",
205
205
  "spec/v1/deprecations.schema.json": "18c87e78bedc210431f795ae44c5b5d202f2f3317850d5cf86867d4f1fa1cdfb",
206
206
  "spec/v1/event-codemap.json": "3da60d884157793a360da532a9fcbbfb5285636db325a74cec94b34622186d97",
@@ -212,14 +212,14 @@
212
212
  "spec/v1/migrations.schema.json": "886779aa6c22e646db097f5df210adb018a4dd14a7b815465a18c8a7056c8f72",
213
213
  "spec/v1/operation-path-manifest.json": "5f5f4e3842669371730ebbd1aace3fb794192615f0018dc144f484ba5db82ac3",
214
214
  "spec/v1/spec-gaps.json": "6cc9962c6b969f632e07a78b52a4f61447ff579e2990cbae989866f584a86042",
215
- "spec/v2/README.md": "d5a6276ce53dee080f23e40dba3ee3980b249d417c0220390a08bb31b79e7759",
215
+ "spec/v2/README.md": "e3d8f6a13338014c349e57bf2b50a398aa1e0aa162c5adda0273e26dd6f48acd",
216
216
  "spec/v2/core/capabilities.md": "c5107a4964e487c20bbb5254cfe1f706e0d9ba7e34da9070bc43b50b385e7948",
217
217
  "spec/v2/core/conformance.md": "622084e3c74c4549087f4d861b1bb98cec201a0c5aec23788d27f53a355fde3b",
218
218
  "spec/v2/core/connection-packs.md": "1d759681e2962f5103228c52c45f24607ed4bb9ae73e30605f690c1d5a59e9ee",
219
219
  "spec/v2/core/errors.md": "74fdb5862f432a831cf0c2eadbe0c8cca6b2660ec578180ae33db54773a1df28",
220
220
  "spec/v2/core/events.md": "defe0969d6ec7c980f415cc0d78b9d065782ac7e5638b12a44aa4895a35d6126",
221
221
  "spec/v2/core/form-content-packs.md": "6ffaae85d4550be48dd53fb6e2959af8aceb35bd87c5343b722885084f197713",
222
- "spec/v2/core/headers.md": "37b7b51639a19de6f913ff6105009235a0d31385f931968aeb5b4a28e1536ca3",
222
+ "spec/v2/core/headers.md": "a795c8cd3e5f004b05dad9603d14b92b639a24ccd23b183716cb01097a106119",
223
223
  "spec/v2/core/idempotency.md": "b86c352d964aebad532727f314e26ed2177d49de382076fa64d3162e2f56118d",
224
224
  "spec/v2/core/identity.md": "dd5299f9cdf54640870e1fffae11edb820cf6396ea298c4ebd14c649c4bd4a87",
225
225
  "spec/v2/core/interop.md": "d4c404e469e1d9d9be88bc2c939d777e7d7c1d0419b1251c18770f039838a401",
@@ -270,8 +270,8 @@
270
270
  "spec/v2/path-manifest.json": "a6f4e652e866889e8bde815874062f62929aee95dc8e58e7152c56adee262163",
271
271
  "spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
272
272
  "spec/v2/profiles.json": "0636f19fceae625390003a347e70ef4797d84766b5c24ce8a02cea52aadebca4",
273
- "spec/v2/release.json": "1e7a0e98832b3a6a9a0646823efa56e1317769ebee65b18e3f17e5f1295362c8",
273
+ "spec/v2/release.json": "2bacef4931ec682b7bf003746eb1f69e3b945bbb93b98d76ddf86128e9922be5",
274
274
  "spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624"
275
275
  },
276
- "corpusCommit": "673fbee887211dfcf736eb2a5a57e050881f92fd"
276
+ "corpusCommit": "4c50dd0dc8b411090dfeeb3a5bbf9466474807c9"
277
277
  }
@@ -36,7 +36,16 @@ import { req } from '../lib/requirement-ids.js';
36
36
  import { softSkip } from '../lib/soft-skip.js';
37
37
 
38
38
  const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
39
- const NODE_ID = 'structured-call';
39
+ /**
40
+ * The mock-AI program seam is keyed by `nodeId` alone, so a node id is the
41
+ * unit of isolation between scenarios — see `host-sample-test-seams.md` §5.
42
+ * This file drives two fixtures, so it resolves the node per fixture rather
43
+ * than holding one shared id.
44
+ */
45
+ const NODE_OF: Record<string, string> = {
46
+ 'conformance-envelope-truncated': 'truncated-structured-call',
47
+ 'conformance-envelope-retry-attempted': 'retry-attempted-structured-call',
48
+ };
40
49
 
41
50
  interface DiscoveryDoc {
42
51
  capabilities?: {
@@ -68,8 +77,8 @@ async function readDiscovery(): Promise<DiscoveryDoc | null> {
68
77
  }
69
78
  }
70
79
 
71
- async function programMock(program: Array<Record<string, unknown>>): Promise<{ status: number }> {
72
- const res = await driver.post('/v1/host/sample/test/mock-ai/program', { nodeId: NODE_ID, program });
80
+ async function programMock(fixture: string, program: Array<Record<string, unknown>>): Promise<{ status: number }> {
81
+ const res = await driver.post('/v1/host/sample/test/mock-ai/program', { nodeId: NODE_OF[fixture], program });
73
82
  return { status: res.status };
74
83
  }
75
84
 
@@ -84,8 +93,8 @@ async function startRunAndRead(workflowId: string): Promise<{ events: RunEvent[]
84
93
  return { events, terminal };
85
94
  }
86
95
 
87
- async function lastBudget(): Promise<number | null> {
88
- const res = await driver.get(`/v1/host/sample/test/mock-ai/last-dispatch-budget?nodeId=${encodeURIComponent(NODE_ID)}`);
96
+ async function lastBudget(fixture: string): Promise<number | null> {
97
+ const res = await driver.get(`/v1/host/sample/test/mock-ai/last-dispatch-budget?nodeId=${encodeURIComponent(NODE_OF[fixture] as string)}`);
89
98
  if (res.status !== 200) return null;
90
99
  return (res.json as { maxTokens?: number | null }).maxTokens ?? null;
91
100
  }
@@ -118,7 +127,7 @@ describe.skipIf(HTTP_SKIP)('envelope-completion-distinguishes-truncation: trunca
118
127
  if (!isFixtureAdvertised(TRUNCATED_FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(TRUNCATED_FIXTURE)` returned early');
119
128
  const d = await readDiscovery();
120
129
  if (capabilityFamily<{ reasoning?: Record<string, unknown>; tierOneSubsetCompliance?: unknown; reliability?: { completion?: Record<string, unknown> } & Record<string, unknown> }>(d, 'envelopes')?.reliability?.completion?.distinguishesTruncation !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `capabilityFamily<{ reasoning?: Record<string, unknown>; tierOneSubsetCompliance?: unknown; reliability?: { completion?: Record<string, unknown> } & Record<stri…');
121
- const seed = await programMock([
130
+ const seed = await programMock(TRUNCATED_FIXTURE, [
122
131
  { stopReason: 'max_tokens', content: '{"partial' },
123
132
  { stopReason: 'end_turn', content: '{"valid":true}' },
124
133
  ]);
@@ -143,14 +152,14 @@ describe.skipIf(HTTP_SKIP)('envelope-completion-distinguishes-truncation: trunca
143
152
  if (!isFixtureAdvertised(TRUNCATED_FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(TRUNCATED_FIXTURE)` returned early');
144
153
  const d = await readDiscovery();
145
154
  if (capabilityFamily<{ reasoning?: Record<string, unknown>; tierOneSubsetCompliance?: unknown; reliability?: { completion?: Record<string, unknown> } & Record<string, unknown> }>(d, 'envelopes')?.reliability?.completion?.distinguishesTruncation !== true) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `capabilityFamily<{ reasoning?: Record<string, unknown>; tierOneSubsetCompliance?: unknown; reliability?: { completion?: Record<string, unknown> } & Record<stri…');
146
- const seed = await programMock([
155
+ const seed = await programMock(TRUNCATED_FIXTURE, [
147
156
  { stopReason: 'max_tokens', content: '{"partial' },
148
157
  { stopReason: 'end_turn', content: '{"valid":true}' },
149
158
  ]);
150
159
  if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
151
160
 
152
161
  await startRunAndRead(TRUNCATED_FIXTURE);
153
- const budget = await lastBudget();
162
+ const budget = await lastBudget(TRUNCATED_FIXTURE);
154
163
  if (budget === null) return softSkip('blocked', 'precondition not met — `budget === null` returned early (seam, prior step, or fixture unavailable)');
155
164
  expect(
156
165
  budget,
@@ -165,7 +174,7 @@ describe.skipIf(HTTP_SKIP)('envelope-completion-distinguishes-truncation: trunca
165
174
  describe.skipIf(HTTP_SKIP)('envelope-completion-distinguishes-truncation: schema-violation path (RFC 0033 §C)', () => {
166
175
  it('schema-violation: NO envelope.truncated; envelope.retry.attempted reason ∈ {schema-violation, parse-error}', async () => {
167
176
  if (!isFixtureAdvertised(SCHEMA_VIOLATION_FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(SCHEMA_VIOLATION_FIXTURE)` returned early');
168
- const seed = await programMock([
177
+ const seed = await programMock(SCHEMA_VIOLATION_FIXTURE, [
169
178
  { content: 'not valid json' },
170
179
  { content: '{"valid":true}' },
171
180
  ]);
@@ -195,14 +204,14 @@ describe.skipIf(HTTP_SKIP)('envelope-completion-distinguishes-truncation: schema
195
204
 
196
205
  it('schema-violation: retry budget UNCHANGED from initial (no budget multiplication on this path)', async () => {
197
206
  if (!isFixtureAdvertised(SCHEMA_VIOLATION_FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(SCHEMA_VIOLATION_FIXTURE)` returned early');
198
- const seed = await programMock([
207
+ const seed = await programMock(SCHEMA_VIOLATION_FIXTURE, [
199
208
  { content: 'not valid json' },
200
209
  { content: '{"valid":true}' },
201
210
  ]);
202
211
  if (seed.status === 404) return softSkip('blocked', 'precondition not met — `seed.status === 404` returned early (seam, prior step, or fixture unavailable)');
203
212
 
204
213
  await startRunAndRead(SCHEMA_VIOLATION_FIXTURE);
205
- const budget = await lastBudget();
214
+ const budget = await lastBudget(SCHEMA_VIOLATION_FIXTURE);
206
215
  if (budget === null) return softSkip('blocked', 'precondition not met — `budget === null` returned early (seam, prior step, or fixture unavailable)');
207
216
  // The schema-violation fixture doesn't set maxTokens explicitly →
208
217
  // budget snapshots whatever the host's default is on each call.
@@ -33,7 +33,7 @@ import { softSkip } from '../lib/soft-skip.js';
33
33
 
34
34
  const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
35
35
  const FIXTURE = 'conformance-envelope-nl-to-format-engaged';
36
- const NODE_ID = 'structured-call';
36
+ const NODE_ID = 'nl-to-format-structured-call';
37
37
 
38
38
  interface RunEvent {
39
39
  type: string;
@@ -126,7 +126,7 @@ import { req } from '../lib/requirement-ids.js';
126
126
  import { softSkip } from '../lib/soft-skip.js';
127
127
 
128
128
  const RECOVERY_FIXTURE = 'conformance-envelope-recovery-applied';
129
- const RECOVERY_NODE_ID = 'structured-call';
129
+ const RECOVERY_NODE_ID = 'recovery-applied-structured-call';
130
130
 
131
131
  interface ProgrammedRunEvent {
132
132
  type: string;
@@ -196,7 +196,7 @@ import { req } from '../lib/requirement-ids.js';
196
196
  import { softSkip } from '../lib/soft-skip.js';
197
197
 
198
198
  const E2E_FIXTURE = 'conformance-envelope-refusal';
199
- const E2E_NODE_ID = 'structured-call';
199
+ const E2E_NODE_ID = 'refusal-structured-call';
200
200
 
201
201
  interface E2eEvent {
202
202
  type: string;
@@ -119,7 +119,7 @@ import { req } from '../lib/requirement-ids.js';
119
119
  import { softSkip } from '../lib/soft-skip.js';
120
120
 
121
121
  const FIXTURE = 'conformance-envelope-retry-attempted';
122
- const NODE_ID = 'structured-call';
122
+ const NODE_ID = 'retry-attempted-structured-call';
123
123
 
124
124
  const RFC_0032_REASONS = new Set([
125
125
  'schema-violation',
@@ -33,7 +33,7 @@ import { softSkip } from '../lib/soft-skip.js';
33
33
 
34
34
  const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
35
35
  const FIXTURE = 'conformance-envelope-retry-exhausted';
36
- const NODE_ID = 'structured-call';
36
+ const NODE_ID = 'retry-exhausted-structured-call';
37
37
 
38
38
  const RFC_0032_REASONS = new Set([
39
39
  'schema-violation',
@@ -25,7 +25,7 @@ import { softSkip } from '../lib/soft-skip.js';
25
25
 
26
26
  const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
27
27
  const FIXTURE = 'conformance-envelope-truncated';
28
- const NODE_ID = 'structured-call';
28
+ const NODE_ID = 'truncated-structured-call';
29
29
 
30
30
  interface RunEvent {
31
31
  type: string;
@@ -24,7 +24,7 @@ import { softSkip } from '../lib/soft-skip.js';
24
24
 
25
25
  const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
26
26
  const FIXTURE = 'conformance-envelope-truncation-cap-exhaustion';
27
- const NODE_ID = 'structured-call';
27
+ const NODE_ID = 'truncation-cap-structured-call';
28
28
 
29
29
  interface RunEvent {
30
30
  type: string;
@@ -227,7 +227,7 @@ describe.skipIf(HTTP_SKIP)('replay-divergence-at-refusal: behavioral (RFC 0041
227
227
  it('Phase 4 host MUST emit replay.divergedAtRefusal + fail with replay_diverged_at_refusal when original=valid + replay=refusal', async (ctx) => {
228
228
  if (!(await gateOnPhase4(ctx))) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await gateOnPhase4(ctx))` returned early');
229
229
 
230
- const NODE_ID = 'structured-call';
230
+ const NODE_ID = 'divergence-structured-call';
231
231
  // Original program: valid envelope. Replay program (set after the
232
232
  // original completes): refusal. Programming twice is the spec-canonical
233
233
  // pattern — see spec/v1/host-sample-test-seams.md §5.
@@ -320,7 +320,7 @@ describe.skipIf(HTTP_SKIP)('replay-divergence-at-refusal: behavioral (RFC 0041
320
320
  it('Phase 4 host MUST emit replay.divergedAtRefusal + fail with replay_diverged_at_refusal when original=refusal + replay=valid (symmetric case)', async (ctx) => {
321
321
  if (!(await gateOnPhase4(ctx))) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await gateOnPhase4(ctx))` returned early');
322
322
 
323
- const NODE_ID = 'structured-call';
323
+ const NODE_ID = 'divergence-structured-call';
324
324
  // Symmetric: original=refusal, replay=valid.
325
325
  const programStatus = await programMock(NODE_ID, [
326
326
  { content: 'safety-refused-for-conformance', stopReason: 'safety' as const, refusalText: 'safety-refused-for-conformance' },
@@ -75,7 +75,20 @@ describe('RFC 0173 §B — compensation-read-projection (gated on compensation)'
75
75
  const doc = await discovery();
76
76
  if (!doc) return softSkip('blocked', 'discovery unreachable');
77
77
  if (!(await gateFamily('compensation'))) return softSkip('inapplicable', 'compensation family not advertised — no obligation (gate recorded under openwop.family.compensation)');
78
- const res = await driver.get('/runs/conformance-no-such-run-0173/compensation');
78
+ // The unknown id MUST be TENANT-BOUND, not bare. A bare id is the v1
79
+ // spelling: `identity.md` §5 admits it through the overlap and requires a
80
+ // host that advertises no `1.x` member to refuse it `400 validation_error`
81
+ // — so on a retired host this probe asked about SHAPE and never reached the
82
+ // existence check it asserts. Found 2026-09-13 by the reference host's
83
+ // retirement lane on its first run; the host was right and the probe was
84
+ // written for the overlap. A bound id answers 404 on both, so this needs no
85
+ // branch on the advertisement — it just needs to stop using the one
86
+ // spelling that is conditional.
87
+ const probe = await driver.post('/runs', { workflowId: FIXTURE });
88
+ const tenant = String((probe.json as { runId?: string }).runId ?? '').split('/')[0];
89
+ if (!tenant) return softSkip('blocked', 'could not learn the caller tenant from a created run, so no tenant-bound unknown id can be formed');
90
+ const unknown = `${tenant}/conformance-no-such-run-0173`;
91
+ const res = await driver.get(`/runs/${encodeURIComponent(unknown)}/compensation`);
79
92
  expect(res.status, req('openwop.requirement.0173.compensation-read-projection.not-found', 'openapi.yaml getRunCompensation 404', 'an unknown runId MUST answer 404')).toBe(404);
80
93
  expect(
81
94
  readErrorCode(res.json),