Spec: add the zero-host-imports gate, found by executing plan 1
Task 1's review found wasm-bindgen in the wasm32 dependency graph via
yrs -> fastrand("js") -> getrandom("wasm_js"). yrs enables that feature
unconditionally, so "no wasm-bindgen in the graph" is untestable while
using yrs -- the constraint as written could never have been satisfied.
The property we actually need is that the module requires no JS host,
and that is directly checkable: zero imports. A dependency that is never
called is dead-stripped and harmless; one that is called shows up as an
import and fails the gate.
The corollary is that Doc::new() must never be called, since it reaches
fastrand for a random client ID. Doc::with_client_id() is what an
assigned server-side client ID wants anyway. fastrand is also reachable
from yrs's observer paths -- which is the argument for a build-time
assertion over a rule, because I cannot enumerate the paths.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011F5CivVm4CmL9ByfSSAaXA
1 file changed,
+15
−0
docs/superpowers/specs/2026-08-30-docs-design.md+15 −0
No CI script — run details
Intent
diff --git a/docs/superpowers/specs/2026-08-30-docs-design.md b/docs/superpowers/specs/2026-08-30-docs-design.md| index 2da9a67..370a3fc 100644 |
| --- a/docs/superpowers/specs/2026-08-30-docs-design.md |
| +++ b/docs/superpowers/specs/2026-08-30-docs-design.md |
| @@ -696,6 +696,21 @@ Corrected. |
| - **Wire compatibility:** our Rust `y-crdt` updates are applied by upstream JS |
| `yjs` and vice versa, against checked-in fixtures generated by the JS side. |
| This is the CRDT half of §0 and the fixtures are the contract. |
| +- **Zero host imports:** the compiled wasm module must import nothing. |
| + *(Added 2026-08-30, from executing plan 1 — the review found `wasm-bindgen` |
| + in the wasm32 dependency graph via `yrs → fastrand("js") → |
| + getrandom("wasm_js")`.)* "No wasm-bindgen in the dependency graph" turned |
| + out to be **untestable while using `yrs`**, which enables that feature |
| + unconditionally. The property actually required is that the module needs no |
| + JS host, and that is directly checkable: zero imports. A dependency that is |
| + never called is dead-stripped and harmless; one that is called appears as an |
| + import and fails the gate. |
| + The corollary, which the gate enforces rather than trusts: **never call |
| + `yrs::Doc::new()`** — it reaches `fastrand` for a random client ID, which is |
| + what would pull JS entropy. Always `Doc::with_client_id()`, which is what an |
| + assigned server-side client ID wants anyway. `fastrand` is also reached from |
| + `yrs`'s observer paths, which is precisely why this is a build-time |
| + assertion rather than a rule somebody remembers. |
| - **Layout agreement:** a browser drive renders fixture documents and asserts |
| the DOM's actual page-break positions match the engine's computed ones. This |
| is the gate revision 1 lacked — its conformance test compared wazero to a |