weavatrix-memory 0.3.3

Event-sourced bitemporal evidence memory and budgeted context retrieval for coding agents
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
# Weavatrix Memory

[![CI](https://github.com/Weavatrix/weavatrix-memory/actions/workflows/ci.yml/badge.svg)](https://github.com/Weavatrix/weavatrix-memory/actions/workflows/ci.yml)
[![crates.io](https://img.shields.io/crates/v/weavatrix-memory.svg)](https://crates.io/crates/weavatrix-memory)
[![docs.rs](https://docs.rs/weavatrix-memory/badge.svg)](https://docs.rs/weavatrix-memory)
[![npm](https://img.shields.io/npm/v/weavatrix-memory.svg)](https://www.npmjs.com/package/weavatrix-memory)
[![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

Part of the [Weavatrix ecosystem](https://weavatrix.com/ecosystem): revision-aware memory and bounded context for AI agents.

`weavatrix-memory` is the event-sourced temporal evidence and context-retrieval
engine used by Weavatrix. It turns immutable code, task, decision, test,
failure, and handoff events into small evidence-carrying graphs for a specific
task and token budget.

The crate is a standalone MIT-licensed Rust library. `weavatrix-rust` composes
it into the wider repository-intelligence engine, while the `weavatrix`
product exposes that engine through MCP. This crate owns temporal memory,
projection, persistence contracts, retrieval, and context compilation; it does
not own MCP transport or a long-running server.

Its deterministic core does not require an LLM, vector database, external
graph database, async runtime, system clock, or Git executable. Durable storage
is available without requiring a database.
The minimum supported Rust version is 1.89.

## Why it exists

Generic agent memory usually answers “what looks similar?” Engineering work
also needs exact answers to:

- What was true on this branch at that time?
- What did the agent know when it made the decision?
- Was this approach already tried and why did it fail?
- Which commit, symbol, test, benchmark, or user correction supports the fact?
- What is the smallest reproducible evidence graph that fits this context
  window?

Weavatrix Memory keeps the immutable history and derives a fresh graph for each
query instead of mutating one opaque “current truth”.

## Current guarantees

- Atomic per-stream append.
- Owned-batch append path that avoids cloning caller-owned payloads.
- Receipt-only append path that moves committed payloads directly into storage.
- Optimistic concurrency with `NoStream`, `Exact`, and `Any` expectations.
- Globally ordered cursors and independently versioned streams.
- `commit_journal` for multi-stream supplied events without forging cursors.
- Globally unique event identifiers.
- Strict replay validation for cursor gaps and stream-version gaps.
- Framed filesystem journal with table-driven CRC32C corruption detection.
- Standard-library exclusive writer locks and `SyncAll` durability by default.
- Strict open by default and explicit recovery for incomplete trailing batches.
- Immutable generation-named projection snapshots and validated resume.
- Explicit-ack catch-up subscriptions with redelivery before acknowledgement.
- Caller-provided timestamps and identifiers for reproducible tests.
- Separate valid-time and known-time queries.
- Evidence required for every memory relation and retraction.
- Explicit supersession without deleting historical facts.
- Versioned retraction history so later corrections keep earlier knowledge cuts.
- Validated bulk projection with compact dual-CSR incident indexes.
- Borrowed temporal views for zero-copy lexical and vector indexing.
- Deterministic projection into canonical `weavatrix-graph` snapshots.
- Provider-neutral literal, lexical, semantic, and hybrid retrieval with
  deterministic reciprocal-rank fusion.
- Provider-neutral auto-extraction with strict, scope-aware entity linking and
  idempotent event plans.
- Hard context budget with a replaceable token estimator and compilation
  receipt.
- Repository and branch-scoped projections.
- Dependency-free, versioned compact binary projection snapshots.
- Optional size-bounded LZ4 envelopes that keep incompressible input raw.
- Optional XChaCha20-Poly1305 authenticated encryption with key identifiers,
  purpose-bound AAD, and OS-generated nonces.
- Optional guarded mmap reads for immutable snapshot generations.
- Belief revision, reasoning-gap, drift, and consolidation analysis over the
  canonical graph.
- Exact-evidence retrieval metrics and adapters for public memory benchmarks.

## Architecture

```text
source / AST / issue / agent observation
        |
        v
ExtractionProvider -> strict EntityLinker -> reviewed event plan
        |
        v
append-only events
        |
        v
in-memory or framed file event store
        |
        v
strict replay validator + optional snapshot resume
        |
        v
bitemporal memory projection
        |
        +<-- lexical / semantic RetrievalProvider
        |
        v
immutable weavatrix-graph snapshot
        |
        +--> belief / gap / drift / consolidation reports
        |
        v
budgeted context compiler + receipt
```

The in-memory and filesystem stores implement the same `EventStore` contract.
Database, Git, lexical, semantic, and MCP adapters belong behind separate
interfaces or higher-level Weavatrix modules.

The source tree follows enforced inward dependencies:

| Layer | Owns | Allowed inward dependencies |
| --- | --- | --- |
| Temporal model | IDs, timestamps, events, evidence, facts, views, shared errors | None |
| Projection | Replay cursors, bitemporal projection, graph projection | Temporal model |
| Storage | Codecs, event stores, snapshots, compact snapshot adapter | Temporal model, projection |
| Retrieval | Context compilation, rank fusion, analytics, evaluation | Temporal model, projection |
| Extraction | Provider port, entity linker, validated event plans | Temporal model, projection |
| Facade | Stable crate-root exports | All internal layers |

Internal modules import the owning module rather than routing dependencies
through the public facade. `.weavatrix/architecture.json` enforces zero runtime
cycles, no dependency exceptions or baseline debt, at most 300 physical lines
per Rust file, and at most 100 physical lines per function. Integration tests,
benchmarks, and the evaluation CLI are verification adapters outside the
runtime layer graph.

Use `append` when the pending events must remain available to the caller. Use
`append_owned` to transfer a batch into the store without cloning its input
payloads. Use `append_owned_receipt` when the caller only needs committed
positions; this avoids cloning committed payloads back out of the store.

## Durable storage

`FileEventStore` writes each append batch as one checksummed frame. Memory state
is updated only after the complete frame has been written and flushed or synced.
On reopen, the journal rebuilds and validates global positions, stream versions,
and event identifiers.

The default `RecoveryPolicy::Strict` rejects any truncated tail. Explicit
`TruncatePartialTail` recovery removes only an incomplete final batch. Invalid
headers, impossible sizes, malformed payloads, and checksum failures are never
silently repaired.

The file store is intentionally one-writer-per-journal. It acquires an
OS-backed exclusive lock through the Rust standard library and retains the
file-length guard against non-cooperating writers. A competing `FileEventStore`
fails before scanning or appending, and the OS releases the lock after process
termination. Applications needing coordinated concurrent writers should
provide a database-backed `EventStore`.

`Durability::SyncAll` is the default and syncs file data plus metadata.
`SyncData` and `Flush` remain explicit lower-cost choices for workloads with
weaker persistence requirements.

Serialization is injected through the `Codec<T>` trait. The crate has no
default codec, compression, encryption, or mmap dependency. Storage features
are opt-in:

| Feature | Adds |
| --- | --- |
| `json` | `JsonCodec`, powered by Tokio-free `blazingly-json` |
| `compression` | Size-bounded `Lz4Codec<C>` |
| `encryption` | `XChaCha20Codec<C, K>` and key-provider contracts |
| `mmap` | Guarded read-only snapshot mapping |
| `secure-storage` | `compression`, `encryption`, and `mmap` |

The `json` feature has no direct or runtime `serde_json` dependency.
`blazingly-json` implements the same Serde-based `JsonCodec` contract, so stored
JSON and the public codec API remain unchanged.

```toml
[dependencies]
weavatrix-memory = { version = "0.3.3", features = ["secure-storage"] }
```

`FileSnapshotStore` writes immutable, position-named snapshots through a
temporary file and atomic rename. `replay_tracked` produces the exact cursor;
`resume` rejects any gap between that cursor and the supplied event tail.
`CompactSnapshotCodec` stores the complete replay cursor, temporal revisions,
facts, supersessions, and versioned retraction histories in a bounds-checked
binary format (`WMEMCB02`, with a read path for `WMEMCB01`). Lookup and CSR
indexes are derived and validated during decode rather than serialized
redundantly.

Codec wrappers compose in encode order. Compress before encrypting:

```rust,ignore
let codec = XChaCha20Codec::new(
    Lz4Codec::new(CompactSnapshotCodec, 512 * 1024 * 1024)?,
    StaticKey::new("2026-q3", key_from_secret_manager)?,
    b"projection-snapshot",
    512 * 1024 * 1024,
)?;
let snapshots = FileSnapshotStore::open(
    directory,
    "context",
    codec,
    SnapshotOptions::default(),
)?
.with_memory_mapped_reads();
```

`Lz4Codec` records the original length and rejects it before allocation when it
exceeds the configured limit. It stores raw bytes when compression would grow
the payload. `XChaCha20Codec` authenticates the envelope header and caller
context as AAD; the key identifier remains visible so an `EncryptionKeys`
provider can retain old decryption keys during rotation. It is a raw-key API,
not a password KDF.

`StaticKey` zeroizes its owned 256-bit key on drop, and the encryption wrapper
zeroizes temporary plaintext buffers after encode and decode. Applications must
still source keys from a secret manager or KMS, protect any copies made before
construction, and never use a deterministic `NonceSource` outside tests.
Authentication failures, wrong contexts, unavailable keys, malformed envelopes,
and oversized plaintexts fail closed.

Memory mapping is opt-in because it is a workload tradeoff, not a universal
speedup. Snapshot generations created by this store are never overwritten.
The mmap adapter holds a shared advisory lock, but a non-cooperating external
process can still truncate a mapped file; all writers must honor the lock and
immutable-generation contract.

`CatchUpSubscription` does not advance its checkpoint during `poll`. Consumers
must explicitly acknowledge a delivered position, so a failed handler receives
the same events again.

## Node.js and Bun

The `weavatrix-memory` npm package keeps bitemporal projection, evidence,
graph materialization, and budgeted context selection in Rust behind Node-API.
It is not a JavaScript rewrite and does not require an LLM or network service:

```console
npm install weavatrix-memory
# or: bun add weavatrix-memory
```

```js
const { Memory } = require('weavatrix-memory')

const memory = new Memory({ nodes, facts, knownAt: Date.now() * 1_000 })
const context = memory.compileContext({
  seeds: ['task:42'],
  validAt: Date.now() * 1_000,
  knownAt: Date.now() * 1_000,
  tokenBudget: 8_000,
  maxDepth: 2,
})
```

The single self-contained package supports Node.js 18+ and Bun 1.4+ and carries
native binaries for Windows, macOS, and glibc Linux on x64 and arm64 without
creating public platform-package names. The
[Node/Bun benchmark report](node/benchmark/RESULTS.md) is generated by the
[weavatrix-benchmarks](https://github.com/Weavatrix/weavatrix-benchmarks)
harness, which separates the narrow Graphology overlap from the extra temporal,
evidence, budget, and serialization work, and does not fake an end-to-end
comparison with model-backed products. Graphology wins that topology-only row —
roughly 20x on Node and 9x on Bun across three independent runs — and the row
stays published; a depth-2 walk on a chain reaches five nodes, so its median
sits at timer resolution. The Rust equal-topology context row remains the
stronger whole-kernel evidence: 0.193 ms versus 5.042 ms for `agentic-memory`,
with the slower bulk-construction row published beside it. This npm library is
a separately released Memory product; it does not belong to Online or MCP.

## Rust example

```rust
use weavatrix_memory::{
    AgentId, ContextCompiler, ContextRequest, EntityId, EventId, EventStore,
    Evidence, ExpectedVersion, FactId, InMemoryStore, MemoryEvent, MemoryFact,
    MemoryNode, MemoryProjection, NewEvent, SessionId, StreamId, Timestamp,
    replay,
};

fn at(value: i64) -> Timestamp {
    Timestamp::from_unix_micros(value)
}

let agent = AgentId::new("agent:codex")?;
let session = SessionId::new("session:714")?;
let task = EntityId::new("task:714")?;
let file = EntityId::new("file:query-builder")?;
let recorded_at = at(20);

let nodes = [
    MemoryNode::new(task.clone(), "task", "Fix one-day query accuracy")?,
    MemoryNode::new(file.clone(), "file", "query-builder.rs")?,
];
let fact = MemoryFact::new(
    FactId::new("fact:714:affected")?,
    task.clone(),
    "affects",
    file,
    at(10),
    recorded_at,
    agent.clone(),
    session.clone(),
    Evidence::new("test", "query-accuracy-suite")?,
)?;

let payloads = vec![
    MemoryEvent::NodeUpserted { node: nodes[0].clone() },
    MemoryEvent::NodeUpserted { node: nodes[1].clone() },
    MemoryEvent::FactRecorded { fact },
];
let pending = payloads
    .into_iter()
    .enumerate()
    .map(|(index, payload)| {
        let event_type = payload.event_type();
        NewEvent::new(
            EventId::new(format!("event:{index}"))?,
            event_type,
            recorded_at,
            recorded_at,
            agent.clone(),
            session.clone(),
            payload,
        )
    })
    .collect::<Result<Vec<_>, weavatrix_memory::MemoryError>>()?;

let stream = StreamId::new("task:714")?;
let mut store = InMemoryStore::default();
store.append(&stream, ExpectedVersion::NoStream, &pending)?;
let projection: MemoryProjection = replay(&store.load_all(None, usize::MAX))?;

let request = ContextRequest::new(vec![task], at(30), at(30), 2_000)?;
let bundle = ContextCompiler::default().compile(&projection, &request)?;
assert_eq!(bundle.graph.edge_count(), 1);
assert!(bundle.receipt.estimated_tokens <= 2_000);

# Ok::<(), weavatrix_memory::MemoryError>(())
```

## Bitemporal semantics

`valid_at` asks when a fact was true in the modeled world. `known_at` asks what
the system had recorded by a given moment. A correction recorded today can
replace a fact from last month without rewriting what an agent knew yesterday.

Facts retain:

- valid interval;
- observation and recording times;
- agent and session identities;
- confidence in basis points;
- one or more evidence records;
- the fact they supersede, when applicable.

## Auto-extraction and entity linking

`ExtractionProvider` isolates parsing or model inference from the deterministic
memory core. Providers return typed local mentions, relation candidates,
confidence, optional byte spans, stable IDs, external IDs, and candidate hints.
`AutoExtractionEngine` then:

- validates UTF-8 spans, temporal intervals, identifiers, and provider identity;
- links by stable ID, external ID, scoped normalized label, alias, or provider
  hint;
- applies an explicit minimum score and winner margin;
- reports ambiguous and unresolved endpoints instead of guessing;
- creates deterministic node and fact IDs for unmatched entities;
- preserves source, locator, digest, provider, span, agent, session, and
  confidence provenance;
- returns a non-mutating event plan suitable for review and atomic append.

`EntityLinker` can be built once from a temporal `MemoryView` and reused for a
batch. This keeps AST, issue-tracker, model, and future semantic adapters outside
the core while giving all of them the same validation and linking contract.

## Context compilation

`ContextCompiler` can start from exact entity identifiers or from one or more
`RetrievalProvider` implementations. A provider returns exact entity IDs from
literal, lexical, semantic, or hybrid search. Integer reciprocal-rank fusion
combines their ranks without pretending BM25 and vector scores share a scale;
the result retains provider, channel, rank, and raw-score provenance.

`MemoryProjection::view_ref` exposes the same bitemporal selection as `view`
while borrowing node, fact, and evidence payloads. A future
`weavatrix-search-vector` adapter can index this view and implement
`RetrievalProvider`; the memory crate therefore does not depend on an embedding
model or vector engine. Provider-local scores are used only to establish each
provider's rank, while reciprocal-rank fusion remains scale-independent.

After seed resolution, the compiler traverses selected relations in both
directions, ranks nearby evidence deterministically, and stops before exceeding
the configured budget. The receipt records:

- projection time and source event position;
- estimator identity and estimated usage;
- examined and selected fact counts;
- omissions caused by budget;
- facts excluded by repository or branch scope.

The built-in byte estimator is deterministic and dependency-free. Applications
that need model-exact counts implement the small `TokenEstimator` trait.

## Memory analytics

`MemoryAnalytics` operates on bitemporal projections and canonical
`weavatrix-graph` topology:

- belief revision finds explicit corrections and competing targets, then
  traces their downstream confidence cascade;
- reasoning-gap analysis reports unsupported decisions, single-source
  inferences, weak foundations, stale evidence, and unstable revision chains;
- drift reconstructs immutable belief timelines and correction rates;
- consolidation returns a deterministic plan for duplicate supersession,
  orphan review, and revision checkpoints without deleting history.

## Accuracy evaluation

The provider-neutral evaluator reports Hit@K, Recall@K, nDCG@K, and MRR from
exact evidence identifiers. The repository includes adapters for the public
[LoCoMo](https://github.com/snap-research/locomo) and
[LongMemEval](https://github.com/xiaowu0162/LongMemEval) formats plus a
coding-agent regression suite. See [benchmark instructions](benchmarks/README.md).

The full official files were parsed successfully in the local verification
run: 1,978 evidence-bearing `LoCoMo` questions and 470 non-abstention
LongMemEval-S questions. The dependency-free literal smoke baseline produced:

| Dataset | Hit@1 | Hit@5 | Recall@5 | MRR |
| --- | ---: | ---: | ---: | ---: |
| `LoCoMo` | 0.2230 | 0.3918 | 0.3559 | 0.2980 |
| LongMemEval-S cleaned | 0.6787 | 0.8894 | 0.7883 | 0.7679 |
| Coding-agent v1 (7 cases) | 0.8571 | 1.0000 | 1.0000 | 0.9286 |

These are adapter smoke results, not claims about the future
`weavatrix-search` or semantic/vector provider.

## Benchmarks

The repository contains executable, median-based benchmarks rather than copied
one-off timings. On an Intel Core Ultra 7 255U, Windows 11, Rust 1.97.1,
`--release`, a 100,000-event run produced:

| Contract | Median | Throughput |
| --- | ---: | ---: |
| In-memory evidence append + load | 38.293 ms | 2,611,456 events/s |
| Owned append, committed envelopes returned | 24.535 ms | 4,075,893 events/s |
| Receipt-only owned append | 16.831 ms | 5,941,347 events/s |
| `cqrs-es` 0.5.0 evidence append + load | 62.110 ms | 1,610,057 events/s |
| CRC32C JSON append + `sync_data` | 187.409 ms | 533,592 events/s |
| Durable reopen + index validation | 222.811 ms | 448,810 events/s |
| Bitemporal projection replay | 36.807 ms | 2,716,874 events/s |

The competitor workload carries the same event identifier, event type,
occurred/recorded timestamps, and agent/session identities. Weavatrix
additionally checks identifier uniqueness and optimistic concurrency and
assigns a zero-based global cursor. `cqrs-es` `MemStore` does not check
unique IDs, does not enforce optimistic concurrency, numbers sequences from
1, and prints on every load and commit; the harness silences that stdout so
the timer measures store work. Each side performs append followed by a cloned
stream load. Fixtures are created outside the timed region; nine samples are
measured after two warmups and the median is reported. Under this
evidence-equivalent contract, Weavatrix used 38.3% less time than `cqrs-es`
in this run. The owned and receipt-only rows expose progressively narrower
return contracts and avoid unneeded output clones. These are local
measurements, not universal hardware claims.

An exploratory Redis 8.8 Streams cross-check used a 512-byte payload, one
client, Docker-local `redis-benchmark`, and AOF enabled. With
`appendfsync always`, `XADD` reached 7,680 events/s at pipeline 100; changing to
the default-like `everysec` policy reached 200,000 events/s but can lose about
one second of writes. This is not an output-equivalent competitor benchmark:
it excludes client-side JSON serialization, projection replay, identifier
validation, and optimistic concurrency. It confirms the useful Redis pattern
is batched writes plus an explicit durability tradeoff. `FileEventStore`
already writes one checksummed frame and performs one selected sync per append
batch, while Redis remains the appropriate separate layer when coordinated
multiwriter access or replication is required.

The graph-memory harness also compares against `agentic-memory` 0.4.2. At
100,000 nodes and 300,000 edges:

| Contract | Weavatrix | `agentic-memory` | Result |
| --- | ---: | ---: | --- |
| Depth-2 context, identical 13-node/33-edge output | 0.167 ms | 4.822 ms | Weavatrix 28.9x faster |
| Validated `try_from_parts` + dual CSR | 222.292 ms | 59.597 ms | `agentic-memory` 3.73x faster |
| Strict replay of 400,000 envelopes | 476.009 ms | n/a | Different contract |

The context row is output-equivalent. Weavatrix walks dual-CSR incidents,
filters valid/known time, enforces a token budget, and materializes a
provenance graph. `agentic-memory::QueryEngine::context` does an unbudgeted
bidirectional BFS and then scans every edge to collect the subgraph; it has
no bitemporal cut and no evidence requirement. The bulk-construction row
compares each crate's parts constructor, but the contracts are still not
identical: Weavatrix validates node and fact domains, evidence, uniqueness,
endpoints, and supersession before building both CSR directions. The harness
records that `agentic-memory::MemoryGraph::from_parts` accepts a dangling
edge. They still win raw construction; we still refuse an invalid graph.
Strict event replay remains a separately reported operation.

The current-view path was also profiled at 100,000 nodes and 300,000 facts.
Replacing ordered membership checks with a validated all-nodes-visible fast
path and adding a borrowed view produced:

| Contract | Before | Current | Improvement |
| --- | ---: | ---: | ---: |
| Owned bitemporal `MemoryView` | 492.755 ms | 193.292 ms | 2.55x |
| Borrowed `MemoryViewRef` | 492.755 ms | 8.738 ms | 56.4x |

The owned row returns the same owned node/fact payload contract before and
after. The borrowed row deliberately returns references and is intended for
search/vector indexing and other read-only consumers. Timings are local medians
from the executable competitor harness, not universal hardware claims.

At 10,000 nodes and 30,000 facts, the versioned compact snapshot codec measured:

| Contract | Compact binary | JSON | Result |
| --- | ---: | ---: | --- |
| Encode | 4.796 ms | 19.153 ms | Compact 3.99x faster |
| Decode and validate indexes | 57.167 ms | 75.933 ms | Compact 1.33x faster |
| Snapshot size | 3,824,378 bytes | 9,543,533 bytes | Compact 59.9% smaller |

Both decoders restore the same projection and rebuild validated lookup and CSR
indexes. Empty retraction maps keep the same compact byte count as the
previous `WMEMCB01` fixture. The benchmark reports nine samples after two
warmups.

The retraction-history harness then records two corrections on each tenth
fact (3,000 retracted facts, 6,000 versions) and measures `WMEMCB02`:

| Contract | Median / size |
| --- | ---: |
| Replay with retraction events | 32.901 ms |
| Owned view over versioned history | 12.778 ms |
| Compact encode | 4.697 ms |
| Compact decode and validate | 39.181 ms |
| Compact snapshot | 3,969,715 bytes |
| JSON snapshot | 10,177,868 bytes |

Two versions on 10% of facts added 145,337 compact bytes (3.8%) versus the
retraction-free snapshot. There is no `agentic-memory` or `cqrs-es` row:
neither crate keeps a bitemporal retraction history.

The `blazingly-json` migration was also measured against a temporary
`serde_json` oracle in the same process, with operations interleaved on the
same 9,543,533-byte snapshot. Two complete runs produced stable relative
results despite unrelated system load:

| JSON operation | `blazingly-json` speedup over `serde_json` |
| --- | ---: |
| Encode | 1.16x-1.23x |
| Decode and validate indexes | 1.13x |

The oracle dependency was removed after the differential measurement;
`serde_json` remains in `Cargo.lock` only through the isolated
`agentic-memory` and `cqrs-es` development benchmarks.

The secure-storage harness first serializes a 100,000-node, 300,000-fact
projection into 38,828,001 compact bytes, then measures only the byte transform.
The copy row is the `Vec` allocation/copy baseline:

| Transform | Encode | Decode | Stored bytes |
| --- | ---: | ---: | ---: |
| Copy only | 9.184 ms | 9.132 ms | 38,828,001 |
| LZ4 | 32.985 ms | 23.027 ms | 5,249,623 |
| XChaCha20-Poly1305 | 76.600 ms | 65.802 ms | 38,828,059 |
| LZ4 then XChaCha20-Poly1305 | 47.962 ms | 32.132 ms | 5,249,681 |

For this repetitive evidence fixture, LZ4 reduced the snapshot by 86.5%.
Authenticated encryption added 58 bytes. The combined path encrypts only the
compressed payload; it remained 86.5% smaller than compact binary alone.

Snapshot loads include directory selection, frame bounds and CRC32C checks,
complete projection decode, and index validation. Buffered and mmap order is
alternated on every sample:

| Projection | Snapshot bytes | Buffered load | mmap load | Result |
| --- | ---: | ---: | ---: | --- |
| 10,000 nodes / 30,000 facts | 3,734,384 | 40.545 ms | 41.115 ms | mmap 1.4% slower |
| 100,000 nodes / 300,000 facts | 38,828,001 | 467.659 ms | 480.409 ms | mmap 2.7% slower |

The mmap path removes the encoded-payload heap allocation and copy, but its
mapping, locking, and page-fault overhead kept both cached local loads in the
same performance range and changed which path won. Buffered reads therefore
remain the default; mmap is for reducing peak heap and enabling large
immutable-file access, not a claimed speedup. Both tables report nine samples
after two warmups, with transform and read order alternated between samples.

The extraction harness indexes a 100,000-entity catalog containing label, alias,
and external-ID keys, then resolves 10,000 mentions:

| Contract | Median | Throughput |
| --- | ---: | ---: |
| Catalog build, 100,000 entities | 156.566 ms | 638,708 entities/s |
| Reused indexed linker, 10,000 mentions | 10.024 ms | 997,625 links/s |
| Validated extraction event plan, 10,000 mentions | 16.840 ms | 593,820 mentions/s |

Provider output and fixtures are created outside the indexed-link timing. Each
row reports the median of nine samples after two warmups.

## Development

```console
cargo fmt --all --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features --lib --tests
cargo audit
cargo bench --bench replay
cargo bench --features json --bench durable
cargo bench --bench event_store_competitors
cargo bench --bench memory_competitors
cargo bench --features json --bench snapshot_codecs
cargo bench --features json --bench retraction_history
cargo bench --features secure-storage --bench secure_storage
cargo bench --bench extraction
cargo run --release --all-features --bin weavatrix-memory-eval
```

Set `WEAVATRIX_BENCH_EVENTS` to change any workload. The in-memory replay
benchmark runs two warmups and reports nine measured iterations. The durable
benchmark reports five isolated append, reopen/index, and projection samples.
The competitor benchmarks report nine samples after two warmups. Set
`WEAVATRIX_BENCH_NODES` and `WEAVATRIX_BENCH_EDGES_PER_NODE` for the graph
workload, and `WEAVATRIX_BENCH_RETRACTION_VERSIONS` for the retraction
history fixture.

## Status

The public API is experimental before `1.0`. The filesystem journal and
snapshots are local embedded stores, not a distributed database. Exclusive
writer exclusion is built in; concurrent multiwriter scheduling, ACL policies,
Git history, MCP tools, compaction, and database adapters remain separate
layers.

## License

MIT