@openwop/openwop-conformance 1.101.0 → 1.103.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "1.101.0",
3
+ "version": "1.103.0",
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.101.0",
4
- "corpusCommit": "be303ce1c692847339ea5bb05f569af72d962cda"
3
+ "suiteVersion": "1.103.0",
4
+ "corpusCommit": "c6f779513a26705a5d9fd8958ec1cd75ab8f6fdb"
5
5
  }
@@ -48,6 +48,9 @@ import { describe, it, expect } from 'vitest';
48
48
  import { driver } from '../lib/driver.js';
49
49
  import { pollUntilTerminal } from '../lib/polling.js';
50
50
  import { isFixtureAdvertised } from '../lib/fixtures.js';
51
+ import { readFileSync } from 'node:fs';
52
+ import { join } from 'node:path';
53
+ import { V1_DIR } from '../lib/paths.js';
51
54
 
52
55
  const WORKFLOW_ID = 'conformance-replay-side-effect';
53
56
  const EFFECT_NODE = 'effect';
@@ -182,3 +185,65 @@ describe.skipIf(SKIP_NO_FIXTURE)('replay-side-effect-suppression: a replay does
182
185
  ).toBe(true);
183
186
  });
184
187
  });
188
+
189
+ /**
190
+ * `replay.md` requirement 4 — the manifest declaration is a classification FLOOR.
191
+ *
192
+ * A pack manifest may declare `role: "side-effect"`. Until 2026-08-14 the only
193
+ * stated purpose of that field was that it "drives engine scheduling", so a host
194
+ * was free to classify side-effecting nodes from a private list and never read
195
+ * it. A tier-1 host did exactly that and a shipped `core.storage.blob-put` node
196
+ * performed real object-store `PUT`s **during a replay**, past two independent
197
+ * guards, because neither consulted the manifest that had declared the node
198
+ * side-effecting all along.
199
+ *
200
+ * Requirement 4 makes the declaration binding: a host's own classifier is a floor
201
+ * ABOVE it and never a substitute — more nodes may be treated as side-effecting,
202
+ * never fewer.
203
+ *
204
+ * **This is a coherence gate, not a behavioral witness, and the distinction
205
+ * matters.** Proving a host honours requirement 4 needs that host advertising
206
+ * `recorded-outcome`, a pack node declaring the role, and an observable external
207
+ * effect across a replay — none of which the suite can synthesize. What it CAN
208
+ * do is ensure the declaration and the obligation cannot drift apart: if someone
209
+ * renames the role value, or drops the requirement, this fails. RFC 0148 §A
210
+ * resolves the behavioral half to `blocked`, not to a pass.
211
+ */
212
+ describe.skipIf(V1_DIR === null)('replay.md req 4 — the manifest role is a classification floor', () => {
213
+ const dir = V1_DIR as string;
214
+ const replay = () => readFileSync(join(dir, 'replay.md'), 'utf8');
215
+ const manifest = () =>
216
+ readFileSync(join(dir, '..', '..', 'schemas', 'node-pack-manifest.schema.json'), 'utf8');
217
+
218
+ it('the manifest still offers the role value the requirement binds', () => {
219
+ // If `side-effect` is renamed or dropped from the taxonomy, requirement 4
220
+ // binds a value nothing can declare — a rule with no reachable trigger.
221
+ expect(
222
+ manifest().includes('side-effect'),
223
+ 'node-pack-manifest.schema.json §role MUST still describe the `side-effect` value that ' +
224
+ 'replay.md requirement 4 makes binding. A requirement whose trigger no longer exists is ' +
225
+ 'not enforcement, it is decoration.',
226
+ ).toBe(true);
227
+ });
228
+
229
+ it('replay.md binds that declaration, naming the schema it comes from', () => {
230
+ const doc = replay();
231
+ expect(
232
+ /MUST\*{0,2} be treated as side-effecting/.test(doc) && doc.includes('node-pack-manifest.schema.json'),
233
+ 'replay.md §"Requirements when a host declares `sideEffectSuppression: \\"recorded-outcome\\"`" ' +
234
+ 'MUST bind a manifest-declared `role: "side-effect"` to the suppression obligation, and MUST ' +
235
+ 'name the schema the declaration comes from so a reader can find it.',
236
+ ).toBe(true);
237
+ });
238
+
239
+ it('the floor direction is stated, not left to inference', () => {
240
+ // The dangerous reading is symmetric: "my classifier disagrees, so the
241
+ // manifest is wrong." The requirement is one-directional and says so.
242
+ expect(
243
+ replay().includes('MUST NOT classify fewer'),
244
+ 'replay.md requirement 4 MUST state the direction: a host classifier may treat MORE nodes as ' +
245
+ 'side-effecting and never fewer. Without the direction, a host that trusts its own list over ' +
246
+ 'the manifest can read the clause as permission to disagree.',
247
+ ).toBe(true);
248
+ });
249
+ });
@@ -122,11 +122,54 @@ describe.skipIf(SKIP_TIMEOUT)('run-execution-bounds: run-duration breach (RFC 00
122
122
  'run-event-payloads.schema.json §capBreached.kind',
123
123
  'cap.breached payload MUST carry kind="run-duration"',
124
124
  )).toBe('run-duration');
125
+ // Three distinct failure modes, asserted separately and WITH THE VALUES.
126
+ //
127
+ // The previous form ANDed all three into one boolean over a message that
128
+ // named none of them, so a failure said only "MUST be strictly greater" —
129
+ // you could not tell whether `observed` was missing, equal, or smaller. A
130
+ // tier-1 host hit this intermittently and had to reason out the mechanism
131
+ // from first principles, because the assertion about observed values did
132
+ // not report the observed values.
125
133
  expect(
126
- typeof payload?.observed === 'number' && typeof payload?.limit === 'number' && payload!.observed > payload!.limit,
134
+ typeof payload?.observed,
127
135
  driver.describe(
128
136
  'run-event-payloads.schema.json §capBreached.observed',
129
- 'observed (elapsedMs) MUST be strictly greater than limit (resolved timeout)',
137
+ `cap.breached MUST carry a numeric \`observed\`; got ${JSON.stringify(payload?.observed)}`,
138
+ ),
139
+ ).toBe('number');
140
+ expect(
141
+ typeof payload?.limit,
142
+ driver.describe(
143
+ 'run-event-payloads.schema.json §capBreached.limit',
144
+ `cap.breached MUST carry a numeric \`limit\`; got ${JSON.stringify(payload?.limit)}`,
145
+ ),
146
+ ).toBe('number');
147
+
148
+ // `capabilities.md` §"Engine-enforced limits": *"Always strictly greater
149
+ // than limit."* This is satisfiable and it constrains the host's comparison:
150
+ // breach when elapsed EXCEEDS the deadline, not when it reaches it. A host
151
+ // testing `elapsed >= limit` emits `observed === limit` exactly when the
152
+ // clock lands on the boundary — which is rare, machine-dependent, and
153
+ // therefore reads as flake rather than as the deterministic defect it is.
154
+ //
155
+ // That asymmetry is why the diagnosis belongs in the message: system load
156
+ // makes elapsed LARGER, so it makes this assertion easier to satisfy, not
157
+ // harder. An `observed === limit` failure is not a loaded box — it is a
158
+ // `>=` comparison in the host.
159
+ const { observed = NaN, limit = NaN } = payload ?? {};
160
+ expect(
161
+ observed > limit,
162
+ driver.describe(
163
+ 'run-event-payloads.schema.json §capBreached.observed',
164
+ `observed (elapsedMs) MUST be strictly greater than limit (resolved timeout). ` +
165
+ `Got observed=${observed}, limit=${limit}` +
166
+ (observed === limit
167
+ ? '. They are EQUAL, which means the host breached at `elapsed >= limit` rather than ' +
168
+ '`elapsed > limit`. The limit is not breached until it has been passed. This is ' +
169
+ 'deterministic in the host and only surfaces when the clock lands exactly on the ' +
170
+ 'boundary, so it presents as an intermittent failure — load makes elapsed larger and ' +
171
+ 'therefore makes this assertion PASS more often, not less.'
172
+ : '.'),
130
173
  ),
131
174
  ).toBe(true);
132
175
  });