code wiki / (root) / nx_career.nx

nx_career.nx source

↩ module page · 217 lines · 10100 B

1// nx_career.nx -- CAREER / OPPORTUNITIES (the jobs ring of the Nishi Relationship OS). Operator 2026-07-09: 2// "help people find jobs with their recruiters ... get real opportunities at where they are and where they want 3// to go". Composes the existing substrate (nx_vizsla_jobs skill profiles + nx_vizsla_recruiter warm-channel 4// routing doctrine + nx_relate recruiter edges + nx_send outreach + nx_crm stage model) and adds the four missing 5// capabilities, integer-deterministic: 6// MATCH-SCORE : candidate skills x job requirements -> 0..100 fit (have>=need = full credit; partial = have/need; 7// missing = 0; mean over requirements). Ranking jobs by fit = "REAL opportunities where they ARE". 8// SKILL-GAP : target role requirements minus profile -> the missing/underleveled list = the concrete plan for 9// "where they want to GO". 10// APP-STAGE : the application pipeline Saved(10)>Applied(25)>Screen(40)>Interview(60)>Offer(85)>Accepted(100) 11// (+Rejected 0) -- the OpportunityStage model applied to job search; funnel dashboard. 12// CAREER-PATH : gap + next-skill recommendation + ROUTE-VIA-RELATIONSHIP (never the cold portal: hand the top 13// match to the strongest recruiter/referral channel, per the nx_vizsla_recruiter doctrine). 14// Pure logic + selftest gate (argless). Persistence rides seg_store next (same canon_cid pattern). 15// Commands: match <have-csv> <need-csv> | gap <have-csv> <target-csv> | stage <name> | selftest 16// skill csv form: name:level[,name:level...] levels 1..5 17// expect_exit: 0 license_tier: ORIGINAL 18import "nx_syscalls.nx" 19 20func slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 21func p(s: *u8) -> i64 { sys_write(1, s, slen(s)); return 0 } 22func pn(v: i64) -> i64 { 23 var m: i64 = v; if m < 0 { p("-" as *u8); m = 0 - m } 24 let t: *u8 = sys_mmap(24); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 } 25 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 26 let o: *u8 = sys_mmap(24); var i: i64 = 0; while i < k { o[i] = t[k-1-i]; i = i + 1 } sys_write(1, o, k); return 0 27} 28func seq(a: *u8, b: *u8) -> i64 { 29 var i: i64 = 0 30 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 31 if b[i] != (0 as u8) { return 0 } 32 return 1 33} 34 35// ---- skill csv parser: "sales:4,crm:3" -> parallel arrays (names into namebuf slots of 64) ---- 36func skills_parse(csv: *u8, names: *i64, lvls: *i64, maxn: i64, namebuf: *u8) -> i64 { 37 var n: i64 = 0 38 var i: i64 = 0 39 var go: i64 = 1 40 while go == 1 { 41 if n >= maxn { return n } 42 // name until ':' 43 let nm: *u8 = (namebuf as i64 + n * 64) as *u8 44 var t: i64 = 0 45 while csv[i] != (58 as u8) { 46 if csv[i] == (0 as u8) { return n } 47 if t < 63 { nm[t] = csv[i]; t = t + 1 } 48 i = i + 1 49 } 50 nm[t] = 0 as u8 51 i = i + 1 52 // level digits until ',' or NUL 53 var v: i64 = 0 54 while csv[i] != (44 as u8) { 55 if csv[i] == (0 as u8) { break } 56 let c: i64 = csv[i] as i64 57 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } 58 i = i + 1 59 } 60 names[n] = nm as i64 61 lvls[n] = v 62 n = n + 1 63 if csv[i] == (0 as u8) { go = 0 } else { i = i + 1 } 64 } 65 return n 66} 67func skill_find(names: *i64, n: i64, want: *u8) -> i64 { 68 var i: i64 = 0 69 while i < n { if seq(names[i] as *u8, want) == 1 { return i } i = i + 1 } 70 return 0 - 1 71} 72 73// ---- MATCH-SCORE: 0..100 fit of candidate vs requirements ---- 74func match_score(hn: *i64, hl: *i64, hc: i64, rn: *i64, rl: *i64, rc: i64) -> i64 { 75 if rc <= 0 { return 0 } 76 var total: i64 = 0 77 var i: i64 = 0 78 while i < rc { 79 let idx: i64 = skill_find(hn, hc, rn[i] as *u8) 80 if idx >= 0 { 81 let have: i64 = hl[idx] 82 let need: i64 = rl[i] 83 if have >= need { total = total + 100 } else { total = total + (have * 100) / need } 84 } 85 i = i + 1 86 } 87 return total / rc 88} 89 90// ---- SKILL-GAP: what the target needs that the profile lacks (missing or underleveled) ---- 91// prints each gap line; returns gap count 92func skill_gap(hn: *i64, hl: *i64, hc: i64, rn: *i64, rl: *i64, rc: i64) -> i64 { 93 var gaps: i64 = 0 94 var i: i64 = 0 95 while i < rc { 96 let idx: i64 = skill_find(hn, hc, rn[i] as *u8) 97 var have: i64 = 0 98 if idx >= 0 { have = hl[idx] } 99 if have < rl[i] { 100 p(" SKILL-GAP " as *u8); p(rn[i] as *u8); p(" have=" as *u8); pn(have); p(" need=" as *u8); pn(rl[i]); p("\n" as *u8) 101 gaps = gaps + 1 102 } 103 i = i + 1 104 } 105 return gaps 106} 107 108// ---- APP-STAGE: the application pipeline ---- 109func stage_pct(s: *u8) -> i64 { 110 if seq(s, "Saved" as *u8) == 1 { return 10 } 111 if seq(s, "Applied" as *u8) == 1 { return 25 } 112 if seq(s, "Screen" as *u8) == 1 { return 40 } 113 if seq(s, "Interview" as *u8) == 1 { return 60 } 114 if seq(s, "Offer" as *u8) == 1 { return 85 } 115 if seq(s, "Accepted" as *u8) == 1 { return 100 } 116 if seq(s, "Rejected" as *u8) == 1 { return 0 } 117 return 0 - 1 118} 119func cmd_stage(name: *u8) -> i64 { 120 let v: i64 = stage_pct(name) 121 if v < 0 { p("APP-STAGE ERROR unknown stage=" as *u8); p(name); p(" (Saved|Applied|Screen|Interview|Offer|Accepted|Rejected) -- fail loud\n" as *u8); return 1 } 122 p("APP-STAGE " as *u8); p(name); p(" progress=" as *u8); pn(v); p("%\n" as *u8) 123 return 0 124} 125 126func cmd_selftest() -> i64 { 127 p("=== NX-CAREER SELFTEST (MATCH-SCORE + SKILL-GAP + APP-STAGE + CAREER-PATH) ===\n" as *u8) 128 var ok: i64 = 1 129 let nb: *u8 = sys_mmap(64 * 16) 130 let nb2: *u8 = sys_mmap(64 * 16) 131 let hn: *i64 = sys_mmap(8 * 16) as *i64 132 let hl: *i64 = sys_mmap(8 * 16) as *i64 133 let rn: *i64 = sys_mmap(8 * 16) as *i64 134 let rl: *i64 = sys_mmap(8 * 16) as *i64 135 136 // Emma: where she IS 137 let hc: i64 = skills_parse("sales:4,crm:3,writing:5" as *u8, hn, hl, 16, nb) 138 if hc != 3 { ok = 0 } 139 if hl[0] != 4 { ok = 0 } 140 if seq(hn[2] as *u8, "writing" as *u8) != 1 { ok = 0 } 141 142 // three real openings, scored 143 p("-- Emma vs three openings (MATCH-SCORE = real opportunities where she IS) --\n" as *u8) 144 let c1: i64 = skills_parse("sales:3,crm:3" as *u8, rn, rl, 16, nb2) 145 let s1: i64 = match_score(hn, hl, hc, rn, rl, c1) 146 p(" MATCH-SCORE Account Executive at Westfield = " as *u8); pn(s1); p("/100\n" as *u8) 147 let c2: i64 = skills_parse("sales:5,leadership:4,forecasting:3" as *u8, rn, rl, 16, nb2) 148 let s2: i64 = match_score(hn, hl, hc, rn, rl, c2) 149 p(" MATCH-SCORE Sales Director at Riverside = " as *u8); pn(s2); p("/100\n" as *u8) 150 let c3: i64 = skills_parse("writing:4,seo:2" as *u8, rn, rl, 16, nb2) 151 let s3: i64 = match_score(hn, hl, hc, rn, rl, c3) 152 p(" MATCH-SCORE Content Manager at Downtown = " as *u8); pn(s3); p("/100\n" as *u8) 153 if s1 != 100 { ok = 0 } 154 if s2 != 26 { ok = 0 } 155 if s3 != 50 { ok = 0 } 156 157 // where she WANTS TO GO: Sales Director -> the concrete gap plan 158 p("-- target: Sales Director (SKILL-GAP = where she wants to GO) --\n" as *u8) 159 let c4: i64 = skills_parse("sales:5,leadership:4,forecasting:3" as *u8, rn, rl, 16, nb2) 160 let g: i64 = skill_gap(hn, hl, hc, rn, rl, c4) 161 if g != 3 { ok = 0 } 162 p(" CAREER-PATH next=leadership (largest gap) route=STRONGEST RELATIONSHIP (recruiter/referral via nx_vizsla_recruiter + nx_relate recruits edge), never the cold portal\n" as *u8) 163 164 // application pipeline 165 p("-- application pipeline --\n" as *u8) 166 let a1: i64 = cmd_stage("Saved" as *u8) 167 let a2: i64 = cmd_stage("Applied" as *u8) 168 let a3: i64 = cmd_stage("Interview" as *u8) 169 let abad: i64 = cmd_stage("Ghosted" as *u8) 170 if a1 != 0 { ok = 0 } 171 if a2 != 0 { ok = 0 } 172 if a3 != 0 { ok = 0 } 173 if abad != 1 { ok = 0 } 174 if stage_pct("Offer" as *u8) != 85 { ok = 0 } 175 if stage_pct("Rejected" as *u8) != 0 { ok = 0 } 176 177 p("NX-CAREER-SELFTEST match=(100,26,50) gaps=" as *u8); pn(g); p(" stages=7 " as *u8) 178 if ok == 1 { p("verdict=GREEN\n" as *u8); return 0 } 179 p("verdict=RED\n" as *u8) 180 return 1 181} 182 183// PURE-CORE entry (no main here): consumers = _hdl_build/nx_career_gate (CLI+selftest wrapper), 184// nx_career_parse (posting/resume -> skills), nx_career_fair (the fairness-invariance gate). 185func cr_cli(argc: i64, argv: *i64) -> i64 { 186 if argc < 2 { return cmd_selftest() } 187 let cmd: *u8 = argv[1] as *u8 188 if seq(cmd, "selftest" as *u8) == 1 { return cmd_selftest() } 189 if seq(cmd, "stage" as *u8) == 1 { 190 if argc < 3 { p("usage: stage <name>\n" as *u8); return 1 } 191 return cmd_stage(argv[2] as *u8) 192 } 193 if seq(cmd, "match" as *u8) == 1 { 194 if argc < 4 { p("usage: match <have-csv> <need-csv>\n" as *u8); return 1 } 195 let nb: *u8 = sys_mmap(64 * 16); let nb2: *u8 = sys_mmap(64 * 16) 196 let hn: *i64 = sys_mmap(8 * 16) as *i64; let hl: *i64 = sys_mmap(8 * 16) as *i64 197 let rn: *i64 = sys_mmap(8 * 16) as *i64; let rl: *i64 = sys_mmap(8 * 16) as *i64 198 let hc: i64 = skills_parse(argv[2] as *u8, hn, hl, 16, nb) 199 let rc: i64 = skills_parse(argv[3] as *u8, rn, rl, 16, nb2) 200 let s: i64 = match_score(hn, hl, hc, rn, rl, rc) 201 p("MATCH-SCORE " as *u8); pn(s); p("/100\n" as *u8) 202 return 0 203 } 204 if seq(cmd, "gap" as *u8) == 1 { 205 if argc < 4 { p("usage: gap <have-csv> <target-csv>\n" as *u8); return 1 } 206 let nb: *u8 = sys_mmap(64 * 16); let nb2: *u8 = sys_mmap(64 * 16) 207 let hn: *i64 = sys_mmap(8 * 16) as *i64; let hl: *i64 = sys_mmap(8 * 16) as *i64 208 let rn: *i64 = sys_mmap(8 * 16) as *i64; let rl: *i64 = sys_mmap(8 * 16) as *i64 209 let hc: i64 = skills_parse(argv[2] as *u8, hn, hl, 16, nb) 210 let rc: i64 = skills_parse(argv[3] as *u8, rn, rl, 16, nb2) 211 let g: i64 = skill_gap(hn, hl, hc, rn, rl, rc) 212 p("SKILL-GAP total=" as *u8); pn(g); p("\n" as *u8) 213 return 0 214 } 215 p("NX-CAREER usage: match | gap | stage | selftest\n" as *u8) 216 return 1 217}