Files
RuView/docs/adr/ADR-350-ruvector-predictive-memory-and-ruvllm-boundary.md
T
rUv e5bf0d4892 feat(forecast): add independent Rust multivariate training stack (#1766)
* feat(forecast): add independent Rust training stack

Signed-off-by: Codex <codex@openai.com>

* docs(forecast): record informal HPO exploration note (unaccepted)

Records an exploratory Darwin Mode numeric-genome hyperparameter search
against the tiny_ci synthetic accuracy protocol: WQL improved 0.257 -> 0.161
-> 0.153 over two search rounds, staying ahead of both baselines throughout.
Explicitly scoped as informal/exploratory (not a frozen leakage-free report,
not eligible for the evidence ledger, not validated for large_linux or any
real dataset) per this doc's own evidence-tagging discipline.

* feat(forecast-core): add per-horizon weighted quantile loss

weighted_quantile_loss collapses the whole horizon into one aggregate
number, hiding whether error grows with lead time. Add a sibling
function using the identical per-cell pinball formula and domain
checks, just reduced per horizon step instead of globally. A unit
test reconstructs the aggregate from the per-step numerators/
denominators to prove the two functions agree exactly, not just
approximately.

* feat(forecast-model): add evaluation-only local activation

Adds activate_for_evaluation and build_eval_input, gated behind the
cpu feature. activate_for_evaluation self-signs an unsigned candidate
with a fixed, publicly-known, non-secret Ed25519 key so a CLI operator
can run inference against their own just-trained candidate without a
real release signature -- explicitly never a production trust path,
documented as such in the module doc comment. The schema-digest check
in ArtifactActivationPolicy still applies in full; only signing is
relaxed. build_eval_input constructs a single-window ModelInput for
CPU inference from raw context-major values/mask arrays, matching the
training batch builders exact time-feature/descriptor encoding.

Six unit tests cover: malformed-candidate rejection, digest-mismatch
rejection (proving the schema check has teeth even on this relaxed
path), getting past the policy gate with a matching digest, and
eval-input shape validation.

* feat(forecast-train): add evaluate and prepare-synthetic-dataset CLI

Turns this sessions throwaway HPO scripts into real, tested CLI
surfaces, replacing two example binaries that bypassed the CLI/TOML
training path entirely.

prepare-synthetic-dataset generates a larger, configurable synthetic
training shard (default 24 windows) plus a matching train-local.toml
with the given OptimizerSpec hyperparameters baked in, and a separate
held-out test.jsonl -- the same synthetic-only, local-validation-only
posture as prepare-local-example, just bigger and configurable.

evaluate scores a trained (unsigned) candidate against held-out
windows and the LastValue/SeasonalNaive baselines: overall weighted
quantile loss, a per-horizon breakdown, 80% interval coverage, and
missingness, for the model and both baselines. Explicitly lists what
docs/benchmarks/ruforecast.mds full accuracy protocol additionally
asks for that this does not cover (abstention coverage, selective
risk, site/device slices, interference regime, RuVector-retrieval
ablation) rather than silently omitting them -- each needs
infrastructure a single-entity synthetic fixture does not have.

Together the three real commands (prepare-synthetic-dataset,
train-local, evaluate) reproduce the exact WQL numbers the old
library-bypassing example scripts produced for the same
hyperparameters, confirmed by re-running the harness/ruview HPO
dry-run end to end against the new CLI path.

New integration test trains a real candidate via smoke, evaluates it
against held-out synthetic windows, and asserts the full report shape
and an empty-input rejection.

* feat(forecast-train): add prepare-synthetic-dataset --seed + Autogenous bridge

--seed (default 0, backward compatible) makes prepare-synthetic-dataset's
generator deterministic per offset: same seed -> byte-identical corpus
across runs (needed to train two genomes on the SAME corpus for a fair
comparison); different seeds -> genuinely independent corpora (needed for
honest multi-seed/multi-judge evaluation upstream, both in
harness/ruview/flywheel/ruforecast/gate.mjs's fitness function and in the
new v2/crates/ruforecast-autogenous-bridge crate added here).

ruforecast-autogenous-bridge is a LOCAL-DEV-ONLY (excluded from the v2
workspace, publish = false) bridge from this crate's real evaluate CLI
into ruvnet/autogenous's new regression-candidate promotion path (separate,
unpushed branch feat/regression-candidate-kind): runs N independent
train+evaluate judges on genuinely distinct synthetic corpora, signs
receipts, and gets a real cryptographically-verified PROMOTE/REJECT
decision -- defense in depth on top of, not a replacement for, Darwin's own
promotion gate. Path-depends on a sibling autogenous checkout that does not
exist in CI; build/run directly via `cargo build --manifest-path
crates/ruforecast-autogenous-bridge/Cargo.toml`.

cargo test -p ruview-forecast-train --no-default-features --features
cpu,cli: all green (unaffected existing tests + this backward-compatible
addition).

* docs(forecast): retract round-2 HPO result, record honest multi-seed finding

The earlier "Informal HPO exploration note" claimed a real improvement
(WQL 0.257 -> 0.153) from a 3-round hyperparameter search. That search
evaluated every candidate against one fixed synthetic corpus (seed 0) for
every round -- textbook overfitting. Independent verification against two
fresh corpora (via the new ruforecast-autogenous-bridge crate) showed the
"winner" losing to the baseline on both.

Fixed the root cause in harness/ruview/flywheel/ruforecast/gate.mjs:
candidates are now scored against three independent corpora, worst-case
across them, not one fixed corpus.

Re-ran the search under the fix. It found a new winner that genuinely beat
baseline on all three of its own search seeds -- and that winner ALSO lost
independent verification on fresh seeds. Two independent search rounds,
pre- and post-fix, both produced an illusory "winner." The honest reading:
at this dataset scale (24 synthetic windows) held-out WQL varies enormously
by which corpus is drawn, regardless of hyperparameters -- confirmed
directly by the baseline genome's own primary swinging from 0.83 to a full
regression across the three fixed search seeds with unchanged
hyperparameters. No RuForecast hyperparameter configuration has been shown
to reliably beat the trivial baselines out-of-sample at this scale.

Also fixes a real design inconsistency surfaced by this exploration: the
Autogenous regression-candidate promotion verifier
(envelope::regression::verify_regression_promotion, separate unpushed
ruvnet/autogenous branch feat/regression-candidate-kind) required all
judges to share one corpus_id, which conflicts with this kind's
intentional cross-corpus judge design. Corrected there (commit bfa4c48);
did not change either REJECT verdict, which were already driven by the
real NotBetterThanParent signal on their own.

Append-only: the original round-2 row is kept, read together with this
new amendment section, per this doc's own evidence-ledger discipline.

* refactor(forecast): extract RuForecast to ruvnet/RuForecast submodule

Mirrors the v2/crates/worldgraph pattern: RuForecast becomes its own
independent public repo/workspace (ruforecast-core/model/train), mounted
at v2/crates/ruforecast as a git submodule, with v2/Cargo.toml path-depping
into its sub-crates and excluding the submodule from the v2 workspace.

Full v2 --workspace check passes; the ruforecast CLI builds and runs
correctly from its new location. Real git history for the extracted
crates (7 commits) was preserved via git-filter-repo, not squashed.

* feat(autogenous-bridge): real-data independent verification tool

real_data_verify.rs: signs two real, temporally-independent judge
measurements (different train/test split boundaries on the same real
household vitals corpus, not synthetic seeds) through the real
Autogenous regression-candidate promotion path, and gets a genuine
cryptographically-checked PROMOTE/REJECT verdict.

Result recorded (see docs/benchmarks/ruforecast.md): REJECT. One judge
nominally beat the trivial baseline but by less than the 0.01
non-inferiority margin; the other judge lost outright. Consistent with
every synthetic-data search this session -- no configuration has yet
been shown to reliably beat trivial baselines out-of-sample, now
including a real 6390-sample household corpus.

Bumps the ruforecast submodule to pick up the evaluate real-gap fix.

* docs(forecast): record real-household-data result (REJECT, signed)

6390 real vital-signs samples, two independent temporal splits,
independently verified via the real Autogenous regression-candidate
promotion path. Signed verdict: REJECT -- neither split cleared the
non-inferiority margin. Same conclusion as every synthetic search this
session, now confirmed with real data too: more real data is the
credible next lever, not further search on this scale of fixture.

* chore(forecast): point ruforecast submodule at published main (post gap-tolerance fix)

* ci(forecast): fix ruforecast-ci.yml for the new v2/crates/ruforecast submodule layout

The RuForecast crates were extracted into a standalone submodule (v2/crates/ruforecast, mirroring the existing v2/crates/worldgraph pattern) and are no longer members of the v2 workspace. The CI workflow still referenced the old in-tree package names/paths (ruview-forecast-core/model/train under v2/), which broke every forecast job with "cannot specify features for packages outside of workspace".

Fixed every job to target the submodule: working-directory -> v2/crates/ruforecast, -p ruview-forecast-* -> -p ruforecast-*, all hardcoded Cargo.toml/source paths in the Python assertion and clean-room-scan blocks, the Swatinem/rust-cache workspaces input, the artifact-tree output paths, and the two levels of relative path (../ -> ../../) in the informational-benchmark steps whose working directory moved one level deeper.

Also collapsed the paths: trigger filters internal-crate globs (v2/crates/ruview-forecast-*/**) down to the single v2/crates/ruforecast path, since GitHub Actions path filters only ever see a submodule gitlink change as one entry in the parent tree, never its internal file paths -- the old globs could never have matched anything.

Includes a real cargo fmt fix (submodule commit d9902f9) for formatting drift left over from the earlier real-data gap-tolerance fix, which the contract job's fmt --check step would otherwise have failed on.

All four forecast CI jobs' real commands verified locally before push: cargo test/check for CPU, CUDA/WGPU compile-check, the contract job's Python assertion block, the hosted-boundaries clean-room scan, and cargo fmt --check.

* docs(forecast): record real BIDMC cross-entity holdout result (REJECT)

Two independent real-patient partitions (34/16 and 24/26 splits, 53 total
ICU patients from PhysioNet BIDMC, Open Data Commons Attribution License
v1.0) both show the model losing to trivial baselines by a decisive margin
(+69% and +601% worse WQL). First genuine cross-entity real-data test this
project has run; same conclusion as every prior synthetic and single-household
test. Full Autogenous signed verification was not run for this entry -
flagged explicitly in the note.

* chore(forecast): point ruforecast submodule at published main (example feature-gate fix)

* chore(forecast): point ruforecast submodule at published main (clippy fix)

---------

Signed-off-by: Codex <codex@openai.com>
2026-09-01 23:35:42 -04:00

12 KiB

ADR-350: RuVector predictive memory and RuVLLM authority boundary

  • Status: Proposed
  • Date: 2026-09-01
  • Deciders: ruv
  • Owners: RuView forecast, RuVector, evidence, and RuVLLM integration maintainers
  • Tags: forecasting, ruvector, ruvllm, retrieval, memory, receipts, authority
  • Parent: ADR-348
  • Extends: ADR-004, ADR-010, ADR-016, ADR-145, ADR-261, ADR-295, ADR-304, ADR-319, ADR-348
  • Supersedes: None

Decision

RuVector may augment RuView Forecast by retrieving analogous historical feature/forecast states from a tenant- and split-scoped predictive-memory index. Retrieval is optional context, never ground truth. Every release must report the same frozen examples with retrieval disabled and enabled so its incremental value and leakage risk remain visible.

Each forecast is published in an immutable, locally signed envelope binding the model artifact, request, output, source evidence, and retrieval receipt. RuVLLM may read that envelope to explain uncertainty and analogous history. It may not rewrite numeric forecasts, create a stronger evidence class, select a model, promote an artifact, spend money, invoke an actuator, or make medical, emergency, access-control, or life-safety decisions.

This child ADR delegates forecasting, clean-room, data, and general production gates to ADR-348. It does not authorize a sensing-server bridge.

Evidence status: canonical forecast/receipt types and an indexable latent state exist in source. A RuVector adapter, retrieval receipt, signature wrapper, outcome reconciler, RuVLLM explanation adapter, paired retrieval evaluation, latency, storage cost, accuracy lift, and operational value are all UNMEASURED and unapproved.

Existing implementation boundary

The current crates establish only the preconditions:

Module Implemented surface used by this ADR
ruview-forecast-core/src/forecast.rs validated ForecastRequest, backend-neutral Forecaster, ordered finite Forecast, canonical output digest, and receipt verification
ruview-forecast-core/src/receipt.rs SourceState, ArtifactReceipt, and ForecastReceipt; derived forecasts cannot retain MEASURED merely because measured input existed
ruview-forecast-core/src/series.rs bounded feature schema, masked time series, canonical series digest, and training-only scaler fit
ruview-forecast-model/src/network.rs ForecastModelOutput.state with shape [batch, variates, d_model], intended as a bounded representation candidate for indexing

ForecastReceipt is content-addressed but not itself a digital signature. ForecastModelOutput.state is indexable but not automatically safe, private, stable across model versions, or useful. Those distinctions remain release gates.

Predictive-memory record

RuVector stores a versioned PredictiveMemoryRecord containing:

  • tenant and index namespace;
  • model/artifact/config/feature-schema digests;
  • source series and forecast-request digests;
  • split, site, subject-class, session, device, calibration, and bounded time scope using pseudonymous identifiers;
  • bounded latent-state or engineered-feature digest plus the approved vector;
  • forecast envelope digest and horizon;
  • observed validity mask and evidence class;
  • optional reconciled outcome digest added only after the forecast horizon;
  • retention/deletion class and creation/expiry time.

Raw CSI, unrestricted feature windows, precise room coordinates, persistent person identity, RuVLLM prompts, and explanation prose are excluded by default. The vector is still potentially sensitive because it can encode routines or location. Tenant isolation, encryption, access control, retention, deletion, and membership-inference review apply.

An outcome is appended through a new immutable record linked to the original; the historical forecast is never rewritten. Outcome reconciliation can measure forecast quality but cannot retroactively turn the forecast into a measured observation.

Split-scoped analogue retrieval

Every index is bound to an immutable corpus manifest, model version, feature schema, preprocessing configuration, and split policy. These rules are mandatory:

  1. Tenants never share an index or query result without a separately authorized privacy-preserving federation protocol.
  2. Training, validation, calibration, and test records have distinct namespaces. A test query may retrieve approved training analogues only; it may not retrieve test examples or validation/calibration records used to set thresholds.
  3. The same site, subject, session, device, calibration episode, overlapping context, or overlapping target horizon is excluded when that dimension is a holdout.
  4. Index construction, distance metric, normalization, filter policy, k, and score threshold are fitted on training data and frozen before test.
  5. Missing, stale, mismatched, unauthorized, or unverifiable indexes disable retrieval and return the no-retrieval forecast or abstention according to the model card. They never trigger an implicit global-index fallback.
  6. Every query returns a bounded RetrievalReceipt even when zero neighbours qualify.

The retrieval receipt binds query digest, index/corpus/policy digests, namespace, exclusion filter, neighbour record IDs and distances, k, latency, and disposition. It excludes raw neighbour payloads from ordinary logs.

Paired ablation

Retrieval has no deployment authority without a paired evaluation on identical frozen requests:

Row Model and examples Retrieval
A exact candidate artifact and frozen examples disabled
B exact candidate artifact and frozen examples enabled with frozen index/policy

The report includes weighted quantile loss, interval coverage/width, abstention, per-horizon error, per-site/device/interference slices, retrieval hit/filtered/empty rates, latency, and memory/storage overhead. It reports both aggregate and paired deltas with uncertainty. A gain on pooled error cannot hide a failed deployment domain, calibration regression, or neighbour leakage.

The no-retrieval row remains a supported fallback. Retrieval is removed when its lower confidence bound does not show useful improvement, when it weakens calibration beyond the ADR-348 gate, or when its privacy/latency/storage cost exceeds its measured value. No uplift is claimed today.

Immutable signed forecast envelope

The signed envelope covers:

envelope schema/version
+ ArtifactReceipt canonical digest
+ ForecastRequest canonical digest
+ Forecast payload/output digest
+ ForecastReceipt canonical digest
+ RetrievalReceipt digest or explicit retrieval-disabled marker
+ policy/calibration/index digests
+ creation/expiry time and tenant namespace

Signing occurs at the trusted local boundary after artifact and retrieval verification. The wrapper records algorithm, public key ID, signer capability, and signature. Private keys never enter model artifacts, RuVector, RuVLLM, or a hosted training worker.

Consumers verify signature, expiry, tenant, schemas, every nested digest, and the Forecast::verify_receipt invariant before using numeric values. Any failure yields unavailable/abstain. Unsigned content-addressed receipts remain useful for local tests but are not described as signed and cannot cross the production trust boundary.

RuVLLM explanation boundary

RuVLLM receives a read-only projection of the verified envelope:

  • exact point and quantile values as structured fields;
  • units, horizons, calibration/OOD/abstention state;
  • bounded analogue summaries and retrieval receipt references;
  • evidence labels, model/version, and envelope digest;
  • approved explanation policy and audience.

Explanation prose is stored separately and linked to the envelope digest. It is CLAIMED_EXPLANATION, not a replacement forecast. Numeric API fields are copied from the verified envelope after generation, never parsed back from LLM text. If prose contradicts a structured number, evidence state, unit, or disposition, the response fails validation and the numeric envelope remains authoritative.

The explanation capability exposes no tools for artifact activation, model promotion, retraining, spending, messaging, emergency dispatch, access control, or actuation. A separate downstream policy may consume a forecast only under its own ADR, capabilities, approvals, and observed-evidence requirements. The LLM cannot grant itself that authority or lower an approval threshold.

Requirements and acceptance

ID Requirement ADR-348 gate Acceptance evidence Current state
PM-001 Every predictive-memory query is tenant-, model-, schema-, corpus-, split-, and time-scope bound. G3, G5 Cross-tenant/split/version negative tests and signed index manifest OPEN / UNMEASURED
PM-002 Holdout identities, overlapping contexts, and target horizons cannot appear as neighbours. G3 Property tests plus frozen leakage report OPEN / UNMEASURED
PM-003 Every query, including disabled/empty/error, produces a bounded retrieval receipt. G2, G3 Receipt round-trip, tamper, bound, and zero-result tests OPEN / UNMEASURED
PM-004 Retrieval-disabled and retrieval-enabled rows use identical artifact/examples and report paired metrics and overhead. G3 Frozen paired ablation with reproducer OPEN / UNMEASURED
PM-005 Forecast envelope signatures bind all nested forecast/retrieval identities and fail closed on mutation, expiry, tenant, or key error. G2, G5 Signature/tamper/replay/cross-tenant tests and local signing receipt OPEN / UNMEASURED
PM-006 Derived forecast and explanation can never become MEASURED_OBSERVATION or exceed source evidence. G2, G4 Evidence-monotonicity schema/property tests OPEN / UNMEASURED
PM-007 RuVLLM cannot mutate structured numbers; contradictions fail validation and explanations remain separately labelled. G4 Numeric/unit/evidence mutation corpus and fail-closed integration tests OPEN / UNMEASURED
PM-008 RuVLLM explanation has no spending, promotion, messaging, safety, access-control, or actuator capability. G4, G5 Default-deny capability and attempted-escalation tests OPEN / UNMEASURED
PM-009 Vector/explanation privacy, retention, deletion, extraction, and membership risks are approved and operationally tested. G5 Privacy review, tenant deletion drill, and access audit OPEN / UNMEASURED

G3 additionally requires the paired retrieval ablation to satisfy ADR-348's forecast and calibration gates. G4 requires shadow evidence that retrieval and explanation improve operator comprehension or forecast value without changing numeric/action authority. G5 requires signed-envelope verification, rollback, tenant deletion, and key-rotation drills. None has passed.

Consequences

RuVector can evolve from retrospective search into outcome-linked predictive memory while preserving a measurable no-retrieval baseline. RuVLLM can make forecasts understandable without becoming the numeric or action authority. The cost is additional index isolation, storage, signature, deletion, evaluation, and policy complexity. If paired evidence does not justify that cost, the correct release keeps retrieval and explanation disabled.

References