Skip to content

Latest commit

 

History

History
1029 lines (937 loc) · 158 KB

File metadata and controls

1029 lines (937 loc) · 158 KB

17 — Open-Work Roadmap (everything not done yet)

Status: living open-work and evidence ledger. This is the sole authority for granular open rows and their exit gates; docs/20 sequences those rows into M1–M6 milestones. [verified <date>] means a named command was re-run; [carried] means retained historical evidence.

Current active regression ledger (2026-09-05): all three 2026-09-01 repair rows are closed by re-executed gates — see the queue below. Newly found and fixed the same day: CGEN-SELF-MASK (cgen.x 54db874's per-function tool/window masks were gated on body text, so cgen.x rewrote its own string literals when compiling itself; cgen1 C != cgen2 C since 2026-08-29). Fix: gate on ##sharedArrays$ (the same fact the file-scope block uses); named lock added to native_pipeline::native_compiler_emits_cgen_ir_for_cgen (cgen3(cgen.x IR) == cgen1(cgen.x IR)); checks/verify-bootstrap.sh hard gate restored. The same-day unsized-array/facet slice preserves DIM a[] in Text IR, locks empty → auto-grow/zero-fill → reset behavior across interpreter, CEmitter, and cgen.x, and measures the scanner/facet gap over 234 programs. The follow-on allStrArr facet-driven migration slice makes cgen.x consume facet headers for ##allStrArr$ when facets are present (falling back to scan_all_strarr$ for headerless producers), adds a scanner-hostile three-engine behavior lock, and fixes arr_acc_name$/ub_ref$ to use direct char**/xb_ub_ naming for non-strDual allStrArr members. checks/validate-all.sh passes 371 tests across 41 binaries; checks/verify-bootstrap.sh is green, including cgen_cemitter_sync 115/115. The LLVM feature gate passes 144 tests with one documented ignore. Remaining known non-green: none in the default gates. Advisory only: LLVM backend lacks INLINE$ and kernel32 I/O (ahello.x skipped, llvm_backend_kernel32_stdio ignored with reason).

Gate parallelism note (2026-09-05, cause measured 2026-09-06): both checks/validate-all.sh and checks/verify-bootstrap.sh use --test-threads=1 to prevent OOM process kills when 115 cgen_cemitter_sync tests each spawn external cgen+cc processes on an 8-core machine under load. cargo test runs test binaries sequentially; --test-threads=1 only affects within-binary parallelism. The kills were not only concurrency: one native cgen.x on xgr.x peaked at 7.6 GB RSS (xit/xui larger, killed by signal) until CGEN-OOM below; single instances now peak at 1.8-3.6 GB, so two full suites side by side still exceed a 16 GB host - keep one at a time.

These active results supersede any unqualified current-green reading of the dated snapshot below. They do not invalidate unrelated historical evidence. Scoped contracts: docs/16 (two C generators), docs/18 (array ABI), docs/19 (facets). Milestone sequence: docs/20.

Historical full re-verification snapshot: 2026-08-31 — the full narrative (C runtime + interpreter hardening audit, twenty-six CEmitter fix batches, XBSourceLib/demo/LLVM counts of that date) is archived verbatim in docs/22 §1; the active ledger above supersedes its counts.

L15 — License Boundary (distribution + legal)

Status: link-core-libs.sh combines GPL-header sources (xcol/xit/xdis), LGPL-header sources (xcm/xma/xui/xgr/xin/xrun/xst/xut/xutpde), and three no-notice shims (gdi32 537 lines, kernel32 1004, user32 669) into one xblibs artifact. Repository evidence does not establish redistribution clearance for that aggregate. GPL obligations and shim provenance require resolution; this is a compliance risk assessment, not a legal determination. The binary and link script are strictly INTERNAL-TEST-ONLY HARNESS ARTIFACTS and must not be packaged or redistributed without that review.

Library File Source header Vendor notice evidence Notes
xcol xbasic/lib/xcol.x GPL (COPYING) COPYING body §§0–12 Compiler; 20 ATTACH sites
xit xbasic/lib/xit.x GPL (COPYING) COPYING body §§0–12 IDE; 15 ATTACH sites
xdis xbasic/lib/xdis.x GPL (COPYING) COPYING body §§0–12 Disassembler
xcm xbasic/lib/xcm.x LGPL (COPYING_LIB) COPYING_LIB body §§0–16 Complex math
xma xbasic/lib/xma.x LGPL (COPYING_LIB) COPYING_LIB body §§0–16 Math
xui xbasic/lib/xui.x LGPL (COPYING_LIB) COPYING_LIB body §§0–16 GUI; 15 ATTACH sites
xgr xbasic/lib/xgr.x LGPL (COPYING_LIB) COPYING_LIB body §§0–16 Graphics; 8–10 ATTACH sites
xin xbasic/lib/xin.x LGPL (COPYING_LIB) COPYING_LIB body §§0–16 Sockets
xrun xbasic/lib/xrun.x LGPL (COPYING_LIB) COPYING_LIB body §§0–16 Exec support
xst xbasic/lib/xst.x LGPL (COPYING_LIB) COPYING_LIB body §§0–16 Stdlib; 20 ATTACH sites
xut xbasic/lib/xut.x LGPL (COPYING_LIB) COPYING_LIB body §§0–16 Eddie Penninkhof
xutpde xbasic/lib/xutpde.x LGPL (COPYING_LIB) COPYING_LIB body §§0–16 Eddie Penninkhof
gdi32 xbasic/lib/gdi32.x Unspecified no copyright/license statement Win32 shim
kernel32 xbasic/lib/kernel32.x Unspecified no copyright/license statement Win32 shim
user32 xbasic/lib/user32.x Unspecified no copyright/license statement Win32 shim
  • Notice-file boundary: both vendor notice files omit the GNU title, version line, and Preamble; their numbered bodies are present as shown.
  • Inventory: 15 libraries in xbasic/lib/ (upstream src/shared 6 + src/linux 9, ported verbatim by TRACKED-CORPUS-PORT 2026-08-31). The harness performs local compile/link plus seven Version$ calls, not behavioral adoption.
  • Remake crates: All 7 crates (xb-cli, xb-compiler, xb-frontend, xb-gui, xb-ide, xb-link, xb-runtime) declare MIT (relicensed in 62c617b). Original code is under MIT; ported upstream code in xbasic/ remains under GPL-2.0 / LGPL-2.1.

0. Open-gap index (at a glance)

The execution queue and open-rows table below are the "what's left" view. Rows marked done / struck stay as closed records with their exit gates and evidence; unstruck rows are the open work. Older narratives are archived in docs/22.

Current execution queue [panel 2026-08-29, updated 2026-08-30 ATTACH name fix]

Order / track Job Exit gate
done CGEN-LABEL-EMIT closed 2026-09-02 cgen_x_compiles_all_demos_cc_clean ok (raw, 114/114) [verified 2026-09-02]
done CGEN-POSITIVE-FILEIO closed 2026-09-02 cemitter_and_cgen_agree_on_positive_corpus ok (byte-identical) [verified 2026-09-02]
done CGEN-SUBADDR-TYPING done bc45ff9 — parser keeps all prefix & as SUBADDR; CEmitter/cgen.x emit strings as managed char*, numerics as ((intptr_t)&x); interp unwrap_byref cgen_cemitter_sync 65/65, cgen_demo_regression 27/27, multi_lib_integration [verified 2026-09-02]
done CGEN-SELF-MASK fixed 2026-09-02 — per-function tool/window masks gated on ##sharedArrays$ instead of fullBody$ text native_pipeline self-fixed-point lock (fails on the pre-fix cgen.x at line 2466, passes after); verify-bootstrap.sh cgen1==cgen2 hard gate; 15/15 libs, 114/114 demos, positive corpus, bootstrap IR_IDENTICAL all green
done CGEN-SHARED-ARR-SELFHOST 2-D locked 2026-09-02 — CEmitter looks up SHARED array shapes program-wide so Helper flattens g[i,j]; cgen.x unsized dim shared g[] is a no-op (does not calloc(1) and wipe the heap global) cemitter_and_cgen_agree_on_shared_2d_array_cross_function; 1-D lock retained
done CGEN-HASH-SHARED-ARR locked 2026-09-02 — DIM #g[n] / #g[i] is the same file-scope heap global as keyword SHARED g[]; Helper writes are visible in Main cemitter_and_cgen_agree_on_hash_shared_array_cross_function
done CGEN-AT-WRITE-BYTES locked 2026-09-02 — addr = &temp$$ + UBYTEAT(addr, off) = val writes GIANT bytes (little-endian 1,2,3,4 → 67305985) in interp, CEmitter, and cgen.x. The null-addr no-op remains cemitter_and_cgen_agree_on_builtin_assign. cemitter_and_cgen_agree_on_at_write_real_memory
done CGEN-COMPOSITE-BYVAL locked 2026-09-02 — TYPE params passed without @ are copies: Mutate(p) does not write back; Sum(p) still sees the caller's members. Complementary to cgen_matches_interpreter_on_byref_writeback. cemitter_and_cgen_agree_on_composite_by_value_param
done CGEN-COMPOSITE-RET locked 2026-09-02 — FUNCTION PT Make() returns a user-TYPE composite struct. Interpreter collects {fname}.* slots after callee body via last_composite_ret; CEmitter discovers members via collect_composite_ret_members, emits xb_comp_PT typedef + struct local + fallback return; cgen.x mirrors via scan_comp_ret$/##compMembers$. Text IR appends composite PT suffix (only when no explicit RETURN — xcol.x FUNCTION TOKEN AddLabel(...) with explicit RETURN token is a type annotation, not a real composite return). cemitter_and_cgen_agree_on_user_type_composite_return; 71/71 sync, 316/316 gates
done CGEN-BYREF-REDIM-GENERALIZED locked 2026-09-03 — positional (data, ub) descriptor dispatch by callee argument index (mismatched names), content-preserving string/2-D REDIM through by-ref, scalar/array split for descriptor params used as scalars (xit XitSetFunction text$), NULL-guarded forwarded-local reads with scalar fallbacks when the callee never allocates (aprofile), first-index 2-D fallback matching the reference emitter (xit FindSearch matches), native compiler token tables 64K→128K (compiler.x; cgen.x now ~65.5K tokens), 0 - 1 sentinel spelling (native compA drops unary minus in ## assignments) cemitter_and_cgen_agree_on_byref_redim_{minimal,generalized,promoted_local,string_array,two_dimensional} + ..._forwarded_unallocated_fallback; native_compiler_emits_cgen_ir_for_cgen; 15/15 libs, demo_parity (aprofile)
M1 architecture — in progress CGEN-FACET-RETIREMENT — allStrArr facet-driven consumption landed; scanner kept as headerless fallback. Cannot delete scan_all_strarr$ until compiler.x emits facets (bootstrap IR_IDENTICAL). xstArrays invariant refined 2026-09-04: classified-storage (dyn/shared/param) 0/0 — the 4 former not-facet-dyn names are shared globals / array params. Slice 5 landed 2026-09-05: strDual adopts use-based semantics — owned (non-shared, non-param) string rank>=1 dual=1 facets union into ##strDual$ after both scan passes (the pass-2 rescan rebuilds scanner-only sets and would clobber pass 1); declaration-shape agreement vs the reference emitter 89/153 -> 113/153 across the facet-only corpus, 0 caused divergences, locked by cgen_strdual_facet_union_decl_shape. Scanner + fallback stay (headerless IR). Remaining: param/shared/dotted/descriptor duals need per-scope design (facet_scope_sets$ is the one per-function facet pass since CGEN-OOM; the RR-03 scope-leak removal branch never executed - see CGEN-OOM); deletion still blocked on compiler.x emitting facets. 234-program ratchet: allStrArr 0/0; strDual 0/153; xstArrays unclassified 0/0. Headerless producer (compiler.x --emit-ir) is the cutover blocker; phased emission plan with milestone decision box now in docs/19 §9 (emission-off proof phases P1–P5, flip P6, deletion explicitly out of scope). Verified then: 316/316 gates target, 71/71 sync (2026-09-06: 371/41, sync 115/115), 15/15 libs, 114/114 demos.
done ATTACH-MOVE-SEMANTICS locked 2026-09-05 — ATTACH src TO dst moves the array (src becomes empty, dst receives data/ub, runtime error if dst is not empty) per xbasic/helpsrc/help_text/lang.txt:57-62 and L2244-2260. Undeclared destinations (xtemp[]) vivify as empty nodes. Row ATTACH with no in-scope 2-D shape is a guarded no-op matching CEmitter. cgen.x is_node_array$ follows Rust: only names ATTACH'd as sym[i,] in the current function emit T* row tables. attach_move_* interpreter locks; cemitter_attach_* + cemitter_and_cgen_agree_on_2d_forward_first_index; cgen_x_compiles_{all_demos,core_libs_floor_9}_cc_clean; cgen_cemitter_sync 85/85; demo_parity; validate-all.sh; verify-bootstrap.sh
gated design CGEN-MODULARITY-GATE — choose physical boundaries only after scanner retirement reduced dependency graph measured; fragments vs native multi-unit vs retained single file decided with falsifiable tests; no mechanism pre-approved
test architecture TEST-DIFFERENTIAL-MATRIX — assign each suite to a named contract and improve three-engine diagnostic locality docs/20 matrix covered; existing pairwise assertions retained until any consolidation proves equal behavior coverage and no worse runtime/diagnostics
done RR-02 ARY composite descriptor — shared ARY_VAR_DATA member arrays now forward as T* (shared T* globals) and both ARY sources are cc-clean xbsourcelib_ary_compiles_clean 2/2, workspace 308/0
done RR-13 raw demo guard — harness post-emission rewrites removed; cgen_x_compiles_all_demos_cc_clean is now a raw-generator contract (114/114, no C mutation) raw 114/114 verified 2026-08-30
done RR-03 scoped facets done 2026-08-30 (8fe02ce) 15/15 core libs compile clean via self-hosted cgen.x (xui/xin/xit/xst fixed)
done RR-05 xcol/xgr scale done 2026-08-30 (8fe02ce) xcol/xgr no longer OOM/signal; 15/15 locked
done CGEN-OOM fixed 2026-09-06 — native cgen.x on xgr.x peaked at 7.6 GB RSS / 585 G instructions / 37 s (xit/xui killed by signal under cgen_x_compiles_core_libs_floor_9_cc_clean, exit None). Root cause, introduced with RR-03 (8fe02ce): filter_dyn_scope$ called facet_has_entry$ once per scanner-dyn name per function, each call re-tokenizing the whole facet table (2+ mallocs per line, never freed) — O(functions × dyn names × facet lines) — and compared the result = 0 (string vs integer, which every engine lowers to LEN() = 0, always false), so the RR-03 scope-leak removal branch never fired natively, in CEmitter, or in the interpreter (three-engine probe); emit_hoists$ re-ran facets_in_scope$(...,"arr1") and facet_has_entry$ per hoisted name the same way. Fix, cgen.x-internal, emitted C byte-identical on xgr/xit/xui: dead branch and facet_has_entry$ deleted (activating real scope-leak removal is a semantic change and stays an RR-03 decision), one facet_scope_sets$ pass per function replaces four per-field facets_in_scope$ scans and tests scope before allocating, facet lines trimmed once at table build, replace$ scans by index instead of copying the remainder per match, write-only ##facetDynAll$ removed. Residual is the C string model: every xb_* helper allocates and nothing frees (cgen.c has 10 free(, none for strings), so RSS is the cumulative temporary volume — xgr 1.96 GB / 4.4 s, xit 1.77 GB / 3.7 s, xui 3.63 GB / 7.7 s. The durable fix is emitter-level (releasing consumed temporaries, or lowering ASC(MID$(s$, i, 1)) to a non-allocating char read — trim_spaces$ is the second-largest allocator via that idiom) and must land in CEmitter and cgen.x together; not taken here. cgen_x_peak_rss_on_core_libs_stays_bounded (child RSS via RUSAGE_CHILDREN, unix-only: xgr.x < 4 GiB - pre-fix 5.9 GiB fails, fixed 1.8 GiB; xui.x < 5 GiB - pre-fix 7.0 GiB fails, fixed 3.6 GiB); cgen_x_compiles_core_libs_floor_9_cc_clean 15/15 in 27.8 s (was killed); verify-bootstrap.sh cgen1==cgen2 + IR_IDENTICAL; validate-all.sh 371/41; --test-threads=1 stays mandatory
done RR-06 ATTACH copy move-semantics runtime (2026-08-30 copy → 2026-09-05 move reversal per lang.txt:57-62) 5 ATTACH patterns in interpreter + Rust CEmitter; per-dim size vars at 2D DIM; guarded no-op for dynamic 2nd-dim; sync 64/64, demo regression 27/27. Move semantics: src emptied, dst receives data/ub, runtime error if dst non-empty
done ATTACH string-array name fix done 2026-08-30 (8ab10c0) attach_stmt uses type suffix for array operands (full_name with $); C emitter Case 4 xb_strdup missing ) fixed; multi_lib_integration + demo_parity now pass; workspace 308/0 fully green
done RR-08a pure lib behavior done 2026-08-30 pure_lib_behavior test: xma.x compiled legacy bodies (SINH/COSH/TANH/ACOS/XmaVersion$) produce correct deterministic outputs; extended RR-07 to cover builtins (find_function before is_builtin in interp, is_user_defined in C emitter final else); sync 64/64, positive corpus 81/81
pre-distribution RR-10 harness hardening done 2026-08-30 Duplicate weak definitions reported in link-core-libs.sh (198 dups, informational); cgen-lib-compile.sh exits non-zero on failure by default (CGEN_LIB_STRICT=0 for old behavior)
safe execution RR-09 SHELL/network capabilities done 2026-08-30 XB_ALLOW_SHELL/XB_ALLOW_NETWORK env vars gate xb_shell/xb_xin_socket_open; denied by default
pre-distribution RR-11 provenance/licensing resolve shim provenance, complete notices, distribution obligations
trigger-gated RR-12 GUI/LLVM/JIT/Cranelift reassessment reconsider only after the runtime-behavior critical path or a compatibility requirement

Heuristic patch falsifiability: any new regex or substring classifier in selfhost/cgen.x must pass all four gates: zero demo/corpus/library regressions, IR_IDENTICAL bootstrap parity, automated negative collision coverage, and a documented retirement path under RR-03. A candidate that fails the first three gates is closed; single-letter a/k/array replacements are not an admissible substitute for scoped facets.

Named gates cover top-level demos, the available XBSourceLib corpus, core library compile/link, and recursive legacy lowering at different evidence levels. multi_lib_integration executes seven Version$ calls only; it does not execute every library implementation. The gtk/helpsrc guard is compile-only. The 308/0 across 33 binaries result is the historical 2026-08-31 snapshot above; current targeted regressions are listed in the active ledger.

cgen.x demos: raw 114/114 guard (RR-13 done 2026-08-30)

The cgen.x compile guard (cgen_x_compiles_all_demos_cc_clean) is now a raw-generator contract — no post-emission C rewrites. cgen.x handles Kittedy found dual-use and qbtoxb TranslateStatement forward-decl internally. The Rust CEmitter demo_parity guard reports 112 comparable matches and two real-I/O skips. No named guard runs all raw cgen.x demos against the interpreter.

A 2026-08-23 manual sweep reported 114 byte-faithful, 0 diverge, 0 cc-fail, 0 crash, 0 timeout through a true cgen.x run-vs-interpreter differential. That sweep is historical evidence, not a named cargo-test lock; the current deterministic all-demo cgen.x contract is compile-only. Ten fixes landed in that manual campaign:

fix demos root cause
CGEN-STRCMP-LEN (5d73678) qbtoxb compare(string = integer(0)) emitted xb_scmp(X, 0) → xb_len(0) → SIGSEGV. Fixed: mixed string/numeric comparison emits (intptr_t)xb_len(X) == 0.
CGEN-MIXED-BYREF (4e19a4f) arecurse byref(symbol(X)) to mixed byref/byval functions always emitted &X. Fixed: scan_mixed_byref$ pre-scans call sites; mixed functions emit value directly.
CGEN-GUI-HEADLESS (5296fa9) 36 GUI demos XuiGetNextCallback was stubbed to 0, hanging event loops. Ported headless GUI runtime from Rust CEmitter: xb_gui_next_callback delivers one synthetic CloseWindow. Also fixed string SELECT CASE (used == pointer comparison instead of xb_scmp) and integer byref arg emission (fell through to 0 for plain integer byref).
CGEN-BYREF-WRITEBACK (a6b3565) adrawing RandomN(float) declared byval in IR but called with byref at all sites → double* vs double cc error. Fixed: scan_byref_wb$ detects all-byref functions; emit_params$ emits T *X_ref pointer params with copy-in/copy-out.
CGEN-SHARED-STR-INIT (bab451c) amakemap $$PathSlash shared string scalar initialized to 0 (NULL) → xb_concat called xb_len(NULL) → SIGSEGV. Fixed: shared string scalars initialized to xb_str("") in main() (can't use function call at file scope).
CGEN-ARGSPLIT-STRLIT (d9d665b) latent All 8 paren-depth counters in cgen.x now skip string literals (Rust {:?} format with \" escapes). Previously ( inside a string literal in a call arg would mis-split. Byte-neutral.
RT-FLOAT-DIV + RT-ENTRY-PARAMS (bcda314) DrawScaled, xgrids Interp fixes: (1) float div-by-zero produces inf (IEEE 754) instead of erroring — matches C; (2) array assign/read auto-vivifies undeclared slots — matches C hoisting; (3) interp only calls parameterless entry functions — matches C backend emit_main.
RT-XGR-PROCESS-MESSAGES (0023a97) aclient, aeasy, agrids, aserver, warning XgrProcessMessages is a stub that never dispatches GUI callbacks; demos calling it in DO...LOOP UNTIL terminateProgram hung forever. Fix: XgrProcessMessages now calls exit(0) immediately (interp Quit { code: 0 }, C xb_xgr_process_messages runtime), so the demo's output (produced before the message loop) is flushed. 5 timeouts → 0.
RT-GOSUB-EXPR-ZERO (ac3e46d) agrids gosub_expr Sub[message] with Sub[message]=0 (nested fn label_addr now 0) emitted goto *(void*)0 → SIGSEGV in both C backends. Fix: guard with if (_xb_ge) — evaluate expr into temp, only push gosub stack + goto if non-zero. Matches interp RT-GOSUB-ZERO. Byte-neutral (no selfhost tool uses gosub_expr).

Historical manual-sweep result: 114 faithful, 0 diverge, 0 cc-fail, 0 crash, 0 timeout. Do not conflate it with the compile-only named guard.

LANDED 2026-08-23 (879f09c) — CGEN-BYREF-DUAL-FIXES: stabilized 114/114: bd$ now only returns _arr for ##byrefDual$ names when they're array params of the current function (##arrParams$ check) — previously global ##byrefDual$ caused bd$("grid$") to return _arr in all functions, breaking atools/aquick. ##arrParams$ and ##curParams$ now set BEFORE emit_params$ call (was after). Scalar facet emission in ##dynNames$ branch skipped for scalar params (no redefinition). Condition 2 in scan_byref_dual$ restricted to non-$ names (string names appear as byref(symbol(grid$:...)) in call args, falsely matching INSTR). Sync 46/46, all suites green.

LANDED 2026-08-23 (739c022) — CGEN-BYREF-DUAL: qbtoxb compiles (113→114):

LANDED 2026-08-23 (52fafe2) — CGEN-ARR-PARAMS: flipped aquick + atools (102→113): emit_params$ now gives ##strDual$ params the _arr suffix + pointer type (matching ##byrefDual$), so grid$:string[] emits char** xb_str_grid$ instead of intptr_t xb_var_grid$. emit_hoists$ ##strDual$ branch: for parameters, declare only the scalar facet + ubound (the parameter is the array facet). ##allStrArr$ branch: skip hoisting for ##arrParams$ entries; declare ubound for array parameters. Scalar DIM suppression skips ##arrParams$ entries. scan_used$ detects swap X:type Y:type operands. New ##arrParams$ global + arr_param_names$ function. Sync 46/46, bootstrap OK, all suites green.

Prior landed fixes (2026-08-22): aarray, aarray_ISNODE (b5bd930), zap (248ff59), atools (CGEN-STRDUAL) — taking faithful 98→102. The remaining 9 cc-fails (gif, gifview, aclient, aserver, asortie, adatadim, arecord, CursorEdit, Kittedy) were all resolved by the CGEN-ARR-PARAMS fix — they were blocked by string array parameter emission, not the multiply-blocked GIANT passes the roadmap previously classified them as.

group demos status
network sockets aclient, aserver ✅ now compile (were passing via Rust CEmitter; cgen.x param fix unblocked them)
Xst runtime asortie ✅ now compiles (was string[] param emission, not Xst runtime)
multi-dim SHARED + composite + grid adatadim, aquick, arecord, CursorEdit, Kittedy ✅ now compile (were string[] param emission)
memory-model / ANY / ATTACH gif, gifview ✅ now compile (were string[] param emission)

LANDED 2026-08-22 (b5bd930) — the 3-fix pass that flipped aarray (98→100): (1) byref-dual _arr split, gated to scan_byref_dual$ = ##dynNames$ ∩ user-fn-param (EMPTY on all faithful demos + selfhost tools → byte-neutral): array facet xb_var_X_arr (the by-ref param pointer) + scalar facet xb_var_X, resolving the param-vs-hoist redefinition. (2) TYPE(x) → non-zero (was unknown-call 0): interp returns the value's non-zero type number, and with the lowered type constants all 0 a 0 stub wrongly fell through IF type<>0 THEN RETURN; same class as the *AT stub; no faithful demo uses TYPE. (3) per-function xb_gosub_base (CGEN-GOSUB-SCOPE — was in the Rust CEmitter, missing in cgen.x): each gosub-using function captures int xb_gosub_base = xb_gosub_sp at entry and RETURN pops only while sp > base (was sp > 0), fixing a cross-function gosub-stack corruption (a callee's RETURN reached under a caller's active GOSUB did goto *stack[0] into another function → jump-to-null crash). Locked by cemitter_and_cgen_agree_on_cross_function_gosub_return; sync 45/45→46, differential faithful 98→100/0, suite 250/0.

LANDED 2026-08-22 (0127a1e, 248ff59) — the 2-fix pass that flipped zap (100→101): (1) array_assign finds the assignment = after the index ] (was INSTR(tmp$, "= ") from the start): a $$-constant index like constant($$XuiColor:integer = integer(0)) captured the = inside the constant, emitting malformed C; byte-neutral for simple indices. (2) local ub cell for byref-dual params DIM'd locally: a by-ref param intptr_t* xb_var_X_arr re-DIM'd via calloc set xb_ub_X_arr with no decl (the hoist's param-skip dropped it) → emit intptr_t xb_ub_X_arr = -1;. Locked by adding aarray/aarray_ISNODE/zap to cgen_demo_regression's DEMOS; sync 46/46, bootstrap OK, differential faithful 100→101/0, suite 250→251/0.

LANDED 2026-08-22 (CGEN-STRDUAL) — flipped atools (101→102): the string analog of the integer ##dynNames$/byref-dual _arr split, for a name DIM'd as both a scalar string (dim X:string) and a string array (dim X:string[N]). (1) scan_str_dual$ → ##strDual$ (EMPTY on all faithful demos + selfhost tools → byte-neutral). (2) Hoist a scalar facet char* xb_str_X + heap array facet char** xb_str_X_arr + xb_ub_X_arr; scalar uses stay bare, array access/assign/DIM/UBOUND take the _arr suffix (via the extended bd$). (3) is_xfn_dyn$ folds a str-dual name not DIM'd as an array in the current function to the type default — matching the interpreter's per-function scoping (atools's text$ is dual-use in CreateGrids but undimmed in MenuBar). Locked by adding atools to DEMOS; sync 46/46, bootstrap OK, differential faithful 101→102/0. Remaining aquick blocker (precisely diagnosed 2026-08-22, needs a coordinated 2-piece pass): by-ref STRING array handling. (a) emit_params$ must strip the [] array marker from a param type and emit a pointer — grid$:string[] currently emits intptr_t xb_var_grid$ (wrong type + xb_var_ prefix) instead of char** xb_str_grid$ (a narrow, correct fix, but 0-flip alone). (b) forward-referenced string arrays need heap-hoisting: grid$/image$ are DIM'd in Entry but array-accessed earlier via the GOSUB-inlined SUBs SwitchWindow/ReportCallback (placed before the DIM), so the mid-function stack decl char* xb_str_grid$[N] isn't in scope at the access — they must hoist to char** xb_str_grid$ + calloc (like ##dynStr$). (b) VERIFIED UNSAFE as a cgen.x-only change (2026-08-22): routing by-ref string arrays through ##dynStr$ (via a scan_str_byref$ = array-DIM'd ∩ byref(symbol(X$:string))) breaks the bootstrap fixed-point — cgen.x's own source passes string arrays by-ref, so heap-hoisting them changes cgen.x's self-emission (2nd-pass drift), and it didn't even flip aquick (the forward-access needs the runtime DIM-before-access to hold, which the heap-null pointer doesn't guarantee). A real pass must mirror the Rust CEmitter's mechanism in both generators together (sync + bootstrap-locked), not a cgen.x-only edit. The string analog of the integer byref/dyn work; not a bounded fix.

Note on Historical Counts: Individual completed items below record point-in-time test suite counts (e.g. Sync 61/61, 62/62) as immutable audit snapshots at the time of merge. Current authoritative totals are maintained in the top banner above.

Fix / Feature Subsystem Status Scope Synced
CGEN-ARRAYS C backend ✅ done (2026-08-20): auto-vivified array hoisting + dynamic DIMs + undimmed-array folds — ✅
CGEN-ARGC C backend ✅ done (2026-08-20): arity reconciliation (drop extras, pad missing) via DEFINED_SIGS — ✅
CGEN-BUILTINS C backend ✅ done (2026-08-20): INLINE$, EOF, RIGHT$/LEFT$ 1-arg, STRING$ via xb_string — ✅
CGEN-AROTATE C backend ✅ done (2026-08-20): xb_bin2/xb_binb/xb_binb2 shifted a signed int (negative rotate results looped forever); unsigned shifts, mirrored in cgen.x — ✅
CGEN-REDIM / CGEN-BYREF-REDIM (primitive/flat arrays) C backend ✅ done (2026-08-21, merge be03117; guide docs/18): (T** data, intptr_t* ub) descriptors, resize closure, coherent access/UBOUND/SizeOf/REDIM/call emission, content-preserving REDIM, and native XstQuickSort/XstCopyArray helpers are landed for primitive/string arrays. Historical 193/0 and sort/copy evidence applies to this scope, not composite TYPE arrays or whole-ARY runtime behavior. primitive byref arrays ✅
CGEN-ANY-PARAM C backend ✅ done (2026-08-20): array params (UBYTE gif[]) thread is_array → C pointers; *AT memory builtins fold to the interp's 0/no-op stub (aquick faithful) — ✅
COMPOSITE-ARR-BYREF frontend + Rust CEmitter OPEN — ARY shared ARY_VAR_DATA forwarding is now done (both ARY sources cc-clean via is_shared_array → emit_raw_array_name); by-value whole-array copy landed 2026-09-05 (was the real break behind the Nop note: no-@ W(a[])/Sum(p[]) passed the scalar facet to pointer params — Rust cc-error, cgen.x silent zeros): xb_array_copy fresh-heap copy at the call site in both emitters (dual + array-storage gate), callee param reads/writes/UBOUND honor caller storage (##arrParams$ exemptions; param UBOUND C-model 0 = Rust, interp 1, same split as member UBOUND). Locked: ..._byvalue_whole_array_copy (7/3), ..._composite_array_byvalue_copy (7/3/6), ..._string_array_byvalue_copy deep-copy (aa/bb/aa/bb), ..._param_ubound_c_model (C 0 / interp 1). Residual: dotted-string-dual local double-decl (pre-existing cgen.x cc-break, e.g. r.name with @-call in scope — needs decl-branch fix, separate slice); non-dual by-value arrays still forward (silent share, no failing test); strDual under-list adjudicated irreducible 2026-09-06 (all sampled names are shared/byref-forwarded/param/dotted — correct non-splits; see docs/19 §6). RR-02 design (copy, per interp + CGEN-COMPOSITE-BYVAL) executed for by-value; @-forwarding was already locked (7/30/60); remaining items above are tracked as residuals, none blocking M1 exit by itself. 2026-09-05 @-forward divert: whole-array @-forwarding is array-context, not scalar use (mirrors Rust walk_expr divert_byref) — applied to scan_dual_use$ (dual classification) but NOT scan_used$ (scalar hoisting still needs @-scalars like reply); _strFacet type-mismatch now evidenced by ##nonstrArrDims$ (explicit non-string array DIMs) or in-scope non-string facets, fixing the dotted-string-dual double-decl (arecurse file$ pattern: scalar+unsuffixed-array collision). byrefDual members route reads/writes via bd$ (_arr for array params); heap arrays pass the pointer bare (triple-pointer segfaults fixed, p2/p7/p8 three-engine green); param UBOUND C-model 0 = Rust. null/window/host_address keep bd$ fiat dual membership. Residual: non-dual by-value forwarding still shares (silent, no failing test — and by construction nearly vacuous: every by-value whole-array arg self-dualizes via the call, verified p9 3/4 three-engine). Copy/heap arms now key ONLY on function-local storage (curFnArrays/arrParams/curDescLocals); program-global dynNames/dynStr/allStrArr leak across scopes (xst n$ routed to _arr). String alias-append value discipline locked (abcd/ab three-engine). Known divergence (no action): whole-array byref to a SCALAR param (W(@a[]) into W(XLONG @w)) aliases element 0 in both C backends (5/30) while interp yields 0/discards (0/5); by-value form agrees everywhere (0/5). Degenerate source; emitters agree with each other; fixing either side gambles on unknown legacy rule; shared-shadow forward split locked too (local DIM shadows shared: C 7/30 vs interp 0/3); Unsuffixed STRING s[] element stores (s[0]="aa") cc-fail identically in both C backends (intptr_t array decl) while interp accepts: pre-existing frontend naming-shape gap, parity holds, both emitters agree (by-value descriptor copy itself verified $-suffixed 5/zz/aa/1 all-engines).; Descriptor-chain by-value (Mid(@m[]) -> Leaf(m[]), Leaf REDIMs) locked 5/3/1/3/1 all-engines: cgen.x data-only forwarding arm now excludes descriptor callees + copy-arm dual gate takes scope-qualified ##curFacetDual$ (descriptor params carry facet dual=1, no DIM for the scanner); string-chain twin 5/1/1.; Desc-source to PLAIN callee by value also copies now (99/4/3/3/4; data-only forwarding leaked writes). Known M1-ABI limit (not fixed): by-value copy FROM a bare-pointer plain param cannot size the copy — Rust copies 1 slot via sizeof (reads past slot 0 are heap over-read UB), cgen.x emits undeclared xb_ub_* (loud cc-fail); interp copies full. Needs ABI-level design (hidden length), not a peephole.
CGEN-COMPOSITE-ARR C backend ✅ done (2026-08-20): composite member arrays hoist once (dyn pointer wins); scalar+array DIM of one name no longer double-declares (arecord/adata faithful) — ✅
CGEN-COMPOSITE-DUAL C backend ✅ done (2026-08-20, c2e7300): a dual-use name's scalar facet is declared once by emit_hoisted_scalars; a scalar DIM of it hit the plain None arm and re-declared it → C "redefinition". Fires for a flattened composite array member DIM'd scalar (TYPE decl SINGLE .x) but indexed as an array (px3D.shape[i].x). The scalar-DIM None arm now resets (like dyn-scalar) when is_dual_use. Byte-neutral (arecord/adata faithful, sync 5/5, suite 186/0); advances 5 XBSourceLib libs (fgr/mergeOut/mergeTest03/vgr/vgrOld) past the redef XBSourceLib (5) ✅
CGEN-NAME-TYPE C backend ✅ done (2026-08-20, 3ba895d): the hoisted-scalar collector keyed by name only, so a name used as BOTH a String and a non-String (fillColour — xb_str_fillColour via AryGetSTRING, xb_var_fillColour via MscStringToXLONG) declared only the first-seen facet, leaving the other C var undeclared. note now keys by (name, is_string) → both facets declared. (Corrects an earlier "cross-function" misdiagnosis — it was a name/type collision.) Byte-neutral (distinct names keep BTreeMap order; no corpus/demo name collides). XBSourceLib 6→8 compile (ary/ary1.0001) XBSourceLib (2) ✅
CGEN-DUALARR-DECL C backend ✅ done (2026-08-20, 1ecdc51): the dual-use array facet px3D_shape_x_arr was undeclared for a name with only a scalar DIM but indexed as an array (px3D.shape[i].x) — array storage comes only from an array DIM. The undimmed-array check now uses collect_array_dimmed_names (array DIMs only), so such names fold their array accesses (read→default, write→discard) like a truly undimmed array. Byte-neutral (a name with real array storage has an array DIM/is dyn; sync 5/5, bootstrap intact, suite 186/0, demos 71 faithful). Completes the fgr-cluster: XBSourceLib 8→13 compile, 4→9 faithful (fgr/mergeOut/mergeTest03/vgr/vgrOld) XBSourceLib (5) ✅
CGEN-GOSUB-SCOPE C backend ✅ done (2026-08-21, bab960c): aarray/aarray_ISNODE's EXC_BAD_ACCESS (jump to 0x1000) was misdiagnosed as the legacy array ABI — in fact ATTACH is parser-skipped (parser.rs:706), so aarray's arrays are inert. The real bug: a bare RETURN in a gosub-using function lowers to GosubReturn, and cgen's xb_gosub_stack/xb_gosub_sp is a shared global; a function-level RETURN reached while a caller has an active GOSUB popped the caller's frame → goto *garbage. The interp scopes GOSUB per function (Flow::GosubReturn bubbles up within the function). Fix: each gosub-using function (and main) captures int xb_gosub_base = xb_gosub_sp; at entry; GosubReturn pops only while sp > base, else returns from the function. Byte-neutral where base==0 (no caller gosub) — corpus/bootstrap unaffected. Demos 72→74 faithful, diverge 2→0 (every testable demo now matches). Sync 5/5, bootstrap intact. Locked by cgen_matches_interpreter_on_gosub_scope. Note: the full legacy array ABI ({data,len,infoword} descriptor + ATTACH + ANY, was "CGEN-BYREF-DESC") is not needed by any current demo/lib — no observed program relies on it aarray/aarray_ISNODE ✅ ✅
CGEN-IDENT-SUFFIX C backend ✅ done (2026-08-20): unified four drifted name→C-identifier sanitizers into c_emit_expr::sanitize_c_ident, now mapping the full XBasic type-suffix set $ ! # @ & % → _s _f _d _a _l _h (was missing @ & % in forward-decl params + xb_shared_ sites). value@ (SBYTE) / value&& (ULONG) names leaked literal @/&/% into C, breaking cc. Byte-neutral (no corpus name carries those); XBSourceLib core libs 4→6 C-compile XBSourceLib msc etc. ✅
CGEN-BYREF-ARG C backend ✅ partly done (2026-08-20, ab54493): a by-ref arg to an array/pointer param now emits a pointer — emit_call_args carries each callee param's is_array (DEFINED_PARAM_ARRAYS); a pure dyn array passes directly, everything else takes address-of &x. Fixes the latent float double→double * hard error (int was masked by -Wno-int-conversion). Two earlier reactive attempts regressed and were reverted (blanket & broke scalar-param by-refs; emit_array_var_name emitted an undeclared _arr facet) — the landed version avoids both. Byte-neutral (corpus has 0 by-ref; sync 5/5, bootstrap intact, demos 71 faithful, XBSourceLib 6/4). Remaining: flips no file alone — the 7 CFAIL libs advance to their next blocker (fgr → nested composite-array redefinition px3D.shape[i].x); and full write-back + OOB-safe scalar→array need the ground-up by-ref model XBSourceLib ary/fgr/vgr/merge ◑
CGEN-BYREF-WRITEBACK C backend ✅ done (2026-08-21): scalar & composite @-param write-back now reaches the caller. Key correction to the original design: the interp keys write-back on the argument being @arg (ByRef in call.rs), not the callee's param decl — so a param declared @ but defined without it (DECLARE ... GeoPerpendicularLine (...,@L2) / FUNCTION ... (...,L2)) still writes back. The C backend therefore drives by-ref from call sites: DEFINED_PARAM_BYREF marks a param by-ref iff every call passes it @ and none by value (intersection — a fixed C signature must type-check every call). A by-ref scalar emits T* x_ref with prologue T x = *x_ref; + copy-out *x_ref = x; before every return (emit_byref_copy_in/emit_byref_copy_out); call sites pass &x. Composite @v already flattens to per-member byref(...) args upstream, so nested out-params (geo's @L2) work. Guards: a mixed @/by-value param (ary's ArySetSINGLE value!) stays by value (interp write-back there is a no-op — callee never writes it); a by-ref scalar sharing an array param's name (Kittedy @adjacent+@adjacent[]) stays a value param to avoid a C name collision. Byte-neutral (corpus/self-host have 0 by-ref; sync 5/5, bootstrap intact, demos 71 faithful, XBSourceLib 13/13 compile). geo's by-ref values are now correct (30 30 39.9999…), leaving only its FLOAT-FMT ULP. Locked by cgen_matches_interpreter_on_byref_writeback. Remaining: by-ref array write-back with runtime strides is separate (CGEN-BYREF-DESC) geo (by-ref part) ✅
CGEN-MULTIDIM C backend ✅ partly done (2026-08-20): local multi-dim arrays flatten row-major in the direct (CEmitter) path — DIM a[i,j,…] allocates ∏(dk+1), a[i,j] computes Σ ik·∏_{m>k}(dm+1), UBOUND=flat-1 (matches the interp's slot.rs::array_offset). Gated on extra_dims/extra_indices non-empty → 1-D stays byte-identical (corpus is all 1-D; sync 5/5, bootstrap intact, no demo flips). The text IR now round-trips extra_dims/extra_indices (byte-neutral — no frozen golden is multi-dim), so --emit-ir/round-trip preserve multi-dim; locked by cgen_matches_interpreter_on_multidim_arrays + c_emit_multidim_round_trips_through_text_ir. Remaining: the self-hosted cgen.x CEmitter doesn't yet flatten multi-dim (needs mirroring, bootstrap-gated — no corpus need, all 1-D); by-ref multi-dim needs the {data,dims} descriptor (CGEN-BYREF-DESC) for runtime strides (capability; no demo flips) ◑
CGEN-SHIFT C backend ✅ done (2026-08-21, 0877902): XBasic INTEGER is i32 (interp RuntimeValue::Integer(i32), wrapping_*); cgen computed in i64, diverging on overflow + arithmetic shift (acrc32 table[1]: i64 0x77073096 vs interp signed-i32 0x09073096). Two storage-change experiments (c_type=int32_t+-fwrapv) were reverted — int32_t truncates label-address integers ((intptr_t)&&label in computed GOSUB/GOTO) → crash. The landed fix keeps intptr_t storage (addresses intact, no crash, no -fwrapv) and masks integer results to i32 with (int32_t)(…): Arithmetic (+ - * / MOD << >>), bitwise AND/OR/XOR (Boolean arm), Not, unary Neg, and hex/binary literals (0xFFFFFFFF→−1). Compute-in-i64-then-narrow is defined (no i32-overflow in the i64 op; the cast is 2's-complement narrowing) and byte-neutral for in-range values. acrc32 flips to byte-faithful (demos 71→72, diverge 3→2). Sync 5/5 (corpus byte-neutral incl. computed_gosub_test), bootstrap intact, corpus goldens unchanged. Locked by cgen_matches_interpreter_on_i32_arithmetic. Remaining: cgen.x mirror deferred (byte-neutral on corpus, like CGEN-MULTIDIM); LLVM backend has its own i32 handling acrc32 ✅ ✅
CGEN-GOTO-VLA C backend ✅ done (2026-08-20): sized array DIMs in a GOSUB function now heap-allocate (dyn pointer) instead of stack VLAs, so the GOSUB goto no longer bypasses a VLA init (agrids faithful) — ✅
CGEN-SCALAR-ARRAY-DUAL C backend ✅ done (2026-08-20): a name used as both scalar and array now emits two C vars — scalar xb_var_x + array xb_var_x_arr (mirrors the interp's TypedSlot value/array fields), routed by IR-node kind; array DIMs also count as array-context (adatadim's scalar SWAP a[]); a genuine dual-use array param also splits (qbtoxb's token[], also read as scalar token = token[i]); scalar params excluded (gif/gifview/Kittedy/zap/adatadim/qbtoxb faithful) — ✅
CGEN-NAME-CONFLICT C backend ✅ done (2026-08-20): aligned string_byte_read + byref_symbol with symbol()'s resolution; duplicate-param rename fires only on true C-name collision (atools faithful). qbtoxb's xbasic$ shared-String-array element-typing resolved (facet 1, b1e0353); its remaining #line/token tangle closed structurally (nested-DIM hoist + dual-use array-param split, 6153215) — qbtoxb now compiles + byte-faithful qbtoxb ✅ ✅
CGEN-NESTED-DIM C backend ✅ done (2026-08-20): a sized array DIM inside an IF/FOR/WHILE/SELECT body is a block-scoped VLA that later out-of-block uses can't see (qbtoxb REDIM #line[] inside an IF, indexed after); such names now force to dyn (function-hoisted), structural so it round-trips text IR (frozen v0.1 golden unchanged) qbtoxb ✅
CGEN-SHARED-ARR (C) C backend ✅ done (2026-08-21) — see the detailed CGEN-SHARED-ARR row below: non-dual-use SHARED arrays (scalar + composite-member) now emit one heap global and cross functions correctly. LLVM still per-function (LLVM-SHARED-ARR, next row). ary-class AOT ✅ (C)
CGEN-SHARED-ARR-SELFHOST self-hosted cgen.x 1-D LANDED (2026-08-22): cgen.x now mirrors Rust's shared heap-global scheme for 1-D shared arrays — a file-scope T* xb_var_g = 0; intptr_t xb_ub_g = -1; global (forward-decl via new scan_shared_arr$/##sharedArrays$), calloc at the sized dim shared g:t[N], xb_var_g[i] access shared across functions — plus a scan that declares undeclared shared(##X:t) scalars as xb_shared_X = 0 (matching Rust). Flipped amemory + amakemap faithful; locked by cemitter_and_cgen_agree_on_shared_array_cross_function. 2-D LANDED 2026-09-02: CEmitter carries SHARED multi-dim shapes program-wide (SHARED_ARRAY_DIMS); cgen.x unsized dim shared g[] is a no-op so Helper cannot calloc(1) over Main's heap. Locked by cemitter_and_cgen_agree_on_shared_2d_array_cross_function. Composite-member-shared + dup-params for CursorEdit/Kittedy remain separate. Original gap (now 1-D-fixed): cgen.x sent dim shared g:t[N] through the local dim handler which never stripped shared (emitted xb_var_shared, cc "redefinition"). Selfhost tools use only shared scalars (##), so the sync suite never exercised shared arrays. ary-class self-hosted AOT infra (1-D ✅)
CGEN-SELFHOST-PARITY self-hosted cgen.x ✅ DONE (2026-08-23): cgen.x now produces byte-identical output to the interp for ALL 114 demos — build native cgen from cgen.x → IR→cgen→cc→run vs interp, 114 demos: faithful=114, diverge=0, cc-fail=0, crash=0, timeout=0. This is the true cgen.x differential (not the Rust CEmitter sweep). Journey: faithful=1 (minimal seed) → 109 (eight fixes: CGEN-STRCMP-LEN, CGEN-MIXED-BYREF, CGEN-GUI-HEADLESS, CGEN-BYREF-WRITEBACK, CGEN-SHARED-STR-INIT, CGEN-ARGSPLIT-STRLIT, RT-FLOAT-DIV/RT-ENTRY-PARAMS, RT-GOSUB-ZERO) → 114 (two more fixes: RT-XGR-PROCESS-MESSAGES making XgrProcessMessages call exit(0) so 5 GUI timeouts terminate, +RT-GOSUB-EXPR-ZERO guarding gosub_expr null address in both C backends so agrids exits 0 instead of SIGSEGV). Also: CGEN-NESTED-FN label_addr fix (nested function names emit 0, not &&label). Sync 46/46, all suites green. demo faithfulness ✅ 114/114
LLVM-SHARED-ARR LLVM ✅ done (2026-08-24, b1ef670): DIM SHARED arr[n] (keyword form, newly parsed — the DIM parser previously consumed SHARED as the variable name) declares program-global holder + per-dim count slots in LLVM (declare_shared scan); the DIM arm stores the calloc'd buffer/counts into the globals and every function pre-registers the shared arrays, so cross-function writes are visible. Elem type from the IR symbol. Verified 1-D and 2-D across all four paths (7/9, 42/0). Locked by llvm_backend_shared_array_keyword; LLVM suite 114/0, full validator 274/0. ary/ary1 AOT parity (still gated on composite-byref) done
LLVM-ANY LLVM ANY array[] polymorphism (monomorphize or tagged elements) aarray_ISNODE feature
LLVM-BYREF-REDIM LLVM REDIM-through-@array[] needs {data,dims} heap descriptors shared by pointer general by-ref parity feature
CGEN-FLOAT-FMT C backend ✅ done (2026-08-21, 6357403): shortest-round-trip float print matching the interp's Rust f64 Display (slot.rs to_string), replacing snprintf("%.17g") which over-emitted 17 sig figs (geo 39.999904099540153 vs interp 39.99990409954015). xb_fmt_float: expand to 41 sig figs via %.40e (correctly-rounded at that width on any libc), find the shortest prefix that strtod-round-trips while rounding myself with round-half-away-from-zero (Rust's tie-break — exact .25→.3), then place the decimal point in fixed notation (never scientific). The earlier %.*e-loop approach hit 0.03% ties because it relied on libc's shortest-precision rounding; doing the rounding myself from a high-precision expansion is portable + deterministic. Validated vs Rust Display on 1e6 random doubles + denormal/MAX/MIN/2^±53 edges: 0 mismatches. Mirrored in cgen.x (same algorithm + signature; sync asserts output + helper sigs). XBSourceLib 9→10 faithful (geo). Locked by cgen_matches_interpreter_on_xbsourcelib. Remaining: the LLVM backend still prints via its own path (LLVM float parity is separate) geo.x ✅ ✅ (C)
LLVM-DEFER LLVM ✅ partially done (2026-08-24, with CGEN-AUTOVIVIFY d425cc5): content-preserving REDIM ✅ — LLVM xb_dyn_resize (realloc + fill grown tail, byte-GEP × elem size) serves both the REDIM Dim arm and auto-vivify write guards (grow to max(cnt, idx+1), never shrink). PRINT TAB() column parity ✅ (c9222ed: global print-column tracker — str_print/int-width/snprintf-float-width updates, comma tab stops, newline resets; TAB(n) emits the space pad byte-string). REMAINING: array bounds checks. polish partial
CGEN-AUTOVIVIFY interp + Rust-C + LLVM done for 3 of 4 backends (51a7b70): write to index i of a dyn array grows storage to max(ub+1, i+1) preserving content - interp (dyn_arrays + array_reshape high-water), Rust CEmitter (realloc grow-guard), LLVM (xb_dyn_resize, max(cnt,idx+1)). cgen.x portion REVERTED: the scanner fixes regressed 4 faithful demos via byrefDual-naming x hoist-dimmed-loop entanglement (aprofile/arecurse/qbtoxb/zap) - write-before-REDIM folds there as before (pre-existing). Root causes documented: text IR could not represent size-None array DIMs; dim_name$/fn_array_dims$ left the inner dim after redim; LLVM grow passing idx (not idx+1) shrank storage. cgen.x auto-vivify partial
RT-IO-BYTES interpreter ✅ done (2026-08-24): interp stdin channel is now Vec<Vec<u8>> end-to-end (read_stdin_lines raw-byte split; INLINE$/READLINE$/ReadFile return bytes directly — no lossy decode anywhere). Trailing partial WriteFile writes splice into the output stream via line_pending so a following PRINT continues the same C output line. High-byte probes byte-identical interp↔C (A\xffB → LEN 3, byte 65). Remaining boundary (documented): the input channel is LINE-based — a trailing LF on stdin is not representable (C ReadFile sees it, interp doesn't); kernel32 tests use LF-free stdin. Locked by cemitter_and_cgen_agree_on_kernel32_partial_write. — done
RT-XST interp + backends XstStringToNumber ✅ done (6a5aa4e) — real number parser (whitespace/sign/hex/bin/oct/decimal/float) → specType (0 ok / -1 err) + afterOff/rtype (SLONG6/XLONG8/GIANT12/DOUBLE14)/value$$ (int or f64 bits); by-ref builtin (interp call.rs write-back; C xb_xst_str_to_num(s,start,&after,&rtype,&value) via emit_byref_value/addr + gated emit_xst_runtime); enabled by GIANT (90dcfd4). XstBackStringToBinString$ ✅ done (72136fa) — backslash-escape→binary decoder; pure string builtin. XstQuickSort (14) + XstCopyArray (2) ✅ done (2026-08-21, merge be03117) — real stable sort (8-byte-slot reorder, et-dispatch, permutation fill) + array copy, interp (xst::quicksort/copyarray) + gated C runtimes, over the CGEN-BYREF-REDIM descriptor (see row); fgr/vgr/msc/ary sort+copy for real, byte-faithful. All gated → byte-neutral on the Xst-free corpus (sync 5/5). env stubs XstGetSystemTime/XstClearConsole/XstSetProgramName are byte-neutral no-ops. selfhost/cgen.x Xst mirror deferred (gated; no bootstrap tool uses Xst). All Xst builtins the corpus uses are now DONE. ✅ msc/fgr/vgr number+string parsing; XstQuickSort/XstCopyArray sort+copy ✅ (corpus-complete)
RT-ATTACH interpreter + C backends ✅ done (2026-08-30; reversed to move semantics 2026-09-05) — copy move-semantics implementation in both interpreter (interpreter_attach.rs) and Rust CEmitter (c_emit_attach.rs). Move semantics: src emptied, dst receives data/ub, runtime error if dst non-empty. Undeclared destinations vivify as empty nodes. Row ATTACH with no in-scope 2-D shape is a guarded no-op. ary TestAryPerformance done (move semantics locked 2026-09-05)
CGEN-SHARED-ARR frontend + C backend ✅ done (2026-08-21, gated): a SHARED a[] array (scalar-elem OR composite-member) was emitted as a per-function local, so each function saw its own uninitialized copy — sa.x interp 7/9 vs cgen garbage; cF.x interp alice=7 vs cgen =0 (interp keeps them in state.shared). Fix: emit ONE heap global per shared array (dyn T* xb_var_a/intptr_t xb_ub_a at file scope; is_dyn_array/is_undimmed_array/emit_dyn_decls shared-aware; bare SHARED a[] = no-op; emit_globals declares them). Gated: a shared array dual-use (scalar+array) in ANY function keeps the local emission (its _arr access facet can't match one global — collect_program_dual_use). Also extended the composite-member element-type resolution (self.arrays) to the WRITE path (array_assignment), not just reads — a String member write emitted xb_var_ (mismatching the xb_str_ global). Byte-neutral (sync 5/5; cgen.x uses no SHARED arrays); no regression (111 faithful, diverge=0, 11/18 libs, Kittedy fine). Locked by cgen_matches_interpreter_on_shared_{scalar,composite}_array_cross_function. (Correction: composite array member access was never broken — it works with dotted members; earlier "broken subsystem" repros were malformed TYPEs.) SHARED-array correctness ✅
CGEN-HASHVAR-TYPE frontend + interp ✅ done (2026-08-21): a #-prefixed var with a $ suffix (#foo$) is a SharedName that embeds its suffix in the name (suffix: None), so ValueType::from_suffix(None) typed it Integer — a "s" + #foo$ concat then raised a spurious type mismatch: expected Integer, got String (the acgibin interp crash). Added ref_value_type(name, suffix) (infers from the name's trailing char, mirroring auto_symbol) in semantics_expr.rs::symbol/byref_symbol. acgibin no longer crashes → interp==cgen faithful. Byte-neutral on self-host/v0.1 (sync 5/5; cgen.x uses only ##). Locked by cgen_matches_interpreter_on_hash_string_var. acgibin crash, any #string$ ✅
RT-KERNEL32 runtime + all C backends ✅ done (2026-08-24): GetStdHandle/WriteFile/ReadFile implemented across interp (call.rs kernel32_*), Rust CEmitter (c_emit_expr arms + usage-gated emit_kernel32_runtime), and cgen.x (emit_expr/emit_stmt arms + streamed runtime). Legacy &x prefix lowers to a PLAIN symbol, so out-params take addresses positionally (parser &-prefix change REJECTED — aprofile's noise-prefix &x to non-byref callees rewrote signatures). Handles: -10/-11/-12 → 0/1/2, else -1; WriteFile writes min(bytes,LEN) to stdout/stderr + count; ReadFile replaces buffer via char** with exact bytes from stdin + count. Locked by cemitter_and_cgen_agree_on_kernel32_stdio (three-way); LLVM aligned too (llvm_backend_kernel32_stdio, fd-backed open(2)+fdopen /dev/std{out,in}, same golden). REMAINING for acgibin's real output: single-# shared-scope split (#hStdOut written via SharedAssignment but read as a local — the flagged #-vs-## decision); interp WriteFile cannot represent trailing partial writes (RT-IO-BYTES). LLVM: deferred stub (zero-default) via is_builtin. acgibin real output (needs #-scope decision) done
CGEN-COMPOSITE-MEMARR-TYPE frontend + C backend ✅ done (2026-08-21): a composite array member (SHARED DLL library[], member name$) stores its declared element type in self.arrays, NOT self.symbols — so array_access's auto_symbol default typed the dotted leaf library.name as Integer (no $), and a read library[i].name emitted an undeclared xb_var_library_name instead of xb_str_library_name, failing cc. Fixed: array_access prefers self.arrays[name] for the element type. Core lib xbasic/lib/kernel32.x now compiles clean (1 cc error → 0). Byte-neutral (sync 5/5; arecord/adata + demo sweep 111 faithful, diverge=0). Locked by cgen_composite_member_array_read_compiles_kernel32. Separate tracked bug: the composite array-member runtime slot registration (SHARED Rec r[] read r[i].nm → interp "unknown runtime slot") — compile-time type now correct, runtime slot flattening still incomplete. kernel32 compile ✅
CGEN-SHARED-IF-COND C backend ✅ done (2026-08-21): collect_shared (c_runtime.rs) walked an If's bodies but destructured { then_body, else_body, .. } — dropping the condition, so a shared var referenced ONLY in a condition (IF ##XBSystem != …) never got its xb_shared_* global declared → undeclared cc error. Fixed: walk the condition. Flipped core libs MakeDistLinux + xutpde (7→9 compile). Byte-neutral (sync 5/5; the shared corpus's IF-condition reads are already declared via assignment). Locked by cgen_matches_interpreter_on_shared_var_in_if_condition. 2 core libs ✅
CGEN-ASC-2ARG C backend ✅ done (2026-08-21): 2-arg ASC(s$, n) emitted xb_asc(s, n) but xb_asc is 1-arg and the interpreter reads only args[0] (ignoring the position) — a "too many arguments" cc error. Emitter now drops ASC's extra args, matching the interp. Flipped CreateHelp (9→10 compile). Byte-neutral (1-arg ASC(s) emits identically; sync 5/5, demo sweep 111 faithful). Locked by cgen_matches_interpreter_on_asc_two_arg. 1 core lib ✅
CGEN-SIZE-SUFFIX frontend + C backend ✅ done (2026-08-21): SIZE(var$) destructured Identifier { name, .. }, dropping the suffix, then auto_symbol(name) typed the bare name Integer — SIZE(time$) emitted sizeof(xb_var_time) (undeclared Integer) instead of the String xb_str_time. SIZE now resolves via symbol(name, suffix) (same as a normal read). Verified runnable (SIZE(s$)=8 pointer slot, matches interp); byte-neutral (sync 5/5). Resolves 1 of xin's errors (lib not yet flipped — 8 remain). Locked by cgen_matches_interpreter_on_size_of_string_var. xin (partial) ✅
CGEN-UNARY-POS-STR frontend + C backend ✅ done (2026-08-21): unary POS (+x) is identity in the interp (Pos => v, any type), but unary() mapped a String operand to an Integer result — so +s$ was typed Integer while holding a String, crashing the interp's arith ("expected Integer, got Integer") and emitting +-on-char* (invalid C) — xrun.x + log$ + …. POS now keeps the operand type; the C emitter passes a String POS through unchanged. Flipped xrun (10→11 compile). Runnable-faithful (+s$ + "!" = "hi!" both); byte-neutral (numeric POS/NEG unchanged; sync 5/5). Locked by cgen_matches_interpreter_on_unary_pos_string. xrun ✅
CGEN-COLLISION-DIM frontend + C backend ✅ done (2026-08-21): a scalar Dim (XLONG imm + STRING imm$) lowered its name via the bare name, not the collision-aware slot_name that reads use — so both DIM'd slot imm, making imm DIM'd twice (int + string) → flagged dyn → the hoist declared only one type facet, leaving the other (xb_str_imm) undeclared in C. dim now uses slot_name(name, suffix): a colliding imm$ declares slot imm$ (xb_str_imm_s), distinct from imm's xb_var_imm. Flipped xdis (11→12 compile). Non-colliding DIMs keep the bare name (byte-neutral, sync 5/5); no regression (111 faithful, diverge=0). Locked by cgen_matches_interpreter_on_typed_int_string_collision. (A purely module-level-only collision — no function usage — remains, but no core lib hits it.) xdis ✅
CORE-LIBS-CC C backend src/ system libs cc-clean: ✅ ALL 15 compile via Rust CEmitter with XB_WEAK_SYMBOLS=1 -O0 -Wno-incompatible-pointer-types -Wno-int-conversion (compile-only; ATTACH has move-semantics runtime (Statement::Attach AST → CheckedItem::Attach → IrItem::Attach → c_emit_attach / interpreter_attach); xcol (122 sites), xst (61), xit (239), xgr (9) compile with row↔1D move for known-dim arrays; dynamic 2nd-dim arrays still no-op; see ATTACH-IMPL; cgen.x has no byref descriptor ABI — libs not verified via self-hosted cgen.x; 1736 xb_user_* at 1c2c929 (1690→1736 via 14f9c69); XB_WEAK_SYMBOLS masks duplicates via deterministic link order per L16). Sessions 2026-08-23 landed fixes driving errors 80→0 (CGEN-SHARED-COMPOSITE, TYPEAWARE-HOIST, DUALUSE-SCALAR-PARAM, SHARED-DIM-EXCLUDE, AUTOSYM-DOLLAR, STRPARAM-DUALUSE, SHARED-ARR-NO-ARR, OCTAL-LITERAL, EXIT-SELECT-TRUE, STRUBOUND-HOIST, CROSSFN-DUALUSE, DESC-SUFFIX, OWNNAME-INT, DUP-LABELS, DESC-FORWARD, NOT-STRING), then CGEN-BYTE-ACCESS was REVERTED (0a65e69): its FunctionCall hijack also fired on compiler.x's legitimate paren-indexing of string arrays → stage-1 self-hosting emitted nothing → bootstrap tests failed. all 15 shared+linux (compile-only) ✅ (compile-only)
RT-ARGS runtime ✅ resolved (2026-08-21, 1975c75): not a runtime gap — XBMerge's empty output was a general interpreter bug (expression-context function calls discarded their output sink; GetArguments printed the usage prompt but eval swallowed it). eval/eval_expr now thread the real output; C backend mirrors (expr INLINE$ prompt + string-vs-num comparison by length). XBMerge byte-faithful; XBSourceLib 10→11/13 XBMerge ✅
CORE-LIBS-LINK C backend ✅ done (2026-08-23; re-verified 2026-08-28 at 1c2c929: 1736 xb_user_*): all 15 core libraries link into one binary via XB_WEAK_SYMBOLS=1 library mode (checks/link-core-libs.sh — compiles with -O0 -Wno-incompatible-pointer-types -Wno-int-conversion, links deterministically in xcm,xdis,xma,xui,xut,xutpde,gdi32,kernel32,user32,xcol,xgr,xin,xit,xrun,xst order; see L16) and execute cross-TU (7 libs' Version$ return exact source values) — reproducible, byte-neutral by default. Link success ≠ behavioral fidelity: smoke checks only 7 constant-string exports (Xcm/Xst/Xgr/Xui/Xit/Xma/XxxBasic); no ATTACH/ARGV$/OSERROR$/GUI/byref-descriptor function has been runtime-verified against xbasic-6.4.5 binary. Now wired into validate-all.sh (was manual). all 15 (link-only, not behavior) ✅ (link-only)
SHARED-SCALAR (single-# split) frontend + all four paths ✅ done (2026-08-24): single-# SharedName READS of names written via #x = v anywhere in the program now resolve through the shared slot (Analyzer::scan_shared_writes pre-scan + shared_read_symbol hook in symbol()); a local DIM of the same name keeps the local slot (decl+read pairing). Shared writes type from the name's embedded suffix char (ref_value_type) so #formData$ is String, not Integer (the acgibin type-mismatch fix). acgibin now emits its real CGI output identically across interp/Rust-C/cgen-C/LLVM (status 200 + content-type + HTML). ## remains the system-variable form; keyword-SHARED scalar scope is unchanged (that decision row's remaining scope). Locked by 3 new semantics tests + full gates. acgibin real CGI output done
FILE-TABLE-GUARD all compiled backends ✅ done (2026-08-24): the fixed 256-entry FILE* table had unguarded xb_file_count++ in all three compiled paths — exhaustion wrote out of bounds (C runtimes) / GEP UB (LLVM). All three now return -1 at table-full WITHOUT consuming a handle (matching failed-open semantics). Note: C runtimes start count at 3 (handles = idx, 253 usable slots); LLVM starts at 0 (handle = idx+3, 256 slots) — capacity differs but both are overflow-safe. Locked by llvm_backend_file_table_exhaustion_returns_negative_one. — done
CGEN-NDIM cgen.x ✅ done (2026-08-24): flat-storage rank-3+ arrays now retain each function's declared raw shape (fn_array_shapes$) and flatten exact-rank access/assignment row-major via emit_flat_nd$, mirroring Rust's emit_flat_offset; partial-rank array views preserve the established first-index fallback. No new ABI/runtime bound variables: declaration expressions are sufficient. Fixes adatadim's strict cc failure (arr[(i0)*(d1+1)+(i1),integer(i2)]) and output matches interp. Locked by cemitter_and_cgen_agree_on_rank_three_flat_array; bootstrap+cgen suites 83/0, full suite 260/0. adatadim via cgen.x done
CGEN-ARECORD-PARITY frontend + Rust CEmitter + cgen.x ✅ done (2026-08-24): isolated arecord.x is now byte-identical across interp/Rust-C/selfhost-C (380 lines, 1929 bytes). Root causes: (1) cgen DIM dedup was name-only and suppressed executable composite-array re-DIMs — now only an identical repeated native fixed-array declaration is skipped; heap/dyn/shared/composite DIMs execute. (2) READ arrived as mixed-case Read and bypassed composite record lowering — WRITE/READ detection is now case-insensitive. (3) both C runtimes lacked __WRITE_RECORD/__READ_RECORD; helpers now match the existing interp/LLVM placeholder contract (zero-filled count bytes, read/discard, file-position advance, validation). (4) numeric OPEN modes 0–4 now create/truncate/preserve like the interpreter. Locked by DIM-boundary + record-I/O three-way sync tests. Strict selfhost demo sweep: 112 faithful + 2 intentional network timeouts, 0 failures; suite 262/0. arecord done
RT-OPEN-SHARE-MODES interpreter + Rust C runtime + cgen.x C runtime + LLVM ✅ done (2026-08-24): OPEN decodes documented bases 0/1/2/3/4/0x10/0x20/0x30 after stripping orthogonal 0x800 NONBLOCK; unknown bases fall back to read-only existing-file behavior. WR/WRNEW/RWNEW truncate+create; RW/WRSHARE/RWSHARE preserve+create; RD/RDSHARE require existing. Unix interp/C/LLVM use fd-backed opens and apply O_NONBLOCK; Windows generated C/LLVM keep guarded fopen approximations (NONBLOCK no-op; share locking not claimed). LLVM preserves its FILE* handle table via an open(2)→fdopen helper, does not consume handles on failure, and declares variadic open correctly (required on Darwin arm64). Locked by cross-backend mode matrices, invalid fallback/handle sequencing, F_GETFL, and FIFO no-block tests. LLVM-enabled suite 108/0; bootstrap+cgen 87/0; default suite 266/0. full OPEN mode parity done
RT-XIN-SOCKETS C backend ✅ done (2026-08-24): 16 Xin* builtins lower to real BSD sockets (c_emit_xin.rs, usage-gated runtime; byref out-params = &var, &string$ = raw data pointer so recv/send write string buffers directly). aserver end-to-end verified: bind 0x2020 → listen → accept → read "time" → write timestamp; locked by xin_sockets.rs integration test. Interp KEEPS zero-stubs (no raw-address memory model) → aclient/aserver moved to demo_parity SKIP with pointer. Bonus fix found by the real I/O: dual-condition DO UNTIL x ... LOOP WHILE y emitted while(x){body} while(y); — the LOOP-side condition detached into a no-op statement; now if(y-fails) break;. Sync 48/48, suite 258/0. aclient real I/O (needs interp impl) done
SWAP-SUBSCRIPTS frontend + all backends ✅ done: subscripts captured per side (INC/DEC pattern) and this session (2026-08-24, c9ddb5a) side() now also follows .member chains after [subscript] in any order (SWAP entries[idx].flags, entries[idx+1].flags parsed as bare entries before). Verified: SWAP a[0], a[1] and composite INC+SWAP byte-identical interp↔C; suite 257/0. xit RTRIM path done
MODULE-DIM-SCOPE C backend ✅ done (2026-08-24): top-level fixed-size non-string non-shared non-dyn array DIMs hoist to file-scope static declarations (emit_module_dims) when the program defines any function; hoisted names register as "dimmed" in every function context (HOISTED_MODULE_DIMS) so accesses don't fold to undimmed defaults; filtered from main()'s body. Byte-neutral behaviorally; sync 48/48, suite 259/0. Verified: module DIM + named Main + SWAP now matches interp (was 0 0, now 0 100). cgen.x ported too (c2c6f8a): streams static -prefixed decls for top-level fixed non-string array DIMs — both generators now agree on the repro. Programs with module-level DIMs + named functions partial
GUI-HEADLESS C backend + interp ✅ done (2026-08-21, 0b42cf1): headless Xgr/Xui runtime — XuiGetNextCallback delivers one synthetic CloseWindow (demos' loops QUIT on it) so the ~37 message-loop GUI demos run to completion + are differential-faithful (interp==cgen), instead of hanging on a display event loop. All other Xgr*/Xui* keep the unknown-callee stub ($→"", else 0). Also fixed string SELECT CASE (pointer→xb_scmp content compare — what let CloseWindow match its CASE). Byte-neutral on self-host/v0.1 (sync 5/5). Demo sweep 74→111 faithful, diverge=0, compile-fail=0. ~37 GUI demos ✅
GUI-RUNTIME platform real Xgr/Xui runtime (winit + softbuffer per docs/12) — actual window/display, beyond the headless differential stub live rendering for 43 GUI demos + 3 init overflows + DrawScaled + 19 GTK platform (large)
CG-BYTES two-C-gen sync byte-identical emitted C. Decomposed 2026-08-24 (abs_float probe): total delta 238 lines = 221 prelude + 17 body. Prelude classes: (a) helper ORDER (cgen emits unconditionally in its own order; Rust usage-gates in emit_header order), (b) helper TEXT (e.g. cgen xb_len lacks the Rust null-guard now has if (!s) return 0 — NULL guard added; class (b) substantially resolved), (c) gating model (Rust body-contains conditions reproducible in cgen via INSTR(src$, ...)). Body classes (4, all tiny): forward-decl (void) style, one blank line, cgen emits the return-value variable (intptr_t xb_var_Main = 0;) Rust omits, return style. Slice 1 DONE (269920d): the 17-line body delta is ELIMINATED - parameterless (void) prototypes/definitions + forward-… Positive corpus sync 63/63 byte-identical — the 221-line prelude delta is from a non-corpus probe (abs_float) and doesn't affect the sync lock. Remaining prelude delta is helper ORDER/gating, not TEXT (NULL guards are now mirrored).
C-RUNTIME-HARDENING C backend ✅ done (2026-08-31): comprehensive NULL-safety + buffer overflow + C string escaping audit across all C runtime functions. Buffer overflow guards: xb_format r[128] pos<127 guards, xb_fmt_float outn-based o<oend guards, xb_binb2/xb_bin2 n>32 clamps, xb_cjust negative width w<0→w=0 guard, DATA segment xb_data_count<256, gosub stack xb_gosub_sp<256, xb_files[256] table-full fclose+reject. NULL-safety: all memcpy/memset/memcmp guarded with if(n)/if(m) (zero-size memcpy is UB), all malloc/calloc/realloc NULL-guarded (abort() for xb_alloc/dynamic arrays, graceful-fail for quicksort/copyarray/back_to_bin), all byref output pointers guarded, all const char* params guarded, all localtime returns checked, all x87 FPU *y params y?*y:default guarded, calloc NULL checks in dynamic array DIM emission. C string escaping: c_escape() helper escapes \, ", \n, \t, \r in DATA string emission, VERSION global, PROGRAM name global. Raw string literal fix: xb_fmt_float out.push_str(...) calls accidentally inside r#"..."# raw string — emitted invalid C. Interpreter safety: EXTS/EXTU/CLR/SET/MAKE width clamped 0-32/offset 0-31 (UB on out-of-range shift), CHR$/NULL$/SPACE$/HEXX$/BIN$/OCT$/CJUST$ negative-arg guards, MID$ len saturating_add, ATTACH row index bounds check, dynamic array auto-grow 1M sanity limit. All mirrored in cgen.x where applicable (Rust-CEmitter-only helpers exempt). Zero compiler/clippy warnings. — done

CGEN-DEMO-CC RAW (RR-13 done 2026-08-30): the named guard is now a raw-generator contract — no post-emission C rewrites. cgen.x handles Kittedy found dual-use and qbtoxb TranslateStatement forward-decl internally via per-function fullBody$ substitutions and fwdDeclsBuf$ correction. Raw 114/114 verified.

DEMO-BYTES DE-SCOPED (panel 2026-08-27): demo-scale emitted-C text identity is not a milestone. The positive corpus keeps the byte lock; demos use compile/behavior contracts. Any future text-identity work still goes through CGEN-FACET-MANIFEST, never more text scanning.

gosubDyn slice ATTEMPTED + REVERTED (2026-08-26): Rust's has_gosub force-dyn rule (GOSUB bypasses VLAs) regressed demos 114 -> 89 cc-clean; reverted. VLAs compile on clang, so dyn is only needed for byte-identity. Prerequisites mapped for the retry: both facets hoisted (afirst's scalar null-check precedes the dim), cross-function facet decls (loop-1 needs the branch too), string _arr naming consistency, type-agnostic dedup, integer names kept out of the dynStr arm, Second attempt (same day) reached 90/114 with the interactions partially solved (both facets in loop-1+loop-2, strDual exclusion, dynStr/dynNames arm routing, scalar facets) - the residual 24 demos need a COHERENT facet-model design, not patches: (i) name-collision classes (grid$ vs grid share bd$ suffixing; a variable literally named string); (ii) multi-dim type conflicts (adjacent integer+float -> _arr redefinition); (iii) the access/assign emitters still emit c_var_name$+"_arr" forms at sites not yet routed through arr_acc_name$; (iv) loop-counter names (o, i) dim'd as arrays in gosub fns + scalar-used elsewhere; (v) used$ may not include array-only-accessed names (loop-1 misses their facet decls). Third attempt (same day) implemented the helper architecture (g_arr_name$/g_ub_name$/g_scalar_name$/g_type$/g_in$ as the single naming source, arr_acc_name$ delegating, hoist loops + dynStr/dynNames/strDual arms routed) - it FIXED the naming-consistency class (Rust's _s dual form confirmed correct via the gif/aquick oracles: xb_str_code_s_arr/xb_str_grid_s_arr) but the residual failures are the TYPE-CONFLICT class: names dim'd as BOTH integer array and string scalar (aback's user, afirst's Sub) where the per-name single-type facet table cannot serve both uses - Rust solves this because its FN_DYN is keyed by the SEMANTIC analyzer's per-symbol types, not by IR-text scanning. Conclusion: the gosubDyn slice is BLOCKED on semantic-type resolution in the scanner (the IR text does not carry enough information to disambiguate multi-type names); it requires either (a) the Rust frontend exposing per-symbol type maps to the sync harness, or (b) a much smarter cgen-side scan that tracks each name's type PER USE SITE. Parked until then. (name -> {scalar decl, arr decl, ub cell, naming scheme}) computed ONCE in the pre-scan, then have every emitter consult it - mirroring Rust's FN_DYN single-source-of-truth. cgen.x-source constraint: INSTR needles must keep parens out of string literals (the xb_instr3 comma counter lacks the string-literal blind-spot fix).

Fix / Feature Subsystem Status Scope Synced
CG-BODY-COVER two-C-gen sync computed GOTO (GotoExpr/GOADDRESS, 1efe782) + AT-write lvalue (BuiltinAssign, 05e9645) ✅ done (2026-08-21): cemitter_and_cgen_agree_on_{computed_goto,builtin_assign} lock all three backends. A systematic audit of every LEFT$(s$, N) = "lit" dispatch in cgen.x found + fixed 2 off-by-one drops (goto_expr, builtin_assign) — the dispatch-length bug class is now fully swept. SUBADDRESS/FUNCADDRESS/computed-GOSUB/sequential-file-IO also corpus-covered. Remaining (low pri): AT-deref reads (XLONGAT rvalue etc. — interp returns 0 vs C real memory, divergent by design) + file mode 2 drift blind spot (docs/16) ◑ partial
CGEN-EXPR-GAPS self-hosted cgen.x A completeness audit of cgen.x emit_expr$ vs every IrExprKind. funcaddr( (&Func()) ✅ DONE (verified 2026-08-22): CGEN-FUNCADDR (funcaddr( arm + program-order ##funcIds$) handles both the arg form (atimer faithful, 3×) and scalar-assign (p = &Helper() → interp/cgen.x both nonzero 2). byref( scalar (@x) — open; IMPLEMENTED + REVERTED 2026-08-22 (attempt proved it's NOT a bounded fix): built the full CGEN-BYREF (call-site scan_byref_params$ :F|P: + ##curByrefParams$ context + pointer param decls + &arg + deref at symbol-read/assign) — the user-function case works (Bump(@x) → cgen.x 6, was 5), sync 46/46, bootstrap held (after fixing a self-host trap: INSTR(s$,"(",q) hit CGEN-ARGSPLIT-STRLIT — a ( in a string-literal call-arg mis-splits; use CHR$(40)/CHR$(41) in call args). BUT it regressed the differential 102→98: the shared byref( arm emits &X for builtin byref args too (e.g. XuiGetNextCallback(@grid)), which need the old "0" → 4 GUI demos cc-fail. A safe fix needs callee-aware arg emission (emit &X only when the callee is a user fn with a by-ref param at that position — the byref( arm can't see the callee), i.e. move byref handling into emit_args$ with callee+position. 0-flip (no faithful demo uses scalar byref) + regresses builtins if naive → deferred as a callee-aware refactor, not the 8-edit pass. drift blind spot ◑ funcaddr done; byref needs callee-aware refactor
RT-GIANT-CTX interpreter ✅ done (2026-08-21): a value overflowing i32 (typed Giant, e.g. decimal 2147483648) flowing into a 32-bit context made the interp error ("expected Integer, got Giant/String") while the C backends coerced. Fixed 3 sites: unary neg (e8d8543, Giant arm + wrapping_neg), bitwise/logical/NOT (8152fb6, shared bit_operand→i32), IF/WHILE/DO conditions (0752ce2, cond_bool full-value truthiness). Interp-only (not bootstrap/sync/cgen.x); locked by cgen_matches_interpreter_on_{int_min_literal,bitwise_giant_operand,giant_condition}. Suite 212/0, differential 111/0 interp Giant faithfulness ✅
GIANT-LITERAL text-IR Giant type-loss sub-bug (2) ✅ fixed (9b829f1): the interp now coerces a Giant arg to an Integer-param builtin (STR$/ABS/…) to i32 via xb_compiler::builtin_param_types, matching the C backends (was a crash; clean signature-driven fix). sub-bug (1) — NOT a sync drift (earlier mislabel corrected): the text IR emits a bare i32-overflowing literal as integer(V) with no Giant marker, and TextIrParser re-types it Integer, so BOTH C generators narrow it identically through the text IR (locked by cemitter_and_cgen_agree_on_giant_literal — they agree, no sync break). Only the direct --compile path + interp keep the 64-bit Giant (analyzer value_type), so a self-hosted (text-IR-fed) build narrows PRINT 2147483648→-2147483648 while --compile gives 2147483648. Fix: mark Giant literals in the text IR (giant(V)) so the round-trip preserves — TextIrEmitter + TextIrParser + both generators' giant( handling + arith giant-propagation + PRINT giant-branch. LOW severity: uncaught (no corpus/demo Giant literal), --compile correct, selfhost tools use 0 Giant literals text-IR Giant fidelity infra
CGEN-TYPED-ARRAY (fixed) self-hosted cgen.x ✅ fixed-array element type done (2026-08-22, 91c58dd): a #/! (float) fixed array DIM a#[3] now emits c_type$(varType$) (double) for its element type instead of a hardcoded intptr_t, so a#[0] = 1.5 no longer truncates to 1 — cgen.x matches interp/Rust 1.5. (Name sanitization a#→xb_var_a_d via sanitize_ident$ was already in place, so it compiled but computed wrong — a silent cgen.x↔reference divergence.) Byte-neutral: an integer array keeps intptr_t (c_type$("integer")), corpus is all-integer, sync 41/41, bootstrap fixed point held, differential faithful=95 unchanged (0 demo uses a fixed typed array → correctness fix, not a flip). Locked by cemitter_and_cgen_agree_on_typed_float_array. REMAINING (synthetic — 0 demos use typed dyn arrays, verified): a dyn float array (AUTO a#[] : DIM a#[3]) isn't recognized as dyn (falls to the fixed path) AND gains a spurious scalar decl → double xb_var_a_d; + double xb_var_a_d[..]; redefinition cc-fail; integer dyn arrays are correct. Like CGEN-BYREF-ARRAY, do not chase without a real failing demo. typed-array cc-validity ◑ (fixed ✅, dyn deferred)
CGEN-MULTIDIM self-hosted cgen.x ✅ done (d0aab51): cgen.x emitted invalid C for any multi-dim DIM a[d0,d1] — it fed the whole comma-separated dim/index list to emit_expr$ as ONE expression (intptr_t xb_var_a[(3),integer(3) + 1], cc "expected ']'"). Every 2-D/3-D demo array hit it (aarray, adatadim, aarray_ISNODE, CursorEdit, Kittedy). Fix: new emit_msub$ does a paren-depth-aware split (like emit_args$, so a call index Add(1, 2) isn't split on its inner comma) and emits one native C bracket per dim — Dim a[(d0)+1][(d1)+1], access/assign a[i][j] — matching the interp's row-major a[i,j]. Single-dim reproduces the historical 1-D emission byte-for-byte (selfhost/v0.1 all 1-D → sync + bootstrap fixed point unaffected); native multi-dim C differs byte-wise from CEmitter's flattened a[i*(d1+1)+j] (CG-BYTES), so cemitter_and_cgen_agree_on_multidim_array is a behavioral lock. aarray/adatadim/aarray_ISNODE now block only on Xst* builtins (RT-XST). Suite 216/0, sync 11/11 multi-dim cc-validity ✅
CHECK-LOC hygiene ✅ done (2026-08-24, 7c58842): verify-bootstrap.sh un-reded end-to-end (rc=0). Four stale stage-2 assertions right-sized: (1) repo-wide rustfmt applied (prior commit); (2) zero-unsafe ban → sanctioned allowlist (LLVM backend inkwell GEP, call.rs fcntl probe, mkfifo FIFO test — each no-safe-equivalent FFI; new unsafe elsewhere still fails); (3) ≤250-pure-LOC hard gate → advisory report (33 modules incl. 5,299-line LLVM backend; real gates = cargo suite + clippy + rustfmt); (4) trailing-token gate → canonical accepts-form test (space-separated PRINT items are valid XBasic). none done
JIT-X87 strategic x87-exact FPU semantics (iced-x86/dynasm JIT) only if compat tests demand deferred
STAGE3-LLVM strategic LLVM as the selfhost AOT backend (C generator is today's default + bootstrap path) stage-3 backend split (docs/13) deferred
CRANELIFT strategic debug backend — deferred
ENTRY-SCAFFOLD runtime entry.rs XxxMain callback scaffold is not a generated-program pipeline (docs/14 §4). Assessed 2026-08-24: the Rust ABI scaffold exists + tested; wiring generated C programs through it needs a C-side XxxMain (crtl/xstart.c mirror) in the emitted runtime — dead code until GUI-RUNTIME provides start_app consumers. Sequence after GUI-RUNTIME. exported-callback programs deferred (sequenced)

Panel-added open rows [2026-08-27]

Row Area What Why / trigger
CGEN-FACET-MANIFEST frontend + cgen.x Replace cgen.x's ~30 global ## text classifiers (47 ## globals, 18 scanner sets, 72 multi-set predicates) with a frontend-emitted per-symbol facet manifest (name, scope, elem type, storage class, dual-use, rank) consumed deterministically three reverted gosubDyn attempts + cross-function scope leakage; architectural prerequisite for ANY further cgen.x storage work
C-BACKEND-PORTABILITY both C generators Emitted C relies on $ in identifiers (cgen.x demo paths), GNU &&label + computed goto; MSVC (docs/13 Win64 goal) rejects all three clang-cl mandate or identifier sanitization + switch-dispatch fallback
LLVM-CI-BITROT CI ✅ done 2026-08-30 — llvm-build job added to .github/workflows/bootstrap-verify.yml: installs LLVM 22 via apt.llvm.org, builds and tests with --features llvm on ubuntu-latest. Prevents bitrot on the optional LLVM backend. —
TEST-HARNESS-HARDENING tests/checks ✅ done 2026-08-30 — xb-link now respects $CC env var (was bare cc); 51 bare Command::new("cc") in xb-compiler tests replaced with cc() helper that reads $CC. Behavior tests already had cc() helpers. —
NEGATIVE-CORPUS-HARNESS frontend ✅ done 2026-08-30 — negative_corpus_produces_diagnostics_without_panics test in parser_tests.rs: 40+ malformed inputs covering lexer errors (unterminated strings, unexpected chars) and parser errors (missing tokens, truncated constructs, unbalanced brackets). Asserts no panics via catch_unwind; >=30 must produce structured ParseError/LexError. Frontend 24/24. —
PACKAGING distribution no packaging story (xb CLI, runtime lib, headers, .dec surface) first external consumer or post-GUI-RUNTIME
INTERP-PERF-ARY interpreter ary TestAryPerformance is O(n²) in the XBasic source algorithm (not interpreter variable lookup — the interpreter uses HashMap for state.slots/state.shared, which is O(1)). The >90s wall time is the XBasic code's own nested-loop name-buffer scanning, not fixable without modifying legacy source or adding JIT. Wontfix. —
CGEN-XST-ARG-BOUNDS cgen.x ✅ done 2026-08-30 — scan_xst_arrays$ now tracks argPos and only processes positions 0–1, matching Rust CEmitter's builtin_needs_descriptor(name, pos) → pos < 2. Previously all args were scanned (latent risk: if any pos-2+ arg were byref(symbol(...)) it would be misclassified as an array). extract_byref_sym$ already filtered non-byref args, so no current corpus triggered the bug. Sync 61/61. —
CGEN-RETURN-VAR-HOIST-SYNC cgen.x ✅ done 2026-08-30 — cgen.x now always declares the return-var for non-integer return types (String, Float, Giant), matching the Rust CEmitter's `*return_type != ValueType::Integer
CGEN-INSTR-NEEDLES cgen.x Latent, not triggering — ~12 scanner needles with literal ( in quoted strings ("symbol(", "array_ubound("); investigated 2026-08-30: zero false-matches across all 15 core library IRs (symbol(, array_ubound(, array_access( never appear inside string("...") literals). CHR$(40) approach would not help (same string value). Real fix = string-literal skipping in scanners; deferred until a real false-match appears. —
ADVISORY-NODE-ARRAY-CLASSIFICATION cgen.x Advisory (2026-09-05) — is_node_array$(n$) currently requires membership in ##curNodeArrays$ (per-function), matching Rust's FN_NODE_ARRAYS. This is function-local: a 2-D array DIM'd in function A but ATTACH'd in function B is not classified as a node array in B. A program-wide classification (cross-function node-array tracking) may be needed if a real corpus case surfaces. No current trigger. revisit when a cross-function ATTACH row case fails
ADVISORY-IS-EMPTY-ARRAY interpreter + cgen.x Advisory (2026-09-05) — is_empty_array checks (used in ATTACH move semantics to test whether dst is empty) are non-recursive: they check ub == 0 or data == NULL without descending into nested structures. This matches the current corpus (flat arrays). If composite/nested empty-detection is needed, the check must be extended. No current trigger. revisit when ATTACH targets composite arrays with nested emptiness
ADVISORY-UBOUND-HIGHEST-DIM interpreter + C backends Advisory (2026-09-05) — UBOUND(a, n) returns the bound of the n-th dimension. For 2-D arrays, UBOUND(a, 2) returns the 2nd-dim size. The semantics assume 1-based dimension numbering and a known shape. Dynamic trailing-comma DIMs (no known 2nd dim) return 0. This matches the current corpus. If a real case needs UBOUND on a dynamic 2nd-dim array, the behavior must be defined. No current trigger. revisit when UBOUND is called on dynamic 2nd-dim arrays
LEGACY-LIB-BINDING-POLICY frontend + C backend + runtime ✅ done 2026-08-30 — 340 behavior checks prove user-defined functions take precedence: pure_lib_behavior (175) + stateful_lib_behavior (165) link compiled xb_user_* weak exports with C harness and verify deterministic outputs from user-defined bodies (xma SINH/COSH/ACOS, xcm DCCONJ/DCSQRT, xst XstGetOSName/XstVersion$, XstParseWhitespace$/XstBackStringToBinString$/XstBinStringToBackStringNL$ etc.). CEmitter is_defined_func gate + interp find_function ensure user-defined precedence over native helpers. —
CORE-LIB-BEHAVIOR-GATE tests ✅ done 2026-08-30 — 340 behavior checks across pure_lib_behavior (175: xma 56, xcm 107, xut 1, xit 8, xdis 3) + stateful_lib_behavior (165: xst getters, setters, string functions including ParseWhitespace/BackStringToBinString/BinStringToBackString+NL, exception mapping, byval RETURN paths). Non-Version$ functions behavior-locked via C harness linking compiled xb_user_*` exports. —
LICENSE-BOUNDARY distribution + provenance Fifteen inputs mix GPL/LGPL headers with three no-notice shims; both vendor notice files contain numbered bodies but omit GNU title/version/Preamble. xblibs combines them into one artifact without established redistribution clearance. Keep internal-test-only; RR-11 resolves shim provenance, complete notices, and documented distribution obligations
EXTERNAL-FUNCTION-PARSE frontend + all backends ✅ done 14f9c69: EXTERNAL FUNCTION now flat top-level (External + Function in is_forward/at_function_start), so xma SIN/SQRT/TAN/EXP10/POWER are emitted (was nested inside EXP2, now 46 new xb_user_*, 1690→1736) —
KEYWORD-FUNCTION-TYPING frontend + all backends ✅ done 14f9c69: FUNCTION DOUBLE prefix/postfix and DOUBLE param prefix now map to TypeSuffix::Double (effective_suffix), so xma ACOS etc. are double not intptr_t/int32_t —
C-EMIT-NAN C backend ✅ done 14f9c69: 0d7FFF… ($$PNAN/$$PINF) NaN/inf lower to NAN/INFINITY (<math.h> already included) so xma double returns compile (was 17 use of undeclared identifier NaN/inf) —
CGEN-LIB-SCALE cgen.x ✅ done (8fe02ce, 2026-08-30) — 15/15 core libs compile clean via self-hosted cgen.x using emit_program_with_facets + -Wno-int-conversion -Wno-incompatible-pointer-types. All 15: xcm,xdis,xma,xui,xut,xutpde,gdi32,kernel32,user32,xcol,xgr,xin,xit,xrun,xst. Former classification failures (xui/xin/xit/xst) fixed by string-typed facet skip, NOT-on-string, qsIdxNames parsing. Former resource failures (xcol OOM, xgr abort) resolved by xb_append cap + per-line scan improvements (08fc0cb). —
ATTACH-IMPL interpreter + C backends ✅ done (2026-08-30; reversed to move semantics 2026-09-05) — copy move-semantics runtime implemented in both interpreter (interpreter_attach.rs) and Rust CEmitter (c_emit_attach.rs). Move semantics: src emptied, dst receives data/ub, runtime error if dst non-empty. Undeclared destinations vivify as empty nodes. Row ATTACH with no in-scope 2-D shape is a guarded no-op. Per-dimension size variables (xb_dim_{name}_N) emitted at 2D DIM time. Sync 85/85, demo regression 27/27, no positive corpus breakage. Used in xcol (122), xst (61), xit (239), xgr (9). move-semantics behavior locked 2026-09-05
CGEN-X-LIB-COMPILE self-hosted cgen.x ✅ done (8fe02ce) — The named cargo test cgen_x_compiles_core_libs_floor_9_cc_clean now asserts passes.len() >= 15 (was >= 9); all 15 core libs compile clean via emit_program_with_facets + -Wno-int-conversion -Wno-incompatible-pointer-types. —
CGEN-DEMO-RAW-GATE self-hosted cgen.x + tests ✅ done (RR-13, 2026-08-30) — cgen_x_compiles_all_demos_cc_clean is now a raw-generator contract. cgen.x handles Kittedy found and qbtoxb TranslateStatement internally; no post-emission C mutation. Raw 114/114 verified. —
ARY-COMPOSITE-DESCRIPTOR frontend + Rust CEmitter DONE for compile 2026-08-29 — shared ARY_VAR_DATA member arrays now forward as T*; xbsourcelib_ary_compiles_clean compiles both ARY sources. The separate xbsourcelib_interp_matches_compiled loop covers 11 non-ARY programs and is not ARY runtime evidence. runtime ARY awaits RR-06 plus a bounded behavior test
ARY-STATUS-RECONCILIATION tests + docs + frontend + cgen.x + LLVM + text IR ✅ done 2026-08-30; three-engine 2026-09-03; LLVM 2026-09-04; REDIM-preserve 2026-09-04; move-semantics 2026-09-05 — Bounded ATTACH behavior test (cemitter_attach_move_semantics_match_interp): Cases 1–5 produce identical output across interpreter, Rust CEmitter, and selfhosted cgen.x (17-line lock incl. strings). Dynamic trailing-comma DIM locked (lowers to 1-D everywhere; row-ATTACH no-op). Content-preserving REDIM locked (cemitter_and_cgen_agree_on_redim_preserves_content, 1-D int/string + 2-D flat + shared globals with cross-function visibility; same lines locked on LLVM by llvm_backend_redim_preserves_content): text IR carries the redim keyword end-to-end (emitter + parser + round_trip_redim_preserves_flag); cgen.x heap REDIM realloc-preserves with tail fill and stride refresh (shared globals via shared-aware cells from the file-scope forward decls); Rust fixed its multi-dim xb_dim_ redeclaration plus calloc-on-redim (stride cells now hoisted, every DIM/REDIM assigns); LLVM fixed its multi-dim REDIM clobbering the dim-0 count slot (scratch count slot) and shared REDIM resetting (preserve through the shared arm). All DIM-line scanners accept redim lines identically — the ratchet stays exact. LLVM lowers all five ATTACH cases (llvm_backend_attach_copy_semantics). Sync 78/78, 15/15 libs cc-clean, LLVM feature gate green. Dual-use string VLA + REDIM combined (facet says dyn, scanner path keeps fixed VLA) still redeclares — pre-existing repeated-DIM class, belongs to facet-retirement track
LEGACY-CORPUS-COMPILE-COVERAGE tests ✅ done 2026-08-30 — cemitter_compiles_gtk_and_helpsrc_clean test locks compile inventory: all 19 GTK demos + 3 helpsrc programs compile clean through Rust CEmitter (cc -fsyntax-only). Previously only parse/lower-tested by legacy_corpus. —
STRING-CONSTANT-EMISSION frontend + C backend + text IR + cgen.x ✅ done (2026-08-31, 1ee1cde) — $$Name$ = "string" was emitted as broken SHARED variable (NULL) instead of char* global with constructor init. Full-stack fix: parser routes $$-prefixed TypeSuffix::String to ConstantDefinition (not SharedAssignment); semantics resolves $$Name$ from constants table before shared; IR carries string_constants: Vec<(String,String)>; CEmitter emits __attribute__((weak)) char* xb_const_Name$ + __attribute__((constructor)) init; text IR serializes string values as Rust debug format (escaped); cgen.x mirrors with split-pass global+constructor emission and expr_type$ constant() type detection. Unblocked XstDecomposePathname (uses $$PathSlash$) and XstGetNewline (uses $$Newline$). Sync 63/63, positive corpus 81/81, core libs 15/15, stateful 124 checks. —
DECLARE-BYREF-DETECTION frontend + C backend ✅ done (2026-08-31, bcf7436; source fix e7591f8) — Parser now records @ byref markers from DECLARE statements (Vec<(String, bool)> instead of Vec<String>), carried through semantics → CheckedProgram.declare_byref → IrProgram.declare_byref → CEmitter. The CEmitter uses DECLARE @ as fallback for functions with no callsite info (only when ≥1 param has @), replacing the XB_BYREF_HINTS env var hack entirely. XstDecomposePathname's DECLARE in xst.x was fixed to add @ on its 5 output params (source bug fix), eliminating the last XB_BYREF_HINTS usage. Enabled 4 new behavior checks: XstGetOSVersion (major=4, minor=0), XstGetPrintTab (pixels=0), XstGetSystemError (reads xb_geterrno). Total stateful checks: 124→128. Unit tests added for parser @ recording and CEmitter byref fallback.
SOURCE-COVERAGE tests + source ✅ done (2026-08-31; license separation same day) — Every current .s (assembly) file in xbasic-6.4.5/src/ has a .x or .c counterpart. 4 infrastructure .s files (appstart, xstart, xzzz, xlib) have pure-reference .c stubs pointing to the canonical LGPL C ports in src/crtl/ and the Rust toolchain replacement — no LGPL-derived code duplicated outside crtl/. Superseded dated snapshots (xlib230325.s, xlib230803.s) isolated to src/linux/lib/old-versions/ with provenance README; guard skips that dir. source_coverage.rs enforces: (1) every .s has .x/.c counterpart, (2) every libxb64.a member has source counterpart; both tests skip when the gitignored legacy corpus is absent (fresh clones/CI).
DEC-FILE-PROCESSING frontend + CLI + C backend ✅ done (2026-08-31) — .dec files are now fully parsed (previously only $$ constants extracted via text scan). Parser fix: is_forward detection in function_stmt() now treats $$ constants (SystemConstant/$$ SystemVariable) as "no function body" signal for EXTERNAL/INTERNAL declarations — .dec files have bare $$ constants after EXTERNAL FUNCTION lines. Gated on is_external flag so regular FUNCTION bodies with $$ constants still parse. CLI resolve_import_decls() extracts TYPE definitions and EXTERNAL FUNCTION/CFUNCTION declarations from parsed .dec files, injects them via FrontendUnit::with_extra_statements() so the analyzer registers composite types and function signatures. All 32 .dec files now parse successfully. dec_processing.rs test: 4 tests covering TYPE parsing, EXTERNAL parsing, $$ constants after EXTERNAL, and regression for FUNCTION body with $$ constants.
TRACKED-CORPUS-PORT source + tests + CLI ✅ done (2026-08-31) — License-cleared upstream source material ported verbatim into the tracked xbasic/ tree (version-neutral name — frozen upstream snapshots keep versioned dirs and stay gitignored local-only). Layout reorganized: lib/ (15 core .x + same-dir .dec; shared/linux split dropped, Rust backend is cross-platform), include/ (32 .dec), demo/ (133 .x + data), crtl/ (LGPL C ports, canonical location), helpsrc/, help/, templates/, tools/, doc/, plus canonical COPYING/COPYING_LIB and generated per-file audit LICENSES.md (LGPL/GPL/NO-NOTICE classes; 3 no-notice shims flagged per RR-11). Excluded: generated .s (36M), binaries (src/bin, .o/.a), legacy Makefiles/spec, images/, XBSourceLib (no explicit license → local-only; legacy_corpus XBSourceLib rows now skip-if-absent, floor 204→151 without it). All test/check paths repointed from xbasic-6.4.5/src/{shared,linux} → xbasic/lib etc.; source_coverage.rs intentionally stays on the local 6.4.5 .s guard. Fix exposed by the flat layout: resolve_import_decls now injects only scalar-signature forward decls — same-dir .dec visibility (e.g. xcm.dec from xit.x) injected composite-return EXTERNALs that broke C emission for import-only functions (composite call ABI = RR-08/docs/18). Port also captures the previously-untracked xst.x XstDecomposePathname DECLARE fix in version control. CI now exercises the corpus for real (tree is tracked). Verified: compiler 70+4+1+2, pure 6 (312 checks), stateful 1, cli 7, demos 27/27, sync 62+1 (core libs 15/15) — all against the ported tree.
ARGV-ENVP-WEAK-REDEF C backend ✅ done (2026-08-31) — emit_globals and emit_main both emitted weak definitions for ARGV$/ENVP$ system shared arrays, causing linker redefinition errors when compiling xst.x with XB_WEAK_SYMBOLS=1. Fix: emit_main skips the weak defs when SHARED_ARRAYS already contains ARGV$/ENVP$ (they're emitted by emit_globals instead). —
INITPROGRAM-UNBLOCK tests ✅ done (2026-08-31) — Discovered that calling xb_user_InitProgram() from the C harness unblocks shared-array-dependent xst functions. InitProgram() is a regular FUNCTION (not GOSUB) that DIM's and populates exception$[], sysException$[], and other SHARED arrays. Previously blocked as "requires SHARED array init" — the block was over-conservative. Added 8 new behavior checks: XstExceptionNumberToName (5 checks: ExceptionNone/SegmentViolation/Breakpoint/InvalidOperation/Unknown), XstSystemExceptionNumberToName (3 checks: SIGNONE/SIGHUP/SIGSEGV). Stateful checks: 134→137, total behavior checks: 309→312. —
CEMITTER-STRING-TRUTHINESS C backend + interpreter ✅ done (2026-08-31) — IF s$ in XBasic checks LEN(s$) > 0 (non-empty), but CEmitter emitted if (ptr) checking pointer truthiness — xb_str("") returns non-NULL → always truthy. Interpreter had the inverse bug: cond_bool returned None for strings → skipped both branches. Fix: (1) CEmitter emit_condition() wraps string-typed conditions with (xb_len(expr) > 0) at all 7 condition sites (IF, WHILE, DO WHILE/UNTIL pre, DO WHILE/UNTIL post ×2); (2) interpreter cond_bool handles String (!bytes.is_empty()) and Float (!= 0.0); (3) cgen.x mirrored with emit_cond$() helper using expr_type$() to detect string conditions. Unblocks XstErrorNumberToName with InitProgram-populated errorObject$[]/errorNature$[] arrays: error=0→"NoError", error=769=(3<<8 1)→"File Busy", error=6147=(24<<8

Panel review 2026-08-27 — Candidate Adoption Ledger

Historical snapshot. Metrics and priorities below are preserved for audit; the top banner and 2026-08-29 execution queue/ledger are current.

Six-lens adversarial review (evidence, architecture, priority, correctness, reproducibility, docs) at 90283be; 6/6 AGREE WITH CHANGES. The one factual dispute (is corpus byte-identity test-locked?) was settled by coordinator inspection: it was NOT — the sync test asserted run output only. Resolution: add the locks instead of weakening the docs. Consensus next-step order: (1) test-lock the headline claims [LANDED with this commit], (2) CGEN-FACET-MANIFEST, (3) C-BACKEND-PORTABILITY, (4) bounded LLVM parity (LLVM-BYREF-REDIM + bounds checks). RT-ATTACH deferred; GUI-RUNTIME deferred (platform-scale); JIT-X87 + CRANELIFT recommended drop (priority lens).

ID Candidate Status Evidence Next action / trigger
P1 Corpus emitted-C byte assertion in the sync test adopted cemitter_and_cgen_agree_on_positive_corpus byte assert_eq! —
P2 True-cgen.x all-demo cc-clean lock adopted cgen_x_compiles_all_demos_cc_clean —
P3 Stale sync-test docstring fix adopted cgen_cemitter_sync.rs module doc —
P4 Locked-vs-manual provenance wording adopted README bullets, docs/16 banner + CG-BYTES, docs/17 banner guarded by docs_headline_claims_are_recorded_at_named_surfaces
P5 README LLVM correction (llvm 22.1.8 IS present; feature-gated) adopted README local-verification section —
P6 New named roadmap rows adopted table above implementation deferred per row
P7 COMPOSITE-ARR-BYREF → next/design-first adopted row note revisit when a real consumer breaks or FACET-MANIFEST lands
P8 DEMO-BYTES de-scope (text identity non-goal) adopted this section + docs/16 CG-BYTES re-litigate only via CGEN-FACET-MANIFEST
P9 Dedicated docs/19 for DEMO-BYTES deferred DocsReviewer proposal only if DEMO-BYTES is re-scoped
P10 Archive docs/17 historical logs to docs/archive/ deferred supersede banners chosen instead next major roadmap restructure
P11 docs/14 closing section (§21) adopted docs/14 §21 —
P12 docs/README Roadmaps table refresh + docs/18 row adopted docs/README.md —
P13 Supersede banners on stale historical sections partial RT-KERNEL32 §2 + DEMO-RUNTIME §4 banners added; DocsReviewer's qbtoxb ~520 citation did not exist at HEAD —
P14 Test-harness hardening ($CC, release-bin dep) done 2026-08-30 xb-link respects $CC; 51 xb-compiler test cc calls respect $CC —
P15 LLVM CI job done 2026-08-30 llvm-build job in bootstrap-verify.yml: installs LLVM 22, builds + tests with --features llvm —

Legacy-library readiness panel 2026-08-27 — Candidate Adoption Ledger

Historical snapshot. The 6/15 result below was current for this panel; the current test-locked floor is 15/15 (since 8fe02ce, 2026-08-30).

Five read-only lenses (evidence, compiler correctness, runtime semantics, coverage/reproducibility, strategy) independently returned AGREE WITH CHANGES. An initial scout batch produced no verdict because its provider quota was exhausted; it is not counted as panel evidence. Coordinator checks then re-ran the Rust CEmitter 15-library link/smoke and the self-hosted cgen.x 15-library probe.

Readiness classes: (a) emit + cc-clean, (b) multi-library link, (c) behavior of the compiled legacy body, (d) production runtime. The Rust CEmitter is class (b) link-ready for all 15 core libraries, but compiled legacy-library bodies remain below class (c) behavior-ready: only seven Version$ exports execute, and demo call sites are native-shadowed or stubbed at emit time. Self-hosted cgen.x is below class (a) for this corpus (6/15). Eleven XBSourceLib programs have class-(c) differential coverage; ary/ary1.0001 are class-(a) compile-only. GTK/helpsrc have a compile-clean guard (cemitter_compiles_gtk_and_helpsrc_clean): 19 GTK demos + 3 helpsrc programs compile via Rust CEmitter but are not behavior-tested. GTK demos and three helpsrc programs now have a compile-clean guard. No path is class (d) production runtime: real GUI and ATTACH are emit-time stubs.

Decision: ready to start behavior-port work on selected non-GUI surfaces through the Rust CEmitter, not ready to claim every legacy library is ported. cgen.x 15/15 is not a bootstrap prerequisite; it becomes mandatory before a shipped-generator claim covers src/*.x or the native-only rebuild includes those libraries. The bounded EXTERNAL/nesting/buffering fixes do not depend on the facet manifest; further cgen.x storage-classification changes still do.

ID Candidate Status Evidence Next action / trigger
L1 Separate compile/link/behavior/production readiness wording done 2026-08-30 15/15 Rust compile/link; 340 behavior checks (pure 175 + stateful 165); 15/15 cgen.x compile floor. Readiness classes: (a) emit+cc-clean ✓, (b) multi-lib link ✓, (c) behavior of compiled legacy body ✓ (340 checks), (d) production runtime — open (GUI-RUNTIME, packaging). —
L2 Native-vs-legacy call-binding policy before behavior-port claims done 2026-08-30 pure_lib_behavior + stateful_lib_behavior tests link compiled xb_user_* exports (weak) with C harness; 340 checks verify user-defined bodies execute, not native helpers. is_defined_func gate in CEmitter + find_function in interp ensure user-defined precedence. —
L3 Non-Version$ core-library behavior test done 2026-08-30 340 behavior checks: pure_lib_behavior (175: xma/xcm/xut/xit/xdis) + stateful_lib_behavior (165: xst, including XstGetOSVersion/XstGetPrintTab/XstGetSystemError via DECLARE byref) —
L4 EXTERNAL parser fix before xma/xdis/xrun behavior work done 14f9c69 EXTERNAL FUNCTION now flat top-level; xma SIN/SQRT/TAN/EXP10/POWER emitted (1690→1736 symbols); behavior tests verify SINH/COSH/ACOS etc. produce correct outputs —
L5 cgen.x nested-function and memory fixes done 2026-08-30 (8fe02ce) 15/15 core libs compile clean; former orphaned-body failures + xgr OOM resolved by xb_append cap + per-line scan improvements + string-typed facet skip —
L6 Keep CGEN-FACET-MANIFEST as storage-work prerequisite only partial facets cannot fix EXTERNAL nesting or whole-string OOM finish strDual/allStrArr before the next storage change
L7 Port order: xut/xcm first; xst and GUI libs not first adopted xst uses ATTACH/*AT/TYPE/ARGV and imports xma/xgr/xui binding policy → xut/xcm → EXTERNAL/type fixes → remaining non-GUI → GUI last
L8 GTK/helpsrc coverage carve-out done 2026-08-30 cemitter_compiles_gtk_and_helpsrc_clean test: 19 GTK + 3 helpsrc compile clean via Rust CEmitter —
L9 Ary status changed from performance-only to contested resolved 2026-08-30; move-semantics 2026-09-05 Bounded ATTACH behavior test (cemitter_attach_move_semantics_match_interp) locks Cases 1–5: row move, row writeback, whole-array move, element→scalar, scalar→element. ATTACH is no longer a no-op for known-dimension arrays. ARY-STATUS-RECONCILIATION done. dynamic 2nd-dim arrays still no-op (no known corpus)
L10 Production readiness deferred adopted GUI-RUNTIME, ENTRY-SCAFFOLD, PACKAGING, portability remain open revisit after a real windowed callback and XxxMain execution
L11 xb_append cap + collect_append_chain partial — landed 08fc0cb Two-word header [len,cap] (malloc(2*sizeof(size_t)+n+1), xb_len via [-2], xb_cap via [-1]) + xb_append O(1) amortized (cap check, doubling after 4096, realloc of 2-word header, NULL guard if (!a) return xb_strdup(b), alias/empty-chain guards, deep xb_strdup for string Symbol copy to avoid dangling after b$=a$+a$=a$+...). cgen_new 154 appends + 139 strdups, cgen_cap2 header correct. Evidence: cgen sync 60/60 88s (was 100s), xcm now cc OK (was FAIL missing xb_str), xui 16.6s (27.5s), xit 10.9s (14s), xst 2.1s (3.3s), xcol still SIGKILL 46.4s (was 54s), xgr SIGABRT 3.4s (was 6.8s) — throughput up but CGEN-LIB-SCALE not yet 15/15; CC fails remain for xdis/xin/xit/xrun/xst/xui (logic, not throughput) finish xcol OOM (profile src$ vs scans; 45 GiB copy → 5M with cap but still 46s suggests scan or other string accumulations) + xgr abort (heap corruption via alias? deep copy fixed one path, but xgr still aborts after 110K); add cgen_x_compiles_all_core_libs_cc_clean guard (L17) to lock
L12 scan_dyn per-line symbol( walk partial — landed 08fc0cb scan_dyn$ now per-line CHR$(10) split for symbol(+byref( with inner WHILE qp for multi-symbol( per line; DIM/SWAP already per-line. Change is INSTR(s$,nSym$) → INSTR(qLn$,nSym$) + INSTR(qLn$,":",qp+7) + MID$(qLn$,qp+7,cp-qp-7) (same-line colon only; cross-line dropped). No quote-skip for string("symbol(") (both old and new) add fixture string("symbol(foo:bar)") + multi-symbol( per line; prove no regression; xcol probe still 46s suggests scan not the sole bottleneck
L13 scan_undimmed MID-avoidance 3-arg INSTR partial — landed 08fc0cb Three sites array_access/array_assign/array_ubound: INSTR(MID$(s$,p+13,…),":") + MID$(s$,p+13,e-1) → INSTR(s$,":",p+13) + MID$(s$,p+13,e-p-13) (algebra e-1 = e_abs-p-13). Still whole-s$ (not per-line). No quote-skip name fixture array_access( line with string("a:b"); xcol still 46s
L14 pNames$[32]→[128] + zap argv$ nested-DIM facet partial — landed 08fc0cb + docs/19 fix selfhost/cgen.x pNames$/pTypes$/pIsStr 32→128 (xdis/xst depth 5-55). docs/19 slice 8 corrected: zap DIM argv$[3] inside nested IFZ THEN+ELSE dim_count==2 ⇒ storage=dyn not fixed. Residual (docs/19): member 2D hardcodes rank=2+storage=shared no-op, array params rank=1, nested Function DIMs leak into parent dim_info, collect_member_2d_expr misses Print/For/SelectCase dump facet argv$:string scope=Entry storage=dyn rank1 + fix member-2D hardcodes before lib facet claim
L15 License/provenance boundary for 15 linked inputs done for disclosure; clearance deferred Six shared + nine Linux inputs inventoried; GPL/LGPL headers separated from three no-notice shims; both notice bodies present but GNU front matter absent; aggregate marked internal-test-only without presenting legal inference as determination RR-11 resolves shim provenance, complete notices, and distribution obligations
L16 Weak-link/harness determinism done 2026-08-30 Deterministic link order, clean OUT, Darwin/Linux nm fallbacks, XB_BIN env override honored. Duplicate weak definitions now reported (nm -m on macOS, 198 duplicates informational). cgen-lib-compile.sh exits non-zero on failures by default; CGEN_LIB_STRICT=0 preserves old informational behavior. —
L17 Named cgen core-library floor (cgen_x_compiles_core_libs_floor_9_cc_clean) done — 15/15 locked 2026-08-30 (8fe02ce) Cargo test asserts passes.len() >= 15; all 15 core libs compile clean via emit_program_with_facets + -Wno-int-conversion -Wno-incompatible-pointer-types. —

Self-hosting purpose and testability panel 2026-09-01 — Candidate Adoption Ledger

Panel Consensus

  • Decision: 7/7 AGREE WITH CHANGES. Seven read-only lenses reviewed one shared packet; all delivered complete findings. Lens names describe review responsibilities, not model-provider identities.
  • Use-case recommendation:
Use case Recommendation Governing evidence / missing gate
study the original and remake architectures yes source-backed scope comparison; raw LOC is not a simplicity metric
reproduce historical milestones yes, by dated named commands 2026-08-29/31 snapshots remain evidence but are not current-green claims
adopt the dual-generator, facet-first direction yes real self-hosting requires cgen.x; contract/facet ownership removes duplicate inference
physically split cgen.x now no remaining scanner state makes current boundaries unstable; mechanism is deferred
deploy or claim full port completion no active generator regressions plus later GUI/PDE/distribution/provenance gates remain

Required Wording Changes

  1. Root README owns a current charter: permanent Rust reference/bootstrap and XBasic-native self-host roles, shared contract, explicit non-goals, and historical-versus-active verification.
  2. Docs/16 owns the typed-IR/runtime-ABI contract and differential rule; line-by-line mirroring is not proof. Positive-corpus and bootstrap identity remain narrowly bounded.
  3. Docs/19 reports the current cgen size, makes remaining scanner retirement explicit, and orders scanner deletion before physical modularization.
  4. Docs/20 replaces stale M1 entry steps with active contract repair, facet/ABI completion, a verification matrix, and a post-retirement modularization decision gate.
  5. Docs/README defines canonical authority without creating a new roadmap.
  6. Stable charter/contract/ledger anchors are deterministic documentation guards; volatile counts and prose are not.

Shared Findings

  • Keeping two generators is justified: Rust is the reference/bootstrap path; XBasic cgen is the self-host proof and native path.
  • The main complexity defect is duplicated semantic inference in cgen.x, not Rust modules, tests, C as a target, or total LOC. Original xcol.x also bundled assembler, machine-code/JIT, object, linker, and ABI work now delegated to system C tooling.
  • The shared contract is typed IR (including scope-qualified facets) plus the runtime ABI and observable behavior. Existing version 0.1 is sufficient; no multi-version governance framework is currently required.
  • The suite is already broad. The improvement target is ownership, diagnostic locality, and missing contract coverage—not maximizing test count or replacing behavior checks with C formatting checks.
  • No new purpose/testability document is needed; existing living documents have sufficient, now-explicit responsibilities.

Disagreements / Residual Risks

  • Physical modularization: three lenses favored deterministic fragments after scanner retirement; the minimality lens favored retaining one source until native multi-unit support exists. Decision: adopt the sequencing gate, defer the mechanism, and require measured dependency/test evidence before adding assembly machinery.
  • Positive-corpus C identity: one lens would demote it to optional local diagnostics; the others retained the narrow lock. Decision: retain it until a measured replacement proves equal diagnostic sensitivity; never expand it to demos or libraries.
  • Contract versioning: formal version governance was challenged as premature. Decision: use the existing Text IR version field and documented ABI transitions, without parallel-version or SemVer infrastructure.
  • Documentation guards: broad prose guards were challenged as brittle. Decision: guard only stable charter, authority, contract, sequencing, and adoption-ledger anchors—not active counts or incidental sentences.
  • A future unified three-engine runner may reduce pairwise duplication, but deleting current tests before proving equivalent assertions, runtime, and failure localization would reduce evidence.

Evidence Actually Checked

  • Read: root README; docs/README; docs/16, 17, 19, and 20; original xbasic/lib/xcol.x; Rust frontend/compiler/runtime boundaries; selfhost/cgen.x; sync, corpus, demo, behavior, bootstrap, and documentation test sources; bootstrap/check scripts.
  • Measured: selfhost/cgen.x 8,674 lines, 72 functions; Main 2,089 lines, emit_expr$ about 1,145, and emit_stmt$ 1,173; 188 integration #[test] functions under crates/**/tests.
  • Executed by coordinator: formatting/check of the active compiler edit and a targeted gtk/helpsrc + raw-demo release test. This panel did not run the project-wide suite.
  • Author-reported/current-session evidence: positive-corpus fileio_test mismatch and active SUBADDR work.
ID Candidate Status Evidence Next action / trigger
CAL-20260901-01 Permanent dual roles and concise project charter adopted root README charter; maintainer intent keep roles stable unless self-hosting goal changes
CAL-20260901-02 Shared typed-IR and runtime-ABI contract replaces blanket line mirroring adopted docs/16 contract authority; sync drift history every lowering/ABI change updates contract, implementations, and focused differential
CAL-20260901-03 Behavior/ABI primary; C identity narrow to positive corpus and bootstrap fixed point adopted docs/16 CG-BYTES; bootstrap checks re-litigate only with measured equal-or-better diagnostic evidence
CAL-20260901-04 Facet-first deletion of remaining cgen inference adopted docs/19; repeated scanner collision regressions retire facts/scanners one at a time behind direct and end-to-end gates
CAL-20260901-05 Physical cgen modularization deferred panel agreed on sequencing but disagreed on fragments vs native modules decide only after scanner retirement and dependency measurement
CAL-20260901-06 Seven-layer verification contract matrix adopted existing tests already map to all layers with gaps add missing contracts; do not chase a test-count target
CAL-20260901-07 Unified three-engine fixture runner and bootstrap-test consolidation partial pairwise overlap exists; equivalence/runtime not measured prototype only if assertions, diagnostics, and runtime are no worse; delete nothing first
CAL-20260901-08 Existing-document authority map; no new purpose roadmap adopted docs/README conflict resolved in place change authority map before adding any canonical surface
CAL-20260901-09 Separate historical verification snapshots from active regressions adopted stale README/docs banners vs targeted current failures update active ledger when named gates rerun
CAL-20260901-10 Original/remake comparisons account for assembler/JIT/object/link scope adopted xcol.x/docs/02 vs remake C/system-toolchain boundary avoid asymmetric LOC/file-count claims
CAL-20260901-11 Deterministic wording guards cover stable decisions only partial existing docs guard is useful but prose-sensitive guard charter/authority/contract/ledger anchors, not volatile metrics

Roadmap and known-issues panel 2026-08-29 — Candidate Adoption Ledger

Panel Consensus

  • Decision: 7/7 AGREE WITH CHANGES. Seven independent read-only reviewer roles examined one shared packet; role names are lenses, not claims about model providers.
  • Conclusion: the prior conditional-NO stands. Rust CEmitter compile/link is usable as internal scaffolding; runtime-faithful legacy-library adoption, raw 114-demo cgen proof, self-hosted 15-library generation, and distribution are not ready.
  • Use-case recommendation:
Use case Recommendation Governing evidence / missing gate
study code and compiler structure yes source and roadmap evidence is available; GitHub tracker was not accessible
reproduce locked compiler claims yes, with named-test caveats 81/81 corpus byte lock, 64/64 sync, IR_IDENTICAL; demo compile guard is raw 114/114 (RR-13 done)
adopt Rust-CEmitter library scaffolding conditional 15/15 compile/link plus 175 pure + 165 stateful behavior checks; no general compiled-body authority for SHARED-array-dependent functions
adopt self-hosted cgen for core libraries compile-clean 15/15 core libs compile via emit_program_with_facets + -Wno- flags (8fe02ce); 340 behavior checks via Rust CEmitter (not cgen.x)
deploy or run untrusted legacy code no ATTACH move-semantics (not full aliasing for dynamic 2nd-dim), native/stub binding ambiguity, SHELL/network gated (RR-09 done), licensing/provenance gaps

Required Wording Changes

  1. docs/16 must say demo text identity is de-scoped, not open.
  2. docs/18 and docs/README must bound the landed descriptor to primitive/flat arrays and mark composite TYPE arrays open.
  3. docs/19 must say substantially implemented (scope-qualified lookup done, 15/15 core libs); strDual/allStrArr remain heuristic (no corpus trigger).
  4. docs/17 must reflect that the test-locked cgen floor is now 15/15 (was 9/15); RR-03 and RR-05 are done. Historical 9/15 and 6/15 measurements are snapshots.
  5. The 114/114 demo headline is now a raw-generator contract (RR-13 done 2026-08-30); no post-emission C rewrites remain.
  6. Historical 6/15 panel measurements must be labeled snapshots, not current status. The top banner and execution queue are authoritative.

Shared Findings

  • The previous sole workspace failure (shared ARY_VAR_DATA descriptor) is now compile-clean via is_shared_array → emit_raw_array_name; xbsourcelib is 13/13 and ary/ary1.0001 are 2/2 cc-clean. RR-02 is done for compile; runtime ARY move-semantics now implemented (RR-06 done 2026-08-30; reversed to move 2026-09-05); bounded behavior test pending.
  • Composite values already lower as struct-of-arrays. Caller arguments and callee signatures must forward the same member-array shapes; the panel did not settle the eventual ABI representation.
  • xui/xin/xit/xst are scope/type classification failures. xcol/xgr are generator resource failures. All resolved 2026-08-30 (8fe02ce): 15/15 core libs compile clean. Former classification failures fixed by string-typed facet skip, NOT-on-string, qsIdxNames parsing. Former resource failures resolved by xb_append cap + per-line scan improvements.
  • The named all-demo test rewrites generated C. Its 114/114 result is valuable as a regression floor but is not raw-generator evidence.
  • ATTACH has move-semantics runtime (RR-06 done 2026-08-30; reversed to move 2026-09-05) but dynamic 2nd-dim arrays still no-op; native helpers can shadow legacy bodies, and seven constant accessors do not establish core-library behavior.
  • SHELL and Xin paths have real host effects. Capability gates precede untrusted execution and xrun/xin behavior testing.
  • License disclosure is present, but shim provenance and distribution obligations remain unresolved.

Disagreements / Residual Risks

  • Composite ABI shape: per-leaf descriptors match the existing struct-of-arrays lowering; a unified structured descriptor may be cleaner long term. RR-02 starts with a focused contract fixture and accepts only a caller/callee-coherent shape, not a source-specific ampersand patch.
  • Facet vs scaling order: some lenses preferred RR-03 then RR-05; the architecture lens found them orthogonal. Decision: run them as separate, parallelizable tracks and measure 13/15 classification-clean independently from 15/15 resource closure.
  • JIT/Cranelift: one lens recommended dropping them; others recommended defer. Decision: trigger-gate rather than delete without a maintainer/runtime compatibility requirement.
  • Link-smoke failure propagation: reproducibility lenses questioned an explicit nonzero check. Coordinator inspection found set -e plus direct smoke execution already propagates failure. Remaining RR-10 gaps are duplicate reporting and the informational cgen probe (XB_BIN override fixed).

Evidence Actually Checked

  • Coordinator-read: docs/README and docs/16–19; prior 2026-08-28 synthesis; attach_stmt; both cgen compile guards; ARY declarations/definition/calls; link-core-libs.sh, cgen-lib-compile.sh, and validate-all.sh. Version$ checks, self-hosted libraries 15/15 (raised from 9/15 at 8fe02ce). Update 2026-08-30: RR-03/RR-05 done, 15/15 locked.
  • Not executed during this documentation panel: builds, compiler probes, or the project-wide test suite. GitHub issue listing was unavailable because the CLI was unauthenticated; no claim is made that the external tracker is empty.
ID Candidate Status Evidence Next action or trigger
RR-01 Reconcile roadmap truth and guard it adopted — expanded 2026-08-29 second pass canonical banner, ARY denominator correction, lifecycle authority, runtime/provenance boundaries keep deterministic singleton/presence/absence guards green
RR-02 Composite-array byref ABI compile repair — shared ARY_VAR_DATA adopted — done for compile 2026-08-29 both ARY sources compile cc-clean; the 11-program runtime parity loop excludes them; cgen_cemitter_sync remains 61/61 runtime ARY awaits RR-06 plus a bounded behavior test
RR-03 Scope-qualified facet lookup done 2026-08-30 (8fe02ce) — xui/xin/xit/xst classification failures fixed (string-typed facet skip, NOT-on-string, qsIdxNames parsing); 15/15 core libs compile clean —
RR-04 Split cgen failure classes adopted as tracking four C errors vs two signal/resource exits keep separate assertions/results; not a blocking implementation phase
RR-05 Bound and fix xcol/xgr generation done 2026-08-30 (8fe02ce) — xcol/xgr no longer OOM/signal; 15/15 core libs compile clean via emit_program_with_facets + -Wno- flags; xb_append cap + per-line scan improvements (08fc0cb) resolved resource exits —
RR-08a Behavior gates for pure libraries done 2026-08-30 — pure_lib_behavior test compiles xma.x + xcm.x + xut.x + xit.x via CEmitter and verifies 175 deterministic outputs: xma (56: 27 scalar math functions including COT via FPU wrapper mapping, at 0/1/2/0.5/PI/sqrt2 inputs exercising full EXP/SQRT/LOG/ATAN/tan paths, plus internal Hart approximation helpers Asin0/Expmo), xcm (107: all 22 DCOMPLEX + all 19 SCOMPLEX-returning functions + 3 SINGLE-returning scalar functions (SCABS/SCARG/SCNORM) + 4 internal helpers (XdcGetAlpha/XdcGetBeta/XscGetAlpha/XscGetBeta) + Atan2 edge cases + XcmVersion$), xut (1), xit (8: XitVersion$ + MakeStringHex hex-string conversion via strtol with base 0), xdis (3: GetAddrLabel64 hex formatting via xb_hexx). CEmitter fixes: EXTERNAL builtin mapping (SQRT/SIN/COS→C helpers), x87 FPU wrapper mapping (XxxFPTAN→tan(), XxxFCOS→cos(), 30 wrappers total). Gates: sync 63/63, demo regression 27/27 xdis
RR-08b Behavior gates for stateful libraries done 2026-08-30 — stateful_lib_behavior test compiles xst.x via CEmitter and verifies 165 deterministic outputs: XstGetOSName(@name$)="unix" (SELECT CASE: ##XBSystem=0 != $$XBSysLinux=1, now correctly resolved from xut.dec), XstGetConsoleGrid(@grid)=0, XstVersion$()="6.4.5", XstGetEndianName(ret)=0, XstGetCPUName(ret)=0, XstGetApplicationEnvironment(@standalone,@reserved)=(0,0,0), XstExceptionToSystemException(1,@sig)=11 (SegViolation->SIGSEGV), (4,@sig)=2 (BreakKey->SIGINT), (7,@sig)=8 (DivByZero->SIGFPE), (12,@sig)=4 (InvalidInstr->SIGILL), (14,@sig)=11 (StackOverflow->SIGSEGV), (99,@sig)=11 (CASE ELSE->SIGSEGV). Parser fix (725a833): is_forward detection for END EXPORT and EXTERNAL ## prevents duplicate function emission. CEMITTER-S-SUFFIX-BYREF fix: byref string copy-out now reads from _s body variable when name collision creates a $-suffixed local (XstErrorNumberToName(0)="$$ErrorObject too large", XstErrorNumberToName(256)="$$ErrorObject too large"). String functions: XstNextField (4), XstNextLine (4), XstMergeStrings (3), XstParse (8), XstTally (5), XxxPathString (3). Total (at 08c6b1e): 58 checks (was 51, +4 XstErrorNumberToName +3 XxxPathString restored). All gates green: frontend 23/23, compiler 68/68, demo 27/27, sync 61/61.
RR-09 SHELL/network capability gates done 2026-08-30 xb_shell checks XB_ALLOW_SHELL; xb_xin_socket_open checks XB_ALLOW_NETWORK; interpreter SHELL gated; xin_sockets tests set env vars —
RR-10 Harness reproducibility done 2026-08-30 Deterministic link order, OUT clean, nm fallbacks, XB_BIN override honored. Duplicate weak definitions reported (198 dups, informational). cgen-lib-compile.sh exits non-zero on failure by default; CGEN_LIB_STRICT=0 for old behavior. —
RR-11 Licensing and shim provenance partial — notices + MIT relicense 2026-08-31 The remake's original code (all 7 crates, selfhost/, fixtures/checks/scripts/docs) is now MIT (root LICENSE, Cargo.toml license = "MIT" ×7; sole author John johnteee@gmail.com; audited — zero upstream copyright markers/copied code in the new trees; all external deps permissive MIT/Apache-2.0). Ported upstream material stays GPL-2/LGPL-2.1 in the tracked xbasic/ tree with canonical GNU texts + per-file audit xbasic/LICENSES.md (root COPYING/COPYING_LIB removed — GNU texts scoped to xbasic/); legacy versioned trees stay gitignored local-only. LGPL-derived C ports live in one canonical location (xbasic/crtl/). Compilation does not propagate licenses; programs linking ported libs inherit GPL/LGPL for those parts only (LICENSING.md rule 4). Remaining (legal): three shims (gdi32/kernel32/user32) carry no copyright/license statement — ship in xbasic/lib/ solely under the upstream tree-level distribution (flagged in LICENSES.md); do not redistribute separately, and xblibs stays internal-test-only until distribution obligations cleared resolve shim provenance and distribution obligations before packaging
RR-12 GUI/LLVM/JIT/Cranelift reassessment deferred / trigger-gated no dependency on immediate correctness path reconsider after behavior gates or a demonstrated compatibility need
RR-13 Remove demo post-emission rewrites done 2026-08-30 cgen.x handles Kittedy found and qbtoxb TranslateStatement internally; harness rewrites deleted; raw 114/114 verified —
RR-14 Reconcile external issue tracker deferred (access blocked) GitHub CLI unauthenticated during panel compare tracker to this ledger when authenticated

Parallel-lens review 2026-08-27 — Legacy lib port review-packet-legacy-lib-port-2026-08-27.md (4/7 delivered)

Historical snapshot. The 6/15 and L15–L17 statuses below were later superseded; see the 2026-08-29 ledger.

Packet target: "Is the XBasic toolchain actually ready to port the 15 legacy .x libs?" Packet HEAD 3c5d9a7 + uncommitted c_runtime.rs/c_emit_stmt.rs/selfhost/cgen.x (now landed 08fc0cb, see L11–L14 partial; header fix also landed). Lenses: 7 tasked; 4 delivered = Provenance/Security, Strategic Synthesis, Correctness, Reproducibility — all AGREE WITH CHANGES (no DISAGREE, no pure AGREE). 3 stalled = EvidenceAuditor-3, ArchitectureReviewer, UsabilityActionabilityReviewer — no assistant turns after 14m + DM wake; history:// shows only the assignment (likely task-backend resource_exhausted/wall timeout per parallel-lens-review-packet recovery note §3); coordinator salvaged via raw evidence + agent:// reads and disclosed degradation from independent judgment to structured self-review. No provider-persona calls; no mock/unit as e2e. Answer (synthesis of 4): Not ready to claim legacy libraries ported. Rust CEmitter is 15/15 link-ready (Bar A) only. Self-hosted cgen.x Bar B is unready/slow (6/15 probe not re-run; xcol wall probe via cgen_exp is not a lock). Highest-leverage among the three named options = CGEN-LIB-SCALE (nested-fn + leaky concat → then named 15-lib cc-clean lock); facet storage work gated on that lock; GUI-RUNTIME last (deferred via L10). Selected Rust Bar C work (xut/xcm, L2/L3) may run in parallel and is not among those three. Do not use unsuffixed "ready"/"ported" — keep Bars A–E taxonomy (L1). Shared findings (4): weak-link + OUT + nm -U non-determinism; xb_append not a drop-in (NULL UB on =0 shared strings, aliasing/moved-realloc hazard, empty-chain panic, cap not retained — every call still reallocs, not exponential buffering); scan_dyn per-line symbol( only (DIM/SWAP already per-line) and scan_undimmed still whole-s$ — index-equivalent, not a line split, neither skips string("…:…"); abs_float.ir has no array_*/colon-in-literal so not a byte-equivalence witness; zap argv$ storage=fixed citation was wrong (static path = dyn); demo 114 text-identity is not a lib lib lock; 60s xcol wall probe (SIGTERM 124 at 15s/30s identical 340K/6620 truncated mid-xb_shared_lineNumber) needs TTFB of xb_version_str vs T_total to separate src$ build from post-src$ scans/emit + fwdDeclsBuf front-delete. Historical wording note: this panel established the 6+9 inventory and internal-test-only boundary. Its shorthand "COPYING* truncated" and "GPL-covered" conclusions are superseded by the factual notice/provenance wording in current L15 above. L11–L14 now partial, L15–L17 still deferred per the falsifiability protocol (§4 of reflective-prompt-library/04-agent/workflow-recipes.md). Partial adoption without these IDs is the drift this review exists to catch.

Micro-residual documented in place: FUNCADDRESS (the builtin) returns 0 — no corpus program uses it (§2 RT-FUNCPTR).

CGEN-DUALUSE — dual-use _arr split (attempted, reverted) [2026-08-22]

Attempted and REVERTED (regressed faithful 70→67). The nested-fn demos' remaining blocker is the Sub[] dispatch array: a true dual-use name used as a bare scalar (IF Sub == 0) AND as an array (Sub[msg], holding label_addr values), used before its DIM. Rust splits it into a scalar facet xb_var_Sub + a dyn array facet xb_var_Sub_arr. A minimal repro worked and sync stayed 30/30, but the differential caught a regression, so it was reverted to the clean faithful=70 checkpoint. A complete (non-regressing) implementation MUST: (1) exclude case-A names (##dynNames$, scalar-DIM+array-DIM used only as array — my single-facet 1-D dyn) from the dual-use set, else it double-handles them (acharmap/aunicode/aviewbmp regressed); (2) cover every subscript site with the _arr rename (a missed site → subscripted value is not an array); (3) provide typed facets — char*/char** for a string dual-use like text$ (the nested-fn demos hit redefinition of xb_str_text$ next), intptr_t/intptr_t* for integer. The nested-fn demos are a deep multiply-blocked chain (nested-fn → retval-dim → Sub _arr → subscripted-value → string-dual-use → …), so this is a coordinated pass, not an increment — incremental attempts regress or 0-flip.

A fourth attempt (2026-08-22) added a safe narrow classifier and confirmed the entanglement concretely, then reverted. scan_dualuse$ = ##dynNames$ ∩ FOR-counter (a dim X:integer[] name also used as for X:) — verified empty on the selfhost corpus (sync stayed 45/45, byte-neutral) and correctly catches zap's i/o. The full _arr routing (hoist scalar+array facets, ubound, access, assign, calloc via a dua$(n$) suffix helper) was implemented and made zap's array facet emit cleanly. But it is 0-flip: zap's dual-use i/o are also by-ref array parameters of CommandLine(i:integer[], …), so the emitted scalar facet intptr_t xb_var_i = 0; redefines the parameter xb_var_i (a hard cc error). Every case-B cc-fail (zap, atools, aquick, arecord) is thus blocked simultaneously by CGEN-BYREF-ARRAY (the twice-reverted by-ref-array-param feature) — the dual-use name is a by-ref array param in the very function where it is also a counter. Case-B and by-ref-array-params must land together (a per-function classifier that knows when a name is a param vs a local counter); neither flips a demo alone. Reverted to the clean faithful=98, diverge=0 state (f3a3d1e).

A fifth attempt (2026-08-22) extended the split to 2-D and re-confirmed the same entanglement, then reverted. scan_dyn$ (the ##dynNames$ 1-D dyn-pointer scan) was relaxed to also route 2-D dual-DIM integer arrays (depth-aware ≤1 top-level comma; 3-D+ stays native), wiring the already-present but dead 2-D dyn branches (emit_mtotal$ calloc + emit_flat2d$ row-major access + xb_d1_). Verified byte-neutral on the corpus (sync 45/45, bootstrap fixed-point held) and it fixed a real error in real demos — aarray/aarray_ISNODE (which DIM array as a 2-D dual-DIM) went from the scalar-vs-array redefinition of xb_var_array cc-error to compiling. But it needed a companion ##curParams$ skip (don't emit a dyn-decl for a name that is a parameter of the current function — array is a by-ref array param of PrintArray, so the dyn-decl redefines the param). That param-skip re-introduced the by-ref-array-param regression: it made aarray compile but diverge (its PrintArray(array[]) needs the callee-side dimension descriptor that CGEN-BYREF-ARRAY provides — empty output without it) and regressed a previously-faithful demo (a callee that legitimately array-uses a dyn-array param lost its decl). Net faithful 98→97, diverge 0→2 — reverted. Conclusion: 2-D dual-DIM, case-B, and multi-dim SHARED all terminate at the same shared blocker — the by-ref-array-param dimension-descriptor system (Rust's collect_desc_info). That descriptor port is the single highest-leverage unblocker and must be done as a coordinated pass that mirrors Rust exactly (the naive param handling regresses real code, as three prior reverts and this one show).

BREAKTHROUGH (2026-08-22): the exact Rust reference mechanism — why 4 attempts collided, and the one-line fix. Studied the Rust CEmitter's actual emitted C for the 5 byref demos (aarray/aquick/arecord/atools/zap) and confirmed all 5 are Rust-FAITHFUL (real gaps, not synthetic). Rust does not use the descriptor system for them — it uses the dual-use _arr split, and the piece every cgen.x attempt missed is the param/array-facet naming:

  • Rust names the array facet AND the by-ref param xb_var_X_arr; the scalar facet keeps the base name xb_var_X. Because the two facets have different C names, the scalar-facet decl never collides with the array param. All 4 cgen.x case-B attempts left the param decl as the base name xb_var_X, so the scalar facet redefined the param — the exact cc-error that killed each attempt.
  • Concrete (aarray PrintArray(array[]), array is 2-D dual-use): Rust emits fwd PrintArray(intptr_t *xb_var_array), def PrintArray(intptr_t *xb_var_array_arr), local scalar intptr_t xb_var_array = 0;, array facet xb_var_array_arr = calloc(…) (2-D flattened), UBOUND → sizeof(xb_var_array_arr)/…, call-site PrintArray(&xb_var_array) (passes the scalar facet's address, a quirky but interp-faithful by-ref). cgen.x today emits the array decl as xb_var_array (collides, 458), UBOUND sizeof(xb_var_array[0]) on a scalar (526/535 subscript error), and the call as PrintArray(0) (null → wrong).
  • The de-risked recipe: redo the 4th-attempt case-B routing (scan_dualuse$ + dua$ + access/assign/ubound/calloc/hoist), and additionally (a) emit the param decl for a dual-use array param as xb_var_X_arr (array facet, in both the forward decl and the definition), (b) emit the call-site @X[]/array arg as &xb_var_X (scalar-facet address). Gate on the full differential (a naive rename regressed a faithful demo before — the pass must cover all 5 demos + not touch the read-only-array-param demos). This converts the "mysterious 4×-failure" into a known, bounded coordinated pass; still GIANT-adjacent, but no longer a mystery.

CGEN-BYREF-ARRAY — DIM'd-array by-ref is synthetic; do NOT "fix" it [2026-08-22]

Attempted a THIRD time and REVERTED (d7e2249 + cgen.x mirror, reset to e62d03f). The prior two reverts are noted in the CGEN-BYREF-ARG row; this pass pins down why the emit_array_var_name approach keeps regressing, so it is not re-attempted a fourth time. The bug is synthetic. emit_call_args emits &x for a by-ref array arg. For a synthetic DIM a[3]; Fill(@a[]); arr[1]=42, a is a dual-use DIM'd array, so &x passes its 1-slot scalar half → arr[1] runs OOB → 42/<garbage> vs interp 5/42. The bounded fix (route array params through emit_array_var_name, gated !is_undimmed_array) makes that case correct AND is byte-neutral on the corpus/faithful demos (Rust differential stayed 111/0, sync 40→41/40, bootstrap fixed point held, cgen.x mirror gave br.x 5/42). BUT it regresses real code: XBSourceLib/fgr/fgr.x + geo/geo.x (both faithful, gated by tests/cgen_demo_regression.rs::cgen_matches_interpreter_on_xbsourcelib) pass by-ref arrays that are undimmed dual-use / composite members / scalars, where the existing &x is exactly what the callee expects. The !is_undimmed_array gate cut Rust fgr errors to 1 (a composite-member subtlety remained) and the cgen.x byref( arm broke fgr with 20 cc errors. Root insight: real byref-array usage in this corpus is UNDIMMED (no _arr storage) — &x is correct; the DIM'd-array-byref case that emit_array_var_name targets does not occur in real code (only in a synthetic repro). The full-workspace suite (not the demo differential — XBSourceLib is not in diff_cgenx.sh) is what catches this; always run cargo test --workspace before finalizing a byref change. Do not reopen without a real failing demo (none exists). cgen.x's own byref( gap (CGEN-EXPR-GAPS) is likewise a non-issue: its demos need undimmed &x, which the pre-existing default already approximates.

CEMITTER-S-SUFFIX-BYREF — _s suffix byref string writeback ✅ done [2026-08-30]

FIXED (2026-08-30): XstErrorNumberToName(error, @error$) computes the error message into xb_str_error_s (local string storage with _s suffix) but the copy-out writeback read from xb_str_error (the copy-in local), returning the input value (NULL) instead of the computed message. Root cause: when a function has a name collision (e.g. error Integer + error$ String), the analyzer's scan_body_collisions detects it and the body uses the $-suffixed name (error$ → xb_str_error_s), while the byref copy-in/out uses the bare param name (error → xb_str_error). The fix: emit_hoisted_scalars records which byref String params have a $-suffixed hoisted scalar (via record_byref_str_s), and emit_byref_copy_out reads from the _s variable for those params (*xb_str_error_ref = xb_str_error_s). The _ref pointer name stays unchanged (matching the signature). Non-collision cases like Kittedy's @reply$ are unaffected (no _s hoisted scalar → copy-out reads from xb_str_reply as before). Verified: XstErrorNumberToName(0) → "$$ErrorObject too large", XstErrorNumberToName(256) → "$$ErrorObject too large" (SHARED arrays uninitialized, UBOUND=-1, object > upperObject always true). All gates green: frontend 26/26, compiler 77 (70 lib + 4 dec + 1 legacy + 2 source), behavior 340 checks (175 pure + 165 stateful), demo 27/27, sync 64/64.

CGEN-ARGSPLIT-STRLIT — call-arg splitter string-literal-aware ✅ done [2026-08-23, d9d665b]

FIXED (d9d665b): all 8 paren-depth counters in cgen.x (emit_args$, emit_args_n$, emit_msub$, first_comma_part$, emit_mtotal$, emit_flat2d$, emit_d1$, scan_dyn$'s comma counter) now skip string literals (Rust {:?} format: "..." with \" escapes) when counting parens/commas. Previously, a ( or ) inside a string literal in a call argument would mis-split the args, producing malformed C. The workaround of using CHR$(40) instead of "(" in call args is no longer needed (but kept since removing it would be a no-op risk). first_expr$ already had this fix (inQuote flag). Byte-neutral: sync 46/46, bootstrap OK, all suites green.

COMPILER-X-FIXES — compiler.x underscore/bracket/single-line-IF/MID$-assign ✅ done [2026-08-23, 7fd48f8]

FIXED (7fd48f8): four bugs in selfhost/compiler.x prevented it from processing cgen.x (5430 lines), causing an apparent OOM (exit 137 after 118s with only 728 lines of IR). Root causes and fixes:

  1. Underscore identifiers: tokenizer only allowed A-Z/a-z as identifier start, not _ (ch=95). Identifiers like _fdTab1 were split into _ (symbol) + fdTab1 (ident), causing parser infinite loops. Fix: add ch=95 to identifier start check.
  2. Bracket array syntax: cgen.x uses [] for arrays (DIM pNames$[32], pNames$[i]), but compiler.x only handled (). Fix: add [] support to DIM handler, array assignment, expression parser, and closing bracket handling.
  3. Single-line IF: IF x=1 THEN y=10 was treated as multi-line IF, causing incorrect nesting. Fix: detect single-line IF in stmtState=3; two-phase singleLineIf flag (1→2) emits end if after body statement; must pop ifStack (not just decrement ifDepth).
  4. MID$ assignment: MID$(s$,pos,len)=val was parsed as compare(call MID$(...) = val) instead of mid_assign. Fix: detect MID$ followed by ( in statement parser; add COMMA_OR_RPAREN expression stop condition; multi-phase stmtState 18-21 to parse target/start/length/value.

Also includes O(n²) src$ concatenation fix (SPACE$ pre-allocation + MID$ in-place fill) and @user[] array reference skip.

Results: compiler.x processes cgen.x in 0.09s (was: OOM after 118s); native compiler IR matches Rust IR byte-for-byte for both compiler.x and cgen.x (zero diff); native_compiler_emits_cgen_ir_for_cgen test un-ignored and passes; compiler_self_compilation_produces_identical_ir passes; all workspace tests pass (0 failed, 0 ignored).

CGEN-NESTED-FN — IMPLEMENTED [2026-08-22, 823008f]

DONE (823008f, sync 28/28, bootstrap fixed point intact, no demo regression): cgen.x now emits single-level nested functions as inline xb_label_<name>: blocks per the design below. awrite (a nested-fn cc-fail) now compiles; the other 18 nested-fn demos are unblocked a layer but stay multiply-blocked on dual-dim/SHARED, so demo faithful is unchanged at 67 (awrite is a GUI interp-timeout, not differentially scored). Locked by cemitter_and_cgen_agree_on_nested_function (minimal non-GUI repro). The design as built, retained for reference:

The 19 function definition is not allowed here cc-fails are all single-level nested functions (verified: 0 demos nest deeper than one level). A nested INTERNAL FUNCTION (e.g. Callback/CreateWindow inside XitMain) captures the parent's locals and is invoked via a computed GOSUB through a dispatch array (gosub_expr Sub[message]). C forbids nested function definitions, so cgen.x's streaming emitter — which PRINTs a xb_user_<name>(...) { for every function token — emits an illegal nested def.

Rust's scheme (the reference to mirror), from awindow's xb_user_XitMain:

  1. Every nested function's **locals + its own name (retval) are hoisted into the

parent**'s declaration block (intptr_t xb_var_Callback = 0; etc. at the top of xb_user_XitMain).

  1. Each nested body becomes a labeled block xb_label_<name>: placed after the

parent's main body (and after the parent's own return-dispatch, so normal flow never falls into it) but before the parent's final return.

  1. funcaddr(NestedFn) lowers to ((intptr_t)&&xb_label_<name>) (address-of-label),

NOT a function pointer — that value is what the Sub[] dispatch array holds.

  1. The nested block ends with the gosub-return dispatch

(if (xb_gosub_sp > xb_gosub_base) goto *xb_gosub_stack[--xb_gosub_sp]; return …;); Rust uses a per-function xb_gosub_base = xb_gosub_sp so a nested block's bare RETURN unwinds to its GOSUB caller, not the outermost frame.

cgen.x changes required (a dedicated pass, byte-neutral on selfhost — the self-host tools nest no functions, so the whole path is a no-op there and the bootstrap fixed point is protected by the sync gate regardless):

  • Track a single-level nest flag + a separate nestBlocks$ buffer. A function

token while already inFunc starts a nested block (record the name in a nestFns$ set; append xb_label_<name>: to nestBlocks$) instead of PRINTing a new C signature. Its statements append to nestBlocks$, and its locals/DIMs still feed the parent's usedSyms$/dimmedSyms$ so emit_hoists$ declares them at the parent top.

  • A nested end function appends the gosub-return dispatch and clears the nest flag

(staying inFunc); the parent's end function emits hoists + parentBody + <return-dispatch> + nestBlocks$ + return + }.

  • emit_expr$'s funcaddr( arm checks nestFns$: a nested-fn arg → ((intptr_t)&&xb_label_<name>).
  • Adopt the per-function xb_gosub_base (replacing the bare xb_gosub_sp > 0 guard) so

nested-block RETURNs unwind correctly — verify byte-neutral on the self-host corpus (its GOSUB use is a single top-level frame, base 0).

Verify against awindow/abuffer (2 nested fns each, simplest). Several nested-fn demos are also multiply-blocked (SHARED arrays, dual-dim), so this unblocks a layer for all 19 and flips the subset whose only remaining blocker is nesting.

CGEN-SHARED-ARR design — qbtoxb, the last demo compile-fail [2026-08-20]

RESOLVED [2026-08-20] — qbtoxb compiles + is byte-faithful (114/114). The full end-to-end shared-array IR feature analyzed below was not required. Two observations collapsed it to a bounded, structural fix: (1) qbtoxb reads stdin, hits EOF, and exits before its translation logic runs, so its interp output is empty — the C backend only needs to compile and produce empty output, not model shared-array runtime semantics; (2) xbasic$'s element-type facet was already fixed (facet 1, b1e0353: shared_name_suffix). The residue was two scoping/naming bugs, both fixed structurally in 6153215: #line REDIM'd inside an IF became a block-scoped C VLA that later out-of-block uses couldn't see (→ CGEN-NESTED-DIM: nested-block array DIMs force to dyn), and token[] was an array param also read as a scalar token = token[i] (→ dual-use array param split under CGEN-SCALAR-ARRAY-DUAL). Both triggers are structural, so they round-trip the frozen text IR; the corpus has neither pattern, so cgen_cemitter_sync 5/5 + bootstrap held. True shared-array runtime semantics (module arrays as real C globals) remain a non-blocking future feature (CGEN-SHARED-ARR, ary-class programs). The analysis below is retained as the record of the deeper feature it was mistaken for.

qbtoxb (a 2800-line QuickBASIC→XBasic translator) is the only remaining C compile-fail (113/114). Its 7 cc errors trace to two variables, both facets of the SHARED-array feature — a coordinated change, not a bounded fix, so it is scoped here rather than attempted reactively (a wrong move risks the cgen.x byte-identity lock; the payoff is one demo, 113→114):

  1. xbasic$ — String-array element type under a shared/local/type-conflict tangle. STRING #xbasic$[] (a #-shared String array, src line 271) coexists with a local DIM xbasic$[#xuline] (line 1671) and an Integer xbasic. The analyzer lowers the DIM as dim xbasic$:integer (wrong — it is a String array), so the dyn-array hoist emits intptr_t* xb_var_xbasic_s while element writes use char** xb_str_xbasic_s → undeclared-identifier + intptr_t*-vs-int cc errors. Fix needs the analyzer to type a $-suffixed array as String even under an Integer/shared name conflict, and the emitter to carry the String element type through the (possibly shared) array.
  2. line — cross-function array flow. @line[] is a by-ref array param of TranslateLine/TranslateStatement (src 105-106), but line[i] is also used in a function where line is neither a param nor a local DIM → undeclared xb_var_line. Fix needs shared/threaded array storage across the call chain (the interp shares one slot; the C backend lowers each function's line independently).

Both reduce to: SHARED/#-prefixed and cross-function arrays must lower to correctly-typed C globals (or a shared {data,len,elem} descriptor), not per-function locals, with the analyzer's $/type-suffix resolution kept consistent for shared arrays. Prerequisite verified: no SHARED array appears in the self-host tools or v0.1 corpus — BUT the corpus does use shared string scalars (##XBDir$, ##funcTypes$, ##sharedDecls$, …) that are currently typed Integer (the embedded $ is not read as a suffix) and lower to intptr_t globals holding punned char* values — and the bootstrap depends on exactly that lowering. So the fix must type shared string arrays as char** without changing shared string scalar typing, and MUST be gated on cgen_cemitter_sync + the native bootstrap fixed point before landing. This entanglement (the "bounded" element-type fix touches load-bearing shared-scalar typing) is why qbtoxb is a deliberate coordinated effort, not a reactive patch.

Entry point (turnkey). xb_frontend::parser::typed_dim_stmt (parser.rs:318) is where a <TYPE> name[] declaration loses three things at once: it skips the leading element-type keyword (STRING), takes a #-prefixed SharedName (#xbasic$) as the bare name "xbasic$" with suffix: None, and hardcodes shared: false. So STRING #xbasic$[] emits Dim { name: "xbasic$", suffix: None, shared: false } → dim xbasic$:integer. The feature therefore starts there: (a) capture the type keyword / read the SharedName's trailing $/!/# as the element type, (b) set shared: true for a SharedName; then (c) semantics::dim routes a shared array to module-shared storage, and (d) the C emitter lowers shared arrays to correctly-typed globals — each step gated on cgen_cemitter_sync + the bootstrap fixed point, and none allowed to change the Integer typing of shared string scalars (assignment-path, not dim).

Progress [2026-08-20]. Facet 1 (element type) landed (b1e0353): Parser::shared_name_suffix splits a SharedName's embedded $/!/# in both dim_stmt and typed_dim_stmt, so STRING #xbasic$[] / DIM #xbasic$[] now type uniformly as String (dim xbasic$:string) — byte-neutral (interp 183/0, bootstrap MATCH, sync 5/5, LLVM 105/0), resolving qbtoxb's xbasic$ element-type error class. Facet 2 (the harder core) is now mapped to code: #line is a shared dual-use slot — a scalar (XLONG #line, #line = n) AND an array (#line[i], REDIM #line[n]), used cross-function (OutputToken, TranslateLine). But #line[i] lowers to a local array_assign line:integer[…] (a Symbol, not a SharedVariable), because parser::primary routes a SharedName through identifier_expr — dropping the # for array/subscript positions. Facet 2 thus needs: (i) primary/semantics to route a SharedName array access/REDIM to shared storage (a SharedVariable), not a local Symbol; (ii) the emitter to lower a shared array to a module global (xb_shared_line_arr + xb_ub_…), reusing the local dual-use split (scalar xb_shared_line + array xb_shared_line_arr) for a shared slot; (iii) REDIM #line[n] to resize it. This is shared-array-globals + shared-dual-use + REDIM combined — a multi-step feature, each step gated on sync + bootstrap, none touching shared-scalar typing.

Scope depth (why facet 2 is a major feature, not a parser tweak). The SharedVariable IR node is scalar-only: semantics::array_access has no shared path, so even ##name[i] lowers to a local ArrayAccess/array_assign (untested — the corpus has no shared arrays, only shared scalars). Routing the parser's SharedName reads to shared therefore does not suffice; facet 2 needs a new end-to-end shared-array concept: parser marks a shared array access/assign/REDIM, semantics tracks shared-array names, a new IR node carries the shared-array op, and the emitter lowers it to a module global. That threads all four layers (parser/semantics/IR/emitter) — the reason qbtoxb (one demo, 113→114) is a dedicated feature, not the reactive continuation of facet 1.

1–4. Backends, runtime semantics, migration coverage, demos (archived)

The 2026-08-17..20 narratives that occupied these sections are in docs/22 §2. Everything in them is closed, superseded, or re-rowed above; nothing there is an open row.

5. Cross-references

  • Two-C-generator drift and byte-identity: 16-cgen-cemitter-sync-roadmap.md.
  • Backend rationale / crate survey: 12-rust-llvm-rewrite-survey.md.
  • Stage status and decisions: 13-bootstrap-scaffold.md, 14-self-hosting-progress.md.
  • Forward milestone sequencing of the open rows (M1–M6 to "everything legacy + full bootstrap"): 20-port-completion-roadmap.md.