mirror of
https://github.com/ruvnet/RuView
synced 2026-08-05 19:41:44 +00:00
46b4d63dad
Two of the "what to optimize next" items, as enforced code: 1. Hard claim gate: extract the release rule into a single acceptance::claim_allowed(entrainment, safety, adherence, repeatability) = strict AND of all four, used by every path. A test proves every 3-of-4 subset is denied, so no path can silently weaken the gate to an OR/subset. 2. Hardware-in-the-loop contract (new hil module): verify_hil grades a captured actuator bench measurement against fixed targets — LED frequency +/-0.1 Hz, audio-visual sync < 5 ms, stop-signal -> actuator-off < 100 ms, session-hash reproducibility 100%, EEG entrainment lift >= 20% over fixed 40 Hz. Every failure mode fails closed (missing/NaN stop measurement, no replay, any hash mismatch). The crate stays a deterministic leaf: firmware records the measurements, this module grades them. README: benchmark table (safety tick ~8 ns, recommend ~15 us, cohort kNN/500 ~15 us, calibration ~115 us, acceptance grading ~425 us enrollment-only) and the positioning line — "a governed personalization engine that refuses to overpromise." 9 new tests; crate now 97 + 1 doctest; pinned witness 13cb164c... preserved. Workspace gate: 2,898 passed / 0 failed. https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
216 lines
11 KiB
Markdown
216 lines
11 KiB
Markdown
# ruview-gamma — Adaptive Sensory Neuromodulation (ADR-250)
|
||
|
||
> **The most valuable thing here is not 40 Hz. It is a governed personalization
|
||
> engine that refuses to overpromise.**
|
||
|
||
The control brain for an adaptive light-and-sound neuromodulation device. The
|
||
device plays stimulation; **RuView** reads the body as feedback; **RuVector**
|
||
learns the personal response map; **RuFlo** governs the safety, audit trail, and
|
||
claim boundary. The breakthrough is not speed alone — it is **safe adaptive
|
||
personalization with proof discipline**.
|
||
|
||
It starts from 40 Hz as the research prior, then learns whether a person
|
||
responds better at 38.5, 40, 41.2, or another safe setting — watching breathing,
|
||
stillness, restlessness, adherence, and sensor confidence. If something goes
|
||
wrong, the session locks. If a program has not proven entrainment, safety,
|
||
adherence, and repeatability, it cannot advertise a benefit — it returns
|
||
*research use only*.
|
||
|
||
## Benchmarks (this container — indicative)
|
||
|
||
| Path | Current | Role |
|
||
|------|---------|------|
|
||
| Safety tick | ~8 ns | real-time stop path |
|
||
| Recommendation | ~15 µs | per-session decision |
|
||
| Cohort kNN (500 profiles) | ~15 µs | warm-start matching |
|
||
| Calibration sweep | ~115 µs | setup and tuning |
|
||
| Full acceptance grading | ~425 µs | enrollment-only (offline) |
|
||
|
||
The per-session control loop is microseconds; the heavier acceptance grading is
|
||
enrollment-time work, not on the loop. No regression across the optimization
|
||
passes.
|
||
|
||
## The hard claim gate
|
||
|
||
A program's benefit claim is releasable through exactly one invariant
|
||
(`acceptance::claim_allowed`), used everywhere:
|
||
|
||
```text
|
||
claim_allowed = entrainment_pass AND safety_pass
|
||
AND adherence_pass AND repeatability_pass
|
||
```
|
||
|
||
Anything short of all four returns `research use only — … no claim`
|
||
(`acceptance::NO_CLAIM`). The marketing claim is unreadable except through the
|
||
gate.
|
||
|
||
## Next milestone — hardware in the loop (`hil`)
|
||
|
||
The software core is proven against a deterministic simulator; the next
|
||
acceptance bar is a real LED + speaker actuator (e.g. ESP32-driven) plus the
|
||
stop path. `hil::verify_hil` grades a captured bench measurement against fixed
|
||
targets:
|
||
|
||
| Test | Target |
|
||
|------|--------|
|
||
| LED frequency accuracy | ±0.1 Hz |
|
||
| Audio-visual sync drift | < 5 ms |
|
||
| Stop signal → actuator off | < 100 ms |
|
||
| Session-hash reproducibility | 100% |
|
||
| EEG entrainment lift vs fixed 40 Hz | ≥ 20% |
|
||
|
||
---
|
||
|
||
Governed, deterministic, **safety-constrained** personalization of 40 Hz-prior
|
||
multisensory (light + sound) stimulation. Treats 40 Hz as the evidence-based
|
||
*starting prior*, then learns each person's safe entrainment response curve using
|
||
passive RuView sensing, optional EEG, a constrained optimizer, and auditable
|
||
RuFlo workflows.
|
||
|
||
> **Not medical advice / not a medical device.** This crate is a research and
|
||
> engineering platform. The only claim it makes is **"personalized entrainment
|
||
> optimization"** (`ruview_gamma::PRODUCT_CLAIM`) — never Alzheimer's treatment,
|
||
> amyloid clearance, or any clinical outcome (ADR-250 §19). It performs **no
|
||
> hardware actuation**: real stimulus delivery, RF sensing, and EEG arrive
|
||
> through external adapters behind feature flags after this governed software
|
||
> core ships (ADR-250 §21, Milestones 2–4).
|
||
|
||
## Why it exists
|
||
|
||
The field mostly treats 40 Hz as a fixed protocol. But individual brains differ
|
||
by baseline gamma, arousal, sleep, sensory acuity, medication, age, and comfort
|
||
(the 2025 PLOS One 36–44 Hz re-evaluation). Fixed 40 Hz (1) assumes one
|
||
frequency fits all, (2) never verifies entrainment, (3) ignores physiological
|
||
state, and (4) cannot safely optimize over time. This crate closes that loop.
|
||
|
||
## The safety invariant
|
||
|
||
**No recommendation, calibration step, bandit arm, or closed-loop nudge can ever
|
||
emit a `StimulusParameters` outside the `SafetyEnvelope`.** Every emitting path
|
||
clamps to the envelope and is asserted against `SafetyEnvelope::contains` in
|
||
tests. The optimizer never widens the envelope — only an operator constructs a
|
||
wider one deliberately (ADR-250 §12). Non-finite (NaN/∞) inputs clamp toward the
|
||
conservative floor, never the cap.
|
||
|
||
## Module map
|
||
|
||
| Module | Role (ADR-250 §) | Highlights |
|
||
|--------|------------------|------------|
|
||
| `stimulus` | §5, §12 | `StimulusParameters`, `SafetyEnvelope` (validate / clamp / grids) |
|
||
| `safety` | §12 | exclusion screen, latched `SafetyMonitor`, hard-stop reasons |
|
||
| `response` | §6, §9, §10 | `RuViewState`, optional `EegMeasurement`, 20-field `PersonResponseVector` (RuVector memory) with sticky adverse flag |
|
||
| `objective` | §7 | safe-entrainment score; safety is a hard gate, not a weight; RF-only proxy when EEG absent |
|
||
| `simulator` | §21 M1 | deterministic ChaCha20 `frequency_response_curve(person, state, stimulus)` |
|
||
| `optimizer` | §8 | Phase-1 calibration sweep, Phase-2 GP + Expected-Improvement, Phase-4 closed-loop control |
|
||
| `bandit` | §8 P3 | LinUCB contextual bandit over envelope-safe arms |
|
||
| `ruvector` | §10 items 3–6 | anonymized `ProfileStore` (one-way hashed tags), deterministic kNN, cohort warm-start priors (down-weighted pseudo-observations), `DriftDetector` over the physiological sub-vector, deterministic k-means clustering |
|
||
| `program` | §23 | `NeuroProgram` catalog (7 use cases) — per-program envelope, prior, objective, state-gating, evidence level, and gated claim |
|
||
| `acceptance` | §18/§23.1 | `AcceptanceHarness` + `ClaimGate` + the `claim_allowed` invariant — entrainment/safety/adherence/repeatability gate; a program's claim is unreadable until all four pass |
|
||
| `hil` | §17/§21 M2 | hardware-in-the-loop contract: `verify_hil` grades a captured actuator measurement (LED ±0.1 Hz, A/V sync < 5 ms, stop < 100 ms, hash 100%, EEG lift ≥ 20%) |
|
||
| `session` | §11, §13 | hashable `SessionRecord`, reproducible `session_hash` (SHA-256, quantized canonical form) |
|
||
| `ruflo` | §11 | consent → exclusion → envelope → run → monitor → score → update → witnessed audit; trial/sham mode; clinician export; claim discipline |
|
||
| `proof` | — | deterministic bundle witness (mirrors `nvsim` / `verify.py`) |
|
||
| `math` | — | dependency-light numerics (erf, normal CDF/PDF, Cholesky, RBF) |
|
||
|
||
## Quick start
|
||
|
||
```rust
|
||
use ruview_gamma::{
|
||
ruflo::{Consent, RufloGovernor},
|
||
response::RuViewState,
|
||
simulator::{LatentPerson, ResponseSimulator},
|
||
stimulus::{SafetyEnvelope, StimulusParameters},
|
||
};
|
||
|
||
let envelope = SafetyEnvelope::conservative();
|
||
let mut gov = RufloGovernor::enroll("subject-001", envelope, &[], Consent::Granted)
|
||
.expect("cleared to participate");
|
||
|
||
// Milestone 1: drive the governed loop with the deterministic simulator.
|
||
let sim = ResponseSimulator::new(42);
|
||
let latent = LatentPerson::from_id("subject-001");
|
||
let state = RuViewState::calm_baseline();
|
||
gov.run_calibration(&sim, &latent, &state, 5.0, 0).unwrap();
|
||
|
||
let rec = gov.recommend(&StimulusParameters::prior());
|
||
assert!(envelope.contains(&rec.stimulus)); // always inside the envelope
|
||
```
|
||
|
||
## Test / validate / benchmark
|
||
|
||
```bash
|
||
cargo test -p ruview-gamma --no-default-features # 64 unit/integration + 1 doctest
|
||
cargo bench -p ruview-gamma --no-default-features # criterion micro-benchmarks
|
||
```
|
||
|
||
Determinism is proven, not assumed: `proof::Proof::reference_witness()` runs a
|
||
fixed reference participant through the full governed pipeline and pins the
|
||
bundle SHA-256 (`Proof::EXPECTED_WITNESS`); the test fails on any silent drift in
|
||
the optimizer, simulator, response update, or session hashing.
|
||
|
||
### Measured (this container, indicative — not a regression gate)
|
||
|
||
| Bench | Median | Note |
|
||
|-------|--------|------|
|
||
| `gamma_safety_tick` | ~9.3 ns | vs ADR-250 §17 < 500 ms hard-stop latency bound |
|
||
| `gamma_bandit_select` | ~74 ns | LinUCB decision |
|
||
| `gamma_bayesian_recommend` | ~19 µs | GP + EI over the 0.1 Hz envelope grid (was ~105 µs: the GP is now factorized once per recommend, not once per grid candidate — −81%, bit-identical) |
|
||
| `gamma_calibration_sweep` | ~135 µs | full 9-session enroll → simulate → score → update → witness (was ~486 µs, −71%) |
|
||
| `gamma_cohort_knn_500` | ~15 µs | exact kNN over 500 anonymized profiles |
|
||
| `gamma_cohort_warm_start_500` | ~16 µs | full cohort prior construction (runs once per enrollment) |
|
||
| `gamma_acceptance_grade_program` | ~425 µs | full 3-repeat program acceptance grading (offline gate) |
|
||
|
||
## Adaptive sensory neuromodulation platform (ADR-250 §23)
|
||
|
||
40 Hz is one prior in one program — the engine is a general personal
|
||
neural-rhythm optimization platform. `NeuroProgram::catalog()` ships seven use
|
||
cases (Alzheimer's research, post-stroke cognition, sleep optimization,
|
||
attention/working-memory, mood/arousal, home wellness, drug+device trial
|
||
infrastructure), each with its **own** safety envelope, prior, objective
|
||
weighting, physiological-state gating (the sleep program permits `Asleep` and
|
||
caps brightness near-dark; attention requires wakefulness), evidence level, and
|
||
a single non-disease claim. `RufloGovernor::enroll_program` wires it all in;
|
||
`enroll` stays the bare Alzheimer's-defaults path (so the pinned witness holds).
|
||
|
||
**Claim discipline is executable.** A program's claim can only be read through
|
||
the acceptance gate:
|
||
|
||
```rust
|
||
use ruview_gamma::acceptance::{AcceptanceHarness, AcceptanceCriteria};
|
||
use ruview_gamma::program::NeuroProgram;
|
||
# use ruview_gamma::simulator::LatentPerson;
|
||
# use ruview_gamma::response::RuViewState;
|
||
|
||
let harness = AcceptanceHarness::new(42, AcceptanceCriteria::default());
|
||
let report = harness.evaluate(
|
||
&NeuroProgram::sleep_optimization(),
|
||
&LatentPerson::from_id("subject"),
|
||
&RuViewState::calm_baseline(),
|
||
);
|
||
// Returns the program claim ONLY if entrainment + safety + adherence +
|
||
// repeatability all pass; otherwise the research-only NO_CLAIM string.
|
||
let _claim = report.claim_gate().claim();
|
||
```
|
||
|
||
## Self-learning across people (ADR-250 §10)
|
||
|
||
`RufloGovernor::export_anonymized_profile()` publishes a participant's 20-field
|
||
vector + per-frequency scores from **safe sessions only** under a one-way hashed
|
||
tag; `seed_from_cohort(&store, k)` warm-starts a new person's optimizer from the
|
||
k nearest responders as **down-weighted pseudo-observations**
|
||
(`observe_prior`, ≥25× the real-observation noise). Priors shape where the
|
||
optimizer looks first but never count as measured data — they are excluded from
|
||
the EI incumbent, the audit log, and the clinician report. Per-session
|
||
`drift_status()` (Welford centroid over the *physiological* sub-vector —
|
||
stimulus inputs masked out) flags when recalibration is warranted.
|
||
|
||
## Roadmap (ADR-250 §21)
|
||
|
||
M1 simulator ✅ · M2 device harness (envelope + e-stop contract) ✅ · M3 RuView
|
||
state contract ✅ · M4 optional EEG input ✅ · M5 adaptive optimizer (BO + bandit
|
||
+ closed-loop) ✅ · M6 trial mode (sham/blinding + clinician export) ✅ ·
|
||
§10 RuVector self-learning (cohort warm-start, drift detection, clustering) ✅.
|
||
Hardware actuation, real RF sensing, and real EEG land behind feature-flagged
|
||
adapters. An HNSW backend (the `ruvector` crates) drops in for `ProfileStore`
|
||
once cohorts grow past ~10⁵ profiles.
|