code wiki / (root) / nx_writehub_grow.nx

nx_writehub_grow.nx source

↩ module page · 192 lines · 11092 B

1// nx_writehub_grow.nx -- WRITING HUB rung R3: the sovereign SYSTEM-DATA GROWTH organ. Operator 2026-06-30: 2// "integrate with system data and EXPAND it so we can grow clothing etc." Reads a new catalog entry 3// (knowledge/staging/writehub_grow_req.txt, key=value lines) and emits an ADDITIVE svc-content add-request: 4// a NEW <entity>_key + prompt_fragment + zimage_sentence (the writer<->image bridge) + keyword aliases. 5// Stages the exact request to knowledge/staging/writehub_grow_out.json and POSTs it to svc-content:8300 6// /v2/content/add-<entity> over sovereign HTTP (127.0.0.1, no curl/shell). Additive ONLY (Rule 13: a NEW key, 7// never an overwrite; Rule 25: MORE options, never fewer). Composes nx_http_client (same primitive as 8// nx_writehub_seat = consolidation). HONEST: the live add-outfit endpoint sets only 4 cols (no zimage_sentence) 9// -> the staged .json carries it for the EXTENDED endpoint (API to extend, build-if-missing); the live POST body 10// uses exactly the endpoint's fields so it never 422s. The add-LOCATION endpoint has a base_prompt-vs- 11// prompt_fragment column bug -> grow outfits/poses until it is fixed. license_tier: ORIGINAL module: nishi-core.writehub.grow 12import "nx_http_client.nx" 13import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host 14import "nx_gate.nx" 15const K_MAGIC_262144: i64 = 262144 16const K_MAGIC_4096: i64 = 4096 17const K_MAGIC_2048: i64 = 2048 18const K_MAGIC_65536: i64 = 65536 19const K_MAGIC_131072: i64 = 131072 20const K_MAGIC_8300: i64 = 8300 21const K_MAGIC_1048576: i64 = 1048576 22 23func wg_strlen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 24func wg_atoi3(buf: *u8, off: i64) -> i64 { var v: i64=0; var i: i64=0; while i<3 { let d: i64=buf[off+i] as i64; if d>=48 { if d<=57 { v=v*10+(d-48) } } i=i+1 } return v } 25func wg_drain(fd: i64, buf: *u8, cap: i64) -> i64 { var off: i64=0; var go: i64=1; while go==1 { if off>=cap { go=0 } else { let r: i64=sys_read(fd, ((buf as i64)+off) as *u8, cap-off); if r<=0 { go=0 } else { off=off+r } } } return off } 26func wg_app(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var i: i64=0; while s[i]!=(0 as u8){ dst[o]=s[i]; o=o+1; i=i+1 } return o } 27func wg_appc(dst: *u8, off: i64, ch: i64) -> i64 { dst[off]=ch as u8; return off+1 } 28func wg_app_range(dst: *u8, off: i64, src: *u8, a: i64, b: i64) -> i64 { var o: i64=off; var i: i64=a; while i<b { dst[o]=src[i]; o=o+1; i=i+1 } return o } 29// JSON-escape src[0..len) into dst at off 30func wg_app_esc(dst: *u8, off: i64, src: *u8, len: i64) -> i64 { 31 var o: i64=off; var i: i64=0 32 while i<len { 33 let c: i64 = src[i] as i64 34 if c==34 { o=wg_appc(dst,o,92); o=wg_appc(dst,o,34) } 35 else { if c==92 { o=wg_appc(dst,o,92); o=wg_appc(dst,o,92) } 36 else { if c==10 { o=wg_appc(dst,o,92); o=wg_appc(dst,o,110) } 37 else { if c==13 { o=o } 38 else { if c<32 { o=o } else { dst[o]=src[i]; o=o+1 } } } } } 39 i=i+1 40 } 41 return o 42} 43func wg_appq_esc(dst: *u8, off: i64, src: *u8, len: i64) -> i64 { var o: i64=off; o=wg_appc(dst,o,34); o=wg_app_esc(dst,o,src,len); o=wg_appc(dst,o,34); return o } 44func wg_appq(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; o=wg_appc(dst,o,34); o=wg_app(dst,o,s); o=wg_appc(dst,o,34); return o } 45 46// key= match at i (i must be a line start); return offset to the char after '=' (0 = no match) 47func wg_kv_match(buf: *u8, i: i64, n: i64, key: *u8) -> i64 { 48 var j: i64=0 49 while key[j]!=(0 as u8) { if i+j>=n { return 0 } if buf[i+j]!=key[j] { return 0 } j=j+1 } 50 if i+j>=n { return 0 } 51 if buf[i+j]!=(61 as u8) { return 0 } 52 return j+1 53} 54// copy the value of `key` (first occurrence at a line start) into out (null-term); return length 55func wg_kv_get(buf: *u8, n: i64, key: *u8, out: *u8, outcap: i64) -> i64 { 56 var i: i64=0; var atline: i64=1 57 while i<n { 58 if atline==1 { 59 let off: i64 = wg_kv_match(buf, i, n, key) 60 if off>0 { 61 var s: i64=i+off; var o: i64=0; var go: i64=1 62 while go==1 { 63 if s>=n { go=0 } else { 64 let c: i64=buf[s] as i64 65 if c==10 { go=0 } else { if c==13 { go=0 } else { if o<outcap-1 { out[o]=buf[s]; o=o+1 } s=s+1 } } 66 } 67 } 68 out[o]=0 as u8 69 return o 70 } 71 } 72 if buf[i]==(10 as u8) { atline=1 } else { atline=0 } 73 i=i+1 74 } 75 out[0]=0 as u8 76 return 0 77} 78// append ["a","b",...] from a comma-separated list (spaces after commas trimmed) 79func wg_app_keywords(dst: *u8, off: i64, kw: *u8, kwlen: i64) -> i64 { 80 var o: i64=off 81 o=wg_appc(dst,o,91) 82 var i: i64=0; var tokstart: i64=0; var first: i64=1; var go: i64=1 83 while go==1 { 84 var atend: i64=0 85 if i>=kwlen { atend=1 } 86 var boundary: i64=atend 87 if atend==0 { if kw[i]==(44 as u8) { boundary=1 } } 88 if boundary==1 { 89 var ts: i64=tokstart // trim leading spaces of the token [tokstart..i) 90 var found: i64=0 91 while found==0 { if ts>=i { found=1 } else { if kw[ts]==(32 as u8) { ts=ts+1 } else { found=1 } } } 92 if i>ts { 93 if first==0 { o=wg_appc(dst,o,44) } 94 o=wg_appc(dst,o,34); o=wg_app_range(dst,o,kw,ts,i); o=wg_appc(dst,o,34) 95 first=0 96 } 97 tokstart=i+1 98 } 99 if atend==1 { go=0 } else { i=i+1 } 100 } 101 o=wg_appc(dst,o,93) 102 return o 103} 104 105func main() -> i64 { 106 let rfd: i64 = sys_openat_rd("knowledge/staging/writehub_grow_req.txt" as *u8) 107 if rfd < 0 { gw("GROW: no knowledge/staging/writehub_grow_req.txt (key=value lines: entity/key/name/category/fragment/zimage/keywords)\n" as *u8); sys_exit(1); return 1 } 108 let rb: *u8 = sys_mmap(K_MAGIC_262144) 109 let rn: i64 = sys_read(rfd, rb, K_MAGIC_262144) 110 sys_close(rfd) 111 if rn <= 0 { gw("GROW: empty grow_req\n" as *u8); sys_exit(1); return 1 } 112 113 let entity: *u8 = sys_mmap(64) 114 let key: *u8 = sys_mmap(256) 115 let name: *u8 = sys_mmap(512) 116 let category: *u8 = sys_mmap(256) 117 let frag: *u8 = sys_mmap(K_MAGIC_4096) 118 let zimg: *u8 = sys_mmap(K_MAGIC_4096) 119 let kw: *u8 = sys_mmap(K_MAGIC_2048) 120 wg_kv_get(rb, rn, "entity" as *u8, entity, 64) 121 let keyn: i64 = wg_kv_get(rb, rn, "key" as *u8, key, 256) 122 wg_kv_get(rb, rn, "name" as *u8, name, 512) 123 wg_kv_get(rb, rn, "category" as *u8, category, 256) 124 let fragn: i64 = wg_kv_get(rb, rn, "fragment" as *u8, frag, K_MAGIC_4096) 125 let zimgn: i64 = wg_kv_get(rb, rn, "zimage" as *u8, zimg, K_MAGIC_4096) 126 let kwn: i64 = wg_kv_get(rb, rn, "keywords" as *u8, kw, K_MAGIC_2048) 127 128 if keyn <= 0 { gw("GROW: REJECT -- missing required 'key' (additive growth needs a NEW unique key, Rule 13)\n" as *u8); sys_exit(2); return 2 } 129 if fragn <= 0 { gw("GROW: REJECT -- missing required 'fragment' (prompt_fragment NOT NULL)\n" as *u8); sys_exit(2); return 2 } 130 131 // resolve entity -> path + key-field (default outfit). pose shares the same shape. 132 var path: *u8 = "/v2/content/add-outfit" as *u8 133 var keyfield: *u8 = "outfit_key" as *u8 134 if entity[0]==(112 as u8) { path = "/v2/content/add-pose" as *u8; keyfield = "pose_key" as *u8 } // 'p' -> pose 135 136 // build the LIVE POST body = EXACTLY the endpoint's fields (no extra -> never 422) 137 let body: *u8 = sys_mmap(K_MAGIC_65536) 138 var bo: i64 = 0 139 bo=wg_appc(body,bo,123) 140 bo=wg_appq(body,bo,keyfield); bo=wg_appc(body,bo,58); bo=wg_appq_esc(body,bo,key,wg_strlen(key)) 141 bo=wg_appc(body,bo,44); bo=wg_appq(body,bo,"name" as *u8); bo=wg_appc(body,bo,58); bo=wg_appq_esc(body,bo,name,wg_strlen(name)) 142 bo=wg_appc(body,bo,44); bo=wg_appq(body,bo,"prompt_fragment" as *u8); bo=wg_appc(body,bo,58); bo=wg_appq_esc(body,bo,frag,fragn) 143 bo=wg_appc(body,bo,44); bo=wg_appq(body,bo,"category" as *u8); bo=wg_appc(body,bo,58); bo=wg_appq_esc(body,bo,category,wg_strlen(category)) 144 bo=wg_appc(body,bo,44); bo=wg_appq(body,bo,"keywords" as *u8); bo=wg_appc(body,bo,58); bo=wg_app_keywords(body,bo,kw,kwn) 145 bo=wg_appc(body,bo,125) 146 body[bo]=0 as u8 147 148 // build the FULL staged entry = POST body + zimage_sentence (the image bridge, for the extended endpoint) 149 let full: *u8 = sys_mmap(K_MAGIC_131072) 150 var fo: i64 = 0 151 fo=wg_appc(full,fo,123) 152 fo=wg_appq(full,fo,keyfield); fo=wg_appc(full,fo,58); fo=wg_appq_esc(full,fo,key,wg_strlen(key)) 153 fo=wg_appc(full,fo,44); fo=wg_appq(full,fo,"name" as *u8); fo=wg_appc(full,fo,58); fo=wg_appq_esc(full,fo,name,wg_strlen(name)) 154 fo=wg_appc(full,fo,44); fo=wg_appq(full,fo,"prompt_fragment" as *u8); fo=wg_appc(full,fo,58); fo=wg_appq_esc(full,fo,frag,fragn) 155 fo=wg_appc(full,fo,44); fo=wg_appq(full,fo,"zimage_sentence" as *u8); fo=wg_appc(full,fo,58); fo=wg_appq_esc(full,fo,zimg,zimgn) 156 fo=wg_appc(full,fo,44); fo=wg_appq(full,fo,"category" as *u8); fo=wg_appc(full,fo,58); fo=wg_appq_esc(full,fo,category,wg_strlen(category)) 157 fo=wg_appc(full,fo,44); fo=wg_appq(full,fo,"enabled" as *u8); fo=wg_appc(full,fo,58); fo=wg_app(full,fo,"1" as *u8) 158 fo=wg_appc(full,fo,44); fo=wg_appq(full,fo,"keywords" as *u8); fo=wg_appc(full,fo,58); fo=wg_app_keywords(full,fo,kw,kwn) 159 fo=wg_appc(full,fo,125) 160 full[fo]=0 as u8 161 162 let sfd: i64 = sys_openat_wr("knowledge/staging/writehub_grow_out.json" as *u8, 0x1a4) 163 if sfd >= 0 { sys_write(sfd, full, fo); sys_close(sfd) } 164 165 gw("GROW entity=" as *u8); gw(entity); gw(" key=" as *u8); gw(key); gw(" (additive, new key)\n" as *u8) 166 gw(" staged full entry (with zimage_sentence bridge) -> knowledge/staging/writehub_grow_out.json\n" as *u8) 167 gw(" POST body: " as *u8); gw(body); gw("\n" as *u8) 168 169 // POST the additive add-request to svc-content:8300 over sovereign HTTP 170 let addr: *u8 = sys_mmap(16) 171 nx_http_client_sockaddr_ipv4(addr, 127, 0, 0, 1, K_MAGIC_8300) 172 let fd: i64 = sys_socket(2, 1, 0) 173 if fd < 0 { gw("GROW socket-fail\n" as *u8); sys_exit(3); return 3 } 174 if nx_connect_bounded(fd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { 175 sys_close(fd) 176 gw("GROW -> CONN-FAIL (svc-content not listening on 127.0.0.1:8300) -- request is BUILT+staged; bring svc-content up to grow live\n" as *u8) 177 sys_exit(2); return 2 178 } 179 let ct: *u8 = "application/json" as *u8 180 let req: *u8 = sys_mmap(K_MAGIC_131072) 181 let reqlen: i64 = nx_http_client_build_request_post(path, wg_strlen(path), "localhost" as *u8, 9, ct, wg_strlen(ct), body, bo, req) 182 sys_write(fd, req, reqlen) 183 let cap: i64 = K_MAGIC_1048576 184 let buf: *u8 = sys_mmap(cap) 185 let nn: i64 = wg_drain(fd, buf, cap) 186 sys_close(fd) 187 var status: i64 = 0 188 if nn > 12 { status = wg_atoi3(buf, 9) } 189 gw("GROW POST " as *u8); gw(path); gw(" -> status=" as *u8); gn(status); gw(" bytes=" as *u8); gn(nn); gw("\n" as *u8) 190 if status >= 200 { if status < 300 { gw("GROW OK -- catalog grew by one (additive); the image-gen now sees it via prompt_fragment\n" as *u8); sys_exit(0); return 0 } } 191 sys_exit(3); return 3 192}