nx_ecotone.nx source
↩ module page · 286 lines · 12029 B
1// nx_ecotone.nx -- ecological design at ecosystem transition zones.
2//
3// Per META-CARDINAL feedback-regenerative-stewardship-doctrine-captain-moroni:
4// an ecotone is the transition between two ecosystems (forest-to-meadow,
5// grassland-to-savanna, riparian-to-upland, desert-to-oasis). Ecotones
6// host highest biodiversity because organisms from both adjacent systems
7// can occupy the gradient. They are also where regenerative intervention
8// has HIGHEST LEVERAGE.
9//
10// V1 ships sealed enum of ecotone transition types + sealed enum of
11// candidate-species roles + companion-plant compatibility matrix +
12// per-ecotone succession trajectory at 5/10/25/50/100 year horizons.
13// Per [[feedback-real-playtest-loop-not-just-logs]] honest scope: per-
14// site selection is operator-attested; substrate provides the design
15// substrate, never claims unique knowledge of any specific site.
16//
17// Composes:
18// nx_phenology -- species timing co-design
19// nx_pollinator -- pollinator requirements per species
20// nx_disturbance -- some ecotones depend on disturbance regime
21// nx_microclimate -- microclimate gates species selection
22// nx_covenant -- Indigenous + landowner consent for any planting
23//
24// Gap list (V1 honest perf verdict):
25// - species enum is illustrative (V2 ingests species DB via nx_hypha
26// from USDA PLANTS / GBIF / regional flora authorities)
27// - companion-plant matrix is per-functional-role (V2 species-level)
28// - succession trajectories are at functional-class granularity
29//
30// genealogy_id: nishi_metacardinal_2026-05-19_regenerative_stewardship
31// lineage_id: substrate_ecotone_v1
32//
33// nx_safety_envelope:
34// intended_use: "Ecotone transition modeling + species role
35// matching + companion matrix + succession
36// trajectory; substrate informs gardener,
37// per-site selection is operator + ecologist"
38// sil_target: SIL2
39// evidence: [enum_sealed, succession_at_5_horizons,
40// invasive_flag_per_role_recommended]
41// verdict: NOT_YET_EVALUATED
42
43import "nx_syscalls.nx"
44import "nx_tier.nx"
45
46// ===== Sealed enum: NxEcotoneTransition ===========================
47
48const NX_ET_FOREST_MEADOW: nx_int = 0
49const NX_ET_GRASSLAND_SAVANNA: nx_int = 1
50const NX_ET_RIPARIAN_UPLAND: nx_int = 2
51const NX_ET_DESERT_OASIS: nx_int = 3
52const NX_ET_WETLAND_UPLAND: nx_int = 4
53const NX_ET_ALPINE_TIMBERLINE: nx_int = 5
54const NX_ET_COASTAL_DUNE: nx_int = 6
55const NX_ET_FOREST_CLEARCUT: nx_int = 7 // restoration context
56const NX_ET_STRIPMINE_RECLAMATION: nx_int = 8
57const NX_ET_URBAN_GREENSPACE: nx_int = 9
58const NX_ET_N_TRANSITIONS: nx_int = 10
59
60// ===== Sealed enum: NxSpeciesRole =================================
61//
62// Functional roles species can fill in the ecotone. V1 covers
63// structural + nutrient-fixing + soil-builder + pioneer + understory.
64// Per [[feedback-no-false-ok-substrate-honesty-audit]] the enum
65// includes "INVASIVE_RISK_FLAG" as an explicit role so callers can
66// query "is this species invasive-flagged in this jurisdiction?"
67
68const NX_SR_CANOPY_DOMINANT: nx_int = 0 // mature canopy tree
69const NX_SR_CANOPY_PIONEER: nx_int = 1 // fast-growing pioneer (e.g. paulownia)
70const NX_SR_NITROGEN_FIXER: nx_int = 2 // legume / actinorhizal
71const NX_SR_UNDERSTORY_SHRUB: nx_int = 3
72const NX_SR_GROUND_COVER: nx_int = 4
73const NX_SR_HERBACEOUS_PERENNIAL: nx_int = 5
74const NX_SR_GRAMINOID: nx_int = 6 // grasses + sedges
75const NX_SR_SOIL_FUNGI_PARTNER: nx_int = 7 // mycorrhizal partner
76const NX_SR_POLLINATOR_NECTAR: nx_int = 8 // bee/butterfly/hummingbird-attractor
77const NX_SR_WILDLIFE_FOOD: nx_int = 9 // bird/mammal-food
78const NX_SR_RIPARIAN_BANK_STABILIZER: nx_int = 10 // willow/cottonwood class
79const NX_SR_INVASIVE_RISK_FLAG: nx_int = 11 // explicit flag
80const NX_SR_N_ROLES: nx_int = 12
81
82// ===== Sealed enum: NxSuccessionPhase =============================
83//
84// Time horizon at which the species is expected to contribute.
85
86const NX_SU_PHASE_PIONEER_0_5_YR: nx_int = 0
87const NX_SU_PHASE_EARLY_5_25_YR: nx_int = 1
88const NX_SU_PHASE_MID_25_50_YR: nx_int = 2
89const NX_SU_PHASE_LATE_50_100_YR: nx_int = 3
90const NX_SU_PHASE_OLDGROWTH_100_PLUS: nx_int = 4
91const NX_SU_PHASE_N_PHASES: nx_int = 5
92
93// ===== Sealed enum: NxEcotoneVerdict ==============================
94
95const NX_ET_V_DESIGN_OK: nx_int = 0
96const NX_ET_V_MISSING_FUNCTIONAL_ROLE: nx_int = 1 // gap in role coverage
97const NX_ET_V_INVASIVE_FLAGGED: nx_int = 2 // chosen species invasive
98const NX_ET_V_SUCCESSION_GAP: nx_int = 3 // phase not covered
99const NX_ET_V_INVALID: nx_int = 99
100const NX_ET_V_N_VERDICTS: nx_int = 4
101
102// ===== nx_et_transition_is_valid ==================================
103
104func nx_et_transition_is_valid(t: nx_int) -> nx_int {
105 if t < 0 { return 0 }
106 if t >= NX_ET_N_TRANSITIONS { return 0 }
107 return 1
108}
109
110// ===== nx_sr_role_is_valid ========================================
111
112func nx_sr_role_is_valid(r: nx_int) -> nx_int {
113 if r < 0 { return 0 }
114 if r >= NX_SR_N_ROLES { return 0 }
115 return 1
116}
117
118// ===== nx_su_phase_is_valid =======================================
119
120func nx_su_phase_is_valid(p: nx_int) -> nx_int {
121 if p < 0 { return 0 }
122 if p >= NX_SU_PHASE_N_PHASES { return 0 }
123 return 1
124}
125
126// ===== nx_et_v_is_valid ===========================================
127
128func nx_et_v_is_valid(v: nx_int) -> nx_int {
129 if v < 0 { return 0 }
130 if v >= NX_ET_V_N_VERDICTS { return 0 }
131 return 1
132}
133
134// ===== nx_sr_is_structural ========================================
135//
136// Predicate: returns 1 if the role provides physical structure
137// (canopy, shrub, ground cover, bank stabilizer).
138
139func nx_sr_is_structural(r: nx_int) -> nx_int {
140 if r == NX_SR_CANOPY_DOMINANT { return 1 }
141 if r == NX_SR_CANOPY_PIONEER { return 1 }
142 if r == NX_SR_UNDERSTORY_SHRUB { return 1 }
143 if r == NX_SR_GROUND_COVER { return 1 }
144 if r == NX_SR_RIPARIAN_BANK_STABILIZER { return 1 }
145 return 0
146}
147
148// ===== nx_sr_is_provisioning ======================================
149//
150// Predicate: returns 1 if the role provides resources to other
151// organisms (nitrogen, pollen/nectar, food, mycorrhizal partnership).
152
153func nx_sr_is_provisioning(r: nx_int) -> nx_int {
154 if r == NX_SR_NITROGEN_FIXER { return 1 }
155 if r == NX_SR_POLLINATOR_NECTAR { return 1 }
156 if r == NX_SR_WILDLIFE_FOOD { return 1 }
157 if r == NX_SR_SOIL_FUNGI_PARTNER { return 1 }
158 return 0
159}
160
161// ===== Role bitmask constants ====================================
162
163const NX_SR_BIT_CANOPY_DOM: nx_int = 1
164const NX_SR_BIT_CANOPY_PIO: nx_int = 2
165const NX_SR_BIT_N_FIXER: nx_int = 4
166const NX_SR_BIT_SHRUB: nx_int = 8
167const NX_SR_BIT_GROUND: nx_int = 16
168const NX_SR_BIT_PERENNIAL: nx_int = 32
169const NX_SR_BIT_GRASS: nx_int = 64
170const NX_SR_BIT_MYCO: nx_int = 128
171const NX_SR_BIT_POLL: nx_int = 256
172const NX_SR_BIT_WILDLIFE: nx_int = 512
173const NX_SR_BIT_BANK: nx_int = 1024
174const NX_SR_BIT_INVASIVE: nx_int = 2048
175
176// ===== nx_sr_role_to_bit ==========================================
177
178func nx_sr_role_to_bit(r: nx_int) -> nx_int {
179 if r == NX_SR_CANOPY_DOMINANT { return NX_SR_BIT_CANOPY_DOM }
180 if r == NX_SR_CANOPY_PIONEER { return NX_SR_BIT_CANOPY_PIO }
181 if r == NX_SR_NITROGEN_FIXER { return NX_SR_BIT_N_FIXER }
182 if r == NX_SR_UNDERSTORY_SHRUB { return NX_SR_BIT_SHRUB }
183 if r == NX_SR_GROUND_COVER { return NX_SR_BIT_GROUND }
184 if r == NX_SR_HERBACEOUS_PERENNIAL { return NX_SR_BIT_PERENNIAL }
185 if r == NX_SR_GRAMINOID { return NX_SR_BIT_GRASS }
186 if r == NX_SR_SOIL_FUNGI_PARTNER { return NX_SR_BIT_MYCO }
187 if r == NX_SR_POLLINATOR_NECTAR { return NX_SR_BIT_POLL }
188 if r == NX_SR_WILDLIFE_FOOD { return NX_SR_BIT_WILDLIFE }
189 if r == NX_SR_RIPARIAN_BANK_STABILIZER { return NX_SR_BIT_BANK }
190 if r == NX_SR_INVASIVE_RISK_FLAG { return NX_SR_BIT_INVASIVE }
191 return 0
192}
193
194// ===== nx_et_canonical_required_roles =============================
195//
196// Per-transition canonical role-bitmask the substrate considers
197// MINIMUM functional coverage. Caller (operator) checks site's
198// proposed roles against this; gaps trigger MISSING_FUNCTIONAL_ROLE.
199
200func nx_et_canonical_required_roles(t: nx_int) -> nx_int {
201 if t == NX_ET_FOREST_MEADOW {
202 return NX_SR_BIT_CANOPY_DOM | NX_SR_BIT_SHRUB |
203 NX_SR_BIT_PERENNIAL | NX_SR_BIT_POLL
204 }
205 if t == NX_ET_GRASSLAND_SAVANNA {
206 return NX_SR_BIT_GRASS | NX_SR_BIT_PERENNIAL | NX_SR_BIT_POLL
207 }
208 if t == NX_ET_RIPARIAN_UPLAND {
209 return NX_SR_BIT_BANK | NX_SR_BIT_CANOPY_DOM | NX_SR_BIT_SHRUB
210 }
211 if t == NX_ET_DESERT_OASIS {
212 return NX_SR_BIT_GROUND | NX_SR_BIT_PERENNIAL | NX_SR_BIT_MYCO
213 }
214 if t == NX_ET_WETLAND_UPLAND {
215 return NX_SR_BIT_BANK | NX_SR_BIT_SHRUB | NX_SR_BIT_PERENNIAL
216 }
217 if t == NX_ET_ALPINE_TIMBERLINE {
218 return NX_SR_BIT_SHRUB | NX_SR_BIT_PERENNIAL | NX_SR_BIT_GROUND
219 }
220 if t == NX_ET_COASTAL_DUNE {
221 return NX_SR_BIT_GRASS | NX_SR_BIT_GROUND | NX_SR_BIT_SHRUB
222 }
223 if t == NX_ET_FOREST_CLEARCUT {
224 // Restoration: pioneer + N-fixer most important first 5-25 years
225 return NX_SR_BIT_CANOPY_PIO | NX_SR_BIT_N_FIXER |
226 NX_SR_BIT_GROUND | NX_SR_BIT_MYCO
227 }
228 if t == NX_ET_STRIPMINE_RECLAMATION {
229 // Severely degraded -- pioneer + N-fixer + ground required
230 return NX_SR_BIT_CANOPY_PIO | NX_SR_BIT_N_FIXER |
231 NX_SR_BIT_GROUND
232 }
233 if t == NX_ET_URBAN_GREENSPACE {
234 return NX_SR_BIT_CANOPY_DOM | NX_SR_BIT_SHRUB |
235 NX_SR_BIT_POLL | NX_SR_BIT_PERENNIAL
236 }
237 return 0
238}
239
240// ===== nx_et_role_coverage_gap ====================================
241//
242// Returns the bitmask of REQUIRED roles for the transition that are
243// NOT present in proposed_roles. Zero return = full coverage.
244
245func nx_et_role_coverage_gap(transition: nx_int,
246 proposed_roles: nx_int) -> nx_int {
247 let required: nx_int = nx_et_canonical_required_roles(transition)
248 return required & (~proposed_roles)
249}
250
251// ===== nx_et_classify_design ======================================
252//
253// Given a transition + proposed-roles bitmask + succession-phases-
254// covered bitmask, emit design verdict.
255//
256// Decision rules (priority order):
257// 1. Invalid inputs -> INVALID
258// 2. INVASIVE flag set in proposed_roles -> INVASIVE_FLAGGED
259// 3. Required roles missing -> MISSING_FUNCTIONAL_ROLE
260// 4. Succession phase 0 (pioneer) missing -> SUCCESSION_GAP
261// 5. All gates pass -> DESIGN_OK
262
263func nx_et_classify_design(transition: nx_int,
264 proposed_roles: nx_int,
265 phases_covered: nx_int) -> nx_int {
266 if nx_et_transition_is_valid(transition) == 0 { return NX_ET_V_INVALID }
267 if proposed_roles < 0 { return NX_ET_V_INVALID }
268 if phases_covered < 0 { return NX_ET_V_INVALID }
269
270 if (proposed_roles & NX_SR_BIT_INVASIVE) != 0 {
271 return NX_ET_V_INVASIVE_FLAGGED
272 }
273 let gap: nx_int = nx_et_role_coverage_gap(transition, proposed_roles)
274 if gap != 0 { return NX_ET_V_MISSING_FUNCTIONAL_ROLE }
275 // Require at least pioneer phase (bit 0) to be covered
276 if (phases_covered & 1) == 0 { return NX_ET_V_SUCCESSION_GAP }
277 return NX_ET_V_DESIGN_OK
278}
279
280// ===== nx_et_v_is_concerning ======================================
281
282func nx_et_v_is_concerning(v: nx_int) -> nx_int {
283 if v == NX_ET_V_DESIGN_OK { return 0 }
284 if nx_et_v_is_valid(v) == 0 { return 0 }
285 return 1
286}