Skip to content

feat: add trust-gated Rydberg quantum RF vector sensing - #1

Closed
ruvnet wants to merge 1 commit into
mainfrom
agent/quantum-rf-rydberg-adapter
Closed

feat: add trust-gated Rydberg quantum RF vector sensing#1
ruvnet wants to merge 1 commit into
mainfrom
agent/quantum-rf-rydberg-adapter

Conversation

@ruvnet

@ruvnet ruvnet commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a vendor-neutral, replay-first Rydberg quantum RF vector sensing contract to RuField, with a detailed architecture decision record and a fail-closed reference implementation.

  • Adds Modality::QuantumRf (wire code 16) plus Cartesian, complex, and direction-candidate tensor axes.
  • Adds optional typed sensor pose and signed string observation attributes.
  • Adds a strict RydbergReplayAdapter whose default output is a P1 antipodal bearing tensor; raw complex electric-field output is explicit P0.
  • Adds bounded, sign-invariant multi-sensor bearing fusion with uncertainty propagation and production trust policy.
  • Adds ADR-266 covering physics, schema, calibration, evidence, privacy, security, fusion, rollout, commercial feasibility, and acceptance gates.
  • Moves workspace crates to 0.2.0 for the Rust source-compatibility event while retaining wire identifier rufield.mfs.v0.1.

Evidence and claims boundary

This is a software contract and deterministic reference replay, not a hardware-performance claim.

  • The checked-in quantum RF fixture is analytic synthetic data.
  • No Rydberg receiver capture, live hardware adapter, angular-accuracy result, indoor multipath result, update-rate measurement, or commercial-readiness claim is included.
  • Analytic synthetic, captured replay, controlled lab, and field-pilot evidence are explicitly distinct.
  • The current viewer LIVE banner reports transport state and is not evidence-kind aware. Quantum RF replay is therefore forbidden through that source until replay-aware viewer states are implemented.

Wire and tensor contract

Default derived bearing:

  • axes: [direction_candidate, cartesian_component]
  • shape: [2, 3]
  • values: [+k, -k]
  • privacy: P1
  • frame: sensor-local, with exactly one typed pose transform downstream

Explicit raw mode:

  • axes: [cartesian_component, complex_component]
  • shape: [3, 2]
  • values: complex three-axis electric field
  • privacy: P0

Signed attributes bind signal grouping, tensor frame, evidence kind, calibration hash, and exact calibration validity timestamps. Legacy JSON remains readable because new pose and attribute fields use Serde defaults and are omitted when absent.

Validation and safety gates

Replay ingestion is bounded to 65,536 bytes per line, 100,000 frames, and 256-byte identifiers. It rejects malformed or non-finite input, timestamp disorder, invalid integration intervals, calibration expiry, optical-lock failure, low calibration quality, inadequate SNR/ellipticity, invalid covariance, inconsistent polarization/axis data, noncanonical f32 conversion, pose mismatches, and calibration-contract changes within a stream.

Captured replay requires an explicit nondefault signing seed. Its signature attests package integrity, not live hardware identity.

Production trust boundary

Production fusion is separate from simulation and captured-replay policy. It requires:

  • strict Ed25519 verification and weak/small-order public-key rejection;
  • pre-enrolled one-key-per-device bindings;
  • exact signer, device, coordinate frame, typed pose, calibration ID/hash/validity, and revocation checks;
  • signed evidence_kind=live;
  • trusted-time freshness and future-skew limits;
  • persistent per-device replay watermarks;
  • monotonic trusted-time updates only between empty fusion windows, preserving replay state.

Malformed signed observations do not consume replay watermarks.

Fusion

QuantumBearingFusion preserves the single-sensor k ↔ -k ambiguity and intersects lines through sign-invariant projectors. It rejects duplicate sensors, mismatched frames/signals/carriers, nonoverlapping half-open integration intervals, insufficient baseline, nearly parallel geometry, ill-conditioned information matrices, stale calibration, and already-expired estimates.

The solver performs range-aware weighting from angular covariance, retains absolute uncertainty scale, inflates covariance by reduced chi-square, and caps estimate lifetime at the earliest supporting calibration expiry. Quality and covariance remain explicitly uncalibrated until hardware validation.

Privacy and governance

  • Derived nonidentity bearings default to P1.
  • Raw complex field is P0 and denied network transmission by the default privacy policy.
  • Bearings become at least P2 when used for occupancy/behavior and P5 when identity-linked.
  • Quantum RF is excluded from safety, occupancy, vital-sign, pose, and identity rules by default.
  • Supporting event and calibration IDs remain attached to estimates.

Validation performed

  • cargo build --workspace --all-targets
  • cargo test --workspace — 144 passing tests including the doctest; zero failures
  • 10,000 deterministic physics/property cases
  • cargo test -p rufield-adapters --release --test quantum_rf_performance -- --nocapture
    • 10,000 frames × 3 trials
    • median 28,379 signed frames/s
    • median p95 conversion latency 45.3 µs
    • gates: >10,000 frames/s and <1 ms p95
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo run -p rufield-bench -- 2026
    • synthetic benchmark p95 0.0059 ms
    • 100% provenance coverage
    • 0 privacy violations
    • 7 distinct inferences

CI now runs the release performance gate and strict all-target clippy.

Compatibility and follow-up

Existing modality codes 1–15 and wire version remain unchanged. Code 16 is permanently assigned to quantum_rf. The Rust crates move to 0.2.0 because exhaustive enum matches and external struct literals require updates.

Before any live-product claim: implement evidence-aware viewer states, obtain a real receiver API and capture, establish calibration authority and device enrollment, run controlled direct-path and multipath trials, calibrate covariance coverage, and pass ADR-266's held-out RuView baseline gates.

@ruvnet

ruvnet commented Aug 24, 2026

Copy link
Copy Markdown
Owner Author

Reviewed for merge alongside #3, #4, #5 and #7. This one cannot be merged as-is, for two independent reasons. Flagging rather than forcing, because both failure modes are silent.

1. Wire-code collision on 16

This PR assigns:

Modality::QuantumRf => 16,

main now assigns the same code to a different modality, from #5:

Modality::BleAdvertisementRssi => 16,

Two modalities cannot share a wire code. Whichever way a merge resolved it, the result would be either a duplicate or a silent renumbering of a published protocol — and a renumber is not a compile error, it is a decoder in the field reading the wrong modality. QuantumRf needs a fresh code (17 is free) and the ADR updating to match.

2. No common ancestor with main

$ git merge-base <pr-head> origin/main
(no output — no common ancestor)

PR #1 root:  c6abe92
main root:   43b1df3   ("ci: pin GitHub Actions to immutable commits")

main's history was rewritten after this PR was opened, orphaning it. A merge needs --allow-unrelated-histories, and because git then has no base to diff against, every shared file conflicts wholesale — 33 of them, including crates/rufield-core/src/privacy.rs, modality.rs, event.rs, and crates/rufield-privacy/src/lib.rs.

Those are exactly the files #7 and #5 just changed. Resolving 33 whole-file conflicts against a 2026-07-12 snapshot is not a merge; it is hand-reconciling the repository, with a live risk of quietly reverting the composite-privacy authorization and the BLE evidence work by taking this branch's older copy of a file.

What is actually salvageable

The feature itself is well-contained. 13 of the changed files do not exist on main at all, and they are the substance:

crates/rufield-adapters/src/quantum_rf_{quality,replay,support,wire}.rs
crates/rufield-adapters/tests/quantum_rf_{performance,properties,replay}.rs
crates/rufield-adapters/tests/fixtures/synthetic_quantum_rf.jsonl
crates/rufield-fusion/src/{bearing,bearing_math,bearing_trust}.rs
crates/rufield-fusion/tests/quantum_bearing.rs
docs/ADR-266-quantum-rf-vector-sensing.md

The other 53 files are shared with main and are where the risk lives.

Suggested path: open a fresh branch from current main, copy those 13 files across, and re-apply the integration points by hand — the Modality::QuantumRf variant (at code 17), the Cartesian/complex/direction-candidate tensor axes, and the sensor-pose fields. That is a bounded, reviewable change against a tree that exists, rather than a graft against one that does not.

Happy to do that port if you want it — say the word and I will open it as a new PR. Leaving this one open and untouched in the meantime, since closing it would lose the reference.

ruvnet added a commit that referenced this pull request Aug 24, 2026
… main (#9)

Re-creates #1 against a tree that exists. That PR shares no commit ancestry
with `main` -- main's history was rewritten after it opened -- so merging it
would have needed --allow-unrelated-histories and conflicted on 33 files
wholesale, including the four that #5 and #7 had just changed. This carries the
feature across instead of grafting the branch.

Wire code moved from 16 to 17. #1 assigned `Modality::QuantumRf => 16`, which
`main` now gives to `BleAdvertisementRssi` (#5). Two modalities cannot share a
code, and a renumber is not a compile error -- it is a deployed decoder reading
the wrong modality -- so 16 stays where it was published and quantum RF takes
the next free code. `recent_wire_codes_are_pinned` asserts both by value, so a
future edit cannot quietly swap them.

Carried across unchanged (13 files, none of which exist on main):
  rufield-adapters: quantum_rf_{quality,replay,support,wire}.rs, three test
  suites, and the synthetic replay fixture
  rufield-fusion:   bearing.rs, bearing_math.rs, bearing_trust.rs, and the
  quantum_bearing test suite
  docs/ADR-270-quantum-rf-vector-sensing.md

Re-applied by hand, because these files exist on main and could not be taken
from the branch without reverting recent work:
  - `Modality::QuantumRf` (code 17, `quantum_rf`) plus the registry contract
    tests, which correctly refused the addition until updated.
  - `FieldAxis::{CartesianComponent, ComplexComponent, DirectionCandidate}`.
  - Optional sensor pose on `SensorDescriptor` -- coordinate_frame, position_m,
    orientation_xyzw -- all `#[serde(default, skip_serializing_if)]`, so events
    that omit them round-trip unchanged.
  - `Observation::attributes`, likewise absent from the wire when empty.
  - `normalize_verifying_key_hex` and `verifying_key_from_hex` in
    rufield-provenance.

`SensorDescriptor` loses its `Eq` derive: the pose carries f32 coordinates and
float equality is not an equivalence relation. `PartialEq` is retained.

ADR renumbered 266 -> 270. 266 is taken on main by field-evidence-promotion
(#4).

255 workspace tests pass, 38 of them from the ported suites (13 replay, 23
bearing, 2 properties; the performance gate stays #[ignore]d by its author,
requiring a release build). fmt and clippy -D warnings clean.


Claude-Session: https://claude.ai/code/session_016QSCkKnxDjqU49NVVpWMK5
@ruvnet

ruvnet commented Aug 24, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #9, now merged.

The feature is on main: all 13 files unique to this branch carried across unchanged, and the integration points re-applied by hand against the current tree — Modality::QuantumRf, the three FieldAxis variants, the optional sensor pose, Observation::attributes, and the provenance key helpers. 38 of its tests run and pass (13 replay, 23 bearing, 2 properties).

Two changes from this branch worth knowing about:

Wire code 16 → 17. main gave 16 to BleAdvertisementRssi in #5. Quantum RF now uses 17, and recent_wire_codes_are_pinned asserts both by value so a future edit cannot swap them silently.

ADR 266 → 270, since 266 is taken by field-evidence-promotion (#4).

Closing rather than merging because this branch has no common ancestor with main — its history was rewritten underneath it — so a merge would have conflicted on 33 files wholesale, including the four that #5 and #7 had just changed. The work is preserved; only the commit lineage is not.

Thanks — the is_weak() check in your key parsing is a real improvement over what main does in verify_event, and I have flagged that separately.

@ruvnet ruvnet closed this Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant