nx_succession.nx source
↩ module page · 206 lines · 8064 B
1// nx_succession.nx -- ecological succession stage classification for primitives.
2//
3// Biology: ecosystems pass through PIONEER -> MID -> CLIMAX stages. Each
4// stage has different role: pioneers stabilize bare ground (fast-growing,
5// resource-light, short-lived); mid-stage builds soil / canopy (medium
6// lifespan, medium investment); climax is mature, slow-turnover, hosts the
7// most biodiversity. Removing a pioneer from a climax stand causes minor
8// disturbance; removing climax keystones cascades.
9//
10// In Nishi: classify every primitive by succession stage so prune-rebuild
11// decisions know what's safe to recycle vs what's load-bearing. Composes
12// with [[nx_taxonomy]] (orthogonal axis to taxonomic class) and with the
13// drift lifecycle (spore/seed/hypha/fruiting/lichen are PIONEER-class for
14// the most part).
15
16import "nx_syscalls.nx"
17import "nx_tier.nx"
18
19// ===== Sealed enum: NxSuccessionStage =============================
20
21const NX_SU_STAGE_PIONEER: nx_int = 0 // fast bootstrap, low investment, removable
22const NX_SU_STAGE_EARLY: nx_int = 1 // first build-out, partial replacement-risk
23const NX_SU_STAGE_MID: nx_int = 2 // load-bearing for current generation
24const NX_SU_STAGE_LATE: nx_int = 3 // load-bearing for multiple generations
25const NX_SU_STAGE_CLIMAX: nx_int = 4 // foundational; removal cascades
26const NX_SU_STAGE_RELICT: nx_int = 5 // mature but isolated; legacy-class
27const NX_SU_STAGE_N: nx_int = 6
28
29// ===== Sealed enum: NxSuccessionVerdict ===========================
30
31const NX_SU_V_OK: nx_int = 0
32const NX_SU_V_PROMOTION_OK: nx_int = 1 // primitive ready to advance stage
33const NX_SU_V_PREMATURE: nx_int = 2 // promotion before evidence
34const NX_SU_V_REGRESSION_BLOCKED: nx_int = 3 // never demote climax to pioneer
35const NX_SU_V_INVALID: nx_int = 4
36const NX_SU_V_NULL: nx_int = 5
37const NX_SU_V_N: nx_int = 6
38
39// ===== Struct: NxSuccessionRecord =================================
40
41struct NxSuccessionRecord {
42 primitive_id: nx_int,
43 stage: nx_int,
44 age_seconds: nx_size,
45 n_dependents: nx_int, // count of other primitives that import this
46 smoke_pass_count: nx_int, // consecutive green builds
47 last_evaluated_us: nx_size,
48}
49
50const NX_SU_R_BYTES: nx_int = 40
51
52struct NxSuccessionRegistry {
53 records: *u8,
54 n_records: nx_int,
55 capacity: nx_int,
56 promotion_min_pass_count: nx_int, // min consecutive green smokes to promote
57 climax_min_dependents: nx_int, // min dependents to count as climax
58}
59
60const NX_SU_BYTES: nx_int = 32
61
62// ===== Validators =================================================
63
64func nx_su_stage_is_valid(s: nx_int) -> nx_int {
65 if s < 0 { return 0 }
66 if s >= NX_SU_STAGE_N { return 0 }
67 return 1
68}
69
70func nx_su_v_is_valid(v: nx_int) -> nx_int {
71 if v < 0 { return 0 }
72 if v >= NX_SU_V_N { return 0 }
73 return 1
74}
75
76func nx_su_stage_is_removable(s: nx_int) -> nx_int {
77 if s == NX_SU_STAGE_PIONEER { return 1 }
78 if s == NX_SU_STAGE_EARLY { return 1 }
79 return 0
80}
81
82func nx_su_stage_is_load_bearing(s: nx_int) -> nx_int {
83 if s == NX_SU_STAGE_MID { return 1 }
84 if s == NX_SU_STAGE_LATE { return 1 }
85 if s == NX_SU_STAGE_CLIMAX { return 1 }
86 return 0
87}
88
89// ===== Constructor ================================================
90
91func nx_su_new(capacity: nx_int,
92 promotion_min_pass_count: nx_int,
93 climax_min_dependents: nx_int) -> *NxSuccessionRegistry {
94 if capacity <= 0 { return 0 as *NxSuccessionRegistry }
95 if promotion_min_pass_count <= 0 { return 0 as *NxSuccessionRegistry }
96 if climax_min_dependents <= 0 { return 0 as *NxSuccessionRegistry }
97 let raw: *u8 = sys_mmap(NX_SU_BYTES)
98 let r: *NxSuccessionRegistry = raw as *NxSuccessionRegistry
99 r.records = sys_mmap(capacity * NX_SU_R_BYTES)
100 r.n_records = 0
101 r.capacity = capacity
102 r.promotion_min_pass_count = promotion_min_pass_count
103 r.climax_min_dependents = climax_min_dependents
104 return r
105}
106
107func _su_record_at(reg: *NxSuccessionRegistry, idx: nx_int) -> *NxSuccessionRecord {
108 if idx < 0 { return 0 as *NxSuccessionRecord }
109 if idx >= reg.n_records { return 0 as *NxSuccessionRecord }
110 let off: nx_int = idx * NX_SU_R_BYTES
111 return (reg.records + off) as *NxSuccessionRecord
112}
113
114// ===== Register + lookup ==========================================
115
116func nx_su_register(reg: *NxSuccessionRegistry,
117 primitive_id: nx_int,
118 stage: nx_int,
119 age_seconds: nx_size,
120 n_dependents: nx_int,
121 smoke_pass_count: nx_int,
122 now_us: nx_size) -> nx_int {
123 if (reg as i64) == 0 { return NX_SU_V_NULL }
124 if nx_su_stage_is_valid(stage) == 0 { return NX_SU_V_INVALID }
125 if reg.n_records >= reg.capacity { return NX_SU_V_INVALID }
126 let off: nx_int = reg.n_records * NX_SU_R_BYTES
127 let r: *NxSuccessionRecord = (reg.records + off) as *NxSuccessionRecord
128 r.primitive_id = primitive_id
129 r.stage = stage
130 r.age_seconds = age_seconds
131 r.n_dependents = n_dependents
132 r.smoke_pass_count = smoke_pass_count
133 r.last_evaluated_us = now_us
134 reg.n_records = reg.n_records + 1
135 return NX_SU_V_OK
136}
137
138func nx_su_find(reg: *NxSuccessionRegistry, primitive_id: nx_int) -> *NxSuccessionRecord {
139 if (reg as i64) == 0 { return 0 as *NxSuccessionRecord }
140 var i: nx_int = 0
141 while i < reg.n_records {
142 let r: *NxSuccessionRecord = _su_record_at(reg, i)
143 if r.primitive_id == primitive_id { return r }
144 i = i + 1
145 }
146 return 0 as *NxSuccessionRecord
147}
148
149// ===== Promotion check ============================================
150
151func nx_su_check_promotion(reg: *NxSuccessionRegistry,
152 primitive_id: nx_int) -> nx_int {
153 if (reg as i64) == 0 { return NX_SU_V_NULL }
154 let r: *NxSuccessionRecord = nx_su_find(reg, primitive_id)
155 if (r as i64) == 0 { return NX_SU_V_INVALID }
156 if r.stage == NX_SU_STAGE_CLIMAX { return NX_SU_V_OK }
157 if r.stage == NX_SU_STAGE_RELICT { return NX_SU_V_OK }
158 if r.smoke_pass_count < reg.promotion_min_pass_count { return NX_SU_V_PREMATURE }
159 // Climax requires dependents threshold
160 if r.stage == NX_SU_STAGE_LATE {
161 if r.n_dependents < reg.climax_min_dependents { return NX_SU_V_PREMATURE }
162 }
163 return NX_SU_V_PROMOTION_OK
164}
165
166// ===== Demotion gate (refuses climax->pioneer regression) =========
167
168func nx_su_check_demotion(current_stage: nx_int, proposed_stage: nx_int) -> nx_int {
169 if nx_su_stage_is_valid(current_stage) == 0 { return NX_SU_V_INVALID }
170 if nx_su_stage_is_valid(proposed_stage) == 0 { return NX_SU_V_INVALID }
171 if proposed_stage >= current_stage { return NX_SU_V_OK }
172 // Refuse climax / late skip-back to pioneer / early. Use relict instead.
173 if current_stage == NX_SU_STAGE_CLIMAX {
174 if proposed_stage < NX_SU_STAGE_RELICT { return NX_SU_V_REGRESSION_BLOCKED }
175 }
176 if current_stage == NX_SU_STAGE_LATE {
177 if proposed_stage < NX_SU_STAGE_MID { return NX_SU_V_REGRESSION_BLOCKED }
178 }
179 return NX_SU_V_OK
180}
181
182// ===== Aggregations ===============================================
183
184func nx_su_count_by_stage(reg: *NxSuccessionRegistry, stage: nx_int) -> nx_int {
185 if (reg as i64) == 0 { return 0 }
186 var count: nx_int = 0
187 var i: nx_int = 0
188 while i < reg.n_records {
189 let r: *NxSuccessionRecord = _su_record_at(reg, i)
190 if r.stage == stage { count = count + 1 }
191 i = i + 1
192 }
193 return count
194}
195
196func nx_su_total_dependents(reg: *NxSuccessionRegistry) -> nx_int {
197 if (reg as i64) == 0 { return 0 }
198 var sum: nx_int = 0
199 var i: nx_int = 0
200 while i < reg.n_records {
201 let r: *NxSuccessionRecord = _su_record_at(reg, i)
202 sum = sum + r.n_dependents
203 i = i + 1
204 }
205 return sum
206}