@openwop/openwop-conformance 1.123.0 → 1.125.0

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.
Files changed (27) hide show
  1. package/README.md +2 -2
  2. package/dist/lib/scenario-disposition.js +14 -1
  3. package/package.json +1 -1
  4. package/schemas/CORPUS-STAMP.json +2 -2
  5. package/src/lib/error-envelope.ts +77 -0
  6. package/src/lib/scenario-disposition.ts +15 -1
  7. package/src/scenarios/conformance-execution-witness.test.ts +12 -0
  8. package/src/scenarios/conversationCapabilityNegotiation.test.ts +3 -2
  9. package/src/scenarios/cross-engine-append-behavior.test.ts +26 -5
  10. package/src/scenarios/cross-host-ancestry-endpoint.test.ts +8 -4
  11. package/src/scenarios/cross-host-causation-shape.test.ts +4 -2
  12. package/src/scenarios/data-residency-admission.test.ts +6 -6
  13. package/src/scenarios/envelope-recovery-applied.test.ts +3 -2
  14. package/src/scenarios/envelope-refusal-shape.test.ts +4 -3
  15. package/src/scenarios/error-envelope-canonical-shape.test.ts +64 -0
  16. package/src/scenarios/fs-path-traversal.test.ts +3 -2
  17. package/src/scenarios/multi-agent-memory-lifecycle.test.ts +8 -4
  18. package/src/scenarios/multi-region-idempotency-behavior.test.ts +29 -7
  19. package/src/scenarios/provider-usage.test.ts +2 -2
  20. package/src/scenarios/replay-divergence-at-refusal.test.ts +12 -6
  21. package/src/scenarios/replay-observable-sequence-determinism.test.ts +4 -2
  22. package/src/scenarios/sandbox-mvp-behavior.test.ts +20 -10
  23. package/src/scenarios/table-schema-enforcement.test.ts +2 -2
  24. package/src/scenarios/voice-streamref-tenant-bound.test.ts +2 -1
  25. package/src/scenarios/voice-transcription-streaming.test.ts +2 -1
  26. package/src/scenarios/voice-transcription-unadvertised.test.ts +2 -1
  27. package/src/scenarios/workload-identity-behavior.test.ts +7 -4
@@ -31,7 +31,8 @@
31
31
  */
32
32
 
33
33
  import { describe, it, expect } from 'vitest';
34
- import { softSkip } from '../lib/soft-skip.js';
34
+ import { softSkip, seamAbsent } from '../lib/soft-skip.js';
35
+ import { capabilityFamily } from '../lib/discovery-capabilities.js';
35
36
  import { driver } from '../lib/driver.js';
36
37
 
37
38
  const HTTP_SKIP = !process.env.OPENWOP_BASE_URL;
@@ -56,6 +57,21 @@ async function simulatePartition(claims: ConflictClaim[]): Promise<{ status: num
56
57
  return { status: res.status, body: (res.json as ConvergenceResult) ?? {} };
57
58
  }
58
59
 
60
+ /**
61
+ * The multi-region simulator seam answered 404. `blocked` when the host advertises
62
+ * a cross-region posture (`idempotency.crossRegion` present) it has made
63
+ * unobservable, `inapplicable` when it advertises none (RFC 0148 §A).
64
+ */
65
+ async function noteSimulatorAbsent(): Promise<void> {
66
+ const disco = await driver.get('/.well-known/openwop');
67
+ const idem = capabilityFamily<{ crossRegion?: unknown }>(disco.json, 'idempotency');
68
+ if (idem?.crossRegion !== undefined) {
69
+ seamAbsent(`host advertises \`idempotency.crossRegion: ${String(idem.crossRegion)}\` but \`POST /v1/host/sample/test/multi-region/simulate-partition\` answered 404 — the convergence rule is unobservable (host-sample-test-seams.md §6)`);
70
+ } else {
71
+ softSkip('inapplicable', 'optional advertisement — `idempotency.crossRegion` not advertised by this host, and the multi-region simulator seam is absent (RFC 0036 §C)');
72
+ }
73
+ }
74
+
59
75
  describe.skipIf(HTTP_SKIP)('multi-region-idempotency-behavior: convergence rule (RFC 0036 §C)', () => {
60
76
  it('two-region conflict resolves to the lex-min runId per annex §"Convergence rule"', async (ctx) => {
61
77
  const probe = await simulatePartition([
@@ -63,8 +79,9 @@ describe.skipIf(HTTP_SKIP)('multi-region-idempotency-behavior: convergence rule
63
79
  { runId: 'run-a-west', tenantId: 't1', endpoint: 'POST /v1/runs', key: 'idem-1', region: 'eu-west-1' },
64
80
  ]);
65
81
  if (probe.status === 404) {
82
+ await noteSimulatorAbsent();
66
83
  ctx.skip(); // host doesn't expose the simulator seam
67
- return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (seam, prior step, or fixture unavailable)');
84
+ return;
68
85
  }
69
86
  expect(
70
87
  probe.status,
@@ -89,8 +106,9 @@ describe.skipIf(HTTP_SKIP)('multi-region-idempotency-behavior: convergence rule
89
106
  { runId: 'mmm-2', tenantId: 't1', endpoint: 'POST /v1/runs', key: 'idem-2', region: 'r3' },
90
107
  ]);
91
108
  if (probe.status === 404) {
109
+ await noteSimulatorAbsent();
92
110
  ctx.skip();
93
- return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (seam, prior step, or fixture unavailable)');
111
+ return;
94
112
  }
95
113
  expect(probe.status).toBe(200);
96
114
  expect(
@@ -115,8 +133,9 @@ describe.skipIf(HTTP_SKIP)('multi-region-idempotency-behavior: convergence rule
115
133
  { runId: 'run-a', tenantId: 't1', endpoint: 'POST /v1/runs', key: 'idem-3', region: 'r2' },
116
134
  ]);
117
135
  if (probe.status === 404) {
136
+ await noteSimulatorAbsent();
118
137
  ctx.skip();
119
- return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (seam, prior step, or fixture unavailable)');
138
+ return;
120
139
  }
121
140
  expect(probe.status).toBe(200);
122
141
  const redirects = probe.body.cacheRedirects ?? [];
@@ -144,8 +163,9 @@ describe.skipIf(HTTP_SKIP)('multi-region-idempotency-behavior: convergence rule
144
163
  { runId: 'run-a', tenantId: 't1', endpoint: 'POST /v1/runs', key: 'idem-4', region: 'r2' },
145
164
  ]);
146
165
  if (probe.status === 404) {
166
+ await noteSimulatorAbsent();
147
167
  ctx.skip();
148
- return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (seam, prior step, or fixture unavailable)');
168
+ return;
149
169
  }
150
170
  expect(probe.status).toBe(200);
151
171
  expect(
@@ -165,8 +185,9 @@ describe.skipIf(HTTP_SKIP)('multi-region-idempotency-behavior: convergence rule
165
185
  ];
166
186
  const p1 = await simulatePartition(claims);
167
187
  if (p1.status === 404) {
188
+ await noteSimulatorAbsent();
168
189
  ctx.skip();
169
- return softSkip('blocked', 'precondition not met — `p1.status === 404` returned early (seam, prior step, or fixture unavailable)');
190
+ return;
170
191
  }
171
192
  expect(p1.status).toBe(200);
172
193
  const p2 = await simulatePartition([claims[2]!, claims[0]!, claims[1]!]);
@@ -190,8 +211,9 @@ describe.skipIf(HTTP_SKIP)('multi-region-idempotency-behavior: convergence rule
190
211
  { runId: 'r2', tenantId: 't2', endpoint: 'POST /v1/runs', key: 'idem-6', region: 'r2' },
191
212
  ]);
192
213
  if (probe.status === 404) {
214
+ await noteSimulatorAbsent();
193
215
  ctx.skip();
194
- return softSkip('blocked', 'precondition not met — `probe.status === 404` returned early (seam, prior step, or fixture unavailable)');
216
+ return;
195
217
  }
196
218
  expect(
197
219
  probe.status,
@@ -20,6 +20,7 @@
20
20
  */
21
21
 
22
22
  import { describe, it, expect } from 'vitest';
23
+ import { readErrorCode } from '../lib/error-envelope.js';
23
24
  import Ajv2020 from 'ajv/dist/2020.js';
24
25
  import { readFileSync } from 'node:fs';
25
26
  import { join } from 'node:path';
@@ -179,8 +180,7 @@ describe('provider-usage: event presence via emit-seam + event-log query (RFC 00
179
180
  res.status,
180
181
  driver.describe('SECURITY/invariants.yaml provider-usage-no-credential-leak', 'payload with credentialRef-shaped content MUST be refused'),
181
182
  ).toBe(400);
182
- const body = res.json as { error?: { code?: string } };
183
- expect(body.error?.code).toBe('provider_usage_credential_leak');
183
+ expect(readErrorCode(res.json)).toBe('provider_usage_credential_leak');
184
184
  await resetTestSeam();
185
185
  });
186
186
  });
@@ -78,13 +78,15 @@ describe.skipIf(HTTP_SKIP)('replay-divergence-at-refusal: advertisement shape (R
78
78
  it('replayDeterminism (when present) conforms to RFC 0041 §D', async (ctx) => {
79
79
  const d = await readDiscovery();
80
80
  if (d === null) {
81
+ softSkip('blocked', 'precondition not met — `d === null` returned early (seam, prior step, or fixture unavailable)');
81
82
  ctx.skip();
82
- return softSkip('blocked', 'precondition not met — `d === null` returned early (seam, prior step, or fixture unavailable)');
83
+ return;
83
84
  }
84
85
  const rd = capabilityFamily<{ executionModel?: { [k: string]: unknown; crossHostCausation?: Record<string, unknown>; replayDeterminism?: Record<string, unknown> } }>(d, 'multiAgent')?.executionModel?.replayDeterminism;
85
86
  if (rd === undefined) {
87
+ softSkip('inapplicable', 'optional advertisement — `multiAgent.executionModel.replayDeterminism` not advertised by this host (RFC 0041 §D)');
86
88
  ctx.skip(); // optional advertisement — host hasn't opted in
87
- return softSkip('blocked', 'precondition not met — `rd === undefined` returned early (seam, prior step, or fixture unavailable)');
89
+ return;
88
90
  }
89
91
 
90
92
  expect(
@@ -192,8 +194,9 @@ describe.skipIf(HTTP_SKIP)('replay-divergence-at-refusal: behavioral (RFC 0041
192
194
  { content: validEnv, stopReason: 'end_turn' as const },
193
195
  ]);
194
196
  if (programStatus === 404) {
197
+ softSkip('blocked', 'precondition not met — `programStatus === 404` returned early (seam, prior step, or fixture unavailable)');
195
198
  ctx.skip(); // mock-AI program seam not exposed — soft-skip
196
- return softSkip('blocked', 'precondition not met — `programStatus === 404` returned early (seam, prior step, or fixture unavailable)');
199
+ return;
197
200
  }
198
201
  expect(programStatus).toBe(200);
199
202
 
@@ -201,8 +204,9 @@ describe.skipIf(HTTP_SKIP)('replay-divergence-at-refusal: behavioral (RFC 0041
201
204
  workflowId: 'conformance-phase4-replay-divergence',
202
205
  });
203
206
  if (createRes.status === 404 || createRes.status === 422) {
207
+ softSkip('blocked', 'precondition not met — `createRes.status === 404 || createRes.status === 422` returned early (seam, prior step, or fixture unavailable)');
204
208
  ctx.skip(); // fixture not advertised
205
- return softSkip('blocked', 'precondition not met — `createRes.status === 404 || createRes.status === 422` returned early (seam, prior step, or fixture unavailable)');
209
+ return;
206
210
  }
207
211
  expect(createRes.status).toBe(201);
208
212
  const sourceRunId = (createRes.json as { runId: string }).runId;
@@ -273,8 +277,9 @@ describe.skipIf(HTTP_SKIP)('replay-divergence-at-refusal: behavioral (RFC 0041
273
277
  { content: 'safety-refused-for-conformance', stopReason: 'safety' as const, refusalText: 'safety-refused-for-conformance' },
274
278
  ]);
275
279
  if (programStatus === 404) {
280
+ softSkip('blocked', 'precondition not met — `programStatus === 404` returned early (seam, prior step, or fixture unavailable)');
276
281
  ctx.skip();
277
- return softSkip('blocked', 'precondition not met — `programStatus === 404` returned early (seam, prior step, or fixture unavailable)');
282
+ return;
278
283
  }
279
284
  expect(programStatus).toBe(200);
280
285
 
@@ -282,8 +287,9 @@ describe.skipIf(HTTP_SKIP)('replay-divergence-at-refusal: behavioral (RFC 0041
282
287
  workflowId: 'conformance-phase4-replay-divergence',
283
288
  });
284
289
  if (createRes.status === 404 || createRes.status === 422) {
290
+ softSkip('blocked', 'precondition not met — `createRes.status === 404 || createRes.status === 422` returned early (seam, prior step, or fixture unavailable)');
285
291
  ctx.skip();
286
- return softSkip('blocked', 'precondition not met — `createRes.status === 404 || createRes.status === 422` returned early (seam, prior step, or fixture unavailable)');
292
+ return;
287
293
  }
288
294
  expect(createRes.status).toBe(201);
289
295
  const sourceRunId = (createRes.json as { runId: string }).runId;
@@ -41,7 +41,7 @@
41
41
  */
42
42
 
43
43
  import { describe, it, expect } from 'vitest';
44
- import { softSkip } from '../lib/soft-skip.js';
44
+ import { softSkip, seamAbsent } from '../lib/soft-skip.js';
45
45
  import { driver } from '../lib/driver.js';
46
46
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
47
47
 
@@ -85,6 +85,7 @@ async function gateOnPhase4(ctx: { skip: () => void }): Promise<boolean> {
85
85
  const em = capabilityFamily<{ executionModel?: ExecutionModelCaps }>(d, 'multiAgent')?.executionModel;
86
86
  const version = typeof em?.version === 'number' ? em.version : 0;
87
87
  if (em?.replayDeterminism?.supported !== true || version < 4) {
88
+ softSkip('inapplicable', 'optional advertisement — `multiAgent.executionModel.replayDeterminism.supported` with `version >= 4` not advertised by this host (RFC 0041 §C)');
88
89
  ctx.skip();
89
90
  return false;
90
91
  }
@@ -152,7 +153,8 @@ function stripVolatile(ev: RunEventDoc): unknown {
152
153
  async function startFixtureRun(ctx: { skip: () => void }): Promise<string | null> {
153
154
  const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
154
155
  if (create.status === 404 || create.status === 422) {
155
- ctx.skip(); // fixture not advertised by this host
156
+ seamAbsent(`fixture \`${FIXTURE}\` not registered on this host (POST /v1/runs → ${create.status}) while replayDeterminism is advertised`);
157
+ ctx.skip();
156
158
  return null;
157
159
  }
158
160
  expect(create.status).toBe(201);
@@ -82,8 +82,9 @@ async function invoke(typeId: string, args: Record<string, unknown> = {}, allowe
82
82
  describe.skipIf(HTTP_SKIP)('sandbox-mvp-behavior: RFC 0035 §B failure-mode invariants (node:vm MVP)', () => {
83
83
  it('host-fs-escape — fs access from sandboxed code fails closed', async (ctx) => {
84
84
  if (!(await isSandboxAdvertised())) {
85
+ softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
85
86
  ctx.skip();
86
- return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
87
+ return;
87
88
  }
88
89
  const probe = await invoke('misbehave.fs-escape-read');
89
90
  expect(probe.status).toBe(200);
@@ -105,8 +106,9 @@ describe.skipIf(HTTP_SKIP)('sandbox-mvp-behavior: RFC 0035 §B failure-mode inva
105
106
 
106
107
  it('host-env-leak — process.env access from sandboxed code fails closed', async (ctx) => {
107
108
  if (!(await isSandboxAdvertised())) {
109
+ softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
108
110
  ctx.skip();
109
- return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
111
+ return;
110
112
  }
111
113
  const probe = await invoke('misbehave.env-leak');
112
114
  expect(probe.status).toBe(200);
@@ -122,8 +124,9 @@ describe.skipIf(HTTP_SKIP)('sandbox-mvp-behavior: RFC 0035 §B failure-mode inva
122
124
 
123
125
  it('network-escape — http/net access from sandboxed code fails closed', async (ctx) => {
124
126
  if (!(await isSandboxAdvertised())) {
127
+ softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
125
128
  ctx.skip();
126
- return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
129
+ return;
127
130
  }
128
131
  const probe = await invoke('misbehave.network-escape');
129
132
  expect(probe.status).toBe(200);
@@ -139,8 +142,9 @@ describe.skipIf(HTTP_SKIP)('sandbox-mvp-behavior: RFC 0035 §B failure-mode inva
139
142
 
140
143
  it('host-process-escape — child_process access from sandboxed code fails closed', async (ctx) => {
141
144
  if (!(await isSandboxAdvertised())) {
145
+ softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
142
146
  ctx.skip();
143
- return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
147
+ return;
144
148
  }
145
149
  const probe = await invoke('misbehave.process-escape');
146
150
  expect(probe.status).toBe(200);
@@ -153,8 +157,9 @@ describe.skipIf(HTTP_SKIP)('sandbox-mvp-behavior: RFC 0035 §B failure-mode inva
153
157
 
154
158
  it('sandbox-timeout — runaway loop terminated by wallClockLimitMs', async (ctx) => {
155
159
  if (!(await isSandboxAdvertised())) {
160
+ softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
156
161
  ctx.skip();
157
- return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
162
+ return;
158
163
  }
159
164
  const start = Date.now();
160
165
  const probe = await invoke('misbehave.timeout');
@@ -179,8 +184,9 @@ describe.skipIf(HTTP_SKIP)('sandbox-mvp-behavior: RFC 0035 §B failure-mode inva
179
184
 
180
185
  it('cross-pack-mutation — fresh vm context per invocation, no state leaks', async (ctx) => {
181
186
  if (!(await isSandboxAdvertised())) {
187
+ softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
182
188
  ctx.skip();
183
- return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
189
+ return;
184
190
  }
185
191
  const r1 = await invoke('misbehave.cross-pack-mutate');
186
192
  const r2 = await invoke('misbehave.cross-pack-mutate');
@@ -205,8 +211,9 @@ describe.skipIf(HTTP_SKIP)('sandbox-mvp-behavior: RFC 0035 §B failure-mode inva
205
211
 
206
212
  it('capability-gate-respected — host call NOT in allowedHostCalls fails with sandbox_capability_denied', async (ctx) => {
207
213
  if (!(await isSandboxAdvertised())) {
214
+ softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
208
215
  ctx.skip();
209
- return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
216
+ return;
210
217
  }
211
218
  const probe = await invoke('misbehave.capability-gate-violation', {}, []);
212
219
  expect(probe.status).toBe(200);
@@ -228,8 +235,9 @@ describe.skipIf(HTTP_SKIP)('sandbox-mvp-behavior: RFC 0035 §B failure-mode inva
228
235
 
229
236
  it('memory-exceeded — runaway allocation fails with sandbox_memory_exceeded', async (ctx) => {
230
237
  if (!(await isSandboxAdvertised())) {
238
+ softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
231
239
  ctx.skip();
232
- return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
240
+ return;
233
241
  }
234
242
  const probe = await invoke('misbehave.memory-bomb');
235
243
  expect(probe.status).toBe(200);
@@ -255,8 +263,9 @@ describe.skipIf(HTTP_SKIP)('sandbox-mvp-behavior: RFC 0035 §B failure-mode inva
255
263
 
256
264
  it('well-behaved.host-fetch — allowedHostCalls=[fetch] permits the host call', async (ctx) => {
257
265
  if (!(await isSandboxAdvertised())) {
266
+ softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
258
267
  ctx.skip();
259
- return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
268
+ return;
260
269
  }
261
270
  const probe = await invoke('well-behaved.host-fetch', {}, ['fetch']);
262
271
  expect(probe.status).toBe(200);
@@ -271,8 +280,9 @@ describe.skipIf(HTTP_SKIP)('sandbox-mvp-behavior: RFC 0035 §B failure-mode inva
271
280
 
272
281
  it('well-behaved.echo — sandboxed code returns args round-trip when no escape attempt', async (ctx) => {
273
282
  if (!(await isSandboxAdvertised())) {
283
+ softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
274
284
  ctx.skip();
275
- return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!(await isSandboxAdvertised())` returned early');
285
+ return;
276
286
  }
277
287
  const probe = await invoke('well-behaved.echo', { input: 'hello-sandbox' });
278
288
  expect(probe.status).toBe(200);
@@ -14,6 +14,7 @@
14
14
  */
15
15
 
16
16
  import { describe, it, expect } from 'vitest';
17
+ import { readErrorCode } from '../lib/error-envelope.js';
17
18
  import { driver } from '../lib/driver.js';
18
19
 
19
20
  interface DiscoveryDoc {
@@ -74,8 +75,7 @@ describe('table-schema-enforcement: behavioral (RFC 0016 §B point 2)', () => {
74
75
  bad.status >= 400 && bad.status < 500,
75
76
  driver.describe('RFC 0016 §B point 2', 'type-divergent insert MUST be rejected with 4xx'),
76
77
  ).toBe(true);
77
- const body = bad.json as { error?: { code?: string } | string };
78
- const code = typeof body.error === 'string' ? body.error : body.error?.code;
78
+ const code = readErrorCode(bad.json);
79
79
  expect(
80
80
  code,
81
81
  driver.describe('RFC 0016 §B point 2', 'rejection MUST carry the table_schema_violation error code'),
@@ -18,6 +18,7 @@
18
18
  */
19
19
 
20
20
  import { describe, it, expect } from 'vitest';
21
+ import { readErrorCode } from '../lib/error-envelope.js';
21
22
  import { driver } from '../lib/driver.js';
22
23
  import { behaviorGate } from '../lib/behavior-gate.js';
23
24
  import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
@@ -29,7 +30,7 @@ function realtimeVoiceOf(ai: Record<string, unknown> | undefined): Record<string
29
30
  return rv && typeof rv === 'object' ? (rv as Record<string, unknown>) : undefined;
30
31
  }
31
32
  function errCode(json: unknown): string | undefined {
32
- return (json as { error?: { code?: string } })?.error?.code;
33
+ return readErrorCode(json);
33
34
  }
34
35
 
35
36
  describe('voice-streamref-tenant-bound (RFC 0106 §F INV-4)', () => {
@@ -19,6 +19,7 @@
19
19
  */
20
20
 
21
21
  import { describe, it, expect } from 'vitest';
22
+ import { readErrorCode } from '../lib/error-envelope.js';
22
23
  import { driver } from '../lib/driver.js';
23
24
  import { behaviorGate } from '../lib/behavior-gate.js';
24
25
  import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
@@ -30,7 +31,7 @@ function realtimeVoiceOf(ai: Record<string, unknown> | undefined): Record<string
30
31
  return rv && typeof rv === 'object' ? (rv as Record<string, unknown>) : undefined;
31
32
  }
32
33
  function errCode(json: unknown): string | undefined {
33
- return (json as { error?: { code?: string } })?.error?.code;
34
+ return readErrorCode(json);
34
35
  }
35
36
  function eventsOf(json: unknown): Array<{ type?: string; payload?: Record<string, unknown> }> {
36
37
  const e = (json as { events?: unknown })?.events;
@@ -14,6 +14,7 @@
14
14
  */
15
15
 
16
16
  import { describe, it, expect } from 'vitest';
17
+ import { readErrorCode } from '../lib/error-envelope.js';
17
18
  import { driver } from '../lib/driver.js';
18
19
  import { behaviorGate } from '../lib/behavior-gate.js';
19
20
  import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
@@ -25,7 +26,7 @@ function realtimeVoiceOf(ai: Record<string, unknown> | undefined): Record<string
25
26
  return rv && typeof rv === 'object' ? (rv as Record<string, unknown>) : undefined;
26
27
  }
27
28
  function errCode(json: unknown): string | undefined {
28
- return (json as { error?: { code?: string } })?.error?.code;
29
+ return readErrorCode(json);
29
30
  }
30
31
 
31
32
  describe('voice-transcription-unadvertised (RFC 0106 §B)', () => {
@@ -33,6 +33,7 @@
33
33
  */
34
34
 
35
35
  import { describe, it, expect } from 'vitest';
36
+ import { readErrorCode, readRetriable } from '../lib/error-envelope.js';
36
37
  import { driver } from '../lib/driver.js';
37
38
  import { behaviorGate } from '../lib/behavior-gate.js';
38
39
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
@@ -76,7 +77,7 @@ async function resolve(body: Record<string, unknown>): Promise<{ status: number;
76
77
  }
77
78
 
78
79
  function reasonOf(json: unknown): string | undefined {
79
- return (json as { error?: { code?: string } })?.error?.code;
80
+ return readErrorCode(json);
80
81
  }
81
82
 
82
83
  const IDENTITY = { scheme: 'spiffe', subject: 'spiffe://example/dispatcher', issuer: 'spiffe://example' };
@@ -154,9 +155,11 @@ describe('RFC 0154 §A — workload identity resolution (capability-gated behavi
154
155
  if (!behaviorGate(PROFILE, (await caps())?.supported === true)) return;
155
156
  const r = await resolve({ identity: { scheme: 'spiffe', subject: 'spiffe://example/unknown' } });
156
157
  if (r === null || r.status < 400) return;
157
- const err = (r.json as { error?: { code?: string; retriable?: boolean } }).error;
158
+ // canonical flat envelope: code = `error`, retriable = `details.retriable`
159
+ // (the legacy nested `error.{code,retriable}` the §20 catalog prescribed until
160
+ // 2026-08-16 is tolerated by the helpers for the deprecation window)
158
161
  expect(
159
- err?.retriable,
162
+ readRetriable(r.json),
160
163
  driver.describe(
161
164
  'spec/v1/host-sample-test-seams.md §20',
162
165
  'an identity that does not resolve will not resolve on retry. Marking it retriable invites ' +
@@ -166,7 +169,7 @@ describe('RFC 0154 §A — workload identity resolution (capability-gated behavi
166
169
  expect(
167
170
  ['identity_unverified', 'identity_unresolvable', 'audience_mismatch', 'delegation_expired', 'sender_constraint_missing'],
168
171
  driver.describe('RFCS/0154 §A', 'failures use a closed reason vocabulary'),
169
- ).toContain(err?.code);
172
+ ).toContain(readErrorCode(r.json));
170
173
  });
171
174
 
172
175
  it('a resolution response carries no credential material', async () => {