Liking cljdoc? Tell your friends :D

L3.3b execution plan (2026-06-09) — owned readers/mutators/converters + oracle retirement

Code-verified Plan-agent design (against f08235d/L3.3a). 6 committable commits B1–B6. Implementation BY HAND.

Structural facts

  • route_overlay() == self.lockfree_root().is_some() == universally true. Every public method is if route_overlay() { return <overlay> } <owned-tail>. The early return borrow-checks the owned tail, so the compiler does NOT flag the dead arms — they must be hand-collapsed (L0-style).
  • Owned readers/mutators are reached ONLY from those dead tails (+ test-only unrouted_* + the differential oracle).
  • Order: collapse dead tails FIRST (B1), then delete orphaned symbols (fail-closed via compile errors).

Commits

  • B1 — collapse the dead owned route-arms (no symbol deletion). Byte: mutation_api / merge_api / parallel_merge / document_tx / cursor_iter / atomic_ops / shared_trait_impl. Char: mutation_api / atomic_ops / batch_insert / document_tx / merge_api / parallel_merge / query_api / prefix_api / lockfree_cas. Keep every overlay arm.
  • B2 — land the byte format-3 fixture (PRECONDITION, see below). No deletions.
  • B3 — retire the differential ORACLE + delete converters: l31_differential_tests (byte f5_loader) + l31_char_differential_tests (char f5_loader); build_overlay_root_from_owned (flip.rs:830) + reestablish_overlay_from_owned (flip.rs:904) + D1 seam decls (flip.rs:235-268) + byte impls (unrouted_*/owned_*, overlay_write_mode.rs:82-526) + char D1 impls; overlay_to_inner (persist.rs:1884, test-only) + tests. KEEP inner_to_overlay (persist.rs:1960) — production char fault-in needs it (BLOCKER #6).
  • B4 — delete owned READERS + MUTATORS (now orphaned): byte query_impl.rs (contains_impl/get_value_impl), mutation_core.rs (insert_impl/insert_impl_core/insert_impl_no_wal/remove_impl/remove_impl_core/remove_impl_no_wal/ upsert_impl_no_wal), cursor_iter collector, unrouted_* atomic_ops seams; char query_api owned_try_, prefix_helpers owned_root_guard/navigate/collect_, mutation_core char no_wal mutators, atomic_ops try_increment_impl_no_wal. ⚠️ UNSAFE DELTA: char mutation_core.rs holds UNSAFE rows 19-20 (char-mutation-core-traversal &*current + char-mutation-core-unique-borrow &mut *current, count 6) — VERIFIED at lines 58/114/119/151/156/189/198. Deleting the char mutators FORCES pruning rows 19-20 + their UNSAFE_CONTRACTS entries in B4 same-commit (the parent work-item's "rows 19-20 at L3.3c" is WRONG). L3.3b char delta = −2 rows; byte delta = 0. Rows 4-16 + 22-30 survive to L3.3c.
  • B5 — delete the byte zipper dead cluster (bucket_has_path/has_path_in_child/is_final_in_child/get_children_in_child/ get_bucket_children/resolve_child) + tests. (NOT resolve_child_for_mutation — different.)
  • B6 — delete the merge drains + tests: byte merge_lockfree_to_persistent/merge_lockfree_values_to_persistent (lockfree_cas.rs:544,1483); char twins (1284,1810). Char merge_lockfree_to_persistent does NOT reject (silent owned-drain via deleted insert_impl_no_wal — code-correction). DO NOT touch vocab's same-named method (persistent_vocab_artrie/lockfree_cas.rs:322, distinct type). Delete the 2 overlay-proptest merge tests + reject tests.

Format-3 fixture (B2) — recommend OPTION (a), byte-only

  • format-3 (StringBucket-suffix) is produced ONLY by the deleted owned serialize path. Production overlay serializers (serialize_overlay_root_iterative, CX serialize_overlay_snapshot_compressed) emit ONLY ART node records; a childless node → empty Node4, NEVER a StringBucket. So byte enumerate_terms_from_disk's ROOT_TYPE_BUCKET-with-terms branch (disk_load.rs:600-631) + the SingleChildData::Bucket suffix branch (:656-665) are genuinely untested post-L3.3a.
  • Char needs NO fixture — char's legacy is an owned CharArtNode tree decoded by the SAME kept char record readers the production fault-in/reopen uses (covered by format-2 + char reopen suites).
  • Construct (in-crate #[cfg(test)], byte f5_loader.rs): create() → build a real StringBucket::with_values() of suffix-terms incl. "" → serialize_bucket_to_diskflush_and_count_arenas → a ROOT_TYPE_BUCKET CheckpointSnapshotpublish_snapshot → drop (NO .wal $\Rightarrow$ RankRegime::Owned $\Rightarrow$ convert_owned reopen path). Then PUBLIC open(). Routing verified: no-WAL $\Rightarrow$ Owned (mmap_ctor:536); open passes force_f5=USE_F5_REOPEN_LOADER=true (flip:758); Owned+eligible-V+force_f5 $\Rightarrow$ convert_owned_to_overlay_on_reopen $\Rightarrow$ load_root_immutable_seam $\Rightarrow$ load_overlay_root_compressed $\Rightarrow$ enumerate_terms_from_disk ROOT_TYPE_BUCKET branch.
    • Assert: (1) enumerate_terms_from_disk returns the terms+values+"" exactly; (2) public open()→contains/get_value incl. ""; (3) post-reopen checkpoint()→reopen re-asserts (legacy image rewritten to the new format, terms survive).
    • Cover BOTH the root-bucket branch AND the suffix-under-ART branch (one Node4 root + a child StringBucket via the kept serialize_node_to_disk + serialize_child_to_disk_with_path) → pins serialize_node_to_disk as a tested KEEP (rather than silently-dead until L3.3c).

Red-team focus (data-loss-critical)

B2 fixture routing + "" survival + rebuild-preserves-terms + descriptor (arena_count/term_count) consistency vs the eager-preload validation (mmap_ctor:374-391); B3 oracle-retire-AFTER-B2 ordering; B4 char-mutator deletion vs any surviving char writer; B6 char silent-drain has no production reliance + vocab untouched.

⚠️ FINDING (2026-06-09, during B1 impl) — B4 mutator-deletion is BLOCKED; move to L3.3c

The byte owned mutators are NOT independently deletable at B4. bucket.rs:434/441 (convert_bucket_to_art — the owned StringBucket→ART split, OWNED-tree machinery that survives to L3.3c with the owned root field/TrieRoot) calls insert_impl, and insert_impl_core calls back into the owned bucket/node helpers. So {insert_impl, insert_impl_core, remove_impl, remove_impl_core, *_impl_no_wal} + bucket.rs::convert_bucket_to_art + the owned-tree node manipulation form a self-referential owned-write CLUSTER: dead-from-production after B1 (the public arms route to the overlay), but internally interlinked and entangled with the owned root field/TrieRoot that only dies at L3.3c.

Refined cut (verify with the Plan agent next session):

  • L3.3b = B1 (collapse public arms) + B2 (format-3 fixture) + B3 (retire oracle + delete CONVERTERS build_overlay_root_from_owned/reestablish_overlay_from_owned/overlay_to_inner + D1 seams) + B5 (byte zipper dead cluster) + B6 (merge drains). These are cleanly isolatable after B1.
  • Owned READERS (contains_impl/get_value_impl/query_impl) + the owned-write CLUSTER (mutators + convert_bucket_to_art + owned node helpers) → DEFER to L3.3c, where they die together with the owned root field + TrieRoot/CharTrieRoot + owned loaders (one coherent owned-representation removal). This also moves the UNSAFE rows 19-20 prune to L3.3c (char mutators), restoring L3.3b to ZERO unsafe delta — consistent with the original work-item.
  • This needs a Plan-agent re-pass to enumerate the exact owned-write cluster boundary (which bucket.rs / node_impl.rs / transitions.rs helpers are owned-only vs shared) before deletion.

⚠️ L3.3c DECISION POINT (surfaced during B1) — doc-tx increment base-read

document_tx.rs::try_tx_increment_bytes computes the increment base via self.get_value_impl(term) (the OWNED reader) UN-guarded by route_overlay() (line ~157). Under the overlay the owned tree is empty, so for a term already valued in the overlay, the base reads 0 → the folded absolute SET in shadow_terms = deltacommit_document upserts delta, OVERWRITING the real overlay count. This is the already-flagged L1.0 open question (campaign memory: "doc-tx cross-document counter accumulation = last-write-SET (3 not 7) — intended idempotency or a bug?") — UNRESOLVED whether the SET-from-base semantics are intended. It is NOT in a route_overlay() arm, so B1 leaves it unchanged. At L3.3c, deleting get_value_impl will make this call a compile error — forcing the decision: route the base-read to the overlay (overlay_get_value/value_read_faulting → accumulate semantics) OR keep SET-from-an-overlay-read. Needs an owner call on the intended doc-tx counter semantics + a RED→GREEN test (tx_increment on an already-valued overlay term). Char document_tx.rs has the twin.

Also: B1's document_tx.rs collapse must additionally drop the dead if !self.route_overlay() { …owned BeginTx/abort… } blocks (lines ~51 + ~271 — negated route_overlay, structurally dead).

✅ B1 STATUS (2026-06-09) — B1 COMPLETE (byte + char)

BYTE committed (each gated green): mutation_api (1b280f6), atomic_ops+merge_api (15f951a), document_tx+cursor_iter (c87f2f5), document_tx begin/abort + dictionary_traits (614d5d5). These collapsed every byte public arm whose dead owned tail called a SEPARABLE owned fn.

CHAR committed (each gated green: nextest 2645/2645 + --no-default-features [+ --all-features for parallel_merge] + unsafe set-eq ZERO delta + fmt): mutation_api (9f18067), atomic_ops, merge_api + batch_insert (133cbf5), document_tx + parallel_merge (a03d1be), query_api + prefix_api + mod (031bac3).

  • document_tx: also deleted the 3 now-orphaned private i128 helpers (current_i128_for_increment / value_to_i128_lossy / value_from_i128_checked — zero test refs) + WalRecord/counter_codec imports; the 2 reject messages dropped their stale OverlayWriteMode::OwnedTree hints. char doc-tx increment uses route_increment (ACCUMULATE), so char has NO byte-style owned-base-read L3.3c question (resolved).
  • parallel_merge: collapsed to the serial merge_entries_overlay funnel (rayon WRITE was always illusory); dropped rayon::prelude imports; batch_size -> _.

Char B1 DEFERRED-to-L3.3c arms (collapsing now would orphan L3.3c-bound owned readers, or the tail is data-loss-critical owned machinery — same discipline as byte arena_iter):

  • query_api get_value — conditional overlay arm + live owned_get (overlay_write_mode D1 + atomic_ops).
  • prefix_api iter_prefix_with_arena / iter_prefix_with_values_and_arena / remove_prefix_batched — inline owned tails are the SOLE callers of navigate_to_prefix_with_arena / collect_terms_with_arena / collect_terms_with_values_and_arena.
  • mod SharedCharARTrie::root (eviction-pin walk; collapsing orphans from_trie + CharWalkGuard) + the SharedCharARTrie checkpoint owned tail (owned capture_snapshot/publish; byte-deferred-precedent).
  • Plus the byte arena_iter inline tails (below). All carry a harmless always-true if route_overlay() guard until L3.3c's owned-root/from_trie/capture_snapshot deletion compile-forces the collapse.

B1 collapse orphaned owned readers/helpers (owned_try_contains, sync_wal, … reached only via cfg(test) or zero callers) → "never used" warnings, joining the campaign's existing owned-orphan set; ALL deleted coherently at L3.3c (tracked in the l3-3c work item). NOT individually #[allow]'d (campaign norm = transient bare warnings; gate excludes -D warnings).

arena_iter.rs is DEFERRED to L3.3c (NOT collapsed in B1) — its 2 prefix-iterator methods (iter_prefix_with_arena ~379, iter_prefix_with_values_and_arena ~547) have INLINE owned tails that read self.root directly (a match &*self.root.read() {…} walk, ~125 lines each), not a separable fn. The Plan agent flagged arena_iter as KEEP/re-point (it backs public APIs), and the inline owned tail collapses NATURALLY at L3.3c when self.root/TrieRoot are deleted (the self.root.read() then errors, forcing the collapse). The redundant always-true if route_overlay() guard is harmless until then. (Do NOT hand-sed the inline tail out now — the if-close boundaries are easy to mis-count; let the L3.3c self.root deletion compiler-force it.)

lockfree_cas.rs:545/1486 are the merge_lockfree_{,values_}to_persistent REJECT arms = B6 (delete the whole drain method), NOT a B1 collapse.

✅ L3.3b COMPLETE (2026-06-09)

All deletion items done + gated green:

  • B1 (collapse) — byte + char (see B1 STATUS above).
  • B2 (772961e) — byte format-3 legacy root-bucket reopen fixture (pins enumerate_terms_from_disk's ROOT_TYPE_BUCKET branch independently of the oracle).
  • B3 (6b184ad) — retired the differential oracle (byte/char l31_differential_tests) + deleted the dead converters (build_overlay_root_from_owned/reestablish_overlay_from_owned) + D1 trait decls + byte/char impls. KEPT inner_to_overlay + build_overlay_root_from_terms + the f5_loader loaders + deep_term_converter_tests.
  • B5 (5cf1563) — deleted the byte zipper owned-tree dead cluster (bucket_has_path / get_bucket_children / resolve_child / has_path_in_child / is_final_in_child / get_children_in_child) + 3 resolve_child tests.
  • B6 (a03c1e4) — deleted the lockfree→persistent merge drains (byte + char merge_lockfree_{,values_}to_persistent) + their tests; KEPT the distinct vocab twin. Cross-repo READ-ONLY: no live downstream caller (libgrammstein references are obsolete-noting comments only).

NEXT: L3.3c keystone (delete owned root field + TYPES TrieRoot/CharTrieRoot [KEEP CharTrieNodeInner] + owned loaders + the owned-write CLUSTER (insert_impl↔convert_bucket_to_art) + owned READERS (the B1/B3-orphaned unrouted_/owned_iter_prefix/owned_get/owned_try_*) + OR-lock collapse + the B1-DEFERRED char arms (get_value / 3 prefix arena methods / SharedCharARTrie::root + checkpoint owned tail) + byte arena_iter inline tails + overlay_to_inner (test scaffold, deferred from B3) + PRUNE UNSAFE rows 4-16,19-20 [KEEP 22-30] + BLOCKER #4 byte in-loader Err→empty fallback (NOT the read_root_descriptor().is_ok() probe) + #41 soak). ⚠️ OWNER-DECISION GATE: byte document_tx::try_tx_increment_bytes reads get_value_impl(term) un-routed (L1.0 open question — doc-tx cross-document counter = last-write-SET vs accumulate). Deleting get_value_impl at L3.3c forces the decision; needs an owner call + a RED→GREEN test. (Char is RESOLVED — uses route_increment = accumulate.)

Gate (each commit)

full nextest + --no-default-features + --doc + verify-formal-correspondence.sh (0) + verify-unsafe-boundary-inventory.sh (set-eq; B4 expects char −2 rows) + fmt + cross-repo (liblevenshtein-rust; L3.3 also libgrammstein/lling-llang).

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close