code wiki / (root) / nx_jobfollow_lib.nx

nx_jobfollow_lib.nx source

↩ module page · 49 lines · 3560 B

1// nx_jobfollow_lib.nx -- THE ONE FOLLOWER of the tools edge's JOB-STARTED promotion. 2// 3// WHY THIS EXISTS. The edge may answer ANY tools/call with `JOB-STARTED id=<n>` instead of the organ's 4// own receipt; the real receipt lands in `_jobs/job_<n>.out`. A client that does not follow that pointer 5// reads the JOB id as if it were the ORGAN's id -- both are spelled `id=`, and every current reader 6// scans for that key UNANCHORED -- and then blames the SERVER for omitting fields the server never sent 7// on that line. 8// 9// MEASURED 2026-09-04, not theorised: nx_content_put_client posted a begin, received 10// `JOB-STARTED id=1788537161`, printed `CP-BEGIN id=1788537161 chunk_raw= nchunks=` and exited 5, while 11// the artifact held the true receipt `CP-BEGIN id=1788537161795845 ... chunk_raw=48402 nchunks=1`. 12// The job id is a PREFIX of the transfer id (both are epoch-derived), which is exactly why the wrong 13// number looked plausible enough to print as a fact. 14// ANCHOR THE PARSE ON THE REPORT LINE: jf_job_id matches the full literal `JOB-STARTED id=`, never a 15// bare `id=`, so a receipt that merely CONTAINS an id cannot be mistaken for a job pointer. 16// 17// ONE IMPLEMENTATION, TWO CALLERS, BY CONSTRUCTION. nx_content_put_client posts three times 18// (begin/chunk/commit); nx_content_get_client posts twice (begin/chunk). That is five call sites which 19// must agree on what a receipt IS. A shared post-then-follow makes disagreement impossible instead of 20// making it something two organs have to remember to do the same way. 21// 22// THE POLL IS NOT A RETRY, AND THE DISTINCTION IS LOAD-BEARING ON A DEGRADED ARRAY. A retry re-issues 23// work whose outcome is UNKNOWN; this waits for a result the server has already ACCEPTED and promised to 24// write. The original request is never re-posted -- which matters because a chunk upload is not 25// idempotent and the hostops lane has filed that a retry loop against the current array is actively 26// harmful. If the budget expires the caller is told the work is STILL PENDING, never that it failed: 27// an unfinished job and a failed job demand opposite actions. 28// 29// NO SILENT CAP: exhausting the poll budget returns 0 and ANNOUNCES the job id, so the caller can read 30// the artifact by hand rather than concluding the organ died. 31// license_tier: ORIGINAL No hw writes (Rule 26). 32import "nx_syscalls.nx" 33import "nx_https_post_lib.nx" 34import "nx_jobfollow_parse.nx" // the pure half: anchored job-id parse, readiness test, buffer arithmetic 35 36func jf_post_follow(store_i: i64, url: *u8, req: *u8, reqlen: i64, 37 out: *u8, outcap: i64, 38 readcap: *u8, readcaplen: i64, 39 polls: i64, sleep_ms: i64) -> i64 { 40 // DELEGATES. The poll loop and the request it builds now live ONCE, in nx_https_post_lib, so that 41 // the shared post path and this explicit entry point cannot drift apart. This function used to 42 // carry its own copy; two implementations of one invariant is the duplicate-ruler defect, and the 43 // whole reason the split into nx_jobfollow_parse exists is so there did not have to be two. 44 // DI4 client half (2026-09-05): a keyed request whose reply the edge lost is re-posted through the ONE 45 // re-issue loop in nx_https_post_lib (replay-by-construction); an unkeyed one still gets its 503 back. 46 let n: i64 = hp_post_json_reissue(store_i, url, 0, 0, req, reqlen, out, outcap, polls, sleep_ms) 47 if n <= 0 { return n } 48 return hp_follow_job(store_i, url, out, outcap, n, readcap, readcaplen, polls, sleep_ms) 49}