code wiki / _hdl_build / nx_staghyg.nx
nx_staghyg.nx
buildroot/runtime/_hdl_build/nx_staghyg.nx
about
nx_staghyg.nx -- STAGING-AREA HYGIENE: census, verdict, and expiry for the deploy queue.
THE MEASURED PROBLEM (debt 1785531571, sev-9): the staging area has no expiry, so a .new file that
PREDATES the live binary it would replace sits there looking deployable forever. Promoting it is a
ROLLBACK WEARING AN UPGRADE FILENAME. Nothing in the tree measured this per-artifact, and nothing
refused it at the moment of promote.
FOUR VERBS, DELIBERATELY SEPARATED (Rule 9 -- each does ONE thing):
census [root] report size+mtime for BOTH sides and DECIDE NOTHING. This is the rung-0
instrument, modelled verbatim on nx_tree_diff, whose banked lesson is that a
tool which picks a direction is the clobber bug with a friendlier face.
scan [root] the classified verdict: every staged artifact bucketed, counts that SUM.
check <livepath> one target. exit 0 = promotable, exit 3 = refused. The gate/promote surface.
expire [root] apply disarm artifacts past the expiry window.
*EXPIRY RENAMES, IT NEVER DELETES. An expired artifact becomes <name>.new.expired-<epoch>, which
md_promote_staged can no longer see (it opens exactly <name>.new) -- so the queue is disarmed while
every byte is kept and the act is reversible with a single rename back (Rule 13 additive-only,
Rule 26 fail-safe by construction). Without `apply` it only reports.
*THE CENSUS COUNTS THE HEALTHY ROWS TOO. An instrument blind to the common case only ever sees
crises -- the banked crawler lesson. FORWARD and ORPHAN are reported beside every refusal, and the
class counts are printed so a reader can check they SUM to the number scanned (a partition is a
claim).
license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
dependencies 2 imports · 0 importers
imports: nx_syscalls.nxnx_staging_guard.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
| 29 | const SH_STDOUT: i64 = 1 |
| 30 | const SH_DIRBUF: i64 = 262144 |
| 31 | const SH_PATHCAP: i64 = 1024 |
| 32 | const SH_DIR_TYPE: i64 = 4 |
| 33 | const SH_ALLOWCAP: i64 = 1048576 |
functions
| 35 | func sh_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(SH_STDOUT, s, n); return 0 } |
| 36 | func sh_num(v: i64) -> i64 |
| 52 | func sh_ends_new(nm: *u8) -> i64 |
| 63 | func sh_join(dir: *u8, nm: *u8, out: *u8, strip: i64) -> i64 |
| 81 | func sh_live_path(dir: *u8, nm: *u8, out: *u8) -> i64 |
| 106 | func sh_ends(s: *u8, suf: *u8) -> i64 |
| 120 | func sh_field(buf: *u8, ls: i64, le: i64, idx: i64, out: *u8, cap: i64) -> i64 called by 1: main |
| 140 | func sh_abs_live(p: *u8, out: *u8) -> i64 |
| 161 | func sh_append_num(out: *u8, pos: i64, v: i64) -> i64 |
| 175 | func sh_append_str(out: *u8, pos: i64, s: *u8) -> i64 called by 1: main |
| 183 | func sh_row(nm: *u8, cls: i64, out: *i64) -> i64 |
| 201 | func main(argc: i64, argv: *i64) -> i64 |