nx_hunt_journal.nx source
↩ module page · 105 lines · 3392 B
1// nx_hunt_journal.nx -- append-only event ring for hunt events.
2//
3// Per [[feedback-hunter-gatherer-meta-primitives-outward-inward-axes]]
4// CARDINAL: "nx_hunt_journal (~/.nishi/hunt.jsonl)." Append-only log
5// of every hunt event (scout / engage / kill / archive) so the
6// session-audit gate can verify hunt_count >= 1 per session, and
7// imbalance flagger can detect 5x ratio for 3 consecutive sessions.
8//
9// V1 ships in-memory ring; on-disk JSONL persistence is queued (matches
10// nx_evict_journal pattern; both write to ~/.nishi/<name>.jsonl in V2).
11//
12// Composes:
13// nx_hunter -- emits events; journal records them
14// nx_gather_journal -- the inward-axis dual; session-audit pairs
15// both for valid-session predicate
16// nx_provenance_chain -- hunt-kill events are transforms; chain-logged
17
18import "nx_syscalls.nx"
19import "nx_tier.nx"
20import "nx_hunter.nx"
21
22// ===== Sealed enum: NxHuntEventKind ===============================
23
24const NX_HEV_SCOUTED: nx_int = 0
25const NX_HEV_ENGAGED: nx_int = 1
26const NX_HEV_KILLED: nx_int = 2
27const NX_HEV_ARCHIVED: nx_int = 3
28const NX_HEV_N_KINDS: nx_int = 4
29
30// ===== Struct: NxHuntEvent ========================================
31
32struct NxHuntEvent {
33 ts_us: nx_size,
34 target_id: nx_int,
35 event_kind: nx_int,
36 target_kind: nx_int, // NX_HK_* at moment of event
37 kill_pillar: nx_int, // NX_KP_NONE except on KILLED events
38}
39
40// ===== Struct: NxHuntJournalRing ==================================
41
42struct NxHuntJournalRing {
43 events: *NxHuntEvent,
44 capacity: nx_size,
45 head: nx_size,
46 count: nx_size,
47}
48
49const NX_HEV_BYTES: nx_size = 40
50
51func nx_hev_kind_is_valid(k: nx_int) -> nx_int {
52 if k < 0 { return 0 }
53 if k >= NX_HEV_N_KINDS { return 0 }
54 return 1
55}
56
57func nx_hunt_journal_new(capacity: nx_size) -> *NxHuntJournalRing {
58 let j: *NxHuntJournalRing = (sys_mmap(32)) as *NxHuntJournalRing
59 let bytes: nx_size = capacity * NX_HEV_BYTES
60 j.events = (sys_mmap(bytes)) as *NxHuntEvent
61 j.capacity = capacity
62 j.head = 0
63 j.count = 0
64 return j
65}
66
67func _hunt_jr_at(j: *NxHuntJournalRing, idx: nx_size) -> *NxHuntEvent {
68 return (j.events as i64 + (idx as i64) * NX_HEV_BYTES) as *NxHuntEvent
69}
70
71func nx_hunt_journal_log(j: *NxHuntJournalRing,
72 ts_us: nx_size,
73 target_id: nx_int,
74 event_kind: nx_int,
75 target_kind: nx_int,
76 kill_pillar: nx_int) -> nx_int {
77 if nx_hev_kind_is_valid(event_kind) == 0 { return 1 }
78 let e: *NxHuntEvent = _hunt_jr_at(j, j.head)
79 e.ts_us = ts_us
80 e.target_id = target_id
81 e.event_kind = event_kind
82 e.target_kind = target_kind
83 e.kill_pillar = kill_pillar
84 j.head = j.head + 1
85 if j.head >= j.capacity { j.head = 0 }
86 j.count = j.count + 1
87 return 0
88}
89
90func nx_hunt_journal_count_kind(j: *NxHuntJournalRing, kind: nx_int) -> nx_int {
91 var hits: nx_int = 0
92 var live: nx_size = j.count
93 if live > j.capacity { live = j.capacity }
94 var i: nx_size = 0
95 while i < live {
96 let e: *NxHuntEvent = _hunt_jr_at(j, i)
97 if e.event_kind == kind { hits = hits + 1 }
98 i = i + 1
99 }
100 return hits
101}
102
103func nx_hunt_journal_event_count(j: *NxHuntJournalRing) -> nx_size {
104 return j.count
105}