@openwop/openwop-conformance 2.1.3 → 2.1.4

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,14 @@
1
1
  # `@openwop/openwop-conformance` Changelog
2
2
 
3
+ ## [2.1.4] — 2026-09-12 — the replay reds now say why
4
+
5
+ `replay-observable-sequence-determinism` and `replay-divergence-at-refusal`
6
+ asserted `status === 'completed'` bare, discarding the run's `error` and
7
+ `currentNodeId`, so a red reported `expected 'failed' to be 'completed'` and
8
+ nothing else. Both now attach the terminal detail, and both honour
9
+ `OPENWOP_POLL_TIMEOUT_SCALE` — they were the only two scenarios that rolled
10
+ their own poll loop and never read it. No assertion changed meaning.
11
+
3
12
  ## [2.1.3] — 2026-09-11 — `since` is the host's minor, said in the schema
4
13
 
5
14
  No scenario changed. The contract peer's generated
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "package": "@openwop/spec-artifacts",
3
- "version": "2.1.3",
4
- "stampSha256": "05c0632125a5265905554cd3203217510192e15dafd6c8cd5654e10120d2265a"
3
+ "version": "2.1.4",
4
+ "stampSha256": "6f496326b394df65a2a9e89547f36693549d59cd13a9a5491dfb3ad34bb78596"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "2.1.3",
3
+ "version": "2.1.4",
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.3"
59
+ "@openwop/spec-artifacts": "2.1.4"
60
60
  }
61
61
  }
package/requirements.json CHANGED
@@ -21552,7 +21552,7 @@
21552
21552
  {
21553
21553
  "id": "openwop.it.replay-divergence-at-refusal.replaydeterminism-when-present-conforms-to-rfc-0041-d",
21554
21554
  "file": "replay-divergence-at-refusal.test.ts",
21555
- "line": 79,
21555
+ "line": 111,
21556
21556
  "title": "replayDeterminism (when present) conforms to RFC 0041 §D",
21557
21557
  "explicitId": "openwop.it.replay-divergence-at-refusal.replaydeterminism-when-present-conforms-to-rfc-0041-d",
21558
21558
  "citations": [
@@ -21573,10 +21573,15 @@
21573
21573
  {
21574
21574
  "id": "openwop.it.replay-divergence-at-refusal.phase-4-host-must-emit-replay-divergedatrefusal-fail-with-replay-diverged-at-ref",
21575
21575
  "file": "replay-divergence-at-refusal.test.ts",
21576
- "line": 186,
21576
+ "line": 227,
21577
21577
  "title": "Phase 4 host MUST emit replay.divergedAtRefusal + fail with replay_diverged_at_refusal when original=valid + replay=refusal",
21578
21578
  "explicitId": "openwop.it.replay-divergence-at-refusal.phase-4-host-must-emit-replay-divergedatrefusal-fail-with-replay-diverged-at-ref",
21579
21579
  "citations": [
21580
+ {
21581
+ "section": "RFCS/0041-multi-agent-replay-under-nondeterminism.md §D",
21582
+ "requirement": null,
21583
+ "interpolated": true
21584
+ },
21580
21585
  {
21581
21586
  "section": "RFCS/0041-multi-agent-replay-under-nondeterminism.md §B + spec/v1/rest-endpoints.md §\"Common error codes\"",
21582
21587
  "requirement": "replay MUST terminate `failed` when refusal-divergence is detected (silent substitution is non-conformant)"
@@ -21602,7 +21607,7 @@
21602
21607
  {
21603
21608
  "id": "openwop.it.replay-divergence-at-refusal.phase-4-host-must-emit-replay-divergedatrefusal-fail-with-replay-diverged-at-ref~2",
21604
21609
  "file": "replay-divergence-at-refusal.test.ts",
21605
- "line": 272,
21610
+ "line": 320,
21606
21611
  "title": "Phase 4 host MUST emit replay.divergedAtRefusal + fail with replay_diverged_at_refusal when original=refusal + replay=valid (symmetric case)",
21607
21612
  "explicitId": "openwop.it.replay-divergence-at-refusal.phase-4-host-must-emit-replay-divergedatrefusal-fail-with-replay-diverged-at-ref~2",
21608
21613
  "citations": [
@@ -22001,10 +22006,15 @@
22001
22006
  {
22002
22007
  "id": "openwop.it.replay-observable-sequence-determinism.original-and-replay-event-log-prefixes-must-be-byte-equivalent-modulo-per-event",
22003
22008
  "file": "replay-observable-sequence-determinism.test.ts",
22004
- "line": 168,
22009
+ "line": 210,
22005
22010
  "title": "original and replay event-log prefixes MUST be byte-equivalent (modulo per-event clock + ULID entropy)",
22006
22011
  "explicitId": "openwop.it.replay-observable-sequence-determinism.original-and-replay-event-log-prefixes-must-be-byte-equivalent-modulo-per-event",
22007
22012
  "citations": [
22013
+ {
22014
+ "section": "RFCS/0041-multi-agent-replay-under-nondeterminism.md §C",
22015
+ "requirement": null,
22016
+ "interpolated": true
22017
+ },
22008
22018
  {
22009
22019
  "section": "RFCS/0041-multi-agent-replay-under-nondeterminism.md §C",
22010
22020
  "requirement": "a mode:replay fork MUST reproduce the original observable event-log sequence byte-for-byte modulo volatile per-event fields (eventId/ULID entropy, per-region observedAt clock)"
@@ -22014,7 +22024,7 @@
22014
22024
  {
22015
22025
  "id": "openwop.it.replay-observable-sequence-determinism.replay-of-a-nondeterministic-tool-node-reproduces-the-original-observable-result",
22016
22026
  "file": "replay-observable-sequence-determinism.test.ts",
22017
- "line": 202,
22027
+ "line": 251,
22018
22028
  "title": "replay of a nondeterministic tool node reproduces the ORIGINAL observable result, NOT a fresh call",
22019
22029
  "explicitId": "openwop.it.replay-observable-sequence-determinism.replay-of-a-nondeterministic-tool-node-reproduces-the-original-observable-result",
22020
22030
  "citations": [
@@ -22023,6 +22033,11 @@
22023
22033
  "requirement": null,
22024
22034
  "interpolated": true
22025
22035
  },
22036
+ {
22037
+ "section": "RFCS/0041-multi-agent-replay-under-nondeterminism.md §C",
22038
+ "requirement": null,
22039
+ "interpolated": true
22040
+ },
22026
22041
  {
22027
22042
  "section": "RFCS/0041-multi-agent-replay-under-nondeterminism.md §C",
22028
22043
  "requirement": "the nondeterministic tool node MUST replay its ORIGINAL observable result (cached event-log entry) rather than re-executing — bit-equivalent re-execution would require unbounded caching, rejected per RFC 0041 §\"Alternatives considered\" #2"
@@ -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.3",
5
- "corpusTag": "v2.1.3",
4
+ "version": "2.1.4",
5
+ "corpusTag": "v2.1.4",
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": "af7bb78f590996cf16ecb5669b34fbafa700177ed06a2ece342574edd25c95b1",
13
- "api/v2/asyncapi.yaml": "a3d6954f17ddc7d4e6ede42a9766a6bdddb20d4b642967358926e488af8ce082",
14
- "api/v2/openapi.yaml": "d8b0526a61dc11ef8d94018e48bd679b7f1c28a2b32a1e914a4824435072150d",
12
+ "api/seams-v2.yaml": "277fb0c788ff5a085e3bb145e55db05ce002202f6c89a88429cf06187d07f610",
13
+ "api/v2/asyncapi.yaml": "7b8dcd7ca9b6be23b6e81ca19ceb6620c97b7df2840d68c547773895d17220aa",
14
+ "api/v2/openapi.yaml": "6afad97fbcfdfeef0776651f6264a27f5598ac8301a20842a9c739c8ccef3ad4",
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": "52a1aa95c365260c58cbbe2cf5e4560808814e066ba15811f9be534ef5c9bbd7",
203
+ "spec/v1/core-standard-manifest.json": "9b9a20963e9e364b0bb784a2fb38c376208393214e7ece962c9961fce03b1623",
204
204
  "spec/v1/deprecations.json": "1d5acb69a9b8ccb57275a95605f74aef1d920685f8407c9d382a46b59dc803bb",
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": "69cffb23e28308482a552b71efaa35d8d8a40550fa8fcc35a8c20c9476ab59a3",
215
+ "spec/v2/README.md": "56a157cb6e35e7313d05514b438b4e148d679f6e084b7022431d8f2e81a519b0",
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": "43c104a39f8c809550553f12d4f80d044006b37a95dc720a39914989de1c3ef5",
222
+ "spec/v2/core/headers.md": "35f9b7e912080bb4ff28d06dc1679a9b2857fcff6ce810d6df266bc66d2b13f2",
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": "2293c3f7bd204432f892936df8625a17f557283c252383b2553e92a18729fb19",
273
+ "spec/v2/release.json": "31797591070c2c06955915a0348dc9964d162e5fd1b544e073d667cbf78d9756",
274
274
  "spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624"
275
275
  },
276
- "corpusCommit": "9bc425c013fcba8d17329df901d384943283b168"
276
+ "corpusCommit": "5e9633c7297c993efc5a8198cd07dffcd2213e11"
277
277
  }
@@ -75,6 +75,38 @@ async function readDiscovery(): Promise<DiscoveryDoc | null> {
75
75
  } catch { return null; }
76
76
  }
77
77
 
78
+ /**
79
+ * Why this says more than `expect(status).toBe('completed')`.
80
+ *
81
+ * A terminal run that is `failed` carries `error` (`schemas/run-snapshot.schema.json`),
82
+ * and the bare equality assertion threw it away: every occurrence of this red
83
+ * reported only `expected 'failed' to be 'completed'`, with no node, no code and
84
+ * no message. A tier-1 host carried exactly that line in its notes across three
85
+ * lanes without ever being able to diagnose it (reported 2026-09-12), because
86
+ * the suite had recorded that something went wrong and nothing about what.
87
+ *
88
+ * That is the suite owing hosts what RFC 0064 §F makes a host owe the wire: a
89
+ * failure MUST be self-describing. This does not make the flake go away — the
90
+ * cause is still unknown, and pretending otherwise would be the third instrument
91
+ * this week that answers a narrower question than it appears to. It makes the
92
+ * next occurrence carry its own evidence.
93
+ */
94
+ function terminalDetail(snap: RunSnapshot): string {
95
+ if (snap.status === 'completed') return '';
96
+ const err = snap.error as Record<string, unknown> | undefined;
97
+ const parts: string[] = [`status=${String(snap.status)}`];
98
+ if (snap.currentNodeId) parts.push(`currentNodeId=${String(snap.currentNodeId)}`);
99
+ if (err && typeof err === 'object') {
100
+ for (const k of ['code', 'message', 'nodeId', 'retriable']) {
101
+ if (err[k] !== undefined) parts.push(`error.${k}=${JSON.stringify(err[k])}`);
102
+ }
103
+ if (!('code' in err) && !('message' in err)) parts.push(`error=${JSON.stringify(err).slice(0, 300)}`);
104
+ } else {
105
+ parts.push('error=(absent — the host reported a non-completed terminal state with no error object)');
106
+ }
107
+ return ` — ${parts.join(' ')}`;
108
+ }
109
+
78
110
  describe.skipIf(HTTP_SKIP)('replay-divergence-at-refusal: advertisement shape (RFC 0041 §D)', () => {
79
111
  it('replayDeterminism (when present) conforms to RFC 0041 §D', async (ctx) => {
80
112
  const d = await readDiscovery();
@@ -127,6 +159,7 @@ describe.skipIf(HTTP_SKIP)('replay-divergence-at-refusal: advertisement shape (R
127
159
  interface RunSnapshot {
128
160
  status?: string;
129
161
  error?: { code?: string; message?: string };
162
+ currentNodeId?: string;
130
163
  }
131
164
  interface RunEventDoc {
132
165
  type: string;
@@ -135,8 +168,16 @@ interface RunEventDoc {
135
168
  payload?: Record<string, unknown>;
136
169
  }
137
170
 
171
+ // The 5s budget below is scaled by OPENWOP_POLL_TIMEOUT_SCALE like every
172
+ // other bound in the suite. `lib/polling.ts` exists so a timeout can say
173
+ // whether it measured the host or the harness — *"observe no change in
174
+ // those scenarios, and record a failure that measured the environment
175
+ // rather than the host"* — and these two replay files were the only ones
176
+ // that rolled their own loop and never read it.
138
177
  async function pollUntilTerminal(runId: string): Promise<RunSnapshot> {
139
- for (let i = 0; i < 50; i++) {
178
+ const scale = Number(process.env['OPENWOP_POLL_TIMEOUT_SCALE'] ?? '1');
179
+ const rounds = Math.max(1, Math.round(50 * (Number.isFinite(scale) && scale > 0 ? scale : 1)));
180
+ for (let i = 0; i < rounds; i++) {
140
181
  const r = await driver.get(`/v1/runs/${encodeURIComponent(runId)}`);
141
182
  const snap = r.json as RunSnapshot;
142
183
  if (snap.status === 'completed' || snap.status === 'failed' || snap.status === 'cancelled') {
@@ -144,7 +185,7 @@ async function pollUntilTerminal(runId: string): Promise<RunSnapshot> {
144
185
  }
145
186
  await new Promise((resolve) => setTimeout(resolve, 100));
146
187
  }
147
- throw new Error(`run ${runId} did not reach terminal within 5s`);
188
+ throw new Error(`run ${runId} did not reach a terminal state within ${rounds * 100}ms (OPENWOP_POLL_TIMEOUT_SCALE=${process.env['OPENWOP_POLL_TIMEOUT_SCALE'] ?? '1'})`);
148
189
  }
149
190
 
150
191
  async function readEvents(runId: string): Promise<RunEventDoc[]> {
@@ -212,7 +253,14 @@ describe.skipIf(HTTP_SKIP)('replay-divergence-at-refusal: behavioral (RFC 0041
212
253
  expect(createRes.status).toBe(201);
213
254
  const sourceRunId = (createRes.json as { runId: string }).runId;
214
255
  const sourceTerminal = await pollUntilTerminal(sourceRunId);
215
- expect(sourceTerminal.status).toBe('completed');
256
+ expect(
257
+ sourceTerminal.status,
258
+ req(
259
+ 'openwop.it.replay-divergence-at-refusal.phase-4-host-must-emit-replay-divergedatrefusal-fail-with-replay-diverged-at-ref',
260
+ 'RFCS/0041-multi-agent-replay-under-nondeterminism.md §D',
261
+ `the source run MUST reach \`completed\` for the replay comparison to mean anything${terminalDetail(sourceTerminal)}`,
262
+ ),
263
+ ).toBe('completed');
216
264
 
217
265
  // Stage refusal for the replay's mock-AI dispatch.
218
266
  await programMock(NODE_ID, [
@@ -62,6 +62,8 @@ interface DiscoveryDoc {
62
62
 
63
63
  interface RunSnapshot {
64
64
  status?: string;
65
+ error?: unknown;
66
+ currentNodeId?: string;
65
67
  }
66
68
  interface RunEventDoc {
67
69
  type: string;
@@ -93,8 +95,16 @@ async function gateOnPhase4(ctx: { skip: () => void }): Promise<boolean> {
93
95
  return true;
94
96
  }
95
97
 
98
+ // The 5s budget below is scaled by OPENWOP_POLL_TIMEOUT_SCALE like every
99
+ // other bound in the suite. `lib/polling.ts` exists so a timeout can say
100
+ // whether it measured the host or the harness — *"observe no change in
101
+ // those scenarios, and record a failure that measured the environment
102
+ // rather than the host"* — and these two replay files were the only ones
103
+ // that rolled their own loop and never read it.
96
104
  async function pollUntilTerminal(runId: string): Promise<RunSnapshot> {
97
- for (let i = 0; i < 50; i++) {
105
+ const scale = Number(process.env['OPENWOP_POLL_TIMEOUT_SCALE'] ?? '1');
106
+ const rounds = Math.max(1, Math.round(50 * (Number.isFinite(scale) && scale > 0 ? scale : 1)));
107
+ for (let i = 0; i < rounds; i++) {
98
108
  const r = await driver.get(`/v1/runs/${encodeURIComponent(runId)}`);
99
109
  const snap = r.json as RunSnapshot;
100
110
  if (snap.status === 'completed' || snap.status === 'failed' || snap.status === 'cancelled') {
@@ -102,7 +112,7 @@ async function pollUntilTerminal(runId: string): Promise<RunSnapshot> {
102
112
  }
103
113
  await new Promise((resolve) => setTimeout(resolve, 100));
104
114
  }
105
- throw new Error(`run ${runId} did not reach terminal within 5s`);
115
+ throw new Error(`run ${runId} did not reach a terminal state within ${rounds * 100}ms (OPENWOP_POLL_TIMEOUT_SCALE=${process.env['OPENWOP_POLL_TIMEOUT_SCALE'] ?? '1'})`);
106
116
  }
107
117
 
108
118
  async function readEvents(runId: string): Promise<RunEventDoc[]> {
@@ -162,6 +172,38 @@ async function startFixtureRun(ctx: { skip: () => void }): Promise<string | null
162
172
  return (create.json as { runId: string }).runId;
163
173
  }
164
174
 
175
+ /**
176
+ * Why this says more than `expect(status).toBe('completed')`.
177
+ *
178
+ * A terminal run that is `failed` carries `error` (`schemas/run-snapshot.schema.json`),
179
+ * and the bare equality assertion threw it away: every occurrence of this red
180
+ * reported only `expected 'failed' to be 'completed'`, with no node, no code and
181
+ * no message. A tier-1 host carried exactly that line in its notes across three
182
+ * lanes without ever being able to diagnose it (reported 2026-09-12), because
183
+ * the suite had recorded that something went wrong and nothing about what.
184
+ *
185
+ * That is the suite owing hosts what RFC 0064 §F makes a host owe the wire: a
186
+ * failure MUST be self-describing. This does not make the flake go away — the
187
+ * cause is still unknown, and pretending otherwise would be the third instrument
188
+ * this week that answers a narrower question than it appears to. It makes the
189
+ * next occurrence carry its own evidence.
190
+ */
191
+ function terminalDetail(snap: RunSnapshot): string {
192
+ if (snap.status === 'completed') return '';
193
+ const err = snap.error as Record<string, unknown> | undefined;
194
+ const parts: string[] = [`status=${String(snap.status)}`];
195
+ if (snap.currentNodeId) parts.push(`currentNodeId=${String(snap.currentNodeId)}`);
196
+ if (err && typeof err === 'object') {
197
+ for (const k of ['code', 'message', 'nodeId', 'retriable']) {
198
+ if (err[k] !== undefined) parts.push(`error.${k}=${JSON.stringify(err[k])}`);
199
+ }
200
+ if (!('code' in err) && !('message' in err)) parts.push(`error=${JSON.stringify(err).slice(0, 300)}`);
201
+ } else {
202
+ parts.push('error=(absent — the host reported a non-completed terminal state with no error object)');
203
+ }
204
+ return ` — ${parts.join(' ')}`;
205
+ }
206
+
165
207
  describe.skipIf(HTTP_SKIP)(
166
208
  'replay-observable-sequence-determinism: prefix byte-equivalence (RFC 0041 §C)',
167
209
  () => {
@@ -171,7 +213,14 @@ describe.skipIf(HTTP_SKIP)(
171
213
  const sourceRunId = await startFixtureRun(ctx);
172
214
  if (sourceRunId === null) return softSkip('blocked', 'precondition not met — `sourceRunId === null` returned early (seam, prior step, or fixture unavailable)');
173
215
  const sourceTerminal = await pollUntilTerminal(sourceRunId);
174
- expect(sourceTerminal.status).toBe('completed');
216
+ expect(
217
+ sourceTerminal.status,
218
+ req(
219
+ 'openwop.it.replay-observable-sequence-determinism.original-and-replay-event-log-prefixes-must-be-byte-equivalent-modulo-per-event',
220
+ 'RFCS/0041-multi-agent-replay-under-nondeterminism.md §C',
221
+ `the source run MUST reach \`completed\` for the replay comparison to mean anything${terminalDetail(sourceTerminal)}`,
222
+ ),
223
+ ).toBe('completed');
175
224
  const sourceEvents = await readEvents(sourceRunId);
176
225
 
177
226
  const forkRes = await driver.post(`/v1/runs/${encodeURIComponent(sourceRunId)}:fork`, {
@@ -204,7 +253,15 @@ describe.skipIf(HTTP_SKIP)(
204
253
 
205
254
  const sourceRunId = await startFixtureRun(ctx);
206
255
  if (sourceRunId === null) return softSkip('blocked', 'precondition not met — `sourceRunId === null` returned early (seam, prior step, or fixture unavailable)');
207
- expect((await pollUntilTerminal(sourceRunId)).status).toBe('completed');
256
+ const t = await pollUntilTerminal(sourceRunId);
257
+ expect(
258
+ t.status,
259
+ req(
260
+ 'openwop.it.replay-observable-sequence-determinism.replay-of-a-nondeterministic-tool-node-reproduces-the-original-observable-result',
261
+ 'RFCS/0041-multi-agent-replay-under-nondeterminism.md §C',
262
+ `the source run MUST reach \`completed\` for the replay comparison to mean anything${terminalDetail(t)}`,
263
+ ),
264
+ ).toBe('completed');
208
265
  const sourceEvents = await readEvents(sourceRunId);
209
266
 
210
267
  // The terminal event(s) for the nondeterministic node carry its