nx_release_delta_lib.nx source
↩ module page · 412 lines · 20851 B
1// nx_release_delta_lib.nx -- THE RELEASE DELTA: what changed on the boards since the operator last judged (2026-09-04).
2//
3// WHY. /world/beach carried a build stamp and nothing about what that build changed. The operator's acceptance
4// plane (knowledge/store/accept-) held four REJECT rows and one UNJUDGED from 2026-08-30 and nothing after, while
5// every seat since shipped plumbing, telemetry, backends and gates -- progress the review page could not show, so
6// "the review page has not moved" was true of the PAGE even where it was false of the WORK. Operator 2026-09-04:
7// consistent progress each release on the review page. This lib is the pure arithmetic under gpe_release_block:
8// given the accept-plane rows, the referee-plane rows and a board's log rows, it derives (1) the latest operator
9// verdict per subject and the date of the newest one, (2) every `land` row dated strictly AFTER that date, (3) the
10// referee percept beside each subject, and (4) the exact accept-row recipe for THIS build, and renders them as one
11// HTML block. It reads NO files and forks NOTHING: the emitter hands it buffers, the gate hands it fixtures, and
12// the same bytes are judged either way.
13// LAW. THE FLIP IS THE RECEIPT, THE ACCEPT ROW IS THE PROOF. A land row here is a seat's claim that something
14// shipped; only an operator ACCEPT row moves a subject out of INCOMPLETE, and this block prints the row to file.
15// ROW ORDER IS WRITE ORDER (nx_accept_lib's law): the last operator row per subject wins; dates are printed, and
16// the since-date is the GREATEST date over operator rows, never the last row's. Two bounds, both ANNOUNCED in the
17// block: RD_LIST_CAP land rows listed (the rest counted), RD_TEXT_CAP bytes of a row's text (the board keeps all).
18// license_tier: ORIGINAL No hw writes (Rule 26).
19import "nx_syscalls.nx"
20import "nx_comparewatch_lib.nx"
21import "nx_accept_lib.nx"
22
23const RD_NL: i64 = 10
24const RD_CR: i64 = 13
25const RD_TAB: i64 = 9
26const RD_PIPE: i64 = 124
27const RD_SPACE: i64 = 32
28const RD_AMP: i64 = 38
29const RD_HASH: i64 = 35
30const RD_SEMI: i64 = 59
31const RD_LT: i64 = 60
32const RD_GT: i64 = 62
33const RD_DQ: i64 = 34
34const RD_SQ: i64 = 39
35const RD_BSL: i64 = 92
36const RD_D0: i64 = 48
37const RD_D9: i64 = 57
38const RD_BYTE: i64 = 255
39const RD_CTRL_MAX: i64 = 31 // bytes at or below this are control characters: rendered as one space
40const RD_BASE10: i64 = 10
41const RD_DATE_W: i64 = 10 // YYYY-MM-DD
42const RD_LOG_PFX: *u8 = "log|"
43const RD_LOG_PFX_LEN: i64 = 4
44const RD_KIND_LAND: *u8 = "land"
45const RD_KIND_RETRACT: *u8 = "retract"
46const RD_KIND_MEASURE: *u8 = "measure"
47const RD_ACTOR_OPERATOR: *u8 = "operator"
48const RD_V_ACCEPT: *u8 = "ACCEPT"
49// accept- row fields (the nx_accept_lib grammar: id subject verdict date actor words targets)
50const RD_AF_ID: i64 = 0
51const RD_AF_SUBJ: i64 = 1
52const RD_AF_VERD: i64 = 2
53const RD_AF_DATE: i64 = 3
54const RD_AF_ACTOR: i64 = 4
55const RD_AF_WORDS: i64 = 5
56// referee- row fields (id subject sha comp percept tier date seat lens frame)
57const RD_RF_SUBJ: i64 = 1
58const RD_RF_PERCEPT: i64 = 4
59const RD_RF_TIER: i64 = 5
60const RD_RF_DATE: i64 = 6
61// the subject table: fixed-width records so every field is addressable by offset from the gate
62const RD_MAX_SUBJ: i64 = 32
63const RD_REC: i64 = 512
64const RD_F_SUBJ: i64 = 0
65const RD_W_SUBJ: i64 = 64
66const RD_F_VERD: i64 = 64
67const RD_W_VERD: i64 = 16
68const RD_F_DATE: i64 = 80
69const RD_W_DATE: i64 = 16
70const RD_F_ID: i64 = 96
71const RD_W_ID: i64 = 96
72const RD_F_WORDS: i64 = 192
73const RD_W_WORDS: i64 = 160
74const RD_F_RPCT: i64 = 352
75const RD_W_RPCT: i64 = 16
76const RD_F_RDATE: i64 = 368
77const RD_W_RDATE: i64 = 16
78const RD_F_RTIER: i64 = 384
79const RD_W_RTIER: i64 = 16
80const RD_F_RSEEN: i64 = 400
81const RD_F_OPSEEN: i64 = 401
82// listing bounds, both ANNOUNCED by the emitted block
83const RD_LIST_CAP: i64 = 24
84const RD_TEXT_CAP: i64 = 240
85const RD_ENT_MAX: i64 = 6 // the longest entity rd_esc_html emits per input byte
86const RD_ITEM_RESERVE: i64 = 2048 // static bytes one list item or table row can add beside its escaped text
87const RD_COUNTS: i64 = 4
88const RD_C_LAND: i64 = 0
89const RD_C_RETRACT: i64 = 1
90const RD_C_MEASURE: i64 = 2
91const RD_C_LISTED: i64 = 3
92
93func rd_rec(tab: *u8, i: i64) -> *u8 { return ((tab as i64) + i * RD_REC) as *u8 }
94func rd_str(rec: *u8, off: i64) -> *u8 { return ((rec as i64) + off) as *u8 }
95func rd_rec_zero(rec: *u8) -> i64 { var i: i64 = 0; while i < RD_REC { rec[i] = 0 as u8; i = i + 1 } return 0 }
96func rd_byte(b: *u8, i: i64) -> i64 { return (b[i] as i64) & RD_BYTE }
97// compare a[ao..ao+an) against the NUL-terminated b: negative, zero or positive like strcmp
98func rd_cmp_slice(a: *u8, ao: i64, an: i64, b: *u8) -> i64 {
99 var i: i64 = 0
100 while i < an {
101 let bc: i64 = rd_byte(b, i)
102 if bc == 0 { return 1 }
103 let ac: i64 = rd_byte(a, ao + i)
104 if ac != bc { return ac - bc }
105 i = i + 1
106 }
107 if rd_byte(b, an) != 0 { return 0 - 1 }
108 return 0
109}
110func rd_slice_eq(a: *u8, ao: i64, an: i64, b: *u8) -> i64 { if rd_cmp_slice(a, ao, an, b) == 0 { return 1 } return 0 }
111func rd_find_subj(tab: *u8, ns: i64, v: *u8, so: i64, n: i64) -> i64 {
112 var i: i64 = 0
113 while i < ns {
114 if rd_slice_eq(v, so, n, rd_str(rd_rec(tab, i), RD_F_SUBJ)) == 1 { return i }
115 i = i + 1
116 }
117 return 0 - 1
118}
119func rd_find_subj_z(tab: *u8, ns: i64, z: *u8) -> i64 { return rd_find_subj(tab, ns, z, 0, al_slen(z)) }
120func rd_line_end(b: *u8, n: i64, p: i64) -> i64 {
121 var e: i64 = p
122 while e < n { if rd_byte(b, e) == RD_NL { return e } e = e + 1 }
123 return n
124}
125// a new subject record, or -1 when the table is full (the caller announces a full table; it never drops silently)
126func rd_subj_add(tab: *u8, ns: i64, v: *u8, so: i64, n: i64) -> i64 {
127 if ns >= RD_MAX_SUBJ { return 0 - 1 }
128 let rec: *u8 = rd_rec(tab, ns)
129 rd_rec_zero(rec)
130 al_setf(rec, RD_F_SUBJ, RD_W_SUBJ, v, so, n)
131 return ns
132}
133
134// THE SUBJECT KEY IS THE ACCEPT LIB'S OWN RULE: the ga_accept_<name> token in the targets field names the subject
135// (garments, hair, skin, locomotion, face); field 1 is the operator's DESCRIPTION of what was judged. MEASURED on the
136// first served release (2026-09-04): keying on field 1 left every operator row unjoined to its referee row -- ten
137// table rows where five subjects exist, with `unjudged` beside every REJECT. A row with no such token keys on its
138// description, so nothing is dropped; the description is kept beside the key for the reader.
139const RD_AF_TARGETS: i64 = 6
140const RD_SYM_PFX: *u8 = "ga_accept_"
141const RD_SYM_PFX_LEN: i64 = 10
142const RD_COMMA: i64 = 44
143const RD_F_DESC: i64 = 408
144const RD_W_DESC: i64 = 104
145func rd_subject_of(v: *u8, vl: i64, f: *i64) -> i64 {
146 let g: *i64 = sys_mmap(16) as *i64
147 if cw_field(v, vl, RD_AF_TARGETS, g) == 1 {
148 var i: i64 = g[0]
149 let e: i64 = g[0] + g[1]
150 while i + RD_SYM_PFX_LEN <= e {
151 if rd_slice_eq(v, i, RD_SYM_PFX_LEN, RD_SYM_PFX) == 1 {
152 let s: i64 = i + RD_SYM_PFX_LEN
153 var k: i64 = s
154 while k < e {
155 let c: i64 = rd_byte(v, k)
156 if c == RD_COMMA { break }
157 if c == RD_SPACE { break }
158 k = k + 1
159 }
160 if k > s { f[0] = s; f[1] = k - s; return 1 }
161 }
162 i = i + 1
163 }
164 }
165 return cw_field(v, vl, RD_AF_SUBJ, f)
166}
167
168// (1) the operator's latest verdict per subject. Returns the subject count; since_out receives the greatest
169// date over all operator rows and stays empty when there is none -- NEVER JUDGED is a state the block prints.
170func rd_op_latest(rows: *u8, n: i64, tab: *u8, since_out: *u8) -> i64 {
171 since_out[0] = 0 as u8
172 var ns: i64 = 0
173 let f: *i64 = sys_mmap(16) as *i64
174 var p: i64 = 0
175 while p < n {
176 let e: i64 = rd_line_end(rows, n, p)
177 var el: i64 = e
178 if el > p { if rd_byte(rows, el - 1) == RD_CR { el = el - 1 } }
179 let vl: i64 = el - p
180 if vl > 0 {
181 let v: *u8 = ((rows as i64) + p) as *u8
182 var isop: i64 = 0
183 if cw_field(v, vl, RD_AF_ACTOR, f) == 1 { if cw_field_eq(v, f[0], f[1], RD_ACTOR_OPERATOR) == 1 { isop = 1 } }
184 if isop == 1 { if rd_subject_of(v, vl, f) == 1 { if f[1] > 0 {
185 var si: i64 = rd_find_subj(tab, ns, v, f[0], f[1])
186 if si < 0 { si = rd_subj_add(tab, ns, v, f[0], f[1]); if si >= 0 { ns = ns + 1 } }
187 if si >= 0 {
188 let rec: *u8 = rd_rec(tab, si)
189 rec[RD_F_OPSEEN] = 1 as u8
190 if cw_field(v, vl, RD_AF_SUBJ, f) == 1 { al_setf(rec, RD_F_DESC, RD_W_DESC, v, f[0], f[1]) }
191 if cw_field(v, vl, RD_AF_VERD, f) == 1 { al_setf(rec, RD_F_VERD, RD_W_VERD, v, f[0], f[1]) }
192 if cw_field(v, vl, RD_AF_ID, f) == 1 { al_setf(rec, RD_F_ID, RD_W_ID, v, f[0], f[1]) }
193 if cw_field(v, vl, RD_AF_WORDS, f) == 1 { al_setf(rec, RD_F_WORDS, RD_W_WORDS, v, f[0], f[1]) }
194 if cw_field(v, vl, RD_AF_DATE, f) == 1 {
195 al_setf(rec, RD_F_DATE, RD_W_DATE, v, f[0], f[1])
196 if rd_cmp_slice(v, f[0], f[1], since_out) > 0 { al_setf(since_out, 0, RD_W_DATE, v, f[0], f[1]) }
197 }
198 }
199 } } }
200 }
201 p = e + 1
202 }
203 return ns
204}
205
206// (2) the referee's latest row per subject (write order, last wins). A subject the operator never judged is
207// APPENDED so a judged-but-unaccepted subject is visible; returns the new subject count.
208func rd_ref_latest(rows: *u8, n: i64, tab: *u8, ns0: i64) -> i64 {
209 var ns: i64 = ns0
210 let f: *i64 = sys_mmap(16) as *i64
211 var p: i64 = 0
212 while p < n {
213 let e: i64 = rd_line_end(rows, n, p)
214 var el: i64 = e
215 if el > p { if rd_byte(rows, el - 1) == RD_CR { el = el - 1 } }
216 let vl: i64 = el - p
217 if vl > 0 {
218 let v: *u8 = ((rows as i64) + p) as *u8
219 if cw_field(v, vl, RD_RF_SUBJ, f) == 1 { if f[1] > 0 {
220 var si: i64 = rd_find_subj(tab, ns, v, f[0], f[1])
221 if si < 0 { si = rd_subj_add(tab, ns, v, f[0], f[1]); if si >= 0 { ns = ns + 1 } }
222 if si >= 0 {
223 let rec: *u8 = rd_rec(tab, si)
224 rec[RD_F_RSEEN] = 1 as u8
225 if cw_field(v, vl, RD_RF_PERCEPT, f) == 1 { al_setf(rec, RD_F_RPCT, RD_W_RPCT, v, f[0], f[1]) }
226 if cw_field(v, vl, RD_RF_TIER, f) == 1 { al_setf(rec, RD_F_RTIER, RD_W_RTIER, v, f[0], f[1]) }
227 if cw_field(v, vl, RD_RF_DATE, f) == 1 { al_setf(rec, RD_F_RDATE, RD_W_RDATE, v, f[0], f[1]) }
228 }
229 } }
230 }
231 p = e + 1
232 }
233 return ns
234}
235
236// numeric entity: & # digits ;
237func rd_ent_num(out: *u8, o0: i64, v: i64) -> i64 {
238 var o: i64 = o0
239 out[o] = RD_AMP as u8; o = o + 1
240 out[o] = RD_HASH as u8; o = o + 1
241 o = al_catn(out, o, v)
242 out[o] = RD_SEMI as u8; o = o + 1
243 return o
244}
245// HTML-escape src[so..so+n) into out. Also JS-string-safe by construction: the double quote, the single quote
246// and the backslash become entities and every control byte (newline included) becomes one space, so the block
247// can ride inside one double-quoted JS string with no second escaping layer to get wrong.
248func rd_esc_html(out: *u8, o0: i64, src: *u8, so: i64, n: i64) -> i64 {
249 var o: i64 = o0
250 var i: i64 = 0
251 while i < n {
252 let c: i64 = rd_byte(src, so + i)
253 var done: i64 = 0
254 if c == RD_AMP { o = al_cat(out, o, "&" as *u8); done = 1 }
255 if c == RD_LT { o = al_cat(out, o, "<" as *u8); done = 1 }
256 if c == RD_GT { o = al_cat(out, o, ">" as *u8); done = 1 }
257 if c == RD_DQ { o = al_cat(out, o, """ as *u8); done = 1 }
258 if c == RD_SQ { o = rd_ent_num(out, o, RD_SQ); done = 1 }
259 if c == RD_BSL { o = rd_ent_num(out, o, RD_BSL); done = 1 }
260 if done == 0 { if c <= RD_CTRL_MAX { out[o] = RD_SPACE as u8; o = o + 1; done = 1 } }
261 if done == 0 { out[o] = c as u8; o = o + 1 }
262 i = i + 1
263 }
264 return o
265}
266func rd_esc_z(out: *u8, o: i64, z: *u8) -> i64 { return rd_esc_html(out, o, z, 0, al_slen(z)) }
267
268// (3) land rows dated strictly after `since`. A board log row is log|<epoch>|<rung>|<kind>|<text> (the
269// gameengine.plan grammar). Each land row after the date is rendered as one list item (board, rung, civil
270// date, the first RD_TEXT_CAP bytes of its text, escaped); retract and measure rows after the date are COUNTED
271// so corrections stand beside deliveries. counts[RD_C_LISTED] stops at RD_LIST_CAP while counts[RD_C_LAND]
272// keeps counting: a cap reached in silence becomes a measurement nobody knows is partial. Returns the new offset.
273func rd_land_since(plan: *u8, n: i64, since: *u8, dom: *u8, out: *u8, o0: i64, cap: i64, counts: *i64) -> i64 {
274 var o: i64 = o0
275 let dbuf: *u8 = sys_mmap(RD_W_DATE)
276 var p: i64 = 0
277 while p < n {
278 let e: i64 = rd_line_end(plan, n, p)
279 if e - p > RD_LOG_PFX_LEN { if rd_slice_eq(plan, p, RD_LOG_PFX_LEN, RD_LOG_PFX) == 1 {
280 var q: i64 = p + RD_LOG_PFX_LEN
281 var ep: i64 = 0
282 var any: i64 = 0
283 var digits: i64 = 1
284 while digits == 1 {
285 if q >= e { digits = 0 } else {
286 let c: i64 = rd_byte(plan, q)
287 if c < RD_D0 { digits = 0 } else { if c > RD_D9 { digits = 0 } else { ep = ep * RD_BASE10 + (c - RD_D0); any = 1; q = q + 1 } }
288 }
289 }
290 var shaped: i64 = 0
291 if any == 1 { if q < e { if rd_byte(plan, q) == RD_PIPE { shaped = 1 } } }
292 if shaped == 1 {
293 q = q + 1
294 let rs: i64 = q
295 while q < e { if rd_byte(plan, q) == RD_PIPE { break } q = q + 1 }
296 let rn: i64 = q - rs
297 if q < e {
298 q = q + 1
299 let ks: i64 = q
300 while q < e { if rd_byte(plan, q) == RD_PIPE { break } q = q + 1 }
301 let kn: i64 = q - ks
302 var ts: i64 = q
303 if ts < e { ts = ts + 1 }
304 al_civil_date(ep, dbuf)
305 if rd_cmp_slice(dbuf, 0, RD_DATE_W, since) > 0 {
306 if rd_slice_eq(plan, ks, kn, RD_KIND_LAND) == 1 {
307 counts[RD_C_LAND] = counts[RD_C_LAND] + 1
308 if counts[RD_C_LISTED] < RD_LIST_CAP { if o + RD_ITEM_RESERVE + RD_TEXT_CAP * RD_ENT_MAX < cap {
309 counts[RD_C_LISTED] = counts[RD_C_LISTED] + 1
310 o = al_cat(out, o, "<li><b>" as *u8)
311 o = rd_esc_z(out, o, dom)
312 out[o] = RD_SPACE as u8; o = o + 1
313 o = rd_esc_html(out, o, plan, rs, rn)
314 o = al_cat(out, o, "</b> " as *u8)
315 o = al_cat(out, o, dbuf)
316 o = al_cat(out, o, ": " as *u8)
317 var tn: i64 = e - ts
318 var cut: i64 = 0
319 if tn > RD_TEXT_CAP { tn = RD_TEXT_CAP; cut = 1 }
320 o = rd_esc_html(out, o, plan, ts, tn)
321 if cut == 1 { o = al_cat(out, o, " [...]" as *u8) }
322 o = al_cat(out, o, "</li>" as *u8)
323 } }
324 }
325 if rd_slice_eq(plan, ks, kn, RD_KIND_RETRACT) == 1 { counts[RD_C_RETRACT] = counts[RD_C_RETRACT] + 1 }
326 if rd_slice_eq(plan, ks, kn, RD_KIND_MEASURE) == 1 { counts[RD_C_MEASURE] = counts[RD_C_MEASURE] + 1 }
327 }
328 }
329 }
330 } }
331 p = e + 1
332 }
333 out[o] = 0 as u8
334 return o
335}
336
337// (4) the block. Single-quoted attributes and rgb() colours only, so the output carries no double quote of its
338// own; every data byte passes rd_esc_html. Returns the new offset; out is NUL-terminated.
339func rd_emit(out: *u8, o0: i64, cap: i64, stamp: i64, since: *u8, tab: *u8, ns: i64, land: *u8, counts: *i64, plans_read: i64, plans_declared: i64) -> i64 {
340 var o: i64 = o0
341 let d: *u8 = sys_mmap(RD_W_DATE)
342 al_civil_date(stamp, d)
343 o = al_cat(out, o, "<div style='font:12px/1.6 ui-monospace,monospace;color:rgb(185,177,220);text-align:left;max-width:96vw;border:1px solid rgb(47,42,74);border-radius:4px;padding:8px 12px;margin:6px 0'><b>RELEASE " as *u8)
344 o = al_cat(out, o, d)
345 o = al_cat(out, o, " emit " as *u8)
346 o = al_catn(out, o, stamp)
347 o = al_cat(out, o, "</b> · since the last operator verdict (" as *u8)
348 if rd_byte(since, 0) == 0 { o = al_cat(out, o, "NEVER JUDGED" as *u8) } else { o = rd_esc_z(out, o, since) }
349 o = al_cat(out, o, "): " as *u8)
350 o = al_catn(out, o, counts[RD_C_LAND])
351 o = al_cat(out, o, " landed · " as *u8)
352 o = al_catn(out, o, counts[RD_C_RETRACT])
353 o = al_cat(out, o, " retracted · " as *u8)
354 o = al_catn(out, o, counts[RD_C_MEASURE])
355 o = al_cat(out, o, " measured · boards read " as *u8)
356 o = al_catn(out, o, plans_read)
357 o = al_cat(out, o, "/" as *u8)
358 o = al_catn(out, o, plans_declared)
359 o = al_cat(out, o, "<br><table style='border-collapse:collapse;margin:4px 0'><tr><th style='text-align:left;padding:0 10px 0 0'>subject</th><th style='text-align:left;padding:0 10px 0 0'>operator</th><th style='text-align:left;padding:0 10px 0 0'>on</th><th style='text-align:left;padding:0 10px 0 0'>referee percept</th><th style='text-align:left'>state</th></tr>" as *u8)
360 var i: i64 = 0
361 while i < ns {
362 if o + RD_ITEM_RESERVE >= cap { break }
363 let rec: *u8 = rd_rec(tab, i)
364 o = al_cat(out, o, "<tr><td style='padding:0 10px 0 0'" as *u8)
365 if rd_byte(rec, RD_F_DESC) != 0 {
366 o = al_cat(out, o, " title='" as *u8)
367 o = rd_esc_z(out, o, rd_str(rec, RD_F_DESC))
368 o = al_cat(out, o, "'" as *u8)
369 }
370 o = al_cat(out, o, ">" as *u8)
371 o = rd_esc_z(out, o, rd_str(rec, RD_F_SUBJ))
372 o = al_cat(out, o, "</td><td style='padding:0 10px 0 0'>" as *u8)
373 if rd_byte(rec, RD_F_OPSEEN) == 1 { o = rd_esc_z(out, o, rd_str(rec, RD_F_VERD)) } else { o = al_cat(out, o, "no verdict" as *u8) }
374 o = al_cat(out, o, "</td><td style='padding:0 10px 0 0'>" as *u8)
375 if rd_byte(rec, RD_F_OPSEEN) == 1 { o = rd_esc_z(out, o, rd_str(rec, RD_F_DATE)) } else { o = al_cat(out, o, "-" as *u8) }
376 o = al_cat(out, o, "</td><td style='padding:0 10px 0 0'>" as *u8)
377 if rd_byte(rec, RD_F_RSEEN) == 1 {
378 o = rd_esc_z(out, o, rd_str(rec, RD_F_RPCT))
379 o = al_cat(out, o, " (" as *u8)
380 o = rd_esc_z(out, o, rd_str(rec, RD_F_RTIER))
381 o = al_cat(out, o, ", " as *u8)
382 o = rd_esc_z(out, o, rd_str(rec, RD_F_RDATE))
383 o = al_cat(out, o, ")" as *u8)
384 } else { o = al_cat(out, o, "unjudged" as *u8) }
385 o = al_cat(out, o, "</td><td>" as *u8)
386 var accepted: i64 = 0
387 if rd_byte(rec, RD_F_OPSEEN) == 1 { if al_streq(rd_str(rec, RD_F_VERD), RD_V_ACCEPT) == 1 { accepted = 1 } }
388 if accepted == 1 { o = al_cat(out, o, "ACCEPTED BY OPERATOR (completion needs the referee row and the attestation: nx_accept emit)" as *u8) } else { o = al_cat(out, o, "INCOMPLETE" as *u8) }
389 o = al_cat(out, o, "</td></tr>" as *u8)
390 i = i + 1
391 }
392 o = al_cat(out, o, "</table>" as *u8)
393 if counts[RD_C_LISTED] > 0 {
394 o = al_cat(out, o, "<ul style='margin:4px 0;padding-left:18px'>" as *u8)
395 o = al_cat(out, o, land)
396 o = al_cat(out, o, "</ul>" as *u8)
397 }
398 if counts[RD_C_LAND] > counts[RD_C_LISTED] {
399 o = al_cat(out, o, "<i>and " as *u8)
400 o = al_catn(out, o, counts[RD_C_LAND] - counts[RD_C_LISTED])
401 o = al_cat(out, o, " more landed rows on the boards (listing capped at " as *u8)
402 o = al_catn(out, o, RD_LIST_CAP)
403 o = al_cat(out, o, ")</i>" as *u8)
404 }
405 o = al_cat(out, o, "<br>To judge this build, file ONE row (id, subject, verdict, date, actor, words, targets): nx_store_put knowledge/store/accept- put operator <subject>-b" as *u8)
406 o = al_catn(out, o, stamp)
407 o = al_cat(out, o, " <subject> ACCEPT-or-REJECT " as *u8)
408 o = al_cat(out, o, d)
409 o = al_cat(out, o, " operator "why" ga_accept_<subject> · a REJECT keeps the subject INCOMPLETE and its words drive the next lane; an ACCEPT completes only with the referee row and the seat attestation.</div>" as *u8)
410 out[o] = 0 as u8
411 return o
412}