feat(agent-memory): TARL transactional memory ledger (PIR WP4, ADR-307) - #858
Conversation
Adds the TARL (Transaction-Aware Reliable Ledgers, arXiv:2608.03699) five-operation transactional ledger to ruvector-agent-memory, per ADR-307 and issues #839/#840: - src/ops.rs: MemoryOp (add / ignore / revise-outdated-belief / reject-unreliable / defer-for-verification), LedgerState (Accepted | Pending | Rejected), full-provenance TransitionRecord, and a serde-serializable LedgerWitnessRecord whose fields follow the ADR-134 witness schema (ADR-134-witness-schema-log-format.md) with an ADR-322C evidence-grade annotation bound into the hashed flags bits, plus a WitnessSink trait (no-op default, chain-verifying in-memory log). - src/ledger.rs: TransactionalLedger applying ops as explicit state transitions with stage -> witness -> commit discipline (no witness, no mutation; no observable partial application). Poisoning containment: revise/reject/defer on an accepted entry transitively demotes accepted dependents to Pending via lightweight dependency edges. Acceptance (Pending -> Accepted) routes through a ProofGate trait; the real ADR-194/047 machinery (ruvector-proof-gate HashChainGate/MerkleGate) is wired via WriteGateAdapter behind the `proof-gate` feature. - tests/tarl_ledger.rs: five-op state machine, poisoning containment, deny-all-gate adversarial check, defer round-trip, per-transition witness emission + tamper detection, history-replay reconstruction, and feature-gated real-gate integration. - Adds crates/ruvector-agent-memory to workspace members (it was previously neither in members nor exclude, so `cargo test -p ruvector-agent-memory` could not run at all) and fixes the clippy warnings that surfaced on joining. Refs #839 #840 Co-Authored-By: claude-flow <ruv@ruv.net>
Security audit — Phase 4 pre-merge reviewAudited at Verdict: FINDINGS — 1 high, 4 medium, 2 low. Checked and clean
HIGH — a mid-operation witness-sink failure permanently breaks the persisted chain
The emission loop persists records one at a time: for s in &staged {
self.sink.emit(&s.witness)?; // early return on failure
}
self.last_witness_hash = prev; // never reached on failure
self.witness_seq += staged.len() as u64;Multi-record operations are the norm —
PoC output (sink budget exhausted partway through Exploit scenario: an attacker who can induce sink write failures (fill the disk, sever the remote-append connection) at a chosen moment both injects a false state-transition record and permanently invalidates chain verification, defeating any later audit — while the ledger keeps accepting writes and reporting success. Suggested fix: make emission all-or-nothing. Either extend MEDIUM —
|
… and rollback Addresses the Phase-4 security audit on PR #858 (1 HIGH, 4 MED, 2 LOW): - HIGH atomic witness emission: WitnessSink now takes emit_batch() with an all-or-nothing contract (on Err the sink retains none of the batch); emit_and_apply emits each operation's records as one atomic batch, so a sink failure can no longer leave orphan records, reuse sequence numbers, or fork the persisted chain. emit() remains as a provided convenience. - MED invariant coverage: FailingSink tests prove a refused batch causes no mutation, no history append, no orphan record, and a still-verifying chain — including a mid-cascade dependent-demotion batch, with recovery showing dense sequences and no fork. LedgerError::WitnessRejected is now exercised. - MED tail truncation: MemoryWitnessLog maintains a head commitment (record count + full-64-byte chain hash of the newest record), checked by verify_chain(), so rollback of the newest records is detected; test added. - MED aux authentication: the head commitment covers all 64 bytes of the last record, closing the [56..64] aux gap without changing the ADR-134 48-byte record_hash preimage (supplementary commitment, schema-compatible). - MED keyless FNV: module and verify_chain docs now state the chain is tamper-evident against accidental corruption/naive edits only, and name the ADR-134 WitnessSigner wiring as a required gate before WP8 anchoring. - LOW evidence_grade drift: verify_chain cross-checks the serde field against flags bits 12-15; test added. - LOW gate asymmetry: accept() documents the granted-then-unwitnessed case and how to treat gate-vs-ledger sequence gaps during reconciliation. Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_012Jib2gQyJpqCoo2xYAbb4X
Security findings addressed —
|
Security re-verification — fix
|
Resolves the workspace-membership overlap with PR #861 (PIR #859 sweep): ruvector-agent-memory now appears exactly once in [workspace.members] (the mid-list TARL entry; the sweep's end-of-list merge-hedge duplicate is dropped, as its own comment prescribed). All of #861's other member and exclude additions are kept. Cargo.lock regenerated on main's lock; the only delta is ruvector-agent-memory's dependency set (ruvector-proof-gate, serde, serde_json). Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_012Jib2gQyJpqCoo2xYAbb4X
…(PIR ruvnet#859) 12 crates under crates/ had a Cargo.toml but were neither workspace members nor excluded, so their tests never ran in CI. Disposition: Added to members (build and test green): - ruvector-agent-memory (also added by PR ruvnet#858; duplicate entries merge cleanly, whichever lands second can drop one) - ruvector-bet4-ivf-bench - ruvector-hnsw-repair - ruvector-temporal-tensor-wasm Added to exclude with per-crate reasons (do not build — see ruvnet#859): - agentic-robotics-{core,rt,embedded,mcp,node,benchmarks}: authored against a different workspace root; workspace.package/dep inheritance fails at manifest parse - ruvector-attention-cli: 51 compile errors, API drift vs ruvector-attention 2.x plus missing bincode dep - ruvector-sparse-inference-wasm: 11 compile errors, API drift vs ruvector-sparse-inference scripts/workspace-check.mjs is the backstop: it fails when any crates/**/Cargo.toml is neither a member, nor excluded, nor under its own [workspace] (and flags member entries with no manifest on disk). Wired into Workspace CI as a fast Node-only job. The workspace keeps literal members entries (no crates/* glob): membership stays an explicit, reviewable decision, and the guard makes silent orphaning impossible. Refs ruvnet#859, ruvnet#837 Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_012Jib2gQyJpqCoo2xYAbb4X
Summary
Implements the PIR WP4 core slice (#840, depends-on/coordinates-with #839): the TARL (Transaction-Aware Reliable Ledgers) five-operation transactional memory ledger (arXiv:2608.03699) layered onto
crates/ruvector-agent-memory, per ADR-307 (docs/adr/ADR-307-three-level-persistent-memory-livemem-tarl.md, branchfeat/pir-adrs). WP3's three-tier architecture will sit on this ledger.What's in the slice
src/ops.rs—MemoryOpwith the five TARL operations (add/ignore/revise-outdated-belief/reject-unreliable/defer-for-verification),LedgerState(Accepted | Pending | Rejected, each distinctly queryable), full-provenanceTransitionRecord(op, prior state, new state, timestamp, reason, actor id, gate receipt), and the witness layer below.src/ledger.rs—TransactionalLedgerapplies every operation as an explicit state transition with full history retained. Each operation follows a stage → witness → commit discipline: all witness records for the operation (including containment cascades) are emitted before any state is applied, and a sink failure aborts the whole operation — no partial application is observable, and no mutation exists without a witness (ADR-134 INV-3 direction: an orphan witness without a mutation is possible on mid-emit failure; a mutation without a witness is not).depends_onedges; arevise,reject, ordeferon an Accepted entry transitively demotes every accepted dependent toPending(each demotion is its own witnessedDependentDemotiontransition), andacceptrefuses entries whose dependencies are not all Accepted. One bad update cannot silently keep downstream inferences accepted.How acceptance routes through the proof gate
Honest scoping note:
ruvector-agent-memoryhad no existing proof-gate usage (it wasn't even a workspace member — neither inmembersnorexclude, socargo test -p ruvector-agent-memorycould not run at all; this PR adds it to the workspace). Per the WP4 brief for that case, acceptance transitions (Pending → Accepted) are wired behind aProofGatetrait:proof-gate):WriteGateAdapteradaptsruvector-proof-gate'sWriteGate(HashChainGate/MerkleGate) — the existing ADR-194/047 machinery (ADR-194-proof-gated-writes.md,ADR-047-proof-gated-mutation-protocol.md) — so every acceptance is admitted by the gate and itsWriteReceipt(sequence + chain commitment) is retained in the transition's provenance and echoed into the witness record (capability_hash,aux). The gate is consulted before witnessing/committing; a denial mutates nothing.AlwaysAdmitGateexists for dev/tests and is documented as such; the adversarial test uses a deny-all gate to show denied writes never reach the accepted ledger.Witness records (ADR-134 schema + ADR-322C alignment)
Every transition emits a
LedgerWitnessRecordthrough aWitnessSinkhook (no-op default; chain-verifyingMemoryWitnessLogincluded). Fields, sizes, byte offsets, and FNV-1aprev_hash/record_hashchaining followdocs/adr/ADR-134-witness-schema-log-format.md; ledger transitions claim the unassigned0xA0-0xA6action-kind block. This slice deliberately does not depend on the rvm crates (RVM anchoring is WP8, cross-repo).ADR-322C alignment (program's canonical witness/receipt contract, per ADR-312): the record is canonical-JSON-friendly (integer-only fields, stable serde field order) and carries an
evidence_gradefield using the three-value vocabularyrecomputed/signature-verified/trusted-assertion, with the grade code bound into the hashedflagsbits so it cannot drift from the chained layout. Follow-up: full record-shape conformance to the ruflo ADR-322C contract spec being extracted in ruflo#3066 (RFC 8785 JCS canonicalization, SHA-256 digests, Ed25519 domain-separated signing) is explicitly deferred to that spec landing.Tests
18 existing tests (12 in
ruvector-agent-memory, kept green) + 8 new integration tests (10 with--features proof-gate):Acceptrecord in historyPending → Accepted → Pending → Accepted, independent gate receipts)HashChainGatewith full cryptographic chain re-derivationReferences
feat/pir-adrs),docs/adr/ADR-134-witness-schema-log-format.md,docs/adr/ADR-194-proof-gated-writes.md,docs/adr/ADR-047-proof-gated-mutation-protocol.md, ADR-252 (ADR-252-agent-memory-compaction.md)docs/research/perpetual-intelligence-runtime/01-evidence-review.mditem 4🤖 Generated with claude-flow