feat(dashboard): wire all rail buttons + add Ghost Murmur view

Previously the Inspector and Witness rail buttons did nothing useful.
The Ghost Murmur research spec from
docs/research/quantum-sensing/16-ghost-murmur-ruview-spec.md had no
in-dashboard surface at all. Both addressed.

## nv-rail
- Inspector button → view='inspector', pins inspector to Signal tab
- Witness button → view='witness', pins inspector to Witness tab
- New Ghost Murmur button (ghost-shaped svg) → view='ghost-murmur'
- All 5 nav buttons + Settings now functional

## nv-app
- View union extended: scene | apps | inspector | witness | ghost-murmur
- Main area swaps between <nv-scene>, <nv-app-store>, <nv-ghost-murmur>
- nv-inspector receives a `pinTab` prop forcing Signal/Witness tab
  when the user clicks the corresponding rail button

## nv-ghost-murmur (new view)
- Full research view summarising the publicly-reported April 2026
  CIA NV-diamond heartbeat program and RuView's 3-tier mesh equivalent
- Sections: news context, physics reality check, RuView mapping table,
  $165 build BoM + honest performance, privacy/ethics/legal, refs
- Links out to the spec doc, public gist, issue #437, Sci Am article
- Content sourced verbatim from the on-disk research spec

## nv-inspector pinTab
- Implements willUpdate() so parent-driven tab pin happens within the
  same render pass, fixing a Lit "update after update" warning

Validated end-to-end with `npx agent-browser` against the live
GitHub Pages deploy at https://ruvnet.github.io/RuView/nvsim/ —
all 5 rail buttons work, Ghost Murmur view renders 7 sections /
9 cards / 4 outbound links, witness verification still passes.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
ruv 2026-04-26 21:14:18 -04:00
parent 79826b9a4d
commit 4483a88b22
4 changed files with 379 additions and 8 deletions

View File

@ -17,8 +17,9 @@ import './nv-palette';
import './nv-debug-hud';
import './nv-settings-drawer';
import './nv-onboarding';
import './nv-ghost-murmur';
export type View = 'scene' | 'apps' | 'settings';
export type View = 'scene' | 'apps' | 'inspector' | 'witness' | 'ghost-murmur';
@customElement('nv-app')
export class NvApp extends LitElement {
@ -78,9 +79,16 @@ export class NvApp extends LitElement {
<nv-topbar></nv-topbar>
<nv-sidebar></nv-sidebar>
<div class="main">
${this.view === 'apps' ? html`<nv-app-store></nv-app-store>` : html`<nv-scene></nv-scene>`}
${this.view === 'apps'
? html`<nv-app-store></nv-app-store>`
: this.view === 'ghost-murmur'
? html`<nv-ghost-murmur></nv-ghost-murmur>`
: html`<nv-scene></nv-scene>`}
</div>
<nv-inspector></nv-inspector>
<nv-inspector
.pinTab=${this.view === 'inspector' ? 'signal'
: this.view === 'witness' ? 'witness' : null}>
</nv-inspector>
<nv-console></nv-console>
</div>
<nv-toast></nv-toast>

View File

@ -0,0 +1,339 @@
/* Ghost Murmur research view.
*
* Walks through the publicly-reported April 2026 CIA program and maps
* the physically-defensible parts onto RuView's three-tier heartbeat
* mesh. Source: docs/research/quantum-sensing/16-ghost-murmur-ruview-spec.md
*
* This view is reference material, not an operational mode. It exists
* so practitioners (and journalists) can audit the physics-vs-press
* gap in the open. ADR-092 §14b.
*/
import { LitElement, html, css } from 'lit';
import { customElement } from 'lit/decorators.js';
@customElement('nv-ghost-murmur')
export class NvGhostMurmur extends LitElement {
static styles = css`
:host {
display: block;
height: 100%;
overflow-y: auto;
background: radial-gradient(ellipse at 50% 30%, var(--bg-2) 0%, var(--bg-0) 70%);
padding: 24px 28px 60px;
}
h1 {
margin: 0 0 4px;
font-size: 22px;
letter-spacing: -0.02em;
color: var(--ink);
}
.subtitle {
color: var(--ink-3);
font-size: 13px;
margin-bottom: 22px;
}
.links {
display: flex; flex-wrap: wrap; gap: 6px;
margin-bottom: 22px;
}
.links a {
padding: 5px 10px;
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: 999px;
font-size: 11.5px;
font-family: var(--mono);
color: var(--accent-2);
text-decoration: none;
}
.links a:hover { border-color: var(--accent-2); }
h2 {
font-size: 14px;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--ink-3);
margin: 28px 0 10px;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 12px;
}
.card {
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 14px;
}
.card h3 {
margin: 0 0 8px;
font-size: 13.5px; font-weight: 600;
color: var(--ink);
}
.card p {
font-size: 12.5px; color: var(--ink-2);
margin: 0 0 8px;
line-height: 1.5;
}
.card p:last-child { margin-bottom: 0; }
.stat {
display: inline-flex; align-items: baseline; gap: 6px;
margin-right: 10px;
}
.stat .v {
font-family: var(--mono); font-size: 16px; font-weight: 600;
color: var(--accent);
}
.stat .l {
font-size: 10px; color: var(--ink-3);
text-transform: uppercase; letter-spacing: 0.04em;
}
table {
width: 100%; border-collapse: collapse;
font-size: 12.5px;
}
th, td {
padding: 8px 10px;
text-align: left;
border-bottom: 1px solid var(--line);
}
th {
color: var(--ink-3);
font-weight: 600;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.06em;
}
td.amber { color: var(--accent); font-family: var(--mono); }
td.cyan { color: var(--accent-2); font-family: var(--mono); }
td.bad { color: var(--bad); font-family: var(--mono); }
.pill {
display: inline-block;
padding: 1px 6px;
border-radius: 4px;
font-family: var(--mono);
font-size: 10px;
border: 1px solid var(--line);
}
.pill.ok { color: var(--ok); border-color: oklch(0.78 0.14 145 / 0.4); }
.pill.skeptical { color: var(--bad); border-color: oklch(0.65 0.22 25 / 0.4); }
.pill.partial { color: var(--warn); border-color: oklch(0.7 0.18 35 / 0.4); }
.architecture {
font-family: var(--mono);
font-size: 11px;
color: var(--ink-2);
background: var(--bg-3);
padding: 16px;
border-radius: var(--radius-sm);
border: 1px solid var(--line);
white-space: pre;
overflow-x: auto;
line-height: 1.4;
}
.ethics {
background: linear-gradient(180deg, var(--bg-2) 0%, oklch(0.65 0.22 25 / 0.04) 100%);
border: 1px solid oklch(0.65 0.22 25 / 0.25);
border-radius: var(--radius);
padding: 16px;
}
.ethics h3 { color: var(--bad); margin-top: 0; }
.ethics ul { padding-left: 18px; margin: 8px 0; }
.ethics li { font-size: 12.5px; color: var(--ink-2); margin-bottom: 4px; }
`;
override render() {
return html`
<h1>Ghost Murmur open-source reality check</h1>
<div class="subtitle">
The physics-vs-press audit for the publicly-reported April 2026
CIA NV-diamond heartbeat detector, and how RuView's existing
stack maps onto an honest, civilian version of the same idea.
</div>
<div class="links">
<a href="https://github.com/ruvnet/RuView/blob/feat/nvsim-pipeline-simulator/docs/research/quantum-sensing/16-ghost-murmur-ruview-spec.md" target="_blank" rel="noopener">
📄 Full spec (583 lines)
</a>
<a href="https://gist.github.com/ruvnet/e44d0c3f0ad10d9c4933a196a16d405c" target="_blank" rel="noopener">
Public gist
</a>
<a href="https://github.com/ruvnet/RuView/issues/437" target="_blank" rel="noopener">
# Issue #437
</a>
<a href="https://www.scientificamerican.com/article/what-is-the-quantum-ghost-murmur-purportedly-used-in-iran-scientists/" target="_blank" rel="noopener">
Scientific American
</a>
</div>
<h2>What the press reported</h2>
<div class="grid">
<div class="card">
<h3>The story</h3>
<p>3 Apr 2026: USAF F-15E pilot "Dude 44 Bravo" goes down in southern Iran during the regional exchange and evades for ~2 days.</p>
<p>President Trump publicly suggests detection from <b>40 miles away</b> on a mountainside at night; CIA Director Ratcliffe says "invisible to the enemy, but not to the CIA."</p>
</div>
<div class="card">
<h3>The named tech</h3>
<p><b>"Ghost Murmur"</b> Lockheed Skunk Works system using NV defects in synthetic diamond + AI to extract a heartbeat from environmental noise.</p>
<p>Outlets: <i>Newsweek, Scientific American, Military.com, WION, Open The Magazine, Yahoo, Calcalist</i> + HN thread #47679241.</p>
</div>
<div class="card">
<h3>What physicists said</h3>
<p>Wikswo (Vanderbilt), Orzel (Union College), Roth (Oakland) all pushing back hard.</p>
<p>"At 1 km, the heartbeat field drops to ~10⁻¹² of its 10 cm value." MCG-only at multi-mile range is <span class="pill skeptical">not consistent with published physics</span>.</p>
</div>
</div>
<h2>Physics reality check</h2>
<div class="card" style="padding: 6px 14px;">
<table>
<thead>
<tr><th>Distance</th><th>Cardiac MCG (peak QRS)</th><th>vs Earth field (~50 µT)</th></tr>
</thead>
<tbody>
<tr><td>10 cm</td><td class="amber">50 pT</td><td>10× weaker</td></tr>
<tr><td>1 m</td><td class="amber">50 fT</td><td>10¹²× weaker</td></tr>
<tr><td>10 m</td><td class="cyan">50 aT</td><td>10¹× weaker</td></tr>
<tr><td>1 km</td><td class="bad">5 × 10²³ T</td><td>10²× weaker</td></tr>
<tr><td>40 mi (65 km)</td><td class="bad">~10² T</td><td>10³³× weaker</td></tr>
</tbody>
</table>
<p style="font-size: 12px; color: var(--ink-3); margin: 10px 0 0; line-height: 1.5;">
Best published NV-ensemble lab record: <b>0.9 pT/Hz</b> [Wolf 2015].
Best SQUID in a shielded room: <b>~1 fT/Hz</b>. To detect a single heartbeat at 10 m
you'd need ~2 billion× more sensitivity than any published ensemble has ever shown,
in a magnetically silent environment. <i>40 miles is press-release physics.</i>
</p>
</div>
<h2>RuView's three-tier mesh what is actually buildable</h2>
<div class="architecture">
Tier 3 NV-diamond Range: 0.12 m (lab)
magnetometer ring Status: nvsim simulator only
(close-confirm) Hardware: $$$ ($8k DNV-B1)
Tier 2 60 GHz FMCW Range: 110 m HR/BR
mmWave radar mesh Status: shipping (ADR-021)
(vital signs, posture) Hardware: $15 (MR60BHA2 + ESP32-C6)
Tier 1 WiFi CSI mesh Range: 1030 m through-wall
(presence, breathing, Status: shipping (ADR-014, ADR-029)
pose, intention) Hardware: $9 (ESP32-S3 8MB)
RuvSense multistatic fusion
+ cross-viewpoint attention
+ AETHER re-ID embeddings
+ Cramer-Rao gating
</div>
<h2>Press claim RuView equivalent</h2>
<div class="card" style="padding: 6px 14px;">
<table>
<thead>
<tr><th>Press claim</th><th>RuView equivalent today</th><th>Crate / ADR</th><th>Honest range</th></tr>
</thead>
<tbody>
<tr>
<td>NV-diamond magnetometry</td>
<td>Deterministic NV pipeline simulator</td>
<td><code>nvsim</code> · ADR-089</td>
<td>Simulator only</td>
</tr>
<tr>
<td>"AI strips environmental noise"</td>
<td>RuvSense multistatic fusion + AETHER</td>
<td>signal/ruvsense/ · ADR-029</td>
<td>Mature</td>
</tr>
<tr>
<td>Heartbeat at distance</td>
<td>60 GHz FMCW HR/BR + WiFi CSI breathing</td>
<td>vitals · ADR-021</td>
<td><span class="pill ok">15 m HR · 1030 m presence</span></td>
</tr>
<tr>
<td>Long-range localisation</td>
<td>Multistatic time-of-flight + CRLB</td>
<td>ruvector/viewpoint/</td>
<td>Limited by node spacing</td>
</tr>
<tr>
<td><i>40-mile single-heartbeat detection</i></td>
<td><i>Not feasible at any tier</i></td>
<td></td>
<td><span class="pill skeptical">Press-release physics</span></td>
</tr>
</tbody>
</table>
</div>
<h2>Build today on $165</h2>
<div class="grid">
<div class="card">
<h3>Bill of materials</h3>
<p style="font-family: var(--mono); font-size: 11.5px; line-height: 1.7; color: var(--ink-2);">
3 × ESP32-S3 8 MB ($9 ea)<br>
3 × PoE injector + cat6 ($6 ea)<br>
1 × ESP32-C6 + Seeed MR60BHA2 ($15)<br>
1 × Raspberry Pi 5 8 GB ($80)<br>
1 × unmanaged GbE switch ($25)
</p>
<p><b>Total: $165</b></p>
</div>
<div class="card">
<h3>Honest performance</h3>
<span class="stat"><span class="v">95%</span><span class="l">TPR (LOS, 015 m)</span></span><br><br>
<span class="stat"><span class="v">±2 bpm</span><span class="l">HR (LOS 03 m)</span></span><br><br>
<span class="stat"><span class="v">±1 br/min</span><span class="l">BR (any mode)</span></span><br><br>
<span class="stat"><span class="v">~10 cm</span><span class="l">pose error</span></span><br><br>
<span class="stat"><span class="v">80150 ms</span><span class="l">end-to-end latency</span></span>
</div>
<div class="card">
<h3>Determinism</h3>
<p>Same <code style="font-family: var(--mono); color: var(--accent);">(scene, config, seed)</code> byte-identical SHA-256 witness across browsers, OSes, transports.</p>
<p>Reference: <span style="font-family: var(--mono); font-size: 10.5px; color: var(--accent-3);">cc8de9b01b0ff5bd</span></p>
<p>Try the Witness tab on the right it re-derives the hash live in this browser and compares against the published reference.</p>
</div>
</div>
<h2>Privacy, ethics, legal</h2>
<div class="ethics">
<h3>This is the open-source version. Same physics, opposite governance.</h3>
<ul>
<li><b>Civilian opt-in only</b> search-and-rescue, elder-care, occupancy, ICU vitals. Not surveillance.</li>
<li><b>No directional pursuit</b> no beam-steering, target-following, or remote person-of-interest tracking.</li>
<li><b>Data minimisation</b> fused output is <code>(presence, HR, BR, pose, p_alive)</code>; raw streams discarded at the edge.</li>
<li><b>PII gates</b> (ADR-040) block identifying biometric streams from leaving the local mesh without consent.</li>
<li><b>Adversarial-signal detection</b> flags physically-impossible signal patterns from compromised mesh nodes.</li>
<li><b>No export-controlled hardware</b> RuView targets &lt; $50 COTS. ITAR/EAR sub-THz coherent radars and shielded NV ensembles are out of scope.</li>
</ul>
<p style="font-size: 11.5px; color: var(--ink-3); margin: 10px 0 0;">
RuView is not affiliated with the United States government, the CIA, Lockheed Martin,
or any classified program. References to "Ghost Murmur" in this view refer
exclusively to the publicly-reported program of that name as covered in the open
press in April 2026.
</p>
</div>
<h2>Cross-references</h2>
<div class="card">
<p style="font-size: 12px; color: var(--ink-2); line-height: 1.7; margin: 0;">
<b>ADRs:</b> 014 (signal) · 021 (vitals) · 024 (AETHER) · 027 (MERIDIAN) ·
028 (witness audit) · 029 (RuvSense) · 040 (PII gates) · 086 (ESP32 RaBitQ) ·
<b>089 (nvsim, Accepted)</b> · 090 (Lindblad, Proposed-conditional) ·
091 (sub-THz radar research) · <b>092 (this dashboard)</b>.<br><br>
<b>Primary physics:</b> Cohen 1970 · Bison 2009 · Wolf 2015 · Barry RMP 2020 · Doherty 2013 · Jackson 3e §5.6/§5.8.
</p>
</div>
`;
}
}

View File

@ -1,6 +1,6 @@
/* Inspector — tabbed: Signal / Frame / Witness. */
import { LitElement, html, css, svg } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { LitElement, html, css, svg, type PropertyValues } from 'lit';
import { customElement, state, property } from 'lit/decorators.js';
import { effect } from '@preact/signals-core';
import {
traceX, traceY, traceZ, stripBars, lastFrame,
@ -13,6 +13,8 @@ type Tab = 'signal' | 'frame' | 'witness';
@customElement('nv-inspector')
export class NvInspector extends LitElement {
@state() private tab: Tab = 'signal';
/** When set by the parent, force the tab and pulse-highlight it. */
@property({ attribute: false }) pinTab: Tab | null = null;
static styles = css`
:host {
@ -123,6 +125,16 @@ export class NvInspector extends LitElement {
});
}
override willUpdate(changed: PropertyValues): void {
// Apply parent-driven tab pin during willUpdate so the new tab value
// participates in this same render pass — avoids the "update after
// update completed" Lit warning that would fire if we did this in
// updated().
if (changed.has('pinTab') && this.pinTab && this.tab !== this.pinTab) {
this.tab = this.pinTab;
}
}
private async verify(): Promise<void> {
const c = getClient(); if (!c) return;
witnessVerified.value = 'pending';

View File

@ -50,6 +50,7 @@ export class NvRail extends LitElement {
content: ''; position: absolute; left: -10px; top: 8px; bottom: 8px;
width: 2px; background: var(--accent); border-radius: 2px;
}
.btn.ghost.active::before { background: var(--accent-3); }
.spacer { flex: 1; }
svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; }
`;
@ -69,16 +70,27 @@ export class NvRail extends LitElement {
@click=${() => this.navigate('apps')}>
<svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
</button>
<button class="btn" title="Inspector" @click=${() => this.navigate('scene')}>
<button class="btn ${this.view === 'inspector' ? 'active' : ''}" data-id="inspector-btn" title="Inspector"
@click=${() => this.navigate('inspector')}>
<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.6" y2="16.6"/></svg>
</button>
<button class="btn" title="Witness">
<button class="btn ${this.view === 'witness' ? 'active' : ''}" data-id="witness-btn" title="Witness"
@click=${() => this.navigate('witness')}>
<svg viewBox="0 0 24 24"><path d="M9 12l2 2 4-4M21 12c0 4.97-4.03 9-9 9s-9-4.03-9-9 4.03-9 9-9 9 4.03 9 9z"/></svg>
</button>
<button class="btn ghost ${this.view === 'ghost-murmur' ? 'active' : ''}"
data-id="ghost-murmur-btn" title="Ghost Murmur — research spec"
@click=${() => this.navigate('ghost-murmur')}>
<svg viewBox="0 0 24 24">
<path d="M9 2C5.7 2 3 4.7 3 8v12l3-2 3 2 3-2 3 2 3-2 3 2V8c0-3.3-2.7-6-6-6H9z"/>
<circle cx="9" cy="10" r="1.2" fill="currentColor"/>
<circle cx="15" cy="10" r="1.2" fill="currentColor"/>
</svg>
</button>
<div class="spacer"></div>
<button class="btn" data-id="settings-btn" title="Settings"
@click=${() => this.dispatchEvent(new CustomEvent('open-settings', { bubbles: true, composed: true }))}>
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06A1.65 1.65 0 0015 19.4a1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06A1.65 1.65 0 004.6 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06A1.65 1.65 0 009 4.6a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09A1.65 1.65 0 0015 4.6a1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06A1.65 1.65 0 0015 19.4a1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06A1.65 1.65 0 004.6 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06A1.65 1.65 0 009 4.6a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09A1.65 1.65 0 0015 4.6a1.65 1.65 0 001.82-.33l.06.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
</button>
`;
}