code wiki / (root) / nx_doc_census.nx

nx_doc_census.nx source

↩ module page · 607 lines · 23157 B

1// nx_doc_census.nx -- D1 stone of NISHI_DOCS_DOCTRINE_ROADMAP V2. 2// 3// Bits-up classifier for substrate documentation files. Operation- 4// alizes the V2 header schema introduced in docs doctrine V2: 5// **Quadrant:** (Diátaxis) 6// **Topic Type:** (DITA) 7// **Status:** (lifecycle) 8// **Trust State:** (composes nx_trust_state cardinal) 9// **Competitive State:** (composes nx_competitive_state cardinal) 10// 11// Per [[feedback-docs-above-book-hyper-efficient-structured-delivery]] 12// + [[feedback-audits-are-ecosystem-citizens-not-islands]] cardinals: 13// this audit measures the substrate's own docs against the V2 KPI 14// surface + emits NxEtgEntry into the substrate's attestation chain. 15// 16// Composes: 17// nx_grep (S1; substring pattern detection in doc bytes) 18// nx_string_ops (helpers) 19// nx_etg (NxEtgEntry attestation) 20// nx_trust_state (composed: docs themselves have trust state) 21// nx_competitive_state (composed: docs decay too) 22// 23// **Quadrant:** REFERENCE + EXPLANATION (Diátaxis) 24// **Topic Type:** REFERENCE (DITA) 25// **Status:** DRAFT (D1 first stone) 26// **Trust State:** WRITTEN_UNTESTED (this file is brand-new; not yet 27// KAT-passed; per the never-trust-just-written cardinal) 28// **Competitive State:** UNCLASSIFIED (no benchmark vs Diátaxis- 29// linter / Vale.sh / textlint incumbents yet) 30 31// nx_safety_envelope: 32// intended_use: "audit substrate docs for V2 header schema 33// compliance; classify; aggregate; attest" 34// sil_target: SIL2 35// evidence: [kat_quadrant_classifier, 36// kat_topic_type_classifier, 37// kat_status_classifier, 38// kat_trust_competitive_presence, 39// kat_aggregator_arithmetic, 40// kat_etg_attestation_emitted] 41// hazard_register: [bug-tape-marker-substring-false-positive, 42// bug-tape-multiple-markers-classification, 43// bug-tape-pct-divbyzero] 44// verdict: NOT_YET_EVALUATED 45 46import "nx_syscalls.nx" 47import "nx_string_ops.nx" 48import "nx_grep.nx" 49import "nx_grep_rt.nx" // nx_grep_any lives here, not in nx_grep.nx 50import "nx_etg.nx" 51import "nx_dir.nx" 52const NX_MAGIC_65536: i64 = 65536 53const NX_MAGIC_1024: i64 = 1024 54 55// ===== Diátaxis quadrant sealed enum ============================== 56 57const NX_DOC_Q_NONE: i64 = 0 58const NX_DOC_Q_TUTORIAL: i64 = 1 59const NX_DOC_Q_HOW_TO: i64 = 2 60const NX_DOC_Q_REFERENCE: i64 = 3 61const NX_DOC_Q_EXPLANATION: i64 = 4 62const NX_DOC_Q_MIXED: i64 = 5 63const NX_DOC_Q_N: i64 = 6 64 65func nx_doc_q_is_valid(q: i64) -> i64 { 66 if q < 0 { return 0 } 67 if q >= NX_DOC_Q_N { return 0 } 68 return 1 69} 70 71func nx_doc_q_name(q: i64) -> *u8 { 72 if q == NX_DOC_Q_NONE { return "NONE" } 73 if q == NX_DOC_Q_TUTORIAL { return "TUTORIAL" } 74 if q == NX_DOC_Q_HOW_TO { return "HOW_TO" } 75 if q == NX_DOC_Q_REFERENCE { return "REFERENCE" } 76 if q == NX_DOC_Q_EXPLANATION { return "EXPLANATION" } 77 if q == NX_DOC_Q_MIXED { return "MIXED" } 78 return "UNKNOWN" 79} 80 81// ===== DITA topic type sealed enum ================================ 82 83const NX_DOC_T_NONE: i64 = 0 84const NX_DOC_T_CONCEPT: i64 = 1 85const NX_DOC_T_TASK: i64 = 2 86const NX_DOC_T_REFERENCE: i64 = 3 87const NX_DOC_T_PATTERN: i64 = 4 88const NX_DOC_T_EXPLANATION: i64 = 5 89const NX_DOC_T_N: i64 = 6 90 91func nx_doc_t_is_valid(t: i64) -> i64 { 92 if t < 0 { return 0 } 93 if t >= NX_DOC_T_N { return 0 } 94 return 1 95} 96 97func nx_doc_t_name(t: i64) -> *u8 { 98 if t == NX_DOC_T_NONE { return "NONE" } 99 if t == NX_DOC_T_CONCEPT { return "CONCEPT" } 100 if t == NX_DOC_T_TASK { return "TASK" } 101 if t == NX_DOC_T_REFERENCE { return "REFERENCE" } 102 if t == NX_DOC_T_PATTERN { return "PATTERN" } 103 if t == NX_DOC_T_EXPLANATION { return "EXPLANATION" } 104 return "UNKNOWN" 105} 106 107// ===== Status sealed enum ========================================= 108 109const NX_DOC_S_NONE: i64 = 0 110const NX_DOC_S_DRAFT: i64 = 1 111const NX_DOC_S_CANONICAL: i64 = 2 112const NX_DOC_S_SUPERSEDED: i64 = 3 113const NX_DOC_S_DEPRECATED: i64 = 4 114const NX_DOC_S_N: i64 = 5 115 116func nx_doc_s_is_valid(s: i64) -> i64 { 117 if s < 0 { return 0 } 118 if s >= NX_DOC_S_N { return 0 } 119 return 1 120} 121 122func nx_doc_s_name(s: i64) -> *u8 { 123 if s == NX_DOC_S_NONE { return "NONE" } 124 if s == NX_DOC_S_DRAFT { return "DRAFT" } 125 if s == NX_DOC_S_CANONICAL { return "CANONICAL" } 126 if s == NX_DOC_S_SUPERSEDED { return "SUPERSEDED" } 127 if s == NX_DOC_S_DEPRECATED { return "DEPRECATED" } 128 return "UNKNOWN" 129} 130 131// ===== Marker detectors via nx_grep =============================== 132// 133// V2 header schema uses Markdown-bold markers like '**Quadrant:** 134// TUTORIAL'. We scan for the LONGEST distinctive marker first so 135// EXPLANATION isn't shadowed by REFERENCE (substring containment). 136 137func nx_doc_classify_quadrant(content: *u8, n: i64) -> i64 { 138 if nx_grep_any(content, n, "**Quadrant:** TUTORIAL", 22) == 1 { return NX_DOC_Q_TUTORIAL } 139 if nx_grep_any(content, n, "**Quadrant:** HOW_TO", 20) == 1 { return NX_DOC_Q_HOW_TO } 140 if nx_grep_any(content, n, "**Quadrant:** REFERENCE + EXPLANATION", 37) == 1 { return NX_DOC_Q_MIXED } 141 if nx_grep_any(content, n, "**Quadrant:** MIXED", 19) == 1 { return NX_DOC_Q_MIXED } 142 if nx_grep_any(content, n, "**Quadrant:** EXPLANATION", 25) == 1 { return NX_DOC_Q_EXPLANATION } 143 if nx_grep_any(content, n, "**Quadrant:** REFERENCE", 23) == 1 { return NX_DOC_Q_REFERENCE } 144 return NX_DOC_Q_NONE 145} 146 147func nx_doc_classify_topic_type(content: *u8, n: i64) -> i64 { 148 if nx_grep_any(content, n, "**Topic Type:** CONCEPT", 23) == 1 { return NX_DOC_T_CONCEPT } 149 if nx_grep_any(content, n, "**Topic Type:** TASK", 20) == 1 { return NX_DOC_T_TASK } 150 if nx_grep_any(content, n, "**Topic Type:** REFERENCE", 25) == 1 { return NX_DOC_T_REFERENCE } 151 if nx_grep_any(content, n, "**Topic Type:** PATTERN", 23) == 1 { return NX_DOC_T_PATTERN } 152 if nx_grep_any(content, n, "**Topic Type:** EXPLANATION", 27) == 1 { return NX_DOC_T_EXPLANATION } 153 return NX_DOC_T_NONE 154} 155 156func nx_doc_classify_status(content: *u8, n: i64) -> i64 { 157 if nx_grep_any(content, n, "**Status:** CANONICAL", 21) == 1 { return NX_DOC_S_CANONICAL } 158 if nx_grep_any(content, n, "**Status:** SUPERSEDED", 22) == 1 { return NX_DOC_S_SUPERSEDED } 159 if nx_grep_any(content, n, "**Status:** DEPRECATED", 22) == 1 { return NX_DOC_S_DEPRECATED } 160 if nx_grep_any(content, n, "**Status:** DRAFT", 17) == 1 { return NX_DOC_S_DRAFT } 161 return NX_DOC_S_NONE 162} 163 164// ===== Presence detectors for Trust + Competitive markers ========= 165 166func nx_doc_has_trust_state(content: *u8, n: i64) -> i64 { 167 if nx_grep_any(content, n, "**Trust State:**", 16) == 1 { return 1 } 168 return 0 169} 170 171func nx_doc_has_competitive_state(content: *u8, n: i64) -> i64 { 172 if nx_grep_any(content, n, "**Competitive State:**", 22) == 1 { return 1 } 173 return 0 174} 175 176// V2 doctrine: research-honest docs cite WebSearch-verified sources. 177// Returns 1 if the doc has any of the WebSearch-verified provenance 178// markers. 179func nx_doc_has_websearch_provenance(content: *u8, n: i64) -> i64 { 180 if nx_grep_any(content, n, "WebSearch-verified", 18) == 1 { return 1 } 181 if nx_grep_any(content, n, "WebSearch verified", 18) == 1 { return 1 } 182 if nx_grep_any(content, n, "Sources:", 8) == 1 { return 1 } 183 return 0 184} 185 186// V2 doctrine: bidirectional links use [[name]] Zettelkasten style. 187// Returns 1 if doc has any bidirectional link. 188func nx_doc_has_bidirectional_links(content: *u8, n: i64) -> i64 { 189 if nx_grep_any(content, n, "[[", 2) == 1 { return 1 } 190 return 0 191} 192 193// ===== Per-doc V2 compliance record ============================== 194 195struct NxDocV2Record { 196 quadrant: i64, // NX_DOC_Q_* 197 topic_type: i64, // NX_DOC_T_* 198 status: i64, // NX_DOC_S_* 199 has_trust_state: i64, // 0/1 200 has_competitive_state: i64, // 0/1 201 has_websearch_provenance: i64, // 0/1 202 has_bidirectional_links: i64, // 0/1 203 compliance_score: i64, // 0..7 (sum of axes met) 204} 205 206func nx_doc_classify( 207 content: *u8, n: i64, 208 r: *NxDocV2Record 209) -> i64 { 210 r.quadrant = nx_doc_classify_quadrant(content, n) 211 r.topic_type = nx_doc_classify_topic_type(content, n) 212 r.status = nx_doc_classify_status(content, n) 213 r.has_trust_state = nx_doc_has_trust_state(content, n) 214 r.has_competitive_state = nx_doc_has_competitive_state(content, n) 215 r.has_websearch_provenance = nx_doc_has_websearch_provenance(content, n) 216 r.has_bidirectional_links = nx_doc_has_bidirectional_links(content, n) 217 218 var score: i64 = 0 219 if r.quadrant != NX_DOC_Q_NONE { score = score + 1 } 220 if r.topic_type != NX_DOC_T_NONE { score = score + 1 } 221 if r.status != NX_DOC_S_NONE { score = score + 1 } 222 if r.has_trust_state == 1 { score = score + 1 } 223 if r.has_competitive_state == 1 { score = score + 1 } 224 if r.has_websearch_provenance == 1 { score = score + 1 } 225 if r.has_bidirectional_links == 1 { score = score + 1 } 226 r.compliance_score = score 227 return 0 228} 229 230// ===== Aggregator counts ========================================== 231 232struct NxDocCensusCounts { 233 n_total: i64, 234 n_quadrant_declared: i64, 235 n_topic_type_declared: i64, 236 n_status_declared: i64, 237 n_trust_state_declared: i64, 238 n_competitive_state_declared: i64, 239 n_websearch_provenance: i64, 240 n_bidirectional_links: i64, 241 n_full_compliance: i64, // score == 7 242 n_zero_compliance: i64, // score == 0 243} 244 245func nx_doc_census_init(c: *NxDocCensusCounts) { 246 c.n_total = 0 247 c.n_quadrant_declared = 0 248 c.n_topic_type_declared = 0 249 c.n_status_declared = 0 250 c.n_trust_state_declared = 0 251 c.n_competitive_state_declared = 0 252 c.n_websearch_provenance = 0 253 c.n_bidirectional_links = 0 254 c.n_full_compliance = 0 255 c.n_zero_compliance = 0 256} 257 258func nx_doc_census_accumulate(c: *NxDocCensusCounts, r: *NxDocV2Record) -> i64 { 259 c.n_total = c.n_total + 1 260 if r.quadrant != NX_DOC_Q_NONE { c.n_quadrant_declared = c.n_quadrant_declared + 1 } 261 if r.topic_type != NX_DOC_T_NONE { c.n_topic_type_declared = c.n_topic_type_declared + 1 } 262 if r.status != NX_DOC_S_NONE { c.n_status_declared = c.n_status_declared + 1 } 263 if r.has_trust_state == 1 { c.n_trust_state_declared = c.n_trust_state_declared + 1 } 264 if r.has_competitive_state == 1 { c.n_competitive_state_declared = c.n_competitive_state_declared + 1 } 265 if r.has_websearch_provenance == 1 { c.n_websearch_provenance = c.n_websearch_provenance + 1 } 266 if r.has_bidirectional_links == 1 { c.n_bidirectional_links = c.n_bidirectional_links + 1 } 267 if r.compliance_score == 7 { c.n_full_compliance = c.n_full_compliance + 1 } 268 if r.compliance_score == 0 { c.n_zero_compliance = c.n_zero_compliance + 1 } 269 return 0 270} 271 272// Compliance percentage (compliance_score sum / max possible) * 100. 273// Returns 0 if no docs scanned (no div-by-zero; INCONCLUSIVE shape). 274func nx_doc_census_pct_compliance(c: *NxDocCensusCounts) -> i64 { 275 if c.n_total <= 0 { return 0 } 276 let max_possible: i64 = c.n_total * 7 277 let achieved: i64 = c.n_quadrant_declared + c.n_topic_type_declared + 278 c.n_status_declared + c.n_trust_state_declared + 279 c.n_competitive_state_declared + c.n_websearch_provenance + 280 c.n_bidirectional_links 281 return (achieved * 100) / max_possible 282} 283 284// ===== ETG attestation ============================================ 285// 286// Maps compliance % to outcome: 287// >= 75% -> CONFIRMED (docs at substrate-honest health) 288// >= 50% -> INCONCLUSIVE (mid-band; needs work) 289// < 50% -> FALSIFIED (docs proliferation pathology) 290 291func nx_doc_census_to_etg_outcome(c: *NxDocCensusCounts) -> i64 { 292 if c.n_total <= 0 { return NX_ETG_OUTCOME_INCONCLUSIVE } 293 let pct: i64 = nx_doc_census_pct_compliance(c) 294 if pct >= 75 { return NX_ETG_OUTCOME_CONFIRMED } 295 if pct >= 50 { return NX_ETG_OUTCOME_INCONCLUSIVE } 296 return NX_ETG_OUTCOME_FALSIFIED 297} 298 299func nx_doc_census_attest( 300 c: *NxDocCensusCounts, 301 entry: *NxEtgEntry, 302 silicon_serial: i64, 303 selector_version: i64, 304 timestamp_q14: i64 305) -> i64 { 306 let outcome: i64 = nx_doc_census_to_etg_outcome(c) 307 let pct: i64 = nx_doc_census_pct_compliance(c) 308 return nx_etg_entry_init( 309 entry, 310 silicon_serial, 311 NX_ETG_PROBE_AUDIT_GENEALOGY_MAP, // reusing audit probe family 312 NX_ETG_CLAIM_PRIOR_CALIBRATION, 313 c.n_total, // claim: docs scanned 314 pct, // measurement: compliance % 315 outcome, 316 selector_version, 317 timestamp_q14 318 ) 319} 320 321// ===== Path join helper =========================================== 322// 323// Build "<dir>/<name>" + NUL into out buffer. Substrate-honest no 324// silent overrun. Returns total bytes (excluding NUL) or -1. 325// (Local copy because nx_tool_census's _census_path_join is file- 326// scoped; future consolidation queued -- per the no-tool- 327// proliferation bit-level cardinal, this duplicate should fold 328// into a shared nx_path_join primitive.) 329 330func _doccensus_path_join( 331 dir: *u8, n_dir: i64, 332 name: *u8, n_name: i64, 333 out: *u8, out_cap: i64 334) -> i64 { 335 if n_dir + 1 + n_name + 1 > out_cap { return -1 } 336 var i: i64 = 0 337 while i < n_dir { 338 out[i] = dir[i] 339 i = i + 1 340 } 341 out[n_dir] = 47 // '/' 342 var j: i64 = 0 343 while j < n_name { 344 out[n_dir + 1 + j] = name[j] 345 j = j + 1 346 } 347 out[n_dir + 1 + n_name] = 0 // NUL 348 return n_dir + 1 + n_name 349} 350 351// ===== Walk + classify + accumulate ============================== 352// 353// Opens dir_path; lists entries via nx_dir_list; for each regular- 354// file with .md suffix opens + reads up to 4 KiB header + classifies 355// via nx_doc_classify + accumulates into NxDocCensusCounts. 356// 357// Composes nx_dir_list (LIST) + sys_openat_rd/sys_read/sys_close 358// (READ) + nx_doc_classify (CLASSIFY) -- all bits-up NishiLang. 359// 360// Returns 0 on success; negative on dir-list failure. 361 362func nx_doc_census_walk_dir( 363 dir_path: *u8, n_dir_path: i64, 364 counts: *NxDocCensusCounts 365) -> i64 { 366 // 512 rows × 32B + 64KB name arena = 80KB total. Handles 367 // substrate's ~278 .md docs in one shot. Substrate-honest 368 // lift of the prior 64-row cap. If a dir genuinely has 369 // >512 entries, result.verdict will be NX_DIR_TRUNCATED; 370 // we accept TRUNCATED but report what we got. 371 let rows_buf: *u8 = sys_mmap(512 * NX_DIR_ROW_BYTES + 64) 372 let rows: *NxDirRow = rows_buf as *NxDirRow 373 let name_arena: *u8 = sys_mmap(NX_MAGIC_65536) 374 let result_buf: *u8 = sys_mmap(NX_DIR_RESULT_BYTES + 16) 375 let result: *NxDirResult = result_buf as *NxDirResult 376 377 nx_dir_list(dir_path, rows, 512, name_arena, NX_MAGIC_65536, 0, result) 378 if result.verdict != NX_DIR_OK { 379 if result.verdict != NX_DIR_TRUNCATED { return -10 } 380 } 381 382 // 64 KiB buffer per doc + read-until-EOF loop. Bumped from 383 // single 16 KiB sys_read because Sources blocks at the end of 384 // 30-55 KB roadmaps were beyond the window. 64 KiB covers 385 // ~95% of substrate roadmaps; the few >64 KB docs (TLS 1.3 / 386 // multi-roadmap merges) get TRUNCATED -- substrate-honest 387 // disclosure via the verdict. 388 let hdr_buf: *u8 = sys_mmap(NX_MAGIC_65536) 389 let path_buf: *u8 = sys_mmap(NX_MAGIC_1024) 390 let r_buf: *u8 = sys_mmap(128) 391 let r: *NxDocV2Record = r_buf as *NxDocV2Record 392 let md_suffix: *u8 = ".md" 393 394 var i: i64 = 0 395 while i < result.n_filled { 396 let row: *NxDirRow = nx_dir_row_at(rows, i) 397 if row.is_dotlike == 1 { 398 i = i + 1 399 continue 400 } 401 if row.dtype != NX_DT_REG { 402 i = i + 1 403 continue 404 } 405 // Filter to .md files only. 406 if nx_str_ends_with(row.name_ptr, row.name_len, md_suffix, 3) != 1 { 407 i = i + 1 408 continue 409 } 410 411 let p_len: i64 = _doccensus_path_join( 412 dir_path, n_dir_path, 413 row.name_ptr, row.name_len, 414 path_buf, NX_MAGIC_1024 415 ) 416 if p_len < 0 { 417 i = i + 1 418 continue 419 } 420 let fd: i64 = sys_openat_rd(path_buf) 421 if fd < 0 { 422 i = i + 1 423 continue 424 } 425 // Read-until-EOF loop into 64 KiB buffer. TRUNCATED at 64 KiB 426 // (substrate-honest cap; future: streaming-scan to avoid the 427 // cap entirely; queued). 428 var content_len: i64 = 0 429 let cap_bytes: i64 = NX_MAGIC_65536 430 var keep_reading: i64 = 1 431 while keep_reading == 1 { 432 let remaining: i64 = cap_bytes - content_len 433 if remaining <= 0 { 434 keep_reading = 0 435 continue 436 } 437 let dst: *u8 = (hdr_buf as *u8) + content_len 438 let n_chunk: i64 = sys_read(fd, dst, remaining) 439 if n_chunk <= 0 { 440 keep_reading = 0 441 continue 442 } 443 content_len = content_len + n_chunk 444 } 445 sys_close(fd) 446 447 nx_doc_classify(hdr_buf, content_len, r) 448 nx_doc_census_accumulate(counts, r) 449 i = i + 1 450 } 451 return 0 452} 453 454// ===== Verdict reporting via sys_write =========================== 455// 456// Substrate-honest visibility: print counts to stdout so humans 457// can see the verdict without parsing attestation hashes. Per 458// the cardinal "document what is written" -- this primitive 459// documents what the audit found. 460 461// Helper: write a string literal + integer + newline to fd 1. 462// Allocates scratch each call (simple; caller doesn't manage). 463func _docc_write_kv(label: *u8, n_label: i64, value: i64) { 464 sys_write(1, label, n_label) 465 let buf: *u8 = sys_mmap(32) 466 let n_int: i64 = nx_str_format_int(value, buf, 32) 467 sys_write(1, buf, n_int) 468 sys_write(1, "\n", 1) 469} 470 471// Write the outcome name (CONFIRMED / INCONCLUSIVE / FALSIFIED) + 472// newline to fd 1. 473func _docc_write_outcome(outcome: i64) { 474 let prefix: *u8 = " outcome: " 475 sys_write(1, prefix, 19) 476 if outcome == NX_ETG_OUTCOME_CONFIRMED { 477 sys_write(1, "CONFIRMED", 9) 478 } 479 if outcome == NX_ETG_OUTCOME_INCONCLUSIVE { 480 sys_write(1, "INCONCLUSIVE", 12) 481 } 482 if outcome == NX_ETG_OUTCOME_FALSIFIED { 483 sys_write(1, "FALSIFIED", 9) 484 } 485 sys_write(1, "\n", 1) 486} 487 488func nx_doc_census_report_stdout(c: *NxDocCensusCounts) { 489 sys_write(1, "nx_doc_census verdict:\n", 23) 490 _docc_write_kv(" total: ", 19, c.n_total) 491 _docc_write_kv(" quadrant: ", 19, c.n_quadrant_declared) 492 _docc_write_kv(" topic_type: ", 19, c.n_topic_type_declared) 493 _docc_write_kv(" status: ", 19, c.n_status_declared) 494 _docc_write_kv(" trust_state: ", 19, c.n_trust_state_declared) 495 _docc_write_kv(" competitive: ", 19, c.n_competitive_state_declared) 496 _docc_write_kv(" websearch: ", 19, c.n_websearch_provenance) 497 _docc_write_kv(" bidir_links: ", 19, c.n_bidirectional_links) 498 _docc_write_kv(" full_compliance: ", 19, c.n_full_compliance) 499 _docc_write_kv(" zero_compliance: ", 19, c.n_zero_compliance) 500 _docc_write_kv(" pct_compliance: ", 19, nx_doc_census_pct_compliance(c)) 501 _docc_write_outcome(nx_doc_census_to_etg_outcome(c)) 502} 503 504// ===== Per-doc verdict listing =================================== 505// 506// Substrate names which specific docs are at which compliance score. 507// Lets V2 backfill prioritize specific docs rather than guessing. 508// 509// Output format per doc: 510// score=N/7 <name>\n 511// followed by the standard aggregate verdict. 512 513func _docc_write_doc_line(name: *u8, n_name: i64, score: i64) { 514 let prefix: *u8 = " score=" 515 sys_write(1, prefix, 8) 516 let buf: *u8 = sys_mmap(8) 517 let n_int: i64 = nx_str_format_int(score, buf, 8) 518 sys_write(1, buf, n_int) 519 sys_write(1, "/7 ", 4) 520 sys_write(1, name, n_name) 521 sys_write(1, "\n", 1) 522} 523 524// Walk + classify + accumulate + EMIT PER-DOC LINE to fd 1. 525// Mirrors nx_doc_census_walk_dir but writes a `score=N/7 <name>` 526// line per doc to stdout. Same buffer/cap discipline. 527 528func nx_doc_census_walk_dir_listing( 529 dir_path: *u8, n_dir_path: i64, 530 counts: *NxDocCensusCounts 531) -> i64 { 532 let rows_buf: *u8 = sys_mmap(512 * NX_DIR_ROW_BYTES + 64) 533 let rows: *NxDirRow = rows_buf as *NxDirRow 534 let name_arena: *u8 = sys_mmap(NX_MAGIC_65536) 535 let result_buf: *u8 = sys_mmap(NX_DIR_RESULT_BYTES + 16) 536 let result: *NxDirResult = result_buf as *NxDirResult 537 538 nx_dir_list(dir_path, rows, 512, name_arena, NX_MAGIC_65536, 0, result) 539 if result.verdict != NX_DIR_OK { 540 if result.verdict != NX_DIR_TRUNCATED { return -10 } 541 } 542 543 let hdr_buf: *u8 = sys_mmap(NX_MAGIC_65536) 544 let path_buf: *u8 = sys_mmap(NX_MAGIC_1024) 545 let r_buf: *u8 = sys_mmap(128) 546 let r: *NxDocV2Record = r_buf as *NxDocV2Record 547 let md_suffix: *u8 = ".md" 548 549 sys_write(1, "nx_doc_census per-doc listing:\n", 31) 550 551 var i: i64 = 0 552 while i < result.n_filled { 553 let row: *NxDirRow = nx_dir_row_at(rows, i) 554 if row.is_dotlike == 1 { 555 i = i + 1 556 continue 557 } 558 if row.dtype != NX_DT_REG { 559 i = i + 1 560 continue 561 } 562 if nx_str_ends_with(row.name_ptr, row.name_len, md_suffix, 3) != 1 { 563 i = i + 1 564 continue 565 } 566 567 let p_len: i64 = _doccensus_path_join( 568 dir_path, n_dir_path, 569 row.name_ptr, row.name_len, 570 path_buf, NX_MAGIC_1024 571 ) 572 if p_len < 0 { 573 i = i + 1 574 continue 575 } 576 let fd: i64 = sys_openat_rd(path_buf) 577 if fd < 0 { 578 i = i + 1 579 continue 580 } 581 582 var content_len: i64 = 0 583 let cap_bytes: i64 = NX_MAGIC_65536 584 var keep_reading: i64 = 1 585 while keep_reading == 1 { 586 let remaining: i64 = cap_bytes - content_len 587 if remaining <= 0 { 588 keep_reading = 0 589 continue 590 } 591 let dst: *u8 = (hdr_buf as *u8) + content_len 592 let n_chunk: i64 = sys_read(fd, dst, remaining) 593 if n_chunk <= 0 { 594 keep_reading = 0 595 continue 596 } 597 content_len = content_len + n_chunk 598 } 599 sys_close(fd) 600 601 nx_doc_classify(hdr_buf, content_len, r) 602 _docc_write_doc_line(row.name_ptr, row.name_len, r.compliance_score) 603 nx_doc_census_accumulate(counts, r) 604 i = i + 1 605 } 606 return 0 607}