@openwop/openwop-conformance 2.42.5 → 2.42.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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # `@openwop/openwop-conformance` Changelog
2
2
 
3
+ ## [2.42.6] — 2026-09-27 — the era-2 writer rule is witnessable again
4
+
5
+ - **`0176.era-2-append-vocabulary` is a real witness again on a host that serves the new seam.** New OPTIONAL seams-profile seam `appendEra2Event` (`POST /conformance/seams/sample/event-log/append`, `api/seams-v2.yaml`; `host-sample-test-seams.md` §27) appends ONE v2-named event to a seeded era-2 run through the host's PRODUCTION writer; the leg appends `agent.reasoning-delta` (a codemap-renamed type, stored v1 `agent.reasoning.delta`) and requires the translated read to return the v2 name. Sabotaged: a host whose writer stores v2 names fails the read `500 event_type_unmapped` → `executed-fail`. Dispositions: seam served → a real assertion (the 2.42.5 `partial-witness-ok` annotation is gone for that path); seam served but contract broken → `blocked`; seam NOT served → the 2.42.5 partial witness, never `blocked` (an optional seam newer than the leg must not take away a certification); seams profile not claimed → `inapplicable`.
6
+ - **Suite `2.42.6`**. `@openwop/spec-artifacts` moves in lockstep at the same exact pin.
7
+
3
8
  ## [2.42.5] — 2026-09-27 — hotfix: a host can certify again
4
9
 
5
10
  - **`0176.era-2-append-vocabulary` no longer blocks every host.** 2.42.3's unfailable-leg audit made this leg `blockedDespiteAssertions` whenever a host's appended types are spelled the same in v1 and v2. No catalogued seam lets any host make a seeded era-2 run append a type the codemap renames, so the row recorded `blocked` on every host, and one `blocked` row denies the whole bundle (RFC 0168 §E.1). **No host could certify on 2.42.3 or 2.42.4.** The leg now records a partial witness whose detail says the codemap-renaming writer rule is unwitnessed. The append, the read-back and the sequence rules are observed, and the acceptance predicate still refuses a partial-witness row. It is annotated `partial-witness-ok` for the softskip gate. openwop-1f is adding the seam that makes the rule witnessable. Verified on the v2 reference host: the row now records `executed-pass` with that detail.
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  # --legacy-peer-deps is REQUIRED, not optional: the exact peer pin is what npm's
12
12
  # default resolver refuses. npm 10.9 fails outright with
13
13
  # "Cannot read properties of null (reading 'edgesOut')" — use npm >= 11.
14
- npm install --legacy-peer-deps @openwop/openwop-conformance@2.42.5 @openwop/spec-artifacts@2.42.5
14
+ npm install --legacy-peer-deps @openwop/openwop-conformance@2.42.6 @openwop/spec-artifacts@2.42.6
15
15
  # or run without install:
16
16
  npx @openwop/openwop-conformance --base-url https://api.example.com --api-key hk_test_...
17
17
  ```
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "package": "@openwop/spec-artifacts",
3
- "version": "2.42.5",
4
- "stampSha256": "3f879249fa54b8619a9a4b156aa3064f2ad660e2e5a7d232eb4da0d3c5734f58"
3
+ "version": "2.42.6",
4
+ "stampSha256": "f52bf05bf4a2f7218557589a5f54422ec57b5ec081102a5f4ab72ed3fb3f72c9"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "2.42.5",
3
+ "version": "2.42.6",
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.42.5"
59
+ "@openwop/spec-artifacts": "2.42.6"
60
60
  }
61
61
  }
package/requirements.json CHANGED
@@ -29109,7 +29109,7 @@
29109
29109
  {
29110
29110
  "id": "openwop.it.v2-era-2-append-vocabulary.a-host-append-to-an-open-era-2-run-keeps-the-log-in-v1-vocabulary-so-the-whole-l",
29111
29111
  "file": "v2-era-2-append-vocabulary.test.ts",
29112
- "line": 48,
29112
+ "line": 50,
29113
29113
  "title": "a host append to an open era-2 run keeps the log in v1 vocabulary, so the whole log still reads",
29114
29114
  "explicitId": "openwop.requirement.0176.era-2-append-vocabulary",
29115
29115
  "citations": [
@@ -29128,6 +29128,11 @@
29128
29128
  "requirement": null,
29129
29129
  "interpolated": true
29130
29130
  },
29131
+ {
29132
+ "section": "spec/v2/core/persistence.md §The writer rule",
29133
+ "requirement": null,
29134
+ "interpolated": true
29135
+ },
29131
29136
  {
29132
29137
  "section": "spec/v2/core/persistence.md §The writer rule",
29133
29138
  "requirement": null,
@@ -29138,7 +29143,7 @@
29138
29143
  {
29139
29144
  "id": "openwop.it.v2-era-2-append-vocabulary.the-run-keeps-the-era-it-was-created-with-an-append-does-not-promote-it-to-era-3",
29140
29145
  "file": "v2-era-2-append-vocabulary.test.ts",
29141
- "line": 174,
29146
+ "line": 189,
29142
29147
  "title": "the run keeps the era it was created with; an append does not promote it to era 3",
29143
29148
  "explicitId": "openwop.requirement.0176.era-2-append-vocabulary",
29144
29149
  "citations": [
@@ -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.42.5",
5
- "corpusTag": "v2.42.5",
4
+ "version": "2.42.6",
5
+ "corpusTag": "v2.42.6",
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": "69464bc0e1e71ef3b543a73d4fa4f67202343d2603c80af8033f6c3619ef1b30",
11
11
  "api/redocly.yaml": "b0604c89b2ca6d5076ec25725c539dad44a741a811fe524439ee6daef8baa09f",
12
- "api/seams-v2.yaml": "33312adcc1cf1185db6f926195dbc2af3cdf626fb686b34e12b3e0f5c2d0f12c",
13
- "api/v2/asyncapi.yaml": "ef7841d00162c7b89caa6df100f48a62927a619f0363733591a4b9a988ef570d",
14
- "api/v2/openapi.yaml": "bb0140dc59875b8d0dc0c04a128372c09a088340dda45f139ff4a218412bbab4",
12
+ "api/seams-v2.yaml": "c188943dbb662ddb1d8ed26759c1daa560f63617fedd2a116b8abcc41c2f42de",
13
+ "api/v2/asyncapi.yaml": "94b9174faeccabea8f2d29fe3e80da7d16037b497ea02f91e61e512ff8ea8d62",
14
+ "api/v2/openapi.yaml": "5f2bcb438b77c250e0732f1808690bb710941a7c7ee5150a27e66537dee9fcb8",
15
15
  "api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
16
16
  "schemas/README.md": "0c0b737ffcf8f30e7d2809cec8a498232de710f41443212922ad8337cdde0b51",
17
17
  "schemas/a2a-task-state.schema.json": "75d5049dea8bd873ff0e7546f1c60c8d36c219bec7084264360be54a8be30ae1",
@@ -204,7 +204,7 @@
204
204
  "schemas/workspace-file.schema.json": "464de85c2a068243084ee9c1d969bc7cd5d8f7948574e58450d6493c38a0e1e4",
205
205
  "spec/v1/alias-detectors.json": "2401fcb1c18cdd688c018b3d716ae6bca85c5e356220ee2b793bd9d81872412d",
206
206
  "spec/v1/capability-declaration-classes.json": "e7729aed5c4b4e1dd02abab0530f14cc95f5d4070fe51fb139e7f5cccefa00c6",
207
- "spec/v1/core-standard-manifest.json": "570aecd605ffb479c3f5aa73e0e0259747d95ecad0fc26f0438f66deaa67ebad",
207
+ "spec/v1/core-standard-manifest.json": "844a89d3bba9b850d8ae949dc7f0b0ecade689c748263d6c7360e2fe7fb7cf8f",
208
208
  "spec/v1/deprecations.json": "2d03f4729810280147ea08c630ea65434f2dc5370567d0aac41c3595f8337d40",
209
209
  "spec/v1/deprecations.schema.json": "3e393c405d2a41b467d8c5e3c468549078df1ce6a6d2588fc488097b95d9b55b",
210
210
  "spec/v1/event-codemap.json": "3da60d884157793a360da532a9fcbbfb5285636db325a74cec94b34622186d97",
@@ -294,9 +294,9 @@
294
294
  "spec/v2/path-manifest.json": "17298a245c5da9a70aa7ab0b7f2db4277e06209d6fcdd7187a8b4cea179c08f3",
295
295
  "spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
296
296
  "spec/v2/profiles.json": "0636f19fceae625390003a347e70ef4797d84766b5c24ce8a02cea52aadebca4",
297
- "spec/v2/release.json": "5132e4752b9e36d2ab61076455e3efbda31127e76565faadb67446f77707a246",
297
+ "spec/v2/release.json": "f599686983f47f146a03edec608d63f31525b3e3268036684434afcb972e1cef",
298
298
  "spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624",
299
- "spec/v2/surface-baseline.json": "ef98caceff85da0d8bae10e78d044762d47960432b7fa885c4c56697c7038274"
299
+ "spec/v2/surface-baseline.json": "34404cc65fb70b8a54bbee40d8e0f5047a29e7df4f1abd55741a697549ccaccf"
300
300
  },
301
- "corpusCommit": "bfa55619a403957a27412e0fa2c8865a48199c38"
301
+ "corpusCommit": "d578116b9665df11f8d1eaad11fc45f71e826d37"
302
302
  }
@@ -85,6 +85,36 @@ export function era2Gate(doc: Record<string, unknown> | null): Seeded | null {
85
85
  return null;
86
86
  }
87
87
 
88
+ export const APPEND_PATH_V1 = '/v1/host/sample/event-log/append';
89
+ export const APPEND_PATH = seamPath(APPEND_PATH_V1);
90
+
91
+ export type Appended =
92
+ | { readonly ok: true; readonly sequence: number }
93
+ | { readonly ok: false; readonly unserved: true; readonly reason: string }
94
+ | { readonly ok: false; readonly unserved: false; readonly kind: 'blocked'; readonly reason: string };
95
+
96
+ /**
97
+ * Append ONE v2-named event to a seeded era-2 run through the host's production
98
+ * writer (host-sample-test-seams.md §27, `appendEra2Event`). The seam is only
99
+ * the trigger: which spelling is STORED is the host writer's decision, and the
100
+ * production read is what the caller asserts on. UNSERVED (404/405/501) is
101
+ * reported separately from a served seam that breaks its contract: §27 is an
102
+ * OPTIONAL seam added after the leg already certified, so its absence falls back
103
+ * to the pre-seam partial witness rather than denying certification.
104
+ */
105
+ export async function appendEra2Event(runId: string, type: string, payload: Record<string, unknown>): Promise<Appended> {
106
+ const res = await http(() => driver.post(APPEND_PATH_V1, { runId, type, payload }));
107
+ if (res === null) return { ok: false, unserved: false, kind: 'blocked', reason: `${APPEND_PATH} unreachable (fetch failed)` };
108
+ if (res.status === 404 || res.status === 405 || res.status === 501) {
109
+ return { ok: false, unserved: true, reason: `${APPEND_PATH} answered ${res.status} — the host does not serve the optional appendEra2Event seam (host-sample-test-seams.md §27)` };
110
+ }
111
+ const sequence = (res.json as { sequence?: unknown } | undefined)?.sequence;
112
+ if (res.status !== 202 || typeof sequence !== 'number') {
113
+ return { ok: false, unserved: false, kind: 'blocked', reason: `${APPEND_PATH} answered ${res.status} ${readErrorCode(res.json) ?? ''} without a 202 { runId, sequence } — the append contract is not honoured`.trim() };
114
+ }
115
+ return { ok: true, sequence };
116
+ }
117
+
88
118
  /** Seed an era-2 log in v1 vocabulary; the reason names the seam when it is absent. */
89
119
  export async function seedEra2Log(events: readonly SeedEvent[], status: 'completed' | 'running' | 'paused' = 'completed'): Promise<Seeded> {
90
120
  const res = await http(() => driver.post(SEED_PATH_V1, { eventLogSchemaVersion: 2, status, events }));
@@ -31,10 +31,12 @@ import { v2Discovery } from '../lib/v2.js';
31
31
  import { softSkip } from '../lib/soft-skip.js';
32
32
  import { req } from '../lib/requirement-ids.js';
33
33
  import { readErrorCode } from '../lib/error-envelope.js';
34
- import { codemapV1toV2, era2Gate, eventsOf, pollEvents, seedEra2Log, v1FixtureLog, type ReadEvent } from '../lib/era2-seed.js';
34
+ import { appendEra2Event, codemapV1toV2, era2Gate, eventsOf, pollEvents, seedEra2Log, v1FixtureLog, type ReadEvent } from '../lib/era2-seed.js';
35
35
 
36
36
  const ID = 'openwop.requirement.0176.era-2-append-vocabulary';
37
37
  const DOC = 'spec/v2/core/persistence.md §The writer rule';
38
+ /** A type the codemap renames (v1 `agent.reasoning.delta`); artifact event, no projection effect. */
39
+ const WITNESS_TYPE = 'agent.reasoning-delta';
38
40
 
39
41
  async function http(fn: () => Promise<OpenWOPResponse>): Promise<OpenWOPResponse | null> {
40
42
  try {
@@ -88,20 +90,31 @@ describe('v2-era-2-append-vocabulary (RFC 0176 §A — the writer rule)', () =>
88
90
  // Neither answer is asserted: the resume transition's event set is not
89
91
  // pinned to a renamed type, and a seeded run's pause is host latitude. If
90
92
  // no renamed type is appended, the leg records `blocked` below, not a pass.
93
+ // 2.42.6: the writer-rule witness proper. `appendEra2Event` (host-sample-test-
94
+ // seams.md §27) hands the host ONE event of a type the codemap RENAMES
95
+ // (`agent.reasoning-delta`, stored v1 `agent.reasoning.delta`) and the host's
96
+ // production writer chooses the stored spelling; the read below is the witness.
97
+ // It is an artifact event, so it moves no projection. A host that does not
98
+ // serve the seam falls through to the pre-seam path below and ends in the
99
+ // annotated partial witness, never `blocked` (see the next comment); the gate
100
+ // above already made a host without the seams profile `inapplicable`.
101
+ const appended = await appendEra2Event(runId, WITNESS_TYPE, { agentId: 'conformance', delta: 'era-2 writer-rule witness', sequence: 0 });
102
+ // A served seam that breaks its contract is `blocked`. An UNSERVED seam is not:
103
+ // §27 is optional and newer than this leg, so its absence falls through to the
104
+ // pre-seam path and ends in the partial witness below — it never denies a
105
+ // host certification it held before the seam existed.
106
+ if (!appended.ok && !appended.unserved) return softSkip(appended.kind, appended.reason);
107
+ const seamServed = appended.ok;
108
+
91
109
  const enc = encodeURIComponent(runId);
92
110
  const paused = await http(() => driver.post(`/runs/${enc}:pause`, { reason: 'conformance', drainPolicy: 'immediate' }));
93
- const resumed = paused?.status === 202 ? await http(() => driver.post(`/runs/${enc}:resume`, { reason: 'conformance' })) : null;
94
-
95
- // One canonical mutation so the HOST's own writer appends. Cancel is the
96
- // universally available terminal transition; a host that refuses it on a
97
- // seeded run records `blocked` rather than a pass — unless the pause and
98
- // resume above were both accepted, which already drove the host's writer.
99
- const cancelled = await http(() => driver.post(`/runs/${enc}/cancel`, {}));
100
- const cancelOk = cancelled !== null && (cancelled.status === 200 || cancelled.status === 202 || cancelled.status === 204);
101
- const pauseResumeOk = paused?.status === 202 && resumed?.status === 202;
102
- if (!cancelOk && !pauseResumeOk) {
103
- return softSkip('blocked', `POST /runs/{runId}/cancel answered ${cancelled?.status ?? 'no response'} on a seeded era-2 run (pause ${paused?.status ?? 'no response'}, resume ${resumed?.status ?? 'not attempted'}) — no canonical mutation drove the host's writer, so the append is unwitnessed`);
104
- }
111
+ if (paused?.status === 202) await http(() => driver.post(`/runs/${enc}:resume`, { reason: 'conformance' }));
112
+
113
+ // A canonical mutation as well, so the host's own lifecycle writer appends
114
+ // beside the seam's append. The seam append above already drove the
115
+ // production writer, so a host that refuses cancel on a SEEDED run no longer
116
+ // leaves the rule unwitnessed — the answer is not asserted here.
117
+ await http(() => driver.post(`/runs/${enc}/cancel`, {}));
105
118
 
106
119
  const after = await pollEvents(runId);
107
120
  if (after === null) return softSkip('blocked', 'the event read failed after the append');
@@ -157,18 +170,20 @@ describe('v2-era-2-append-vocabulary (RFC 0176 §A — the writer rule)', () =>
157
170
  // writer rule is unobserved.
158
171
  const renamedV2 = new Set([...codemapV1toV2()].filter(([v1, v2]) => v1 !== v2).map(([, v2]) => v2));
159
172
  const appendedTypes = rows.slice(seedCount).map((r) => String(r.type ?? ''));
160
- if (!appendedTypes.some((t) => renamedV2.has(t))) {
161
- // partial-witness-ok: HOTFIX 2.42.5 (2026-09-27). 2.42.3 and 2.42.4 made this
162
- // `blockedDespiteAssertions`, which is bundle-fatal (RFC 0168 §E.1). No catalogued
163
- // seam lets ANY host make a seeded era-2 run append a type the codemap renames,
164
- // so the row blocked on every host and no host could certify. That is the suite's
165
- // gap, not a host's. The append, the read-back and the sequence rules above ARE
166
- // observed. The codemap-renaming writer rule is NOT, and this partial witness says
167
- // so in its detail; the acceptance predicate refuses a partial-witness row. The
168
- // seams-contract addition that makes the rule witnessable is in progress
169
- // (openwop-1f); when it lands, this becomes a real assertion again.
170
- return softSkip('inapplicable', `partial witness — the codemap-renaming writer rule is UNWITNESSED: every type the host appended to the era-2 log (${appendedTypes.join(', ') || 'none'}) is spelled identically in v1 and v2 (spec/v2/event-codemap.json), so a host writing v2 names reads back the same as one writing v1 names — the writer rule is unobserved. The leg needs a canonical mutation that appends a renamed type (e.g. run.resuming → run.resume-started); pause ${paused?.status ?? 'no response'} / resume ${resumed?.status ?? 'not attempted'} on the seeded run appended none, and the seed seam (api/seams-v2.yaml seedEra2EventLog) cannot seed a paused or interrupt-suspended era-2 run to resume. A seams-contract addition to seed one is in progress.`);
173
+ if (!seamServed && !appendedTypes.some((t) => renamedV2.has(t))) {
174
+ // partial-witness-ok: the host does not serve the optional appendEra2Event
175
+ // seam (host-sample-test-seams.md §27), and none of its own appends was a
176
+ // codemap-RENAMED type, so the writer rule is unobserved here. The append,
177
+ // the read-back and the sequence rules above ARE observed. This is the
178
+ // 2.42.5 disposition, kept for hosts without the seam; the acceptance
179
+ // predicate refuses a partial-witness row. (A host without the seam whose
180
+ // own pause/resume appended a renamed type falls through: that IS a witness.)
181
+ return softSkip('inapplicable', `partial witness — the codemap-renaming writer rule is UNWITNESSED: ${appended.ok ? '' : appended.reason}; every type the host appended to the era-2 log (${appendedTypes.join(', ') || 'none'}) is spelled identically in v1 and v2 (spec/v2/event-codemap.json)`);
171
182
  }
183
+ if (seamServed) expect(
184
+ appendedTypes.includes(WITNESS_TYPE),
185
+ req(ID, DOC, `the event appended through appendEra2Event (${WITNESS_TYPE}, a codemap-RENAMED type) MUST read back under its v2 name through the translated read — it read as ${appendedTypes.join(', ') || 'nothing'}: a host that stored the v2 spelling in an era-2 log makes the reader map it a second time (event_type_unmapped), and one that bypassed the storage boundary leaks the v1 spelling`),
186
+ ).toBe(true);
172
187
  });
173
188
 
174
189
  it('the run keeps the era it was created with; an append does not promote it to era 3', async () => {