mirror of
https://github.com/ruvnet/RuView
synced 2026-07-29 18:31:44 +00:00
a34bfc246e
Add a read-only guidance CLI/MCP surface with reviewed capability maturity, repository citations, validation commands, limitations, and shared-brain evidence. Bump @ruvnet/ruview to 0.3.1 and add release-tarball smoke coverage.
424 lines
19 KiB
JavaScript
424 lines
19 KiB
JavaScript
// SPDX-License-Identifier: MIT
|
|
// Source-cited repository and capability guidance for humans and agents.
|
|
//
|
|
// The catalog is intentionally small, reviewed, and dependency-free. It is a
|
|
// navigation aid, not a substitute for reading the cited source and tests.
|
|
|
|
import { existsSync } from 'node:fs';
|
|
import { join, resolve } from 'node:path';
|
|
import { searchBrain } from './brain.js';
|
|
|
|
/** Supported topic filters for the RuView guidance API. */
|
|
export const GUIDANCE_TOPICS = Object.freeze([
|
|
'overview',
|
|
'architecture',
|
|
'sensing',
|
|
'hardware',
|
|
'training',
|
|
'homecore',
|
|
'integrations',
|
|
'deployment',
|
|
'community',
|
|
'testing',
|
|
]);
|
|
|
|
const TOPIC_SUMMARIES = Object.freeze({
|
|
overview: 'A source-cited map of RuView subsystems and their current maturity.',
|
|
architecture: 'Repository layout, production boundaries, and primary entry points.',
|
|
sensing: 'CSI ingestion, signal processing, inference, and unified RF capabilities.',
|
|
hardware: 'ESP32-S3/C6 firmware, capture, provisioning, and hardware evidence.',
|
|
training: 'Calibration, training, evaluation, and data-dependent capability limits.',
|
|
homecore: 'HOMECORE runtime, restore, plugins, API compatibility, migration, HAP, and voice.',
|
|
integrations: 'Home Assistant, MQTT, Matter, Apple Home HAP, and related boundaries.',
|
|
deployment: 'Runnable servers, transports, feature flags, and operational entry points.',
|
|
community: 'Contributor harness, reviewed shared brain, local agents, and learning flywheel.',
|
|
testing: 'Deterministic proofs, package gates, Rust CI, and hardware witness requirements.',
|
|
});
|
|
|
|
const CAPABILITIES = Object.freeze([
|
|
{
|
|
id: 'repository-map',
|
|
name: 'Repository architecture',
|
|
topics: ['architecture'],
|
|
status: 'implemented',
|
|
evidence: 'REPOSITORY',
|
|
summary: 'Production Rust is in v2, the maintained deterministic Python reference is under archive/v1, ESP32 firmware is under firmware, and contributor automation is under harness/ruview.',
|
|
sources: [
|
|
'v2/Cargo.toml',
|
|
'README.md',
|
|
'AGENTS.md',
|
|
],
|
|
validation: ['cargo metadata --manifest-path v2/Cargo.toml --no-deps'],
|
|
limitations: ['Archive code is reference/proof material; new production features belong in v2.'],
|
|
},
|
|
{
|
|
id: 'wifi-csi-sensing',
|
|
name: 'WiFi CSI sensing pipeline',
|
|
topics: ['sensing', 'deployment'],
|
|
status: 'implemented',
|
|
evidence: 'REPOSITORY',
|
|
summary: 'The sensing server ingests ESP32 CSI over UDP, applies signal processing and inference modules, and publishes bounded real-time updates to clients.',
|
|
sources: [
|
|
'v2/crates/wifi-densepose-sensing-server/README.md',
|
|
'v2/crates/wifi-densepose-signal/README.md',
|
|
'v2/crates/wifi-densepose-core/README.md',
|
|
],
|
|
validation: [
|
|
'cargo test -p wifi-densepose-core -p wifi-densepose-signal --no-default-features',
|
|
'cargo test -p wifi-densepose-sensing-server --no-default-features',
|
|
],
|
|
limitations: ['Live sensing quality depends on RF geometry, calibration, hardware, and measured data; implementation is not an accuracy claim.'],
|
|
},
|
|
{
|
|
id: 'esp32-firmware',
|
|
name: 'ESP32 CSI node firmware',
|
|
topics: ['hardware', 'sensing', 'deployment'],
|
|
status: 'hardware-dependent',
|
|
evidence: 'REPOSITORY',
|
|
summary: 'ESP32-S3 is the production CSI capture target and ESP32-C6 is a research target; firmware covers CSI streaming, provisioning, edge processing, and optional sensing modules.',
|
|
sources: [
|
|
'firmware/esp32-csi-node/README.md',
|
|
'docs/adr/ADR-028-esp32-capability-audit.md',
|
|
'.github/workflows/firmware-ci.yml',
|
|
],
|
|
validation: ['Follow firmware/esp32-csi-node/README.md for the exact target, then capture a real boot/runtime log.'],
|
|
limitations: ['A successful build or simulator is not hardware validation.', 'Ports, credentials, board target, and flash layout require operator confirmation.'],
|
|
},
|
|
{
|
|
id: 'calibration-training',
|
|
name: 'Calibration and model training',
|
|
topics: ['training', 'sensing', 'testing'],
|
|
status: 'data-gated',
|
|
evidence: 'REPOSITORY',
|
|
summary: 'Rust crates provide per-room calibration, dataset handling, training, inference, and deterministic evaluation surfaces.',
|
|
sources: [
|
|
'v2/crates/wifi-densepose-calibration/src/lib.rs',
|
|
'v2/crates/wifi-densepose-train/README.md',
|
|
'aether-arena/VERIFY.md',
|
|
],
|
|
validation: [
|
|
'cargo test -p wifi-densepose-calibration -p wifi-densepose-train --no-default-features',
|
|
'cargo run -q -p wifi-densepose-train --bin aa_score_runner --no-default-features',
|
|
],
|
|
limitations: ['Model quality remains data- and split-dependent.', 'Accuracy must be evidence-labelled and pose PCK must include the mean-pose baseline on a leakage-free held-out split.'],
|
|
},
|
|
{
|
|
id: 'homecore-runtime-restore',
|
|
name: 'HOMECORE runtime and startup restore',
|
|
topics: ['homecore', 'architecture', 'deployment'],
|
|
status: 'implemented',
|
|
evidence: 'REPOSITORY',
|
|
summary: 'HOMECORE provides concurrent entity/device state and service/event registries; server startup restores registries before the latest recorder states while isolating malformed rows.',
|
|
sources: [
|
|
'v2/docs/homecore-capabilities.md',
|
|
'v2/crates/homecore-server/src/restore.rs',
|
|
'v2/crates/homecore-recorder/src/db.rs',
|
|
],
|
|
validation: ['cargo test -p homecore -p homecore-recorder -p homecore-server --no-default-features'],
|
|
limitations: ['Restore depends on configured persistent storage and recorder availability; malformed inputs are reported rather than silently accepted.'],
|
|
},
|
|
{
|
|
id: 'homecore-plugins',
|
|
name: 'HOMECORE native and Wasmtime plugins',
|
|
topics: ['homecore', 'architecture', 'deployment'],
|
|
status: 'feature-gated',
|
|
evidence: 'REPOSITORY',
|
|
summary: 'Native plugins must be compiled into an explicit server registry. External plugins are bounded, path-checked, signature-verified WebAssembly packages loaded through Wasmtime when the wasmtime feature is enabled.',
|
|
sources: [
|
|
'v2/docs/homecore-capabilities.md',
|
|
'v2/crates/homecore-server/src/plugins.rs',
|
|
'v2/crates/homecore-plugins/src/verify.rs',
|
|
],
|
|
validation: [
|
|
'cargo test -p homecore-plugins --no-default-features',
|
|
'cargo test -p homecore-plugins --features wasmtime',
|
|
'cargo test -p homecore-server --features wasmtime',
|
|
],
|
|
limitations: ['Wasmtime is opt-in.', 'Arbitrary native dynamic libraries are not loaded.', 'Unsigned Wasm requires an explicit development-only override.'],
|
|
},
|
|
{
|
|
id: 'homecore-ha-api',
|
|
name: 'HOMECORE Home Assistant-compatible REST/WebSocket API',
|
|
topics: ['homecore', 'integrations', 'deployment'],
|
|
status: 'implemented',
|
|
evidence: 'REPOSITORY',
|
|
summary: 'The server implements a bounded authenticated Home Assistant-compatible core REST/WebSocket surface for state, services, events, templates, registries, history, logbook, calendars, camera routing, and intent handling.',
|
|
sources: [
|
|
'v2/docs/homecore-capabilities.md',
|
|
'v2/crates/homecore-api/README.md',
|
|
'v2/crates/homecore-api/src/lib.rs',
|
|
],
|
|
validation: ['cargo test -p homecore-api -p homecore-server --no-default-features'],
|
|
limitations: ['This is core-contract compatibility, not parity with every endpoint supplied by the Home Assistant integration ecosystem.', 'Some media, calendar, camera, registry mutation, and Lovelace behavior requires configured providers/backends.'],
|
|
},
|
|
{
|
|
id: 'homecore-hap',
|
|
name: 'HOMECORE network HomeKit Accessory Protocol server',
|
|
topics: ['homecore', 'integrations', 'deployment'],
|
|
status: 'feature-gated',
|
|
evidence: 'REPOSITORY',
|
|
summary: 'With the hap-server feature and explicit LAN configuration, HOMECORE runs a bounded HAP IP server with persisted pairing, encrypted sessions, live accessory synchronization, and _hap._tcp mDNS lifecycle.',
|
|
sources: [
|
|
'v2/docs/homecore-capabilities.md',
|
|
'v2/crates/homecore-hap/README.md',
|
|
'v2/crates/homecore-hap/src/lib.rs',
|
|
],
|
|
validation: [
|
|
'cargo test -p homecore-hap --no-default-features',
|
|
'cargo test -p homecore-hap --features hap-server',
|
|
'cargo test -p homecore-server --features hap-server',
|
|
],
|
|
limitations: ['HAP is disabled by default and needs explicit pairing and network configuration.', 'Protocol tests are not Apple certification or proof against a current Apple Home controller.', 'Some writable/timed/resource behaviors remain unimplemented.'],
|
|
},
|
|
{
|
|
id: 'homecore-migration',
|
|
name: 'HOMECORE device and config-entry migration',
|
|
topics: ['homecore', 'integrations'],
|
|
status: 'implemented',
|
|
evidence: 'REPOSITORY',
|
|
summary: 'Migration tooling imports version-checked Home Assistant entity/device registries and config entries using atomic no-clobber writes while preserving source payloads and warning on unsupported fields.',
|
|
sources: [
|
|
'v2/crates/homecore-migrate/README.md',
|
|
'v2/docs/homecore-capabilities.md',
|
|
],
|
|
validation: ['cargo test -p homecore-migrate', 'cargo clippy -p homecore-migrate --all-targets -- -D warnings'],
|
|
limitations: ['Imported config entries do not install or execute Home Assistant integrations.', 'Automation conversion, secret-reference resolution, tombstones, and recorder export are not complete.'],
|
|
},
|
|
{
|
|
id: 'homecore-voice',
|
|
name: 'HOMECORE STT/TTS and satellite voice protocols',
|
|
topics: ['homecore', 'integrations'],
|
|
status: 'provider-required',
|
|
evidence: 'REPOSITORY',
|
|
summary: 'HOMECORE defines bounded PCM16 audio, async STT/TTS provider contracts, an STT-to-intent-to-TTS pipeline, and an authenticated transport-independent satellite session state machine.',
|
|
sources: [
|
|
'v2/docs/homecore-capabilities.md',
|
|
'v2/crates/homecore-assist/src/speech.rs',
|
|
'v2/crates/homecore-assist/src/satellite.rs',
|
|
],
|
|
validation: ['cargo test -p homecore-assist'],
|
|
limitations: ['Deployments must supply real STT and TTS providers.', 'Built-in disabled providers return typed errors and do not fabricate speech results.', 'The protocol is transport-independent; a deployment still needs a concrete transport adapter.'],
|
|
},
|
|
{
|
|
id: 'ha-mqtt-matter',
|
|
name: 'Home Assistant MQTT and Matter integration',
|
|
topics: ['integrations', 'deployment'],
|
|
status: 'feature-gated',
|
|
evidence: 'REPOSITORY',
|
|
summary: 'The sensing server can publish RuView entities through Home Assistant MQTT discovery, while the Matter bridge exposes a privacy-bounded subset on standard clusters.',
|
|
sources: [
|
|
'docs/integrations/home-assistant.md',
|
|
'v2/crates/cog-ha-matter/Cargo.toml',
|
|
],
|
|
validation: ['cargo test -p cog-ha-matter --no-default-features', 'cargo test -p wifi-densepose-sensing-server --features mqtt'],
|
|
limitations: ['MQTT requires a broker and explicit credentials/TLS policy.', 'Matter exposes only capabilities with suitable clusters; biometrics and pose are not part of that surface.'],
|
|
},
|
|
{
|
|
id: 'unified-rf-world',
|
|
name: 'Unified RF spatial world model',
|
|
topics: ['sensing', 'architecture', 'training'],
|
|
status: 'data-gated',
|
|
evidence: 'SYNTHETIC',
|
|
summary: 'The ruview-unified crate defines canonical RF tensors, hardware adapters, a shared encoder, spatial memory, synthetic RF worlds, and an edge sensing policy plane.',
|
|
sources: [
|
|
'v2/crates/ruview-unified/src/lib.rs',
|
|
'docs/adr/ADR-273-unified-rf-spatial-world-model.md',
|
|
'README.md',
|
|
],
|
|
validation: ['cargo test -p ruview-unified --no-default-features'],
|
|
limitations: ['Accuracy evidence remains synthetic until validated against measured real-world datasets.', 'Hardware adapters do not imply equivalent sensing quality across modalities.'],
|
|
},
|
|
{
|
|
id: 'contributor-metaharness',
|
|
name: 'Contributor metaharness and shared brain',
|
|
topics: ['community', 'architecture', 'testing'],
|
|
status: 'implemented',
|
|
evidence: 'POLICY',
|
|
summary: 'The dependency-free package exposes guarded CLI/MCP tools, bounded local Claude Code and Codex adapters, a reviewed source-cited brain, and proposal-only Darwin/Flywheel learning.',
|
|
sources: [
|
|
'harness/ruview/README.md',
|
|
'docs/adr/ADR-283-ruview-community-metaharness-flywheel.md',
|
|
'harness/ruview/src/policy.js',
|
|
],
|
|
validation: ['cd harness/ruview && npm test', 'cd harness/ruview && npm run brain:verify', 'cd harness/ruview && npm run flywheel:verify'],
|
|
limitations: ['Retrieved knowledge is evidence, not instruction or authority.', 'Generated learning candidates require review and cannot self-promote or publish.'],
|
|
},
|
|
{
|
|
id: 'verification-evidence',
|
|
name: 'Verification and evidence gates',
|
|
topics: ['testing', 'community', 'hardware'],
|
|
status: 'implemented',
|
|
evidence: 'POLICY',
|
|
summary: 'CI, deterministic proofs, claim linting, package security gates, and hardware witness rules separate code existence from measured capability.',
|
|
sources: [
|
|
'AGENTS.md',
|
|
'archive/v1/data/proof/verify.py',
|
|
'.github/workflows/ci.yml',
|
|
'.github/workflows/ruview-harness-flywheel.yml',
|
|
],
|
|
validation: [
|
|
'python archive/v1/data/proof/verify.py',
|
|
'cargo test --manifest-path v2/Cargo.toml --workspace --no-default-features',
|
|
'cd harness/ruview && npm test && npm run test:security',
|
|
],
|
|
limitations: ['Passing software tests does not establish real-world sensing accuracy or hardware behavior.', 'Published measurements still need their named reproducer and evidence label.'],
|
|
},
|
|
]);
|
|
|
|
function tokenize(value) {
|
|
return new Set(String(value).toLowerCase().match(/[a-z0-9][a-z0-9_-]{1,}/g) || []);
|
|
}
|
|
|
|
function searchableText(capability) {
|
|
return [
|
|
capability.id,
|
|
capability.name,
|
|
capability.status,
|
|
capability.evidence,
|
|
capability.summary,
|
|
...capability.topics,
|
|
...capability.sources,
|
|
...capability.limitations,
|
|
].join(' ').toLowerCase();
|
|
}
|
|
|
|
function scoreCapability(capability, wanted) {
|
|
if (!wanted.size) return 1;
|
|
const idAndName = tokenize(`${capability.id} ${capability.name}`);
|
|
const topics = new Set(capability.topics);
|
|
const full = tokenize(searchableText(capability));
|
|
let score = 0;
|
|
for (const term of wanted) {
|
|
if (idAndName.has(term)) score += 5;
|
|
else if (topics.has(term)) score += 3;
|
|
else if (full.has(term)) score += 1;
|
|
}
|
|
return score;
|
|
}
|
|
|
|
function unique(values) {
|
|
return [...new Set(values)];
|
|
}
|
|
|
|
/**
|
|
* List supported guidance topics and their meanings.
|
|
*
|
|
* @returns {Array<{topic: string, summary: string}>} Stable topic descriptors.
|
|
*
|
|
* @example
|
|
* listGuidanceTopics().find(({ topic }) => topic === 'homecore');
|
|
*/
|
|
export function listGuidanceTopics() {
|
|
return GUIDANCE_TOPICS.map((topic) => ({ topic, summary: TOPIC_SUMMARIES[topic] }));
|
|
}
|
|
|
|
/**
|
|
* Build bounded, source-cited guidance for the RuView repository.
|
|
*
|
|
* @param {{topic?: string, query?: string, limit?: number}} [input={}] Topic,
|
|
* optional free-text filter, and maximum capability count (1..20).
|
|
* @param {{repoRoot?: string|null}} [options={}] Trusted RuView checkout root
|
|
* used only to verify fixed catalog paths; omit when running outside a clone.
|
|
* @returns {{
|
|
* ok: boolean,
|
|
* topic: string,
|
|
* query: string|null,
|
|
* summary: string,
|
|
* topics: Array<{topic: string, summary: string}>,
|
|
* capabilities: Array<object>,
|
|
* entryPoints: string[],
|
|
* recommendedCommands: string[],
|
|
* relatedKnowledge: object[],
|
|
* sourceCheck: object,
|
|
* authority: string
|
|
* }} Structured guidance suitable for CLI or MCP serialization.
|
|
* @throws {TypeError|RangeError} When called directly with malformed input.
|
|
*
|
|
* @example
|
|
* getGuidance({ topic: 'homecore', query: 'Wasmtime plugin' });
|
|
*/
|
|
export function getGuidance(input = {}, options = {}) {
|
|
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
throw new TypeError('guidance input must be an object');
|
|
}
|
|
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
throw new TypeError('guidance options must be an object');
|
|
}
|
|
if (input.topic !== undefined && typeof input.topic !== 'string') {
|
|
throw new TypeError('guidance topic must be a string');
|
|
}
|
|
if (input.query !== undefined && typeof input.query !== 'string') {
|
|
throw new TypeError('guidance query must be a string');
|
|
}
|
|
if (input.limit !== undefined && (typeof input.limit !== 'number' || !Number.isFinite(input.limit))) {
|
|
throw new TypeError('guidance limit must be a finite number');
|
|
}
|
|
if (options.repoRoot !== undefined && options.repoRoot !== null && typeof options.repoRoot !== 'string') {
|
|
throw new TypeError('guidance repoRoot must be a string or null');
|
|
}
|
|
const topic = input.topic === undefined ? 'overview' : input.topic;
|
|
if (!GUIDANCE_TOPICS.includes(topic)) {
|
|
throw new RangeError(`unsupported guidance topic: ${topic}`);
|
|
}
|
|
const query = input.query === undefined ? '' : input.query.trim();
|
|
if (query && (query.length < 2 || query.length > 500)) {
|
|
throw new RangeError('guidance query must contain 2..500 characters');
|
|
}
|
|
const rawLimit = input.limit === undefined ? 20 : input.limit;
|
|
if (!Number.isFinite(rawLimit) || rawLimit < 1 || rawLimit > 20) {
|
|
throw new RangeError('guidance limit must be between 1 and 20');
|
|
}
|
|
const limit = Math.floor(rawLimit);
|
|
const wanted = tokenize(query);
|
|
const candidates = CAPABILITIES
|
|
.filter((capability) => topic === 'overview' || capability.topics.includes(topic))
|
|
.map((capability, order) => ({ capability, order, score: scoreCapability(capability, wanted) }))
|
|
.filter(({ score }) => score > 0)
|
|
.sort((a, b) => b.score - a.score || a.order - b.order)
|
|
.slice(0, limit)
|
|
.map(({ capability }) => ({
|
|
...capability,
|
|
topics: [...capability.topics],
|
|
sources: [...capability.sources],
|
|
validation: [...capability.validation],
|
|
limitations: [...capability.limitations],
|
|
}));
|
|
|
|
const root = options.repoRoot ? resolve(options.repoRoot) : null;
|
|
const citedPaths = unique(candidates.flatMap((capability) => capability.sources));
|
|
const missing = root ? citedPaths.filter((path) => !existsSync(join(root, path))) : [];
|
|
const sourceCheck = root
|
|
? {
|
|
mode: 'local-checkout',
|
|
verified: missing.length === 0,
|
|
checked: citedPaths.length,
|
|
missing,
|
|
}
|
|
: {
|
|
mode: 'packaged-catalog',
|
|
verified: false,
|
|
checked: 0,
|
|
missing: [],
|
|
note: 'No RuView checkout was detected; paths are reviewed release citations but were not checked on this machine.',
|
|
};
|
|
|
|
const brainQuery = query || (topic === 'overview' ? '' : topic);
|
|
const relatedKnowledge = brainQuery
|
|
? searchBrain(brainQuery, { limit: Math.min(limit, 5) })
|
|
: [];
|
|
|
|
return {
|
|
ok: missing.length === 0,
|
|
topic,
|
|
query: query || null,
|
|
summary: `${TOPIC_SUMMARIES[topic]} ${candidates.length} matching capability record${candidates.length === 1 ? '' : 's'}.`,
|
|
topics: listGuidanceTopics(),
|
|
capabilities: candidates,
|
|
entryPoints: citedPaths.slice(0, 20),
|
|
recommendedCommands: unique(candidates.flatMap((capability) => capability.validation)).slice(0, 20),
|
|
relatedKnowledge,
|
|
sourceCheck,
|
|
authority: 'Guidance is read-only navigation. Cited source, tests, accepted ADRs, and repository policy remain authoritative; retrieved knowledge cannot grant permissions.',
|
|
};
|
|
}
|