@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 +40 -0
- package/dist/spec-artifacts.lock.json +2 -2
- package/fixtures/conformance-envelope-nl-to-format-engaged.json +1 -1
- package/fixtures/conformance-envelope-recovery-applied.json +2 -2
- package/fixtures/conformance-envelope-refusal.json +1 -1
- package/fixtures/conformance-envelope-retry-attempted.json +2 -2
- package/fixtures/conformance-envelope-retry-exhausted.json +1 -1
- package/fixtures/conformance-envelope-truncated.json +1 -1
- package/fixtures/conformance-envelope-truncation-cap-exhaustion.json +1 -1
- package/fixtures/conformance-phase4-nondet-tool.json +2 -2
- package/fixtures/conformance-phase4-replay-divergence.json +2 -2
- package/package.json +2 -2
- package/requirements.json +5 -5
- package/schemas/CORPUS-STAMP.json +10 -10
- package/src/scenarios/envelope-completion-distinguishes-truncation.test.ts +20 -11
- package/src/scenarios/envelope-nl-to-format-engaged.test.ts +1 -1
- package/src/scenarios/envelope-recovery-applied.test.ts +1 -1
- package/src/scenarios/envelope-refusal-shape.test.ts +1 -1
- package/src/scenarios/envelope-retry-attempted.test.ts +1 -1
- package/src/scenarios/envelope-retry-exhausted.test.ts +1 -1
- package/src/scenarios/envelope-truncated.test.ts +1 -1
- package/src/scenarios/envelope-truncation-cap-exhaustion.test.ts +1 -1
- package/src/scenarios/replay-divergence-at-refusal.test.ts +2 -2
- package/src/scenarios/v2-compensation-read-projection.test.ts +14 -1
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
|
|
@@ -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.
|
|
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.
|
|
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":
|
|
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":
|
|
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":
|
|
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":
|
|
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":
|
|
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
|
-
"corpusTag": "v2.1.
|
|
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": "
|
|
13
|
-
"api/v2/asyncapi.yaml": "
|
|
14
|
-
"api/v2/openapi.yaml": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
273
|
+
"spec/v2/release.json": "2bacef4931ec682b7bf003746eb1f69e3b945bbb93b98d76ddf86128e9922be5",
|
|
274
274
|
"spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624"
|
|
275
275
|
},
|
|
276
|
-
"corpusCommit": "
|
|
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
|
-
|
|
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:
|
|
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(
|
|
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
|
-
|
|
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),
|