@openwop/openwop-conformance 2.45.22 → 2.45.23
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 +14 -0
- package/README.md +2 -2
- package/dist/spec-artifacts.lock.json +2 -2
- package/fixtures/conformance-multi-party-council-oversize.json +244 -0
- package/fixtures/conformance-multi-party-council.json +61 -0
- package/fixtures/conformance-nondeterminism.json +23 -0
- package/fixtures/conformance-wasm-pack-memory-cap-breach.json +23 -5
- package/fixtures/frontend-plugin-packs/ui/narrow.html +4 -0
- package/fixtures/frontend-plugin-packs/ui-plugin-pack-narrow.json +16 -0
- package/fixtures/wasm-packs/build.mjs +43 -0
- package/fixtures/wasm-packs/misbehaving-abi/module.wasm +0 -0
- package/fixtures/wasm-packs/misbehaving-abi/module.wat +24 -0
- package/fixtures/wasm-packs/misbehaving-abi/pack.json +27 -0
- package/fixtures/wasm-packs/misbehaving-memory/module.wasm +0 -0
- package/fixtures/wasm-packs/misbehaving-memory/module.wat +30 -0
- package/fixtures/wasm-packs/misbehaving-memory/pack.json +27 -0
- package/fixtures.md +32 -0
- package/package.json +2 -2
- package/requirements.json +251 -7
- package/scenario-majors.json +17 -2
- package/schemas/CORPUS-STAMP.json +29 -27
- package/src/lib/council-roster-witness.ts +144 -0
- package/src/lib/ui-plugin-boundary-witness.ts +180 -0
- package/src/lib/wasm-memory-cap-witness.ts +46 -0
- package/src/scenarios/v2-bound-id-path-projection.test.ts +20 -0
- package/src/scenarios/v2-envelope-contracts-advertisement.test.ts +8 -0
- package/src/scenarios/v2-multi-party-council.test.ts +56 -0
- package/src/scenarios/v2-nondeterminism-policy-advertisement.test.ts +11 -1
- package/src/scenarios/v2-nondeterminism-sources.test.ts +83 -0
- package/src/scenarios/v2-purpose-propagation-advertisement.test.ts +1 -1
- package/src/scenarios/v2-purpose-propagation-onward.test.ts +119 -0
- package/src/scenarios/v2-ui-plugin-boundary.test.ts +68 -0
- package/src/scenarios/v2-wasm-memory-cap.test.ts +34 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# `@openwop/openwop-conformance` Changelog
|
|
2
2
|
|
|
3
|
+
## [2.45.23] — 2026-10-06 — `0184.mint-no-tilde` also mints under the second-tenant credential; RFC 0237 filed
|
|
4
|
+
|
|
5
|
+
- **Erratum: `host.announce` is a `ui-plugin/1` request method at v2.** The v2 `ui-plugin-message.schema.json` request enum omitted it, although the manifest `hostApi` enum and the `uiPlugins.hostApi` facet admit it (RFC 0130), so a host advertising it had to refuse it at `dispatchUiPluginRequest`. The schema is now hand-owned (no v1 seed marker). Found by openwop-app (ADR 0840).
|
|
6
|
+
- **New `v2-multi-party-council` (RFC 0239).** Gated on `multiPartyConversation` and the advertised `conformance-multi-party-council` fixtures: `conversation.opened` carries the configured roster; resuming the exchange with a non-member `speakerId` is `422 conversation_speaker_not_participant`, unconsumed, and a member's turn then resolves it; with `maxParticipants` below 64 the 64-member oversize fixture is refused at run creation with `422 conversation_roster_exceeded`. Proof: a 9-case self-test double (`lib/council-roster-witness.test.ts`), each defect failing only its leg.
|
|
7
|
+
- **WASM fixture packs and `v2-wasm-memory-cap`.** `conformance/fixtures/wasm-packs/` ships two operator-installed node packs written in WebAssembly text (`build.mjs` assembles them with `wabt`, so no Rust toolchain is needed): `misbehaving-memory` grows memory until the host refuses, `misbehaving-abi` reports ABI version 999. New `v2-wasm-memory-cap` (`openwop.requirement.node-pack-runtimes.wasm-memory-cap`) runs `conformance-wasm-pack-memory-cap-breach` and expects `cap.breached` `kind: wasm-memory` and a `failed` run; proven by a 7-case double. `src/coherence/wasm-fixture-packs.test.ts` instantiates both modules and checks their exports, ABI version, pack name and typeId against their v2 manifests. The ABI-rejection leg is not written: rejection at load has no protocol observation path yet.
|
|
8
|
+
- **New `v2-ui-plugin-boundary` (RFC 0238).** Gated on `uiPlugins.served` and the operator-installed `ui-plugin-pack-narrow` fixture: the frame's `Content-Security-Policy` sandboxes without `allow-same-origin` and denies egress; the dispatch refuses `artifact.write` and `host.navigate` (undeclared) with `method_not_allowed`, does not execute `host.exec`, and runs `artifact.read`; no response carries canary material. The four `frontend-plugin-*` invariants move from `seam-gated` to `witnessable-gated`. Proof: a 12-case self-test double (`lib/ui-plugin-boundary-witness.test.ts`), each defect failing only its leg. The fixture's shape is a corpus check (`src/coherence/ui-plugin-fixtures.test.ts`).
|
|
9
|
+
- **No scenario change.** RFC 0237 (declared nondeterminism names its sources) is filed `Draft`; its gap rows change the packed `spec/v1/gaps.json`.
|
|
10
|
+
|
|
11
|
+
- **`0184.mint-no-tilde` also mints under `OPENWOP_TEST_TENANT_B_API_KEY`.** A host can bind tenant B to a non-clean tenant, so the leg bites in a production cut without moving the primary key. Asked by openwop-app, whose primary tenant is clean. A refused tenant-B create skips only that half. Proven against a double.
|
|
12
|
+
|
|
13
|
+
- **New `v2-purpose-propagation-onward` (seam-gated).** The suite hands the host a `permittedPurposes` label through the §22 `invoke` seam (new optional field; the seam is now in `api/seams-v2.yaml`), and its fake A2A peer checks the onward message: re-emitted, never widened, and nothing sent for `[]`. Proven against a double.
|
|
14
|
+
|
|
15
|
+
- **RFC 0237 Active: new `v2-nondeterminism-sources` (fixture-gated) and the §C legs.** A host listing `nondeterminismPolicy.sources` runs `conformance-nondeterminism` (reserved typeId `core.conformance.nondeterminism`); a `replay` fork from `fromSeq: 0` must reproduce each listed source's output. The two advertisement scenarios fail `declared: false` / `advertised: false`.
|
|
16
|
+
|
|
3
17
|
## [2.45.22] — 2026-10-06 — an empty `dataResidency.regions` still binds the reject leg; RFC 0236's registers close
|
|
4
18
|
|
|
5
19
|
- **No scenario change.** RFC 0236 goes `Accepted` on the v2 reference host's certified 2.45.21 cut; its gaps G1 and G2 close, which changes the packed `spec/v1/gaps.json`.
|
package/README.md
CHANGED
|
@@ -135,7 +135,7 @@ Exit code is non-zero on any failed assertion. `--certify` distinguishes: `0`
|
|
|
135
135
|
|
|
136
136
|
## What's Covered
|
|
137
137
|
|
|
138
|
-
The current suite has
|
|
138
|
+
The current suite has 649 scenario files under `src/scenarios/`.
|
|
139
139
|
- 2026-09-29 (suite 2.45.0, openwop#1763): NEW `v2-cors-preflight.test.ts` — `headers.md` §Cross-origin preflight. For every operation it preflights with `Origin`, the method, and the operation's declared request headers (plus `authorization` / `content-type` where they apply, from `spec/v2/path-manifest.json`), and reads the answer as the Fetch standard's CORS-preflight check does. Self-gated: `inapplicable` when no operation's preflight grants `OPENWOP_CORS_ORIGIN` (default `https://conformance.invalid`).
|
|
140
140
|
- 2026-09-23 (suite 2.37.0 cycle, RFC 0213): NEW `v2-sse-last-event-id-cursor.test.ts` (a `Last-Event-ID` past the log is an exclusive cursor; a malformed id, when refused, is `400 validation_error`; the cursor never changes the answer for an unknown or foreign-tenant run — public test of `event-cursor-after-authorization`), `v2-idempotency-in-flight.test.ts` (five concurrent same-key creates yield one run; each loser is a marked replay or `409 idempotency_in_flight` with no retry timing in `details`; `partial-witness` when no loser was refused in flight) and `v2-interrupt-resolve-terminal.test.ts` (a run-scoped resolve after cancel or completion is `409 interrupt_already_resolved`, never `interrupt_cancelled`). All three sit off the core-standard floor until measured on the three bundle hosts.
|
|
141
141
|
- 2026-09-03 (suite `1.157.0 -> 1.158.0`, gap G17): NEW `idempotency-concurrent-claim.test.ts` — drives the new `host-sample-test-seams.md` §25 concurrent duplicate-delivery seam for the RFC 0150 §B / `idempotency.md` §"Concurrent duplicates (Layer 2)" atomic-claim MUST, which is unconditional and had no witness of any kind. Asserts every executor mints the SAME `logicalInvocationId` **before** asserting `delivered === 1` — without the identity check a host passes by minting different ids and never colliding, one effect because nothing raced. Not profile-gated and so not opt-out-able (the obligation is unconditional); an unmounted seam records `blocked`, which is not certifiable. Graduates `layer2-invocation-claim-atomic` reference-impl -> protocol.
|
|
@@ -482,7 +482,7 @@ Server-required (added in 1.7.0):
|
|
|
482
482
|
| ------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
483
483
|
| **Redaction** | [`capabilities.md`](../spec/v1/capabilities.md) §"Secrets" + NFR-7 + §"aiProviders" | Vendor-neutral assertions that the server doesn't leak secret material. Three scenario groups: (a) discovery shape contract — `secrets` + `aiProviders` advertisements are well-formed regardless of `secrets.supported`; when `supported === true`, scopes MUST be non-empty + `resolution === 'host-managed'`; `byok ⊆ supported`. (b) bearer-token redaction — invalid Bearer canary in `Authorization` header is not echoed in the 401 response body. (c) credentialRef echo control — gated on `secrets.supported === true`; canary planted in `configurable.ai.credentialRef` MUST NOT appear in any RunEvent payload (poll-based capture; transport-agnostic). Uses runtime-built canary fixtures (`lib/canaries.ts`) that defeat static secret scanners. 6 scenarios. |
|
|
484
484
|
|
|
485
|
-
Current source tree:
|
|
485
|
+
Current source tree: 649 scenario files. Use [`coverage.md`](./coverage.md) for current grade/gap tracking.
|
|
486
486
|
|
|
487
487
|
## Remaining Gaps
|
|
488
488
|
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "conformance-multi-party-council-oversize",
|
|
3
|
+
"name": "Conformance: Multi-Party Council (oversize roster)",
|
|
4
|
+
"version": "1.0",
|
|
5
|
+
"description": "RFC 0239 §E.2. The same gate with a 64-member roster. A host advertising multiPartyConversation.maxParticipants below 64 MUST refuse run creation with 422 conversation_roster_exceeded and open no conversation. See v2-multi-party-council.test.ts.",
|
|
6
|
+
"nodes": [
|
|
7
|
+
{
|
|
8
|
+
"id": "convo",
|
|
9
|
+
"typeId": "core.conversationGate",
|
|
10
|
+
"name": "Conversation Gate",
|
|
11
|
+
"position": {
|
|
12
|
+
"x": 0,
|
|
13
|
+
"y": 0
|
|
14
|
+
},
|
|
15
|
+
"config": {
|
|
16
|
+
"lifecycle": "open-exchange-close",
|
|
17
|
+
"mockAutoResume": false,
|
|
18
|
+
"participants": [
|
|
19
|
+
{
|
|
20
|
+
"agentId": "host:conformance-council-01"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"agentId": "host:conformance-council-02"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"agentId": "host:conformance-council-03"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"agentId": "host:conformance-council-04"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"agentId": "host:conformance-council-05"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"agentId": "host:conformance-council-06"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"agentId": "host:conformance-council-07"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"agentId": "host:conformance-council-08"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"agentId": "host:conformance-council-09"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"agentId": "host:conformance-council-10"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"agentId": "host:conformance-council-11"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"agentId": "host:conformance-council-12"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"agentId": "host:conformance-council-13"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"agentId": "host:conformance-council-14"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"agentId": "host:conformance-council-15"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"agentId": "host:conformance-council-16"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"agentId": "host:conformance-council-17"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"agentId": "host:conformance-council-18"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"agentId": "host:conformance-council-19"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"agentId": "host:conformance-council-20"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"agentId": "host:conformance-council-21"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"agentId": "host:conformance-council-22"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"agentId": "host:conformance-council-23"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"agentId": "host:conformance-council-24"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"agentId": "host:conformance-council-25"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"agentId": "host:conformance-council-26"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"agentId": "host:conformance-council-27"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"agentId": "host:conformance-council-28"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"agentId": "host:conformance-council-29"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"agentId": "host:conformance-council-30"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"agentId": "host:conformance-council-31"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"agentId": "host:conformance-council-32"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"agentId": "host:conformance-council-33"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"agentId": "host:conformance-council-34"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"agentId": "host:conformance-council-35"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"agentId": "host:conformance-council-36"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"agentId": "host:conformance-council-37"
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"agentId": "host:conformance-council-38"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"agentId": "host:conformance-council-39"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"agentId": "host:conformance-council-40"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"agentId": "host:conformance-council-41"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"agentId": "host:conformance-council-42"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"agentId": "host:conformance-council-43"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"agentId": "host:conformance-council-44"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"agentId": "host:conformance-council-45"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"agentId": "host:conformance-council-46"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"agentId": "host:conformance-council-47"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"agentId": "host:conformance-council-48"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"agentId": "host:conformance-council-49"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"agentId": "host:conformance-council-50"
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"agentId": "host:conformance-council-51"
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"agentId": "host:conformance-council-52"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"agentId": "host:conformance-council-53"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"agentId": "host:conformance-council-54"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"agentId": "host:conformance-council-55"
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"agentId": "host:conformance-council-56"
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"agentId": "host:conformance-council-57"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"agentId": "host:conformance-council-58"
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"agentId": "host:conformance-council-59"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"agentId": "host:conformance-council-60"
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"agentId": "host:conformance-council-61"
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"agentId": "host:conformance-council-62"
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"agentId": "host:conformance-council-63"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"agentId": "host:conformance-council-64"
|
|
210
|
+
}
|
|
211
|
+
]
|
|
212
|
+
},
|
|
213
|
+
"inputs": {}
|
|
214
|
+
}
|
|
215
|
+
],
|
|
216
|
+
"edges": [],
|
|
217
|
+
"triggers": [
|
|
218
|
+
{
|
|
219
|
+
"id": "manual",
|
|
220
|
+
"type": "manual",
|
|
221
|
+
"enabled": true
|
|
222
|
+
}
|
|
223
|
+
],
|
|
224
|
+
"variables": [],
|
|
225
|
+
"channels": {
|
|
226
|
+
"conversation": {
|
|
227
|
+
"schema": {
|
|
228
|
+
"type": "array"
|
|
229
|
+
},
|
|
230
|
+
"reducer": "message"
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
"metadata": {
|
|
234
|
+
"tags": [
|
|
235
|
+
"conformance",
|
|
236
|
+
"multi-agent",
|
|
237
|
+
"conversation",
|
|
238
|
+
"rfc-0239"
|
|
239
|
+
]
|
|
240
|
+
},
|
|
241
|
+
"settings": {
|
|
242
|
+
"timeout": 20000
|
|
243
|
+
}
|
|
244
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "conformance-multi-party-council",
|
|
3
|
+
"name": "Conformance: Multi-Party Council",
|
|
4
|
+
"version": "1.0",
|
|
5
|
+
"description": "RFC 0239 §E.1. One core.conversationGate with a three-member roster (host:conformance-council-a/-b/-c) and mockAutoResume false: the run waits on a conversation.exchange interrupt the suite resumes. conversation.opened MUST carry the roster; a turn from a non-member is refused 422 conversation_speaker_not_participant and left unconsumed. See v2-multi-party-council.test.ts.",
|
|
6
|
+
"nodes": [
|
|
7
|
+
{
|
|
8
|
+
"id": "convo",
|
|
9
|
+
"typeId": "core.conversationGate",
|
|
10
|
+
"name": "Conversation Gate",
|
|
11
|
+
"position": {
|
|
12
|
+
"x": 0,
|
|
13
|
+
"y": 0
|
|
14
|
+
},
|
|
15
|
+
"config": {
|
|
16
|
+
"lifecycle": "open-exchange-close",
|
|
17
|
+
"mockAutoResume": false,
|
|
18
|
+
"participants": [
|
|
19
|
+
{
|
|
20
|
+
"agentId": "host:conformance-council-a"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"agentId": "host:conformance-council-b"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"agentId": "host:conformance-council-c"
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"inputs": {}
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"edges": [],
|
|
34
|
+
"triggers": [
|
|
35
|
+
{
|
|
36
|
+
"id": "manual",
|
|
37
|
+
"type": "manual",
|
|
38
|
+
"enabled": true
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"variables": [],
|
|
42
|
+
"channels": {
|
|
43
|
+
"conversation": {
|
|
44
|
+
"schema": {
|
|
45
|
+
"type": "array"
|
|
46
|
+
},
|
|
47
|
+
"reducer": "message"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"metadata": {
|
|
51
|
+
"tags": [
|
|
52
|
+
"conformance",
|
|
53
|
+
"multi-agent",
|
|
54
|
+
"conversation",
|
|
55
|
+
"rfc-0239"
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
"settings": {
|
|
59
|
+
"timeout": 20000
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "conformance-nondeterminism",
|
|
3
|
+
"name": "Conformance: Nondeterminism Sources",
|
|
4
|
+
"version": "1.0",
|
|
5
|
+
"description": "RFC 0237 §D. One core.conformance.nondeterminism node that draws one value per source the host lists in nondeterminismPolicy.sources into the run outputs. A replay fork MUST reproduce them. See conformance/fixtures.md.",
|
|
6
|
+
"nodes": [
|
|
7
|
+
{
|
|
8
|
+
"id": "draw",
|
|
9
|
+
"typeId": "core.conformance.nondeterminism",
|
|
10
|
+
"name": "Draw declared sources",
|
|
11
|
+
"position": { "x": 0, "y": 0 },
|
|
12
|
+
"config": {},
|
|
13
|
+
"inputs": {}
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"edges": [],
|
|
17
|
+
"triggers": [
|
|
18
|
+
{ "id": "manual", "type": "manual", "enabled": true }
|
|
19
|
+
],
|
|
20
|
+
"variables": [],
|
|
21
|
+
"metadata": { "tags": ["conformance"] },
|
|
22
|
+
"settings": { "timeout": 10000 }
|
|
23
|
+
}
|
|
@@ -2,22 +2,40 @@
|
|
|
2
2
|
"id": "conformance-wasm-pack-memory-cap-breach",
|
|
3
3
|
"name": "Conformance: WASM Pack Memory-Cap Breach (Positive Path)",
|
|
4
4
|
"version": "1.0",
|
|
5
|
-
"description": "RFC 0008 §K fixture. Invokes
|
|
5
|
+
"description": "RFC 0008 §K fixture. Invokes `vendor.openwop.misbehaving.memory-bomb`, whose module grows linear memory until the host refuses. A host advertising nodePackRuntimes.wasm.maxMemoryBytes MUST stop it, emit `cap.breached` with `kind: 'wasm-memory'`, and end the run `failed`. The operator installs the pack from `conformance/fixtures/wasm-packs/misbehaving-memory/` (hand-written WebAssembly, no Rust toolchain needed). Fixture-only; not signed for the registry.",
|
|
6
6
|
"nodes": [
|
|
7
7
|
{
|
|
8
8
|
"id": "bomb",
|
|
9
9
|
"typeId": "vendor.openwop.misbehaving.memory-bomb",
|
|
10
10
|
"name": "Memory bomb",
|
|
11
|
-
"position": {
|
|
11
|
+
"position": {
|
|
12
|
+
"x": 0,
|
|
13
|
+
"y": 0
|
|
14
|
+
},
|
|
12
15
|
"config": {},
|
|
13
16
|
"inputs": {}
|
|
14
17
|
}
|
|
15
18
|
],
|
|
16
19
|
"edges": [],
|
|
17
20
|
"triggers": [
|
|
18
|
-
{
|
|
21
|
+
{
|
|
22
|
+
"id": "manual",
|
|
23
|
+
"type": "manual",
|
|
24
|
+
"enabled": true
|
|
25
|
+
}
|
|
19
26
|
],
|
|
20
27
|
"variables": [],
|
|
21
|
-
"metadata": {
|
|
22
|
-
|
|
28
|
+
"metadata": {
|
|
29
|
+
"tags": [
|
|
30
|
+
"conformance",
|
|
31
|
+
"wasm",
|
|
32
|
+
"node-pack",
|
|
33
|
+
"rfc-0008",
|
|
34
|
+
"cap-breach",
|
|
35
|
+
"misbehaving"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"settings": {
|
|
39
|
+
"timeout": 0
|
|
40
|
+
}
|
|
23
41
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "core.openwop.conformance.ui-plugin-narrow",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"kind": "frontend-plugin",
|
|
5
|
+
"engines": {
|
|
6
|
+
"openwop": ">=2.0.0 <3.0.0"
|
|
7
|
+
},
|
|
8
|
+
"uiPlugins": [
|
|
9
|
+
{
|
|
10
|
+
"pluginId": "narrow",
|
|
11
|
+
"surface": "artifact-viewer",
|
|
12
|
+
"entry": "ui/narrow.html",
|
|
13
|
+
"hostApi": ["artifact.read"]
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Builds the two operator-installed WASM fixture packs from their WebAssembly
|
|
4
|
+
* text sources, so no Rust toolchain is needed (RFC 0008 §B exports, hand-written).
|
|
5
|
+
*
|
|
6
|
+
* misbehaving-memory/ `vendor.openwop.misbehaving.memory-bomb` grows linear
|
|
7
|
+
* memory until the host refuses, then traps (RFC 0008 §K;
|
|
8
|
+
* node-pack-runtimes.md §WASM: cap.breached kind wasm-memory).
|
|
9
|
+
* misbehaving-abi/ `vendor.openwop.misbehaving.abi-bomb` reports ABI version
|
|
10
|
+
* 999, which no host lists, so a conforming host refuses it
|
|
11
|
+
* at load (node-pack-runtimes.md §WASM).
|
|
12
|
+
*
|
|
13
|
+
* The built `module.wasm` files are committed. `--check` rebuilds in memory and
|
|
14
|
+
* fails when a committed binary does not match its source.
|
|
15
|
+
*
|
|
16
|
+
* node conformance/fixtures/wasm-packs/build.mjs [--check]
|
|
17
|
+
*
|
|
18
|
+
* Needs the `wabt` package only to build: `npx -y -p wabt@1.0.36 node build.mjs`.
|
|
19
|
+
*/
|
|
20
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
21
|
+
import { dirname, join } from 'node:path';
|
|
22
|
+
import { fileURLToPath } from 'node:url';
|
|
23
|
+
import { createRequire } from 'node:module';
|
|
24
|
+
|
|
25
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
26
|
+
const check = process.argv.includes('--check');
|
|
27
|
+
const require = createRequire(import.meta.url);
|
|
28
|
+
const wabt = await require('wabt')();
|
|
29
|
+
let bad = 0;
|
|
30
|
+
for (const dir of ['misbehaving-memory', 'misbehaving-abi']) {
|
|
31
|
+
const src = readFileSync(join(HERE, dir, 'module.wat'), 'utf8');
|
|
32
|
+
const bin = Buffer.from(wabt.parseWat(`${dir}/module.wat`, src).toBinary({}).buffer);
|
|
33
|
+
const out = join(HERE, dir, 'module.wasm');
|
|
34
|
+
if (check) {
|
|
35
|
+
let committed = Buffer.alloc(0);
|
|
36
|
+
try { committed = readFileSync(out); } catch { /* missing */ }
|
|
37
|
+
if (!committed.equals(bin)) { console.error(`${dir}/module.wasm does not match module.wat — run build.mjs`); bad++; }
|
|
38
|
+
} else {
|
|
39
|
+
writeFileSync(out, bin);
|
|
40
|
+
console.log(`wrote ${dir}/module.wasm (${bin.length} bytes)`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
process.exit(bad ? 1 : 0);
|
|
Binary file
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
;; vendor.openwop.misbehaving-abi — reports ABI version 999, which no host lists.
|
|
2
|
+
;; RFC 0008 §B exports, hand-written so no Rust toolchain is needed. Strings are
|
|
3
|
+
;; returned packed as (len << 32) | ptr. Built by ../build.mjs.
|
|
4
|
+
(module
|
|
5
|
+
(memory (export "memory") 16)
|
|
6
|
+
(global $heap (mut i32) (i32.const 1120))
|
|
7
|
+
(data (i32.const 1024) "vendor.openwop.misbehaving-abi")
|
|
8
|
+
(data (i32.const 1062) "vendor.openwop.misbehaving.abi-bomb")
|
|
9
|
+
(func (export "openwop_abi_version") (result i32) (i32.const 999))
|
|
10
|
+
(func (export "openwop_pack_name") (result i64) (i64.or (i64.shl (i64.const 30) (i64.const 32)) (i64.const 1024)))
|
|
11
|
+
(func (export "openwop_node_count") (result i32) (i32.const 1))
|
|
12
|
+
(func (export "openwop_node_id_at") (param $i i32) (result i64)
|
|
13
|
+
(if (result i64) (i32.eqz (local.get $i)) (then (i64.or (i64.shl (i64.const 35) (i64.const 32)) (i64.const 1062))) (else (i64.const 0))))
|
|
14
|
+
(func (export "openwop_alloc") (param $size i32) (result i32)
|
|
15
|
+
(local $p i32)
|
|
16
|
+
(local.set $p (global.get $heap))
|
|
17
|
+
(global.set $heap (i32.add (global.get $heap) (i32.and (i32.add (local.get $size) (i32.const 15)) (i32.const -16))))
|
|
18
|
+
(local.get $p))
|
|
19
|
+
(func (export "openwop_free") (param $ptr i32) (param $size i32))
|
|
20
|
+
(func (export "openwop_node_invoke") (param $node i32) (param $ptr i32) (param $len i32) (result i64)
|
|
21
|
+
;; Never reached: a conforming host refuses the pack at load because ABI
|
|
22
|
+
;; version 999 is not in its advertised nodePackRuntimes.wasm.abiVersions.
|
|
23
|
+
unreachable)
|
|
24
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "node",
|
|
3
|
+
"name": "vendor.openwop.misbehaving-abi",
|
|
4
|
+
"version": "2.0.0",
|
|
5
|
+
"description": "Conformance fixture, operator-installed only: reports openwop_abi_version 999, which no host lists, so a conforming host refuses it at load (node-pack-runtimes.md \u00a7WASM). Not for registry publication.",
|
|
6
|
+
"author": "openwop project",
|
|
7
|
+
"license": "Apache-2.0",
|
|
8
|
+
"engines": {
|
|
9
|
+
"openwop": ">=2.0.0 <3.0.0"
|
|
10
|
+
},
|
|
11
|
+
"nodes": [
|
|
12
|
+
{
|
|
13
|
+
"typeId": "vendor.openwop.misbehaving.abi-bomb",
|
|
14
|
+
"version": "1.0.0",
|
|
15
|
+
"category": "data",
|
|
16
|
+
"role": "transform",
|
|
17
|
+
"label": "ABI bomb",
|
|
18
|
+
"description": "Conformance fixture, operator-installed only: reports openwop_abi_version 999, which no host lists, so a conforming host refuses it at load (node-pack-runtimes.md \u00a7WASM). Not for registry publication."
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"runtime": {
|
|
22
|
+
"language": "wasm",
|
|
23
|
+
"entry": "module.wasm",
|
|
24
|
+
"format": "wasm",
|
|
25
|
+
"minRuntimeVersion": "1"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
;; vendor.openwop.misbehaving — memory bomb (RFC 0008 §K).
|
|
2
|
+
;; RFC 0008 §B exports, hand-written so no Rust toolchain is needed. Strings are
|
|
3
|
+
;; returned packed as (len << 32) | ptr. Built by ../build.mjs.
|
|
4
|
+
(module
|
|
5
|
+
(memory (export "memory") 16)
|
|
6
|
+
(global $heap (mut i32) (i32.const 1104))
|
|
7
|
+
(data (i32.const 1024) "vendor.openwop.misbehaving")
|
|
8
|
+
(data (i32.const 1058) "vendor.openwop.misbehaving.memory-bomb")
|
|
9
|
+
(func (export "openwop_abi_version") (result i32) (i32.const 1))
|
|
10
|
+
(func (export "openwop_pack_name") (result i64) (i64.or (i64.shl (i64.const 26) (i64.const 32)) (i64.const 1024)))
|
|
11
|
+
(func (export "openwop_node_count") (result i32) (i32.const 1))
|
|
12
|
+
(func (export "openwop_node_id_at") (param $i i32) (result i64)
|
|
13
|
+
(if (result i64) (i32.eqz (local.get $i)) (then (i64.or (i64.shl (i64.const 38) (i64.const 32)) (i64.const 1058))) (else (i64.const 0))))
|
|
14
|
+
(func (export "openwop_alloc") (param $size i32) (result i32)
|
|
15
|
+
(local $p i32)
|
|
16
|
+
(local.set $p (global.get $heap))
|
|
17
|
+
(global.set $heap (i32.add (global.get $heap) (i32.and (i32.add (local.get $size) (i32.const 15)) (i32.const -16))))
|
|
18
|
+
(local.get $p))
|
|
19
|
+
(func (export "openwop_free") (param $ptr i32) (param $size i32))
|
|
20
|
+
(func (export "openwop_node_invoke") (param $node i32) (param $ptr i32) (param $len i32) (result i64)
|
|
21
|
+
;; Grow 1024 pages (64 MiB) at a time until the host refuses (memory.grow
|
|
22
|
+
;; returns -1), then trap. A host enforcing maxMemoryBytes refuses at its
|
|
23
|
+
;; ceiling; one that does not is stopped only by the wasm32 4 GiB limit,
|
|
24
|
+
;; and emits no cap.breached, which the leg catches.
|
|
25
|
+
(block $refused
|
|
26
|
+
(loop $grow
|
|
27
|
+
(br_if $refused (i32.eq (memory.grow (i32.const 1024)) (i32.const -1)))
|
|
28
|
+
(br $grow)))
|
|
29
|
+
unreachable)
|
|
30
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "node",
|
|
3
|
+
"name": "vendor.openwop.misbehaving",
|
|
4
|
+
"version": "2.0.0",
|
|
5
|
+
"description": "Conformance fixture, operator-installed only: grows linear memory until the host refuses, so a host enforcing nodePackRuntimes.wasm.maxMemoryBytes emits cap.breached kind wasm-memory (RFC 0008 \u00a7K). Not for registry publication.",
|
|
6
|
+
"author": "openwop project",
|
|
7
|
+
"license": "Apache-2.0",
|
|
8
|
+
"engines": {
|
|
9
|
+
"openwop": ">=2.0.0 <3.0.0"
|
|
10
|
+
},
|
|
11
|
+
"nodes": [
|
|
12
|
+
{
|
|
13
|
+
"typeId": "vendor.openwop.misbehaving.memory-bomb",
|
|
14
|
+
"version": "1.0.0",
|
|
15
|
+
"category": "data",
|
|
16
|
+
"role": "transform",
|
|
17
|
+
"label": "Memory bomb",
|
|
18
|
+
"description": "Conformance fixture, operator-installed only: grows linear memory until the host refuses, so a host enforcing nodePackRuntimes.wasm.maxMemoryBytes emits cap.breached kind wasm-memory (RFC 0008 \u00a7K). Not for registry publication."
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"runtime": {
|
|
22
|
+
"language": "wasm",
|
|
23
|
+
"entry": "module.wasm",
|
|
24
|
+
"format": "wasm",
|
|
25
|
+
"minRuntimeVersion": "1"
|
|
26
|
+
}
|
|
27
|
+
}
|