nx_teacher_rt.nx source
↩ module page · 269 lines · 12565 B
1// RETIRE CANDIDATE -- ORPHANED BYTE-IDENTICAL DUPLICATE (measured 2026-08-15).
2// This file is a byte-for-byte copy of runtime/nx_teacher_lesson.nx: both were
3// 11087 bytes at content-hash h0e2954e3cb38a146 immediately before this note was
4// added (the note is the ONLY difference; re-derive by hashing both).
5// nx_teacher_lesson.nx has THREE importers (nx_teacher_gate, nx_teacher_run,
6// nx_teacher_serve). THIS file has ZERO references anywhere in buildroot --
7// nx_shelltool grep, 56,311 files, corpus_complete=1. So retiring it cannot cost
8// a capability, and the live twin already carries every function it defines.
9// WHY IT EXISTS: the three importers each carry the comment "the lesson organ the
10// 2026-07-21 dedupe deleted" -- a previous dedupe removed the LIVE organ, broke
11// those three importers, and it had to be restored. This is the leftover of that
12// repair, not a second implementation. ONE FILE, ONE WRITER: both copies append to
13// knowledge/registry/nishi_lessons.tsv, so leaving both buildable invites the
14// two-writers-one-file race.
15// ACTION: `nx_retire_path retire buildroot/runtime/nx_teacher_rt.nx` (rename-only,
16// reversible). NOT done here -- that tool is capability-denied to this seat, and
17// hand-rolling a rename would bypass the guard that refuses protected paths.
18// The header below is INHERITED FROM THE COPY and names nx_teacher.nx, which is a
19// DIFFERENT, REAL organ (3,969 B, the M0->M4 autonomy ladder). Do not trust it.
20//
21// nx_teacher.nx -- the NISHI TEACHER: turn a work-session into a structured,
22// machine-AND-human-educatable LESSON, validate it is S-class, and organize+log it.
23//
24// WHY (operator 2026-06-17): "take our data from [what] we did meaningful for the nishi
25// team and my growth to learn -- machine and human educatable... build a nishi teacher
26// role... organize and log what we need to practice, takeaways, etc."
27//
28// GROUNDED IN LEARNING SCIENCE (Bjork "desirable difficulties", cited evidence in
29// knowledge/registry/teaching_evidence.tsv): a lesson only TRANSFERS if it has
30// - CONTEXT concrete situation (concrete-before-abstract)
31// - TAKEAWAYS the S-class abstractions
32// - CARDINALS durable reusable rules (the MACHINE-applicable principles) -> cardinal log
33// - DRILLS retrieval-practice prompts (the testing effect) -> drill log
34// - EVIDENCE cited provenance ("we do things with evidence & science, ALWAYS")
35// A lesson missing any of these is NOT S-class -> RED (the teacher's liar-kill / publish-gate).
36//
37// The lesson is a markdown file (so the EXISTING wiki ingestor auto-discovers it as
38// /wiki/<slug>); the teacher additionally EXTRACTS the machine-readable structure
39// (cardinals + drills) into registries the Nishi team / spore can ingest. Two audiences,
40// one source. DRY: reuses nx_syscalls only; no new primitives.
41//
42// genealogy_id: nishi_teacher_lesson_capture
43// lineage_id: markdown_section_parse + cardinal_drill_extract + sclass_publish_gate
44// license_tier: ORIGINAL
45import "nx_syscalls.nx"
46
47// verdict codes
48const NX_TEACH_OK: i64 = 0
49const NX_TEACH_NO_FILE: i64 = 1
50const NX_TEACH_NO_TITLE: i64 = 2
51const NX_TEACH_MISSING: i64 = 3 // a required section is absent
52
53// does body[off..] start with the NUL-terminated literal lit (bounded by n)?
54func mem_eq(body: *u8, off: i64, n: i64, lit: *u8) -> i64 {
55 var i: i64 = 0
56 while lit[i] != (0 as u8) {
57 if off + i >= n { return 0 }
58 if body[off + i] != lit[i] { return 0 }
59 i = i + 1
60 }
61 return 1
62}
63
64// find a "## <NAME>" section header at a LINE START. Returns 1 if found and sets
65// cstart (first byte after that header line) and cend (start of the next "## " line, or n).
66func sec_bounds(body: *u8, n: i64, marker: *u8, out: *i64) -> i64 {
67 var i: i64 = 0
68 var found: i64 = 0 - 1
69 while i < n {
70 var at_line_start: i64 = 0
71 if i == 0 { at_line_start = 1 }
72 if i > 0 { if body[i - 1] == (10 as u8) { at_line_start = 1 } }
73 if at_line_start == 1 {
74 if mem_eq(body, i, n, marker) == 1 { found = i; i = n }
75 }
76 if i < n { i = i + 1 }
77 }
78 if found < 0 { return 0 }
79 // content starts after the header line's newline
80 var c: i64 = found
81 while c < n { if body[c] == (10 as u8) { c = c + 1; out[0] = c; c = n } else { c = c + 1 } }
82 if out[0] < found { out[0] = n }
83 // content ends at next line-start "## "
84 var e: i64 = out[0]
85 var cend: i64 = n
86 while e < n {
87 if e > 0 { if body[e - 1] == (10 as u8) { if mem_eq(body, e, n, "## \x00" as *u8) == 1 { cend = e; e = n } } }
88 if e < n { e = e + 1 }
89 }
90 out[1] = cend
91 return 1
92}
93
94// count bullet lines ("- " at a line start) within [start,end)
95func count_bullets(body: *u8, start: i64, end: i64) -> i64 {
96 var i: i64 = start
97 var c: i64 = 0
98 while i < end {
99 var ls: i64 = 0
100 if i == start { ls = 1 }
101 if i > start { if body[i - 1] == (10 as u8) { ls = 1 } }
102 if ls == 1 { if mem_eq(body, i, end, "- \x00" as *u8) == 1 { c = c + 1 } }
103 i = i + 1
104 }
105 return c
106}
107
108// append each bullet line's TEXT (after "- ") as "lesson_id<TAB>text\n" to fd.
109func emit_bullets(fd: i64, lesson_id: *u8, body: *u8, start: i64, end: i64) -> i64 {
110 var i: i64 = start
111 while i < end {
112 var ls: i64 = 0
113 if i == start { ls = 1 }
114 if i > start { if body[i - 1] == (10 as u8) { ls = 1 } }
115 if ls == 1 {
116 if mem_eq(body, i, end, "- \x00" as *u8) == 1 {
117 // write id + TAB
118 var k: i64 = 0
119 while lesson_id[k] != (0 as u8) { k = k + 1 }
120 sys_write(fd, lesson_id, k)
121 sys_write(fd, "\t\x00" as *u8, 1)
122 // write the bullet text (skip "- ") to end of line
123 var j: i64 = i + 2
124 while j < end { if body[j] == (10 as u8) { j = end } else { sys_write(fd, (body as i64 + j) as *u8, 1); j = j + 1 } }
125 sys_write(fd, "\n\x00" as *u8, 1)
126 }
127 }
128 i = i + 1
129 }
130 return 0
131}
132
133func tp(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
134func tn(v: i64) -> i64 {
135 let bb: *u8 = sys_mmap(28); var m: i64 = v
136 if m < 0 { sys_write(1, "-\x00" as *u8, 1); m = 0 - m }
137 let t: *u8 = sys_mmap(28); var k: i64 = 0
138 if m == 0 { t[0] = 48 as u8; k = 1 }
139 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
140 var i: i64 = 0
141 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
142 sys_write(1, bb, k); return 0
143}
144
145// EMIT the curriculum index wiki page from nishi_lessons.tsv (the "structurer": one /wiki
146// page that organizes every logged lesson, regenerated from the registry -- emit, never hand-write).
147func nx_teacher_emit_index(out_path: *u8) -> i64 {
148 let szp: *i64 = sys_mmap(16) as *i64
149 let body: *u8 = sys_read_file("knowledge/registry/nishi_lessons.tsv\x00" as *u8, szp)
150 let ofd: i64 = sys_openat_wr(out_path, O_WRONLY_CT)
151 if ofd < 0 { return 0 - 1 }
152 let hdr: *u8 = "# Nishi Lessons (curriculum index)\n\nAuto-emitted by the Nishi Teacher from nishi_lessons.tsv -- each row is a validated S-class lesson (has context, takeaways, cardinals, drills, cited evidence). Machine-readable cardinals + drills live in nishi_cardinals.tsv / nishi_drills.tsv for the team and spore to ingest.\n\n## Lessons\n\n\x00" as *u8
153 var h: i64 = 0
154 while hdr[h] != (0 as u8) { h = h + 1 }
155 sys_write(ofd, hdr, h)
156 if (body as i64) != 0 {
157 let n: i64 = szp[0]
158 var i: i64 = 0
159 while i < n {
160 // find id end (first TAB) and line end (newline) for this line
161 var idend: i64 = 0 - 1
162 var lineend: i64 = n
163 var s: i64 = i
164 while s < n {
165 if body[s] == (10 as u8) { lineend = s; s = n }
166 else {
167 if body[s] == (9 as u8) { if idend < 0 { idend = s } }
168 s = s + 1
169 }
170 }
171 if idend < 0 { idend = lineend }
172 // "- [<id>](/wiki/<id>) <rest of line after tab>\n"
173 sys_write(ofd, "- [\x00" as *u8, 3)
174 var a: i64 = i
175 while a < idend { sys_write(ofd, (body as i64 + a) as *u8, 1); a = a + 1 }
176 sys_write(ofd, "](/wiki/\x00" as *u8, 8)
177 a = i
178 while a < idend { sys_write(ofd, (body as i64 + a) as *u8, 1); a = a + 1 }
179 sys_write(ofd, ") \x00" as *u8, 2)
180 var b: i64 = idend
181 if b < lineend { if body[b] == (9 as u8) { b = b + 1 } }
182 while b < lineend { sys_write(ofd, (body as i64 + b) as *u8, 1); b = b + 1 }
183 sys_write(ofd, "\n\x00" as *u8, 1)
184 i = lineend + 1
185 }
186 }
187 sys_close(ofd)
188 return 0
189}
190
191// write a decimal number to a file descriptor
192func tn_fd(fd: i64, v: i64) -> i64 {
193 let bb: *u8 = sys_mmap(28); var m: i64 = v
194 if m < 0 { sys_write(fd, "-\x00" as *u8, 1); m = 0 - m }
195 let t: *u8 = sys_mmap(28); var k: i64 = 0
196 if m == 0 { t[0] = 48 as u8; k = 1 }
197 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
198 var i: i64 = 0
199 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
200 sys_write(fd, bb, k); return 0
201}
202
203// INGEST one lesson markdown file. Validate required sections, extract cardinals+drills to
204// registries, log the lesson. Returns NX_TEACH_OK (green) or a negative verdict code.
205func nx_teacher_ingest(path_z: *u8, lesson_id: *u8, domain: *u8) -> i64 {
206 let szp: *i64 = sys_mmap(16) as *i64
207 let body: *u8 = sys_read_file(path_z, szp)
208 if (body as i64) == 0 { return 0 - NX_TEACH_NO_FILE }
209 let n: i64 = szp[0]
210 // require a "# " title at the very top
211 if mem_eq(body, 0, n, "# \x00" as *u8) == 0 { return 0 - NX_TEACH_NO_TITLE }
212
213 let ob: *i64 = sys_mmap(16) as *i64
214 // required sections
215 var ok: i64 = 1
216 if sec_bounds(body, n, "## CONTEXT\x00" as *u8, ob) == 0 { ok = 0; tp(" MISSING ## CONTEXT\n\x00" as *u8) }
217 if sec_bounds(body, n, "## TAKEAWAYS\x00" as *u8, ob) == 0 { ok = 0; tp(" MISSING ## TAKEAWAYS\n\x00" as *u8) }
218 if sec_bounds(body, n, "## EVIDENCE\x00" as *u8, ob) == 0 { ok = 0; tp(" MISSING ## EVIDENCE\n\x00" as *u8) }
219
220 // CARDINALS: required + extract
221 var ncard: i64 = 0
222 if sec_bounds(body, n, "## CARDINALS\x00" as *u8, ob) == 0 { ok = 0; tp(" MISSING ## CARDINALS\n\x00" as *u8) }
223 if ok == 1 {
224 sec_bounds(body, n, "## CARDINALS\x00" as *u8, ob)
225 ncard = count_bullets(body, ob[0], ob[1])
226 let cfd: i64 = sys_openat_append("knowledge/registry/nishi_cardinals.tsv\x00" as *u8, 0x1a4)
227 emit_bullets(cfd, lesson_id, body, ob[0], ob[1])
228 if cfd > 0 { sys_close(cfd) }
229 }
230 // DRILLS: required + extract
231 var ndrill: i64 = 0
232 if sec_bounds(body, n, "## DRILLS\x00" as *u8, ob) == 0 { ok = 0; tp(" MISSING ## DRILLS\n\x00" as *u8) }
233 if ok == 1 {
234 sec_bounds(body, n, "## DRILLS\x00" as *u8, ob)
235 ndrill = count_bullets(body, ob[0], ob[1])
236 let dfd: i64 = sys_openat_append("knowledge/registry/nishi_drills.tsv\x00" as *u8, 0x1a4)
237 emit_bullets(dfd, lesson_id, body, ob[0], ob[1])
238 if dfd > 0 { sys_close(dfd) }
239 }
240 var nev: i64 = 0
241 if sec_bounds(body, n, "## EVIDENCE\x00" as *u8, ob) == 1 { nev = count_bullets(body, ob[0], ob[1]) }
242
243 if ok == 0 { return 0 - NX_TEACH_MISSING }
244
245 // log the lesson row: id<TAB>domain<TAB>cards<TAB>drills<TAB>evidence<TAB>bytes<TAB>GREEN
246 let lfd: i64 = sys_openat_append("knowledge/registry/nishi_lessons.tsv\x00" as *u8, 0x1a4)
247 if lfd > 0 {
248 var k: i64 = 0
249 while lesson_id[k] != (0 as u8) { k = k + 1 }
250 sys_write(lfd, lesson_id, k)
251 sys_write(lfd, "\t\x00" as *u8, 1)
252 k = 0
253 while domain[k] != (0 as u8) { k = k + 1 }
254 sys_write(lfd, domain, k)
255 // counts via a tiny number writer
256 sys_write(lfd, "\tcards=\x00" as *u8, 7)
257 tn_fd(lfd, ncard)
258 sys_write(lfd, "\tdrills=\x00" as *u8, 8)
259 tn_fd(lfd, ndrill)
260 sys_write(lfd, "\tevidence=\x00" as *u8, 10)
261 tn_fd(lfd, nev)
262 sys_write(lfd, "\tbytes=\x00" as *u8, 7)
263 tn_fd(lfd, n)
264 sys_write(lfd, "\tverdict=GREEN\n\x00" as *u8, 15)
265 sys_close(lfd)
266 }
267 tp(" ingested cards=\x00" as *u8); tn(ncard); tp(" drills=\x00" as *u8); tn(ndrill); tp(" evidence=\x00" as *u8); tn(nev); tp(" bytes=\x00" as *u8); tn(n); tp("\n\x00" as *u8)
268 return NX_TEACH_OK
269}