code wiki / (root) / nx_card_import_gate.nx

nx_card_import_gate.nx source

↩ module page · 181 lines · 13381 B

1// nx_card_import_gate.nx -- the REFEREE for companionchat CC2 (ccm_card_import in nx_card_compile.nx): a Tavern V2 PNG card 2// (base64 JSON in a tEXt chunk keyed chara) imports to the card SSOT with every field MAPPED or REFUSED BY NAME, the age gate 3// stays fail-closed (no age = refuse, under 18 = refuse, a card-declared age beats the argument), the compiled persona carries 4// the card's own prose and NONE of the native defaults, and export round-trips the V2 source byte-stable. 5// END-TO-END: forks the PROMOTED nx_card_compile.elf (what the operator runs), fixtures built at run time under /tmp/<gate>/ 6// with nx_png_chunks' writer and base64.nx -- no checked-in binary fixture. license_tier: ORIGINAL expect_exit: 0 7import "nx_syscalls.nx" 8import "nx_gate_verdict.nx" 9import "nx_tool_run.nx" 10import "nx_png_chunks.nx" 11import "base64.nx" 12 13const CG_ELF: *u8 = "/volume1/homes/elderwesto/nishihost/nx_card_compile.elf" as *u8 14const CG_DIR: *u8 = "/tmp/nx_card_import_gate" as *u8 15const CG_PNG: *u8 = "/tmp/nx_card_import_gate/mira.png" as *u8 16const CG_PNG_MINOR: *u8 = "/tmp/nx_card_import_gate/minor.png" as *u8 17const CG_PNG_COMMENT: *u8 = "/tmp/nx_card_import_gate/comment.png" as *u8 18const CG_PNG_CUT: *u8 = "/tmp/nx_card_import_gate/cut.png" as *u8 19const CG_NOT_PNG: *u8 = "/tmp/nx_card_import_gate/notes.txt" as *u8 20const CG_SSOT: *u8 = "/tmp/nx_card_import_gate/elara_card.json" as *u8 21const CG_V2: *u8 = "/tmp/nx_card_import_gate/elara_card.v2.json" as *u8 22const CG_PERSONA: *u8 = "/tmp/nx_card_import_gate/elara_persona.txt" as *u8 23const CG_SSOT_BAK: *u8 = "/tmp/nx_card_import_gate/elara_card.json.bak-preimport" as *u8 24const CG_PERSONA_BAK: *u8 = "/tmp/nx_card_import_gate/elara_persona.txt.bak-precard" as *u8 25const CG_EXPORT: *u8 = "/tmp/nx_card_import_gate/exported.json" as *u8 26const CG_MODE_DIR: i64 = 511 27const CG_MODE_FILE: i64 = 420 28const CG_BUF: i64 = 65536 29const CG_OUT: i64 = 65536 30const CG_TMO_MS: i64 = 10000 31const CG_ARGV_CAP: i64 = 8 32const CG_I64: i64 = 8 33const CG_CUT_TAIL: i64 = 40 34const CG_EXIT_REFUSED: i64 = 3 35const CG_IHDR_LEN: i64 = 13 36const CG_JSON: *u8 = "{\"spec\":\"chara_card_v2\",\"spec_version\":\"2.0\",\"data\":{\"name\":\"Mira Vale\",\"description\":\"A marine biologist who lives in a lighthouse and talks to the tide.\",\"personality\":\"curious, dry-witted, gentle\",\"scenario\":\"You meet her on the pier at dusk.\",\"first_mes\":\"Oh -- you came.\",\"mes_example\":\"\",\"creator_notes\":\"fixture card\",\"system_prompt\":\"\",\"post_history_instructions\":\"\",\"alternate_greetings\":[],\"tags\":[\"scientist\",\"lighthouse\"],\"creator\":\"fixture\",\"character_version\":\"1.0\",\"extensions\":{},\"character_book\":{\"name\":\"pier lore\",\"entries\":[]}}}" as *u8 37const CG_JSON_MINOR: *u8 = "{\"spec\":\"chara_card_v2\",\"spec_version\":\"2.0\",\"data\":{\"name\":\"Too Young\",\"age\":17,\"description\":\"a fixture that must be refused\",\"personality\":\"n/a\",\"scenario\":\"n/a\",\"first_mes\":\"\",\"mes_example\":\"\",\"tags\":[]}}" as *u8 38const CG_COMMENT_TEXT: *u8 = "Comment" as *u8 39const CG_COMMENT_BODY: *u8 = "hello" as *u8 40 41func cg_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 42func cg_find(buf: *u8, n: i64, needle: *u8) -> i64 { 43 let nl: i64 = cg_len(needle) 44 if nl <= 0 { return 0 - 1 } 45 var i: i64 = 0 46 while i + nl <= n { 47 var j: i64 = 0 48 var same: i64 = 1 49 var scan: i64 = 1 50 while scan == 1 { if j >= nl { scan = 0 } else { if buf[i + j] != needle[j] { same = 0; scan = 0 } else { j = j + 1 } } } 51 if same == 1 { return i } 52 i = i + 1 53 } 54 return 0 - 1 55} 56func cg_write(path: *u8, body: *u8, n: i64) -> i64 { 57 let fd: i64 = sys_openat_wr(path, CG_MODE_FILE) 58 if fd < 0 { return 0 - 1 } 59 sys_write(fd, body, n) 60 sys_close(fd) 61 return 0 62} 63// read a whole file: size, buffer in bp[0]; 0 when absent/empty 64func cg_read(path: *u8, bp: *i64) -> i64 { 65 let lp: *i64 = sys_mmap(16) as *i64 66 lp[0] = 0 67 let b: *u8 = sys_read_file(path, lp) 68 bp[0] = b as i64 69 if (b as i64) == 0 { return 0 } 70 return lp[0] 71} 72func cg_absent(path: *u8) -> i64 { let bp: *i64 = sys_mmap(16) as *i64; if cg_read(path, bp) == 0 { return 1 } return 0 } 73// a card PNG: signature + IHDR + tEXt(<keyword> NUL <base64 json>) + IEND. Returns bytes written into buf. 74func cg_build_png(buf: *u8, keyword: *u8, payload: *u8, plen: i64, b64: i64) -> i64 { 75 var o: i64 = png_put_sig(buf) 76 let ihdr: *u8 = sys_mmap(CG_IHDR_LEN + 8) 77 var i: i64 = 0 78 while i < CG_IHDR_LEN { ihdr[i] = 0 as u8; i = i + 1 } 79 ihdr[3] = 1 as u8; ihdr[7] = 1 as u8; ihdr[8] = 8 as u8; ihdr[9] = 6 as u8 80 o = png_put_chunk(buf, o, "IHDR" as *u8, ihdr, CG_IHDR_LEN) 81 let data: *u8 = sys_mmap(CG_BUF) 82 var d: i64 = 0 83 let kl: i64 = cg_len(keyword) 84 while d < kl { data[d] = keyword[d]; d = d + 1 } 85 data[d] = 0 as u8 86 d = d + 1 87 if b64 == 1 { d = d + b64_encode(payload, plen, ((data as i64) + d) as *u8) } else { var k: i64 = 0; while k < plen { data[d] = payload[k]; d = d + 1; k = k + 1 } } 88 o = png_put_chunk(buf, o, "tEXt" as *u8, data, d) 89 let none: *u8 = sys_mmap(8) 90 o = png_put_chunk(buf, o, "IEND" as *u8, none, 0) 91 return o 92} 93// fork the promoted compiler with up to 5 args; returns the exit code, stdout in out (NUL-terminated), bytes in olen[0] 94func cg_run(a1: *u8, a2: *u8, a3: *u8, a4: *u8, nargs: i64, out: *u8, olen: *i64) -> i64 { 95 let argv: *i64 = sys_mmap(CG_ARGV_CAP * CG_I64) as *i64 96 argv[0] = CG_ELF as i64 97 if nargs >= 1 { argv[1] = a1 as i64 } 98 if nargs >= 2 { argv[2] = a2 as i64 } 99 if nargs >= 3 { argv[3] = a3 as i64 } 100 if nargs >= 4 { argv[4] = a4 as i64 } 101 argv[nargs + 1] = 0 102 olen[0] = 0 103 let rc: i64 = tr_run_capture_to(CG_ELF, argv, out, CG_OUT - 1, olen, CG_TMO_MS) 104 var n: i64 = olen[0] 105 if n < 0 { n = 0 } 106 if n > CG_OUT - 1 { n = CG_OUT - 1 } 107 out[n] = 0 as u8 108 olen[0] = n 109 return rc 110} 111 112func main() -> i64 { 113 gv_head("nx_card_import_gate -- CC2: a Tavern V2 PNG card imports to the SSOT with every field mapped or refused by name, the age gate stays fail-closed, the persona carries the card not the native defaults, export round-trips byte-stable" as *u8) 114 let ctr: *i64 = gv_ctr() 115 sys_mkdir(CG_DIR, CG_MODE_DIR) 116 // idempotent: a previous run's artifacts must not satisfy this run's teeth 117 sys_unlinkat(CG_SSOT); sys_unlinkat(CG_V2); sys_unlinkat(CG_PERSONA); sys_unlinkat(CG_SSOT_BAK); sys_unlinkat(CG_PERSONA_BAK); sys_unlinkat(CG_EXPORT) 118 let png: *u8 = sys_mmap(CG_BUF) 119 let pn: i64 = cg_build_png(png, "chara" as *u8, CG_JSON, cg_len(CG_JSON), 1) 120 let w1: i64 = cg_write(CG_PNG, png, pn) 121 let png2: *u8 = sys_mmap(CG_BUF) 122 let pn2: i64 = cg_build_png(png2, "chara" as *u8, CG_JSON_MINOR, cg_len(CG_JSON_MINOR), 1) 123 let w2: i64 = cg_write(CG_PNG_MINOR, png2, pn2) 124 let png3: *u8 = sys_mmap(CG_BUF) 125 let pn3: i64 = cg_build_png(png3, CG_COMMENT_TEXT, CG_COMMENT_BODY, cg_len(CG_COMMENT_BODY), 0) 126 let w3: i64 = cg_write(CG_PNG_COMMENT, png3, pn3) 127 let w4: i64 = cg_write(CG_PNG_CUT, png, pn - CG_CUT_TAIL) 128 let notpng: *u8 = "these are notes, not a portable network graphic\n" as *u8 129 let w5: i64 = cg_write(CG_NOT_PNG, notpng, cg_len(notpng)) 130 gv_check("setup-fixtures-written" as *u8, (w1 == 0) & (w2 == 0) & (w3 == 0) & (w4 == 0) & (w5 == 0), ctr) 131 let pos: *i64 = sys_mmap(16) as *i64 132 gv_check("fixture-reached-condition-card-png-walks-to-three-chunks-with-a-chara-text" as *u8, (png_chunk_count(png, pn) == 3) & (png_find_text(png, pn, "chara" as *u8, pos) == PNG_FOUND), ctr) 133 gv_check("fixture-reached-condition-cut-png-is-malformed-to-the-walker" as *u8, png_find_text(png, pn - CG_CUT_TAIL, "chara" as *u8, pos) == PNG_MALFORMED, ctr) 134 135 let out: *u8 = sys_mmap(CG_OUT) 136 let olen: *i64 = sys_mmap(16) as *i64 137 let bp: *i64 = sys_mmap(16) as *i64 138 139 // T1 no age anywhere -> refused, nothing written 140 var rc: i64 = cg_run("import" as *u8, CG_PNG, CG_DIR, 0 as *u8, 3, out, olen) 141 gv_check("neg-control-undeclared-age-refuses-with-exit-3-and-names-the-remedy" as *u8, (rc == CG_EXIT_REFUSED) & (cg_find(out, olen[0], "age undeclared" as *u8) >= 0) & (cg_find(out, olen[0], "age=N" as *u8) >= 0), ctr) 142 gv_check("neg-control-undeclared-age-writes-nothing" as *u8, (cg_absent(CG_SSOT) == 1) & (cg_absent(CG_V2) == 1) & (cg_absent(CG_PERSONA) == 1), ctr) 143 // T2 age=17 by argument -> refused 144 rc = cg_run("import" as *u8, CG_PNG, "age=17" as *u8, CG_DIR, 4, out, olen) 145 gv_check("neg-control-argument-age-below-18-refuses" as *u8, (rc == CG_EXIT_REFUSED) & (cg_find(out, olen[0], "below 18" as *u8) >= 0) & (cg_absent(CG_SSOT) == 1), ctr) 146 // T3 a card that DECLARES 17 refuses even when the argument says 25 -- the card's own declaration wins 147 rc = cg_run("import" as *u8, CG_PNG_MINOR, "age=25" as *u8, CG_DIR, 4, out, olen) 148 gv_check("neg-control-card-declared-age-below-18-beats-a-permissive-argument" as *u8, (rc == CG_EXIT_REFUSED) & (cg_find(out, olen[0], "below 18" as *u8) >= 0) & (cg_absent(CG_SSOT) == 1), ctr) 149 // T4 the real import 150 rc = cg_run("import" as *u8, CG_PNG, "age=19" as *u8, CG_DIR, 4, out, olen) 151 gv_check("import-with-age-argument-succeeds-and-compiles" as *u8, (rc == 0) & (cg_find(out, olen[0], "CARD-IMPORT OK name=Mira Vale age=19 age_src=argument" as *u8) >= 0) & (cg_find(out, olen[0], "CARD-COMPILE OK name=Mira Vale" as *u8) >= 0) & (cg_find(out, olen[0], "source=chara_card_v2" as *u8) >= 0), ctr) 152 gv_check("ledger-maps-the-prose-fields-and-tags-by-name" as *u8, (cg_find(out, olen[0], "MAPPED description" as *u8) >= 0) & (cg_find(out, olen[0], "MAPPED personality" as *u8) >= 0) & (cg_find(out, olen[0], "MAPPED scenario" as *u8) >= 0) & (cg_find(out, olen[0], "MAPPED first_mes" as *u8) >= 0) & (cg_find(out, olen[0], "MAPPED tags -- as core_traits" as *u8) >= 0), ctr) 153 gv_check("ledger-refuses-unconsumed-fields-by-name-never-silently" as *u8, (cg_find(out, olen[0], "REFUSED-BY-NAME character_book" as *u8) >= 0) & (cg_find(out, olen[0], "REFUSED-BY-NAME alternate_greetings" as *u8) >= 0) & (cg_find(out, olen[0], "REFUSED-BY-NAME extensions" as *u8) >= 0) & (cg_find(out, olen[0], "refused_by_name=4" as *u8) >= 0), ctr) 154 let sn: i64 = cg_read(CG_SSOT, bp) 155 let sb: *u8 = bp[0] as *u8 156 gv_check("ssot-carries-source-name-age-and-tags-as-core-traits" as *u8, (sn > 0) & (cg_find(sb, sn, "\"source\":\"chara_card_v2\"" as *u8) >= 0) & (cg_find(sb, sn, "\"name\":\"Mira Vale\"" as *u8) >= 0) & (cg_find(sb, sn, "\"age\":19" as *u8) >= 0) & (cg_find(sb, sn, "\"core_traits\":[\"scientist\",\"lighthouse\"]" as *u8) >= 0), ctr) 157 let prn: i64 = cg_read(CG_PERSONA, bp) 158 let pr: *u8 = bp[0] as *u8 159 gv_check("compiled-persona-carries-the-cards-own-prose" as *u8, (prn > 0) & (cg_find(pr, prn, "You are Mira Vale, nineteen years old" as *u8) >= 0) & (cg_find(pr, prn, "marine biologist" as *u8) >= 0) & (cg_find(pr, prn, "curious, dry-witted, gentle" as *u8) >= 0) & (cg_find(pr, prn, "pier at dusk" as *u8) >= 0), ctr) 160 gv_check("neg-control-imported-persona-inherits-no-native-look-or-relationship" as *u8, (prn > 0) & (cg_find(pr, prn, "platinum blonde" as *u8) < 0) & (cg_find(pr, prn, "doe eyes" as *u8) < 0) & (cg_find(pr, prn, "devoted girlfriend" as *u8) < 0) & (cg_find(pr, prn, "Elara" as *u8) < 0), ctr) 161 gv_check("compiled-persona-keeps-the-house-laws" as *u8, (prn > 0) & (cg_find(pr, prn, "never mention being an AI" as *u8) >= 0) & (cg_find(pr, prn, "first person" as *u8) >= 0), ctr) 162 // T5 export round-trips the V2 source byte-stable 163 rc = cg_run("export" as *u8, CG_DIR, CG_EXPORT, 0 as *u8, 3, out, olen) 164 let en: i64 = cg_read(CG_EXPORT, bp) 165 let eb: *u8 = bp[0] as *u8 166 var same: i64 = 0 167 if en == cg_len(CG_JSON) { same = 1; var i: i64 = 0; while i < en { if eb[i] != CG_JSON[i] { same = 0; i = en } else { i = i + 1 } } } 168 gv_check("export-round-trips-the-v2-source-byte-stable" as *u8, (rc == 0) & (en > 0) & (same == 1), ctr) 169 // T6 NEG-CONTROLS on the file: a PNG whose only text chunk is a Comment, a non-PNG, a truncated PNG 170 rc = cg_run("import" as *u8, CG_PNG_COMMENT, "age=19" as *u8, CG_DIR, 4, out, olen) 171 gv_check("neg-control-png-without-a-chara-chunk-refuses-by-name" as *u8, (rc == CG_EXIT_REFUSED) & (cg_find(out, olen[0], "no tEXt chunk keyed chara" as *u8) >= 0), ctr) 172 rc = cg_run("import" as *u8, CG_NOT_PNG, "age=19" as *u8, CG_DIR, 4, out, olen) 173 gv_check("neg-control-non-png-refuses-as-not-a-png" as *u8, (rc == CG_EXIT_REFUSED) & (cg_find(out, olen[0], "not a PNG" as *u8) >= 0), ctr) 174 rc = cg_run("import" as *u8, CG_PNG_CUT, "age=19" as *u8, CG_DIR, 4, out, olen) 175 gv_check("neg-control-truncated-png-refuses-as-malformed" as *u8, (rc == CG_EXIT_REFUSED) & (cg_find(out, olen[0], "malformed PNG" as *u8) >= 0), ctr) 176 // the refusals above ran AFTER the good import: none may have disturbed the SSOT it wrote 177 let sn2: i64 = cg_read(CG_SSOT, bp) 178 gv_check("refused-imports-leave-the-existing-ssot-untouched" as *u8, sn2 == sn, ctr) 179 180 return gv_verdict("CARD-IMPORT-GATE" as *u8, ctr, "CC2 done-rule: V2 PNG card imports with every field mapped or refused by name, age fail-closed exactly as the native compiler, export round-trips byte-stable, the persona is the card and not the native defaults" as *u8) 181}