@openwop/openwop-conformance 2.41.0 → 2.41.1

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.41.1] — 2026-09-26 — an SSE projection row no longer depends on a host's front forwarding headers early
4
+
5
+ - **`v2-stream-sse-projection` reads a terminal run's stream.** It streamed the noop run the moment it was created, so on a loaded certification cut the run was still queued and the stream stayed open. A public front that buffers SSE (openwop-app's Firebase Hosting) withholds the headers of an open stream, `streamEvents`' 8 s budget ran out before any header arrived, and `0171.stream-sse-projection` recorded `blocked` ("answered 0") on both of openwop-app's 2.40.3 production cuts. Quiet-machine time to first byte for the same request was 0.19 s. No rule bounds time to headers. The row tests how every frame names its run, and a terminal run's backlog carries every frame, so the leg now waits up to 30 s for the run to finish before it reads the stream (the host closes a terminal run's stream, events.md §SSE), and a missing response now says which of the two it was.
6
+ - **Version moved ahead of publication.** `@openwop/openwop-conformance` and its exact-pinned peer `@openwop/spec-artifacts` move to `2.41.1` because `2.41.0` is tagged. Not tagged, not published.
7
+
3
8
  ## [2.41.0] — 2026-09-26 — RFC 0111's context-budget witness can fail
4
9
 
5
10
  - **Conformance Soak is green again: the receiver modules count as harness doubles.** `host-callback-declaration` keys on imports of modules that stand up a server the host must reach, and its list omitted `scoped-receiver` and `effect-receiver`. So `v2-a2a-push-delivery`'s correct `REQUIRES_HOST_CALLBACK` read as a callback it "does not make", and the post-merge Soak failed on every run from #1546 (2026-09-24) on, which no PR check saw. Both modules join the list, and the eight scenarios that take a host callback through them now declare it (`v2-bound-id-kinds`, `v2-webhook-delivery-isolation`, `v2-webhook-delivery-shape`, `v2-webhook-durable-delivery`, `v2-webhook-unregister-stops-delivery`, `webhook-signed-delivery`, `v2-durability-recovery`, `v2-terminal-event-once`), so an off-process consumer can list them before running. Sabotage: dropping `scoped-receiver` again reproduces the Soak failure.
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.41.0 @openwop/spec-artifacts@2.41.0
14
+ npm install --legacy-peer-deps @openwop/openwop-conformance@2.41.1 @openwop/spec-artifacts@2.41.1
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.41.0",
4
- "stampSha256": "6b53adc27c8d6212c78dd08bf0be7b3c2d740d4290a72ce55324fd1fb4a6fe46"
3
+ "version": "2.41.1",
4
+ "stampSha256": "65a8826755d00694cf357104040996db9220790ddc8579ec4eaca262f75a444d"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "2.41.0",
3
+ "version": "2.41.1",
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.41.0"
59
+ "@openwop/spec-artifacts": "2.41.1"
60
60
  }
61
61
  }
package/requirements.json CHANGED
@@ -32891,7 +32891,7 @@
32891
32891
  {
32892
32892
  "id": "openwop.it.v2-stream-sse-projection.every-data-frame-on-the-major-2-stream-carries-the-tenant-bound-runid-of-the-run",
32893
32893
  "file": "v2-stream-sse-projection.test.ts",
32894
- "line": 52,
32894
+ "line": 81,
32895
32895
  "title": "every data: frame on the major-2 stream carries the tenant-bound runId of the run it belongs to",
32896
32896
  "explicitId": "openwop.requirement.0171.stream-sse-projection",
32897
32897
  "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.41.0",
5
- "corpusTag": "v2.41.0",
4
+ "version": "2.41.1",
5
+ "corpusTag": "v2.41.1",
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": "6366b5514be71098e9ea41fc81ff52bc39db1a547b353c0b344cc2a2bf5370aa",
11
11
  "api/redocly.yaml": "b0604c89b2ca6d5076ec25725c539dad44a741a811fe524439ee6daef8baa09f",
12
- "api/seams-v2.yaml": "1309f1f087272e74b836b436fa08160a1009feb9c341b6d359332e875ee7475f",
13
- "api/v2/asyncapi.yaml": "b34dcda44b69de1d9d41ede8c2c69cc0fdbb6cc3533588057dd3a71acc42da5a",
14
- "api/v2/openapi.yaml": "12ccdda2cf32ec21392fe84366607e0c69c4e07ae960a9fda3fd407a5cd7be78",
12
+ "api/seams-v2.yaml": "ce8ae43688e1b7d2a7906e320b99237f12adeebc7c854938679e91133cf76f37",
13
+ "api/v2/asyncapi.yaml": "6a8e4010cb2af7c0ef46ee1239a1da4742648667a97e121ab0bfa3023d0c414c",
14
+ "api/v2/openapi.yaml": "93b08e6581269e5cdeb39506a74aa41062666e29fc6fb9f4fb038977ab7453fd",
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": "a8008e40cb7e5e68ccd4fe6761e563354d34b5ab278722fab75bc7ed4bc40cae",
207
+ "spec/v1/core-standard-manifest.json": "809b6f0c90f84309e75c0f8425988baf0d65607d9f24a3453457080ac931792c",
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": "ae9b56d03a701061fd48ad0b73cb0373493f6a4547da92763b422295b5e19535",
295
295
  "spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
296
296
  "spec/v2/profiles.json": "0636f19fceae625390003a347e70ef4797d84766b5c24ce8a02cea52aadebca4",
297
- "spec/v2/release.json": "6849e210dc510d008596f51718f01f8deb32c6f5f9c3114f6e2707e778f38dcc",
297
+ "spec/v2/release.json": "7ea3c5b5441f86fc7e22eb13407e7554eba5f3c138fe30aaf76dc2a83a75de25",
298
298
  "spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624",
299
- "spec/v2/surface-baseline.json": "18c79a71b4b65cf8f9f8bf345d3828627bb17673b7279de276c1120d540a4746"
299
+ "spec/v2/surface-baseline.json": "b0c9d00354358f46f032b26d889d7fc4a71765833d2f1f21a6d822a5868780ad"
300
300
  },
301
- "corpusCommit": "d0da968bd70f57f4844b58f1b2544029dc576efb"
301
+ "corpusCommit": "55ec9e964bd782903c06fc687016d6e98a6ae5f2"
302
302
  }
@@ -48,15 +48,46 @@ async function createRun(): Promise<{ runId: string } | { reason: string }> {
48
48
  return { runId };
49
49
  }
50
50
 
51
+ const TERMINAL = new Set(['completed', 'failed', 'cancelled']);
52
+ /** How long the noop run gets to finish before the stream is read. */
53
+ const TERMINAL_WAIT_MS = 30_000;
54
+
55
+ /**
56
+ * Read the stream of a TERMINAL run (2.41.0). The requirement is how every
57
+ * frame names its run, and a terminal run's backlog carries every frame the
58
+ * projector renders, the batch flush included. Streaming a run that is still
59
+ * queued made the row depend on something it does not test: whether the host's
60
+ * public front forwards response headers before the stream ends. A front that
61
+ * buffers SSE (openwop-app's Firebase Hosting) holds the headers of an open
62
+ * stream, so during a loaded certification cut the noop run queued, the stream
63
+ * stayed open, streamEvents' 8 s budget ran out before any header arrived, and
64
+ * the row recorded `blocked` ("answered 0") on a host whose quiet-machine time
65
+ * to first byte for the same request is 0.19 s. A terminal run's stream is
66
+ * finite (the host closes it at the terminal event, events.md §SSE), so any
67
+ * front can deliver it.
68
+ */
69
+ async function waitTerminal(runId: string): Promise<string | null> {
70
+ const deadline = Date.now() + TERMINAL_WAIT_MS;
71
+ for (;;) {
72
+ const r = await http(() => driver.get(`/runs/${encodeURIComponent(runId)}`));
73
+ const status = r !== null && r.status === 200 ? String((r.json as { status?: unknown } | null)?.status ?? '') : '';
74
+ if (TERMINAL.has(status)) return status;
75
+ if (Date.now() > deadline) return null;
76
+ await new Promise((res) => setTimeout(res, 250));
77
+ }
78
+ }
79
+
51
80
  describe('v2 stream-sse-projection (events.md §SSE frames)', () => {
52
81
  it('every data: frame on the major-2 stream carries the tenant-bound runId of the run it belongs to', async () => {
53
82
  const c = await createRun();
54
83
  if ('reason' in c) return softSkip('blocked', c.reason);
55
84
  expect(RUN_ID.test(c.runId), req(ID, 'spec/v2/core/identity.md §5', `the created runId MUST be tenant-bound before the stream can be held to it (got ${c.runId})`)).toBe(true);
56
85
 
86
+ if ((await waitTerminal(c.runId)) === null) return softSkip('blocked', `the ${NOOP_WORKFLOW_ID} run ${c.runId} did not reach a terminal status within ${TERMINAL_WAIT_MS}ms, so its stream is not a finite backlog yet — nothing was read`);
57
87
  const s = await streamEvents(c.runId);
58
88
  if (s === null) return softSkip('blocked', 'GET /runs/{runId}/events (SSE, OpenWOP-Version: 2.0) unreachable (fetch failed)');
59
- if (s.status !== 200) return softSkip('blocked', `GET /runs/{runId}/events (SSE) answered ${s.status} for the run just created`);
89
+ if (s.status === 0) return softSkip('blocked', 'GET /runs/{runId}/events (SSE) for a TERMINAL run returned no response headers within 8 s — the host closes a terminal run\'s stream (events.md §SSE), so a front that buffers the response should still have delivered it');
90
+ if (s.status !== 200) return softSkip('blocked', `GET /runs/{runId}/events (SSE) answered ${s.status} for a terminal run`);
60
91
  if (s.events.length === 0) return softSkip('blocked', 'the stream delivered no data: frames within the window — nothing to hold to the grammar');
61
92
 
62
93
  const validate = v2Validator('run-event');