Accepted
2026-03-06
ruv
- ADR-085 Neural Trader architecture (core, coherence, replay crates)
- ADR-084 ruvllm-wasm publish & Rust 1.91 WASM codegen workaround
- ADR-040 WASM programmable sensing
- ADR-041 curated module registry
The three Neural Trader Rust crates (neural-trader-core, neural-trader-coherence, neural-trader-replay) provide a coherence-gated market graph system with 12 passing tests. However, they have no browser/WASM bindings — meaning dashboards, backtesting UIs, and browser-based research tools cannot access the coherence gate or replay memory directly.
The repository has an established WASM crate pattern used by 15+ crates (ruvector-wasm, ruvector-gnn-wasm, ruvector-attention-wasm, ruvllm-wasm, etc.) all using wasm-bindgen + serde-wasm-bindgen + wasm-pack. We follow that pattern here.
Rust 1.91 has a known WASM codegen bug in release mode. The workaround (same as ruvllm-wasm, documented in ADR-084) is:
CARGO_PROFILE_RELEASE_CODEGEN_UNITS=256 CARGO_PROFILE_RELEASE_LTO=off \
wasm-pack build --target web --scope ruvector --releaseAdditionally, wasm-opt is disabled via [package.metadata.wasm-pack.profile.release] wasm-opt = false.
Create crates/neural-trader-wasm/ with a single src/lib.rs that wraps all three crates using the XxxWasm { inner: Xxx } pattern.
| Rust Type | WASM Approach |
|---|---|
C-style enums (EventType, Side, RegimeLabel, etc.) |
Mirror as #[wasm_bindgen] enums with From conversions |
[u8; 16] fields (event IDs, hashes) |
Hex strings in JS (32-char), decoded at boundary |
Simple structs (GateConfig) |
Direct field getters/setters via #[wasm_bindgen(getter/setter)] |
Complex nested structs (MarketEvent, ReplaySegment) |
toJson()/fromJson() via serde-wasm-bindgen |
Vec<T> and VecDeque<T> |
Serialized to JsValue via serde-wasm-bindgen |
anyhow::Result |
Converted to Result<T, JsValue> at WASM boundary |
Trait objects (CoherenceGate) |
Concrete ThresholdGateWasm wrapper (no dyn dispatch in WASM) |
| Source Crate | WASM Type | Key Methods |
|---|---|---|
| core | MarketEventWasm |
new(), field getters/setters, toJson(), fromJson() |
| core | GraphDeltaWasm |
new(), nodesAdded(), edgesAdded(), propertiesUpdated() |
| core | EventTypeWasm, SideWasm, NodeKindWasm, EdgeKindWasm |
C-style enums |
| coherence | GateConfigWasm |
new() with defaults, all threshold getters/setters |
| coherence | ThresholdGateWasm |
new(config), evaluate(ctx) |
| coherence | GateContextWasm |
new(...), field getters |
| coherence | CoherenceDecisionWasm |
allowRetrieve, allowWrite, allowLearn, allowAct, reasons(), toJson() |
| coherence | RegimeLabelWasm |
C-style enum |
| replay | ReservoirStoreWasm |
new(maxSize), len(), isEmpty(), maybeWrite(segJson, decisionJson) |
| replay | ReplaySegmentWasm |
toJson(), fromJson(), field getters |
| replay | SegmentKindWasm |
C-style enum |
| — | version() |
Crate version string |
| — | healthCheck() |
Returns true |
- Built with
wasm-pack build --target web --scope ruvector --release - Published to npm as
@ruvector/neural-trader-wasm publish = falsein Cargo.toml (Rust crate not on crates.io)
- Browser dashboards can evaluate coherence gates without a backend roundtrip
- Research notebooks (Observable, Jupyter) can use the replay memory directly
- TypeScript types auto-generated by wasm-pack from
#[wasm_bindgen]annotations - Follows the same pattern as 15+ existing WASM crates in the workspace
- Trait objects (
CoherenceGate,MemoryStore) cannot be passed across WASM boundary; only concrete implementations are exposed [u8; 16]hex encoding adds a small overhead at the boundary (negligible vs. WASM call overhead)ReservoirStore.maybeWrite()takes JSON values rather than typed structs due to WASM ownership constraints
- Rust 1.91 WASM codegen bug requires env-var workaround; future Rust versions should fix this
serde-wasm-bindgen0.6 is a newer dependency not yet in the workspace; pinned to avoid surprises