code wiki / _hdl_build / nx_claims.nx
nx_claims.nx source
↩ module page · 866 lines · 37392 B
1// nx_claims.nx -- SESSION-CLAIMS AS AN ECOSYSTEM CAPABILITY (ws=ws-intel2, 2026-07-20).
2// Eats debt 1784408445 (claims protocol shell-only via WSL) + feeds 1784408708 (claims hub
3// to NAS = standup claim-holder liveness): claim/beat/release/walk become MCP tools/call
4// verbs on an append-only NAS journal -- peers of nx_ws_sync (journal) / nx_ws_cycle (phase).
5// HOLDERSHIP IS DERIVED, NEVER STORED (evidence law): latest CLAIM not RELEASEd by its
6// holder = the claim; freshness = latest CLAIM/BEAT ts by that holder vs its TTL ->
7// HELD | EXPIRED (crash-expiry BY CONSTRUCTION -- a dead session's claim self-expires,
8// the F214 lease semantics) | FREE (released) | ABSENT. Claim on a contended resource is
9// REFUSED fail-closed (exit 3) -- never silently stolen; an EXPIRED claim is claimable.
10// Frame: <ts>\t<VERB>\t<resource>\t<actor>\t<ttl>\t<note> VERB in {CLAIM, BEAT, RELEASE}
11// SCALE LAW: windowed reads + declared envelope (window_bytes/truncated/claims_capped).
12// license_tier: ORIGINAL No hw writes (Rule 26).
13// claim <journal> <resource> <actor> <ttl_sec> <note> -> HELD | CONTENDED(3)
14// beat <journal> <resource> <actor> <note> -> BEAT | NOT-HOLDER(3)
15// release <journal> <resource> <actor> <note> -> RELEASED | NOT-HOLDER(3)
16// state <journal> <resource> -> ONE-LINE probe: state=HELD|EXPIRED|FREE|ABSENT holder=..
17// (seq543/seq552: lets nx_seat checkout classify NOT-HOLDER honestly; exit 0 always)
18// walk <journal> -> JSON all resources
19// reap <journal> <reaper> <note> -> F214: release ONLY provably-EXPIRED claims
20// (age>ttl by the claim's OWN ttl field) + ORPHAN-REAPED audit note; idempotent; exit 0
21// selftest <scratch-journal> -> gate T1..T8 (caller pre-cleans)
22import "nx_syscalls.nx"
23import "nx_gate_verdict.nx"
24const CL_MAGIC_4096: i64 = 4096
25const CL_MAGIC_2000: i64 = 2000
26const CL_MAGIC_200000: i64 = 200000
27const CL_MAGIC_5000: i64 = 5000
28
29const CL_WIN: i64 = 4194304
30const CL_OUT: i64 = 262144
31const CL_SOFT: i64 = 49152
32const CL_NOTE_MAX: i64 = 200
33const CL_NAME_MAX: i64 = 120
34// THE LEASE CEILING (2026-08-09). MEASURED: `walk` showed held=2 expired=0, BOTH held by one dead seat
35// claude-76b93d3f at ttl_sec=7200000 (83.3 DAYS) against age 16.4 days -- so age < ttl, cl_state derived
36// HELD, and cl_reap (which acts ONLY on st==2) skipped them BY CONSTRUCTION. Two lanes were locked until
37// ~2026-10-14. Proven corpus_complete=1 over 22,811 files: the ONLY ttl validation in the estate was
38// `if ttl <= 0`, with NO UPPER BOUND ANYWHERE. Live journal TTLs are 3600/5400/7200/10800/14400/21600/
39// 86400; 7200000 is exactly 1000x the modal 7200 -- a typo nothing said no to.
40// ***HOLDERSHIP IS DERIVED SO THE HOLDER CANNOT ASSERT ITS OWN LIVENESS -- BUT THE TTL IT IS DIVIDED BY
41// WAS A NUMBER THE HOLDER CHOSE, SO THE HOLDER DEFINED ITS OWN DEATH. A LEASE WHOSE EXPIRY THE LESSEE
42// SETS IS NOT A LEASE.*** The 2026 distributed-systems rule is the same: never trust a process to know
43// that its own lock has expired.
44// 86400 MATCHES THE INCUMBENT rather than inventing a number: nx_no_cookie_session.nx (`if ttl_s > 86400
45// -> BAD_INPUT`) and nx_modern_auth_flow.nx (NX_MAUTH_HARD_MAX_TTL_S = 86400) already bound session TTL
46// at 24h, and 86400 is also the largest legitimate TTL observed in the live journal.
47const CL_TTL_MAX: i64 = 86400
48
49func cl_cat(d: *u8, o: i64, s: *u8) -> i64 { var p: i64=o; var i: i64=0; while s[i]!=(0 as u8){ d[p]=s[i]; p=p+1; i=i+1 } return p }
50func cl_catn(d: *u8, o: i64, v: i64) -> i64 {
51 let t: *u8 = sys_mmap(28)
52 var m: i64 = v
53 var p: i64 = o
54 if m < 0 { d[p]=45 as u8; p=p+1; m=0-m }
55 var k: i64 = 0
56 if m == 0 { t[0]=48 as u8; k=1 }
57 while m > 0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
58 var i: i64 = 0
59 while i < k { d[p]=t[k-1-i]; p=p+1; i=i+1 }
60 return p
61}
62func cl_vlen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
63func cl_atoi_z(s: *u8) -> i64 { var v: i64=0; var i: i64=0; while s[i]!=(0 as u8){ let c: i64=s[i] as i64; if c>=48 { if c<=57 { v=v*10+(c-48) } } i=i+1 } return v }
64func cl_read(path: *u8, buf: *u8, cap: i64) -> i64 {
65 let fd: i64 = sys_openat_rd(path)
66 if fd < 0 { return 0 }
67 var n: i64 = 0
68 var go: i64 = 1
69 while go == 1 { let r: i64 = sys_read(fd, ((buf as i64)+n) as *u8, cap-n); if r <= 0 { go = 0 } else { n = n + r } if n >= cap { go = 0 } }
70 sys_close(fd)
71 return n
72}
73// append one frame: <ts>\t<verb>\t<resource>\t<actor>\t<ttl>\t<note>\n (ts<0 => real clock; note flattened)
74func cl_append(journal: *u8, ts: i64, verb: *u8, res: *u8, actor: *u8, ttl: i64, note: *u8) -> i64 {
75 var t: i64 = ts
76 if t < 0 { t = sys_now_realtime_sec() }
77 let ln: *u8 = sys_mmap(CL_MAGIC_4096)
78 var o: i64 = 0
79 o = cl_catn(ln, o, t)
80 ln[o]=9 as u8; o=o+1
81 o = cl_cat(ln, o, verb)
82 ln[o]=9 as u8; o=o+1
83 o = cl_cat(ln, o, res)
84 ln[o]=9 as u8; o=o+1
85 o = cl_cat(ln, o, actor)
86 ln[o]=9 as u8; o=o+1
87 o = cl_catn(ln, o, ttl)
88 ln[o]=9 as u8; o=o+1
89 var i: i64 = 0
90 var used: i64 = 0
91 while note[i]!=(0 as u8) {
92 if used < CL_MAGIC_2000 {
93 let c: i64 = note[i] as i64
94 if c==9 { ln[o]=32 as u8; o=o+1 } else { if c==10 { ln[o]=32 as u8; o=o+1 } else { if c==13 { ln[o]=32 as u8; o=o+1 } else { ln[o]=c as u8; o=o+1 } } }
95 used = used + 1
96 }
97 i = i + 1
98 }
99 ln[o]=10 as u8; o=o+1
100 let fd: i64 = sys_openat_append(journal, 0x1a4)
101 if fd < 0 { return -1 }
102 sys_write(fd, ln, o)
103 sys_close(fd)
104 return 0
105}
106func cl_le(q: *u8, i: i64, n: i64) -> i64 {
107 var le: i64 = i
108 var s: i64 = 1
109 while s==1 { if le>=n { s=0 } else { if q[le]==(10 as u8){s=0} else {le=le+1} } }
110 return le
111}
112func cl_col(q: *u8, ls: i64, le: i64, c: i64, out: *i64) -> i64 {
113 var col: i64 = 0
114 var p: i64 = ls
115 while col < c {
116 var s: i64 = 1
117 while s == 1 { if p >= le { return 0 } if q[p]==(9 as u8) { s = 0 } else { p = p+1 } }
118 p = p + 1
119 col = col + 1
120 }
121 var e: i64 = p
122 var s2: i64 = 1
123 while s2 == 1 { if e >= le { s2 = 0 } else { if q[e]==(9 as u8) { s2 = 0 } else { e = e+1 } } }
124 out[0] = p
125 out[1] = e
126 return 1
127}
128func cl_lit_eq(q: *u8, s: i64, e: i64, lit: *u8) -> i64 {
129 var i: i64 = 0
130 while s+i < e { if lit[i]==(0 as u8) { return 0 } if q[s+i]!=lit[i] { return 0 } i=i+1 }
131 if lit[i]!=(0 as u8) { return 0 }
132 return 1
133}
134func cl_span_eq(q: *u8, s1: i64, e1: i64, s2: i64, e2: i64) -> i64 {
135 if e1-s1 != e2-s2 { return 0 }
136 var i: i64 = 0
137 while s1+i < e1 { if q[s1+i]!=q[s2+i] { return 0 } i=i+1 }
138 return 1
139}
140func cl_atoi(q: *u8, s: i64, e: i64) -> i64 {
141 var v: i64 = 0
142 var i: i64 = s
143 while i < e { let c: i64 = q[i] as i64; if c>=48 { if c<=57 { v = v*10 + (c-48) } } i=i+1 }
144 return v
145}
146// DERIVED claim state for a resource span. Returns -1 ABSENT / 1 HELD / 2 EXPIRED / 3 FREE.
147// out[0..1]=holder span, out[2]=ttl, out[3]=last_activity_ts (valid when state 1|2).
148func cl_state(q: *u8, cx: *i64, res_s: i64, res_e: i64, out: *i64) -> i64 {
149 let n: i64 = cx[0]
150 let cv: *i64 = sys_mmap(16) as *i64
151 let cr: *i64 = sys_mmap(16) as *i64
152 let ca: *i64 = sys_mmap(16) as *i64
153 let ct: *i64 = sys_mmap(16) as *i64
154 let c0: *i64 = sys_mmap(16) as *i64
155 var mode: i64 = 0
156 var ever: i64 = 0
157 var hs: i64 = -1
158 var he: i64 = -1
159 var ttl: i64 = 0
160 var last: i64 = 0
161 var i: i64 = 0
162 while i < n {
163 let le: i64 = cl_le(q,i,n)
164 if cl_col(q,i,le,2,cr)==1 { if cl_span_eq(q,cr[0],cr[1],res_s,res_e)==1 {
165 if cl_col(q,i,le,1,cv)==1 { if cl_col(q,i,le,0,c0)==1 { if cl_col(q,i,le,3,ca)==1 {
166 let ts: i64 = cl_atoi(q,c0[0],c0[1])
167 if cl_lit_eq(q,cv[0],cv[1],"CLAIM" as *u8)==1 {
168 mode=1
169 ever=1
170 hs=ca[0]
171 he=ca[1]
172 last=ts
173 if cl_col(q,i,le,4,ct)==1 { ttl = cl_atoi(q,ct[0],ct[1]) }
174 }
175 if cl_lit_eq(q,cv[0],cv[1],"BEAT" as *u8)==1 {
176 if mode==1 { if cl_span_eq(q,ca[0],ca[1],hs,he)==1 { last=ts } }
177 }
178 if cl_lit_eq(q,cv[0],cv[1],"RELEASE" as *u8)==1 {
179 if mode==1 { if cl_span_eq(q,ca[0],ca[1],hs,he)==1 { mode=0 } }
180 }
181 } } }
182 } }
183 i = le + 1
184 }
185 if ever==0 { return -1 }
186 if mode==0 { return 3 }
187 out[0]=hs
188 out[1]=he
189 out[2]=ttl
190 out[3]=last
191 let age: i64 = cx[1] - last
192 // THE GUARD AND ITS PAIRED REPAIR IN ONE PLACE. Bounding only the CLAIM path would fix future claims
193 // and leave the two already-written immortals locked forever -- and the estate's own law is that a
194 // guard shipped without its paired repair is a permanent outage of the thing it guards. Capping HERE,
195 // in the single derivation every consumer (walk/state/beat/release/reap) already shares, retroactively
196 // corrects out-of-policy rows WITHOUT rewriting the append-only journal (additive-only holds).
197 // FAIL DIRECTION: this can only ever make a lease expire SOONER. It cannot manufacture a false HELD.
198 // out[2] deliberately still reports the RECORDED ttl, so `walk` shows state=EXPIRED beside
199 // ttl_sec=7200000 and the original defect stays VISIBLE rather than being silently normalised.
200 var eff: i64 = ttl
201 if eff > CL_TTL_MAX { eff = CL_TTL_MAX }
202 if age > eff { return 2 }
203 return 1
204}
205func cl_state_name(st: i64) -> *u8 {
206 if st==1 { return "HELD" as *u8 }
207 if st==2 { return "EXPIRED" as *u8 }
208 if st==3 { return "FREE" as *u8 }
209 return "ABSENT" as *u8
210}
211// resource span first occurrence? (walk dedupe)
212func cl_res_seen(q: *u8, upto: i64, r_s: i64, r_e: i64) -> i64 {
213 let cr: *i64 = sys_mmap(16) as *i64
214 var i: i64 = 0
215 while i < upto {
216 let le: i64 = cl_le(q,i,upto)
217 if cl_col(q,i,le,2,cr)==1 { if cl_span_eq(q,cr[0],cr[1],r_s,r_e)==1 { return 1 } }
218 i = le + 1
219 }
220 return 0
221}
222// find span of a resource by literal name; 1 if any frame carries it
223func cl_res_span_lit(q: *u8, cx: *i64, res: *u8, out: *i64) -> i64 {
224 let n: i64 = cx[0]
225 let cr: *i64 = sys_mmap(16) as *i64
226 var i: i64 = 0
227 while i < n {
228 let le: i64 = cl_le(q,i,n)
229 if cl_col(q,i,le,2,cr)==1 { if cl_lit_eq(q,cr[0],cr[1],res)==1 { out[0]=cr[0]; out[1]=cr[1]; return 1 } }
230 i = le + 1
231 }
232 return 0
233}
234// last note (col5) for a resource
235func cl_note_span(q: *u8, cx: *i64, res_s: i64, res_e: i64, out: *i64) -> i64 {
236 let n: i64 = cx[0]
237 let cr: *i64 = sys_mmap(16) as *i64
238 let cn: *i64 = sys_mmap(16) as *i64
239 var ns: i64 = -1
240 var ne: i64 = -1
241 var i: i64 = 0
242 while i < n {
243 let le: i64 = cl_le(q,i,n)
244 if cl_col(q,i,le,2,cr)==1 { if cl_span_eq(q,cr[0],cr[1],res_s,res_e)==1 {
245 if cl_col(q,i,le,5,cn)==1 { ns=cn[0]; ne=cn[1] }
246 } }
247 i = le + 1
248 }
249 if ns >= 0 { out[0]=ns; out[1]=ne; return 1 }
250 return 0
251}
252func cl_jesc(d: *u8, o: i64, q: *u8, s: i64, e: i64, maxb: i64) -> i64 {
253 var p: i64 = o
254 var i: i64 = s
255 var used: i64 = 0
256 while i < e {
257 if used >= maxb { i = e } else {
258 let c: i64 = q[i] as i64
259 if c == 34 { d[p]=92 as u8; p=p+1; d[p]=34 as u8; p=p+1 } else { if c == 92 { d[p]=92 as u8; p=p+1; d[p]=92 as u8; p=p+1 } else { if c < 32 { d[p]=32 as u8; p=p+1 } else { d[p]=c as u8; p=p+1 } } }
260 used = used + 2
261 i = i + 1
262 }
263 }
264 return p
265}
266func cl_key(d: *u8, o: i64, name: *u8) -> i64 {
267 d[o]=34 as u8
268 var p: i64 = o + 1
269 p = cl_cat(d,p,name)
270 d[p]=34 as u8
271 p = p + 1
272 d[p]=58 as u8
273 p = p + 1
274 return p
275}
276func cl_qlit(d: *u8, o: i64, s: *u8) -> i64 {
277 var p: i64 = o
278 d[p]=34 as u8; p=p+1
279 p = cl_cat(d,p,s)
280 d[p]=34 as u8; p=p+1
281 return p
282}
283func cl_emit_res(d: *u8, o: i64, q: *u8, cx: *i64, r_s: i64, r_e: i64) -> i64 {
284 var p: i64 = o
285 let st_out: *i64 = sys_mmap(32) as *i64
286 let no: *i64 = sys_mmap(16) as *i64
287 d[p]=123 as u8; p=p+1
288 p = cl_key(d,p,"resource" as *u8)
289 d[p]=34 as u8; p=p+1
290 p = cl_jesc(d,p,q,r_s,r_e,CL_NAME_MAX)
291 d[p]=34 as u8; p=p+1
292 d[p]=44 as u8; p=p+1
293 let st: i64 = cl_state(q,cx,r_s,r_e,st_out)
294 p = cl_key(d,p,"state" as *u8)
295 let sn: *u8 = cl_state_name(st)
296 p = cl_qlit(d,p,sn)
297 d[p]=44 as u8; p=p+1
298 p = cl_key(d,p,"holder" as *u8)
299 d[p]=34 as u8; p=p+1
300 if st==1 { p = cl_jesc(d,p,q,st_out[0],st_out[1],CL_NAME_MAX) }
301 if st==2 { p = cl_jesc(d,p,q,st_out[0],st_out[1],CL_NAME_MAX) }
302 d[p]=34 as u8; p=p+1
303 d[p]=44 as u8; p=p+1
304 p = cl_key(d,p,"ttl_sec" as *u8)
305 var tv: i64 = -1
306 if st==1 { tv = st_out[2] }
307 if st==2 { tv = st_out[2] }
308 p = cl_catn(d,p,tv)
309 d[p]=44 as u8; p=p+1
310 p = cl_key(d,p,"age_sec" as *u8)
311 var age: i64 = -1
312 if st==1 { age = cx[1] - st_out[3] }
313 if st==2 { age = cx[1] - st_out[3] }
314 p = cl_catn(d,p,age)
315 d[p]=44 as u8; p=p+1
316 p = cl_key(d,p,"last_note" as *u8)
317 d[p]=34 as u8; p=p+1
318 let hn: i64 = cl_note_span(q,cx,r_s,r_e,no)
319 if hn==1 { p = cl_jesc(d,p,q,no[0],no[1],CL_NOTE_MAX) }
320 d[p]=34 as u8; p=p+1
321 d[p]=125 as u8; p=p+1
322 return p
323}
324func cl_walk_json(q: *u8, cx: *i64, d: *u8) -> i64 {
325 let n: i64 = cx[0]
326 var p: i64 = 0
327 d[p]=123 as u8; p=p+1
328 p = cl_key(d,p,"v" as *u8)
329 p = cl_catn(d,p,1)
330 d[p]=44 as u8; p=p+1
331 p = cl_key(d,p,"tool" as *u8)
332 p = cl_qlit(d,p,"nx_claims" as *u8)
333 d[p]=44 as u8; p=p+1
334 p = cl_key(d,p,"journal" as *u8)
335 d[p]=34 as u8; p=p+1
336 let jp: *u8 = cx[5] as *u8
337 let jl: i64 = cl_vlen(jp)
338 p = cl_jesc(d,p,jp,0,jl,200)
339 d[p]=34 as u8; p=p+1
340 d[p]=44 as u8; p=p+1
341 p = cl_key(d,p,"journal_bytes" as *u8)
342 p = cl_catn(d,p,n)
343 d[p]=44 as u8; p=p+1
344 p = cl_key(d,p,"window_bytes" as *u8)
345 p = cl_catn(d,p,CL_WIN)
346 d[p]=44 as u8; p=p+1
347 p = cl_key(d,p,"truncated" as *u8)
348 p = cl_catn(d,p,cx[4])
349 d[p]=44 as u8; p=p+1
350 p = cl_key(d,p,"now" as *u8)
351 p = cl_catn(d,p,cx[1])
352 d[p]=44 as u8; p=p+1
353 // totals pass
354 let cr: *i64 = sys_mmap(16) as *i64
355 let st_out: *i64 = sys_mmap(32) as *i64
356 var theld: i64 = 0
357 var texp: i64 = 0
358 var tfree: i64 = 0
359 var i: i64 = 0
360 while i < n {
361 let le: i64 = cl_le(q,i,n)
362 if cl_col(q,i,le,2,cr)==1 { if cl_res_seen(q,i,cr[0],cr[1])==0 {
363 let st: i64 = cl_state(q,cx,cr[0],cr[1],st_out)
364 if st==1 { theld=theld+1 }
365 if st==2 { texp=texp+1 }
366 if st==3 { tfree=tfree+1 }
367 } }
368 i = le + 1
369 }
370 p = cl_key(d,p,"totals" as *u8)
371 d[p]=123 as u8; p=p+1
372 p = cl_key(d,p,"held" as *u8)
373 p = cl_catn(d,p,theld)
374 d[p]=44 as u8; p=p+1
375 p = cl_key(d,p,"expired" as *u8)
376 p = cl_catn(d,p,texp)
377 d[p]=44 as u8; p=p+1
378 p = cl_key(d,p,"free" as *u8)
379 p = cl_catn(d,p,tfree)
380 d[p]=125 as u8; p=p+1
381 d[p]=44 as u8; p=p+1
382 p = cl_key(d,p,"resources" as *u8)
383 d[p]=91 as u8; p=p+1
384 var capped: i64 = 0
385 var first: i64 = 1
386 var j: i64 = 0
387 while j < n {
388 let je: i64 = cl_le(q,j,n)
389 if cl_col(q,j,je,2,cr)==1 { if cl_res_seen(q,j,cr[0],cr[1])==0 {
390 if p > CL_SOFT { capped=1 } else {
391 if first==0 { d[p]=44 as u8; p=p+1 }
392 first=0
393 p = cl_emit_res(d,p,q,cx,cr[0],cr[1])
394 }
395 } }
396 j = je + 1
397 }
398 d[p]=93 as u8; p=p+1
399 d[p]=44 as u8; p=p+1
400 p = cl_key(d,p,"claims_capped" as *u8)
401 p = cl_catn(d,p,capped)
402 d[p]=125 as u8; p=p+1
403 d[p]=10 as u8; p=p+1
404 return p
405}
406func cl_hesc(d: *u8, o: i64, q: *u8, s: i64, e: i64, maxb: i64) -> i64 {
407 var p: i64 = o
408 var i: i64 = s
409 var used: i64 = 0
410 while i < e {
411 if used >= maxb { i = e } else {
412 let c: i64 = q[i] as i64
413 if c == 60 { p = cl_cat(d,p,"<" as *u8) } else { if c == 62 { p = cl_cat(d,p,">" as *u8) } else { if c == 38 { p = cl_cat(d,p,"&" as *u8) } else { if c < 32 { d[p]=32 as u8; p=p+1 } else { d[p]=c as u8; p=p+1 } } } }
414 used = used + 5
415 i = i + 1
416 }
417 }
418 return p
419}
420func cl_page_html(q: *u8, cx: *i64, d: *u8) -> i64 {
421 let n: i64 = cx[0]
422 var p: i64 = 0
423 p = cl_cat(d,p,"<" as *u8)
424 d[p]=33 as u8
425 p = p + 1
426 p = cl_cat(d,p,"doctype html><html><head><meta charset=utf-8><title>Nishi Claims</title></head><body>" as *u8)
427 p = cl_cat(d,p,"<h1>Nishi Claims -- who holds what (TTL crash-expiry, derived never asserted)</h1>" as *u8)
428 let cr: *i64 = sys_mmap(16) as *i64
429 let st_out: *i64 = sys_mmap(32) as *i64
430 let no: *i64 = sys_mmap(16) as *i64
431 var theld: i64 = 0
432 var texp: i64 = 0
433 var tfree: i64 = 0
434 var i: i64 = 0
435 while i < n {
436 let le: i64 = cl_le(q,i,n)
437 if cl_col(q,i,le,2,cr)==1 { if cl_res_seen(q,i,cr[0],cr[1])==0 {
438 let st: i64 = cl_state(q,cx,cr[0],cr[1],st_out)
439 if st==1 { theld=theld+1 }
440 if st==2 { texp=texp+1 }
441 if st==3 { tfree=tfree+1 }
442 } }
443 i = le + 1
444 }
445 p = cl_cat(d,p,"<p>held=" as *u8)
446 p = cl_catn(d,p,theld)
447 p = cl_cat(d,p," expired=" as *u8)
448 p = cl_catn(d,p,texp)
449 p = cl_cat(d,p," free=" as *u8)
450 p = cl_catn(d,p,tfree)
451 p = cl_cat(d,p,"</p>" as *u8)
452 p = cl_cat(d,p,"<table border=1><tr><th>resource</th><th>state</th><th>holder</th><th>ttl-sec</th><th>age-sec</th><th>last note</th></tr>" as *u8)
453 var truncated_rows: i64 = 0
454 var j: i64 = 0
455 while j < n {
456 let je: i64 = cl_le(q,j,n)
457 if cl_col(q,j,je,2,cr)==1 { if cl_res_seen(q,j,cr[0],cr[1])==0 {
458 if p > CL_MAGIC_200000 { truncated_rows = truncated_rows + 1 } else {
459 p = cl_cat(d,p,"<tr><td>" as *u8)
460 p = cl_hesc(d,p,q,cr[0],cr[1],CL_NAME_MAX)
461 p = cl_cat(d,p,"</td><td>" as *u8)
462 let st2: i64 = cl_state(q,cx,cr[0],cr[1],st_out)
463 let sn: *u8 = cl_state_name(st2)
464 p = cl_cat(d,p,sn)
465 p = cl_cat(d,p,"</td><td>" as *u8)
466 if st2==1 { p = cl_hesc(d,p,q,st_out[0],st_out[1],CL_NAME_MAX) }
467 if st2==2 { p = cl_hesc(d,p,q,st_out[0],st_out[1],CL_NAME_MAX) }
468 p = cl_cat(d,p,"</td><td>" as *u8)
469 var tv: i64 = -1
470 if st2==1 { tv = st_out[2] }
471 if st2==2 { tv = st_out[2] }
472 p = cl_catn(d,p,tv)
473 p = cl_cat(d,p,"</td><td>" as *u8)
474 var age: i64 = -1
475 if st2==1 { age = cx[1] - st_out[3] }
476 if st2==2 { age = cx[1] - st_out[3] }
477 p = cl_catn(d,p,age)
478 p = cl_cat(d,p,"</td><td>" as *u8)
479 let hn: i64 = cl_note_span(q,cx,cr[0],cr[1],no)
480 if hn==1 { p = cl_hesc(d,p,q,no[0],no[1],CL_NOTE_MAX) }
481 p = cl_cat(d,p,"</td></tr>" as *u8)
482 }
483 } }
484 j = je + 1
485 }
486 p = cl_cat(d,p,"</table>" as *u8)
487 if truncated_rows > 0 {
488 p = cl_cat(d,p,"<p>ENVELOPE: rows_truncated=" as *u8)
489 p = cl_catn(d,p,truncated_rows)
490 p = cl_cat(d,p,"</p>" as *u8)
491 }
492 p = cl_cat(d,p,"<p>envelope window_bytes=" as *u8)
493 p = cl_catn(d,p,CL_WIN)
494 p = cl_cat(d,p," journal_bytes=" as *u8)
495 p = cl_catn(d,p,n)
496 p = cl_cat(d,p," generated=" as *u8)
497 p = cl_catn(d,p,cx[1])
498 p = cl_cat(d,p," | source=knowledge/status/claims.jrnl | tool=nx_claims (MCP) | <a href=/standup>standup</a> <a href=/wsmine>wsmine</a> <a href=/frontier>frontier</a></p>" as *u8)
499 p = cl_cat(d,p,"</body></html>" as *u8)
500 d[p]=10 as u8
501 p = p + 1
502 return p
503}
504func cl_find(d: *u8, dn: i64, lit: *u8) -> i64 {
505 let ll: i64 = cl_vlen(lit)
506 if ll == 0 { return 0 }
507 var i: i64 = 0
508 while i + ll <= dn {
509 var k: i64 = 0
510 var ok: i64 = 1
511 while k < ll { if d[i+k]!=lit[k] { ok=0; k=ll } else { k=k+1 } }
512 if ok==1 { return 1 }
513 i = i + 1
514 }
515 return 0
516}
517
518// F214 ORPHAN REAP -- fail-safe BY CONSTRUCTION: derive+append on ONE snapshot; release ONLY
519// claims whose state derives EXPIRED (age>ttl by the claim's OWN ttl field). NEVER by name,
520// NEVER by looks-stale, no special cases. Additive-only (RELEASE frames appended, history kept).
521// Idempotent: post-reap state=FREE so a re-run appends NOTHING. Audit=who/when/why in the note.
522func cl_span_z(q: *u8, s: i64, e: i64, d: *u8, cap: i64) -> i64 {
523 if e - s >= cap { return 0 }
524 var i: i64 = 0
525 while s + i < e { d[i] = q[s+i]; i = i + 1 }
526 d[i] = 0 as u8
527 return 1
528}
529func cl_reap(journal: *u8, q: *u8, cx: *i64, reaper: *u8, note: *u8) -> i64 {
530 let n: i64 = cx[0]
531 let cr: *i64 = sys_mmap(16) as *i64
532 let so: *i64 = sys_mmap(32) as *i64
533 let rname: *u8 = sys_mmap(512)
534 let hname: *u8 = sys_mmap(512)
535 let rnote: *u8 = sys_mmap(CL_MAGIC_4096)
536 var reaped: i64 = 0
537 var scanned: i64 = 0
538 var i: i64 = 0
539 while i < n {
540 let le: i64 = cl_le(q,i,n)
541 if cl_col(q,i,le,2,cr)==1 { if cl_res_seen(q,i,cr[0],cr[1])==0 {
542 scanned = scanned + 1
543 let st: i64 = cl_state(q,cx,cr[0],cr[1],so)
544 if st==2 {
545 let okr: i64 = cl_span_z(q,cr[0],cr[1],rname,500)
546 let okh: i64 = cl_span_z(q,so[0],so[1],hname,500)
547 if okr==1 { if okh==1 {
548 let age: i64 = cx[1] - so[3]
549 var o: i64 = 0
550 o = cl_cat(rnote,0,"ORPHAN-REAPED by=" as *u8)
551 o = cl_cat(rnote,o,reaper)
552 o = cl_cat(rnote,o," expired age=" as *u8)
553 o = cl_catn(rnote,o,age)
554 o = cl_cat(rnote,o,"s ttl=" as *u8)
555 o = cl_catn(rnote,o,so[2])
556 o = cl_cat(rnote,o,"s " as *u8)
557 o = cl_cat(rnote,o,note)
558 rnote[o] = 0 as u8
559 let arc: i64 = cl_append(journal,-1,"RELEASE" as *u8,rname,hname,0,rnote)
560 if arc==0 {
561 reaped = reaped + 1
562 gv_puts("NX-CLAIMS REAPED resource=" as *u8)
563 gv_puts(rname)
564 gv_puts(" holder=" as *u8)
565 gv_puts(hname)
566 gv_puts(" age=" as *u8)
567 gv_num(age)
568 gv_puts(" ttl=" as *u8)
569 gv_num(so[2])
570 gv_puts("\n" as *u8)
571 } else {
572 gv_puts("NX-CLAIMS REAP-APPEND-FAIL resource=" as *u8)
573 gv_puts(rname)
574 gv_puts("\n" as *u8)
575 }
576 } }
577 if okr==0 { gv_puts("NX-CLAIMS REAP-SKIP resource-name-too-long\n" as *u8) } else { if okh==0 { gv_puts("NX-CLAIMS REAP-SKIP holder-too-long\n" as *u8) } }
578 }
579 } }
580 i = le + 1
581 }
582 gv_puts("NX-CLAIMS REAP-DONE reaped=" as *u8)
583 gv_num(reaped)
584 gv_puts(" scanned=" as *u8)
585 gv_num(scanned)
586 gv_puts(" now=" as *u8)
587 gv_num(cx[1])
588 gv_puts("\n" as *u8)
589 return reaped
590}
591
592// ---------- selftest gate (T1..T13, deterministic via injected timestamps) ----------
593func cl_selftest(journal: *u8) -> i64 {
594 let ctr: *i64 = gv_ctr()
595 gv_head("nx_claims selftest -- lease/claim teeth (caller pre-cleans journal)" as *u8)
596 let q: *u8 = sys_mmap(CL_WIN)
597 let cx: *i64 = sys_mmap(64) as *i64
598 let sp: *i64 = sys_mmap(16) as *i64
599 let st_out: *i64 = sys_mmap(32) as *i64
600 let now: i64 = sys_now_realtime_sec()
601 let rcap: i64 = CL_WIN - 8
602 // T1 empty journal -> absent resource honestly ABSENT
603 var n: i64 = cl_read(journal,q,rcap)
604 cx[0]=n; cx[1]=now; cx[2]=0; cx[3]=0; cx[4]=0; cx[5]=journal as i64
605 var ok1: i64 = 0
606 if n==0 { ok1=1 }
607 gv_check("T1 empty journal no fabricated claims" as *u8, ok1, ctr)
608 // T2 fresh claim -> HELD by claimer
609 let t2ts: i64 = now - 100
610 cl_append(journal, t2ts, "CLAIM" as *u8, "resA" as *u8, "sess1" as *u8, 1000, "claimA" as *u8)
611 n = cl_read(journal,q,rcap)
612 cx[0]=n
613 var ok2: i64 = 0
614 if cl_res_span_lit(q,cx,"resA" as *u8,sp)==1 {
615 let st2: i64 = cl_state(q,cx,sp[0],sp[1],st_out)
616 if st2==1 { if cl_lit_eq(q,st_out[0],st_out[1],"sess1" as *u8)==1 { ok2=1 } }
617 }
618 gv_check("T2 fresh claim derives HELD by claimer" as *u8, ok2, ctr)
619 // T3 contender predicate: HELD + holder != sess2 -> claim verb would refuse
620 var ok3: i64 = 0
621 if cl_res_span_lit(q,cx,"resA" as *u8,sp)==1 {
622 let st3: i64 = cl_state(q,cx,sp[0],sp[1],st_out)
623 if st3==1 { if cl_lit_eq(q,st_out[0],st_out[1],"sess2" as *u8)==0 { ok3=1 } }
624 }
625 gv_check("T3 contended claim predicate refuses other actor" as *u8, ok3, ctr)
626 // T4 beat by holder revives freshness: old claim + fresh beat -> HELD
627 let t4c: i64 = now - CL_MAGIC_5000
628 let t4b: i64 = now - 50
629 cl_append(journal, t4c, "CLAIM" as *u8, "resB" as *u8, "sess1" as *u8, 1000, "claimB" as *u8)
630 cl_append(journal, t4b, "BEAT" as *u8, "resB" as *u8, "sess1" as *u8, 0, "beatB" as *u8)
631 n = cl_read(journal,q,rcap)
632 cx[0]=n
633 var ok4: i64 = 0
634 if cl_res_span_lit(q,cx,"resB" as *u8,sp)==1 { let st4: i64 = cl_state(q,cx,sp[0],sp[1],st_out); if st4==1 { ok4=1 } }
635 gv_check("T4 holder beat revives freshness HELD" as *u8, ok4, ctr)
636 // T5 stale claim without beats -> EXPIRED (crash-expiry)
637 let t5c: i64 = now - CL_MAGIC_5000
638 cl_append(journal, t5c, "CLAIM" as *u8, "resC" as *u8, "sess3" as *u8, 1000, "claimC" as *u8)
639 n = cl_read(journal,q,rcap)
640 cx[0]=n
641 var ok5: i64 = 0
642 if cl_res_span_lit(q,cx,"resC" as *u8,sp)==1 { let st5: i64 = cl_state(q,cx,sp[0],sp[1],st_out); if st5==2 { ok5=1 } }
643 gv_check("T5 stale claim derives EXPIRED crash-expiry" as *u8, ok5, ctr)
644 // T6 release -> FREE, then re-claim by other -> HELD by new holder
645 let t6c: i64 = now - 300
646 let t6r: i64 = now - 200
647 let t6c2: i64 = now - 100
648 cl_append(journal, t6c, "CLAIM" as *u8, "resD" as *u8, "sess1" as *u8, 1000, "claimD" as *u8)
649 cl_append(journal, t6r, "RELEASE" as *u8, "resD" as *u8, "sess1" as *u8, 0, "releaseD" as *u8)
650 n = cl_read(journal,q,rcap)
651 cx[0]=n
652 var ok6a: i64 = 0
653 if cl_res_span_lit(q,cx,"resD" as *u8,sp)==1 { let st6: i64 = cl_state(q,cx,sp[0],sp[1],st_out); if st6==3 { ok6a=1 } }
654 cl_append(journal, t6c2, "CLAIM" as *u8, "resD" as *u8, "sess2" as *u8, 1000, "reclaimD" as *u8)
655 n = cl_read(journal,q,rcap)
656 cx[0]=n
657 var ok6: i64 = 0
658 if ok6a==1 { if cl_res_span_lit(q,cx,"resD" as *u8,sp)==1 {
659 let st6b: i64 = cl_state(q,cx,sp[0],sp[1],st_out)
660 if st6b==1 { if cl_lit_eq(q,st_out[0],st_out[1],"sess2" as *u8)==1 { ok6=1 } }
661 } }
662 gv_check("T6 release frees then other actor claims" as *u8, ok6, ctr)
663 // T7 neg-control: absent resource stays absent
664 var ok7: i64 = 0
665 if cl_res_span_lit(q,cx,"resNOPE" as *u8,sp)==0 { ok7=1 }
666 gv_check("T7 neg-control absent resource stays absent" as *u8, ok7, ctr)
667 // T8 walk JSON smoke: totals + states + envelope
668 let d: *u8 = sys_mmap(CL_OUT)
669 let dl: i64 = cl_walk_json(q,cx,d)
670 var ok8: i64 = 0
671 if dl > 0 { if d[0]==(123 as u8) { if cl_find(d,dl,"EXPIRED" as *u8)==1 { if cl_find(d,dl,"claims_capped" as *u8)==1 { if cl_find(d,dl,"held" as *u8)==1 { ok8=1 } } } } }
672 gv_check("T8 walk JSON carries totals plus declared envelope" as *u8, ok8, ctr)
673 // T9 page HTML smoke: table + states + envelope
674 let d2: *u8 = sys_mmap(CL_OUT)
675 let dl9: i64 = cl_page_html(q,cx,d2)
676 var ok9: i64 = 0
677 if dl9 > 0 { if cl_find(d2,dl9,"<table" as *u8)==1 { if cl_find(d2,dl9,"EXPIRED" as *u8)==1 { if cl_find(d2,dl9,"window_bytes" as *u8)==1 { ok9=1 } } } }
678 gv_check("T9 page HTML carries table states envelope" as *u8, ok9, ctr)
679 // T10 F214 reap: ONLY the provably-expired claim (resC age>ttl) is released; audit note lands
680 n = cl_read(journal,q,rcap)
681 cx[0]=n
682 let r10: i64 = cl_reap(journal,q,cx,"reaper1" as *u8,"t10-orphan-sweep" as *u8)
683 n = cl_read(journal,q,rcap)
684 cx[0]=n
685 var ok10: i64 = 0
686 if r10==1 { if cl_res_span_lit(q,cx,"resC" as *u8,sp)==1 {
687 let st10: i64 = cl_state(q,cx,sp[0],sp[1],st_out)
688 if st10==3 { if cl_find(q,n,"ORPHAN-REAPED by=reaper1" as *u8)==1 { ok10=1 } }
689 } }
690 gv_check("T10 reap releases only the expired claim with audit note" as *u8, ok10, ctr)
691 // T11 NEGATIVE: unexpired claims SURVIVE the reap untouched
692 var ok11: i64 = 0
693 if cl_res_span_lit(q,cx,"resA" as *u8,sp)==1 {
694 let st11a: i64 = cl_state(q,cx,sp[0],sp[1],st_out)
695 if st11a==1 { if cl_lit_eq(q,st_out[0],st_out[1],"sess1" as *u8)==1 {
696 if cl_res_span_lit(q,cx,"resD" as *u8,sp)==1 {
697 let st11d: i64 = cl_state(q,cx,sp[0],sp[1],st_out)
698 if st11d==1 { if cl_lit_eq(q,st_out[0],st_out[1],"sess2" as *u8)==1 { ok11=1 } }
699 }
700 } }
701 }
702 gv_check("T11 negative unexpired claims survive reap" as *u8, ok11, ctr)
703 // T12 idempotent: nothing expired -> reap changes NOTHING (0 reaped, journal byte-identical)
704 let nb12: i64 = n
705 let r12: i64 = cl_reap(journal,q,cx,"reaper1" as *u8,"t12-rerun" as *u8)
706 let n12: i64 = cl_read(journal,q,rcap)
707 cx[0]=n12
708 var ok12: i64 = 0
709 if r12==0 { if n12==nb12 { ok12=1 } }
710 gv_check("T12 reap idempotent when nothing expired" as *u8, ok12, ctr)
711 // T13 a reaped resource is claimable by a NEW actor (the unblocked-sibling tooth)
712 cl_append(journal, -1, "CLAIM" as *u8, "resC" as *u8, "sess9" as *u8, 1000, "post-reap-claim" as *u8)
713 let n13: i64 = cl_read(journal,q,rcap)
714 cx[0]=n13
715 var ok13: i64 = 0
716 if cl_res_span_lit(q,cx,"resC" as *u8,sp)==1 {
717 let st13: i64 = cl_state(q,cx,sp[0],sp[1],st_out)
718 if st13==1 { if cl_lit_eq(q,st_out[0],st_out[1],"sess9" as *u8)==1 { ok13=1 } }
719 }
720 gv_check("T13 reaped resource claimable by new actor" as *u8, ok13, ctr)
721 let rc: i64 = gv_verdict("CLAIMS-GATE" as *u8, ctr, "claim lease + F214 orphan-reap teeth green" as *u8)
722 return rc
723}
724
725func main(argc: i64, argv: *i64) -> i64 {
726 if argc < 3 { gv_puts("usage: nx_claims {claim <journal> <resource> <actor> <ttl_sec> <note> | beat <journal> <resource> <actor> <note> | release <journal> <resource> <actor> <note> | walk <journal> | state <journal> <resource> | reap <journal> <reaper> <note> | selftest <journal>}\n" as *u8); sys_exit(2); return 2 }
727 let verb: *u8 = argv[1] as *u8
728 let journal: *u8 = argv[2] as *u8
729 let vl: i64 = cl_vlen(verb)
730 if cl_lit_eq(verb,0,vl,"selftest" as *u8)==1 { let rc: i64 = cl_selftest(journal); sys_exit(rc); return rc }
731 let q: *u8 = sys_mmap(CL_WIN)
732 let rcap: i64 = CL_WIN - 8
733 let n: i64 = cl_read(journal,q,rcap)
734 let cx: *i64 = sys_mmap(64) as *i64
735 cx[0]=n
736 cx[1]=sys_now_realtime_sec()
737 cx[2]=0
738 cx[3]=0
739 cx[4]=0
740 if n >= rcap { cx[4]=1 }
741 cx[5]=journal as i64
742 if cl_lit_eq(verb,0,vl,"walk" as *u8)==1 {
743 let d: *u8 = sys_mmap(CL_OUT)
744 let dl: i64 = cl_walk_json(q,cx,d)
745 sys_write(1,d,dl)
746 sys_exit(0)
747 return 0
748 }
749 if cl_lit_eq(verb,0,vl,"page" as *u8)==1 {
750 if argc < 4 { gv_puts("page needs <journal> <outfile>\n" as *u8); sys_exit(2); return 2 }
751 let outfile: *u8 = argv[3] as *u8
752 let d: *u8 = sys_mmap(CL_OUT)
753 let dl: i64 = cl_page_html(q,cx,d)
754 let fd: i64 = sys_openat_wr(outfile, 0x1a4)
755 if fd < 0 { gv_puts("CLAIMS-PAGE OPEN-FAIL\n" as *u8); sys_exit(1); return 1 }
756 sys_write(fd,d,dl)
757 sys_close(fd)
758 gv_puts("CLAIMS-PAGE bytes=" as *u8)
759 gv_num(dl)
760 gv_puts("\n" as *u8)
761 sys_exit(0)
762 return 0
763 }
764 // seq543/seq552: single-resource STATE probe (additive, rule 19) -- the SAME derivation the
765 // beat/release path uses (cl_res_span_lit + cl_state), so holdership judgment stays one copy.
766 // Lets nx_seat checkout classify a NOT-HOLDER release honestly (already-free vs live-foreign)
767 // without a full walk. Read-only, exit 0 always.
768 if cl_lit_eq(verb,0,vl,"state" as *u8)==1 {
769 if argc < 4 { gv_puts("state needs <journal> <resource>\n" as *u8); sys_exit(2); return 2 }
770 let sres: *u8 = argv[3] as *u8
771 let ssp: *i64 = sys_mmap(16) as *i64
772 let sso: *i64 = sys_mmap(32) as *i64
773 var sv: i64 = -1
774 if cl_res_span_lit(q,cx,sres,ssp)==1 { sv = cl_state(q,cx,ssp[0],ssp[1],sso) }
775 gv_puts("NX-CLAIMS STATE resource=" as *u8)
776 gv_puts(sres)
777 gv_puts(" state=" as *u8)
778 gv_puts(cl_state_name(sv))
779 gv_puts(" holder=" as *u8)
780 var svh: i64 = 0
781 if sv == 1 { svh = 1 }
782 if sv == 2 { svh = 1 }
783 if svh == 1 { sys_write(1,(q as i64 + sso[0]) as *u8, sso[1]-sso[0]) }
784 if svh == 0 { gv_puts("-" as *u8) }
785 gv_puts("\n" as *u8)
786 sys_exit(0)
787 return 0
788 }
789 if cl_lit_eq(verb,0,vl,"reap" as *u8)==1 {
790 if argc < 5 { gv_puts("reap needs <journal> <reaper> <note>\n" as *u8); sys_exit(2); return 2 }
791 let reaper: *u8 = argv[3] as *u8
792 let rpnote: *u8 = argv[4] as *u8
793 let rk: i64 = cl_reap(journal, q, cx, reaper, rpnote)
794 if rk >= 0 { sys_exit(0); return 0 }
795 sys_exit(1)
796 return 1
797 }
798 if argc < 5 { gv_puts("claim|beat|release need <journal> <resource> <actor> ...\n" as *u8); sys_exit(2); return 2 }
799 let res: *u8 = argv[3] as *u8
800 let actor: *u8 = argv[4] as *u8
801 let sp: *i64 = sys_mmap(16) as *i64
802 let st_out: *i64 = sys_mmap(32) as *i64
803 var st: i64 = -1
804 if cl_res_span_lit(q,cx,res,sp)==1 { st = cl_state(q,cx,sp[0],sp[1],st_out) }
805 if cl_lit_eq(verb,0,vl,"claim" as *u8)==1 {
806 if argc < 7 { gv_puts("claim needs <journal> <resource> <actor> <ttl_sec> <note>\n" as *u8); sys_exit(2); return 2 }
807 let ttlz: *u8 = argv[5] as *u8
808 let ttl: i64 = cl_atoi_z(ttlz)
809 let note: *u8 = argv[6] as *u8
810 // REFUSE, NEVER SILENTLY SHRINK. The estate banked that law in the memfloor incident, and the
811 // 2026-07 "ttl ceiling" recorded 86400 for a 7200000 request -- a SILENT clamp, in a CALLER.
812 // A ceiling that lives in one caller is bypassable by every other caller, so the bound belongs
813 // HERE, in the organ that owns holdership. Loud + exit 2 so a typo is a usage error a human sees,
814 // never a lease quietly rounded down and never an 83-day lock nobody notices for 16 days.
815 if ttl > CL_TTL_MAX { gv_puts("NX-CLAIMS BAD-TTL-CEILING requested=" as *u8); gv_num(ttl); gv_puts(" max=" as *u8); gv_num(CL_TTL_MAX); gv_puts(" seconds (a lease whose expiry the lessee sets is not a lease)\n" as *u8); sys_exit(2); return 2 }
816 if ttl <= 0 { gv_puts("NX-CLAIMS BAD-TTL (must be positive seconds)\n" as *u8); sys_exit(2); return 2 }
817 if st == 1 {
818 if cl_lit_eq(q,st_out[0],st_out[1],actor)==0 {
819 gv_puts("NX-CLAIMS CONTENDED resource=" as *u8)
820 gv_puts(res)
821 gv_puts(" holder=" as *u8)
822 sys_write(1,(q as i64 + st_out[0]) as *u8, st_out[1]-st_out[0])
823 gv_puts("\n" as *u8)
824 sys_exit(3)
825 return 3
826 }
827 }
828 let rc: i64 = cl_append(journal, -1, "CLAIM" as *u8, res, actor, ttl, note)
829 if rc==0 { gv_puts("NX-CLAIMS HELD resource=" as *u8); gv_puts(res); gv_puts(" actor=" as *u8); gv_puts(actor); gv_puts(" ttl=" as *u8); gv_num(ttl); gv_puts("\n" as *u8); sys_exit(0); return 0 }
830 gv_puts("append FAILED\n" as *u8)
831 sys_exit(1)
832 return 1
833 }
834 // beat | release require an un-released claim whose holder == actor (EXPIRED still owned)
835 if argc < 6 { gv_puts("beat|release need <journal> <resource> <actor> <note>\n" as *u8); sys_exit(2); return 2 }
836 let note2: *u8 = argv[5] as *u8
837 var owner_ok: i64 = 0
838 if st == 1 { if cl_lit_eq(q,st_out[0],st_out[1],actor)==1 { owner_ok=1 } }
839 if st == 2 { if cl_lit_eq(q,st_out[0],st_out[1],actor)==1 { owner_ok=1 } }
840 if owner_ok == 0 {
841 gv_puts("NX-CLAIMS NOT-HOLDER resource=" as *u8)
842 gv_puts(res)
843 gv_puts(" actor=" as *u8)
844 gv_puts(actor)
845 gv_puts("\n" as *u8)
846 sys_exit(3)
847 return 3
848 }
849 if cl_lit_eq(verb,0,vl,"beat" as *u8)==1 {
850 let rc: i64 = cl_append(journal, -1, "BEAT" as *u8, res, actor, 0, note2)
851 if rc==0 { gv_puts("NX-CLAIMS BEAT resource=" as *u8); gv_puts(res); gv_puts("\n" as *u8); sys_exit(0); return 0 }
852 gv_puts("append FAILED\n" as *u8)
853 sys_exit(1)
854 return 1
855 }
856 if cl_lit_eq(verb,0,vl,"release" as *u8)==1 {
857 let rc: i64 = cl_append(journal, -1, "RELEASE" as *u8, res, actor, 0, note2)
858 if rc==0 { gv_puts("NX-CLAIMS RELEASED resource=" as *u8); gv_puts(res); gv_puts("\n" as *u8); sys_exit(0); return 0 }
859 gv_puts("append FAILED\n" as *u8)
860 sys_exit(1)
861 return 1
862 }
863 gv_puts("unknown verb\n" as *u8)
864 sys_exit(2)
865 return 2
866}