code wiki / (root) / nx_rungclose.nx

nx_rungclose.nx source

↩ module page · 1385 lines · 71331 B

1// nx_rungclose.nx -- CLI over nx_rungclose_lib (ecosystem EC57 v2): ONE MEASURED stage per rung, the weakest link BY NAME, 2// and the census that moves every rung with NO SEAT and NO SIGNATURE in the loop. 3// nx_rungclose check <domain> <rung> [anchor] every link with its evidence and the rule that decided it, the issues, 4// the stage, the weakest link and its remedy; verdict LAST 5// nx_rungclose census <domain> [anchor] the stage of every rung on the board, counted, the partition summing 6// nx_rungclose census all [anchor] every published board. Writes the fleet status line, the close-first 7// worklist and the STAGE SNAPSHOT whole (temp, then rename), and appends 8// one ledger row per rung whose stage moved since the last census -- 9// THE CLOSER: the clock row rungclose runs it daily, nobody signs anything 10// nx_rungclose journal <domain> <rung> <kind> <text> [anchor] one board journal row, by grammar 11// nx_rungclose close|reopen <domain> <rung> <actor> [anchor] RETIRED 2026-09-18 (operator: completion needs no manual 12// sign-off): prints the check and writes NOTHING 13// exit: 0 PRODUCTION, CENSUSED or JOURNALED | 1 below PRODUCTION or REFUSED | 2 usage | 3 UNOBSERVABLE or NO-PLAN 14// Evidence, read once per run: the board's plan and matrix (both published trees), tool_allowlist.conf, 15// EVERY ROSTER as a (conf, journal) pair (nx_rosterconf_lib: the runner's default knowledge/gateroster.conf writing the production 16// journal RCF_JRNL, knowledge/status/gateroster.jrnl, then each pair an agent-plane row runs through nx_gate_roster_run beat -- the 17// heavy roster into the production journal, the slow rosters into their OWN journals, any later roster without an edit here): 18// every conf, the production journal always, and the journal of every pair whose conf lists the gate being judged -- so a slow 19// beat's GREEN is credited where that beat writes it, and printed as green_in=; the execution-surface census 20// knowledge/status/execsurface.tsv, the actlog's tail window (the freshness margin, read by a doubling ladder so its size is 21// the window's, never a guess), the clock DESIRED plane, the contract organ's source, the gate artifacts the ship loop's own 22// resolver finds (nx_organ_ship_lib: knowledge/organ_gate.conf, then derivation), and the published page's mtime. 23// A run given an [anchor] writes every artifact BESIDE that path, never production. 24// license_tier: ORIGINAL No hw writes (Rule 26). 25import "nx_syscalls.nx" 26import "nx_estate_path.nx" 27import "nx_barfresh_lib.nx" 28import "nx_store_seed_lib.nx" 29import "nx_treatment_lib.nx" 30import "nx_symdecl_lib.nx" 31import "nx_srcfresh.nx" 32import "nx_gate.nx" 33import "nx_logtail.nx" 34import "nx_organ_ship_lib.nx" 35import "nx_rosterconf_lib.nx" 36import "nx_rungclose_lib.nx" 37 38const RP_EXIT_DONE: i64 = 0 39const RP_EXIT_NOT: i64 = 1 40const RP_EXIT_USAGE: i64 = 2 41const RP_EXIT_UNOBS: i64 = 3 42const RP_PATH_CAP: i64 = 1024 43const RP_MODE_644: i64 = 420 44const RP_ALLOW: *u8 = "tool_allowlist.conf" 45// the production roster journal is RCF_JRNL (nx_rosterconf_lib), and every other roster journal is read from the pair naming it: 46// a roster the ruler counts is always a roster whose evidence the ruler reads where that roster writes it 47const RP_SURFACES: *u8 = "knowledge/status/execsurface.tsv" 48const RP_ACTLOG: *u8 = "knowledge/status/actlog.jrnl" 49const RP_GATE_CONF: *u8 = "knowledge/organ_gate.conf" 50const RP_BUILT_DIR: *u8 = "buildroot/_build/" 51const RP_BUILT_SFX: *u8 = ".sov.elf" 52const RP_LIVE_SFX: *u8 = ".elf" 53const RP_SRC_SFX: *u8 = ".nx" 54const RP_LIB_SFX: *u8 = "_lib" 55const RP_TAIL_FIRST_SHARE: i64 = 64 // the actlog window's first probe reads 1/64th of the journal and the ladder DOUBLES until 56 // the window's oldest row predates the margin: this sets how many probes a read costs, 57 // never what it can see (the ladder ends at the whole file) 58const RP_CLOCK_PLANE: *u8 = "knowledge/store/clockjobs-" // the DESIRED plane: the treat judge reads it, as nx_treatment does 59const RP_CLOCK_LIVE: *u8 = "knowledge/store/clocksched-" // the LIVE plane: what the scheduler actually runs 60const RP_PLANE_CAP: i64 = 1048576 // the clock DESIRED plane read, the same reserve nx_treatment declares; a fill makes agent reads UNPROVEN 61const RP_WF_PFX: *u8 = "knowledge/store/plan-" 62const RP_WF_SFX: *u8 = "-manifest.txt" 63const RP_ANCHOR_DEFAULT: *u8 = "knowledge/status/ws_sync.jrnl" // the default anchor: production artifacts keep their own names 64const RP_LEDGER: *u8 = "knowledge/status/rungclose.jrnl" 65const RP_LEDGER_SFX: *u8 = ".rungclose" 66const RP_STATUS: *u8 = "knowledge/status/rungclose.status" // ONE canonical fleet line, truncate-written by census all 67const RP_STATUS_SFX: *u8 = ".rungclose.status" 68const RP_WORKLIST: *u8 = "knowledge/status/rungclose.worklist" // the close-first worklist, rewritten WHOLE by census all 69const RP_WORKLIST_SFX: *u8 = ".rungclose.worklist" 70const RP_STAGES: *u8 = "knowledge/status/rungclose.stages" // v2: every rung's stage, rewritten WHOLE; the next census diffs against it 71const RP_STAGES_SFX: *u8 = ".rungclose.stages" 72const RP_TMP_SFX: *u8 = ".tmp" 73const RP_PAGE_PFX: *u8 = "sites/nishifamily/compare/" 74const RP_PAGE_SFX: *u8 = "/index.html" 75const RP_MATRIX_SFX: *u8 = ".matrix" 76const RP_SRC_PFX: *u8 = "buildroot/" 77const RP_CENSUS_ACTOR: *u8 = "census" 78// the evidence, read once (rp_gather_shared + rp_gather_board) 79const RP_E_PLAN_P: i64 = 0 80const RP_E_PLAN_N: i64 = 1 81const RP_E_MX_P: i64 = 2 82const RP_E_MX_N: i64 = 3 83const RP_E_ALLOW_P: i64 = 4 84const RP_E_ALLOW_N: i64 = 5 85const RP_E_ROSTER_P: i64 = 6 86const RP_E_ROSTER_N: i64 = 7 87const RP_E_CLOCK_P: i64 = 8 88const RP_E_CLOCK_N: i64 = 9 89const RP_E_GR_P: i64 = 10 // the roster journal, whole 90const RP_E_GR_N: i64 = 11 91const RP_E_PAGE_S: i64 = 12 92const RP_E_TAB_P: i64 = 13 93const RP_E_TAB_ROWS: i64 = 14 94const RP_E_GX_P: i64 = 15 // its index 95const RP_E_GX_ROWS: i64 = 16 96const RP_E_ES_P: i64 = 17 // the surface census 97const RP_E_ES_N: i64 = 18 98const RP_E_AL_P: i64 = 19 // the actlog window 99const RP_E_AL_N: i64 = 20 100const RP_E_AX_P: i64 = 21 // its clock-lane index 101const RP_E_AX_ROWS: i64 = 22 102const RP_E_AL_BYTES: i64 = 23 // the whole journal's size 103const RP_E_AL_COVER: i64 = 24 // 1 when the window reaches back past the margin (or holds the whole file) 104const RP_E_NOW: i64 = 25 105const RP_E_ROOT_A: i64 = 26 // the resolver's roots and declaration file 106const RP_E_ROOT_B: i64 = 27 107const RP_E_GCONF: i64 = 28 108const RP_E_ES_ASOF: i64 = 29 109const RP_E_DOM: i64 = 30 // *u8 the board's domain name (the first half of a rung's gate key) 110const RP_E_RT_P: i64 = 31 // *i64 each roster pair's conf text, RCF_TEXT_STRIDE slots each, in pair order (pair 0 the default) 111const RP_E_RT_K: i64 = 32 // how many roster pairs 112const RP_E_RL_P: i64 = 33 // *u8 the roster pair list, conf TAB journal per line 113const RP_E_RL_N: i64 = 34 114const RP_E_RT_READ: i64 = 35 // how many distinct roster confs could be read 115const RP_E_R_BEATS: i64 = 36 // how many agent-plane rows are roster beats, whatever journal they write 116const RP_E_R_OTHER: i64 = 37 // of them, the beats writing a journal other than the production one (read from that journal) 117const RP_E_AGP_P: i64 = 38 // *u8 THE AGENT PLANE: what the scheduler runs (the live plane), else the desired plane 118const RP_E_AGP_N: i64 = 39 119const RP_E_AGP_SRC: i64 = 40 // RP_AGP_LIVE or RP_AGP_DESIRED: which one answered, printed as the evidence 120const RP_E_RJ_P: i64 = 41 // *i64 the roster journal table, RGC_RJ_STRIDE slots per roster pair (pair 0 = production) 121const RP_E_RJ_K: i64 = 42 // how many distinct roster journals 122const RP_E_RJ_READ: i64 = 43 // how many of them could be read 123const RP_E_RC_SEEN: i64 = 44 // *i64 rgc_roster_credit scratch, one slot per pair, allocated once for every rung 124const RP_E_RC_LISTS: i64 = 45 // *i64 which pairs' confs list the name being judged 125const RP_E_RC_GT: i64 = 46 // *i64 one journal's gate facts 126const RP_E_RT_DISTINCT: i64 = 47 // how many distinct roster confs 127const RP_E_SLOTS: i64 = 48 128const RP_AGP_LIVE: i64 = 1 129const RP_AGP_DESIRED: i64 = 2 130// A RUNG'S OWN GATE KEY in organ_gate.conf: "<domain>:<rung>". No organ name carries a colon, so the ship loop, whose targets are 131// organ names, can never match a row keyed this way; only the close ruler reads it. 132const RP_RUNG_KEY_SEP: *u8 = ":" 133// how the check line names a gate the RUNG declared, distinct from the resolver's own reasons, so the output says which of the 134// two declarations decided (a rung row and an organ row naming the same gate would otherwise print identically) 135const RP_GW_RUNG: i64 = 0 - 2 136const RP_GW_RUNG_NAME: *u8 = "organ_gate.conf-rung-declaration" 137// one evaluation: scalars, then scratch the caller allocates ONCE (rp_detail_new), so a census of two thousand rungs maps 138// its buffers once instead of once per rung 139const RP_D_SD: i64 = 0 140const RP_D_RESOLVED: i64 = 1 141const RP_D_TREAT_ROW: i64 = 2 142const RP_D_TREAT_V: i64 = 3 143const RP_D_MCP: i64 = 4 144const RP_D_ORGAN_O: i64 = 5 145const RP_D_ORGAN_L: i64 = 6 146const RP_D_TOOL_L: i64 = 7 147const RP_D_GATE_L: i64 = 8 // 0 when no gate resolves 148const RP_D_GATE_WHY: i64 = 9 149const RP_D_ROSTERED: i64 = 10 150const RP_D_SCLASS: i64 = 11 151const RP_D_ES_FOUND: i64 = 12 152const RP_D_RUN: i64 = 13 153const RP_D_RUN_SRC: i64 = 14 154const RP_D_AGENT: i64 = 15 155const RP_D_CHAIN: i64 = 16 156const RP_D_STAGE: i64 = 17 157const RP_D_REWORK: i64 = 18 158const RP_D_WEAK: i64 = 19 159const RP_D_NISS: i64 = 20 160const RP_D_WHY: i64 = 21 // *i64 per-link why codes 161const RP_D_GF: i64 = 22 // *i64 the gate facts 162const RP_D_GF2: i64 = 23 // *i64 the roster facts of the subject ITSELF (a gate the roster runs) 163const RP_D_ISS: i64 = 24 // *i64 issue flags 164const RP_D_TOOL: i64 = 25 // *u8 the subject tool 165const RP_D_GATE: i64 = 26 // *u8 the gate 166const RP_D_TARGET: i64 = 27 // *u8 the name the gate is resolved for 167const RP_D_DST: i64 = 28 // *u8 the resolver's artifact path 168const RP_D_TRIED: i64 = 29 // *u8 the resolver's search transcript 169const RP_D_W: i64 = 30 // *i64 one why 170const RP_D_STOP: i64 = 31 171const RP_D_O2: i64 = 32 172const RP_D_ESOUT: i64 = 33 173const RP_D_OFF: i64 = 34 174const RP_D_GWHY: i64 = 35 175const RP_D_SYM: i64 = 36 // *u8 176const RP_D_FULL: i64 = 37 // *u8 177const RP_D_RULE: i64 = 38 // *i64 178const RP_D_ES_FO: i64 = 39 179const RP_D_ES_FL: i64 = 40 180const RP_D_ROSTER_IN: i64 = 41 // which roster conf holds the gate (its list index), RCF_NONE when none does 181const RP_D_GREEN_IN: i64 = 42 // which roster pair's journal supplied the GREEN that TESTED holds on, RCF_NONE when none 182const RP_D_RCOUT: i64 = 43 // *i64 rgc_roster_credit's out (RGC_RC_SLOTS) 183const RP_D_SLOTS: i64 = 44 184const RP_RUN_PROGRAM: i64 = 1 // where the newest run was read: the clock ran the program itself, 185const RP_RUN_AGENT: i64 = 2 // the clock ran the treat row's agent command, 186const RP_RUN_ROSTER: i64 = 3 // or the roster ran the subject 187const RP_RUN_AGENT_JOB: i64 = 4 // the clock ran the treat row's agent, witnessed by its own job= name 188// what one board's census hands back 189const RP_W_BUF: i64 = 0 190const RP_W_FILL: i64 = 1 191const RP_W_ROWS: i64 = 2 192const RP_W_SBUF: i64 = 3 // its stage rows 193const RP_W_SFILL: i64 = 4 194const RP_W_MBUF: i64 = 5 // its ledger rows (moves) 195const RP_W_MFILL: i64 = 6 196const RP_W_SLOTS: i64 = 8 197// the fleet totals, after the stage counts 198const RP_T_RUNGS: i64 = 0 199const RP_T_UNOBS: i64 = 1 200const RP_T_DUPS: i64 = 2 201const RP_T_REWORK: i64 = 3 202const RP_T_PROMOTED: i64 = 4 203const RP_T_DEMOTED: i64 = 5 204const RP_T_ENTERED: i64 = 6 // rungs that entered PRODUCTION this census 205const RP_T_NEW: i64 = 7 206const RP_T_ISS0: i64 = 8 // then one count per issue class 207const RP_T_EXTRA: i64 = 12 208// the previous snapshot handed to each board's census 209const RP_P_BUF: i64 = 0 210const RP_P_N: i64 = 1 211const RP_P_CUR: i64 = 2 // *i64 the cursor 212const RP_P_BASELINE: i64 = 3 // 1 when there is no previous snapshot: this census records, and moves nothing 213const RP_P_SLOTS: i64 = 4 214 215func rp_span_copy(buf: *u8, off: i64, len: i64) -> *u8 { 216 var l: i64 = len 217 if l < 0 { l = 0 } 218 let s: *u8 = sys_mmap(l + 1) 219 var i: i64 = 0 220 while i < l { s[i] = buf[off + i]; i = i + 1 } 221 s[l] = 0 as u8 222 return s 223} 224 225// a span into a buffer of cap bytes, NUL-terminated; -1 (and nothing copied) when it does not fit 226func rp_span_into(dst: *u8, cap: i64, buf: *u8, off: i64, len: i64) -> i64 { 227 dst[0] = 0 as u8 228 if len < 0 { return 0 - 1 } 229 if len >= cap { return 0 - 1 } 230 var i: i64 = 0 231 while i < len { dst[i] = buf[off + i]; i = i + 1 } 232 dst[len] = 0 as u8 233 return len 234} 235 236// a whole file resolved through the estate path probe; 0 bytes when absent (outp[0] = 0), so absent and empty differ by 237// the pointer 238func rp_read_estate(rel: *u8, outp: *i64) -> i64 { 239 let path: *u8 = sys_mmap(RP_PATH_CAP) 240 if ep_artifact_path(path, rel) == 0 { outp[0] = 0; return 0 } 241 let np: *i64 = sys_mmap(RGC_I64) as *i64 242 np[0] = 0 243 let b: *u8 = sys_read_file(path, np) 244 outp[0] = b as i64 245 return np[0] 246} 247 248// the board's matrix from the published trees, first then second 249func rp_read_matrix(dom: *u8, outp: *i64) -> i64 { 250 let path: *u8 = sys_mmap(CT_PATH_CAP) 251 let np: *i64 = sys_mmap(RGC_I64) as *i64 252 np[0] = 0 253 ct_build_path(ct_first_published(), dom, RP_MATRIX_SFX, path) 254 var b: *u8 = sys_read_file(path, np) 255 if np[0] <= 0 { 256 ct_build_path(ct_second_published(), dom, RP_MATRIX_SFX, path) 257 b = sys_read_file(path, np) 258 } 259 outp[0] = b as i64 260 return np[0] 261} 262 263// THE WINDOW OF AN APPEND-ONLY JOURNAL SINCE AN EPOCH, sized by the window and never by a guess: the tail doubles until its 264// oldest row predates `since` or the whole file is held (nx_logtail's honest tail reader does every read). env receives 265// the reader's envelope; cover[0] = 1 when the window reaches `since` or holds the whole file. Returns the bytes held, or 266// -1 when the journal cannot be opened (outp[0] = 0). 267func rp_read_since(rel: *u8, since: i64, outp: *i64, env: *i64, cover: *i64) -> i64 { 268 outp[0] = 0 269 cover[0] = 0 270 let path: *u8 = sys_mmap(RP_PATH_CAP) 271 if ep_artifact_path(path, rel) == 0 { return 0 - 1 } 272 let one: *u8 = sys_mmap(RGC_I64) 273 if lt_read_tail(path, one, 0, env) < 0 { return 0 - 1 } 274 let sz: i64 = env[0] 275 if sz <= 0 { outp[0] = one as i64; cover[0] = 1; return 0 } 276 var cap: i64 = sz / RP_TAIL_FIRST_SHARE 277 if cap <= 0 { cap = sz } 278 var n: i64 = 0 279 var buf: *u8 = one 280 var held: i64 = 0 281 var go: i64 = 1 282 while go == 1 { 283 if held > 0 { sys_munmap(buf, held + 1) } 284 buf = sys_mmap(cap + 1) 285 held = cap 286 n = lt_read_tail(path, buf, cap, env) 287 if n < 0 { return 0 - 1 } 288 if env[3] == 0 { cover[0] = 1; go = 0 } else { 289 let e0: i64 = rgc_first_epoch(buf, n) 290 if e0 >= 0 { if e0 <= since { cover[0] = 1; go = 0 } } 291 if go == 1 { if cap >= sz { go = 0 } else { cap = cap * 2; if cap > sz { cap = sz } } } 292 } 293 } 294 outp[0] = buf as i64 295 return n 296} 297 298// the estate root the resolver probes under: the allowlist's resolved path less its own name ("" when CWD is the root) 299func rp_root(out: *u8) -> i64 { 300 out[0] = 0 as u8 301 let p: *u8 = sys_mmap(RP_PATH_CAP) 302 if ep_artifact_path(p, RP_ALLOW) == 0 { return 0 } 303 var k: i64 = ces_slen(p) - ces_slen(RP_ALLOW) 304 if k < 0 { k = 0 } 305 var i: i64 = 0 306 while i < k { out[i] = p[i]; i = i + 1 } 307 out[k] = 0 as u8 308 return k 309} 310 311// EVERY ROSTER, read once per run through rp_read_estate, the one reader. The (conf, journal) pairs are derived from the agent plane 312// by nx_rosterconf_lib (the runner's default pair first, then each pair a beat row runs, whatever journal it writes); each conf's 313// text is read once under the first pair naming it and kept in pair order, and each journal likewise -- the production journal is 314// the read rp_gather_shared already made. Pair 0's conf is the default, so the treat judge keeps reading exactly the conf it read 315// before (ev ROSTER_P/N): the treat row is judged against the same evidence nx_treatment reads. Returns how many confs were read. 316func rp_gather_rosters(clock: *u8, cn: i64, ev: *i64) -> i64 { 317 let meta: *i64 = sys_mmap(RCF_META_SLOTS * RGC_I64) as *i64 318 let list: *u8 = sys_mmap(rcf_confs_bound(clock, cn)) 319 let k: i64 = rcf_confs(clock, cn, list, meta) 320 let ln: i64 = meta[RCF_META_BYTES] 321 let texts: *i64 = sys_mmap(k * RCF_TEXT_STRIDE * RGC_I64) as *i64 322 let jt: *i64 = sys_mmap(k * RGC_RJ_STRIDE * RGC_I64) as *i64 323 let lo: *i64 = sys_mmap(RGC_I64) as *i64 324 let tp: *i64 = sys_mmap(RGC_I64) as *i64 325 let path: *u8 = sys_mmap(RP_PATH_CAP) 326 var nread: i64 = 0 327 var ndistinct: i64 = 0 328 var jread: i64 = 0 329 var jdistinct: i64 = 0 330 var i: i64 = 0 331 while i < k { 332 // the conf: read once, under the first pair that names it 333 let b: i64 = i * RCF_TEXT_STRIDE 334 texts[b + RCF_TEXT_P] = 0 335 texts[b + RCF_TEXT_N] = 0 336 let co: i64 = rcf_pair_origin(list, ln, i, RCF_PART_CONF) 337 if co < i { 338 texts[b + RCF_TEXT_P] = texts[co * RCF_TEXT_STRIDE + RCF_TEXT_P] 339 texts[b + RCF_TEXT_N] = texts[co * RCF_TEXT_STRIDE + RCF_TEXT_N] 340 } else { 341 ndistinct = ndistinct + 1 342 let cl: i64 = rcf_pair_conf(list, ln, i, lo) 343 if rp_span_into(path, RP_PATH_CAP, list, lo[0], cl) > 0 { 344 tp[0] = 0 345 let tn: i64 = rp_read_estate(path, tp) 346 texts[b + RCF_TEXT_P] = tp[0] 347 texts[b + RCF_TEXT_N] = tn 348 if tp[0] != 0 { nread = nread + 1 } 349 } 350 } 351 // the journal: read and indexed once, under the first pair that names it; the production journal is the read 352 // rp_gather_shared already made, so the largest file is never read twice 353 let jb: i64 = i * RGC_RJ_STRIDE 354 let jo: i64 = rcf_pair_origin(list, ln, i, RCF_PART_JRNL) 355 jt[jb + RGC_RJ_ORIGIN] = jo 356 jt[jb + RGC_RJ_LEN] = RGC_RJ_UNREAD 357 jt[jb + RGC_RJ_BUF] = 0 358 jt[jb + RGC_RJ_GX] = 0 359 jt[jb + RGC_RJ_GXROWS] = 0 360 if jo == i { 361 jdistinct = jdistinct + 1 362 let jl: i64 = rcf_pair_jrnl(list, ln, i, lo) 363 if ces_span_is(list, lo[0], jl, RCF_JRNL) == 1 { 364 if ev[RP_E_GR_N] >= 0 { 365 jt[jb + RGC_RJ_BUF] = ev[RP_E_GR_P] 366 jt[jb + RGC_RJ_LEN] = ev[RP_E_GR_N] 367 jt[jb + RGC_RJ_GX] = ev[RP_E_GX_P] 368 jt[jb + RGC_RJ_GXROWS] = ev[RP_E_GX_ROWS] 369 jread = jread + 1 370 } 371 } else { 372 if rp_span_into(path, RP_PATH_CAP, list, lo[0], jl) > 0 { 373 tp[0] = 0 374 let jn: i64 = rp_read_estate(path, tp) 375 if tp[0] != 0 { 376 jt[jb + RGC_RJ_BUF] = tp[0] 377 jt[jb + RGC_RJ_LEN] = jn 378 if jn > 0 { 379 let gcap: i64 = rgc_lines(tp[0] as *u8, jn) 380 let gx: *i64 = sys_mmap(gcap * RGC_GX_STRIDE * RGC_I64) as *i64 381 jt[jb + RGC_RJ_GX] = gx as i64 382 jt[jb + RGC_RJ_GXROWS] = rgc_gx_index(tp[0] as *u8, jn, gx, gcap) 383 } 384 jread = jread + 1 385 } 386 } 387 } 388 } 389 i = i + 1 390 } 391 ev[RP_E_ROSTER_P] = texts[RCF_TEXT_P] 392 ev[RP_E_ROSTER_N] = texts[RCF_TEXT_N] 393 ev[RP_E_RT_P] = texts as i64 394 ev[RP_E_RT_K] = k 395 ev[RP_E_RT_DISTINCT] = ndistinct 396 ev[RP_E_RL_P] = list as i64 397 ev[RP_E_RL_N] = ln 398 ev[RP_E_RT_READ] = nread 399 ev[RP_E_R_BEATS] = meta[RCF_META_BEATS] 400 ev[RP_E_R_OTHER] = meta[RCF_META_OTHER] 401 ev[RP_E_RJ_P] = jt as i64 402 ev[RP_E_RJ_K] = jdistinct 403 ev[RP_E_RJ_READ] = jread 404 ev[RP_E_RC_SEEN] = sys_mmap(k * RGC_I64) as i64 405 ev[RP_E_RC_LISTS] = sys_mmap(k * RGC_I64) as i64 406 ev[RP_E_RC_GT] = sys_mmap(RGC_G_SLOTS * RGC_I64) as i64 407 return nread 408} 409 410// THE ROSTER EVIDENCE OF ONE NAME (a gate, or a subject a roster runs): which pairs' confs list it, read with the beat's own row 411// grammar, then rgc_roster_credit over the production journal and those pairs' journals. g receives the facts, out the rest. 412func rp_roster_evidence(ev: *i64, name: *u8, nl: i64, since: i64, g: *i64, out: *i64) -> i64 { 413 let k: i64 = ev[RP_E_RT_K] 414 let texts: *i64 = ev[RP_E_RT_P] as *i64 415 let lists: *i64 = ev[RP_E_RC_LISTS] as *i64 416 var i: i64 = 0 417 while i < k { 418 lists[i] = rcf_has_gate(texts[i * RCF_TEXT_STRIDE + RCF_TEXT_P] as *u8, texts[i * RCF_TEXT_STRIDE + RCF_TEXT_N], name, nl) 419 i = i + 1 420 } 421 return rgc_roster_credit(ev[RP_E_RJ_P] as *i64, k, lists, name, since, g, ev[RP_E_RC_GT] as *i64, ev[RP_E_RC_SEEN] as *i64, out) 422} 423 424// the roster confs as one comma-joined word, after the label: what a STALE or unrostered reader is told to check 425func rp_roster_confs_tail(ev: *i64) -> i64 { 426 let n: i64 = ev[RP_E_RL_N] 427 let w: *u8 = sys_mmap(n + 1) 428 rcf_join_confs(ev[RP_E_RL_P] as *u8, n, w, 0) 429 gw(" roster_confs=" as *u8); gw(w) 430 sys_munmap(w, n + 1) 431 return 0 432} 433 434// the evidence every board shares, read ONCE per run 435func rp_gather_shared(ev: *i64) -> i64 { 436 let now: i64 = sys_now_realtime_sec() 437 ev[RP_E_NOW] = now 438 let ap: *i64 = sys_mmap(RGC_I64) as *i64 439 ev[RP_E_ALLOW_N] = rp_read_estate(RP_ALLOW, ap) 440 ev[RP_E_ALLOW_P] = ap[0] 441 let clock: *u8 = sys_mmap(RP_PLANE_CAP) 442 var cn: i64 = sts_load(RP_CLOCK_PLANE, clock, RP_PLANE_CAP) 443 if cn >= RP_PLANE_CAP - 1 { cn = 0 } 444 if cn < 0 { cn = 0 } 445 ev[RP_E_CLOCK_P] = clock as i64 446 ev[RP_E_CLOCK_N] = cn 447 // THE AGENT PLANE: what the scheduler RUNS is the live plane; when it cannot be read (absent, empty, or filled past its 448 // reserve) the desired plane stands in, read last-declaration-wins as the scheduler reads it (rgc_clock_cmd). Agents resolve 449 // and rosters derive from it, so neither can name a command or a roster the scheduler is not running. 450 let live: *u8 = sys_mmap(RP_PLANE_CAP) 451 var ln: i64 = sts_load(RP_CLOCK_LIVE, live, RP_PLANE_CAP) 452 if ln >= RP_PLANE_CAP - 1 { ln = 0 } 453 if ln < 0 { ln = 0 } 454 if ln > 0 { 455 ev[RP_E_AGP_P] = live as i64 456 ev[RP_E_AGP_N] = ln 457 ev[RP_E_AGP_SRC] = RP_AGP_LIVE 458 } else { 459 sys_munmap(live, RP_PLANE_CAP) 460 ev[RP_E_AGP_P] = clock as i64 461 ev[RP_E_AGP_N] = cn 462 ev[RP_E_AGP_SRC] = RP_AGP_DESIRED 463 } 464 // the production roster journal, whole, and its production-roster index sized from its own line count 465 let gp: *i64 = sys_mmap(RGC_I64) as *i64 466 let grn: i64 = rp_read_estate(RCF_JRNL, gp) 467 ev[RP_E_GR_P] = gp[0] 468 ev[RP_E_GR_N] = grn 469 if gp[0] == 0 { ev[RP_E_GR_N] = 0 - 1 } 470 ev[RP_E_GX_ROWS] = 0 471 if grn > 0 { 472 let gcap: i64 = rgc_lines(gp[0] as *u8, grn) 473 let gx: *i64 = sys_mmap(gcap * RGC_GX_STRIDE * RGC_I64) as *i64 474 ev[RP_E_GX_P] = gx as i64 475 ev[RP_E_GX_ROWS] = rgc_gx_index(gp[0] as *u8, grn, gx, gcap) 476 } 477 // the rosters are derived from the agent plane and share the production journal just read, so they are gathered after both 478 rp_gather_rosters(ev[RP_E_AGP_P] as *u8, ev[RP_E_AGP_N], ev) 479 // the surface census, whole 480 let sp: *i64 = sys_mmap(RGC_I64) as *i64 481 let sn: i64 = rp_read_estate(RP_SURFACES, sp) 482 ev[RP_E_ES_P] = sp[0] 483 ev[RP_E_ES_N] = sn 484 if sp[0] == 0 { ev[RP_E_ES_N] = 0 - 1 } 485 // the actlog window since the margin, and its clock-lane index 486 let lp: *i64 = sys_mmap(RGC_I64) as *i64 487 let env: *i64 = sys_mmap(LT_ENV_SLOTS * RGC_I64) as *i64 488 let cov: *i64 = sys_mmap(RGC_I64) as *i64 489 let an: i64 = rp_read_since(RP_ACTLOG, now - rgc_margin_s(), lp, env, cov) 490 ev[RP_E_AL_P] = lp[0] 491 ev[RP_E_AL_N] = an 492 ev[RP_E_AL_BYTES] = env[0] 493 ev[RP_E_AL_COVER] = cov[0] 494 ev[RP_E_AX_ROWS] = 0 495 if an > 0 { 496 let acap: i64 = rgc_lines(lp[0] as *u8, an) 497 let ax: *i64 = sys_mmap(acap * RGC_AX_STRIDE * RGC_I64) as *i64 498 ev[RP_E_AX_P] = ax as i64 499 ev[RP_E_AX_ROWS] = rgc_ax_index(lp[0] as *u8, an, ax, acap) 500 } 501 // the resolver's roots: the promoted serving root, then the build scratch; its declaration file 502 let ra: *u8 = sys_mmap(RP_PATH_CAP) 503 let rl: i64 = rp_root(ra) 504 let rb: *u8 = sys_mmap(RP_PATH_CAP) 505 var bo: i64 = ces_cat(rb, 0, ra) 506 bo = ces_cat(rb, bo, RP_BUILT_DIR) 507 let gc: *u8 = sys_mmap(RP_PATH_CAP) 508 var go: i64 = ces_cat(gc, 0, ra) 509 go = ces_cat(gc, go, RP_GATE_CONF) 510 ev[RP_E_ROOT_A] = ra as i64 511 ev[RP_E_ROOT_B] = rb as i64 512 ev[RP_E_GCONF] = gc as i64 513 return rl 514} 515 516// the evidence of ONE board. Returns 1 when the plan was readable. 517func rp_gather_board(dom: *u8, ev: *i64) -> i64 { 518 let lenp: *i64 = sys_mmap(RGC_I64) as *i64 519 let which: *i64 = sys_mmap(RGC_I64) as *i64 520 lenp[0] = 0 521 ev[RP_E_DOM] = dom as i64 522 let plan: *u8 = bf_read_plan(ct_first_published(), ct_second_published(), dom, lenp, which) 523 ev[RP_E_PLAN_P] = plan as i64 524 ev[RP_E_PLAN_N] = lenp[0] 525 if lenp[0] <= 0 { return 0 } 526 let mp: *i64 = sys_mmap(RGC_I64) as *i64 527 ev[RP_E_MX_N] = rp_read_matrix(dom, mp) 528 ev[RP_E_MX_P] = mp[0] 529 let pg: *u8 = sys_mmap(RP_PATH_CAP) 530 var po: i64 = ces_cat(pg, 0, RP_PAGE_PFX) 531 po = ces_cat(pg, po, dom) 532 po = ces_cat(pg, po, RP_PAGE_SFX) 533 let pgr: *u8 = sys_mmap(RP_PATH_CAP) 534 var pms: i64 = 0 - 1 535 if ep_artifact_path(pgr, pg) == 1 { 536 let ns: i64 = sf_mtime_ns(pgr) 537 if ns >= 0 { pms = ns / SF_NS_PER_SEC } 538 } 539 ev[RP_E_PAGE_S] = pms 540 let cap: i64 = trt_cap(lenp[0]) 541 let tab: *i64 = sys_mmap(cap * TRT_STRIDE * RGC_I64) as *i64 542 ev[RP_E_TAB_ROWS] = trt_scan(plan, lenp[0], tab, cap) 543 ev[RP_E_TAB_P] = tab as i64 544 return 1 545} 546 547func rp_detail_new() -> *i64 { 548 let d: *i64 = sys_mmap(RP_D_SLOTS * RGC_I64) as *i64 549 d[RP_D_WHY] = sys_mmap(RGC_LINKS * RGC_I64) as i64 550 d[RP_D_GF] = sys_mmap(RGC_G_SLOTS * RGC_I64) as i64 551 d[RP_D_GF2] = sys_mmap(RGC_G_SLOTS * RGC_I64) as i64 552 d[RP_D_ISS] = sys_mmap(RGC_ISSUES * RGC_I64) as i64 553 d[RP_D_TOOL] = sys_mmap(OSL_NAMECAP) as i64 554 d[RP_D_GATE] = sys_mmap(OSL_NAMECAP) as i64 555 d[RP_D_TARGET] = sys_mmap(OSL_NAMECAP) as i64 556 d[RP_D_DST] = sys_mmap(OSL_PATHCAP) as i64 557 d[RP_D_TRIED] = sys_mmap(osl_tried_cap()) as i64 558 d[RP_D_W] = sys_mmap(RGC_I64) as i64 559 d[RP_D_STOP] = sys_mmap(RGC_I64) as i64 560 d[RP_D_O2] = sys_mmap(2 * RGC_I64) as i64 561 d[RP_D_ESOUT] = sys_mmap(RGC_ES_OUT_SLOTS * RGC_I64) as i64 562 d[RP_D_OFF] = sys_mmap(RGC_I64) as i64 563 d[RP_D_GWHY] = sys_mmap(RGC_I64) as i64 564 d[RP_D_SYM] = sys_mmap(OSL_NAMECAP) as i64 565 d[RP_D_FULL] = sys_mmap(RP_PATH_CAP) as i64 566 d[RP_D_RULE] = sys_mmap(RGC_I64) as i64 567 d[RP_D_RCOUT] = sys_mmap(RGC_RC_SLOTS * RGC_I64) as i64 568 return d 569} 570 571// the basename of the span [off, off+len) less a trailing suffix, into dst (cap bytes); its length, -1 when it does not fit 572func rp_base_into(dst: *u8, cap: i64, buf: *u8, off: i64, len: i64, sfx: *u8) -> i64 { 573 var s: i64 = off 574 var k: i64 = off 575 while k < off + len { if buf[k] == (RGC_CH_SLASH as u8) { s = k + 1 } k = k + 1 } 576 var l: i64 = off + len - s 577 let xl: i64 = ces_slen(sfx) 578 if l > xl { if ces_span_is(buf, off + len - xl, xl, sfx) == 1 { l = l - xl } } 579 return rp_span_into(dst, cap, buf, s, l) 580} 581 582// a NUL-terminated name ending in suffix sfx loses it IN PLACE; returns 1 when it did 583func rp_strip_sfx(name: *u8, sfx: *u8) -> i64 { 584 let n: i64 = ces_slen(name) 585 let xl: i64 = ces_slen(sfx) 586 if n <= xl { return 0 } 587 if ces_span_is(name, n - xl, xl, sfx) == 0 { return 0 } 588 name[n - xl] = 0 as u8 589 return 1 590} 591 592// the gate's name off the resolver's artifact path: after the last slash, up to the first dot 593func rp_gate_name(dst: *u8, path: *u8) -> i64 { 594 let n: i64 = ces_slen(path) 595 var s: i64 = 0 596 var k: i64 = 0 597 while k < n { if path[k] == (RGC_CH_SLASH as u8) { s = k + 1 } k = k + 1 } 598 var e: i64 = s 599 while e < n { if path[e] == (RGC_CH_DOT as u8) { break } e = e + 1 } 600 return rp_span_into(dst, OSL_NAMECAP, path, s, e - s) 601} 602 603// THE LINKS OF ONE RUNG from the gathered evidence; f receives the journal facts, d the detail. Returns declared. 604func rp_eval(rung: *u8, ev: *i64, links: *i64, f: *i64, d: *i64) -> i64 { 605 let plan: *u8 = ev[RP_E_PLAN_P] as *u8 606 let pn: i64 = ev[RP_E_PLAN_N] 607 let mx: *u8 = ev[RP_E_MX_P] as *u8 608 let why: *i64 = d[RP_D_WHY] as *i64 609 let w: *i64 = d[RP_D_W] as *i64 610 let g: *i64 = d[RP_D_GF] as *i64 611 let g2: *i64 = d[RP_D_GF2] as *i64 612 let iss: *i64 = d[RP_D_ISS] as *i64 613 let tool: *u8 = d[RP_D_TOOL] as *u8 614 let gate: *u8 = d[RP_D_GATE] as *u8 615 let target: *u8 = d[RP_D_TARGET] as *u8 616 let off: *i64 = d[RP_D_OFF] as *i64 617 let now: i64 = ev[RP_E_NOW] 618 var k: i64 = 0 619 while k < RP_D_WHY { d[k] = 0; k = k + 1 } 620 d[RP_D_TREAT_ROW] = 0 - 1 621 d[RP_D_TREAT_V] = 0 - 1 622 d[RP_D_SD] = 0 - 1 623 d[RP_D_RUN] = CES_NONE 624 d[RP_D_ES_FO] = 0 625 d[RP_D_ES_FL] = 0 626 tool[0] = 0 as u8 627 gate[0] = 0 as u8 628 target[0] = 0 as u8 629 k = 0 630 while k < RGC_G_SLOTS { g[k] = 0; g2[k] = 0; k = k + 1 } 631 g[RGC_G_NEWEST] = CES_NONE 632 g[RGC_G_GREEN] = CES_NONE 633 g2[RGC_G_NEWEST] = CES_NONE 634 let declared: i64 = rgc_journal(plan, pn, rung, f) 635 links[RGC_L_JOURNAL] = rgc_journal_link(f[RGC_F_ROWS], w) 636 why[RGC_L_JOURNAL] = w[0] 637 // BUILT: the contract symbol through the ONE symbol ruler, its organ named by the board's own matrix row 638 let hasc: i64 = rgc_has_contract(plan, f) 639 var resolved: i64 = 0 640 var sd: i64 = 0 - 1 641 if hasc == 1 { 642 let o: *i64 = d[RP_D_O2] as *i64 643 resolved = rgc_matrix_organ(mx, ev[RP_E_MX_N], plan, f[RGC_F_SYM_O], f[RGC_F_SYM_L], o) 644 if resolved == 1 { 645 d[RP_D_ORGAN_O] = o[0] 646 d[RP_D_ORGAN_L] = o[1] 647 let sym: *u8 = d[RP_D_SYM] as *u8 648 let full: *u8 = d[RP_D_FULL] as *u8 649 if rp_span_into(sym, OSL_NAMECAP, plan, f[RGC_F_SYM_O], f[RGC_F_SYM_L]) >= 0 { 650 var fo: i64 = ces_cat(full, 0, RP_SRC_PFX) 651 if o[1] + fo < RP_PATH_CAP { rp_span_into((((full as i64) + fo) as *u8), RP_PATH_CAP - fo, mx, o[0], o[1]) } 652 let rule: *i64 = d[RP_D_RULE] as *i64 653 sd = sd_present(full, sym, rule) 654 if sd < 0 { sd = sd_present(((full as i64) + fo) as *u8, sym, rule) } 655 } 656 } 657 } 658 d[RP_D_SD] = sd 659 d[RP_D_RESOLVED] = resolved 660 links[RGC_L_BUILT] = rgc_built(hasc, resolved, sd, w) 661 why[RGC_L_BUILT] = w[0] 662 // the treat row, judged against the same evidence nx_treatment reads 663 let tab: *i64 = ev[RP_E_TAB_P] as *i64 664 let r: i64 = trt_find(tab, ev[RP_E_TAB_ROWS], plan, rung) 665 d[RP_D_TREAT_ROW] = r 666 var treat_mcp: i64 = 0 667 var treat_complete: i64 = 0 668 var agent_o: i64 = 0 669 var agent_l: i64 = 0 670 if r >= 0 { 671 let b: i64 = r * TRT_STRIDE 672 var prim_ok: i64 = 0 - 1 673 var wf_ok: i64 = 0 - 1 674 if tab[b + TRT_ST_PRIM] == TRT_F_DECLARED { 675 let spp: *u8 = sys_mmap(RP_PATH_CAP) 676 prim_ok = ep_src_path(spp, rp_span_copy(plan, tab[b + TRT_PRIM_O], tab[b + TRT_PRIM_L])) 677 } 678 if tab[b + TRT_ST_WF] == TRT_F_DECLARED { 679 let wfp: *u8 = sys_mmap(RP_PATH_CAP) 680 var wo: i64 = ces_cat(wfp, 0, RP_WF_PFX) 681 wo = ces_cat(wfp, wo, rp_span_copy(plan, tab[b + TRT_WF_O], tab[b + TRT_WF_L])) 682 wo = ces_cat(wfp, wo, RP_WF_SFX) 683 let probe: *u8 = sys_mmap(RP_PATH_CAP) 684 wf_ok = ep_artifact_path(probe, wfp) 685 } 686 let v: i64 = trt_judge_row(tab, r, plan, ev[RP_E_ALLOW_P] as *u8, ev[RP_E_ALLOW_N], ev[RP_E_ROSTER_P] as *u8, ev[RP_E_ROSTER_N], ev[RP_E_CLOCK_P] as *u8, ev[RP_E_CLOCK_N], prim_ok, wf_ok) 687 d[RP_D_TREAT_V] = v 688 d[RP_D_MCP] = tab[b + TRT_ST_MCP] 689 if tab[b + TRT_ST_MCP] == TRT_F_PRESENT { 690 treat_mcp = 1 691 d[RP_D_TOOL_L] = rp_span_into(tool, OSL_NAMECAP, plan, tab[b + TRT_MCP_O], tab[b + TRT_MCP_L]) 692 } 693 if v == TRT_V_COMPLETE { treat_complete = 1 } 694 if tab[b + TRT_ST_AGENT] == TRT_F_PRESENT { agent_o = tab[b + TRT_AGENT_O]; agent_l = tab[b + TRT_AGENT_L] } 695 } 696 // LIVE: the subject tool. Declared by the treat row, else derived from the contract organ: its basename, and for a lib 697 // the basename less _lib (the CLI the estate registers beside a lib), whichever the allowlist carries 698 var tool_on: i64 = 0 699 if treat_mcp == 0 { if resolved == 1 { 700 let tl: i64 = rp_base_into(tool, OSL_NAMECAP, mx, d[RP_D_ORGAN_O], d[RP_D_ORGAN_L], RP_SRC_SFX) 701 d[RP_D_TOOL_L] = tl 702 if tl > 0 { 703 if trt_allow_has(ev[RP_E_ALLOW_P] as *u8, ev[RP_E_ALLOW_N], tool, 0, tl) == 1 { tool_on = 1 } else { 704 if rp_strip_sfx(tool, RP_LIB_SFX) == 1 { 705 d[RP_D_TOOL_L] = ces_slen(tool) 706 if trt_allow_has(ev[RP_E_ALLOW_P] as *u8, ev[RP_E_ALLOW_N], tool, 0, d[RP_D_TOOL_L]) == 1 { tool_on = 1 } 707 } 708 } 709 } 710 } } 711 var allow_ok: i64 = 0 712 if ev[RP_E_ALLOW_N] > 0 { allow_ok = 1 } 713 links[RGC_L_LIVE] = rgc_live(hasc, treat_mcp, allow_ok, tool_on, w) 714 why[RGC_L_LIVE] = w[0] 715 // TESTED: the gate the ship loop's own resolver finds for the ORGAN (a lib resolves through its one-segment strip), or 716 // for the treat tool when the rung names no organ. 717 // A RUNG MAY NAME ITS OWN GATE, AND THAT DECLARATION IS TRIED FIRST: organ_gate.conf keyed "<domain>:<rung>". A library 718 // carries many rungs behind one organ, and one organ row can name only one gate -- the face library's row names the lip 719 // ratio gate (declared for the AT4 lip axis), so WITHOUT a rung key every other rung in that library (the E-line, the 720 // palpebral fissure) resolves its TESTED link to the LIP gate: a wrong proof, which that conf's own comment calls worse than 721 // none because it looks like it worked. Only a DECLARATION counts for the rung key (the gate it names is probed as an 722 // explicit gate), so no derivation rule can ever invent a rung's proof; a rung with no row resolves exactly as before. 723 var gate_ok: i64 = 0 724 let dst: *u8 = d[RP_D_DST] as *u8 725 let gw1: *i64 = d[RP_D_GWHY] as *i64 726 if ev[RP_E_DOM] != 0 { 727 var rkl: i64 = ces_cat(target, 0, ev[RP_E_DOM] as *u8) 728 rkl = ces_cat(target, rkl, RP_RUNG_KEY_SEP) 729 rkl = ces_cat(target, rkl, rung) 730 if osl_conf_gate(ev[RP_E_GCONF] as *u8, target, gate) == 1 { 731 if osl_gate_resolve(target, gate, ev[RP_E_GCONF] as *u8, ev[RP_E_ROOT_A] as *u8, RP_LIVE_SFX, ev[RP_E_ROOT_B] as *u8, RP_BUILT_SFX, d[RP_D_TRIED] as *u8, osl_tried_cap(), dst, gw1) == 1 { 732 let rgl: i64 = rp_gate_name(gate, dst) 733 if rgl > 0 { gate_ok = 1; d[RP_D_GATE_L] = rgl; d[RP_D_GATE_WHY] = RP_GW_RUNG } 734 } 735 } 736 } 737 var tgl: i64 = 0 - 1 738 if gate_ok == 0 { 739 if resolved == 1 { tgl = rp_base_into(target, OSL_NAMECAP, mx, d[RP_D_ORGAN_O], d[RP_D_ORGAN_L], RP_SRC_SFX) } 740 if tgl <= 0 { if d[RP_D_TOOL_L] > 0 { tgl = rp_span_into(target, OSL_NAMECAP, tool, 0, d[RP_D_TOOL_L]) } } 741 } 742 if tgl > 0 { 743 if osl_gate_resolve(target, 0 as *u8, ev[RP_E_GCONF] as *u8, ev[RP_E_ROOT_A] as *u8, RP_LIVE_SFX, ev[RP_E_ROOT_B] as *u8, RP_BUILT_SFX, d[RP_D_TRIED] as *u8, osl_tried_cap(), dst, gw1) == 1 { 744 let gl: i64 = rp_gate_name(gate, dst) 745 if gl > 0 { gate_ok = 1; d[RP_D_GATE_L] = gl; d[RP_D_GATE_WHY] = gw1[0] } 746 } 747 } 748 var jr_ok: i64 = 0 749 if ev[RP_E_GR_N] >= 0 { jr_ok = 1 } 750 d[RP_D_ROSTER_IN] = RCF_NONE 751 d[RP_D_GREEN_IN] = RCF_NONE 752 if gate_ok == 1 { 753 // ROSTERED: the gate is a row of ANY roster conf a beat runs, read the way that beat reads it (nx_rosterconf_lib) 754 d[RP_D_ROSTER_IN] = rcf_find(ev[RP_E_RT_P] as *i64, ev[RP_E_RT_K], gate, d[RP_D_GATE_L]) 755 if d[RP_D_ROSTER_IN] >= 0 { d[RP_D_ROSTERED] = 1 } 756 // THE EVIDENCE: the production journal and the journal of every roster whose conf lists the gate -- a slow beat's 757 // GREEN is read where that beat writes it; a journal no roster listing the gate writes is never consulted 758 let rco: *i64 = d[RP_D_RCOUT] as *i64 759 rp_roster_evidence(ev, gate, d[RP_D_GATE_L], f[RGC_F_LAND], g, rco) 760 d[RP_D_GREEN_IN] = rco[RGC_RC_GREEN_IN] 761 jr_ok = 1 - rco[RGC_RC_UNREAD] 762 } 763 links[RGC_L_TESTED] = rgc_tested(gate_ok, jr_ok, g[RGC_G_GREEN], d[RP_D_ROSTERED], w) 764 why[RGC_L_TESTED] = w[0] 765 // WIRED: declaration first (a COMPLETE treat row), then the execution-surface census row of the subject tool 766 let eo: *i64 = d[RP_D_ESOUT] as *i64 767 var es_ok: i64 = 0 768 if ev[RP_E_ES_N] > 0 { es_ok = 1 } 769 if es_ok == 1 { if d[RP_D_TOOL_L] > 0 { 770 d[RP_D_ES_FOUND] = rgc_es_class(ev[RP_E_ES_P] as *u8, ev[RP_E_ES_N], tool, eo) 771 d[RP_D_SCLASS] = eo[RGC_ES_OUT_CLASS] 772 d[RP_D_ES_FO] = eo[RGC_ES_OUT_FO] 773 d[RP_D_ES_FL] = eo[RGC_ES_OUT_FL] 774 } } 775 links[RGC_L_WIRED] = rgc_wired(treat_complete, es_ok, d[RP_D_ES_FOUND], d[RP_D_SCLASS], w) 776 why[RGC_L_WIRED] = w[0] 777 // OPERATED: the newest unattended run of the subject -- the clock ran the program itself, the clock ran the treat row's 778 // agent command, or the roster ran the subject (a gate) 779 var run: i64 = CES_NONE 780 var src: i64 = 0 781 let al: *u8 = ev[RP_E_AL_P] as *u8 782 if d[RP_D_TOOL_L] > 0 { 783 if ev[RP_E_AX_ROWS] > 0 { 784 let r1: i64 = rgc_ax_newest_prog(al, ev[RP_E_AX_P] as *i64, ev[RP_E_AX_ROWS], tool) 785 if r1 > run { run = r1; src = RP_RUN_PROGRAM } 786 } 787 // the roster ran it: its newest roster row in the production journal or the journal of any roster listing it 788 rp_roster_evidence(ev, tool, d[RP_D_TOOL_L], CES_NONE, g2, d[RP_D_RCOUT] as *i64) 789 if g2[RGC_G_NEWEST] > run { run = g2[RGC_G_NEWEST]; src = RP_RUN_ROSTER } 790 } 791 var has_agent: i64 = 0 792 if agent_l > 0 { 793 let co: *i64 = d[RP_D_O2] as *i64 794 // the agent's command, from the plane the scheduler runs (RP_E_AGP); the agent EXISTS when either plane declares it 795 let cl: i64 = rgc_clock_cmd(ev[RP_E_AGP_P] as *u8, ev[RP_E_AGP_N], agent_o, agent_l, plan, co) 796 if cl > 0 { has_agent = 1 } else { if rgc_clock_cmd(ev[RP_E_CLOCK_P] as *u8, ev[RP_E_CLOCK_N], agent_o, agent_l, plan, off) > 0 { has_agent = 1 } } 797 if ev[RP_E_AX_ROWS] > 0 { 798 // THE WITNESS IS THE JOB NAME. The clock writes the forked path alone (clk_actlog), so a row running 799 // nx_plan_run.elf <plan> is named ONLY by the job= token the clock appends; the whole-command match stays as the 800 // fallback for a writer that carried the arguments 801 let rj: i64 = rgc_ax_newest_job(al, ev[RP_E_AX_P] as *i64, ev[RP_E_AX_ROWS], plan, agent_o, agent_l) 802 if rj > run { run = rj; src = RP_RUN_AGENT_JOB } 803 if cl > 0 { 804 let r2: i64 = rgc_ax_newest_cmd(al, ev[RP_E_AX_P] as *i64, ev[RP_E_AX_ROWS], ev[RP_E_AGP_P] as *u8, co[0], cl) 805 if r2 > run { run = r2; src = RP_RUN_AGENT } 806 } 807 } 808 } 809 d[RP_D_RUN] = run 810 d[RP_D_RUN_SRC] = src 811 d[RP_D_AGENT] = has_agent 812 var act_ok: i64 = 0 813 if ev[RP_E_AL_N] >= 0 { act_ok = 1 } 814 links[RGC_L_OPERATED] = rgc_operated(run, f[RGC_F_LAND], now, act_ok, d[RP_D_SCLASS], has_agent, w) 815 why[RGC_L_OPERATED] = w[0] 816 links[RGC_L_PAGE] = rgc_page(ev[RP_E_PAGE_S], f[RGC_F_LAND], w) 817 why[RGC_L_PAGE] = w[0] 818 // the climb, then the ceilings 819 d[RP_D_NISS] = rgc_issues(g, f[RGC_F_LAND], f[RGC_F_RETRACT], now, iss) 820 let stop: *i64 = d[RP_D_STOP] as *i64 821 let chain: i64 = rgc_state(declared, links, stop) 822 let stage: i64 = rgc_cap(chain, iss) 823 d[RP_D_CHAIN] = chain 824 d[RP_D_STAGE] = stage 825 d[RP_D_REWORK] = rgc_rework(iss) 826 var weak: i64 = stop[0] 827 if stage < chain { weak = rgc_weak_issue0() + rgc_capping_issue(chain, iss) } 828 d[RP_D_WEAK] = weak 829 return declared 830} 831 832func rp_epoch(v: i64) -> i64 { 833 if v < 0 { gw("none" as *u8); return 0 } 834 gn(v) 835 return 0 836} 837 838// the conf (which = RCF_PART_CONF) or the journal (RCF_PART_JRNL) of roster pair i, or none 839func rp_roster_part(ev: *i64, i: i64, which: i64) -> i64 { 840 if i < 0 { gw("none" as *u8); return 0 } 841 let lo: *i64 = sys_mmap(RGC_I64) as *i64 842 let ll: i64 = rcf_pair_part(ev[RP_E_RL_P] as *u8, ev[RP_E_RL_N], i, which, lo) 843 if ll <= 0 { gw("none" as *u8); return 0 } 844 gwb(ev[RP_E_RL_P] as *u8, lo[0], ll) 845 sys_munmap(lo as *u8, RGC_I64) 846 return 0 847} 848 849func rp_run_src_name(s: i64) -> *u8 { 850 if s == RP_RUN_PROGRAM { return "clock-ran-the-program" as *u8 } 851 if s == RP_RUN_AGENT { return "clock-ran-the-agent" as *u8 } 852 if s == RP_RUN_ROSTER { return "roster-ran-it" as *u8 } 853 if s == RP_RUN_AGENT_JOB { return "clock-ran-the-agent-by-its-job-name" as *u8 } 854 return "none" as *u8 855} 856 857func rp_agp_name(s: i64) -> *u8 { 858 if s == RP_AGP_LIVE { return "live" as *u8 } 859 if s == RP_AGP_DESIRED { return "desired-last-declaration" as *u8 } 860 return "none" as *u8 861} 862 863func rp_sclass_name(c: i64) -> *u8 { 864 if c == RGC_S_CLOCK { return "clock" as *u8 } 865 if c == RGC_S_ROSTER { return "roster" as *u8 } 866 if c == RGC_S_BLIND { return "unattended-unwitnessed" as *u8 } 867 if c == RGC_S_MCP { return "mcp-only" as *u8 } 868 return "none" as *u8 869} 870 871func rp_verdict_word(v: i64) -> *u8 { 872 if v == RGC_V_GREEN { return "GREEN" as *u8 } 873 if v == RGC_V_RED { return "RED" as *u8 } 874 return "OTHER" as *u8 875} 876 877func rp_link_line(name: *u8, v: i64, w: i64) -> i64 { 878 gw(" link " as *u8); gw(name); gw("=" as *u8); gw(rgc_hold_name(v)); gw(" why=" as *u8); gw(rgc_why_word(w)) 879 return 0 880} 881 882func rp_print(dom: *u8, rung: *u8, ev: *i64, links: *i64, f: *i64, d: *i64) -> i64 { 883 let plan: *u8 = ev[RP_E_PLAN_P] as *u8 884 let why: *i64 = d[RP_D_WHY] as *i64 885 let g: *i64 = d[RP_D_GF] as *i64 886 let iss: *i64 = d[RP_D_ISS] as *i64 887 gw("RUNGCLOSE domain=" as *u8); gw(dom); gw(" rung=" as *u8); gw(rung); gw("\n" as *u8) 888 rp_link_line("journal" as *u8, links[RGC_L_JOURNAL], why[RGC_L_JOURNAL]); gw(" rows=" as *u8); gn(f[RGC_F_ROWS]); gw(" land=" as *u8); rp_epoch(f[RGC_F_LAND]); gw(" retract=" as *u8); rp_epoch(f[RGC_F_RETRACT]); gw("\n" as *u8) 889 rp_link_line("built" as *u8, links[RGC_L_BUILT], why[RGC_L_BUILT]); gw(" contract=" as *u8) 890 if rgc_has_contract(plan, f) == 1 { gwb(plan, f[RGC_F_SYM_O], f[RGC_F_SYM_L]) } else { gw("none-declared" as *u8) } 891 gw(" matrix_row=" as *u8); gn(d[RP_D_RESOLVED]) 892 if d[RP_D_RESOLVED] == 1 { gw(" organ=" as *u8); gwb(ev[RP_E_MX_P] as *u8, d[RP_D_ORGAN_O], d[RP_D_ORGAN_L]); gw(" symbol_ruler=" as *u8); gn(d[RP_D_SD]) } 893 gw("\n" as *u8) 894 rp_link_line("live" as *u8, links[RGC_L_LIVE], why[RGC_L_LIVE]); gw(" tool=" as *u8) 895 if d[RP_D_TOOL_L] > 0 { gw(d[RP_D_TOOL] as *u8) } else { gw("none" as *u8) } 896 gw(" treat_row=" as *u8) 897 if d[RP_D_TREAT_ROW] >= 0 { gw("1 mcp=" as *u8); gw(trt_state_name(d[RP_D_MCP])) } else { gw("0" as *u8) } 898 gw("\n" as *u8) 899 rp_link_line("tested" as *u8, links[RGC_L_TESTED], why[RGC_L_TESTED]); gw(" gate=" as *u8) 900 if d[RP_D_GATE_L] > 0 { 901 gw(d[RP_D_GATE] as *u8); gw(" resolved_by=" as *u8) 902 if d[RP_D_GATE_WHY] == RP_GW_RUNG { gw(RP_GW_RUNG_NAME) } else { gw(osl_why_name(d[RP_D_GATE_WHY])) } 903 gw(" rostered=" as *u8); gn(d[RP_D_ROSTERED]); gw(" rostered_in=" as *u8); rp_roster_part(ev, d[RP_D_ROSTER_IN], RCF_PART_CONF) 904 gw(" green_in=" as *u8); rp_roster_part(ev, d[RP_D_GREEN_IN], RCF_PART_JRNL) 905 gw(" roster_confs_read=" as *u8); gn(ev[RP_E_RT_READ]); gw("/" as *u8); gn(ev[RP_E_RT_DISTINCT]) 906 gw(" roster_journals_read=" as *u8); gn(ev[RP_E_RJ_READ]); gw("/" as *u8); gn(ev[RP_E_RJ_K]); gw(" roster_beats_other_journal=" as *u8); gn(ev[RP_E_R_OTHER]) 907 gw(" roster_rows=" as *u8); gn(g[RGC_G_ROWS]); gw(" newest=" as *u8); rp_epoch(g[RGC_G_NEWEST]); gw(" newest_verdict=" as *u8); gw(rp_verdict_word(g[RGC_G_NEWEST_V])) 908 gw(" green_since_land=" as *u8); rp_epoch(g[RGC_G_GREEN]); gw(" turns_since_land=" as *u8); gn(g[RGC_G_FLIPS]) 909 } else { gw("none tried=" as *u8); gw(d[RP_D_TRIED] as *u8) } 910 gw("\n" as *u8) 911 rp_link_line("wired" as *u8, links[RGC_L_WIRED], why[RGC_L_WIRED]); gw(" treat=" as *u8) 912 if d[RP_D_TREAT_ROW] >= 0 { gw(trt_verdict_name(d[RP_D_TREAT_V])) } else { gw("NO-ROW" as *u8) } 913 gw(" surface_census_row=" as *u8); gn(d[RP_D_ES_FOUND]); gw(" surface=" as *u8); gw(rp_sclass_name(d[RP_D_SCLASS])) 914 if d[RP_D_ES_FL] > 0 { gw(" first_surface=" as *u8); gwb(ev[RP_E_ES_P] as *u8, d[RP_D_ES_FO], d[RP_D_ES_FL]) } 915 gw("\n" as *u8) 916 rp_link_line("operated" as *u8, links[RGC_L_OPERATED], why[RGC_L_OPERATED]); gw(" run=" as *u8); rp_epoch(d[RP_D_RUN]); gw(" read_from=" as *u8); gw(rp_run_src_name(d[RP_D_RUN_SRC])) 917 gw(" agent=" as *u8); gn(d[RP_D_AGENT]); gw(" agent_plane=" as *u8); gw(rp_agp_name(ev[RP_E_AGP_SRC])); gw(" margin_s=" as *u8); gn(rgc_margin_s()); gw(" actlog_window_covers_margin=" as *u8); gn(ev[RP_E_AL_COVER]); gw(" clock_runs_indexed=" as *u8); gn(ev[RP_E_AX_ROWS]) 918 gw("\n" as *u8) 919 rp_link_line("page" as *u8, links[RGC_L_PAGE], why[RGC_L_PAGE]); gw(" page_mtime=" as *u8); rp_epoch(ev[RP_E_PAGE_S]); gw("\n" as *u8) 920 let iw: *u8 = sys_mmap(rgc_issue_word_bound() + 1) 921 rgc_issue_word(iss, iw, 0) 922 gw(" issues=" as *u8); gw(iw); gw(" rework=" as *u8); gn(d[RP_D_REWORK]); gw("\n" as *u8) 923 var k: i64 = 0 924 while k < RGC_ISSUES { if iss[k] == 1 { gw(" " as *u8); gw(rgc_issue_name(k)); gw(": " as *u8); gw(rgc_issue_need(k)); if k == RGC_I_STALE { rp_roster_confs_tail(ev) } gw("\n" as *u8) } k = k + 1 } 925 gw("RUNGCLOSE stage=" as *u8); gw(rgc_state_name(d[RP_D_STAGE])); gw(" links_earn=" as *u8); gw(rgc_state_name(d[RP_D_CHAIN])); gw(" weakest=" as *u8); gw(rgc_weak_name(d[RP_D_WEAK])) 926 let wk: i64 = d[RP_D_WEAK] 927 if wk < RGC_LINKS { gw(" weakest_reads=" as *u8); gw(rgc_hold_name(links[wk])); gw(" need=" as *u8); gw(rgc_why_need(why[wk])); if why[wk] == RGC_W_UNROSTERED { rp_roster_confs_tail(ev) } } 928 if wk >= rgc_weak_issue0() { gw(" need=" as *u8); gw(rgc_issue_need(wk - rgc_weak_issue0())); if wk - rgc_weak_issue0() == RGC_I_STALE { rp_roster_confs_tail(ev) } } 929 return 0 930} 931 932func rp_exit_for(links: *i64, d: *i64) -> i64 { 933 if d[RP_D_STAGE] == RGC_ST_PRODUCTION { return RP_EXIT_DONE } 934 if d[RP_D_STAGE] == RGC_ST_NORUNG { return RP_EXIT_NOT } 935 let wk: i64 = d[RP_D_WEAK] 936 if wk < RGC_LINKS { if links[wk] == RGC_UNOBS { return RP_EXIT_UNOBS } } 937 return RP_EXIT_NOT 938} 939 940func rp_exit_word(x: i64) -> *u8 { 941 if x == RP_EXIT_DONE { return "PRODUCTION" as *u8 } 942 if x == RP_EXIT_UNOBS { return "UNOBSERVABLE" as *u8 } 943 return "NOT-PRODUCTION" as *u8 944} 945 946func rp_usage() -> i64 { 947 gw("usage: nx_rungclose check <domain> <rung> [anchor] | census <domain-or-all> [anchor] | journal <domain> <rung> <measure|land|retract|lesson|queue|done> <text> [anchor] | close|reopen (RETIRED: prints the check, writes nothing)\n" as *u8) 948 gw("exit 0 PRODUCTION, CENSUSED or JOURNALED | 1 below PRODUCTION or REFUSED | 2 usage | 3 UNOBSERVABLE or NO-PLAN\n" as *u8) 949 return RP_EXIT_USAGE 950} 951 952// the bytes one ledger row needs beyond its domain and rung: epoch, two stage words, the actor, the issue word, the 953// rework digit, the pipes and keys -- derived 954func rp_move_row_fixed() -> i64 { 955 var longest: i64 = 0 956 var s: i64 = 0 957 while s < RGC_STATES { let l: i64 = ces_slen(rgc_state_name(s)); if l > longest { longest = l } s = s + 1 } 958 return CES_NUM_CAP + longest * 2 + ces_slen("NEW>|" as *u8) + ces_slen(RP_CENSUS_ACTOR) + ces_slen("|||issues=|rework=" as *u8) + rgc_issue_word_bound() + CES_NUM_CAP + 2 959} 960 961// ONE BOARD: the stage of every rung; worklist rows, snapshot rows and ledger rows into buffers sized from the board. 962// prev carries the last census's snapshot (RP_P_*). Totals accumulate into tot. 963func rp_census(dom: *u8, ev: *i64, quiet: i64, tot: *i64, wk: *i64, prev: *i64, d: *i64) -> i64 { 964 let plan: *u8 = ev[RP_E_PLAN_P] as *u8 965 let pn: i64 = ev[RP_E_PLAN_N] 966 let off: *i64 = sys_mmap(RGC_I64) as *i64 967 var nr: i64 = 0 968 var pp: i64 = 0 969 while pp < pn { 970 let pe: i64 = ces_line_end(plan, pn, pp) 971 let pl0: i64 = ces_field(plan, pp, pe, 0, off) 972 if ces_span_is(plan, off[0], pl0, CES_RUNG_TAG) == 1 { if ces_nfields(plan, pp, pe) >= CES_RUNG_NF { nr = nr + 1 } } 973 pp = pe + 1 974 } 975 // every row is its rung id (a span of the plan, so the ids sum to at most pn) plus the domain plus a fixed part 976 let dl: i64 = ces_slen(dom) 977 let wbuf: *u8 = sys_mmap(pn + nr * (dl + rgc_work_row_fixed()) + 1) 978 let sbuf: *u8 = sys_mmap(pn + nr * (dl + rgc_stage_row_fixed()) + 1) 979 let mbuf: *u8 = sys_mmap(pn + nr * (dl + rp_move_row_fixed()) + 1) 980 var wo: i64 = 0 981 var so: i64 = 0 982 var mo: i64 = 0 983 var wrows: i64 = 0 984 let counts: *i64 = sys_mmap(RGC_STATES * RGC_I64) as *i64 985 let links: *i64 = sys_mmap(RGC_LINKS * RGC_I64) as *i64 986 let f: *i64 = sys_mmap(RGC_F_SLOTS * RGC_I64) as *i64 987 let pout: *i64 = sys_mmap(RGC_SG_OUT_SLOTS * RGC_I64) as *i64 988 let iw: *u8 = sys_mmap(rgc_issue_word_bound() + 1) 989 let iss: *i64 = d[RP_D_ISS] as *i64 990 let now: i64 = ev[RP_E_NOW] 991 var rungs: i64 = 0 992 var unobs: i64 = 0 993 var dups: i64 = 0 994 var rework: i64 = 0 995 var p: i64 = 0 996 while p < pn { 997 let e: i64 = ces_line_end(plan, pn, p) 998 let l0: i64 = ces_field(plan, p, e, 0, off) 999 if ces_span_is(plan, off[0], l0, CES_RUNG_TAG) == 1 { if ces_nfields(plan, p, e) >= CES_RUNG_NF { 1000 let il: i64 = ces_field(plan, p, e, CES_RUNG_F_ID, off) 1001 let rid: *u8 = rp_span_copy(plan, off[0], il) 1002 rp_eval(rid, ev, links, f, d) 1003 let stage: i64 = d[RP_D_STAGE] 1004 counts[stage] = counts[stage] + 1 1005 rungs = rungs + 1 1006 var isun: i64 = 0 1007 if d[RP_D_WEAK] < RGC_LINKS { if links[d[RP_D_WEAK]] == RGC_UNOBS { isun = 1 } } 1008 unobs = unobs + isun 1009 if f[RGC_F_DECLS] > 1 { dups = dups + 1 } 1010 rework = rework + d[RP_D_REWORK] 1011 var k: i64 = 0 1012 while k < RGC_ISSUES { tot[RGC_STATES + RP_T_ISS0 + k] = tot[RGC_STATES + RP_T_ISS0 + k] + iss[k]; k = k + 1 } 1013 if rgc_work_member(stage) == 1 { 1014 wo = rgc_work_row(wbuf, wo, stage, dom, rid, d[RP_D_WEAK], isun, f[RGC_F_LAST], f[RGC_F_COST]) 1015 wrows = wrows + 1 1016 } 1017 // THE CLOSER: the stage against the last census's. Unchanged stage and issues keep their since; a move is a row. 1018 rgc_issue_word(iss, iw, 0) 1019 var since: i64 = now 1020 var from: i64 = 0 - 1 1021 var found: i64 = 0 1022 if prev[RP_P_BASELINE] == 0 { 1023 found = rgc_stage_find(prev[RP_P_BUF] as *u8, prev[RP_P_N], dom, rid, prev[RP_P_CUR] as *i64, pout) 1024 if found == 1 { 1025 from = pout[RGC_SG_OUT_STATE] 1026 if from == stage { if ces_span_is(prev[RP_P_BUF] as *u8, pout[RGC_SG_OUT_IO], pout[RGC_SG_OUT_IL], iw) == 1 { if pout[RGC_SG_OUT_SINCE] > 0 { since = pout[RGC_SG_OUT_SINCE] } } } 1027 } 1028 } 1029 so = rgc_stage_row(sbuf, so, dom, rid, stage, iss, since, d[RP_D_REWORK]) 1030 var moved: i64 = 0 1031 if found == 1 { if from != stage { moved = 1 } } 1032 if prev[RP_P_BASELINE] == 0 { if found == 0 { if stage >= RGC_ST_STARTED { moved = 1; tot[RGC_STATES + RP_T_NEW] = tot[RGC_STATES + RP_T_NEW] + 1 } } } 1033 if moved == 1 { 1034 if found == 1 { 1035 if stage > from { tot[RGC_STATES + RP_T_PROMOTED] = tot[RGC_STATES + RP_T_PROMOTED] + 1 } 1036 if stage < from { tot[RGC_STATES + RP_T_DEMOTED] = tot[RGC_STATES + RP_T_DEMOTED] + 1 } 1037 } 1038 if stage == RGC_ST_PRODUCTION { if from != RGC_ST_PRODUCTION { tot[RGC_STATES + RP_T_ENTERED] = tot[RGC_STATES + RP_T_ENTERED] + 1 } } 1039 mo = ces_fmt_int(mbuf, mo, now) 1040 mo = rgc_work_pipe(mbuf, mo) 1041 mo = ces_cat(mbuf, mo, dom) 1042 mo = rgc_work_pipe(mbuf, mo) 1043 mo = ces_cat(mbuf, mo, rid) 1044 mo = rgc_work_pipe(mbuf, mo) 1045 mo = rgc_move_word(mbuf, mo, from, stage) 1046 mo = rgc_work_pipe(mbuf, mo) 1047 mo = ces_cat(mbuf, mo, RP_CENSUS_ACTOR) 1048 mo = ces_cat(mbuf, mo, "|issues=" as *u8) 1049 mo = ces_cat(mbuf, mo, iw) 1050 mo = ces_cat(mbuf, mo, "|rework=" as *u8) 1051 mo = ces_fmt_int(mbuf, mo, d[RP_D_REWORK]) 1052 mbuf[mo] = CES_CH_NL as u8 1053 mo = mo + 1 1054 } 1055 if quiet == 0 { 1056 gw(" rung=" as *u8); gw(rid); gw(" stage=" as *u8); gw(rgc_state_name(stage)); gw(" weakest=" as *u8); gw(rgc_weak_name(d[RP_D_WEAK])) 1057 if isun == 1 { gw(" (UNOBSERVABLE)" as *u8) } 1058 if d[RP_D_NISS] > 0 { gw(" issues=" as *u8); gw(iw) } 1059 gw("\n" as *u8) 1060 } 1061 } } 1062 p = e + 1 1063 } 1064 var sum: i64 = 0 1065 var s: i64 = 0 1066 gw("RUNGCLOSE-CENSUS domain=" as *u8); gw(dom); gw(" rungs=" as *u8); gn(rungs) 1067 while s < RGC_STATES { 1068 if s != RGC_ST_NORUNG { gw(" " as *u8); gw(rgc_state_name(s)); gw("=" as *u8); gn(counts[s]) } 1069 sum = sum + counts[s] 1070 s = s + 1 1071 } 1072 gw(" sum=" as *u8); gn(sum) 1073 gw(" started_not_production=" as *u8); gn(rungs - counts[RGC_ST_OPEN] - counts[RGC_ST_PRODUCTION]) 1074 gw(" weakest_unobservable=" as *u8); gn(unobs) 1075 gw(" rework=" as *u8); gn(rework) 1076 gw(" duplicate_id_rows=" as *u8); gn(dups) 1077 gw(" verdict=CENSUSED\n" as *u8) 1078 var ts: i64 = 0 1079 while ts < RGC_STATES { tot[ts] = tot[ts] + counts[ts]; ts = ts + 1 } 1080 tot[RGC_STATES + RP_T_RUNGS] = tot[RGC_STATES + RP_T_RUNGS] + rungs 1081 tot[RGC_STATES + RP_T_UNOBS] = tot[RGC_STATES + RP_T_UNOBS] + unobs 1082 tot[RGC_STATES + RP_T_DUPS] = tot[RGC_STATES + RP_T_DUPS] + dups 1083 tot[RGC_STATES + RP_T_REWORK] = tot[RGC_STATES + RP_T_REWORK] + rework 1084 wk[RP_W_BUF] = wbuf as i64 1085 wk[RP_W_FILL] = wo 1086 wk[RP_W_ROWS] = wrows 1087 wk[RP_W_SBUF] = sbuf as i64 1088 wk[RP_W_SFILL] = so 1089 wk[RP_W_MBUF] = mbuf as i64 1090 wk[RP_W_MFILL] = mo 1091 return RP_EXIT_DONE 1092} 1093 1094// an artifact path: the production name, or the anchor plus a suffix for a non-default run 1095func rp_art_path(anchor: *u8, isdefault: i64, prodname: *u8, sfx: *u8) -> *u8 { 1096 let p: *u8 = sys_mmap(ces_slen(anchor) + ces_slen(prodname) + ces_slen(sfx) + ces_slen(RP_TMP_SFX) + 1) 1097 if isdefault == 1 { ces_cat(p, 0, prodname) } else { var o: i64 = ces_cat(p, 0, anchor); ces_cat(p, o, sfx) } 1098 return p 1099} 1100 1101func rp_tmp_path(path: *u8) -> *u8 { 1102 let t: *u8 = sys_mmap(ces_slen(path) + ces_slen(RP_TMP_SFX) + 1) 1103 var o: i64 = ces_cat(t, 0, path) 1104 ces_cat(t, o, RP_TMP_SFX) 1105 return t 1106} 1107 1108// every board on the published list, one summary line each, then the FLEET line -- written as ONE canonical status line 1109// for the digest and the hub to pass through -- plus the worklist, the stage snapshot and the moves 1110func rp_census_all(anchor: *u8, isdefault: i64) -> i64 { 1111 let path: *u8 = sys_mmap(CT_PATH_CAP) 1112 let lenp: *i64 = sys_mmap(RGC_I64) as *i64 1113 lenp[0] = 0 1114 ct_build_path(ct_first_published(), "regen" as *u8, ".list" as *u8, path) 1115 var lst: *u8 = sys_read_file(path, lenp) 1116 if lenp[0] <= 0 { 1117 ct_build_path(ct_second_published(), "regen" as *u8, ".list" as *u8, path) 1118 lst = sys_read_file(path, lenp) 1119 } 1120 if lenp[0] <= 0 { gw("RUNGCLOSE-FLEET regen.list UNREADABLE in both trees verdict=NO-POPULATION\n" as *u8); return RP_EXIT_UNOBS } 1121 let ln: i64 = lenp[0] 1122 let ev: *i64 = sys_mmap(RP_E_SLOTS * RGC_I64) as *i64 1123 rp_gather_shared(ev) 1124 let now: i64 = ev[RP_E_NOW] 1125 let d: *i64 = rp_detail_new() 1126 let tot: *i64 = sys_mmap((RGC_STATES + RP_T_EXTRA) * RGC_I64) as *i64 1127 let wk: *i64 = sys_mmap(RP_W_SLOTS * RGC_I64) as *i64 1128 // the previous snapshot: ABSENT means this census is the BASELINE -- it records every stage and moves nothing 1129 let spath: *u8 = rp_art_path(anchor, isdefault, RP_STAGES, RP_STAGES_SFX) 1130 let prev: *i64 = sys_mmap(RP_P_SLOTS * RGC_I64) as *i64 1131 let pnp: *i64 = sys_mmap(RGC_I64) as *i64 1132 pnp[0] = 0 1133 let pbuf: *u8 = sys_read_file(spath, pnp) 1134 prev[RP_P_BUF] = pbuf as i64 1135 prev[RP_P_N] = pnp[0] 1136 let cur: *i64 = sys_mmap(RGC_I64) as *i64 1137 cur[0] = 0 1138 prev[RP_P_CUR] = cur as i64 1139 prev[RP_P_BASELINE] = 0 1140 if pnp[0] <= 0 { prev[RP_P_BASELINE] = 1 } 1141 // the three whole-file artifacts: temp beside the final name, renamed over it only when every write landed 1142 let wpath: *u8 = rp_art_path(anchor, isdefault, RP_WORKLIST, RP_WORKLIST_SFX) 1143 let wtmp: *u8 = rp_tmp_path(wpath) 1144 let stmp: *u8 = rp_tmp_path(spath) 1145 let wfd: i64 = sys_openat_wr(wtmp, RP_MODE_644) 1146 let sfd: i64 = sys_openat_wr(stmp, RP_MODE_644) 1147 var wshort: i64 = 0 1148 var sshort: i64 = 0 1149 var wbytes: i64 = 0 1150 var sbytes: i64 = 0 1151 if wfd >= 0 { 1152 let hb: *u8 = sys_mmap(rgc_work_head_bound()) 1153 let hn: i64 = rgc_work_head(hb, 0, now) 1154 if sys_write(wfd, hb, hn) != hn { wshort = 1 } 1155 wbytes = hn 1156 } 1157 if sfd >= 0 { 1158 let sb: *u8 = sys_mmap(rgc_stage_head_bound()) 1159 let sn: i64 = rgc_stage_head(sb, 0, now) 1160 if sys_write(sfd, sb, sn) != sn { sshort = 1 } 1161 sbytes = sn 1162 } 1163 let lpath: *u8 = rp_art_path(anchor, isdefault, RP_LEDGER, RP_LEDGER_SFX) 1164 var lfd: i64 = 0 - 1 1165 var lrows: i64 = 0 1166 var lshort: i64 = 0 1167 var wrows: i64 = 0 1168 var boards: i64 = 0 1169 var noplan: i64 = 0 1170 var p: i64 = 0 1171 while p < ln { 1172 let e: i64 = ces_line_end(lst, ln, p) 1173 let l: i64 = e - p 1174 if l > 0 { if lst[p] != (CES_CH_HASH as u8) { 1175 let name: *u8 = rp_span_copy(lst, p, l) 1176 if rp_gather_board(name, ev) == 1 { 1177 boards = boards + 1 1178 rp_census(name, ev, 1, tot, wk, prev, d) 1179 wrows = wrows + wk[RP_W_ROWS] 1180 if wfd >= 0 { if wk[RP_W_FILL] > 0 { 1181 if sys_write(wfd, wk[RP_W_BUF] as *u8, wk[RP_W_FILL]) != wk[RP_W_FILL] { wshort = 1 } 1182 wbytes = wbytes + wk[RP_W_FILL] 1183 } } 1184 if sfd >= 0 { if wk[RP_W_SFILL] > 0 { 1185 if sys_write(sfd, wk[RP_W_SBUF] as *u8, wk[RP_W_SFILL]) != wk[RP_W_SFILL] { sshort = 1 } 1186 sbytes = sbytes + wk[RP_W_SFILL] 1187 } } 1188 if wk[RP_W_MFILL] > 0 { 1189 if lfd < 0 { lfd = sys_openat_append(lpath, RP_MODE_644) } 1190 if lfd >= 0 { 1191 let mb: *u8 = wk[RP_W_MBUF] as *u8 1192 if sys_write(lfd, mb, wk[RP_W_MFILL]) != wk[RP_W_MFILL] { lshort = 1 } 1193 var q: i64 = 0 1194 while q < wk[RP_W_MFILL] { if mb[q] == (CES_CH_NL as u8) { lrows = lrows + 1 } q = q + 1 } 1195 } else { lshort = 1 } 1196 } 1197 } else { noplan = noplan + 1 } 1198 } } 1199 p = e + 1 1200 } 1201 if lfd >= 0 { sys_close(lfd) } 1202 let base: i64 = RGC_STATES 1203 // the fleet line, bounded by the digest's row budget: the stage partition with its sum, the rework count and the moves 1204 let fl: *u8 = sys_mmap(RP_PATH_CAP * 2) 1205 var fo: i64 = ces_cat(fl, 0, "RUNGCLOSE-FLEET asof=" as *u8) 1206 fo = ces_fmt_int(fl, fo, now) 1207 fo = ces_cat(fl, fo, " boards=" as *u8) 1208 fo = ces_fmt_int(fl, fo, boards) 1209 fo = ces_cat(fl, fo, " rungs=" as *u8) 1210 fo = ces_fmt_int(fl, fo, tot[base + RP_T_RUNGS]) 1211 var sum: i64 = 0 1212 var s: i64 = RGC_ST_OPEN 1213 while s < RGC_STATES { 1214 fo = ces_cat(fl, fo, " " as *u8) 1215 fo = ces_cat(fl, fo, rgc_state_name(s)) 1216 fo = ces_cat(fl, fo, "=" as *u8) 1217 fo = ces_fmt_int(fl, fo, tot[s]) 1218 sum = sum + tot[s] 1219 s = s + 1 1220 } 1221 fo = ces_cat(fl, fo, " sum=" as *u8) 1222 fo = ces_fmt_int(fl, fo, sum) 1223 fo = ces_cat(fl, fo, " rework=" as *u8) 1224 fo = ces_fmt_int(fl, fo, tot[base + RP_T_REWORK]) 1225 fo = ces_cat(fl, fo, " moved=" as *u8) 1226 fo = ces_fmt_int(fl, fo, lrows) 1227 fo = ces_cat(fl, fo, " verdict=CENSUSED\n" as *u8) 1228 let sp: *u8 = rp_art_path(anchor, isdefault, RP_STATUS, RP_STATUS_SFX) 1229 let stfd: i64 = sys_openat_wr(sp, RP_MODE_644) 1230 var swrote: i64 = 0 1231 if stfd >= 0 { sys_write(stfd, fl, fo); sys_close(stfd); swrote = 1 } 1232 gw("RUNGCLOSE-STATUS path=" as *u8); gw(sp); gw(" wrote=" as *u8); gn(swrote); gw("\n" as *u8) 1233 // a torn list is never published: on a short write the temp stays beside the old list and the old list stands. 1234 // rows against started_not_production is two counters of one population: they reconcile or the line says they do not. 1235 var wlanded: i64 = 0 1236 if wfd >= 0 { 1237 sys_close(wfd) 1238 if wshort == 0 { if sys_renameat(wtmp, wpath) == 0 { wlanded = 1 } } 1239 } 1240 var slanded: i64 = 0 1241 if sfd >= 0 { 1242 sys_close(sfd) 1243 if sshort == 0 { if sys_renameat(stmp, spath) == 0 { slanded = 1 } } 1244 } 1245 let snp: i64 = tot[base + RP_T_RUNGS] - tot[RGC_ST_OPEN] - tot[RGC_ST_PRODUCTION] 1246 var recon: i64 = 0 1247 if wrows == snp { recon = 1 } 1248 gw("RUNGCLOSE-WORKLIST path=" as *u8); gw(wpath); gw(" rows=" as *u8); gn(wrows); gw(" started_not_production=" as *u8); gn(snp); gw(" reconciled=" as *u8); gn(recon) 1249 gw(" bytes=" as *u8); gn(wbytes); gw(" short_write=" as *u8); gn(wshort); gw(" landed=" as *u8); gn(wlanded); gw("\n" as *u8) 1250 gw("RUNGCLOSE-STAGES path=" as *u8); gw(spath); gw(" rungs=" as *u8); gn(tot[base + RP_T_RUNGS]); gw(" bytes=" as *u8); gn(sbytes); gw(" short_write=" as *u8); gn(sshort); gw(" landed=" as *u8); gn(slanded) 1251 gw(" baseline=" as *u8); gn(prev[RP_P_BASELINE]); gw("\n" as *u8) 1252 gw("RUNGCLOSE-MOVES ledger=" as *u8); gw(lpath); gw(" rows=" as *u8); gn(lrows); gw(" promoted=" as *u8); gn(tot[base + RP_T_PROMOTED]); gw(" demoted=" as *u8); gn(tot[base + RP_T_DEMOTED]) 1253 gw(" entered_production=" as *u8); gn(tot[base + RP_T_ENTERED]); gw(" new=" as *u8); gn(tot[base + RP_T_NEW]); gw(" short_write=" as *u8); gn(lshort) 1254 if prev[RP_P_BASELINE] == 1 { gw(" (BASELINE: no previous snapshot, so nothing can have moved -- the next census diffs against this one)" as *u8) } 1255 gw("\n" as *u8) 1256 gw("RUNGCLOSE-ISSUES" as *u8) 1257 var k: i64 = 0 1258 while k < RGC_ISSUES { gw(" " as *u8); gw(rgc_issue_name(k)); gw("=" as *u8); gn(tot[base + RP_T_ISS0 + k]); k = k + 1 } 1259 gw(" rework=" as *u8); gn(tot[base + RP_T_REWORK]); gw(" weakest_unobservable=" as *u8); gn(tot[base + RP_T_UNOBS]); gw(" duplicate_id_rows=" as *u8); gn(tot[base + RP_T_DUPS]) 1260 gw(" noplan=" as *u8); gn(noplan); gw("\n" as *u8) 1261 gw("RUNGCLOSE-EVIDENCE roster_rows_indexed=" as *u8); gn(ev[RP_E_GX_ROWS]); gw(" surface_census_bytes=" as *u8); gn(ev[RP_E_ES_N]); gw(" actlog_bytes=" as *u8); gn(ev[RP_E_AL_BYTES]) 1262 gw(" actlog_window_bytes=" as *u8); gn(ev[RP_E_AL_N]); gw(" actlog_window_covers_margin=" as *u8); gn(ev[RP_E_AL_COVER]); gw(" clock_runs_indexed=" as *u8); gn(ev[RP_E_AX_ROWS]) 1263 gw(" agent_plane=" as *u8); gw(rp_agp_name(ev[RP_E_AGP_SRC])); gw(" roster_beats=" as *u8); gn(ev[RP_E_R_BEATS]); gw(" roster_beats_other_journal=" as *u8); gn(ev[RP_E_R_OTHER]); gw(" roster_confs_read=" as *u8); gn(ev[RP_E_RT_READ]); gw("/" as *u8); gn(ev[RP_E_RT_DISTINCT]); gw(" roster_journals_read=" as *u8); gn(ev[RP_E_RJ_READ]); gw("/" as *u8); gn(ev[RP_E_RJ_K]); rp_roster_confs_tail(ev); gw("\n" as *u8) 1264 gwb(fl, 0, fo) 1265 return RP_EXIT_DONE 1266} 1267 1268// the path of the board's plan in the tree that answers, in the resolver's published order; 1 when one opens 1269func rp_plan_path(dom: *u8, path: *u8) -> i64 { 1270 ct_build_path(ct_first_published(), dom, BF_PLAN_SUFFIX, path) 1271 let f1: i64 = sys_openat_rd(path) 1272 if f1 >= 0 { sys_close(f1); return 1 } 1273 ct_build_path(ct_second_published(), dom, BF_PLAN_SUFFIX, path) 1274 let f2: i64 = sys_openat_rd(path) 1275 if f2 >= 0 { sys_close(f2); return 1 } 1276 return 0 1277} 1278 1279// JOURNAL (EC55 item 3): one row appended to the board's own plan BY GRAMMAR -- the rung is declared (main refuses NORUNG 1280// before this), the kind is one the boards render, the text cannot break the row, and the row starts a line. ONE O_APPEND 1281// write, so a concurrent writer can interleave rows and never bytes. Returns 1 appended, 0 refused (the reason is printed). 1282func rp_journal(rung: *u8, kind: *u8, text: *u8, dom: *u8, ev: *i64) -> i64 { 1283 let off: *i64 = sys_mmap(RGC_I64) as *i64 1284 if rgc_kind_ok(kind) == 0 { 1285 gw("JOURNAL REFUSED kind=" as *u8); gw(kind); gw(" is not a kind the boards render: measure, land, retract, lesson, queue or done\n" as *u8) 1286 return 0 1287 } 1288 let tk: i64 = rgc_text_ok(text, off) 1289 if tk == 0 { gw("JOURNAL REFUSED the text is empty: a row that says nothing is not a journal row\n" as *u8); return 0 } 1290 if tk != 1 { 1291 gw("JOURNAL REFUSED the text carries a pipe or a line break at byte " as *u8); gn(off[0]); gw(": a pipe invents a field and a line break invents a row\n" as *u8) 1292 return 0 1293 } 1294 let path: *u8 = sys_mmap(CT_PATH_CAP) 1295 if rp_plan_path(dom, path) == 0 { gw("JOURNAL REFUSED the board's plan could not be opened in either tree\n" as *u8); return 0 } 1296 let plan: *u8 = ev[RP_E_PLAN_P] as *u8 1297 let pn: i64 = ev[RP_E_PLAN_N] 1298 var lead: i64 = 0 1299 if pn > 0 { if plan[pn - 1] != (CES_CH_NL as u8) { lead = 1 } } 1300 let row: *u8 = sys_mmap(rgc_log_row_bound(rung, kind, text)) 1301 let rn: i64 = rgc_log_row(row, 0, lead, sys_now_realtime_sec(), rung, kind, text) 1302 let fd: i64 = sys_openat_append(path, RP_MODE_644) 1303 if fd < 0 { gw("JOURNAL REFUSED the plan could not be opened for append: " as *u8); gw(path); gw("\n" as *u8); return 0 } 1304 let w: i64 = sys_write(fd, row, rn) 1305 sys_close(fd) 1306 gw("JOURNAL path=" as *u8); gw(path); gw(" kind=" as *u8); gw(kind); gw(" row_bytes=" as *u8); gn(rn); gw(" wrote=" as *u8); gn(w) 1307 if w == rn { gw(" appended=1\n" as *u8); return 1 } 1308 gw(" appended=0 SHORT-WRITE: read the plan's tail before journaling again\n" as *u8) 1309 return 0 1310} 1311 1312func main(argc: i64, argv: *i64) -> i64 { 1313 if argc < 3 { return rp_usage() } 1314 let verb: *u8 = argv[1] as *u8 1315 let dom: *u8 = argv[2] as *u8 1316 var ischeck: i64 = 0 1317 var iscensus: i64 = 0 1318 var isjournal: i64 = 0 1319 var isretired: i64 = 0 1320 if ces_span_is(verb, 0, ces_slen(verb), "check" as *u8) == 1 { ischeck = 1 } 1321 if ces_span_is(verb, 0, ces_slen(verb), "census" as *u8) == 1 { iscensus = 1 } 1322 if ces_span_is(verb, 0, ces_slen(verb), "journal" as *u8) == 1 { isjournal = 1 } 1323 if ces_span_is(verb, 0, ces_slen(verb), "close" as *u8) == 1 { isretired = 1 } 1324 if ces_span_is(verb, 0, ces_slen(verb), "reopen" as *u8) == 1 { isretired = 1 } 1325 if ischeck + iscensus + isjournal + isretired == 0 { return rp_usage() } 1326 var anchor: *u8 = RP_ANCHOR_DEFAULT 1327 var isdefault: i64 = 1 1328 var aarg: i64 = 4 1329 if isretired == 1 { aarg = 5 } 1330 if iscensus == 1 { aarg = 3 } 1331 if isjournal == 1 { aarg = 6 } 1332 if isjournal == 1 { if argc < 6 { return rp_usage() } } 1333 if argc > aarg { anchor = argv[aarg] as *u8; isdefault = 0 } 1334 if iscensus == 0 { if argc < 4 { return rp_usage() } } 1335 if iscensus == 1 { if ces_span_is(dom, 0, ces_slen(dom), "all" as *u8) == 1 { return rp_census_all(anchor, isdefault) } } 1336 let ev: *i64 = sys_mmap(RP_E_SLOTS * RGC_I64) as *i64 1337 rp_gather_shared(ev) 1338 if rp_gather_board(dom, ev) == 0 { 1339 gw("RUNGCLOSE domain=" as *u8); gw(dom); gw(" plan=UNREADABLE-IN-BOTH-TREES verdict=NO-PLAN\n" as *u8) 1340 return RP_EXIT_UNOBS 1341 } 1342 let d: *i64 = rp_detail_new() 1343 if iscensus == 1 { 1344 let tot1: *i64 = sys_mmap((RGC_STATES + RP_T_EXTRA) * RGC_I64) as *i64 1345 let wk1: *i64 = sys_mmap(RP_W_SLOTS * RGC_I64) as *i64 1346 let prev1: *i64 = sys_mmap(RP_P_SLOTS * RGC_I64) as *i64 1347 prev1[RP_P_BASELINE] = 1 1348 return rp_census(dom, ev, 0, tot1, wk1, prev1, d) 1349 } 1350 let rung: *u8 = argv[3] as *u8 1351 let links: *i64 = sys_mmap(RGC_LINKS * RGC_I64) as *i64 1352 let f: *i64 = sys_mmap(RGC_F_SLOTS * RGC_I64) as *i64 1353 var declared: i64 = rp_eval(rung, ev, links, f, d) 1354 if declared == 0 { 1355 gw("RUNGCLOSE domain=" as *u8); gw(dom); gw(" rung=" as *u8); gw(rung); gw(" stage=NORUNG (the board declares no such rung) verdict=NOT-PRODUCTION\n" as *u8) 1356 return RP_EXIT_NOT 1357 } 1358 // A rung id DECLARED MORE THAN ONCE makes every row that names it ambiguous (measured 2026-09-17: two seats appended 1359 // their own EC64 to one board minutes apart). check SAYS so; journal REFUSES until one of them is renumbered. 1360 if f[RGC_F_DECLS] > 1 { 1361 gw("DUPLICATE-RUNG-ID domain=" as *u8); gw(dom); gw(" rung=" as *u8); gw(rung); gw(" declared=" as *u8); gn(f[RGC_F_DECLS]) 1362 gw(": two rung rows share this id, so every journal row that names it is ambiguous -- renumber the later one\n" as *u8) 1363 if isjournal == 1 { gw(" verdict=REFUSED\n" as *u8); return RP_EXIT_NOT } 1364 } 1365 if isretired == 1 { 1366 gw("SIGN-OFF RETIRED 2026-09-18 (operator: completion needs no manual sign-off): the stage below is MEASURED from the gate roster, the surface census and the clock's run rows; nothing was written\n" as *u8) 1367 } 1368 var jok: i64 = 0 1369 if isjournal == 1 { 1370 jok = rp_journal(rung, argv[4] as *u8, argv[5] as *u8, dom, ev) 1371 if jok == 1 { 1372 rp_gather_board(dom, ev) 1373 declared = rp_eval(rung, ev, links, f, d) 1374 } 1375 } 1376 rp_print(dom, rung, ev, links, f, d) 1377 let x: i64 = rp_exit_for(links, d) 1378 if isjournal == 1 { 1379 if jok == 1 { gw(" verdict=JOURNALED\n" as *u8); return RP_EXIT_DONE } 1380 gw(" verdict=REFUSED\n" as *u8) 1381 return RP_EXIT_NOT 1382 } 1383 gw(" verdict=" as *u8); gw(rp_exit_word(x)); gw("\n" as *u8) 1384 return x 1385}