Accepted
2026-02-25
RuVector's graph transformer operates on mutable graph state -- nodes are added, edges are rewired, attention weights are updated, and topology evolves during self-organizing operations. In safety-critical deployments (genomic pipelines, financial computation, cognitive containers), every mutation must be auditable and formally justified.
The existing ruvector-verified crate provides ProofEnvironment, VerifiedOp<T>, ProofAttestation (82-byte witnesses), and three-tier proof routing (Reflex, Standard, Deep) in crates/ruvector-verified/src/gated.rs. However, there is no protocol for composing these primitives into a mutation control substrate -- no defined lifecycle for how a graph mutation acquires its proof, how local proofs compose into regional proofs, how proof scopes align with min-cut partition boundaries, or how the attestation chain grows without unbounded memory.
We need a protocol that makes "no proof, no mutation" the default, while keeping hot-path overhead below 2%.
We will implement the Proof-Gated Mutation Protocol as the proof_gated module within ruvector-graph-transformer. The protocol defines a type-level gate (ProofGate<T>), a scoping mechanism (ProofScope), a composition algebra for attestation chains, and epoch boundaries for protocol upgrades.
ProofGate<T> is a wrapper that makes the inner value inaccessible without a valid proof:
/// A value gated behind a machine-checked proof.
///
/// The inner `T` cannot be accessed without presenting a proof that
/// satisfies the gate's `ProofRequirement`. This is enforced at the
/// type level -- there is no `unsafe` escape hatch.
pub struct ProofGate<T> {
/// The gated value. Private -- only accessible via `unlock()`.
inner: T,
/// The proof requirement that must be satisfied.
requirement: ProofRequirement,
/// Attestation produced when the gate was satisfied.
attestation: Option<ProofAttestation>,
}
impl<T> ProofGate<T> {
/// Create a new proof gate with the given requirement.
pub fn new(value: T, requirement: ProofRequirement) -> Self;
/// Attempt to unlock the gate by providing a proof.
/// Returns `&T` on success, `Err(ProofGateRejected)` on failure.
pub fn unlock(&self, env: &mut ProofEnvironment) -> Result<&T>;
/// Consume the gate, returning the value and its attestation chain.
pub fn into_inner(self, env: &mut ProofEnvironment) -> Result<(T, ProofAttestation)>;
/// Check if this gate has been satisfied (attestation present).
pub fn is_satisfied(&self) -> bool;
}ProofRequirement is an enum that maps to ruvector-verified::gated::ProofKind:
pub enum ProofRequirement {
/// Dimension equality: vector has expected dimension.
DimensionMatch { expected: u32 },
/// Type constructor: node/edge type matches schema.
TypeMatch { schema_id: u64 },
/// Invariant preservation: graph property holds after mutation.
InvariantPreserved { invariant_id: u32 },
/// Coherence bound: attention coherence above threshold.
CoherenceBound { min_coherence: f64 },
/// Composition: all sub-requirements must be satisfied.
Composite(Vec<ProofRequirement>),
}Every mutation routes through the existing ruvector-verified::gated::route_proof function, which selects the cheapest sufficient proof tier:
| Tier | Target Latency | Use Case | Implementation |
|---|---|---|---|
| Reflex | < 10 ns | Dimension checks, reflexivity, literal equality | Direct comparison, no reduction engine. Maps to ProofTier::Reflex |
| Standard | < 1 us | Type application (depth <= 5), short pipelines (<=3 stages) | Bounded fuel via ProofTier::Standard { max_fuel }, auto-escalates on failure |
| Deep | < 100 us | Long pipelines, custom proofs, invariant verification | Full 10,000-step kernel via ProofTier::Deep |
Routing is automatic: the ProofRequirement is classified into a ProofKind, passed to route_proof(), and the returned TierDecision determines which verification path to take. If a tier fails, it escalates to the next tier (Reflex -> Standard -> Deep) via verify_tiered() as implemented in crates/ruvector-verified/src/gated.rs.
Each successful proof produces a ProofAttestation (82 bytes, defined in crates/ruvector-verified/src/proof_store.rs). Attestations are stored in a MutationLedger:
pub struct MutationLedger {
/// Append-only log of attestations for this scope.
attestations: Vec<ProofAttestation>,
/// Running content hash (FNV-1a) over all attestation bytes.
chain_hash: u64,
/// Epoch counter for proof algebra versioning.
epoch: u64,
/// Maximum attestations before compaction.
compaction_threshold: usize,
}
impl MutationLedger {
/// Append an attestation. Returns the chain position.
pub fn append(&mut self, att: ProofAttestation) -> u64;
/// Compact old attestations into a single summary attestation.
/// Preserves the chain hash but reduces memory.
pub fn compact(&mut self) -> ProofAttestation;
/// Verify the chain hash is consistent.
pub fn verify_integrity(&self) -> bool;
}Local proofs compose into regional proofs via compose_chain:
/// Compose a sequence of local proof attestations into a regional proof.
///
/// The regional proof's `proof_term_hash` is the hash of all constituent
/// attestation hashes. The `reduction_steps` field is the sum of all
/// constituent steps. This is sound because proofs are append-only and
/// each attestation covers a disjoint mutation.
pub fn compose_chain(attestations: &[ProofAttestation]) -> ProofAttestation;Composition respects partition boundaries: a ProofScope is defined by a min-cut partition (from ruvector-mincut), and proofs within a scope compose locally. Cross-scope composition requires a GlobalCoherenceProof that verifies the boundary edges between partitions maintain coherence above the threshold.
pub struct ProofScope {
/// Partition ID from ruvector-mincut.
partition_id: u32,
/// Boundary nodes shared with adjacent partitions.
boundary_nodes: Vec<u64>,
/// The ledger for this scope.
ledger: MutationLedger,
/// Coherence measurement for this scope.
coherence: Option<f64>,
}When the graph self-organizes (topology changes via ruvector-mincut), proof scopes are re-derived from the new partition. Attestations from the old scope are sealed with a ScopeTransitionAttestation that records the old and new partition IDs, the min-cut value at transition, and the composition proof of the old scope.
Attestations are append-only. There is no delete operation on the MutationLedger. Rollback is achieved by appending a supersession proof -- a new attestation that proves the rolled-back state is valid, referencing the original attestation by position:
pub struct SupersessionProof {
/// Position of the attestation being superseded.
superseded_position: u64,
/// The new attestation that replaces it.
replacement: ProofAttestation,
/// Proof that the replacement is sound (e.g., inverse mutation).
soundness_proof_id: u32,
}The proof algebra may be upgraded (new invariants, changed reduction limits, new built-in symbols). Epoch boundaries are explicit:
pub struct EpochBoundary {
/// Previous epoch number.
from_epoch: u64,
/// New epoch number.
to_epoch: u64,
/// Summary attestation sealing all proofs in the previous epoch.
seal: ProofAttestation,
/// New proof environment configuration.
new_config: ProofEnvironmentConfig,
}At an epoch boundary, the MutationLedger is compacted, a seal attestation is produced, and the ProofEnvironment is reconfigured with new symbols and fuel budgets. Old proofs remain valid (sealed) but new proofs use the updated algebra.
The target is less than 2% overhead on the hot path. This is achieved by:
- Reflex tier dominance: In steady-state graph transformer inference, 90%+ of mutations are dimension checks and reflexivity proofs, which route to Reflex (< 10 ns)
- FastTermArena: Bump allocation with O(1) dedup from
crates/ruvector-verified/src/fast_arena.rsavoids heap allocation - Proof caching:
ProofEnvironment::cache_lookupavoids re-proving identical obligations - Lazy attestation:
ProofAttestationis constructed only when the caller requestsproof_chain(), not on every mutation - Batch gating: Multiple mutations within a single forward pass share one
ProofScope, amortizing the scope setup cost
Benchmarks must demonstrate: Reflex < 10 ns, Standard < 1 us, Deep < 100 us, composition of 1000 attestations < 50 us, ledger compaction of 10,000 entries < 1 ms.
- Every graph mutation carries a machine-checked proof -- auditable, reproducible, and tamper-evident
- Three-tier routing keeps the common case (Reflex) at near-zero cost
- Attestation chains provide a complete audit trail for compliance (GDPR provenance, SOC2 audit logs)
- Epoch boundaries allow upgrading the proof system without invalidating historical proofs
- Monotonic semantics prevent accidental attestation loss
ProofGate<T>adds one level of indirection to every graph access- Developers must reason about
ProofRequirementwhen defining new mutation types - Supersession proofs add complexity compared to simple deletion
- The
MutationLedgergrows linearly with mutations until compaction (mitigated by compaction threshold)
- If Reflex tier coverage drops below 90%, the 2% overhead budget may be exceeded. Mitigated by monitoring
ProofStats::cache_hitsratio in production - Attestation chain integrity depends on FNV-1a hash -- not cryptographically secure. For production audit trails, upgrade to BLAKE3 (available via
ruvector-graph'sblake3dependency) - Epoch boundary migration is a manual operation -- if forgotten, the ledger grows unbounded. Mitigated by a configurable auto-epoch threshold in
GraphTransformerConfig
- Implement
ProofGate<T>andProofRequirementincrates/ruvector-graph-transformer/src/proof_gated/gate.rs - Implement
MutationLedgerwith append, compact, and verify incrates/ruvector-graph-transformer/src/proof_gated/mod.rs - Implement
compose_chainandProofScopeincrates/ruvector-graph-transformer/src/proof_gated/attestation.rs - Implement
EpochBoundaryincrates/ruvector-graph-transformer/src/proof_gated/epoch.rs - Add benchmark suite:
benches/proof_gate_bench.rscovering all three tiers, composition, and compaction - Integration test: full forward pass with 10,000 mutations, verifying attestation chain integrity
- ADR-045: Lean-Agentic Integration (establishes
ProofEnvironment,ProofAttestation,FastTermArena) - ADR-046: Graph Transformer Unified Architecture (module structure)
crates/ruvector-verified/src/gated.rs:ProofTier,ProofKind,route_proof,verify_tieredcrates/ruvector-verified/src/proof_store.rs:ProofAttestation,ATTESTATION_SIZE(82 bytes)crates/ruvector-verified/src/fast_arena.rs:FastTermArena, bump allocation with FxHash dedupcrates/ruvector-verified/src/error.rs:VerificationErrorvariantscrates/ruvector-mincut/Cargo.toml:canonicalfeature for pseudo-deterministic min-cutcrates/ruvector-mincut-gated-transformer/src/energy_gate.rs:EnergyGatedecision model