nx_teacher_publish.nx source
↩ module page · 112 lines · 6168 B
1// nx_teacher_publish.nx -- the PUBLISHER of the Nishi Teacher loop (Teacher arc, rung R5 = surface it).
2//
3// Operator: "see the nishi teacher and the nishi trainer and the nishi publisher COORDINATING." The
4// publisher EMITS a data-driven curriculum page FROM the unified native capability store (the teacher's
5// output) annotated with the trainer's coverage meter + per-insight practiced/due badge -- so the page
6// is a JOINT artifact of teacher (insights) + trainer (schedule), the coordination made visible.
7// Emit-never-handwrite ([[feedback-live-testable-publish-loop-standard]]); a LIVE push to the site is
8// outward-facing -> operator go-ahead (this organ only STAGES the page to web_assets/).
9//
10// tpub_emit(cap_store, state_store, out_path, now) -> bytes written (or -1)
11// The page carries machine-checkable markers (INSIGHTS=/COVERED=/DUE=/COVERAGE_PERMIL=) + a card per
12// insight (source, id, HTML-escaped rule, practiced|due badge). No hardware writes/network (Rule 26).
13// license_tier: ORIGINAL
14import "nx_teacher_trainer.nx"
15import "nx_syscalls.nx"
16
17func tpub_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != 0 as u8 { n = n + 1 } sys_write(1, s, n); return 0 }
18func tpub_putn(v: i64) -> i64 {
19 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
20 var m: i64 = v; if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
21 let d: *u8 = sys_mmap(24); var k: i64 = 0
22 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
23 var j: i64 = k - 1
24 while j >= 0 { sys_write(1, ((d as i64)+j) as *u8, 1); j = j - 1 }
25 return 0
26}
27func tpub_wr(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return n }
28func tpub_wrn(fd: i64, v: i64) -> i64 {
29 let b: *u8 = sys_mmap(28)
30 if v == 0 { b[0] = 48 as u8; sys_write(fd, b, 1); return 1 }
31 var m: i64 = v; var neg: i64 = 0
32 if m < 0 { neg = 1; m = 0 - m }
33 let t: *u8 = sys_mmap(28); var k: i64 = 0
34 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
35 var o: i64 = 0
36 if neg == 1 { b[0] = 45 as u8; o = 1 }
37 var i: i64 = 0
38 while i < k { b[o + i] = t[k - 1 - i]; i = i + 1 }
39 sys_write(fd, b, o + k); return o + k
40}
41// HTML-escaped write of a NUL-terminated string (& < >). returns bytes written.
42func tpub_wresc(fd: i64, s: *u8) -> i64 {
43 var n: i64 = 0
44 var i: i64 = 0
45 while s[i] != (0 as u8) {
46 let c: i64 = s[i] as i64
47 if c == 38 { sys_write(fd, "&" as *u8, 5); n = n + 5 }
48 else { if c == 60 { sys_write(fd, "<" as *u8, 4); n = n + 4 }
49 else { if c == 62 { sys_write(fd, ">" as *u8, 4); n = n + 4 }
50 else { sys_write(fd, (s as i64 + i) as *u8, 1); n = n + 1 } } }
51 i = i + 1
52 }
53 return n
54}
55// EMIT the curriculum page from the unified store + the trainer meter. returns bytes, or -1.
56func tpub_emit(cap_store: *u8, state_store: *u8, out_path: *u8, now: i64) -> i64 {
57 let h: *i64 = ncfg_open(cap_store)
58 if (h as i64) == 0 { return 0 - 1 }
59 let total: i64 = ncfg_count(h, "insight\x00" as *u8)
60 let covered: i64 = trn_coverage(cap_store, state_store)
61 let due: i64 = trn_due_count(cap_store, state_store, now)
62 var permil: i64 = 0
63 if total > 0 { permil = covered * 1000 / total }
64 let fd: i64 = sys_openat_wr(out_path, 0x1a4)
65 if fd < 0 { return 0 - 1 }
66 var bytes: i64 = 0
67 bytes = bytes + tpub_wr(fd, "<!doctype html><html><head><meta charset=\"utf-8\"><title>Nishi Teacher Capability Curriculum</title></head><body>\n" as *u8)
68 bytes = bytes + tpub_wr(fd, "<h1>Nishi Teacher Capability Curriculum</h1>\n<p class=\"meter\">INSIGHTS=" as *u8)
69 bytes = bytes + tpub_wrn(fd, total)
70 bytes = bytes + tpub_wr(fd, " COVERED=" as *u8); bytes = bytes + tpub_wrn(fd, covered)
71 bytes = bytes + tpub_wr(fd, " DUE=" as *u8); bytes = bytes + tpub_wrn(fd, due)
72 bytes = bytes + tpub_wr(fd, " COVERAGE_PERMIL=" as *u8); bytes = bytes + tpub_wrn(fd, permil)
73 bytes = bytes + tpub_wr(fd, "</p>\n<ul>\n" as *u8)
74 let rk: *i64 = sys_mmap(8 * 16) as *i64
75 let rv: *i64 = sys_mmap(8 * 16) as *i64
76 let st: *i64 = sys_mmap(8 * 4) as *i64
77 var i: i64 = 0
78 while i < total {
79 let rf: i64 = ncfg_row(h, "insight\x00" as *u8, i, rk, rv, 16)
80 if rf > 0 {
81 let src: *u8 = ncfg_field(rk, rv, rf, "source\x00" as *u8)
82 let id: *u8 = ncfg_field(rk, rv, rf, "id\x00" as *u8)
83 let rule: *u8 = ncfg_field(rk, rv, rf, "rule\x00" as *u8)
84 bytes = bytes + tpub_wr(fd, "<li>[" as *u8)
85 if (src as i64) != 0 { bytes = bytes + tpub_wresc(fd, src) }
86 bytes = bytes + tpub_wr(fd, "] " as *u8)
87 if (id as i64) != 0 { bytes = bytes + tpub_wresc(fd, id) }
88 bytes = bytes + tpub_wr(fd, ": " as *u8)
89 if (rule as i64) != 0 { bytes = bytes + tpub_wresc(fd, rule) }
90 var practiced: i64 = 0
91 if (id as i64) != 0 { if trn_state(state_store, id, st) == 1 { if st[0] >= 1 { practiced = 1 } } }
92 if practiced == 1 { bytes = bytes + tpub_wr(fd, " <span class=\"b\">practiced</span>" as *u8) }
93 else { bytes = bytes + tpub_wr(fd, " <span class=\"b\">due</span>" as *u8) }
94 bytes = bytes + tpub_wr(fd, "</li>\n" as *u8)
95 }
96 i = i + 1
97 }
98 bytes = bytes + tpub_wr(fd, "</ul>\n<p>Emitted by nx_teacher_publish from the native unified capability store (data-driven, not hand-written).</p>\n</body></html>\n" as *u8)
99 sys_close(fd)
100 return bytes
101}
102
103func main() -> i64 {
104 let cap: *u8 = "knowledge/store/teacher-capability-\x00" as *u8
105 let state: *u8 = "knowledge/store/teacher-review-\x00" as *u8
106 let out: *u8 = "web_assets/nishi-curriculum.html\x00" as *u8
107 let now: i64 = sys_now_realtime_sec()
108 let bytes: i64 = tpub_emit(cap, state, out, now)
109 if bytes < 0 { tpub_puts("nx_teacher_publish: FATAL (no capability store -- run nx_teacher_merge first)\n"); sys_exit(1); return 1 }
110 tpub_puts("nx_teacher_publish: emitted "); tpub_putn(bytes); tpub_puts(" bytes -> web_assets/nishi-curriculum.html (STAGED; live publish needs operator go-ahead)\n")
111 sys_exit(0); return 0
112}