nx_calibration_drift_monitor.nx source
↩ module page · 223 lines · 11423 B
1// nx_calibration_drift_monitor.nx -- scheduled re-characterization + drift alerts.
2//
3// module: nishi-core.perception.calibration_drift_monitor
4// depends: nishi-core.perception.profile + nishi-core.perception.transducer_characterizer +
5// nishi-core.perception.provenance_curve_store + nishi-core.perception.instrument_diagnostician +
6// nishi-core.io.syscalls
7// disk_kb: 5
8// capability: PERCEPTION
9// wired_status: PARTIAL_WIRED
10//
11// MISSING_CAPABILITIES:
12// - SCHEDULE_PERSIST (cron-style schedule serialized to disk + restored
13// on substrate restart; depends on nx_race_timing already FULLY_WIRED
14// — wire the call site)
15// - CURVE_DIFF (compute delta between current FRF and stored baseline;
16// queued, depends on nx_fft once landed)
17// - ALERT_DISPATCH (surface drift findings to nx_audit_dashboard +
18// optional notification path; depends on dashboard UI)
19// - DIAGNOSTIC_HANDOFF (on drift detected, hand result to
20// nx_instrument_diagnostician for "is this just aging or is something
21// broken?" verdict)
22//
23// license_tier: PUBLIC_NISHI_SUBSTRATE
24// genealogy_id: feedback-substrate-does-heavy-lifting-user-is-partner-not-gate_2026 +
25// feedback-substrate-primitives-meta-not-one-off_2026 +
26// feedback-self-surfacing-intelligence-staged-autonomy +
27// feedback-racing-timing-modes-sealed-enum +
28// shewhart_1931_statistical_process_control
29//
30// Per cardinals [[feedback-substrate-does-heavy-lifting-user-is-partner-not-gate]]
31// and [[feedback-substrate-primitives-meta-not-one-off]]:
32// THIS primitive re-runs nx_transducer_characterize on a schedule,
33// compares the result to the stored baseline curve, and surfaces drift
34// before the drift invalidates the measurement chain.
35//
36// User contribution: zero day-to-day. Substrate watches. User only
37// hears from substrate when drift exceeds a configurable threshold +
38// substrate needs ratification on the proposed action (re-calibrate,
39// replace component, accept as new baseline).
40//
41// Reuse set served by this single primitive:
42// - Verification mic aging (MEMS capsule sensitivity drifts over months)
43// - Speaker ribbon tension creep (ribbon tweeter shifts resonance over months)
44// - DAC reference voltage drift (LT1019 PPM/°C tracks ambient temp)
45// - Amplifier bias-current drift (lateral MOSFET thermal coefficient)
46// - Camera sensor pixel-response drift (dead pixels, dark current)
47// - Scent cartridge dispense depletion (chemical inventory monitoring)
48// - Hydrophone pressure-response aging (saltwater + epoxy degradation)
49// - Display backlight degradation (LED brightness fade)
50// - Heart-rate strap electrode contact resistance change (conductive
51// fabric wears over wash cycles)
52// - Every calibrated artifact in every Nishi hardware product, indefinitely
53
54import "nx_syscalls.nx"
55import "nx_perceptual_profile.nx"
56import "nx_transducer_characterizer.nx"
57import "nx_provenance_curve_store.nx"
58import "nx_instrument_diagnostician.nx"
59
60// ===== Drift kind sealed enum =====================================
61//
62// What KIND of drift was detected. Drives downstream action selection.
63
64const NX_DRIFT_KIND_NONE: i64 = 0 // no drift detected
65const NX_DRIFT_KIND_MONOTONIC_DECLINE: i64 = 1 // sensitivity dropping
66const NX_DRIFT_KIND_MONOTONIC_INCREASE: i64 = 2 // gain creep
67const NX_DRIFT_KIND_FREQUENCY_SHIFT: i64 = 3 // resonance moved
68const NX_DRIFT_KIND_NEW_RESONANCE_PEAK: i64 = 4 // peak appeared not in baseline
69const NX_DRIFT_KIND_LOST_RESONANCE_PEAK: i64 = 5 // baseline peak disappeared
70const NX_DRIFT_KIND_NOISE_FLOOR_RISE: i64 = 6 // SNR degradation
71const NX_DRIFT_KIND_DISTORTION_INCREASE: i64 = 7 // THD climbed
72const NX_DRIFT_KIND_INTERMITTENT_DROPOUT: i64 = 8 // not always reproducing
73const NX_DRIFT_KIND_TEMPERATURE_RELATED: i64 = 9 // correlates with ambient temp
74const NX_DRIFT_KIND_CARTRIDGE_DEPLETION: i64 = 10 // chemical inventory low
75const NX_DRIFT_KIND_PIXEL_DEFECT: i64 = 11 // visual: new dead/hot pixels
76
77func nx_drift_kind_name(k: i64) -> *u8 {
78 if k == NX_DRIFT_KIND_NONE { return "NONE" }
79 if k == NX_DRIFT_KIND_MONOTONIC_DECLINE { return "MONOTONIC_DECLINE" }
80 if k == NX_DRIFT_KIND_MONOTONIC_INCREASE { return "MONOTONIC_INCREASE" }
81 if k == NX_DRIFT_KIND_FREQUENCY_SHIFT { return "FREQUENCY_SHIFT" }
82 if k == NX_DRIFT_KIND_NEW_RESONANCE_PEAK { return "NEW_RESONANCE_PEAK" }
83 if k == NX_DRIFT_KIND_LOST_RESONANCE_PEAK { return "LOST_RESONANCE_PEAK" }
84 if k == NX_DRIFT_KIND_NOISE_FLOOR_RISE { return "NOISE_FLOOR_RISE" }
85 if k == NX_DRIFT_KIND_DISTORTION_INCREASE { return "DISTORTION_INCREASE" }
86 if k == NX_DRIFT_KIND_INTERMITTENT_DROPOUT { return "INTERMITTENT_DROPOUT" }
87 if k == NX_DRIFT_KIND_TEMPERATURE_RELATED { return "TEMPERATURE_RELATED" }
88 if k == NX_DRIFT_KIND_CARTRIDGE_DEPLETION { return "CARTRIDGE_DEPLETION" }
89 if k == NX_DRIFT_KIND_PIXEL_DEFECT { return "PIXEL_DEFECT" }
90 return "UNKNOWN_DRIFT_KIND"
91}
92
93// ===== Drift action sealed enum ===================================
94//
95// What substrate recommends doing about a detected drift.
96
97const NX_DRIFT_ACTION_NONE: i64 = 0 // no action; drift within tolerance
98const NX_DRIFT_ACTION_ACCEPT_NEW_BASELINE: i64 = 1 // drift normal; new baseline stored
99const NX_DRIFT_ACTION_AUTO_RECOMPENSATE: i64 = 2 // substrate updates compensator
100 // curve in place; no user action
101const NX_DRIFT_ACTION_PROMPT_RECALIBRATE: i64 = 3 // ask user to re-run characterize
102const NX_DRIFT_ACTION_PROMPT_REPLACE_PART: i64 = 4 // drift beyond compensation
103const NX_DRIFT_ACTION_HALT_USE: i64 = 5 // measurement no longer trustworthy
104const NX_DRIFT_ACTION_DIAGNOSTIC_HANDOFF: i64 = 6 // hand to instrument_diagnostician
105 // for root-cause analysis
106
107func nx_drift_action_name(a: i64) -> *u8 {
108 if a == NX_DRIFT_ACTION_NONE { return "NONE" }
109 if a == NX_DRIFT_ACTION_ACCEPT_NEW_BASELINE { return "ACCEPT_NEW_BASELINE" }
110 if a == NX_DRIFT_ACTION_AUTO_RECOMPENSATE { return "AUTO_RECOMPENSATE" }
111 if a == NX_DRIFT_ACTION_PROMPT_RECALIBRATE { return "PROMPT_RECALIBRATE" }
112 if a == NX_DRIFT_ACTION_PROMPT_REPLACE_PART { return "PROMPT_REPLACE_PART" }
113 if a == NX_DRIFT_ACTION_HALT_USE { return "HALT_USE" }
114 if a == NX_DRIFT_ACTION_DIAGNOSTIC_HANDOFF { return "DIAGNOSTIC_HANDOFF" }
115 return "UNKNOWN_DRIFT_ACTION"
116}
117
118// ===== Monitor schedule sealed enum ===============================
119//
120// Composes with [[feedback-racing-timing-modes-sealed-enum]] — the
121// drift monitor is a CONTINUOUS / SCHEDULED / IDLE-opportunistic
122// substrate process that yields to user-interactive workloads.
123
124const NX_DRIFT_SCHED_DAILY: i64 = 1
125const NX_DRIFT_SCHED_WEEKLY: i64 = 2
126const NX_DRIFT_SCHED_MONTHLY: i64 = 3
127const NX_DRIFT_SCHED_QUARTERLY: i64 = 4
128const NX_DRIFT_SCHED_ON_TEMPERATURE_DROP: i64 = 5 // when ambient temp shifts >5°C
129const NX_DRIFT_SCHED_ON_USE_HOURS: i64 = 6 // after N hours of use
130const NX_DRIFT_SCHED_ON_DEMAND: i64 = 7 // manual trigger only
131const NX_DRIFT_SCHED_CONTINUOUS: i64 = 8 // every characterization run
132
133func nx_drift_sched_name(s: i64) -> *u8 {
134 if s == NX_DRIFT_SCHED_DAILY { return "DAILY" }
135 if s == NX_DRIFT_SCHED_WEEKLY { return "WEEKLY" }
136 if s == NX_DRIFT_SCHED_MONTHLY { return "MONTHLY" }
137 if s == NX_DRIFT_SCHED_QUARTERLY { return "QUARTERLY" }
138 if s == NX_DRIFT_SCHED_ON_TEMPERATURE_DROP { return "ON_TEMPERATURE_DROP" }
139 if s == NX_DRIFT_SCHED_ON_USE_HOURS { return "ON_USE_HOURS" }
140 if s == NX_DRIFT_SCHED_ON_DEMAND { return "ON_DEMAND" }
141 if s == NX_DRIFT_SCHED_CONTINUOUS { return "CONTINUOUS" }
142 return "UNKNOWN_SCHEDULE"
143}
144
145// ===== Drift finding struct =======================================
146
147struct NxDriftFinding {
148 instrument_serial_hash_ptr: *u8
149 instrument_serial_hash_len: i64
150 drift_kind: i64 // NX_DRIFT_KIND_*
151 drift_magnitude_x1000: i64 // milli-units of deviation from baseline
152 affected_freq_lo_hz: i64
153 affected_freq_hi_hz: i64
154 recommended_action: i64 // NX_DRIFT_ACTION_*
155 confidence_score: i64 // 0..100
156 baseline_curve_hash_ptr: *u8
157 baseline_curve_hash_len: i64
158 current_curve_hash_ptr: *u8
159 current_curve_hash_len: i64
160}
161
162// ===== Top-level entry stubs ======================================
163
164// nx_drift_monitor_enable -- begin watching a specific instrument on the
165// given schedule. Schedule persisted; substrate re-arms across restarts.
166
167func nx_drift_monitor_enable(instrument_serial_ptr: *u8, instrument_serial_len: i64,
168 schedule: i64,
169 threshold_pct_x10: i64) -> i64 {
170 if instrument_serial_len <= 0 { return 0 }
171 if schedule < NX_DRIFT_SCHED_DAILY { return 0 }
172 if schedule > NX_DRIFT_SCHED_CONTINUOUS { return 0 }
173 if threshold_pct_x10 <= 0 { return 0 }
174 // PARTIAL_WIRED: schedule persistence + tick wiring queued.
175 return 0
176}
177
178// nx_drift_monitor_disable -- stop monitoring this instrument.
179
180func nx_drift_monitor_disable(instrument_serial_ptr: *u8, instrument_serial_len: i64) -> i64 {
181 if instrument_serial_len <= 0 { return 0 }
182 return 1
183}
184
185// nx_drift_monitor_check_now -- run a single drift check immediately
186// outside the schedule. Used by audit dashboard "verify rig is still good"
187// button + by post-incident triage.
188
189func nx_drift_monitor_check_now(instrument_serial_ptr: *u8, instrument_serial_len: i64,
190 finding_ptr: *NxDriftFinding) -> i64 {
191 if instrument_serial_len <= 0 { return NX_DRIFT_KIND_NONE }
192 // PARTIAL_WIRED: needs nx_transducer_characterize + curve_diff +
193 // pattern matching.
194 return NX_DRIFT_KIND_NONE
195}
196
197// nx_drift_monitor_handle_finding -- substrate decides what to do with
198// a detected drift: accept new baseline, auto-recompensate, prompt user,
199// or hand off to instrument diagnostician. Per
200// [[feedback-self-surfacing-intelligence-staged-autonomy]] Stage 1
201// SUPERVISED: substrate proposes action, user ratifies.
202
203func nx_drift_monitor_handle_finding(finding_ptr: *NxDriftFinding,
204 user_ratified: i64) -> i64 {
205 if user_ratified != 1 { return NX_DRIFT_ACTION_NONE } // refuse Stage-3
206 // unsupervised today
207 return NX_DRIFT_ACTION_NONE
208}
209
210// nx_drift_monitor_get_age_days -- how many days since this instrument
211// was last characterized? Useful for batch "what needs checking" queries.
212
213func nx_drift_monitor_get_age_days(instrument_serial_ptr: *u8, instrument_serial_len: i64) -> i64 {
214 if instrument_serial_len <= 0 { return -1 }
215 return -1
216}
217
218// nx_drift_monitor_count_monitored -- inspector: how many instruments
219// currently being watched?
220
221func nx_drift_monitor_count_monitored() -> i64 {
222 return 0
223}