⛔ RED-TEAM VERDICT: NO-GO as written (agent
aec7447, code-grounded against HEAD1f120e8)The design below treats the byte flip as "the char pattern via the trait + 3 hazards." That is wrong. Byte is missing the ENTIRE durable-overlay subsystem the char flip is built on. The flip is NOT a thin Step-2 impl — it requires first BUILDING byte's equivalent of char's Phases C/D/E (the durable-write layer, the checkpoint route-split, the watermark/retention, regime-aware recovery + A2). Byte today has only Phase A/B (the overlay node +
enable_lockfree+ NO-WAL CAS). Blocking defects:
- C1 (CRITICAL): byte's
insert_cas/increment_cas/try_increment_cas(lockfree_cas.rs:112/461/595) are NO-WAL; byte has ZERO*_cas_durable. Routing production writes to them = total loss on reopen. $\Rightarrow$ must build a byte Order-A durable-overlay-write layer first.- C2 (CRITICAL):
persist_to_diskreadsself.root(serialize_impl.rs:112) +term_count; NO overlay-capture route-split (char S5-9). First post-reestablish checkpoint persists the EMPTY owned tree
- authorizes WAL truncation = total loss. $
\Rightarrow$ must build a bytecapture_snapshot_immutable+ route-split.- C3 (CRITICAL): no WAL-retention/commit_seq floor/watermark (0 byte matches). Checkpoint truncates below the overlay frontier. $
\Rightarrow$ must port the retaining publisher + floor.- H3 (HIGH): no regime-aware recovery /
reconcile_lww/ A2 (0 byte matches); dumb in-order replay. Once durable writers emit CommitRank, recovery resurrects orphans. $\Rightarrow$ must threadrank_regime+reconcile_lwwthrough byte's 3 sinks BEFORE the durable writers land.- C4 (CRITICAL): byte i64 counters CAN be negative (
increment_bytes/fetch_add/recovery takedelta: i64); the seam'sv as u64wraps a negative →increment_casPANICS. $\Rightarrow$ reject negatives on every overlay-write + reestablish path; usetry_increment_cas(never the panickingincrement_cas).- C5/C6 (CRITICAL): missing write routes (6
insert_batch*, bareremove_prefix,fetch_add, ALLSharedARTrie/Dictionary/MappedDictionarywriters) + missing read routes (the trait impls +get_value_bytes/contains_bytes+Dictionary::len→term_count+root()/zipper DEFER+warn). The real byte public surface is the trait impls +*_byteswrappers, NOT char-shaped inherent methods.- H1/H2 (HIGH): the owned_* seam needs UN-ROUTED, UNCAPPED enumerators; byte's only enumerators (
arena_iter.rs:354/497) get routed in B1 AND cap at 100k (silent reestablish truncation).- H4 (HIGH): byte has NO overlay remove primitive $
\Rightarrow$remove/remove_prefix*MUST reject (not route).Confirmed NON-defects: the
overlay_write_modefield is serialization-benign (the struct isn't serialized, onlyself.root); the i64=CounterValue TypeId identity is sound (read-back lossless); the 3 recovery sinks are correctly enumerated.CONSEQUENCE: the byte flip is a major, multi-phase, IRREVERSIBLE effort comparable to the char Phases C–E (durable subsystem + watermark + A2 + formal verification), NOT the thin impl this doc assumed. The §1–§7 below are SUPERSEDED as a standalone plan — they are the flip-layer half (the ~45% the trait already provides); the durable-subsystem half (C1/C2/C3/H3) must be designed + built + red-teamed + formally verified first, per-variant. Full verdict + defect list: the red-team result in the session transcript; corrected-items list reproduced at the end of this doc.
The trait EXTRACTION (Step 1,
1f120e8) is unaffected + complete — char is on the shared trait, and the generic flip-layer (read engine, route, flip/kill, reestablish fold, value-route) is ready for byte to reuse ONCE byte's durable subsystem exists.
Crate libdictenstein, byte src/persistent_artrie/. Baseline HEAD 1f120e8 (char on the shared
LockFreeOverlay<K,V,S> trait). DATA-LOSS-CRITICAL, IRREVERSIBLE at the write/regime layer. Inputs:
docs/design/byte-flip-reachability-audit.md (the hazard set), docs/design/overlay-flip-genericization.md
(§5 Step 2), and the char precedent (docs/design/s5-12-e1-readflip-design.md). The byte flip = the char
flip applied via the trait, V $\in$ {(), i64}, plus 3 char-absent hazards.
overlay_write_mode field + owned readers + public
read routing + the 10 reject/route guards + byte correspondence tests driven by explicit
enable_lockfree+set_overlay_write_mode(LockFreeOverlay) (like char's pre-flip tests). NO ctor change.
Gateable, reversible.impl LockFreeOverlay<ByteKey, V, S> for PersistentARTrie<V, S>type CounterValue = i64 (the byte counter trie is PersistentARTrie<i64>). The i64↔u64 conversion lives
in the publisher/getter seams (byte's overlay primitives speak u64; the counter is $\ge$0, bounded by
LOCKFREE_COUNTER_MAX = i64::MAX, so both directions are lossless):
overlay_publish_counter(units, v: i64) → self.increment_cas(units, v as u64) (no-WAL publisher).overlay_counter_get(units) -> Option<i64> → self.get_lockfree(units).map(|u| u as i64).overlay_publish_membership(units) → self.insert_cas(units).overlay_contains(units) → self.contains_lockfree(units).overlay_eligible_v() → TypeId::<V>()==() || ==i64.lockfree_root/overlay_write_mode/set_overlay_write_mode/enable_lockfree → field accessors (+ the
new field).wal_current_lsn/wal_is_overlay_regime/wal_stamp_overlay_regime/wal_stamp_owned_regime → byte's
wal_writer: Option<Arc<AsyncWalWriter>> (same AsyncWalWriter API char uses).self.root via byte's _impl, NEVER the routed public reads):
owned_first_units/owned_units_under/owned_units_with_values_under/owned_has_empty_term_value
built over byte's owned enumerators (iter_prefix_with_values_and_arena's OWNED body / get_value_impl);
clear_owned → self.root = TrieRoot::empty(); self.term_count.store(0). Convert Vec<u8>↔Vec<u8>
units via ByteKey::units_from_str/units_to_term (identity for byte). Because byte's public
iter_prefix/get will be routed in B1, the owned readers must call byte's _impl/owned-body
enumerators directly — extract owned_iter_prefix-equivalents if byte lacks un-routed enumerator entry
points (byte's arena_iter/cursor_iter bodies that match &self.root are the un-routed source).overlay_write_mode field + ctors (B1, reversible)Add pub(crate) overlay_write_mode: OverlayWriteMode to PersistentARTrie (dict_impl.rs:264), default
OwnedTree (inert). Initialize it in EVERY byte ctor (mmap_ctor + io_uring_ctor + any Default/test
ctor) — OverlayWriteMode::default(). This changes NO behavior (inert default), so the suite stays green.
Mirror char's E1, adapted to byte's API shapes:
contains/try_contains → if route_overlay() { overlay_contains via units } else { owned _impl }.get/try_get (&V) → return None under overlay (no borrowable overlay value); callers use get_value.get_value (owned Option) → if route_overlay() { overlay_route_get_value (trait) } else { owned }.len/is_empty/term_count → if route_overlay() { overlay_len/overlay_is_empty (trait) } else { owned }.iter/iter_prefix/iter_prefix_with_arena/
iter_prefix_from_cursor → overlay_collect_units (trait) mapped via ByteKey::units_to_term
(arena_id: None for the arena variants, like char). iter_with_values/iter_prefix_with_values/
iter_prefix_with_values_and_arena → overlay_collect_units_with_values (trait) — the VALUE-CARRYING
route (audit §C.2: NOT enumerate-overlay-then-value-owned).owned_* bodies for each so reestablish + the false-arms read owned.if self.route_overlay() { return Err(InvalidOperation("... not valid under the lock-free overlay ...")) }
at the top of: merge_from, merge_from_batched_with_options, merge_from_parallel,
merge_lockfree_values_to_persistent, merge_lockfree_to_persistent, begin_document+commit_document,
remove_prefix_batched, compact() (the P0 char-absent file-replacer). Plus the write-flip routes
(audit §D): increment_bytes/upsert_bytes/get_or_insert_bytes/insert/remove get
if route_overlay() { <overlay CAS> } else { owned }; compare_and_swap_bytes REJECTS under overlay
(no byte overlay CAS-with-expected exists). The recovery RMW recompute_recovered_increment stays on
get_value_impl (protect, do not change).
apply_create_flip (the byte twin: if overlay_eligible_v() && !flip_to_overlay() { return Err(internal) }).if rank_regime==Overlay && overlay_eligible_v() { flip_to_overlay(); reestablish_overlay_dispatch()? }.
Byte needs a reestablish_overlay_dispatch (the byte twin: TypeId i64→reestablish_overlay_counter,
()→reestablish_overlay_membership — both are now TRAIT DEFAULTS; the dispatch is the ~10-LOC seam that
Any-downcasts to <ByteKey,i64,S>).cargo nextest --features persistent-artrie green + NEW byte correspondence tests
(overlay==owned for len/contains/get_value/iter_prefix*, None-vs-Some(empty), deep-key) + the D1 seam
grep on the byte impl + verify-formal-correspondence.sh exit 0 + 0 new unsafe + the 10 reject-guard
tests.OverlayReestablishSpec.v is variant-agnostic $\Rightarrow$ byte inherits the D1 guard
(a one-line doc note, no new proof).compact() file-replacement (§C.1) — if not rejected, atomic-renames a values-lost image over the
durable file. The compact-rejects test is the guard.iter_with_values mixed-read (§C.2) — must use the value-carrying overlay route, not
enumerate-then-owned-value. The correspondence test pins it.\ge$0 (LOCKFREE_COUNTER_MAX) so both
directions are lossless; the correspondence test reads back i64 counters.The flip-layer (§1–§7, the trait-provided ~45%) is the LAST step. Before it, build byte's durable subsystem (the char Phases C–E equivalents), each its own gated + red-teamed + (where char was) formally verified phase:
try_increment_cas_durable/insert_cas_durable/
upsert_cas_durable/remove_cas_durable (durable WAL append + CommitRank, bound-before-log, THEN
publish). The i64-domain bound + negative-reject live here (C4/M3).capture_snapshot_immutable over
lockfree_root.load(); under route_overlay() serialize the overlay, owned arm asserts
!route_overlay() (C2).header.regime() at the 3 sinks, replay via reconcile_lww,
gate reestablish on rank_regime == Overlay (H3). MUST precede #1's CommitRank-emitting writers.remove_prefix,
fetch_add, all SharedARTrie/Dictionary/MappedDictionary writers; remove paths REJECT (no byte
overlay remove primitive); compact() rejects (M1, confirmed safe — no internal caller) (C5/H4).get_value_bytes/contains_bytes +
Dictionary::len→term_count + iter_with_values value-carrying route + root()/zipper DEFER+warn
(C6/M2).Each of 1–4 is a data-loss-critical, mostly-irreversible subsystem of its own; this is the char Phase C–E effort replicated for byte (the trait only saved the flip-layer half). Scope accordingly.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |