code wiki / _hdl_build / nx_jrnlguard.nx
nx_jrnlguard.nx
buildroot/runtime/_hdl_build/nx_jrnlguard.nx
about
nx_jrnlguard.nx -- DATA-LAYER CLOBBER/SHRINK DETECTOR (ws=ws-intel9, 2026-07-20).
The ecosystem guards its ROUTES (nx_route_diff), its TOOLS (nx_tooldiff), its BUILDS and
its CRONS -- but nothing guards the append-only JOURNALS AND PLANES that a dozen concurrent
sessions write all day (ws_sync, claims, pm_intake, actlog, every knowledge/store plane).
This is the data-layer twin, and it eats the filed anti-clobber CLASS (F744-F748).
CONTRACT: an append-only journal may only GROW. So:
bytes shrink -> RED (truncation / overwrite)
frames shrink -> RED (lines lost)
ANY per-key count DECREASES -> RED <-- THE SUBTLE ONE: a rewrite can ADD bytes while
silently DROPPING frames, so a totals-only check passes while data is already gone.
(Observed for real on the memory index 2026-07-20: it grew and lost entries at once.)
malformed lines rise -> WARN (corruption creeping in; a column-shifted row emits invalid
downstream JSON -- observed live in the commontask catalog this same session)
A shrink is REPORTED, never "repaired": if the shrink was an INTENTIONAL compaction, the
operator re-snapshots the baseline (the same discipline nx_route_diff uses for routes).
key column is an ARGUMENT (default 1) so one organ guards every journal shape.
SCALE LAW: windowed read + declared envelope (window_bytes/truncated/keys_capped).
license_tier: ORIGINAL No hw writes (Rule 26).
snapshot <journal> <baseline> [keycol] -> write baseline, JSON summary
check <journal> <baseline> [keycol] -> JSON verdict; exit 0 GREEN / 3 RED / 4 no-baseline
scan <journal> [keycol] -> structural audit JSON (READ-ONLY)
selftest <scratch-base> -> gate T1..T8 (caller pre-cleans)
dependencies 2 imports · 0 importers
imports: nx_syscalls.nxnx_gate_verdict.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 25 | const JG_MAGIC_1024: i64 = 1024 |
| 27 | const JG_WIN: i64 = 4194304 |
| 28 | const JG_BASE: i64 = 262144 |
| 29 | const JG_OUT: i64 = 262144 |
| 30 | const JG_SOFT: i64 = 49152 |
| 31 | const JG_KEY_MAX: i64 = 120 |
| 32 | const JG_MIN_COLS: i64 = 3 |
| 33 | const JG_KEYS_CAP: i64 = 200 |
functions
| 35 | func jg_cat(d: *u8, o: i64, s: *u8) -> i64 { var p: i64=o; var i: i64=0; while s[i]!=(0 as u8){ d[p]=s[i]; p=p+1; i=i+1 } return p } |
| 36 | func jg_catn(d: *u8, o: i64, v: i64) -> i64 |
| 48 | func jg_vlen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } |
| 49 | func jg_atoi_z(s: *u8) -> i64 { var v: i64=0; var i: i64=0; while s[i]!=(0 as u8){ let c: i64=s[i] as i64; if c>=48 { if c<=57 { v=v*10+(c-48) } } i=i+1 } return v } called by 1: main |
| 50 | func jg_read(path: *u8, buf: *u8, cap: i64) -> i64 |
| 59 | func jg_le(q: *u8, i: i64, n: i64) -> i64 |
| 65 | func jg_col(q: *u8, ls: i64, le: i64, c: i64, out: *i64) -> i64 |
| 81 | func jg_ncols(q: *u8, ls: i64, le: i64) -> i64 called by 1: jg_malformed |
| 88 | func jg_span_eq(q: *u8, s1: i64, e1: i64, s2: i64, e2: i64) -> i64 |
| 94 | func jg_lit_eq(q: *u8, s: i64, e: i64, lit: *u8) -> i64 |
| 101 | func jg_cross_eq(a: *u8, as1: i64, ae: i64, b: *u8, bs: i64, be: i64) -> i64 called by 1: jg_key_count_x |
| 108 | func jg_is_frame(q: *u8, ls: i64, le: i64) -> i64 |
| 113 | func jg_frames(q: *u8, n: i64) -> i64 |
| 119 | func jg_malformed(q: *u8, n: i64) -> i64 |
| 130 | func jg_key_first(q: *u8, upto: i64, keycol: i64, ks: i64, ke: i64) -> i64 |
| 140 | func jg_key_count(q: *u8, n: i64, keycol: i64, ks: i64, ke: i64) -> i64 |
| 152 | func jg_key_count_x(q: *u8, n: i64, keycol: i64, b: *u8, bs: i64, be: i64) -> i64 |
| 164 | func jg_write_baseline(path: *u8, q: *u8, n: i64, keycol: i64, cap_hit: *i64) -> i64 |
| 205 | func jg_base_num(b: *u8, bn: i64, name: *u8) -> i64 |
| 218 | func jg_key(d: *u8, o: i64, name: *u8) -> i64 |
| 228 | func jg_qlit(d: *u8, o: i64, s: *u8) -> i64 |
| 235 | func jg_jesc(d: *u8, o: i64, q: *u8, s: i64, e: i64, maxb: i64) -> i64 |
| 252 | func jg_verdict(q: *u8, n: i64, b: *u8, bn: i64, keycol: i64, vx: *i64, d: *u8, dpos: i64) -> i64 |
| 311 | func jg_emit_common(d: *u8, dpos: i64, jp: *u8, n: i64, keycol: i64, trunc: i64, mode: *u8) -> i64 |
| 339 | func jg_wr(path: *u8, body: *u8) -> i64 |
| 346 | func jg_mkpath(dst: *u8, src: *u8, suf: *u8) -> i64 |
| 353 | func jg_find(d: *u8, dn: i64, lit: *u8) -> i64 |
| 368 | func jg_selftest(base: *u8) -> i64 |
| 434 | func main(argc: i64, argv: *i64) -> i64 |