nx_jobfollow_parse.nx source
↩ module page · 157 lines · 8600 B
1// nx_jobfollow_parse.nx -- THE PURE HALF of the job-pointer follower: parsing and buffer arithmetic,
2// with NO transport dependency at all.
3//
4// WHY IT IS A SEPARATE FILE. The follower has to live in TWO places: nx_jobfollow_lib (which an
5// explicit caller composes) and nx_https_post_lib (so that EVERY caller of hp_post_json inherits it
6// without editing 81 organs). But nx_jobfollow_lib imports nx_https_post_lib to do its polling, so
7// putting the follow INTO the post lib would close an import cycle. Splitting the pure half out is the
8// standard break: this file imports only nx_syscalls, so both layers above can share ONE implementation
9// and there is still exactly one job-id parser in the estate.
10//
11// AND THE PURE HALF IS THE HALF WORTH GATING. Everything here is buffer arithmetic over inputs a test
12// can construct, so a gate walks it without opening a socket -- the same reason the TLS fragmentation
13// split was pulled out of its loop on the same day.
14//
15// THE ANCHORED PARSE IS THE POINT. jf_job_id matches the whole literal `JOB-STARTED id=`, never a bare
16// `id=`. MEASURED 2026-09-04: a client scanning unanchored read the JOB id as the ORGAN id -- they are
17// both epoch-derived, so 1788537161 looked like a plausible prefix of the real 1788537161795845 -- and
18// then reported a receipt that never existed.
19// license_tier: ORIGINAL No hw writes (Rule 26).
20import "nx_syscalls.nx"
21
22const JF_PATHCAP: i64 = 256
23const JF_REQCAP: i64 = 4096
24const JF_QUOTE: i64 = 34
25const JF_BACKSLASH: i64 = 92
26const JF_SPACE: i64 = 32
27const JF_DEL: i64 = 127
28// ---- THE KEYED RE-ISSUE (dataio DI4, client half, 2026-09-05) ------------------------------------------------
29// The edge answers an unsafe call whose backend ACCEPTED the request but produced no reply inside the edge window
30// with a problem+json carrying `"stage":"read-after-accept"` ("Outcome Unknown"). Re-posting blind double-applies;
31// not re-posting loses a receipt the server already wrote. The tools/call transport now reads params._idem and
32// answers a repeated key with `NX-IDEM REPLAY ... job=<id>` (the FIRST outcome, never a second execution) or
33// `NX-IDEM INFLIGHT` (the first is still running). So a re-post is safe BY CONSTRUCTION exactly when the request
34// carries `_idem`, and never otherwise. MEASURED 2026-09-05: nx_content_put_client posted a begin for a SOURCE
35// destination (the receiver hashes the 43 KB incumbent before answering), the edge answered Outcome Unknown, the
36// client printed `CP-BEGIN id= chunk_raw=` and exited 5 -- while the receiver had already accepted the transfer.
37const JF_UNKNOWN: *u8 = "\x22stage\x22:\x22read-after-accept\x22" as *u8
38const JF_KEYED: *u8 = "\x22_idem\x22" as *u8
39const JF_REPLAY: *u8 = "NX-IDEM REPLAY" as *u8
40const JF_INFLIGHT: *u8 = "NX-IDEM IN-FLIGHT" as *u8 // the transport's literal (nx_tools_api ta_mcp_call), hyphenated -- verified by grep, not recalled
41
42func jf_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
43func jf_eo(s: *u8) -> i64 { sys_write(2, s, jf_slen(s)); return 0 }
44func jf_enum(v: i64) -> i64 {
45 let b: *u8 = sys_mmap(32)
46 var m: i64 = v
47 var i: i64 = 31
48 if m == 0 { i = i - 1; b[i] = 48 as u8 }
49 while m > 0 { i = i - 1; b[i] = (48 + (m % 10)) as u8; m = m / 10 }
50 sys_write(2, (b as i64 + i) as *u8, 31 - i)
51 return 0
52}
53// index just past needle, or -1. Exits on a FLAG, never by clobbering the cursor -- the loop-exit
54// sentinel defect this estate has recorded four times in one day.
55func jf_find(buf: *u8, n: i64, needle: *u8) -> i64 {
56 let m: i64 = jf_slen(needle)
57 if m == 0 { return 0 - 1 }
58 var i: i64 = 0
59 var hit: i64 = 0 - 1
60 while i + m <= n {
61 var j: i64 = 0
62 var same: i64 = 1
63 while j < m { if buf[i + j] != needle[j] { same = 0; j = m } else { j = j + 1 } }
64 if same == 1 { if hit < 0 { hit = i + m } }
65 i = i + 1
66 }
67 return hit
68}
69func jf_has(buf: *u8, n: i64, needle: *u8) -> i64 { if jf_find(buf, n, needle) >= 0 { return 1 } return 0 }
70// THE ANCHORED job-pointer read. Matches the whole report token, never a bare `id=`.
71func jf_job_id(buf: *u8, n: i64) -> i64 {
72 var p: i64 = jf_find(buf, n, "JOB-STARTED id=" as *u8)
73 if p < 0 {
74 // the transport's REPLAY of a keyed call names the FIRST execution's job: `NX-IDEM REPLAY key=... job=<id>`;
75 // anchored on the report literal and then on its own ` job=` field, never a bare `id=`
76 let r: i64 = jf_find(buf, n, JF_REPLAY)
77 if r < 0 { return 0 - 1 }
78 let q: i64 = jf_find(((buf as i64) + r) as *u8, n - r, " job=" as *u8)
79 if q < 0 { return 0 - 1 }
80 p = r + q
81 }
82 var i: i64 = p
83 var v: i64 = 0
84 var got: i64 = 0
85 while i < n {
86 let c: i64 = buf[i] as i64
87 if c < 48 { i = n } else { if c > 57 { i = n } else { v = v * 10 + (c - 48); got = 1; i = i + 1 } }
88 }
89 if got == 0 { return 0 - 1 }
90 return v
91}
92func jf_append(out: *u8, o: i64, s: *u8) -> i64 {
93 var p: i64 = o
94 var i: i64 = 0
95 let n: i64 = jf_slen(s)
96 while i < n { out[p] = s[i]; p = p + 1; i = i + 1 }
97 return p
98}
99func jf_append_num(out: *u8, o: i64, v: i64) -> i64 {
100 if v == 0 { out[o] = 48 as u8; return o + 1 }
101 let tmp: *u8 = sys_mmap(32)
102 var m: i64 = v
103 var i: i64 = 0
104 while m > 0 { tmp[i] = (48 + (m % 10)) as u8; m = m / 10; i = i + 1 }
105 var p: i64 = o
106 var j: i64 = 0
107 while j < i { out[p] = tmp[i - 1 - j]; p = p + 1; j = j + 1 }
108 return p
109}
110// REFUSE RATHER THAN ESCAPE, on the sibling clients' precedent: a capability token comes from a
111// known-safe alphabet, so a byte needing JSON escaping means the input is wrong, not that an escaper
112// is owed. A hand-rolled escaper fails silently and yields a request rejected for the wrong reason.
113func jf_json_safe(s: *u8, n: i64) -> i64 {
114 var i: i64 = 0
115 while i < n {
116 let c: i64 = s[i] as i64
117 if c == JF_QUOTE { return 0 }
118 if c == JF_BACKSLASH { return 0 }
119 if c < JF_SPACE { return 0 }
120 if c == JF_DEL { return 0 }
121 i = i + 1
122 }
123 return 1
124}
125// Is the artifact readable yet? nx_fs answers ABSENT before the job writes and EMPTY while it is still
126// zero-length, so readiness is the ABSENCE of both -- a positive test on a body whose shape we do not
127// yet know would pass on the error envelope itself.
128func jf_artifact_ready(buf: *u8, n: i64) -> i64 {
129 if n <= 0 { return 0 }
130 if jf_has(buf, n, "NX-FS DENIED" as *u8) == 1 { return 0 - 1 }
131 if jf_has(buf, n, "NX-FS ABSENT" as *u8) == 1 { return 0 }
132 if jf_has(buf, n, "NX-FS EMPTY" as *u8) == 1 { return 0 }
133 return 1
134}
135// 1 iff the reply is the edge's outcome-unknown envelope (503 read-after-accept)
136func jf_outcome_unknown(buf: *u8, n: i64) -> i64 { if n <= 0 { return 0 } return jf_has(buf, n, JF_UNKNOWN) }
137// 1 iff the REQUEST carries an idempotency key -- the only condition under which a re-post is safe
138func jf_req_keyed(req: *u8, n: i64) -> i64 { if n <= 0 { return 0 } return jf_has(req, n, JF_KEYED) }
139// 1 iff the transport says the keyed call's first execution is still running: wait one interval, ask again
140func jf_inflight(buf: *u8, n: i64) -> i64 { if n <= 0 { return 0 } return jf_has(buf, n, JF_INFLIGHT) }
141// THE DECISION, pure and gate-walkable: re-post iff (outcome unknown OR in-flight) AND the request is keyed.
142// An unkeyed request is NEVER re-posted, whatever the reply says: its 503 is returned as the reply, honestly.
143func jf_should_reissue(buf: *u8, n: i64, req: *u8, reqlen: i64) -> i64 {
144 if jf_req_keyed(req, reqlen) == 0 { return 0 }
145 // NO REPLY AT ALL is the same uncertainty as Outcome Unknown, and the key is what makes re-posting safe -- MEASURED
146 // 2026-09-05: under a load1 of 19 the FIRST post of a run failed at the transport (`CC-BEGIN: post failed`) on
147 // five of eleven transfers while every later post of the same run succeeded; the earlier rule ("an empty reply is a
148 // transport error, not an accepted call") was true and beside the point for a keyed request.
149 if n <= 0 { return 1 }
150 if jf_outcome_unknown(buf, n) == 1 { return 1 }
151 if jf_inflight(buf, n) == 1 { return 1 }
152 return 0
153}
154// POST, and if the edge answered with a job pointer, wait for the artifact and return THAT as the
155// receipt. Returns the receipt length in `out`, or 0. `readcap` may be zero length: then a promotion is
156// reported as UNFOLLOWABLE rather than silently mis-parsed -- naming the missing input is the whole
157// point, since without it the caller would otherwise read the job id as the organ's id.