code wiki / (root) / nx_scaffolding_audit.nx

nx_scaffolding_audit.nx source

↩ module page · 389 lines · 20024 B

1// nx_scaffolding_audit.nx -- REPO-FILE audit primitive that enforces the 2// no-temporary-third-party-scaffolding-when-substrate-arc-exists cardinal. 3// 4// AUDIT-FIRST CORRECTION (2026-05-20, post-creation): user caught that I 5// built this without checking for existing prior art. The substrate 6// already ships an audit family: 7// - nx_audit_dashboard.nx : substrate-STATE aggregation 8// (hunter kills / gatherer yields / 9// xenocell alerts / immune health / 10// book unseen-alert / organism pressure / 11// pollinate pending) 12// - nx_audit_server_daemon.nx : exposes substrate state over HTTP 13// - nx_audit_server_main.nx : daemon entry point 14// - nx_audit_compose_test.nx : test harness 15// - nx_ai_audit.nx : AI-output audit 16// - nx_adversarial_pattern_audit.nx: adversarial input pattern detection 17// 18// THIS primitive (nx_scaffolding_audit) is a DIFFERENT DOMAIN: it audits 19// REPO FILES against the SCAFFOLDING_REPLACED_BY cardinal. Distinct from 20// the substrate-state-aggregation primitives above. However it could 21// COMPOSE INTO the dashboard: future nx_audit_dashboard rows could include 22// a "scaffolding violations" counter sourced from nx_saudit_run, surfacing 23// the cardinal-compliance state alongside other substrate health metrics. 24// 25// Per feedback-no-temporary-third-party-scaffolding-when-substrate-arc-exists 26// audit-first rule: ls nxc2/runtime/ before authoring new primitives. 27// 28// 29// module: nishi-core.ops.scaffolding_audit 30// depends: nishi-core.perception.profile + nishi-core.io.syscalls 31// disk_kb: 9 32// capability: OPS 33// wired_status: PARTIAL_WIRED (nx_saudit_check_one_file + nx_saudit_run 34// both GRADUATED to basic-glue 2026-05-20: 35// compose sys_read_file + nx_str_find + 36// nx_dir_list; refuses self-exempt loophole 37// at substrate level; aggregates per-file 38// findings into NxSauditSummary; rest of 39// entries still PARTIAL_WIRED) 40// 41// MISSING_CAPABILITIES (still queued): 42// - FS_WALK_FILTER (walk repo via nx_dir_list + prefix-exclusion + 43// content-heuristic-match; nx_dir FULLY_WIRED; just needs glue) 44// - HEADER_VALUE_EXTRACT (parse primitive-name list out of the header line) 45// - PRIMITIVE_FILE_EXISTS (nx_fs_stat against runtime/nx_<name>.nx) 46// - WIRED_STATUS_PARSE (read "wired_status:" header from each named 47// primitive .nx file -- enables SUNSET_OVERDUE detection) 48// - VIOLATION_REPORT_EMIT (structured JSONL via nx_log_jsonl FULLY_WIRED) 49// - CI_GATE_DISPATCH (exit-code-bits encoding for pre-commit / CI gate) 50// 51// license_tier: PUBLIC_NISHI_SUBSTRATE 52// genealogy_id: feedback-no-temporary-third-party-scaffolding-when-substrate-arc-exists_2026 + 53// feedback-substrate-primitives-meta-not-one-off + 54// feedback-self-surfacing-intelligence-staged-autonomy + 55// feedback-substrate-does-heavy-lifting-user-is-partner-not-gate 56// 57// USER CALLED OUT 2026-05-20: "lint is this nishi lint or what the is it" 58// 59// The earlier lint_scaffolding_audit.sh was itself a shell script + grep 60// + awk + sed + find -- exactly the kind of third-party scaffolding the 61// cardinal it enforces forbids. Self-exempting the auditor via a 62// SCAFFOLDING_AUDIT_SELF_EXEMPT header was a loophole; the cardinal does 63// NOT permit auditor self-exemption. The lint script gets a proper 64// SCAFFOLDING_REPLACED_BY header naming this primitive in the same 65// commit. 66// 67// THIS primitive IS the Nishi-native replacement. Same audit logic, 68// implemented in NishiLang, composing with already-FULLY_WIRED substrate 69// (nx_dir for fs walk, nx_fs_stat for primitive existence check, 70// nx_log_jsonl for violation output). When this primitive reaches 71// FULLY_WIRED, the shell lint gets DELETED. 72// 73// Reuse set: 74// - Enforce the no-temporary-third-party-scaffolding cardinal repo-side 75// - Future cardinal-class lints will be sibling primitives 76// (nx_genealogy_audit, nx_dependency_audit, nx_naming_audit, etc.) 77// sharing a common nx_audit_engine base when one is extracted 78// - Pre-commit hook substrate (composes with nx_proc_spawn to register 79// itself as the hook handler) 80// - CI gate substrate (same pre-commit interface used by remote CI) 81 82import "nx_syscalls.nx" 83import "nx_perceptual_profile.nx" 84import "nx_string_ops.nx" 85import "nx_dir.nx" 86 87// ===== Violation class sealed enum =============================== 88 89const NX_SAUDIT_VIOLATION_MISSING_HEADER: i64 = 1 // no SCAFFOLDING_REPLACED_BY line 90const NX_SAUDIT_VIOLATION_BAD_REFERENCE: i64 = 2 // named primitive file missing 91const NX_SAUDIT_VIOLATION_SUNSET_OVERDUE: i64 = 4 // primitive FULLY_WIRED; 92 // scaffolding should be gone 93const NX_SAUDIT_VIOLATION_AUDITOR_SELF_EXEMPT: i64 = 8 // ANY file claiming 94 // self-exempt header -- 95 // refused per cardinal 96 97func nx_saudit_violation_name(v: i64) -> *u8 { 98 if v == NX_SAUDIT_VIOLATION_MISSING_HEADER { return "MISSING_HEADER" } 99 if v == NX_SAUDIT_VIOLATION_BAD_REFERENCE { return "BAD_REFERENCE" } 100 if v == NX_SAUDIT_VIOLATION_SUNSET_OVERDUE { return "SUNSET_OVERDUE" } 101 if v == NX_SAUDIT_VIOLATION_AUDITOR_SELF_EXEMPT { return "AUDITOR_SELF_EXEMPT" } 102 return "UNKNOWN_VIOLATION" 103} 104 105// ===== Scope policy sealed enum ================================== 106// 107// Per SCAFFOLDING_INVENTORY.md. Content-heuristic in-scope detection 108// avoids exclude-by-prefix being too permissive or too strict. 109 110const NX_SAUDIT_SCOPE_OPS_DIR: i64 = 1 // bench/ops/* 111const NX_SAUDIT_SCOPE_OPERATIONAL_FILENAME: i64 = 2 // nishifamily_* etc 112const NX_SAUDIT_SCOPE_CONTENT_HEURISTIC: i64 = 3 // grep for nginx/sudo/etc 113const NX_SAUDIT_SCOPE_OUT_OF_SCOPE: i64 = 4 // substrate-test prefixes 114 115func nx_saudit_scope_name(s: i64) -> *u8 { 116 if s == NX_SAUDIT_SCOPE_OPS_DIR { return "OPS_DIR" } 117 if s == NX_SAUDIT_SCOPE_OPERATIONAL_FILENAME { return "OPERATIONAL_FILENAME" } 118 if s == NX_SAUDIT_SCOPE_CONTENT_HEURISTIC { return "CONTENT_HEURISTIC" } 119 if s == NX_SAUDIT_SCOPE_OUT_OF_SCOPE { return "OUT_OF_SCOPE" } 120 return "UNKNOWN_SCOPE" 121} 122 123// ===== Audit verdict ============================================= 124 125const NX_SAUDIT_VERDICT_CLEAN: i64 = 0 126const NX_SAUDIT_VERDICT_VIOLATIONS_PRESENT: i64 = 1 // any violation class 127const NX_SAUDIT_VERDICT_DEPENDENCY_MISSING: i64 = 2 // PARTIAL_WIRED default 128 129func nx_saudit_verdict_name(v: i64) -> *u8 { 130 if v == NX_SAUDIT_VERDICT_CLEAN { return "CLEAN" } 131 if v == NX_SAUDIT_VERDICT_VIOLATIONS_PRESENT { return "VIOLATIONS_PRESENT" } 132 if v == NX_SAUDIT_VERDICT_DEPENDENCY_MISSING { return "DEPENDENCY_MISSING" } 133 return "UNKNOWN_SAUDIT_VERDICT" 134} 135 136// ===== Per-file finding struct =================================== 137 138struct NxSauditFinding { 139 file_path_hash_ptr: *u8 140 file_path_hash_len: i64 141 scope_class: i64 // NX_SAUDIT_SCOPE_* 142 violation_class: i64 // NX_SAUDIT_VIOLATION_* 143 named_primitives_hash_ptr: *u8 // content-hash of declared 144 // replacement primitives string 145 named_primitives_hash_len: i64 146 primitive_count: i64 147 primitives_existing_count: i64 // how many of named primitives 148 // have .nx files 149 primitives_fully_wired_count: i64 // how many are FULLY_WIRED 150} 151 152// ===== Audit result summary struct =============================== 153 154struct NxSauditSummary { 155 total_in_scope: i64 156 ok_count: i64 157 missing_header_count: i64 158 bad_reference_count: i64 159 sunset_overdue_count: i64 160 auditor_self_exempt_count: i64 161 overall_verdict: i64 // NX_SAUDIT_VERDICT_* 162 exit_code_bits: i64 // OR'd violation classes for CI gate 163} 164 165// ===== Top-level entry stubs ===================================== 166 167// nx_saudit_run -- walk the repo, audit every in-scope file, emit findings. 168// Returns OR'd violation-class exit code bits for CI gate use. 169// 170// GRADUATED 2026-05-20 from PARTIAL_WIRED -> wired-at-basic-glue. 171// Composes nx_dir_list (FULLY_WIRED) -> per-row nx_saudit_check_one_file -> 172// aggregate counters into NxSauditSummary. 173// 174// v1 scope: hardcoded walk of "nxc2/bench/ops/" + filter to *.sh files. 175// v2 expands to bench/ + content-heuristic per SCAFFOLDING_INVENTORY.md. 176// repo_root_ptr/len accepted for forward compatibility; v1 ignores. 177// 178// Per cardinal feedback-no-temporary-third-party-scaffolding-when-substrate-arc-exists: 179// this is the Nishi-native replacement for the lint_scaffolding_audit.sh 180// repo-walk + per-file-grep chain. When v2 expands scope to match the 181// shell lint's full coverage, the shell lint becomes SUNSET_OVERDUE. 182 183func nx_saudit_run(repo_root_ptr: *u8, repo_root_len: i64, 184 summary_out_ptr: *NxSauditSummary) -> i64 { 185 if repo_root_len <= 0 { return NX_SAUDIT_VERDICT_DEPENDENCY_MISSING } 186 if summary_out_ptr == 0 as *NxSauditSummary { return NX_SAUDIT_VERDICT_DEPENDENCY_MISSING } 187 188 // Initialize summary counters. 189 summary_out_ptr.total_in_scope = 0 190 summary_out_ptr.ok_count = 0 191 summary_out_ptr.missing_header_count = 0 192 summary_out_ptr.bad_reference_count = 0 193 summary_out_ptr.sunset_overdue_count = 0 194 summary_out_ptr.auditor_self_exempt_count = 0 195 summary_out_ptr.overall_verdict = NX_SAUDIT_VERDICT_CLEAN 196 summary_out_ptr.exit_code_bits = 0 197 198 // Build dir path "nxc2/bench/ops" (15 bytes + NUL). 199 let dir: *u8 = sys_mmap(64) 200 dir[0]=0x6E as u8; dir[1]=0x78 as u8; dir[2]=0x63 as u8; dir[3]=0x32 as u8 // "nxc2" 201 dir[4]=0x2F as u8 // "/" 202 dir[5]=0x62 as u8; dir[6]=0x65 as u8; dir[7]=0x6E as u8; dir[8]=0x63 as u8; dir[9]=0x68 as u8 // "bench" 203 dir[10]=0x2F as u8 // "/" 204 dir[11]=0x6F as u8; dir[12]=0x70 as u8; dir[13]=0x73 as u8 // "ops" 205 dir[14]=0 // NUL 206 207 // Allocate row storage: 256 rows × 32 bytes = 8 KB. 208 let rows: *NxDirRow = sys_mmap(8192) as *NxDirRow 209 // Name arena: 16 KB of name bytes. 210 let names: *u8 = sys_mmap(16384) 211 let dres: *NxDirResult = sys_mmap(64) as *NxDirResult 212 213 let dv: i64 = nx_dir_list(dir, rows, 256, names, 16384, 0, dres) 214 if dv != NX_DIR_OK { 215 // Directory unreadable; substrate cannot audit. 216 return NX_SAUDIT_VERDICT_DEPENDENCY_MISSING 217 } 218 219 // Build ".sh" suffix for filename filter (3 bytes + NUL). 220 let sh: *u8 = sys_mmap(16) 221 sh[0]=0x2E as u8; sh[1]=0x73 as u8; sh[2]=0x68 as u8; sh[3]=0 // ".sh" 222 // Build "_" leading-underscore byte for prefix exclusion. 223 // (helpers prefixed _ are out-of-scope per inventory) 224 225 let n: i64 = dres.n_filled 226 let finding: *NxSauditFinding = sys_mmap(96) as *NxSauditFinding 227 let fullpath: *u8 = sys_mmap(512) 228 229 var i: i64 = 0 230 while i < n { 231 let row: *NxDirRow = nx_dir_row_at(rows, i) 232 // Filter: regular file, name ends in ".sh", not prefixed "_". 233 if nx_dir_row_is_regular_file(row) != 0 { 234 if nx_str_ends_with(row.name_ptr, row.name_len, sh, 3) != 0 { 235 if row.name_ptr[0] != 0x5F as u8 { // skip "_" prefix 236 // Build fullpath = "nxc2/bench/ops/" + name + NUL. 237 var j: i64 = 0 238 while j < 14 { fullpath[j] = dir[j]; j = j + 1 } 239 fullpath[14] = 0x2F as u8 // "/" 240 var k: i64 = 0 241 while k < row.name_len { 242 fullpath[15 + k] = row.name_ptr[k] 243 k = k + 1 244 } 245 fullpath[15 + row.name_len] = 0 246 247 summary_out_ptr.total_in_scope = summary_out_ptr.total_in_scope + 1 248 249 let v: i64 = nx_saudit_check_one_file(fullpath, 250 15 + row.name_len, 251 finding) 252 if v == NX_SAUDIT_VERDICT_CLEAN { 253 summary_out_ptr.ok_count = summary_out_ptr.ok_count + 1 254 } 255 if v == NX_SAUDIT_VIOLATION_MISSING_HEADER { 256 summary_out_ptr.missing_header_count = summary_out_ptr.missing_header_count + 1 257 summary_out_ptr.exit_code_bits = summary_out_ptr.exit_code_bits | NX_SAUDIT_VIOLATION_MISSING_HEADER 258 } 259 if v == NX_SAUDIT_VIOLATION_BAD_REFERENCE { 260 summary_out_ptr.bad_reference_count = summary_out_ptr.bad_reference_count + 1 261 summary_out_ptr.exit_code_bits = summary_out_ptr.exit_code_bits | NX_SAUDIT_VIOLATION_BAD_REFERENCE 262 } 263 if v == NX_SAUDIT_VIOLATION_SUNSET_OVERDUE { 264 summary_out_ptr.sunset_overdue_count = summary_out_ptr.sunset_overdue_count + 1 265 summary_out_ptr.exit_code_bits = summary_out_ptr.exit_code_bits | NX_SAUDIT_VIOLATION_SUNSET_OVERDUE 266 } 267 if v == NX_SAUDIT_VIOLATION_AUDITOR_SELF_EXEMPT { 268 summary_out_ptr.auditor_self_exempt_count = summary_out_ptr.auditor_self_exempt_count + 1 269 summary_out_ptr.exit_code_bits = summary_out_ptr.exit_code_bits | NX_SAUDIT_VIOLATION_AUDITOR_SELF_EXEMPT 270 } 271 } 272 } 273 } 274 i = i + 1 275 } 276 277 if summary_out_ptr.exit_code_bits == 0 { 278 summary_out_ptr.overall_verdict = NX_SAUDIT_VERDICT_CLEAN 279 } else { 280 summary_out_ptr.overall_verdict = NX_SAUDIT_VERDICT_VIOLATIONS_PRESENT 281 } 282 return summary_out_ptr.overall_verdict 283} 284 285// nx_saudit_check_one_file -- per-file audit; used by editor / pre-commit 286// for fast single-file checks before running the whole repo sweep. 287// 288// GRADUATED 2026-05-20 from PARTIAL_WIRED -> wired-at-basic-glue. 289// Composes sys_read_file + nx_str_find for marker detection. 290// Checks: 291// 1. Does the file contain "# SCAFFOLDING_AUDIT_SELF_EXEMPT" (loophole)? 292// -> NX_SAUDIT_VIOLATION_AUDITOR_SELF_EXEMPT 293// 2. Does it contain "# SCAFFOLDING_REPLACED_BY:" header? 294// -> if YES: NX_SAUDIT_VERDICT_CLEAN 295// -> if NO: NX_SAUDIT_VIOLATION_MISSING_HEADER 296// Per-primitive existence + wired_status checks (BAD_REFERENCE + 297// SUNSET_OVERDUE) still queued -- require parsing primitive names out of 298// the header value and nx_fs_stat against runtime/nx_<name>.nx. 299 300func nx_saudit_check_one_file(file_path_ptr: *u8, file_path_len: i64, 301 finding_out_ptr: *NxSauditFinding) -> i64 { 302 if file_path_len <= 0 { return NX_SAUDIT_VERDICT_DEPENDENCY_MISSING } 303 304 let content_len_ptr: *i64 = sys_mmap(16) as *i64 305 let content: *u8 = sys_read_file(file_path_ptr, content_len_ptr) 306 let content_len: i64 = content_len_ptr[0] 307 308 if content_len <= 0 { 309 // Unreadable / empty file -- treat as missing-header so it surfaces. 310 finding_out_ptr.violation_class = NX_SAUDIT_VIOLATION_MISSING_HEADER 311 return NX_SAUDIT_VIOLATION_MISSING_HEADER 312 } 313 314 // Build "# SCAFFOLDING_AUDIT_SELF_EXEMPT" marker (30 bytes) first -- 315 // self-exempt is a stronger violation than missing-header, so we check 316 // it before the proper-header check. 317 let exempt: *u8 = sys_mmap(40) 318 exempt[0]=0x23 as u8; exempt[1]=0x20 as u8 // "# " 319 exempt[2]=0x53 as u8; exempt[3]=0x43 as u8; exempt[4]=0x41 as u8 // "SCA" 320 exempt[5]=0x46 as u8; exempt[6]=0x46 as u8; exempt[7]=0x4F as u8 // "FFO" 321 exempt[8]=0x4C as u8; exempt[9]=0x44 as u8; exempt[10]=0x49 as u8 // "LDI" 322 exempt[11]=0x4E as u8; exempt[12]=0x47 as u8 // "NG" 323 exempt[13]=0x5F as u8 // "_" 324 exempt[14]=0x41 as u8; exempt[15]=0x55 as u8; exempt[16]=0x44 as u8 // "AUD" 325 exempt[17]=0x49 as u8; exempt[18]=0x54 as u8 // "IT" 326 exempt[19]=0x5F as u8 // "_" 327 exempt[20]=0x53 as u8; exempt[21]=0x45 as u8; exempt[22]=0x4C as u8 // "SEL" 328 exempt[23]=0x46 as u8 // "F" 329 exempt[24]=0x5F as u8 // "_" 330 exempt[25]=0x45 as u8; exempt[26]=0x58 as u8; exempt[27]=0x45 as u8 // "EXE" 331 exempt[28]=0x4D as u8; exempt[29]=0x50 as u8; exempt[30]=0x54 as u8 // "MPT" 332 333 let epos: i64 = nx_str_find(content, content_len, exempt, 31) as i64 334 if epos >= 0 { 335 // Auditor self-exemption REFUSED per cardinal. 336 finding_out_ptr.violation_class = NX_SAUDIT_VIOLATION_AUDITOR_SELF_EXEMPT 337 return NX_SAUDIT_VIOLATION_AUDITOR_SELF_EXEMPT 338 } 339 340 // Build "# SCAFFOLDING_REPLACED_BY:" marker (26 bytes). 341 let marker: *u8 = sys_mmap(40) 342 marker[0]=0x23 as u8; marker[1]=0x20 as u8 // "# " 343 marker[2]=0x53 as u8; marker[3]=0x43 as u8; marker[4]=0x41 as u8 // "SCA" 344 marker[5]=0x46 as u8; marker[6]=0x46 as u8; marker[7]=0x4F as u8 // "FFO" 345 marker[8]=0x4C as u8; marker[9]=0x44 as u8; marker[10]=0x49 as u8 // "LDI" 346 marker[11]=0x4E as u8; marker[12]=0x47 as u8 // "NG" 347 marker[13]=0x5F as u8 // "_" 348 marker[14]=0x52 as u8; marker[15]=0x45 as u8; marker[16]=0x50 as u8 // "REP" 349 marker[17]=0x4C as u8; marker[18]=0x41 as u8; marker[19]=0x43 as u8 // "LAC" 350 marker[20]=0x45 as u8; marker[21]=0x44 as u8 // "ED" 351 marker[22]=0x5F as u8 // "_" 352 marker[23]=0x42 as u8; marker[24]=0x59 as u8 // "BY" 353 marker[25]=0x3A as u8 // ":" 354 355 let pos: i64 = nx_str_find(content, content_len, marker, 26) as i64 356 if pos >= 0 { 357 finding_out_ptr.violation_class = 0 // CLEAN -- no violation 358 return NX_SAUDIT_VERDICT_CLEAN 359 } 360 361 finding_out_ptr.violation_class = NX_SAUDIT_VIOLATION_MISSING_HEADER 362 return NX_SAUDIT_VIOLATION_MISSING_HEADER 363} 364 365// nx_saudit_in_scope -- inspector: does this file fall under audit scope? 366// Returns NX_SAUDIT_SCOPE_* class or NX_SAUDIT_SCOPE_OUT_OF_SCOPE. 367 368func nx_saudit_in_scope(file_path_ptr: *u8, file_path_len: i64) -> i64 { 369 if file_path_len <= 0 { return NX_SAUDIT_SCOPE_OUT_OF_SCOPE } 370 return NX_SAUDIT_SCOPE_OUT_OF_SCOPE 371} 372 373// nx_saudit_refuse_self_exempt -- per cardinal: NO auditor may exempt 374// itself from the rule it enforces. This function structurally refuses 375// any SCAFFOLDING_AUDIT_SELF_EXEMPT marker. Closes the loophole my 376// earlier shell lint exploited. 377 378func nx_saudit_refuse_self_exempt(file_path_ptr: *u8, file_path_len: i64) -> i64 { 379 if file_path_len <= 0 { return 0 } 380 // PARTIAL_WIRED: marker detection queued. When wired, returns 381 // NX_SAUDIT_VIOLATION_AUDITOR_SELF_EXEMPT if the marker is present. 382 return 0 383} 384 385// nx_saudit_get_last_verdict -- inspector. 386 387func nx_saudit_get_last_verdict() -> i64 { 388 return NX_SAUDIT_VERDICT_DEPENDENCY_MISSING 389}