nx_homeostasis.nx source
↩ module page · 144 lines · 6411 B
1// nx_homeostasis.nx -- steady-state monitor that triggers migration.
2//
3// Biological analogue: homeostasis is the body's set-point control --
4// when blood glucose / pH / temperature drift past tolerance bands,
5// counter-regulatory signals fire. Nishi homeostasis watches each
6// cell's budget pressure, eviction history, and metabolism heat; when
7// any drifts past its set-point, it emits a migration signal that
8// nx_tropism resolves into a concrete relocation target.
9//
10// This is the structural EXCEED-axis vs the kernel OOM-killer: the
11// kernel waits until the host is OUT of memory and then kills the
12// biggest offender after the fact. Homeostasis fires PROACTIVELY at
13// 90% pressure (Q10 921), giving the substrate slack to relocate the
14// cell cleanly before any user-visible disruption.
15//
16// Composes:
17// nx_budget -- pressure_q10 per resource
18// nx_evict_journal -- count_for_cell to spot eviction storms
19// nx_metabolism -- hot sites are migration candidates
20// nx_attention_class -- foreground cells prefer local migration;
21// idle cells are offload-OK to peer/cloud
22// nx_tropism -- consumes the migration signal to pick target
23//
24// V1 ships per-cell snapshot polling. Continuous monitoring (an
25// integrator that fires on sustained pressure rather than instantaneous)
26// is queued; today the caller drives polling at its preferred cadence
27// (a 100ms tick is the canonical default).
28//
29// Gap list (V1 honest perf verdict):
30// - no integrator / leaky-bucket dampening (transient spikes can
31// fire migration signals)
32// - no cross-cell pressure (only per-cell, not host-aggregate)
33// - no de-bounce on repeated MIGRATE signals from same cell
34// - no return-to-set-point hysteresis (cell re-evaluated each tick)
35//
36// genealogy_id: nishi_cardinal_2026-05-17_cooperative_resource_arbitration + cardinal-21
37// lineage_id: substrate_homeostasis_v1
38//
39// nx_safety_envelope:
40// intended_use: "Proactive migration trigger before host OOM;
41// composes budget+evict+metabolism into one
42// decision signal"
43// sil_target: SIL2
44// evidence: [proactive_not_reactive, named_thresholds,
45// decision_observable_via_signal_enum]
46// hazard_register: [bug-tape-homeostasis-thrash-on-edge-pressure]
47// verdict: NOT_YET_EVALUATED
48
49import "nx_syscalls.nx"
50import "nx_tier.nx"
51import "nx_budget.nx"
52import "nx_evict_journal.nx"
53import "nx_metabolism.nx"
54import "nx_attention_class.nx"
55
56// ===== Sealed enum: NxHomeoSignal =================================
57
58const NX_HOMEO_STEADY: nx_int = 0 // within bands, no action
59const NX_HOMEO_MIGRATE_RAM: nx_int = 1 // RAM pressure -> move
60const NX_HOMEO_MIGRATE_VRAM: nx_int = 2 // VRAM pressure -> move
61const NX_HOMEO_MIGRATE_DISK: nx_int = 3 // disk pressure -> archive
62const NX_HOMEO_MIGRATE_THERMAL: nx_int = 4 // CPU pressure -> offload
63const NX_HOMEO_DEMOTE_TIER: nx_int = 5 // metabolism says step down
64const NX_HOMEO_PROMOTE_TIER: nx_int = 6 // metabolism says step up
65const NX_HOMEO_EVICTION_STORM: nx_int = 7 // displaced too often
66const NX_HOMEO_N_SIGNALS: nx_int = 8
67
68// ===== Set-points ================================================
69//
70// Migration fires at 90% (Q10 921); demotion fires at 95% (Q10 972).
71// Eviction-storm threshold: 5 events in the journal window for this
72// cell. Per Cardinal 11 these are named constants for one-line tuning.
73
74const NX_HOMEO_MIGRATE_PRESSURE_Q10: nx_int = 921
75const NX_HOMEO_DEMOTE_PRESSURE_Q10: nx_int = 972
76const NX_HOMEO_EVICTION_STORM_AT: nx_int = 5
77
78// ===== nx_homeo_check =============================================
79//
80// Single-cell snapshot. Returns the first signal that crosses its
81// set-point; caller is expected to handle that signal then re-check.
82// Ordering: thermal first (most user-visible), then RAM, VRAM, disk,
83// then eviction-storm, then metabolism-driven tier signals.
84//
85// budget may be NULL when the cell has no budget assigned (e.g.,
86// initialization phase); homeostasis returns STEADY in that case
87// rather than spuriously firing migration on uninitialized state.
88
89func nx_homeo_check(b: *NxBudget,
90 j: *NxEvictJournal,
91 m: *NxMetabolism,
92 cell_id: nx_int,
93 site_id: nx_int) -> nx_int {
94 if (b as i64) == 0 { return NX_HOMEO_STEADY }
95
96 let cpu_q: nx_int = nx_budget_pressure_q10(b, NX_RES_CPU)
97 if cpu_q >= NX_HOMEO_MIGRATE_PRESSURE_Q10 { return NX_HOMEO_MIGRATE_THERMAL }
98
99 let ram_q: nx_int = nx_budget_pressure_q10(b, NX_RES_RAM)
100 if ram_q >= NX_HOMEO_MIGRATE_PRESSURE_Q10 { return NX_HOMEO_MIGRATE_RAM }
101
102 let vram_q: nx_int = nx_budget_pressure_q10(b, NX_RES_VRAM)
103 if vram_q >= NX_HOMEO_MIGRATE_PRESSURE_Q10 { return NX_HOMEO_MIGRATE_VRAM }
104
105 let disk_q: nx_int = nx_budget_pressure_q10(b, NX_RES_DISK)
106 if disk_q >= NX_HOMEO_MIGRATE_PRESSURE_Q10 { return NX_HOMEO_MIGRATE_DISK }
107
108 if (j as i64) != 0 {
109 let storms: nx_int = nx_evict_count_for_cell(j, cell_id)
110 if storms >= NX_HOMEO_EVICTION_STORM_AT { return NX_HOMEO_EVICTION_STORM }
111 }
112
113 if (m as i64) != 0 {
114 let suggested: nx_int = nx_metab_suggest_target(m, site_id)
115 // current tier is held in nx_budget metadata in the future;
116 // V1 assumes NX_TIER_WORKSTATION baseline and signals when
117 // metabolism suggests a different tier.
118 if suggested > NX_TIER_WORKSTATION { return NX_HOMEO_PROMOTE_TIER }
119 if suggested < NX_TIER_FAMILY_DEVICE { return NX_HOMEO_DEMOTE_TIER }
120 }
121
122 return NX_HOMEO_STEADY
123}
124
125// ===== nx_homeo_signal_is_migration ===============================
126//
127// Convenience predicate for callers that only care whether ANY
128// migration is indicated (any of RAM/VRAM/DISK/THERMAL).
129
130func nx_homeo_signal_is_migration(sig: nx_int) -> nx_int {
131 if sig == NX_HOMEO_MIGRATE_RAM { return 1 }
132 if sig == NX_HOMEO_MIGRATE_VRAM { return 1 }
133 if sig == NX_HOMEO_MIGRATE_DISK { return 1 }
134 if sig == NX_HOMEO_MIGRATE_THERMAL { return 1 }
135 return 0
136}
137
138// ===== nx_homeo_signal_is_valid ===================================
139
140func nx_homeo_signal_is_valid(sig: nx_int) -> nx_int {
141 if sig < 0 { return 0 }
142 if sig >= NX_HOMEO_N_SIGNALS { return 0 }
143 return 1
144}