code wiki / _hdl_build / nx_coach_curriculum_lib.nx
nx_coach_curriculum_lib.nx source
↩ module page · 137 lines · 7684 B
1// nx_coach_curriculum_lib.nx -- the CURRICULUM-COACH engine as a PURE CORE (no main), extracted verbatim from
2// nx_coach_curriculum.nx so a second discipline can ADOPT the engine instead of forking it.
3//
4// WHY THE EXTRACTION. nx_coach_curriculum.nx was a lib wearing an organ's clothes: the whole engine plus a
5// main(). Nothing can import a file that defines main(), so the only way to reuse it was to copy it -- which is
6// exactly the duplication the estate is already RED on (179 cross-tree dup basenames). The estate already has
7// the split convention (nx_gate_verdict / nx_gate_verdict_lib); this applies it here. The organ keeps its main()
8// and imports this; behaviour is unchanged and proven by re-running it.
9//
10// THE ENGINE, unchanged: assess -> serve the FRAMEWORK from the store -> the person practises -> grade the
11// SELF-REPORT (a bitmask of which framework elements they used) against the checklist -> name the MISSING
12// element -> hand back its drill. "Coaching is coaching": the framework's required elements are the target
13// chord, the reported elements are the played notes, missing is the gap. Works for any discipline whose
14// technique decomposes into nameable elements -- interpersonal frameworks, or a knife on a board.
15//
16// Frameworks are DATA in knowledge/store/coach-curriculum- (rule 11), so a new discipline is rows, not code.
17// license_tier: ORIGINAL No hw writes (Rule 26).
18import "nx_seg_store.nx"
19import "nx_itoa_lib.nx"
20import "nx_syscalls.nx"
21
22const CC_PFX: *u8 = "knowledge/store/coach-curriculum-" as *u8
23
24func cc_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
25func cc_putn(v: i64) -> i64 { nxi_out(v); return 0 }
26func cc_atoi(s: *u8, n: i64) -> i64 { var v: i64 = 0; var i: i64 = 0; while i < n { let c: i64 = s[i]; if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } i = i + 1 } return v }
27func cc_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
28func cc_add(w: *i64, key: *u8, val: *u8) -> i64 { return ss_add(w, 1, key, val, cc_slen(val)) }
29
30// build "<skill>:<suffix>" or "<skill>:<suffix>:<idx>" into out
31func cc_key(skill: *u8, suffix: *u8, idx: i64, hasidx: i64, out: *u8) -> i64 {
32 var o: i64 = ss_cat(out, 0, skill); o = ss_cat(out, o, ":" as *u8); o = ss_cat(out, o, suffix)
33 if hasidx == 1 {
34 o = ss_cat(out, o, ":" as *u8)
35 let t: *u8 = sys_mmap(8); var k: i64 = 0; var m: i64 = idx
36 if m == 0 { t[0] = 48 as u8; k = 1 }
37 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
38 var j: i64 = k - 1; while j >= 0 { out[o] = t[j]; o = o + 1; j = j - 1 }
39 }
40 out[o] = 0 as u8; return o
41}
42
43func cc_get(h: *i64, key: *u8, outlen: *i64) -> *u8 {
44 let pp: *i64 = sys_mmap(16) as *i64; let ll: *i64 = sys_mmap(16) as *i64
45 if ss_hget(h, key, pp, ll) != 1 { outlen[0] = 0; return 0 as *u8 }
46 outlen[0] = ll[0]; return pp[0] as *u8
47}
48
49func cc_seed(prefix: *u8) -> i64 {
50 let w: *i64 = ss_begin()
51 // CONFRONTATION = Nonviolent Communication (4 elements)
52 cc_add(w, "confront:prompt" as *u8, "A coworker took credit for your work. Respond using all 4 NVC steps." as *u8)
53 cc_add(w, "confront:n" as *u8, "4" as *u8)
54 cc_add(w, "confront:elem:0" as *u8, "Observation (the fact, no judgment)" as *u8)
55 cc_add(w, "confront:elem:1" as *u8, "Feeling (name how you feel)" as *u8)
56 cc_add(w, "confront:elem:2" as *u8, "Need (the underlying need)" as *u8)
57 cc_add(w, "confront:elem:3" as *u8, "Request (a clear, doable ask)" as *u8)
58 cc_add(w, "confront:drill:0" as *u8, "state ONLY the observable fact, strip the judgment" as *u8)
59 cc_add(w, "confront:drill:1" as *u8, "name the feeling ('I feel...') without blaming" as *u8)
60 cc_add(w, "confront:drill:2" as *u8, "connect it to the need ('because I need...')" as *u8)
61 cc_add(w, "confront:drill:3" as *u8, "end with a specific, doable request" as *u8)
62 // CONVERSATION = active listening (3 elements)
63 cc_add(w, "converse:prompt" as *u8, "A friend is venting about a hard day. Practice active listening." as *u8)
64 cc_add(w, "converse:n" as *u8, "3" as *u8)
65 cc_add(w, "converse:elem:0" as *u8, "Paraphrase (reflect back what you heard)" as *u8)
66 cc_add(w, "converse:elem:1" as *u8, "Open question (invite more)" as *u8)
67 cc_add(w, "converse:elem:2" as *u8, "Validate (acknowledge the feeling)" as *u8)
68 cc_add(w, "converse:drill:0" as *u8, "paraphrase in your own words before responding" as *u8)
69 cc_add(w, "converse:drill:1" as *u8, "ask an open 'what/how' question" as *u8)
70 cc_add(w, "converse:drill:2" as *u8, "validate ('that sounds really...')" as *u8)
71 return ss_commit(prefix, w, sys_now_ms())
72}
73
74// one curriculum attempt: response_mask = which framework elements the person used. Returns # missing (0=complete).
75func cc_attempt(h: *i64, skill: *u8, response_mask: i64) -> i64 {
76 let key: *u8 = sys_mmap(96)
77 let ol: *i64 = sys_mmap(16) as *i64
78 cc_key(skill, "n" as *u8, 0, 0, key)
79 let ns: *u8 = cc_get(h, key, ol); var n: i64 = 0; if (ns as i64) != 0 { n = cc_atoi(ns, ol[0]) }
80 let checklist: i64 = (1 << n) - 1
81 let missing: i64 = checklist & (0xffffff ^ response_mask)
82 cc_key(skill, "prompt" as *u8, 0, 0, key); let pr: *u8 = cc_get(h, key, ol)
83 cc_puts(" ["); cc_puts(skill); cc_puts("] "); if (pr as i64) != 0 { sys_write(1, pr, ol[0]) } cc_puts("\n" as *u8)
84 var nmiss: i64 = 0
85 var i: i64 = 0
86 while i < n {
87 if (missing & (1 << i)) != 0 {
88 nmiss = nmiss + 1
89 cc_key(skill, "elem" as *u8, i, 1, key); let em: *u8 = cc_get(h, key, ol)
90 cc_puts(" MISSED: "); if (em as i64) != 0 { sys_write(1, em, ol[0]) } cc_puts(" -> DRILL: ")
91 cc_key(skill, "drill" as *u8, i, 1, key); let dr: *u8 = cc_get(h, key, ol)
92 if (dr as i64) != 0 { sys_write(1, dr, ol[0]) } cc_puts("\n" as *u8)
93 }
94 i = i + 1
95 }
96 if nmiss == 0 { cc_puts(" COMPLETE -- all framework elements present -> advance (harder scenario)\n" as *u8) }
97 return nmiss
98}
99
100// ---- ADDED BY THE EXTRACTION (the organ never needed these; a second discipline does) ----
101
102// how many elements a framework declares (0 = the store has never heard of this skill).
103func cc_element_count(h: *i64, skill: *u8) -> i64 {
104 let key: *u8 = sys_mmap(96)
105 let ol: *i64 = sys_mmap(16) as *i64
106 cc_key(skill, "n" as *u8, 0, 0, key)
107 let ns: *u8 = cc_get(h, key, ol)
108 if (ns as i64) == 0 { return 0 }
109 return cc_atoi(ns, ol[0])
110}
111
112// SILENT grade: same arithmetic as cc_attempt with no printing, so a page renderer can use the engine without
113// the CLI's stdout. Returns the missing-element BITMASK (0 = complete), or -1 for an unknown skill --
114// an unknown framework must never grade as a perfect attempt.
115func cc_missing_mask(h: *i64, skill: *u8, response_mask: i64) -> i64 {
116 let n: i64 = cc_element_count(h, skill)
117 if n <= 0 { return 0 - 1 }
118 let checklist: i64 = (1 << n) - 1
119 return checklist & (0xffffff ^ response_mask)
120}
121
122// the element text / drill text for one index; length into outlen (0 = absent).
123func cc_element(h: *i64, skill: *u8, idx: i64, outlen: *i64) -> *u8 {
124 let key: *u8 = sys_mmap(96)
125 cc_key(skill, "elem" as *u8, idx, 1, key)
126 return cc_get(h, key, outlen)
127}
128func cc_drill(h: *i64, skill: *u8, idx: i64, outlen: *i64) -> *u8 {
129 let key: *u8 = sys_mmap(96)
130 cc_key(skill, "drill" as *u8, idx, 1, key)
131 return cc_get(h, key, outlen)
132}
133func cc_prompt(h: *i64, skill: *u8, outlen: *i64) -> *u8 {
134 let key: *u8 = sys_mmap(96)
135 cc_key(skill, "prompt" as *u8, 0, 0, key)
136 return cc_get(h, key, outlen)
137}