@openwop/openwop-conformance 1.136.5 → 1.136.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "1.136.5",
3
+ "version": "1.136.6",
4
4
  "description": "Production-ready black-box conformance suite for OpenWOP v1.0 compliant servers.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "_comment": "Provenance of this vendored schemas/ copy. See conformance/README.md \u00a7\"Resolving the contract\". Compare against the stamp in your installed @openwop/openwop-conformance to detect a stale hand-copied contract.",
3
- "suiteVersion": "1.136.5",
4
- "corpusCommit": "d1107230a2b68b61a854b340a054f3770b40f783"
3
+ "suiteVersion": "1.136.6",
4
+ "corpusCommit": "8f907101a7f1b6b630c0d1f2271b6e60d1e7c8fe"
5
5
  }
@@ -306,6 +306,26 @@ export function expandChain(chain: WorkflowChain, ctx: ExpansionContext): Expand
306
306
  // If it is gated on an advertised capability, it goes below.
307
307
  // ─────────────────────────────────────────────────────────────────────────────
308
308
 
309
+ // ─── Begin the MIRRORED COMPENSATION pass ───────────────────────────────────
310
+ //
311
+ // SP-01 (2026-08-18): this pass is a SECOND byte-mirrored region, gated by
312
+ // `scripts/check-workflow-chain-expansion-sync.mjs` alongside the core above.
313
+ //
314
+ // It sits below "End of the MIRRORED CORE" because it composes on the core's
315
+ // OUTPUT rather than editing it — but unlike the capability-gated surfaces that
316
+ // follow (RFC 0124 deferred parameters, RFC 0133 sub-chains, which a host that
317
+ // does not advertise them MUST REFUSE rather than implement), carrying a
318
+ // chain's compensation declaration is UNCONDITIONAL: `compensation.md`
319
+ // §"Workflow policy" makes the declaration descriptive, so a host that does not
320
+ // advertise `capabilities.compensation` still MUST carry it verbatim and refuse
321
+ // only a chain-level POLICY (`capability_required`). Unconditional ⇒ genuinely
322
+ // mirrorable ⇒ gated. RFC 0157's own commit claimed this pass was already
323
+ // "CI-gated against the reference host"; it was not — the gate stopped at the
324
+ // core sentinel — so the mirror never received it. Anything added between the
325
+ // two sentinels below MUST be mirrored in the reference host — that is the
326
+ // whole RFC 0157 unit: the types, the two error classes, the helpers, and
327
+ // `carryCompensation` / `expandChainWithCompensation`.
328
+
309
329
  // ---------------------------------------------------------------------------
310
330
  // RFC 0157 (RFC 0013 revision × RFC 0151 §B) — chain fragments carry
311
331
  // compensation.
@@ -525,6 +545,8 @@ export function expandChainWithCompensation(
525
545
  return { ...expanded, nodes: carried.nodes, settingsCompensation: carried.settingsCompensation };
526
546
  }
527
547
 
548
+ // ─── End of the MIRRORED COMPENSATION pass ──────────────────────────────────
549
+
528
550
  // ---------------------------------------------------------------------------
529
551
  // RFC 0124 (WCP4) — Portable per-run parameter deferral.
530
552
  //
@@ -17,6 +17,7 @@
17
17
  import { describe, it, expect } from 'vitest';
18
18
  import { driver } from '../lib/driver.js';
19
19
  import { isFixtureAdvertised } from '../lib/fixtures.js';
20
+ import { readErrorCode } from '../lib/error-envelope.js';
20
21
 
21
22
  const WORKFLOW_ID = 'conformance-idempotent';
22
23
  const SKIP_NO_FIXTURE = !isFixtureAdvertised(WORKFLOW_ID);
@@ -85,5 +86,44 @@ describe.skipIf(SKIP_NO_FIXTURE)('idempotency: same key + different body conflic
85
86
  'idempotency.md §Layer 1',
86
87
  'same Idempotency-Key with a different body MUST return 409',
87
88
  )).toBe(409);
89
+
90
+ // SP-03 (2026-08-18): the code, not just the status. `idempotency.md`
91
+ // named NO mismatch error until v1.5, and implementations diverged exactly
92
+ // as an unnamed error invites: `grpc-transport.md` mapped BOTH
93
+ // `idempotency_key_conflict` and `idempotency_key_mismatch` in one row, the
94
+ // published TypeScript SDK's `HTTP_ERROR_CODES` carried
95
+ // `idempotency_key_mismatch`, the SQLite reference host emitted
96
+ // `idempotency_key_conflict`, and a tier-1 host emitted
97
+ // `idempotency_key_replay_mismatch` — a spelling in no corpus artifact at
98
+ // all. This leg asserted the status alone, so every one of them passed.
99
+ //
100
+ // `idempotency_key_mismatch` is canonical (idempotency.md §"Record shape,
101
+ // digest, and lease"): the only spelling already in more than one shipped
102
+ // artifact. The two legacy spellings are TOLERATED here through the first
103
+ // minor after 2026-11-10 so a converging host is not red on a rename it is
104
+ // mid-flight on — the same deprecation shape S22 used for the error
105
+ // envelope. Remove the tolerance then; the canonical assertion stays.
106
+ const code = readErrorCode(conflict.json);
107
+ const LEGACY = ['idempotency_key_conflict', 'idempotency_key_replay_mismatch'];
108
+ expect(
109
+ code === 'idempotency_key_mismatch' || LEGACY.includes(code ?? ''),
110
+ driver.describe(
111
+ 'idempotency.md §"Record shape, digest, and lease"',
112
+ `a different request digest under the same scoped key MUST fail with the canonical ` +
113
+ `\`idempotency_key_mismatch\` (legacy \`${LEGACY.join('` / `')}\` tolerated through the ` +
114
+ `first minor after 2026-11-10); got \`${code ?? '<none>'}\``,
115
+ ),
116
+ ).toBe(true);
117
+
118
+ // …and MUST NOT hand back the first run's body. Answering a question the
119
+ // caller did not ask is the read half of the same confusion.
120
+ const conflictRunId = (conflict.json as { runId?: unknown } | undefined)?.runId;
121
+ expect(
122
+ conflictRunId,
123
+ driver.describe(
124
+ 'idempotency.md §"Record shape, digest, and lease"',
125
+ 'a digest mismatch MUST NOT return the cached body (no runId on the 409)',
126
+ ),
127
+ ).toBeUndefined();
88
128
  });
89
129
  });