ADR-0180 §1.5.4 + CLAUDE.md work-sequencing item 5 require these four
properties green on main before any core-rs/src/vault.rs change. They are
also the foundation ADR-0181 PR-5 (audio Delta-CRDT wiring) rides on.
T-1 set-equality of vault writes under shuffled ingest (+ idempotent
re-ingest at the content-addressed layer)
T-2 trace-hash invariance to vault order, + recall result-set invariance
to insertion order (the genuinely-failable half)
T-3 versor_apply non-commutativity (negative guard)
T-4 ProjectionHead.project purity across calls and threads
Findings (docs/audit/ADR-0180-t1-t4-findings.md):
- compute_trace_hash folds only vault_hits (a count), NOT vault contents, so
ADR-0180 §1.5.3's "re-sort vault state in content-addressed order" is
currently vacuous at the trace-hash layer; the live order-invariance
obligation is at recall() (result-set + count). Recommend amending §1.5.3.
- equal-score recall ties are index-sensitive; the Merge Kernel needs a
content-addressed tiebreak (mirrors ADR-0181 §2.2 merge key). Recommend
amending §2.2.
- append is genuinely semilattice-eligible; versor_apply is non-commutative.
7 passed; smoke suite green. No runtime/core mutation — tests + audit only.
4.6 KiB
ADR-0180 §1.5.4 — T-1…T-4 Foundation: Findings
Date: 2026-05-29
Branch: adr-0180-crdt-t1-t4-foundation
Tests: tests/test_adr_0180_crdt_foundation.py
Scope: the Python-side pre-refactor obligations that must be green on main
before any core-rs/src/vault.rs change (CLAUDE.md work-sequencing item 5; ADR-0180 §1.5.4).
Also the foundation ADR-0181 PR-5 (audio Delta-CRDT wiring) rides on.
Status
All four obligations are now covered and green (7 tests, all passing). Below are the substantive findings discovered while grounding them against the actual substrate, not the abstract baseline ADR-0180 §1.5 assumed.
Finding 1 (load-bearing) — compute_trace_hash does not fold vault contents
ADR-0180 §1.5.3 point 2 worried that "the trace-hash reduction must consume vault state in a content-addressed order … not in wall-clock arrival order" and that §4.3 "cannot hold under [a time-driven flush] policy unless the hashing step re-sorts."
Actual code (core/cognition/trace.py:27): compute_trace_hash folds
vault_hits — an int count — plus a serialized prefix of upstream turn
fields. It does not fold vault contents at all.
Consequence: the §1.5.3 "re-sort vault state in content-addressed order"
obligation is currently vacuous at the trace-hash layer — there is nothing
order-sensitive about vault contents in the payload to re-sort, because contents
are not in the payload. The CRDT merge may reorder the vault deque freely and
compute_trace_hash is unaffected, provided the recalled result count
(vault_hits) is order-invariant.
This shifts where the real obligation lives:
- It is not at
compute_trace_hashtoday (T-2a confirms count-stability). - It is at
recall()— the count and content of recall results must be order-invariant under a reordered deque. T-2b (recall_result_set_invariant _to_insertion_order) pins this for distinct-score entries and is the genuinely-failable half of T-2.
The §1.5.3 re-sort obligation becomes live only if/when vault contents (not just
a count) enter the trace-hash payload. Recommendation: ADR-0180 §1.5.3
should be amended to say the content-addressed-sort requirement applies to the
recall result set and to any future contents-bearing hash, not to today's
count-based compute_trace_hash.
Finding 2 (edge) — equal-score recall ties are index-sensitive
vault_recall breaks score ties by ascending index (vault/store.py). Index
is assigned by storage order, so two entries with exactly equal CGA inner
scores can surface in an order that depends on insertion order. For distinct
scores (the common case, and what T-2b asserts) recall is order-invariant.
Consequence for the CRDT merge: the merge must content-address tie-scored
entries (e.g. sort by versor bytes) before assigning deque indices, or the
sub-50ms reorder window (ADR-0180 §3.2) could change which of two equal-score
entries a recall returns. This is exactly the role ADR-0181 §2.2's
(canonical, ir, projection) merge key plays for audio — it gives a total,
content-addressed order independent of arrival. Recommendation: ADR-0180's
Merge Kernel spec (§2.2) should adopt a content-addressed tiebreak for the
general path, mirroring ADR-0181 §2.2.
Finding 3 (confirmed) — append is genuinely semilattice-eligible
T-1 confirms VaultStore.store is set-equal under any write permutation
(append never dedups/drops/order-mutates), and re-ingest is set-stable at the
content-addressed layer. This validates ADR-0180 §1.5.2 row 5 ("vault/store
write … commutative, associative, idempotent — semilattice-eligible") against
the real implementation. Note the deque itself appends duplicates (length
grows on re-ingest); idempotence holds at the content-addressed layer the CRDT
merge dedups on, not at the raw deque.
Finding 4 (confirmed) — versor_apply is non-commutative; barrier is justified
T-3 confirms the sandwich V·F·rev(V) does not commute. This is the load
under ADR-0181 §2.1: in-chunk audio composition is a serialization barrier
because this product is order-sensitive, and only the order-invariant
AudioCompilationUnit crosses into the sharded merge layer. If a refactor ever
makes versor_apply commutative, T-3 fails loudly before the substrate can
wrongly shard it.
Net
- T-1, T-3, T-4 hold against the real code as ADR-0180 §1.5.4 assumed.
- T-2 holds, but the obligation moved: the order-invariance that matters today
lives at
recall()(result-set + count), not atcompute_trace_hash(which is count-only). Two small amendments to ADR-0180 §1.5.3 and §2.2 are recommended above. Neither blocks the substrate; both sharpen it.