ADR-0067: The playground's wasm facade, and the JavaScript engine as an optional feature
- Status: Accepted (2026-10-11, by the owner: "Accept ADRs 0067, 0072 and 0073"; proposed 2026-10-10)
- Date: 2026-10-10
- Derives from: plan 0004, Step 2 and its decision rule in § 1.7 ("Every crate that compiles to
wasm32-unknown-unknownwithout a patch; one that does not (the candidates arerquickjsand anynotifyor process code inrb-cli) is feature-gated out ofrb-wasm, and the playground page says which inputs it therefore cannot take"); ADR-0002 (the playground as the consequence of Rust) - Amends: ADR-0010's crate table, by one row. ADR-0010's rules are unchanged.
- Constrains:
crates/rb-wasm,crates/rb-config(js-configfeature), the workspace'srb-configdependency,crates/rb-cli/tests/crate_boundary.rs - Requirements: FR-REACH-01, FR-CFG-03
Context
Plan 0004 adds a browser playground: the library crates compiled to wasm32-unknown-unknown behind a thin facade, rb-wasm, whose exports take and return strings. ADR-0010's table names every crate and the crates it may depend on, and the crate-boundary test holds the workspace to it. rb-wasm is not in the table.
Every library crate builds for the target. One links badly: rquickjs-sys 0.14, the QuickJS engine behind JavaScript configurations and plugin: reporters (ADR-0006). Its wasm bindings declare JS_DupValue and JS_FreeContext as functions, but QuickJS's C source emits neither as a symbol for that target, so the module fails to link. Fixing that means patching the dependency, which the plan's rule says not to do.
Decision
rb-wasm joins ADR-0010's table as a facade. It may depend on rb-model, rb-config, rb-rules, rb-report and rb-ingest, and no crate depends on it. ADR-0073 adds rb-extract-ts and rb-extract-python to the row, for cruise_ts and cruise_py. It never links rb-cli. The logic it needs from rb-cli's cruise --graph path moves into rb-rules, so both call the same code:
reset, which clears an earlier evaluation from a saved graph;report_format, the report's filters and collapse.
QuickJS becomes rb-config's js-config feature, on by default in rb-config and asked for explicitly by rb-cli.
- The workspace's
rb-configdependency turns default features off, so thatrb-rulesdoes not pull the engine into the playground. - Without the feature, every type, constant and policy of the JavaScript module stays. Evaluating a JavaScript configuration is
JsError::Unavailable, exit 3, naming the file and the way out: "write it as YAML or JSON". - JSON5, which the engine reads, is refused in the same words.
rb_config::js::JS_ENGINEsays which build is running.
The playground page states that it takes YAML and JSON configurations only.
Consequences
- The command line is unchanged:
rb-clialways has the engine, and its tests, the sandbox-escape tests among them, run with it. cargo test -p rb-config --no-default-featuresruns in thewasmCI job, so the build without the engine is tested, and its refusals have tests of their own.- Tested on its own,
rb-wasmhas no engine, as in the browser. Tested besiderb-cli, Cargo's feature unification gives it one. Its test of a JavaScript configuration asserts whichever build it is in. - When a later
rquickjsrelease links for the target, turningjs-configon inrb-wasmbrings JavaScript configurations to the playground with no other change.
Alternatives considered
| Option | Why not |
|---|---|
Patch rquickjs-sys with the two missing symbols |
A patched dependency is what the plan's rule rules out, and the sandbox is a security boundary (ADR-0006) that should run exactly the engine the command line runs. |
| Evaluate JavaScript configurations with the browser's own engine | Breaks the rule that a configuration runs only in the sandbox, never a page-level eval (plan 0004 § 1.7, ADR-0006). |
Leave rb-wasm out of the crate table and the test |
The table is how the dependency direction is enforced. A crate outside it is unguarded. |