Merge pull request #2189 from ruvnet/prototype/uncertainty-virtual-view

Add opt-in Dream Machine virtual-view research adapter
This commit is contained in:
rUv authored and GitHub committed 2026-10-12 08:38:50 -07:00
commit 3c8f7ed96e
4 files changed
+281

No files matched your search

+3
View File
@@ -206,6 +206,9 @@ jobs:
- name: Run UI unit tests
run: node --test ui/sw.test.mjs ui/services/ws-ticket.test.mjs ui/services/websocket.service.test.mjs ui/services/sensing.service.test.mjs ui/components/DashboardTab.test.mjs v2/crates/wifi-densepose-desktop/ui/build-config.test.mjs
- name: Run virtual-view adapter boundary tests
run: node --test integrations/virtual-view/adapter.test.mjs
# Unit and Integration Tests
# Python pytest matrix — runs against the archived v1 Python tree.
# `continue-on-error: true` for the same reason as code-quality above:
+50
View File
@@ -0,0 +1,50 @@
# Virtual-view broker adapter (research prototype)
Opt-in integration with Dream Machine's `scripts/virtual-view-broker.mjs`.
Disabled by default. No cameras, model calls, network, hardware commands or
automatic promotion. It is NOT part of the Rust production perception pipeline.
The reviewed companion is Dream Machine commit
[`5707c825b2c065de44abed02594e1abf5eab95ea`](https://github.com/ruvnet/dream-machine/tree/5707c825b2c065de44abed02594e1abf5eab95ea),
merged in [Dream Machine PR #162](https://github.com/ruvnet/dream-machine/pull/162).
Use a checkout at that revision when reproducing the integration.
```js
import { generateKeyPairSync } from 'node:crypto';
import { broker } from '../../../dream-machine/scripts/virtual-view-broker.mjs';
import { createVirtualViewAdapter } from './adapter.mjs';
// Example in-memory research identity only; use a trusted host signer in deployment.
const { privateKey } = generateKeyPairSync('ed25519');
const adapter = createVirtualViewAdapter({ broker, privateKey, enabled: true });
// adapter.observe(calibratedSnapshot) returns RENDER, SKIP or BLOCK plus authority:none.
```
The example assumes sibling RuView and Dream Machine clones; adjust the import
from the executing module's location. Pass calibrated `virtual-view.v1` scene
snapshots in room-local ENU metres. See Dream Machine ADR-0108 for every field,
threshold, trust boundary, receipt and acceptance rule. The adapter does not
transform raw `spatial.evidence.v1` RF Gaussians or `ruview.lidar.depth.v1` packets:
neither supplies the independently tracked manipulation scene this method needs.
The injected broker is trusted executable code, not sandboxed by this adapter.
The no-network/no-actuation behavior applies to this adapter and the reviewed
companion broker. Before reading scene markers or calling the broker, the adapter
checks the entire snapshot as bounded plain JSON using property descriptors.
It rejects proxies, getters, inherited/custom prototypes, nonfinite values,
sparse arrays and non-JSON data, including nested values. Structural limits match
the reviewed companion: depth 12, 20,000 values, 1,024 array elements, 64 object
properties, 128-character keys and 4,096-character strings. Full geometry,
provenance-label and signing-key validation remain the companion broker's responsibility.
```sh
node --test integrations/virtual-view/adapter.test.mjs
# From the sibling Dream Machine checkout:
node scripts/bench-virtual-view.mjs ../ruview/integrations/virtual-view/adapter.mjs
```
The cross-repository benchmark exercises this adapter with the real broker,
renders 256-object synthetic scenes, signs and replays receipts. It measures local
CPU overhead only. Required robot evidence remains missing: +20 points on two of
three held-out tasks, no regression beyond five points, added p95 below 150 ms,
at least 95% pre-action drift detection, and replayable receipts for all trials.
No receipt grants actuator authority. Do not wire this to live robot control.
+58
View File
@@ -0,0 +1,58 @@
// Opt-in cross-repository prototype. Imports no hardware, network or model client.
import { types } from 'node:util';
const snapshotError = () => new TypeError(
'calibrated metric scene snapshot required; raw RF/LiDAR packets and non-JSON data are not supported',
);
// Structural preflight only, matching the reviewed companion's JSON budgets.
// Never invoke input methods, getters or proxy traps while inspecting data.
function validateSnapshotData(value, depth = 0, budget = { nodes: 0 }) {
if (depth > 12 || ++budget.nodes > 20000) throw snapshotError();
if (value === null || typeof value === 'boolean') return;
if (typeof value === 'string' && value.length <= 4096) return;
if (typeof value === 'number' && Number.isFinite(value)) return;
if (typeof value !== 'object' || types.isProxy(value)) throw snapshotError();
const array = Array.isArray(value);
if (Object.getPrototypeOf(value) !== (array ? Array.prototype : Object.prototype)) {
throw snapshotError();
}
if (array) {
const length = Object.getOwnPropertyDescriptor(value, 'length').value;
if (length > 1024) throw snapshotError();
if (Reflect.ownKeys(value).length !== length + 1) throw snapshotError();
for (let index = 0; index < length; index++) {
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
if (!descriptor?.enumerable || !Object.hasOwn(descriptor, 'value')) throw snapshotError();
validateSnapshotData(descriptor.value, depth + 1, budget);
}
return;
}
const keys = Reflect.ownKeys(value);
if (keys.length > 64) throw snapshotError();
for (const key of keys) {
if (typeof key !== 'string' || key.length > 128) throw snapshotError();
const descriptor = Object.getOwnPropertyDescriptor(value, key);
if (!descriptor.enumerable || !Object.hasOwn(descriptor, 'value')) throw snapshotError();
validateSnapshotData(descriptor.value, depth + 1, budget);
}
}
export function createVirtualViewAdapter({ broker, privateKey, enabled = false }) {
if (typeof broker !== 'function' || typeof enabled !== 'boolean') throw new TypeError('adapter configuration');
return Object.freeze({
observe(snapshot) {
if (!enabled) return { decision: 'BLOCK', reason: 'disabled', authority: 'none', svg: null };
validateSnapshotData(snapshot);
// Explicit metric scene contract only. RF Gaussians are not manipulation geometry.
const schema = snapshot && Object.getOwnPropertyDescriptor(snapshot, 'schema');
const frame = snapshot && Object.getOwnPropertyDescriptor(snapshot, 'frame');
if (schema?.value !== 'virtual-view.v1' || frame?.value !== 'room_enu') {
throw snapshotError();
}
return broker(snapshot, { privateKey });
},
hardwareActuation: false,
});
}
+170
View File
@@ -0,0 +1,170 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { createVirtualViewAdapter } from './adapter.mjs';
test('disabled by default and never actuates', () => {
const adapter = createVirtualViewAdapter({ broker: () => { throw Error('not called'); } });
let reads = 0;
const snapshot = { get schema() { reads++; throw Error('not read'); } };
assert.deepEqual(adapter.observe(snapshot), {
decision: 'BLOCK', reason: 'disabled', authority: 'none', svg: null,
});
assert.equal(reads, 0);
assert.equal(adapter.hardwareActuation, false);
assert.equal(Object.isFrozen(adapter), true);
});
test('raw RF, LiDAR and malformed scene markers never reach the broker', () => {
let calls = 0;
const adapter = createVirtualViewAdapter({ enabled: true, broker: () => { calls++; } });
for (const snapshot of [
null, undefined, 'virtual-view.v1', [], {},
{ schema: 'spatial.evidence.v1', frame: 'room_enu' },
{ schema: 'ruview.lidar.depth.v1', frame: 'room_enu' },
{ schema: 'virtual-view.v1' },
{ schema: 'virtual-view.v1', frame: 'camera' },
Object.create({ schema: 'virtual-view.v1', frame: 'room_enu' }),
Object.defineProperty({ frame: 'room_enu' }, 'schema', { value: 'virtual-view.v1' }),
]) {
assert.throws(() => adapter.observe(snapshot), /calibrated metric scene snapshot required/);
}
assert.equal(calls, 0);
});
test('scene marker getters are rejected without executing input code', () => {
let reads = 0, calls = 0;
const adapter = createVirtualViewAdapter({ enabled: true, broker: () => { calls++; } });
for (const key of ['schema', 'frame']) {
const snapshot = { schema: 'virtual-view.v1', frame: 'room_enu' };
Object.defineProperty(snapshot, key, {
enumerable: true,
get() { reads++; return key === 'schema' ? 'virtual-view.v1' : 'room_enu'; },
});
assert.throws(() => adapter.observe(snapshot), /calibrated metric scene snapshot required/);
}
assert.equal(reads, 0);
assert.equal(calls, 0);
});
test('proxy snapshots are rejected without running inspection traps', () => {
let traps = 0, calls = 0;
const snapshot = new Proxy({ schema: 'virtual-view.v1', frame: 'room_enu' }, {
get() { traps++; throw Error('get trap'); },
getPrototypeOf() { traps++; throw Error('prototype trap'); },
getOwnPropertyDescriptor() { traps++; throw Error('descriptor trap'); },
});
const adapter = createVirtualViewAdapter({ enabled: true, broker: () => { calls++; } });
assert.throws(() => adapter.observe(snapshot), /calibrated metric scene snapshot required/);
assert.equal(traps, 0);
assert.equal(calls, 0);
});
test('nested object and array getters are rejected without reads or broker calls', () => {
let reads = 0, calls = 0;
const adapter = createVirtualViewAdapter({ enabled: true, broker: () => { calls++; } });
const getter = { enumerable: true, get() { reads++; return 0; } };
const object = Object.defineProperty({}, 'uncertaintyM', getter);
const array = Object.defineProperty([0], '0', getter);
for (const extra of [object, [object], { nested: array }]) {
assert.throws(() => adapter.observe({ schema: 'virtual-view.v1', frame: 'room_enu', extra }),
/calibrated metric scene snapshot required/);
}
assert.equal(reads, 0);
assert.equal(calls, 0);
});
test('nested proxies and custom array prototypes never execute input code', () => {
let traps = 0, calls = 0;
const adapter = createVirtualViewAdapter({ enabled: true, broker: () => { calls++; } });
const handler = {
get() { traps++; throw Error('get trap'); },
getPrototypeOf() { traps++; throw Error('prototype trap'); },
ownKeys() { traps++; throw Error('keys trap'); },
getOwnPropertyDescriptor() { traps++; throw Error('descriptor trap'); },
};
const custom = Object.setPrototypeOf([], {
get [Symbol.iterator]() { traps++; throw Error('iterator getter'); },
});
class SceneArray extends Array {}
for (const extra of [
{ nested: new Proxy({}, handler) },
[new Proxy([], handler)],
{ nested: custom },
new SceneArray(),
]) {
assert.throws(() => adapter.observe({ schema: 'virtual-view.v1', frame: 'room_enu', extra }),
/calibrated metric scene snapshot required/);
}
assert.equal(traps, 0);
assert.equal(calls, 0);
});
test('cyclic, non-JSON and oversized structures are rejected before the broker', () => {
let calls = 0;
const adapter = createVirtualViewAdapter({ enabled: true, broker: () => { calls++; } });
const cyclic = {}; cyclic.self = cyclic;
let deep = 0;
for (let index = 0; index < 13; index++) deep = { next: deep };
const sparse = Array(2); sparse[1] = 0;
const extraProperty = [0]; extraProperty.extra = 0;
for (const extra of [
cyclic, deep, sparse, extraProperty, NaN, Infinity, undefined, () => {}, new Date(),
'x'.repeat(4097), Array(1025).fill(0),
{ ['x'.repeat(129)]: 0 },
Object.fromEntries(Array.from({ length: 65 }, (_, index) => [`k${index}`, 0])),
Array.from({ length: 20 }, () => Array(1024).fill(0)),
]) {
assert.throws(() => adapter.observe({ schema: 'virtual-view.v1', frame: 'room_enu', extra }),
/calibrated metric scene snapshot required/);
}
assert.equal(calls, 0);
});
test('a complete 256-object synthetic scene stays within the structural budget', () => {
const scene = {
schema: 'virtual-view.v1', sceneId: 'fixture', requestId: 'request:1', policyId: 'fixed',
revision: 1, frame: 'room_enu', proof: 'SYNTHETIC', nowMs: 1000, observedMs: 990,
contact: 'static', target: 'o0', reference: 'o1', physicalAxis: 'x',
objects: Array.from({ length: 256 }, (_, index) => ({
id: `o${index}`, observed: [index / 100, index % 3, 0.1],
predicted: [index / 100, index % 3, 0.1], size: [0.01, 0.01, 0.01],
uncertaintyM: 0.015, sourceReceipt: `fixture:${index}`,
})),
};
let calls = 0;
const result = { authority: 'none' };
const adapter = createVirtualViewAdapter({ enabled: true, broker: received => {
calls++;
assert.equal(received, scene);
return result;
} });
assert.equal(adapter.observe(scene), result);
assert.equal(calls, 1);
});
test('explicit immutable scene and signer are forwarded unchanged to trusted broker', () => {
const snapshot = Object.freeze({ schema: 'virtual-view.v1', frame: 'room_enu' });
const privateKey = Object.freeze({ researchSigner: true });
const result = Object.freeze({ decision: 'SKIP', authority: 'none', svg: null });
let calls = 0;
const adapter = createVirtualViewAdapter({ enabled: true, privateKey, broker: (received, options) => {
calls++;
assert.equal(received, snapshot);
assert.equal(options.privateKey, privateKey);
return result;
} });
assert.equal(adapter.observe(snapshot), result);
assert.equal(calls, 1);
});
test('full scene validation remains delegated and broker rejection propagates', () => {
const rejection = new TypeError('missing tracked scene geometry');
const adapter = createVirtualViewAdapter({ enabled: true, broker: () => { throw rejection; } });
assert.throws(() => adapter.observe({ schema: 'virtual-view.v1', frame: 'room_enu' }),
error => error === rejection);
});
test('enablement must be explicit and configuration types are checked', () => {
for (const configuration of [
{ broker: null }, { broker: () => {}, enabled: 'true' }, { broker: () => {}, enabled: 1 },
]) assert.throws(() => createVirtualViewAdapter(configuration), /adapter configuration/);
});