nx_hunter.nx source
↩ module page · 297 lines · 10678 B
1// nx_hunter.nx -- outward-axis collection primitive.
2//
3// Per [[feedback-hunter-gatherer-meta-primitives-outward-inward-axes]]
4// CARDINAL: "nx_hunter (outward — collect/kill bugs+features+feedback+
5// threats+competitor LOSE-axes)." Hunter is the racing-crew arm of
6// the substrate: it goes OUT, finds threats and opportunities, and
7// either KILLS them (bugs fixed, EXCEED-axis shipped) or CARRIES
8// them home (feedback absorbed, cardinals updated).
9//
10// THE STRATEGIC PRIMITIVE per user 2026-05-17: "hunting being the
11// collection and 'killing' of bugs features feedback etc as part of
12// the racing crew team." Every session must emit at least one hunt
13// event (a target scouted, an active hunt logged, or a kill recorded)
14// or the session was a non-session per the cardinal's audit gate.
15//
16// Composes:
17// nx_hunt_journal -- append-only event log; what was hunted, when
18// nx_gatherer -- the inward-axis dual; hunter and gatherer
19// together form the session-audit gate
20// nx_xenocell -- a threat actor caught = a hunt KILL event
21// nx_antibody -- detection signature derived from a kill
22// nx_provenance_chain -- the kill is a transform; chain-logged
23// nx_pollinate -- federation-ready kills propagate to peers
24// (community-shared hunt intelligence)
25//
26// V1 ships:
27// - sealed enum NxHuntKind (5 target classes from cardinal)
28// - sealed enum NxHuntStatus (4 states: scouted/active/killed/archived)
29// - sealed enum NxKillPillar (4 kill-classes from cardinal)
30// - struct NxHuntTarget + NxHunter container
31// - scout/promote/kill/archive verbs
32// - count_by_status + count_kills_by_pillar queries
33//
34// Gap list (V1 honest perf verdict):
35// - target descriptions are caller-supplied byte buffers (V2 adds
36// structured taxonomy via content-addressed schema)
37// - no per-pillar specialized lifecycle (V2 differentiates)
38// - no automatic federation-readiness predicate (V2 composes with
39// pollinate per kill-pillar policy)
40// - no time-windowed metrics (V2 adds rolling-window kill rate)
41//
42// genealogy_id: cardinal_2026-05-17_hunter_gatherer_meta_primitives +
43// racing_crew_metaphor + biology_predator_hunting
44// lineage_id: substrate_hunter_v1
45//
46// nx_safety_envelope:
47// intended_use: "Outward-axis collection of bugs / features /
48// feedback / threats / competitor LOSE-axes;
49// defensive only per Captain Moroni; never
50// offensive hunt"
51// sil_target: SIL2
52// evidence: [defensive_only, captain_moroni_aligned,
53// kills_are_substrate_outputs_not_attacks]
54// verdict: NOT_YET_EVALUATED
55
56import "nx_syscalls.nx"
57import "nx_tier.nx"
58
59// ===== Sealed enum: NxHuntKind ====================================
60//
61// Per cardinal "competitor product / bug class / user feedback /
62// threat actor / missing feature."
63
64const NX_HK_COMPETITOR_PRODUCT: nx_int = 0
65const NX_HK_BUG_CLASS: nx_int = 1
66const NX_HK_USER_FEEDBACK: nx_int = 2
67const NX_HK_THREAT_ACTOR: nx_int = 3
68const NX_HK_MISSING_FEATURE: nx_int = 4
69const NX_HK_N_KINDS: nx_int = 5
70
71// ===== Sealed enum: NxHuntStatus ==================================
72
73const NX_HS_SCOUTED: nx_int = 0 // target identified, not yet engaged
74const NX_HS_ACTIVE: nx_int = 1 // active hunt in progress
75const NX_HS_KILLED: nx_int = 2 // hunt succeeded (one of 4 pillars)
76const NX_HS_ARCHIVED: nx_int = 3 // closed without kill; learning kept
77const NX_HS_N_STATES: nx_int = 4
78
79// ===== Sealed enum: NxKillPillar ==================================
80//
81// Per cardinal "four-pillar bug-fix / EXCEED-axis-ship / threat-
82// hardened / cardinal-absorbed."
83
84const NX_KP_NONE: nx_int = 0
85const NX_KP_BUG_FIXED: nx_int = 1
86const NX_KP_EXCEED_AXIS_SHIPPED: nx_int = 2
87const NX_KP_THREAT_HARDENED: nx_int = 3
88const NX_KP_CARDINAL_ABSORBED: nx_int = 4
89const NX_KP_N_PILLARS: nx_int = 5
90
91// ===== Sealed enum: NxHunterVerdict ===============================
92
93const NX_HUNTER_OK: nx_int = 0
94const NX_HUNTER_ERR_FULL: nx_int = 1
95const NX_HUNTER_ERR_BAD_KIND: nx_int = 2
96const NX_HUNTER_ERR_BAD_STATUS: nx_int = 3
97const NX_HUNTER_ERR_BAD_PILLAR: nx_int = 4
98const NX_HUNTER_ERR_NOT_FOUND: nx_int = 5
99const NX_HUNTER_ERR_BAD_TRANSITION: nx_int = 6
100
101// ===== Struct: NxHuntTarget =======================================
102
103struct NxHuntTarget {
104 target_id: nx_int,
105 kind: nx_int,
106 status: nx_int,
107 kill_pillar: nx_int, // NX_KP_NONE until status=KILLED
108 content_hash: nx_size,
109 scouted_us: nx_size,
110 last_event_us: nx_size,
111}
112
113// ===== Struct: NxHunter ===========================================
114
115struct NxHunter {
116 targets: *NxHuntTarget,
117 capacity: nx_size,
118 head: nx_size,
119 count: nx_size,
120}
121
122const NX_HUNT_TARGET_BYTES: nx_size = 56
123
124// ===== Validators ================================================
125
126func nx_hk_is_valid(k: nx_int) -> nx_int {
127 if k < 0 { return 0 }
128 if k >= NX_HK_N_KINDS { return 0 }
129 return 1
130}
131
132func nx_hs_is_valid(s: nx_int) -> nx_int {
133 if s < 0 { return 0 }
134 if s >= NX_HS_N_STATES { return 0 }
135 return 1
136}
137
138func nx_kp_is_valid(p: nx_int) -> nx_int {
139 if p < 0 { return 0 }
140 if p >= NX_KP_N_PILLARS { return 0 }
141 return 1
142}
143
144// ===== nx_hunter_new ==============================================
145
146func nx_hunter_new(capacity: nx_size) -> *NxHunter {
147 let h: *NxHunter = (sys_mmap(32)) as *NxHunter
148 let bytes: nx_size = capacity * NX_HUNT_TARGET_BYTES
149 h.targets = (sys_mmap(bytes)) as *NxHuntTarget
150 h.capacity = capacity
151 h.head = 0
152 h.count = 0
153 return h
154}
155
156// ===== _hunter_at =================================================
157
158func _hunter_at(h: *NxHunter, idx: nx_size) -> *NxHuntTarget {
159 return (h.targets as i64 + (idx as i64) * NX_HUNT_TARGET_BYTES) as *NxHuntTarget
160}
161
162// ===== _hunter_find ===============================================
163
164func _hunter_find(h: *NxHunter, target_id: nx_int) -> nx_int {
165 var i: nx_size = 0
166 while i < h.count {
167 let t: *NxHuntTarget = _hunter_at(h, i)
168 if t.target_id == target_id { return i as i64 }
169 i = i + 1
170 }
171 return -1
172}
173
174// ===== nx_hunter_scout ============================================
175//
176// Identify a new target. Status begins SCOUTED. Returns the index
177// or -1 if full / bad kind / duplicate.
178
179func nx_hunter_scout(h: *NxHunter,
180 target_id: nx_int,
181 kind: nx_int,
182 content_hash: nx_size,
183 now_us: nx_size) -> nx_int {
184 if nx_hk_is_valid(kind) == 0 { return -1 }
185 if _hunter_find(h, target_id) >= 0 { return -1 }
186 if h.count >= h.capacity { return -1 }
187 let t: *NxHuntTarget = _hunter_at(h, h.head)
188 t.target_id = target_id
189 t.kind = kind
190 t.status = NX_HS_SCOUTED
191 t.kill_pillar = NX_KP_NONE
192 t.content_hash = content_hash
193 t.scouted_us = now_us
194 t.last_event_us = now_us
195 h.head = h.head + 1
196 if h.head >= h.capacity { h.head = 0 }
197 h.count = h.count + 1
198 return (h.count - 1) as i64
199}
200
201// ===== nx_hunter_engage ===========================================
202//
203// Transition SCOUTED -> ACTIVE. Allowed only from SCOUTED.
204
205func nx_hunter_engage(h: *NxHunter, target_id: nx_int, now_us: nx_size) -> nx_int {
206 let idx: nx_int = _hunter_find(h, target_id)
207 if idx < 0 { return NX_HUNTER_ERR_NOT_FOUND }
208 let t: *NxHuntTarget = _hunter_at(h, idx as nx_size)
209 if t.status != NX_HS_SCOUTED { return NX_HUNTER_ERR_BAD_TRANSITION }
210 t.status = NX_HS_ACTIVE
211 t.last_event_us = now_us
212 return NX_HUNTER_OK
213}
214
215// ===== nx_hunter_kill ============================================
216//
217// Record a kill. Allowed from ACTIVE or SCOUTED (sometimes a target
218// is found-and-killed in same beat, like a regression test catching
219// a bug as it's being introduced). pillar must be one of NX_KP_*.
220
221func nx_hunter_kill(h: *NxHunter,
222 target_id: nx_int,
223 pillar: nx_int,
224 now_us: nx_size) -> nx_int {
225 if nx_kp_is_valid(pillar) == 0 { return NX_HUNTER_ERR_BAD_PILLAR }
226 if pillar == NX_KP_NONE { return NX_HUNTER_ERR_BAD_PILLAR }
227 let idx: nx_int = _hunter_find(h, target_id)
228 if idx < 0 { return NX_HUNTER_ERR_NOT_FOUND }
229 let t: *NxHuntTarget = _hunter_at(h, idx as nx_size)
230 if t.status == NX_HS_KILLED { return NX_HUNTER_ERR_BAD_TRANSITION }
231 if t.status == NX_HS_ARCHIVED { return NX_HUNTER_ERR_BAD_TRANSITION }
232 t.status = NX_HS_KILLED
233 t.kill_pillar = pillar
234 t.last_event_us = now_us
235 return NX_HUNTER_OK
236}
237
238// ===== nx_hunter_archive =========================================
239//
240// Close a hunt without a kill. The learning is preserved but the
241// target is no longer actively pursued. Allowed from any pre-
242// archived status.
243
244func nx_hunter_archive(h: *NxHunter, target_id: nx_int, now_us: nx_size) -> nx_int {
245 let idx: nx_int = _hunter_find(h, target_id)
246 if idx < 0 { return NX_HUNTER_ERR_NOT_FOUND }
247 let t: *NxHuntTarget = _hunter_at(h, idx as nx_size)
248 if t.status == NX_HS_ARCHIVED { return NX_HUNTER_OK }
249 t.status = NX_HS_ARCHIVED
250 t.last_event_us = now_us
251 return NX_HUNTER_OK
252}
253
254// ===== Queries ===================================================
255
256func nx_hunter_count_by_status(h: *NxHunter, status: nx_int) -> nx_int {
257 var hits: nx_int = 0
258 var i: nx_size = 0
259 while i < h.count {
260 let t: *NxHuntTarget = _hunter_at(h, i)
261 if t.status == status { hits = hits + 1 }
262 i = i + 1
263 }
264 return hits
265}
266
267func nx_hunter_count_kills_by_pillar(h: *NxHunter, pillar: nx_int) -> nx_int {
268 var hits: nx_int = 0
269 var i: nx_size = 0
270 while i < h.count {
271 let t: *NxHuntTarget = _hunter_at(h, i)
272 if t.status == NX_HS_KILLED {
273 if t.kill_pillar == pillar { hits = hits + 1 }
274 }
275 i = i + 1
276 }
277 return hits
278}
279
280func nx_hunter_total_kills(h: *NxHunter) -> nx_int {
281 return nx_hunter_count_by_status(h, NX_HS_KILLED)
282}
283
284func nx_hunter_count(h: *NxHunter) -> nx_size {
285 return h.count
286}
287
288// ===== nx_hunter_session_emitted =================================
289//
290// Predicate: did at least one hunt event occur in this hunter?
291// True if any target has been scouted (count > 0). Used by the
292// session-audit gate to confirm hunt_count >= 1.
293
294func nx_hunter_session_emitted(h: *NxHunter) -> nx_int {
295 if h.count > 0 { return 1 }
296 return 0
297}