nx_filecopy.nx
buildroot/runtime/nx_filecopy.nx
about
nx_filecopy.nx -- A COPY THAT PROVES IT COPIED (2026-08-07; STREAMING 2026-08-13).
WHY IT EXISTS: `nx_gate_dry_apply` produces a migration candidate and `nx_gate_migrate verify` proves
it judge-equivalent (exit code, verdict and the whole PASS/FAIL vector identical) -- and then the
migration cannot be APPLIED, because nothing in the estate moves bytes from one path to another.
`nx_filehash cmp` can tell you two files match; no organ can MAKE them match. A capability search over
938 tools returned no copier. So a proven-safe automated migration path was blocked on the most
ordinary primitive there is.
*AN AUTOMATED PATH THAT CANNOT APPLY ITS OWN RESULT IS A DEMONSTRATION, NOT A TOOL.*
The alternative was round-tripping the candidate through JSON via read+write, and this codebase has
already banked that chunked JSON re-assembly is NOT byte-faithful. A silent one-byte corruption in a
GATE would be the mutation-class defect the estate has been bitten by twice.
SO IT VERIFIES BY CONSTRUCTION: after writing, it RE-READS the destination and compares every byte to
the source. A copy that did not land, landed short, or landed altered REPORTS FAILURE and exits
non-zero. **AN UNVERIFIED COPY IS INDISTINGUISHABLE FROM A SUCCESSFUL ONE UNTIL SOMETHING DOWNSTREAM
BREAKS** -- which for a gate means a silently weakened guard, the worst possible failure mode.
2026-08-13 STREAMING FIX (debt 1786597688): v1 read the WHOLE source into one 8MiB buffer and treated
that read as the file -- so any source >8MiB was SILENTLY TRUNCATED and then "verified=1" against its
own truncation (it verified what it copied, not the source; caught when an 8.8MB model banked as an
8,388,608-byte corrupt copy). Now: the 8MiB buffer is a CHUNK, the copy streams to source EOF, and the
verify re-reads BOTH files in aligned chunks to EOF and requires identical totals. The buffer can
never again masquerade as the population.
OVERWRITE IS EXPLICIT. Clobbering an existing destination requires the literal 3rd arg `overwrite`,
because for a migration the clobber IS the intent and for everything else it is an accident.
nx_filecopy <src> <dst> [overwrite]
exit 0 copied+verified | 1 verify FAILED | 2 io error | 3 usage/refused
license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
dependencies 1 imports · 0 importers
imports: nx_syscalls.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 35 | const FC_CAP: i64 = 8388608 // CHUNK size, not a file-size cap -- both copy and verify stream to EOF |
functions
| 37 | func fcp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } |
| 38 | func fce(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(2,s,n); return 0 } |
| 39 | func fcn(v: i64) -> i64 |
| 52 | func fc_exists(p: *u8) -> i64 { let fd: i64 = sys_openat_rd(p); if fd < 0 { return 0 } sys_close(fd); return 1 } |
| 53 | func fc_streq(a: *u8, b: *u8) -> i64 called by 1: main |
| 60 | func fc_chunk(fd: i64, buf: *u8, cap: i64) -> i64 |
| 71 | func main(argc: i64, argv: *i64) -> i64 |