code wiki / (root) / nx_arousal_clinical.nx

nx_arousal_clinical.nx source

↩ module page · 205 lines · 10519 B

1// nx_arousal_clinical.nx -- the THERAPY READOUT. Given a dual-control profile (SES/SIS1/SIS2), 2// identify WHICH axis is limiting this person and what changing it would actually buy them, in 3// minutes rather than in permil. 4// 5// ★WHY THIS AND NOT A SCORE: the clinically load-bearing finding is that most arousal difficulty is 6// EXCESS INHIBITION, not deficit excitation -- and SIS1 (threat of performance failure) vs SIS2 7// (threat of consequences) point to DIFFERENT treatments. A single "arousal score" collapses exactly 8// the distinction the clinician needs. So this organ reports COUNTERFACTUALS per axis, never a scalar. 9// 10// ★AND IT REFUSES TO GUESS. When two axes are within `margin` of each other the limiting factor is 11// genuinely ambiguous, and the organ says AMBIGUOUS instead of naming one. An instrument that always 12// names a cause is a horoscope; the refusal is the difference between a measurement and a reading. 13// 14// ★TIME IS THE UNIT A PERSON FEELS. Permil drive means nothing to a user; "this adds 9 minutes to 15// your time-to-plateau" is the thing a therapy session can act on. 16// 17// usage: nx_arousal_clinical profile <stim> <ses> <sis1> <sis2> [sex0m1f] [target_permil] 18// nx_arousal_clinical selftest 19// license_tier: ORIGINAL expect_exit: 0 20import "nx_arousal_lib.nx" 21const CL_MAGIC_185000: i64 = 185000 22const CL_MAGIC_210000: i64 = 210000 23 24const CL_MAXMS: i64 = 3600000 25const CL_MARGIN: i64 = 40 26 27// time in ms for perfusion to first reach `target` under a constant drive; CL_MAXMS if never. 28func cl_time_to(target: i64, drive: i64, tau: i64) -> i64 { 29 if drive <= 0 { return CL_MAXMS } 30 if target <= 0 { return 0 } 31 var cur: i64 = 0 32 let tgt: i64 = drive * 1000 33 var t: i64 = 0 34 while t < CL_MAXMS { 35 cur = a_lag(cur, tgt, tau) 36 t = t + 50 37 if cur / 1000 >= target { return t } 38 } 39 return CL_MAXMS 40} 41func cl_name(ax: i64) -> *u8 { 42 if ax == 0 { return "SIS1_performance_threat" as *u8 } 43 if ax == 1 { return "SIS2_consequence_threat" as *u8 } 44 return "SES_excitation_deficit" as *u8 45} 46// what a clinician should actually do about the winning axis 47func cl_advice(ax: i64) -> *u8 { 48 if ax == 0 { return "performance-anxiety pathway: sensate focus, non-demand touch, remove goal" as *u8 } 49 if ax == 1 { return "consequence/context pathway: privacy, safety, relational or situational" as *u8 } 50 return "excitation pathway: stimulus intensity, novelty, directed self-focus" as *u8 51} 52 53func main(argc: i64, argv: *i64) -> i64 { 54 if argc < 2 { a_puts("REFUSED reason=no_verb\n" as *u8); return 2 } 55 let verb: *u8 = argv[1] as *u8 56 let cl2: *i64 = sys_mmap(16) as *i64 57 cl2[0] = 0 58 let cbuf: *u8 = sys_read_file("arousal_params.conf" as *u8, cl2) 59 let cn: i64 = cl2[0] 60 61 if verb[0] == (115 as u8) { 62 a_puts("CLINICAL-SELFTEST margin=" as *u8); a_putn(CL_MARGIN) 63 a_puts(" conf_bytes=" as *u8); a_putn(cn) 64 a_puts("\n" as *u8) 65 return 0 66 } 67 // ★OBSERVED MODE: diagnose from what session data can ACTUALLY recover. 68 // The `profile` verb needs SES and SIS1 separately -- precisely the pair nx_arousal_profile 69 // proves are structurally confounded. So the two organs could not be composed at all, which 70 // would have shipped as an architecture that quietly requires a questionnaire nobody ran. 71 // SIS2 IS identifiable (it is the intercept), so this much is always decidable: is the limiting 72 // factor consequence-threat, or the net excitation term? The net term is then reported as 73 // un-attributable BY CONSTRUCTION rather than guessed at. 74 if verb[0] == (111 as u8) { 75 if argc < 5 { a_puts("REFUSED reason=observed_needs_stim_net_sis2\n" as *u8); return 2 } 76 let ostim: i64 = a_atoi(argv[2] as *u8) 77 let onet: i64 = a_atoi(argv[3] as *u8) 78 let osis2: i64 = a_atoi(argv[4] as *u8) 79 if ostim < 0 { a_puts("REFUSED reason=stim_out_of_range\n" as *u8); return 3 } 80 if ostim > 1000 { a_puts("REFUSED reason=stim_out_of_range\n" as *u8); return 3 } 81 if osis2 < 0 { a_puts("REFUSED reason=sis2_out_of_range\n" as *u8); return 3 } 82 if osis2 > 1000 { a_puts("REFUSED reason=sis2_out_of_range\n" as *u8); return 3 } 83 if onet > 1000 { a_puts("REFUSED reason=net_out_of_range\n" as *u8); return 3 } 84 // ★COUNTERFACTUALS MUST BE COMPUTED UNCLAMPED. drive clamps to [0,1000], so a profile whose 85 // true drive is -220 reports 0 -- and differencing against that clamped 0 understates the 86 // gain from removing the term that pushed it negative. That distortion can FLIP which axis 87 // looks limiting, which is the one error a treatment recommendation must not make. 88 // Same law as refusing a saturated session: a clamp is a bound, not a measurement. 89 let raw: i64 = (onet * ostim) / 1000 - osis2 90 let raw_a: i64 = (onet * ostim) / 1000 91 let raw_b: i64 = ostim - osis2 92 let ga: i64 = raw_a - raw 93 let gb: i64 = raw_b - raw 94 var obase: i64 = raw 95 if obase < 0 { obase = 0 } 96 if obase > 1000 { obase = 1000 } 97 var clamped: i64 = 0 98 if raw != obase { clamped = 1 } 99 a_puts("ok drive=" as *u8); a_putn(obase) 100 a_puts(" raw_drive=" as *u8); a_putn(raw) 101 a_puts(" clamped=" as *u8); a_putn(clamped) 102 a_puts(" gain_remove_sis2=" as *u8); a_putn(ga) 103 a_puts(" gain_max_net=" as *u8); a_putn(gb) 104 var diff: i64 = ga - gb 105 if diff < 0 { diff = 0 - diff } 106 if diff < CL_MARGIN { 107 a_puts(" limiting=AMBIGUOUS margin=" as *u8); a_putn(diff) 108 a_puts(" verdict=REFUSED_TOO_CLOSE_TO_CALL\n" as *u8) 109 return 0 110 } 111 if ga > gb { 112 a_puts(" limiting=SIS2_consequence_threat attributable=YES" as *u8) 113 a_puts(" pathway=" as *u8); a_puts(cl_advice(1)) 114 a_puts("\n" as *u8) 115 return 0 116 } 117 // ★the net term wins -- and it CANNOT be split into SES vs SIS1 from session data. 118 a_puts(" limiting=NET_EXCITATION_TERM attributable=NO" as *u8) 119 a_puts(" note=cannot_split_ses_from_sis1_without_the_SIS/SES_instrument\n" as *u8) 120 return 0 121 } 122 if verb[0] != (112 as u8) { a_puts("REFUSED reason=unknown_verb\n" as *u8); return 2 } 123 if argc < 6 { a_puts("REFUSED reason=profile_needs_4_args\n" as *u8); return 2 } 124 125 let stim: i64 = a_atoi(argv[2] as *u8) 126 let ses: i64 = a_atoi(argv[3] as *u8) 127 let sis1: i64 = a_atoi(argv[4] as *u8) 128 let sis2: i64 = a_atoi(argv[5] as *u8) 129 var sex: i64 = 1 130 if argc >= 7 { sex = a_atoi(argv[6] as *u8) } 131 var target: i64 = 650 132 if argc >= 8 { target = a_atoi(argv[7] as *u8) } 133 if stim < 0 { a_puts("REFUSED reason=stim_out_of_range\n" as *u8); return 3 } 134 if stim > 1000 { a_puts("REFUSED reason=stim_out_of_range\n" as *u8); return 3 } 135 if ses < 0 { a_puts("REFUSED reason=ses_out_of_range\n" as *u8); return 3 } 136 if ses > 1000 { a_puts("REFUSED reason=ses_out_of_range\n" as *u8); return 3 } 137 if sis1 < 0 { a_puts("REFUSED reason=sis1_out_of_range\n" as *u8); return 3 } 138 if sis1 > 1000 { a_puts("REFUSED reason=sis1_out_of_range\n" as *u8); return 3 } 139 if sis2 < 0 { a_puts("REFUSED reason=sis2_out_of_range\n" as *u8); return 3 } 140 if sis2 > 1000 { a_puts("REFUSED reason=sis2_out_of_range\n" as *u8); return 3 } 141 if target <= 0 { a_puts("REFUSED reason=target_out_of_range\n" as *u8); return 3 } 142 if target > 1000 { a_puts("REFUSED reason=target_out_of_range\n" as *u8); return 3 } 143 144 var tau: i64 = a_conf(cbuf, cn, "tau_slow_m_ms" as *u8, CL_MAGIC_185000) 145 if sex == 1 { tau = a_conf(cbuf, cn, "tau_slow_f_ms" as *u8, CL_MAGIC_210000) } 146 147 // baseline and the three single-axis counterfactuals 148 let base: i64 = a_drive(stim, ses, sis1, sis2) 149 let d_no1: i64 = a_drive(stim, ses, 0, sis2) 150 let d_no2: i64 = a_drive(stim, ses, sis1, 0) 151 let d_ses: i64 = a_drive(stim, 1000, sis1, sis2) 152 let g1: i64 = d_no1 - base 153 let g2: i64 = d_no2 - base 154 let g3: i64 = d_ses - base 155 156 // rank the gains; the limiting axis is the one whose removal buys the most 157 var top: i64 = 0 158 var tg: i64 = g1 159 if g2 > tg { top = 1; tg = g2 } 160 if g3 > tg { top = 2; tg = g3 } 161 var second: i64 = 0 162 if top == 0 { second = g2; if g3 > second { second = g3 } } 163 if top == 1 { second = g1; if g3 > second { second = g3 } } 164 if top == 2 { second = g1; if g2 > second { second = g2 } } 165 166 let t_base: i64 = cl_time_to(target, base, tau) 167 let t_fix: i64 = cl_time_to(target, base + tg, tau) 168 // ★REACHABILITY IS THE HEADLINE, NOT THE CLOCK. Perfusion asymptotes at the drive, so a profile 169 // whose drive sits below the target NEVER reaches it -- time is unbounded, not large. Reporting 170 // the CL_MAXMS cap as if it were a duration would turn "never reaches plateau" into "takes a 171 // while", which understates the finding in the one direction a clinical readout must not. 172 var reach_base: i64 = 1 173 if t_base >= CL_MAXMS { reach_base = 0 } 174 var reach_fix: i64 = 1 175 if t_fix >= CL_MAXMS { reach_fix = 0 } 176 var saved: i64 = 0 177 if reach_base == 1 { if reach_fix == 1 { saved = t_base - t_fix } } 178 if saved < 0 { saved = 0 } 179 180 a_puts("ok drive=" as *u8); a_putn(base) 181 a_puts(" gain_sis1=" as *u8); a_putn(g1) 182 a_puts(" gain_sis2=" as *u8); a_putn(g2) 183 a_puts(" gain_ses=" as *u8); a_putn(g3) 184 a_puts(" reach_base=" as *u8); a_putn(reach_base) 185 a_puts(" reach_fixed=" as *u8); a_putn(reach_fix) 186 if reach_base == 1 { a_puts(" t_base_s=" as *u8); a_putn(t_base / 1000) } else { a_puts(" t_base_s=NEVER" as *u8) } 187 if reach_fix == 1 { a_puts(" t_fixed_s=" as *u8); a_putn(t_fix / 1000) } else { a_puts(" t_fixed_s=NEVER" as *u8) } 188 if reach_base == 1 { a_puts(" saved_s=" as *u8); a_putn(saved / 1000) } else { a_puts(" saved_s=UNBOUNDED" as *u8) } 189 190 // ★THE REFUSAL: no axis dominates -> say so. Naming one here would be invention. 191 if tg <= 0 { 192 a_puts(" limiting=NONE verdict=NOT_INHIBITION_LIMITED\n" as *u8) 193 return 0 194 } 195 if tg - second < CL_MARGIN { 196 a_puts(" limiting=AMBIGUOUS margin=" as *u8); a_putn(tg - second) 197 a_puts(" verdict=REFUSED_TOO_CLOSE_TO_CALL\n" as *u8) 198 return 0 199 } 200 a_puts(" limiting=" as *u8); a_puts(cl_name(top)) 201 a_puts(" margin=" as *u8); a_putn(tg - second) 202 a_puts(" pathway=" as *u8); a_puts(cl_advice(top)) 203 a_puts("\n" as *u8) 204 return 0 205}