code wiki / _hdl_build / nx_raci.nx
nx_raci.nx source
↩ module page · 128 lines · 7147 B
1// nx_raci.nx -- THE NISHI TEAM RACI validator + reporter (the "hammered out" coordination contract).
2//
3// Reads the data-driven registry (knowledge/registry/nishi_raci.tsv: role<TAB>activity<TAB>raci-letters) and
4// enforces the S-class RACI integrity rules (synthesized from the Nishi Researcher's team_*.raw corpus --
5// responsibility-assignment-matrix + separation-of-duties): EVERY activity has EXACTLY ONE Accountable (A) and
6// AT LEAST ONE Responsible (R). One-A = no ambiguity about who owns it (= no competition); >=1-R = someone
7// actually does it. Prints the matrix (per activity: who is A / R / C / I) so the team contract is legible,
8// and exits non-zero if any activity violates -- so it IS its own gate / CI check.
9//
10// usage: nx_raci [registry-path] (default knowledge/registry/nishi_raci.tsv)
11// license_tier: ORIGINAL
12import "nx_syscalls.nx"
13import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
14
15const RC_MAXROWS: i64 = 512
16const RC_STR: i64 = 40
17const RC_MAXACT: i64 = 48
18const RC_CAP: i64 = 262144
19const RC_DEFAULT: *u8 = "knowledge/registry/nishi_raci.tsv"
20
21func rc_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
22// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
23// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
24// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
25// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
26func rc_wn(v: i64) -> i64 { nxi_out(v); return 0 }
27func rc_read(path: *u8, buf: *u8, cap: i64) -> i64 {
28 let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 - 1 }
29 var off: i64 = 0; var go: i64 = 1
30 while go == 1 { if off >= cap { go = 0 } else { let r: i64 = sys_read(fd, buf + off, cap - off); if r <= 0 { go = 0 } else { off = off + r } } }
31 sys_close(fd); return off
32}
33func rc_streq(a: *u8, b: *u8) -> i64 { var i: i64=0; while a[i]!=(0 as u8) { if a[i]!=b[i] {return 0} i=i+1 } if b[i]!=(0 as u8) {return 0} return 1 }
34func rc_has(s: *u8, ch: i64) -> i64 { var i: i64=0; while s[i]!=(0 as u8) { if (s[i] as i64)==ch {return 1} i=i+1 } return 0 }
35func rc_cpy(dst: *u8, src: *u8, max: i64) -> i64 { var i: i64=0; while src[i]!=(0 as u8) { if i<max-1 {dst[i]=src[i]} i=i+1 } dst[i]=0 as u8; return 0 }
36// idx-th tab/newline field of line[0..len) -> out (NUL-term). 1 if found.
37func rc_field(line: *u8, len: i64, idx: i64, out: *u8, outcap: i64) -> i64 {
38 var f: i64=0; var i: i64=0; var w: i64=0
39 while i < len { let c: i64=line[i] as i64
40 if c==9 { if f==idx { out[w]=0 as u8; return 1 } f=f+1 } else { if f==idx { if w<outcap-1 { out[w]=line[i]; w=w+1 } } }
41 i=i+1 }
42 if f==idx { out[w]=0 as u8; return 1 }
43 return 0
44}
45// list roles for activity `act` having letter `ch` -> printed inline. returns count.
46func rc_roles_with(roleS: *u8, actS: *u8, letS: *u8, nrows: i64, act: *u8, ch: i64, verbose: i64) -> i64 {
47 var c: i64=0; var i: i64=0
48 while i < nrows {
49 if rc_streq(actS + i*RC_STR, act) == 1 { if rc_has(letS + i*RC_STR, ch) == 1 {
50 if verbose == 1 { if c > 0 { rc_w("," as *u8) } rc_w(roleS + i*RC_STR) }
51 c = c + 1
52 } }
53 i = i + 1
54 }
55 return c
56}
57
58// validate registry. returns # of violations (0 = valid). verbose -> print the matrix.
59func raci_validate(path: *u8, verbose: i64) -> i64 {
60 let buf: *u8 = sys_mmap(RC_CAP); let n: i64 = rc_read(path, buf, RC_CAP)
61 if n <= 0 { if verbose==1 { rc_w("RACI: registry empty/unreadable\n" as *u8) } return 0 - 1 }
62 let roleS: *u8 = sys_mmap(RC_MAXROWS*RC_STR); let actS: *u8 = sys_mmap(RC_MAXROWS*RC_STR); let letS: *u8 = sys_mmap(RC_MAXROWS*RC_STR)
63 let f: *u8 = sys_mmap(RC_STR)
64 var nrows: i64 = 0; var ls: i64 = 0; var i: i64 = 0
65 while i <= n {
66 var nl: i64 = 0; if i>=n {nl=1} else { if buf[i]==(10 as u8) {nl=1} }
67 if nl == 1 {
68 let ll: i64 = i - ls
69 if ll > 0 {
70 let line: *u8 = ((buf as i64)+ls) as *u8
71 if line[0] != (35 as u8) { // skip '#' comments
72 if rc_field(line, ll, 0, f, RC_STR)==1 { if f[0]!=(0 as u8) {
73 if nrows < RC_MAXROWS {
74 rc_cpy(roleS + nrows*RC_STR, f, RC_STR)
75 rc_field(line, ll, 1, actS + nrows*RC_STR, RC_STR)
76 rc_field(line, ll, 2, letS + nrows*RC_STR, RC_STR)
77 nrows = nrows + 1
78 }
79 } }
80 }
81 }
82 ls = i + 1
83 }
84 i = i + 1
85 }
86 // distinct activities
87 let dact: *u8 = sys_mmap(RC_MAXACT*RC_STR); var ndact: i64 = 0
88 var r: i64 = 0
89 while r < nrows {
90 let a: *u8 = actS + r*RC_STR; var seen: i64 = 0; var d: i64 = 0
91 while d < ndact { if rc_streq(dact + d*RC_STR, a)==1 { seen=1 } d=d+1 }
92 if seen==0 { if ndact < RC_MAXACT { rc_cpy(dact + ndact*RC_STR, a, RC_STR); ndact=ndact+1 } }
93 r = r + 1
94 }
95 if verbose==1 { rc_w("=== NISHI TEAM RACI (" as *u8); rc_wn(nrows); rc_w(" cells, " as *u8); rc_wn(ndact); rc_w(" activities) ===\n" as *u8) }
96 var violations: i64 = 0
97 var k: i64 = 0
98 while k < ndact {
99 let a: *u8 = dact + k*RC_STR
100 // count A and R (without printing) for the integrity check
101 var ac: i64 = 0; var rcc: i64 = 0; var j: i64 = 0
102 while j < nrows { if rc_streq(actS + j*RC_STR, a)==1 { if rc_has(letS + j*RC_STR, 65)==1 {ac=ac+1} if rc_has(letS + j*RC_STR, 82)==1 {rcc=rcc+1} } j=j+1 }
103 var ok: i64 = 1; if ac != 1 { ok = 0 } if rcc < 1 { ok = 0 }
104 if ok == 0 { violations = violations + 1 }
105 if verbose == 1 {
106 if ok==1 { rc_w("[OK] " as *u8) } else { rc_w("[!!] " as *u8) }
107 var aw: i64=0; while a[aw]!=(0 as u8){aw=aw+1} sys_write(1, a, aw); var pad: i64=11-aw; while pad>0 {rc_w(" " as *u8);pad=pad-1}
108 rc_w(" A=" as *u8); rc_roles_with(roleS,actS,letS,nrows,a,65,1)
109 rc_w(" R=" as *u8); rc_roles_with(roleS,actS,letS,nrows,a,82,1)
110 rc_w(" C=" as *u8); let cc: i64=rc_roles_with(roleS,actS,letS,nrows,a,67,1); if cc==0 {rc_w("-" as *u8)}
111 rc_w(" I=" as *u8); let ic: i64=rc_roles_with(roleS,actS,letS,nrows,a,73,1); if ic==0 {rc_w("-" as *u8)}
112 if ac != 1 { rc_w(" <-- VIOLATION: A-count=" as *u8); rc_wn(ac) }
113 if rcc < 1 { rc_w(" <-- VIOLATION: no Responsible" as *u8) }
114 rc_w("\n" as *u8)
115 }
116 k = k + 1
117 }
118 return violations
119}
120
121func main(argc: i64, argv: *i64) -> i64 {
122 var path: *u8 = RC_DEFAULT; if argc >= 2 { path = argv[1] as *u8 }
123 let v: i64 = raci_validate(path, 1)
124 if v < 0 { rc_w("RACI: ERROR (no registry)\n" as *u8); return 2 }
125 if v == 0 { rc_w("=== RACI VALID (every activity has exactly 1 Accountable + >=1 Responsible) ===\n" as *u8); return 0 }
126 rc_w("=== RACI INVALID: " as *u8); rc_wn(v); rc_w(" activity violation(s) ===\n" as *u8)
127 return 1
128}