mirror of
https://github.com/ruvnet/RuView.git
synced 2026-10-22 06:32:35 -05:00
Adds a portable RuView compute kernel for JavaScript, built from the Rust
source of truth and wired into the @ruvnet/ruview metaharness.
Rust
- v2/crates/ruview-kernel: bounded single-function JSON ABI
(call(op, json) -> envelope) over the ADR-021 wifi-densepose-vitals
pipeline: streaming sessions, batch analyze, deterministic SplitMix64
synthetic CSI (SYNTHETIC), bounded session registry, strict input
validation (deny_unknown_fields, width/finite/magnitude checks, size and
count limits). wasm32-only zero-import C ABI (rvk_alloc/free/call).
- v2/crates/ruview-kernel-napi: napi-rs transport exposing only
abiVersion()/call(); standalone workspace (excluded from v2) so napi never
enters `cargo test --workspace`; panic=unwind + catch_unwind.
npm (harness/ruview-kernel, zero runtime deps)
- loadKernel({backend: wasm|napi|auto}); WASM default (no host authority),
napi never silently falls back, auto reports fallbackReason.
- SHA256SUMS integrity verification; mismatch is always fatal; loader
rejects any WASM module that imports host functions.
- Browser-safe ./wasm entry; CLI doctor/info/selftest/parity/synth/
analyze/bench; build.mjs builds artifacts from source (no shell).
- Unit, security, and e2e tests (CLI round trip, cross-backend parity).
Metaharness
- New read-only tool ruview_kernel_selftest / `ruview kernel`: imports the
fixed optional package, fails closed with kernel_not_installed; policy,
claims, MCP policy, guidance capability, and manifest updated.
CI: .github/workflows/ruview-kernel-npm.yml builds both transports on
Linux/macOS/Windows, requires napi in tests, runs parity, verifies
artifacts, packs, and runs the cross-package e2e. No publish step.
Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_015H55jmprfz4oHWXSFbX5rf
103 lines
4.4 KiB
JavaScript
103 lines
4.4 KiB
JavaScript
// End-to-end: Rust core → WASM/napi transports → JS API → CLI (ADR-368).
|
|
// Evidence for every numeric assertion here: SYNTHETIC.
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { spawnSync } from 'node:child_process';
|
|
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { dirname, join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { kernelStatus, loadKernel, parity, selfTest } from '../src/index.js';
|
|
import { napiSkip, requireWasm } from './helpers.mjs';
|
|
|
|
const CLI = join(dirname(dirname(fileURLToPath(import.meta.url))), 'bin', 'cli.js');
|
|
const cli = (...args) => {
|
|
const r = spawnSync(process.execPath, [CLI, ...args], { encoding: 'utf8', env: { ...process.env, RUVIEW_KERNEL_BACKEND: '' } });
|
|
return { status: r.status, stdout: r.stdout, stderr: r.stderr, json: () => JSON.parse(r.stdout) };
|
|
};
|
|
|
|
test('wasm backend passes the SYNTHETIC self-test', () => {
|
|
requireWasm();
|
|
const k = loadKernel({ backend: 'wasm' });
|
|
assert.equal(k.backend, 'wasm');
|
|
assert.equal(k.integrity, 'verified');
|
|
const result = selfTest(k);
|
|
assert.ok(result.ok, JSON.stringify(result.checks.filter((c) => !c.pass)));
|
|
assert.equal(result.evidence, 'SYNTHETIC');
|
|
assert.equal(result.kernel.target, 'wasm32');
|
|
});
|
|
|
|
test('napi backend passes the SYNTHETIC self-test', { skip: napiSkip }, () => {
|
|
const k = loadKernel({ backend: 'napi' });
|
|
assert.equal(k.backend, 'napi');
|
|
const result = selfTest(k);
|
|
assert.ok(result.ok, JSON.stringify(result.checks.filter((c) => !c.pass)));
|
|
assert.equal(result.kernel.target, 'native');
|
|
});
|
|
|
|
test('wasm and napi produce the same outputs for the same input', { skip: napiSkip }, () => {
|
|
const result = parity(loadKernel({ backend: 'wasm' }), loadKernel({ backend: 'napi' }));
|
|
assert.ok(result.ok, result.differences.join('\n'));
|
|
assert.equal(result.readings, 45);
|
|
});
|
|
|
|
test('auto prefers napi and reports an honest fallback to wasm', () => {
|
|
requireWasm();
|
|
const k = loadKernel({ backend: 'auto', nativePath: '/nonexistent/ruview_kernel.node' });
|
|
assert.equal(k.backend, 'wasm');
|
|
assert.equal(k.requestedBackend, 'auto');
|
|
assert.match(k.fallbackReason, /no napi-rs addon|not found|nonexistent/);
|
|
});
|
|
|
|
test('kernelStatus reports both backends without throwing', () => {
|
|
const status = kernelStatus();
|
|
assert.equal(status.abi, 1);
|
|
assert.equal(typeof status.wasm.available, 'boolean');
|
|
assert.equal(typeof status.napi.available, 'boolean');
|
|
});
|
|
|
|
test('CLI: doctor, selftest, synth → analyze round trip', () => {
|
|
requireWasm();
|
|
const doctor = cli('doctor');
|
|
assert.equal(doctor.status, 0, doctor.stderr);
|
|
assert.equal(doctor.json().wasm.available, true);
|
|
|
|
const st = cli('selftest', '--seconds', '45');
|
|
assert.equal(st.status, 0, st.stdout);
|
|
assert.equal(st.json().backend, 'wasm');
|
|
|
|
const dir = mkdtempSync(join(tmpdir(), 'ruview-kernel-e2e-'));
|
|
try {
|
|
const file = join(dir, 'frames.json');
|
|
const synth = cli('synth', '--seconds', '40', '--breathing', '12', '--subcarriers', '24', '--out', file);
|
|
assert.equal(synth.status, 0, synth.stderr);
|
|
assert.equal(cli('synth', '--seconds', '1', '--out', file).status, 1, 'synth must refuse to overwrite');
|
|
|
|
const analyzed = cli('analyze', '--file', file, '--readings');
|
|
assert.equal(analyzed.status, 0, analyzed.stderr);
|
|
const report = analyzed.json();
|
|
assert.equal(report.summary.frames, 800);
|
|
assert.equal(report.readings.length, 40);
|
|
assert.ok(Math.abs(report.summary.last.respiratory.bpm - 12) <= 3, `SYNTHETIC RR ${report.summary.last.respiratory.bpm}`);
|
|
|
|
// JSONL input takes the same path.
|
|
const frames = JSON.parse(cli('synth', '--seconds', '2', '--subcarriers', '4').stdout).frames;
|
|
const jsonl = join(dir, 'frames.jsonl');
|
|
writeFileSync(jsonl, frames.map((f) => JSON.stringify(f)).join('\n'));
|
|
const small = cli('analyze', '--file', jsonl, '--config', '{"n_subcarriers":4}');
|
|
assert.equal(small.status, 0, small.stdout);
|
|
assert.equal(small.json().summary.frames, 40);
|
|
|
|
const bad = cli('analyze', '--file', jsonl);
|
|
assert.equal(bad.status, 1, 'width mismatch against default 56 subcarriers must fail');
|
|
assert.equal(bad.json().code, 'invalid_request');
|
|
} finally {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('CLI rejects unknown backends and commands', () => {
|
|
assert.equal(cli('info', '--backend', 'gpu').status, 2);
|
|
assert.equal(cli('frobnicate').status, 2);
|
|
});
|