code wiki / (root) / nx_gather_journal.nx

nx_gather_journal.nx source

↩ module page · 154 lines · 5310 B

1// nx_gather_journal.nx -- append-only event ring for gather events. 2// 3// Per [[feedback-hunter-gatherer-meta-primitives-outward-inward-axes]] 4// CARDINAL: "nx_gather_journal (~/.nishi/gather.jsonl)." Append-only 5// log of every gather event (field sown / yield harvested) so the 6// session-audit gate can verify gather_count >= 1 per session. 7// 8// Mirrors nx_hunt_journal structure; pairs with it for the dual-axis 9// audit predicate (valid session = hunt_count >= 1 AND gather_count >= 1). 10// 11// Composes: 12// nx_gatherer -- emits events; journal records them 13// nx_hunt_journal -- the outward-axis dual 14 15import "nx_syscalls.nx" 16import "nx_tier.nx" 17import "nx_gatherer.nx" 18import "nx_hunt_journal.nx" 19const NX_MAGIC_1024: i64 = 1024 20 21// ===== Sealed enum: NxGatherEventKind ============================= 22 23const NX_GEV_SOWN: nx_int = 0 24const NX_GEV_HARVESTED: nx_int = 1 25const NX_GEV_FALLOWED: nx_int = 2 // field set aside for season 26const NX_GEV_COMPOSTED: nx_int = 3 // prior-arc knowledge re-applied 27const NX_GEV_N_KINDS: nx_int = 4 28 29// ===== Struct: NxGatherEvent ====================================== 30 31struct NxGatherEvent { 32 ts_us: nx_size, 33 field_id: nx_int, 34 event_kind: nx_int, 35 field_kind: nx_int, // NX_GFK_* at moment of event 36 yield_kind: nx_int, // NX_GYK_* on HARVESTED events; NX_GYK_N_KINDS otherwise 37 yield_value: nx_size, 38} 39 40// ===== Struct: NxGatherJournalRing ================================ 41 42struct NxGatherJournalRing { 43 events: *NxGatherEvent, 44 capacity: nx_size, 45 head: nx_size, 46 count: nx_size, 47} 48 49const NX_GEV_BYTES: nx_size = 48 50 51func nx_gev_kind_is_valid(k: nx_int) -> nx_int { 52 if k < 0 { return 0 } 53 if k >= NX_GEV_N_KINDS { return 0 } 54 return 1 55} 56 57func nx_gather_journal_new(capacity: nx_size) -> *NxGatherJournalRing { 58 let j: *NxGatherJournalRing = (sys_mmap(32)) as *NxGatherJournalRing 59 let bytes: nx_size = capacity * NX_GEV_BYTES 60 j.events = (sys_mmap(bytes)) as *NxGatherEvent 61 j.capacity = capacity 62 j.head = 0 63 j.count = 0 64 return j 65} 66 67func _gather_jr_at(j: *NxGatherJournalRing, idx: nx_size) -> *NxGatherEvent { 68 return (j.events as i64 + (idx as i64) * NX_GEV_BYTES) as *NxGatherEvent 69} 70 71func nx_gather_journal_log(j: *NxGatherJournalRing, 72 ts_us: nx_size, 73 field_id: nx_int, 74 event_kind: nx_int, 75 field_kind: nx_int, 76 yield_kind: nx_int, 77 yield_value: nx_size) -> nx_int { 78 if nx_gev_kind_is_valid(event_kind) == 0 { return 1 } 79 let e: *NxGatherEvent = _gather_jr_at(j, j.head) 80 e.ts_us = ts_us 81 e.field_id = field_id 82 e.event_kind = event_kind 83 e.field_kind = field_kind 84 e.yield_kind = yield_kind 85 e.yield_value = yield_value 86 j.head = j.head + 1 87 if j.head >= j.capacity { j.head = 0 } 88 j.count = j.count + 1 89 return 0 90} 91 92func nx_gather_journal_count_kind(j: *NxGatherJournalRing, kind: nx_int) -> nx_int { 93 var hits: nx_int = 0 94 var live: nx_size = j.count 95 if live > j.capacity { live = j.capacity } 96 var i: nx_size = 0 97 while i < live { 98 let e: *NxGatherEvent = _gather_jr_at(j, i) 99 if e.event_kind == kind { hits = hits + 1 } 100 i = i + 1 101 } 102 return hits 103} 104 105func nx_gather_journal_event_count(j: *NxGatherJournalRing) -> nx_size { 106 return j.count 107} 108 109// ===== nx_session_audit_valid ==================================== 110// 111// THE SESSION-AUDIT GATE per cardinal: "bench/nx_session_audit.sh 112// asserts hunt_count >= 1 AND gather_count >= 1 per session." 113// Returns 1 if both journals have at least one event each, 0 otherwise. 114// Imported from the gather_journal side because both journals must be 115// passed; pair-validation lives here arbitrarily (could equally live 116// in hunt_journal). 117 118func nx_session_audit_valid(hunt: *NxHuntJournalRing, 119 gather: *NxGatherJournalRing) -> nx_int { 120 if (hunt as i64) == 0 { return 0 } 121 if (gather as i64) == 0 { return 0 } 122 if hunt.count == 0 { return 0 } 123 if gather.count == 0 { return 0 } 124 return 1 125} 126 127// ===== nx_session_imbalance_q10 ================================== 128// 129// Returns Q10 ratio of hunt/(hunt+gather). 512 = balanced, 0 = all 130// gather, 1024 = all hunt. The cardinal flags imbalance at 5x ratio 131// for 3 consecutive sessions; the 5x equivalent is Q10 ratio outside 132// [171, 853] (1:5 or 5:1). 133 134func nx_session_imbalance_q10(hunt: *NxHuntJournalRing, 135 gather: *NxGatherJournalRing) -> nx_int { 136 let h: nx_size = hunt.count 137 let g: nx_size = gather.count 138 let total: nx_size = h + g 139 if total == 0 { return 512 } 140 return ((h as i64) * NX_MAGIC_1024) / (total as i64) 141} 142 143// ===== nx_session_is_imbalanced =================================== 144// 145// Predicate: is this session outside the 5x ratio band? 146// q10 < 171 means gather >> hunt; q10 > 853 means hunt >> gather. 147 148func nx_session_is_imbalanced(hunt: *NxHuntJournalRing, 149 gather: *NxGatherJournalRing) -> nx_int { 150 let q10: nx_int = nx_session_imbalance_q10(hunt, gather) 151 if q10 < 171 { return 1 } 152 if q10 > 853 { return 1 } 153 return 0 154}