code wiki / (root) / nx_memindex_put.nx

nx_memindex_put.nx

buildroot/runtime/nx_memindex_put.nx

18926 B387 linesdepth 5pulls 5 transitivereach 0 importersview sourcekind tooltopic memindex
docsdependenciesstructsconstsfunctions

about

nx_memindex_put.nx -- the ATOMIC, SLUG-KEYED upsert for the HAND-CURATED half of the memory index (F826 / debt seq273; the contention half of the class whose budget half shipped as F825). WHY THIS EXISTS. The COINDEX block is a conflict-free rail: sessions APPEND to a journal and one writer splices the derived block. The curated pointer block below it had no such path, so every session mutated the shared index with a read-modify-write Edit -- which raced siblings FIVE times in one session and once produced a MANGLED CONCATENATED LINE. The defect is structural: a stale read plus a blind overwrite. This organ removes both -- it re-reads the file INSIDE the lock and rewrites exactly ONE slug-owned line, so a sibling's line can never be clobbered by a stale view. WHY NOT MAKE THE CURATED BLOCK JOURNAL-DERIVED TOO? Because it is genuinely EDITORIAL: the pointers are grouped thematically (DEPLOY/MGMT, COORDINATION/PM, ...) by human judgment, and deriving it per-slug would destroy that grouping. So authorship stays human; only the MUTATION becomes atomic. nx_memindex_put <journal> <md> <slug> <line> [ovfl] Locks <journal>.lock -- the SAME lock nx_coindex append and nx_memindex_emit take -- so put, append and splice are fully serialized against each other, not merely against other puts. ---- BUDGET + AUTO-EVICT (added 2026-07-20 for debt seq255 sev7 / F847; ADDITIVE, rule-19) -------- The upsert above kills the CLOBBER half of seq255. It does NOT kill the SCALE half, which the debt row names precisely: "unbounded growth (20.4->21.2KB mid-session, over the 17.1KB soft target, siblings add faster than anyone can hand-compact) + no session can safely compact". A WARNING does not fix that -- warnings are addressed to a librarian who, by the row's own account, cannot keep up. So with an [ovfl] file the zone becomes a true RAIL, symmetric with the COINDEX block: the budget is DERIVED (whole-file target minus everything outside the zone, so it self-adjusts as the rest of the file moves) and the OLDEST lines auto-evict into the overflow file's CURATED-OVFL block. Orientation matters and is not arbitrary: this organ APPENDS new lines at the END of the block, so the zone is oldest-first and ci_split_at_budget -- which keeps the newest SUFFIX -- is exactly the right splitter, reused rather than re-derived. NO-LOSS beats budget: if the overflow file is missing or markerless, NOTHING is evicted and the organ says so. Omitting [ovfl] preserves the old behavior byte-for-byte (warn, never evict). Structural lines (thematic groups, LAWS, DOCTRINE, the gotcha tail) live OUTSIDE the markers and are unreachable here, so the round-4 injury -- silently dropping the file's own laws -- cannot recur. Each managed line carries an invisible owner marker `<!--s:SLUG-->` at its end. Upsert = replace the line carrying that marker IN PLACE (position preserved, so the editorial grouping survives), else append just before the CURATED:END marker. FAIL-CLOSED: missing/misordered CURATED markers -> file untouched and says so (never guesses where to write). ATOMIC: tmp + fsync + renameat, so a concurrent reader sees whole-old or whole-new. IDEMPOTENT: putting identical text twice leaves the file byte-identical. It also REPORTS the resulting curated size against the whole-file target, so a writer learns it is

dependencies 2 imports · 0 importers

nx_syscalls.nx nx_coindex_core.nx nx_memindex_put.nx

imports: nx_syscalls.nxnx_coindex_core.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main mp_puts sys_write sys_mmap ccz_read sys_openat_rd sys_read sys_close ccz_cat_str mp_putn nxi_out nxi_fd sys_mmap ↻ ccz_cat_num sys_write ↻ sys_munmap ci_lock sys_openat_wr sys_flock sys_close ↻ ci_unlock sys_flock ↻ sys_close ↻ mp_find ccz_slen mp_span_has ccz_slen ↻ mp_parse_num ci_split_at_budget mp_ovfl_bank sys_mmap ↻ ccz_read ↻ mp_find ↻ ccz_cat_str ↻ sys_openat_wr ↻ sys_write ↻ sys_fsync sys_close ↻ sys_renameat sys_munmap ↻

structs

none

consts

46const MP_BUF: i64 = 262144
47const MP_NL: i64 = 10
48const MP_TARGET: i64 = 17100
51const MP_BLOCK_MIN: i64 = 2000
58const MP_ENTRY_CAP: i64 = 700
59const MP_PATHCAP: i64 = 1024
60const MP_MSGCAP: i64 = 1024
61const MP_MODE: i64 = 420
62const MP_EXIT_USAGE: i64 = 2
63const MP_EXIT_MARKERS: i64 = 3
64const MP_EXIT_IO: i64 = 1

functions

67func mp_parse_num(s: *u8) -> i64
called by 1: main
79func mp_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
called by 1: main calls 1: sys_write
80func mp_putn(v: i64) -> i64 { nxi_out(v); return 0 }
called by 1: main calls 1: nxi_out
83func mp_find(hay: *u8, n: i64, needle: *u8) -> i64
called by 2: mp_ovfl_bankmain calls 1: ccz_slen
97func mp_span_has(hay: *u8, a: i64, b: i64, needle: *u8) -> i64
called by 1: main calls 1: ccz_slen
122func mp_ovfl_bank(ovp: *u8, lines: *u8, n: i64) -> i64
172func main(argc: i64, argv: *i64) -> i64