@openwop/openwop-conformance 1.136.8 → 1.136.10
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,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.
|
|
4
|
-
"corpusCommit": "
|
|
3
|
+
"suiteVersion": "1.136.10",
|
|
4
|
+
"corpusCommit": "0d8fd3fde8cca5785c23d4b7e64281f1876ab342"
|
|
5
5
|
}
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
*/
|
|
41
41
|
|
|
42
42
|
import { describe, it, expect } from 'vitest';
|
|
43
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
43
44
|
import { driver } from '../lib/driver.js';
|
|
44
45
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
45
46
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
@@ -224,4 +225,74 @@ describe('RFC 0151 §C — compensation lifecycle (capability-gated behavior)',
|
|
|
224
225
|
}
|
|
225
226
|
}
|
|
226
227
|
});
|
|
228
|
+
|
|
229
|
+
it('a healthy run that DECLARES a compensator reports compensationStatus `none` (SP-11a)', async () => {
|
|
230
|
+
if (!behaviorGate(PROFILE, await advertised())) return;
|
|
231
|
+
|
|
232
|
+
// The rollup folds over the PLAN, and no plan exists until a trigger fires
|
|
233
|
+
// (`compensation.md` §"Run rollup"). So a run that completes successfully while
|
|
234
|
+
// declaring compensable nodes reads `none` — it has nothing to unwind.
|
|
235
|
+
//
|
|
236
|
+
// Nothing observed this. Every other leg of the §21 seam drives a FAILURE, so a
|
|
237
|
+
// host that derived the rollup from the existence of obligation rows — minted per
|
|
238
|
+
// compensable node, healthy or not — reported `pending` on every successful run
|
|
239
|
+
// that declared a compensator and passed all of them. A tier-1 host shipped exactly
|
|
240
|
+
// that (SP-11a / their AP-03). This leg is the healthy case, and it needs no failure:
|
|
241
|
+
// `fail: false` on the unwind seam runs the same compensator-declaring workflow to
|
|
242
|
+
// completion.
|
|
243
|
+
const seam = await driver.post('/v1/host/sample/test/compensation/unwind', { fail: false });
|
|
244
|
+
if (seam.status === 404) {
|
|
245
|
+
return softSkip('blocked', 'the §21 unwind seam is not wired — the healthy-run rollup has no black-box witness');
|
|
246
|
+
}
|
|
247
|
+
if (seam.status >= 400) {
|
|
248
|
+
// The host mounts the seam but rejects `fail: false`. That is the pre-extension
|
|
249
|
+
// shape, not a pass: without it the healthy-run rollup stays unobservable.
|
|
250
|
+
return softSkip(
|
|
251
|
+
'blocked',
|
|
252
|
+
`the §21 unwind seam does not honour \`fail: false\` (HTTP ${seam.status}) — the healthy-run ` +
|
|
253
|
+
'rollup is unobservable until it does (host-sample-test-seams.md §21)',
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const body = seam.json as { runId?: string; events?: { type: string }[] };
|
|
258
|
+
expect(
|
|
259
|
+
typeof body.runId === 'string' && body.runId.length > 0,
|
|
260
|
+
driver.describe('host-sample-test-seams.md §21', '`fail: false` MUST still return the runId it created'),
|
|
261
|
+
).toBe(true);
|
|
262
|
+
|
|
263
|
+
// Positive control: the seam really did run a healthy compensator-declaring
|
|
264
|
+
// workflow. If it emitted a `compensation.requested`, it failed the node anyway and
|
|
265
|
+
// this is not the healthy case — say so rather than assert `none` against a run
|
|
266
|
+
// that legitimately has a plan.
|
|
267
|
+
const types = (body.events ?? []).map((e) => e.type);
|
|
268
|
+
if (types.includes('compensation.requested')) {
|
|
269
|
+
return softSkip(
|
|
270
|
+
'blocked',
|
|
271
|
+
`the seam emitted ${JSON.stringify(types)} under \`fail: false\` — a trigger fired, so this run ` +
|
|
272
|
+
'is not the healthy case the leg needs',
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const snap = await driver.get(`/v1/runs/${encodeURIComponent(body.runId ?? '')}`);
|
|
277
|
+
expect(snap.status, driver.describe('rest-endpoints.md', 'GET /v1/runs/{runId} for the healthy seam run')).toBe(200);
|
|
278
|
+
const snapshot = snap.json as { status?: unknown; compensationStatus?: unknown };
|
|
279
|
+
|
|
280
|
+
expect(
|
|
281
|
+
snapshot.compensationStatus,
|
|
282
|
+
driver.describe(
|
|
283
|
+
'compensation.md §"Run rollup: compensationStatus"',
|
|
284
|
+
'a host advertising `compensation` MUST carry the field on EVERY snapshot — presence is the wire witness of the advert',
|
|
285
|
+
),
|
|
286
|
+
).toBeDefined();
|
|
287
|
+
|
|
288
|
+
expect(
|
|
289
|
+
snapshot.compensationStatus,
|
|
290
|
+
driver.describe(
|
|
291
|
+
'compensation.md §"Run rollup: compensationStatus"',
|
|
292
|
+
'a run that completed while declaring compensable nodes has nothing to unwind: no `compensation.requested` ' +
|
|
293
|
+
'was recorded, so the fold is `none`. `pending` here means the rollup was derived from obligation ROWS ' +
|
|
294
|
+
`rather than from plan state — got \`${String(snapshot.compensationStatus)}\``,
|
|
295
|
+
),
|
|
296
|
+
).toBe('none');
|
|
297
|
+
});
|
|
227
298
|
});
|
|
@@ -13,16 +13,21 @@
|
|
|
13
13
|
* events; a tier-2 host suppressed them, which leaves the replayed log SHORT
|
|
14
14
|
* of the source's and breaks RFC 0041 §C byte-equivalence).
|
|
15
15
|
*
|
|
16
|
-
* The
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
16
|
+
* The spec was never actually ambiguous, and an earlier revision of this file
|
|
17
|
+
* said it was — that was wrong. `replay.md` §"Determinism guarantees"
|
|
18
|
+
* caveat 5 (Stable, unchanged since v1.2) names this exact case: recorded-fact
|
|
19
|
+
* events such as `memory.written` "are fixed history. On replay against a
|
|
20
|
+
* checkpoint a host MUST re-emit them from the event log and MUST NOT
|
|
21
|
+
* regenerate their identifiers or timestamps." RFC 0041 §C independently
|
|
22
|
+
* requires caching "emitted events" so a replay reproduces the observable
|
|
23
|
+
* sequence. RFC 0057 §D's implementation note appeared to bless suppression
|
|
24
|
+
* and conceded in its own words that it satisfied only the "MUST NOT
|
|
25
|
+
* regenerate" half; that note is retired (RFC 0057, Correction 2026-08-18).
|
|
26
|
+
*
|
|
27
|
+
* So this asserts BOTH halves — every replayed id is a recorded id, AND the
|
|
28
|
+
* source's recorded ids all reappear. A suppressing host fails, and always
|
|
29
|
+
* should have: nothing normative changed, the instrument just could not see
|
|
30
|
+
* it before.
|
|
26
31
|
*
|
|
27
32
|
* Gated on `capabilities.memory.attribution.emitsWriteEvents`; soft-skips
|
|
28
33
|
* when unadvertised, when the seeded run wrote no memory, or when the host
|
|
@@ -68,19 +73,26 @@ describe('memory-attribution-replay-stable (RFC 0057 §D)', () => {
|
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
const replayed = await memoryWrittenEvents(forkId);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
76
|
+
// Half 1 — MUST re-emit. The source's recorded ids all reappear on the
|
|
77
|
+
// replay. This is the half that went unasserted for months: the loop below
|
|
78
|
+
// is vacuously true on an empty array, so a host that suppressed the
|
|
79
|
+
// re-emission entirely passed.
|
|
80
|
+
const replayedIds = new Set(
|
|
81
|
+
replayed.map((e) => memoryIdOf(e.payload)).filter((x): x is string => x !== null),
|
|
82
|
+
);
|
|
83
|
+
const missing = [...recordedIds].filter((id) => !replayedIds.has(id));
|
|
84
|
+
expect(
|
|
85
|
+
missing,
|
|
86
|
+
driver.describe(
|
|
87
|
+
'replay.md §"Determinism guarantees" caveat 5',
|
|
88
|
+
'recorded-fact events are fixed history: a replay MUST re-emit the source run\'s ' +
|
|
89
|
+
'`memory.written` events from the log. A replay whose log is SHORT of the source\'s is not ' +
|
|
90
|
+
'a reproduction of the observable sequence (RFC 0041 §C). Suppressing the write is not ' +
|
|
91
|
+
'enough — that satisfies only the MUST-NOT-regenerate half',
|
|
92
|
+
),
|
|
93
|
+
).toEqual([]);
|
|
94
|
+
|
|
95
|
+
// Half 2 — MUST NOT regenerate. Every id on the replay is one the source recorded.
|
|
84
96
|
for (const e of replayed) {
|
|
85
97
|
const id = memoryIdOf(e.payload);
|
|
86
98
|
expect(
|