code wiki / _hdl_build / nx_kindle_reextract_gate.nx
nx_kindle_reextract_gate.nx source
↩ module page · 90 lines · 6162 B
1import "nx_gate_gn.nx"
2// nx_kindle_reextract_gate.nx -- liar-kill gate for the MOBI "© still there" root cause: the cache-heal correctly
3// DECIDES to re-extract a stale (pre-rfix) kindle cache, but ms_handle_open's kf8->mobi chain was fooled by the
4// LEFTOVER stale book.json -- nx_kf8_book DECLINES a MOBI6 file without writing book.json, so the stale file's mere
5// existence made ms_run_extractor report "kf8 succeeded" and nx_mobi_book never ran -> stale mojibake served forever.
6// FIX = unlink the stale book.json BEFORE the chain. This gate reproduces BOTH: (A) the bug (no unlink -> stale
7// survives, no rfix) and (B) the fix (unlink -> mobi runs -> fresh book.json carries rfix). Uses the REAL extractors
8// on the real mobi fixture. expect_exit: 0 license_tier: ORIGINAL
9import "nx_syscalls.nx"
10
11func gp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
12func gslen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
13func ensure_dir(path: *u8) -> i64 { __syscall(258, 0-100, path, 0x1ed, 0, 0, 0); return 0 }
14func gunlink(path: *u8) -> i64 { __syscall(263, 0-100, path, 0, 0, 0, 0); return 0 }
15
16func spawn(path: *u8, a0: *u8, a1: *u8, redir: *u8) -> i64 {
17 let pid: i64 = sys_fork()
18 if pid == 0 {
19 let fd: i64 = sys_openat_wr(redir, 420)
20 if fd >= 0 { sys_dup3(fd, 1, 0); sys_dup3(fd, 2, 0) }
21 let argv: *i64 = sys_mmap(64) as *i64
22 argv[0] = path as i64; var n: i64 = 1
23 if (a0 as i64) != 0 { argv[n] = a0 as i64; n = n + 1 }
24 if (a1 as i64) != 0 { argv[n] = a1 as i64; n = n + 1 }
25 argv[n] = 0
26 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = "PATH=/usr/bin:/bin\x00" as *u8 as i64; envp[1] = 0
27 sys_execve(path, argv, envp)
28 sys_exit(127)
29 }
30 let st: *i64 = sys_mmap(16) as *i64
31 sys_wait4(pid, st, 0)
32 return (st[0] >> 8) & 0xff
33}
34func exists(path: *u8) -> i64 { let fd: i64 = sys_openat_rd(path); if fd >= 0 { sys_close(fd); return 1 } return 0 }
35func file_has(path: *u8, needle: *u8) -> i64 {
36 let lp: *i64 = sys_mmap(8) as *i64; lp[0] = 0
37 let b: *u8 = sys_read_file(path, lp)
38 if (b as i64) == 0 { return 0 }
39 let n: i64 = lp[0]; let nl: i64 = gslen(needle)
40 var i: i64 = 0
41 while i + nl <= n { var k: i64=0; var hit: i64=1; while k<nl { if (b[i+k]&0xff)!=(needle[k]&0xff){hit=0;k=nl}else{k=k+1} } if hit==1 {return 1} i=i+1 }
42 return 0
43}
44func write_stale(path: *u8) -> i64 {
45 let fd: i64 = sys_openat_wr(path, 0x1a4)
46 if fd < 0 { return 0-1 }
47 let s: *u8 = "{\"title\":\"STALE-PRE-RFIX\",\"author\":\"x\",\"format\":\"mobi\",\"chapters\":[{\"idx\":0,\"title\":\"x\",\"file\":\"chap0.txt\",\"chars\":9,\"is_nav\":0,\"fn\":[]}],\"nchapters\":1}\x00" as *u8
48 sys_write(fd, s, gslen(s)); sys_close(fd)
49 return 0
50}
51
52func main() -> i64 {
53 gp("=== nx_kindle_reextract_gate: stale MOBI cache must RE-EXTRACT (kf8 declines -> mobi runs) ===\n" as *u8)
54 let RUNNER: *u8 = "_offc/nx_sov_build_run.elf\x00" as *u8
55 let SCRATCH: *u8 = "knowledge/status/kindle_reextract_scratch.log\x00" as *u8
56 let FIX: *u8 = "knowledge/fixtures/nishi_mobi_fixture.mobi\x00" as *u8
57 let DIR: *u8 = "knowledge/staging/media/reader/kindle_reextract_test\x00" as *u8
58 let BJ: *u8 = "knowledge/staging/media/reader/kindle_reextract_test/book.json\x00" as *u8
59
60 // rebuild the real extractors so we test current source
61 spawn(RUNNER, "nx_kf8_book\x00" as *u8, 0 as *u8, SCRATCH)
62 spawn(RUNNER, "nx_mobi_book\x00" as *u8, 0 as *u8, SCRATCH)
63 ensure_dir("knowledge/staging/media/reader\x00" as *u8)
64 ensure_dir(DIR)
65
66 let pf: *i64 = sys_mmap(16) as *i64; pf[0]=0; pf[1]=0
67
68 // ---- PART A: reproduce the BUG (old chain, NO unlink) ----
69 write_stale(BJ)
70 spawn("_offc/nx_kf8_book.elf\x00" as *u8, FIX, "kindle_reextract_test\x00" as *u8, SCRATCH) // declines MOBI6, writes nothing
71 var haveA: i64 = exists(BJ) // old ms_run_extractor: book.json exists? -> "success"
72 // the buggy outcome: kf8 "succeeds" (stale json present) so mobi is skipped -> json is STILL the stale one
73 if haveA == 1 { if file_has(BJ, "\"rfix\":1" as *u8) == 0 { gp(" PART A (bug reproduced): stale json survives kf8-decline, NO rfix -> mobi would be skipped PASS\n" as *u8); pf[0]=pf[0]+1 } else { gp(" PART A unexpected: rfix already present\n" as *u8); pf[1]=pf[1]+1 } }
74 else { gp(" PART A unexpected: book.json missing\n" as *u8); pf[1]=pf[1]+1 }
75
76 // ---- PART B: the FIX (unlink stale FIRST, then the chain) ----
77 write_stale(BJ)
78 gunlink(BJ) // THE FIX
79 spawn("_offc/nx_kf8_book.elf\x00" as *u8, FIX, "kindle_reextract_test\x00" as *u8, SCRATCH) // declines, writes nothing
80 var haveB: i64 = exists(BJ) // book.json exists? -> NO (unlinked + kf8 declined)
81 if haveB == 0 { gp(" after unlink+kf8-decline: book.json absent -> mobi WILL run PASS\n" as *u8); pf[0]=pf[0]+1 } else { gp(" FAIL: book.json present after kf8 decline (unlink didn't take)\n" as *u8); pf[1]=pf[1]+1 }
82 spawn("_offc/nx_mobi_book.elf\x00" as *u8, FIX, "kindle_reextract_test\x00" as *u8, SCRATCH) // MOBI6 -> writes fresh book.json (rfix:1)
83 if exists(BJ) == 1 { gp(" mobi ran: book.json present PASS\n" as *u8); pf[0]=pf[0]+1 } else { gp(" FAIL: mobi did not write book.json\n" as *u8); pf[1]=pf[1]+1 }
84 if file_has(BJ, "\"rfix\":1" as *u8) == 1 { gp(" FRESH cache carries rfix:1 (re-extracted, mojibake-repaired) PASS\n" as *u8); pf[0]=pf[0]+1 } else { gp(" FAIL: rfix:1 absent -> NOT re-extracted\n" as *u8); pf[1]=pf[1]+1 }
85 if file_has(BJ, "STALE-PRE-RFIX" as *u8) == 0 { gp(" stale content gone PASS\n" as *u8); pf[0]=pf[0]+1 } else { gp(" FAIL: stale content survived\n" as *u8); pf[1]=pf[1]+1 }
86
87 gp("KINDLE-REEXTRACT-GATE pass="); gn(pf[0]); gp(" fail="); gn(pf[1])
88 if pf[1] == 0 { gp(" verdict=GREEN\n" as *u8); sys_exit(0); return 0 }
89 gp(" verdict=RED\n" as *u8); sys_exit(1); return 1
90}