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-functiontool/windowmasks 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 tonative_pipeline::native_compiler_emits_cgen_ir_for_cgen(cgen3(cgen.x IR) == cgen1(cgen.x IR));checks/verify-bootstrap.shhard gate restored. The same-day unsized-array/facet slice preservesDIM 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 toscan_all_strarr$for headerless producers), adds a scanner-hostile three-engine behavior lock, and fixesarr_acc_name$/ub_ref$to use directchar**/xb_ub_naming for non-strDual allStrArr members.checks/validate-all.shpasses 371 tests across 41 binaries;checks/verify-bootstrap.shis green, includingcgen_cemitter_sync115/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 lacksINLINE$and kernel32 I/O (ahello.xskipped,llvm_backend_kernel32_stdioignored with reason).Gate parallelism note (2026-09-05, cause measured 2026-09-06): both
checks/validate-all.shandchecks/verify-bootstrap.shuse--test-threads=1to prevent OOM process kills when 115cgen_cemitter_synctests each spawn external cgen+cc processes on an 8-core machine under load.cargo testruns test binaries sequentially;--test-threads=1only affects within-binary parallelism. The kills were not only concurrency: one native cgen.x onxgr.xpeaked 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.
Status:
link-core-libs.shcombines GPL-header sources (xcol/xit/xdis), LGPL-header sources (xcm/xma/xui/xgr/xin/xrun/xst/xut/xutpde), and three no-notice shims (gdi32537 lines,kernel321004,user32669) into onexblibsartifact. 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/(upstreamsrc/shared6 +src/linux9, ported verbatim by TRACKED-CORPUS-PORT 2026-08-31). The harness performs local compile/link plus sevenVersion$calls, not behavioral adoption. - Remake crates: All 7 crates (
xb-cli,xb-compiler,xb-frontend,xb-gui,xb-ide,xb-link,xb-runtime) declareMIT(relicensed in62c617b). Original code is under MIT; ported upstream code inxbasic/remains under GPL-2.0 / LGPL-2.1.
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.
| Order / track | Job | Exit gate |
|---|---|---|
| done | cgen_x_compiles_all_demos_cc_clean ok (raw, 114/114) [verified 2026-09-02] |
|
| done | cemitter_and_cgen_agree_on_positive_corpus ok (byte-identical) [verified 2026-09-02] |
|
| 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 | 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 | 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 | 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 | 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 | 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 | 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 | (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 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 | 8fe02ce) |
15/15 core libs compile clean via self-hosted cgen.x (xui/xin/xit/xst fixed) |
| done | 8fe02ce) |
xcol/xgr no longer OOM/signal; 15/15 locked |
| done | 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 | 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 | 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 | 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 | 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 | 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.xmust pass all four gates: zero demo/corpus/library regressions,IR_IDENTICALbootstrap parity, automated negative collision coverage, and a documented retirement path under RR-03. A candidate that fails the first three gates is closed; single-lettera/k/arrayreplacements 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_integrationexecutes sevenVersion$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.
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 Kittedyfounddual-use and qbtoxbTranslateStatementforward-decl internally. The Rust CEmitterdemo_parityguard 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))emittedxb_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 XuiGetNextCallbackwas stubbed to0, hanging event loops. Ported headless GUI runtime from Rust CEmitter:xb_gui_next_callbackdelivers one synthetic CloseWindow. Also fixed string SELECT CASE (used==pointer comparison instead ofxb_scmp) and integer byref arg emission (fell through to0for plain integer byref).CGEN-BYREF-WRITEBACK ( a6b3565)adrawing RandomN(float)declared byval in IR but called with byref at all sites →double*vsdoublecc error. Fixed:scan_byref_wb$detects all-byref functions;emit_params$emitsT *X_refpointer params with copy-in/copy-out.CGEN-SHARED-STR-INIT ( bab451c)amakemap $$PathSlashshared string scalar initialized to 0 (NULL) →xb_concatcalledxb_len(NULL)→ SIGSEGV. Fixed: shared string scalars initialized toxb_str("")inmain()(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 XgrProcessMessagesis a stub that never dispatches GUI callbacks; demos calling it inDO...LOOP UNTIL terminateProgramhung forever. Fix:XgrProcessMessagesnow callsexit(0)immediately (interpQuit { code: 0 }, Cxb_xgr_process_messagesruntime), 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]withSub[message]=0(nested fn label_addr now 0) emittedgoto *(void*)0→ SIGSEGV in both C backends. Fix: guard withif (_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_arrfor##byrefDual$names when they're array params of the current function (##arrParams$check) — previously global##byrefDual$causedbd$("grid$")to return_arrin all functions, breaking atools/aquick.##arrParams$and##curParams$now set BEFOREemit_params$call (was after). Scalar facet emission in##dynNames$branch skipped for scalar params (no redefinition). Condition 2 inscan_byref_dual$restricted to non-$names (string names appear asbyref(symbol(grid$:...))in call args, falsely matchingINSTR). 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_arrsuffix + pointer type (matching##byrefDual$), sogrid$:string[]emitschar** xb_str_grid$instead ofintptr_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$detectsswap X:type Y:typeoperands. 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.
groupdemosstatusnetwork socketsaclient, aserver✅ now compile (were passing via Rust CEmitter; cgen.x param fix unblocked them) Xst runtimeasortie✅ now compiles (was string[] param emission, not Xst runtime) multi-dim SHARED + composite + gridadatadim, aquick, arecord, CursorEdit, Kittedy✅ now compile (were string[] param emission) memory-model / ANY / ATTACHgif, gifview✅ now compile (were string[] param emission) LANDED 2026-08-22 (
b5bd930) — the 3-fix pass that flipped aarray (98→100): (1) byref-dual_arrsplit, gated toscan_byref_dual$=##dynNames$ ∩ user-fn-param(EMPTY on all faithful demos + selfhost tools → byte-neutral): array facetxb_var_X_arr(the by-ref param pointer) + scalar facetxb_var_X, resolving the param-vs-hoist redefinition. (2)TYPE(x)→ non-zero (was unknown-call0): interp returns the value's non-zero type number, and with the lowered type constants all0a0stub wrongly fell throughIF type<>0 THEN RETURN; same class as the*ATstub; no faithful demo usesTYPE. (3) per-functionxb_gosub_base(CGEN-GOSUB-SCOPE — was in the Rust CEmitter, missing in cgen.x): each gosub-using function capturesint xb_gosub_base = xb_gosub_spat entry andRETURNpops only whilesp > base(wassp > 0), fixing a cross-function gosub-stack corruption (a callee'sRETURNreached under a caller's active GOSUB didgoto *stack[0]into another function → jump-to-null crash). Locked bycemitter_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](wasINSTR(tmp$, "= ")from the start): a$$-constant index likeconstant($$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 paramintptr_t* xb_var_X_arrre-DIM'd viacallocsetxb_ub_X_arrwith no decl (the hoist's param-skip dropped it) → emitintptr_t xb_ub_X_arr = -1;. Locked by addingaarray/aarray_ISNODE/zaptocgen_demo_regression'sDEMOS; 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_arrsplit, 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 facetchar* xb_str_X+ heap array facetchar** xb_str_X_arr+xb_ub_X_arr; scalar uses stay bare, array access/assign/DIM/UBOUND take the_arrsuffix (via the extendedbd$). (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'stext$is dual-use inCreateGridsbut undimmed inMenuBar). Locked by addingatoolstoDEMOS; 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 emitsintptr_t xb_var_grid$(wrong type +xb_var_prefix) instead ofchar** xb_str_grid$(a narrow, correct fix, but 0-flip alone). (b) forward-referenced string arrays need heap-hoisting:grid$/image$are DIM'd inEntrybut array-accessed earlier via the GOSUB-inlined SUBsSwitchWindow/ReportCallback(placed before the DIM), so the mid-function stack declchar* xb_str_grid$[N]isn't in scope at the access — they must hoist tochar** 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 ascan_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 |
|---|---|---|---|---|
| C backend | ✅ done (2026-08-20): auto-vivified array hoisting + dynamic DIMs + undimmed-array folds | — | ✅ | |
| C backend | ✅ done (2026-08-20): arity reconciliation (drop extras, pad missing) via DEFINED_SIGS |
— | ✅ | |
| C backend | ✅ done (2026-08-20): INLINE$, EOF, RIGHT$/LEFT$ 1-arg, STRING$ via xb_string |
— | ✅ | |
| 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 |
— | ✅ | |
| 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 | ✅ | |
| 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. |
|
| 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) | — | ✅ | |
| 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) | ✅ | |
| 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) | ✅ | |
| 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) | ✅ | |
| 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 ✅ |
✅ | |
| 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. |
✅ | |
| 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 |
◑ | |
| 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) |
✅ | |
| 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) | ◑ | |
| 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 ✅ |
✅ | |
| 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) |
— | ✅ | |
| 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) |
— | ✅ | |
| 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 ✅ |
✅ | |
| 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 |
✅ | |
| 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 | ✅ 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 |
| 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 |
| 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 | |
| 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) |
| interpreter + C backends | ✅ done (2026-08-30; reversed to move semantics 2026-09-05) — 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) | |
| 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 | ✅ | |
| 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$ |
✅ | |
| 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 | |
| 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 |
✅ | |
| 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 | ✅ | |
| 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 | ✅ | |
| 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) | ✅ | |
| 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 | ✅ | |
| 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) |
| 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) |
# 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.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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 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 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 |
| 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 |
| 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) | |
| 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 | ✅ | |
| 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) |
| 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 |
| 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. |
— | |
| 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. |
— | |
| 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.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.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 |
| 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. |
— | |
| 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 |
| 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) |
— | |
| 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 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.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). |
— | |
| interpreter + C backends | ✅ done (2026-08-30; reversed to move semantics 2026-09-05) — 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 | |
| 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. |
— | |
| 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 |
| 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 | |
| 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. |
— | |
| 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. |
— | |
| 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. |
||
| 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). |
||
| 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. |
||
| 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. |
||
| 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). |
— | |
| 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. |
— | |
| 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 |
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 | — |
| Test-harness hardening ($CC, release-bin dep) | done 2026-08-30 | xb-link respects $CC; 51 xb-compiler test cc calls respect $CC |
— | |
| LLVM CI job | done 2026-08-30 | llvm-build job in bootstrap-verify.yml: installs LLVM 22, builds + tests with --features llvm |
— |
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.0001are 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 andATTACHare 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/*.xor 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 |
|---|---|---|---|---|
| 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). | — | |
| 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. |
— | |
| 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) |
— | |
| 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 |
— | |
| 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 |
| 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 |
— | |
| 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 |
| 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. |
— | |
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. |
— |
- 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 |
- 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.
- 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.
- Docs/19 reports the current cgen size, makes remaining scanner retirement explicit, and orders scanner deletion before physical modularization.
- Docs/20 replaces stale M1 entry steps with active contract repair, facet/ABI completion, a verification matrix, and a post-retirement modularization decision gate.
- Docs/README defines canonical authority without creating a new roadmap.
- Stable charter/contract/ledger anchors are deterministic documentation guards; volatile counts and prose are not.
- 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.xalso 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.1is 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.
- 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.
- 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.x8,674 lines, 72 functions;Main2,089 lines,emit_expr$about 1,145, andemit_stmt$1,173; 188 integration#[test]functions undercrates/**/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_testmismatch 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 |
- 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 |
- docs/16 must say demo text identity is de-scoped, not open.
- docs/18 and docs/README must bound the landed descriptor to primitive/flat
arrays and mark composite
TYPEarrays open. - docs/19 must say substantially implemented (scope-qualified lookup done,
15/15 core libs);
strDual/allStrArrremain heuristic (no corpus trigger). - 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.
- The 114/114 demo headline is now a raw-generator contract (RR-13 done 2026-08-30); no post-emission C rewrites remain.
- Historical 6/15 panel measurements must be labeled snapshots, not current status. The top banner and execution queue are authoritative.
- The previous sole workspace failure (shared
ARY_VAR_DATAdescriptor) is now compile-clean viais_shared_array→emit_raw_array_name;xbsourcelibis 13/13 andary/ary1.0001are 2/2 cc-clean. RR-02 is done for compile; runtimeARYmove-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.
All resolved 2026-08-30 (xui/xin/xit/xstare scope/type classification failures.xcol/xgrare generator resource failures.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 byxb_appendcap + 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.
ATTACHhas 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.
- 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 -eplus direct smoke execution already propagates failure. Remaining RR-10 gaps are duplicate reporting and the informational cgen probe (XB_BINoverride fixed).
- 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, andvalidate-all.sh.Version$checks, self-hosted libraries 15/15 (raised from 9/15 at8fe02ce). 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 |
| 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 |
| 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 |
— | ||
| 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 | ||
| 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 |
— |
| 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
.xlibs?" Packet HEAD3c5d9a7+ uncommittedc_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 (likelytask-backendresource_exhausted/wall timeout perparallel-lens-review-packetrecovery 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-hostedcgen.xBar B is unready/slow (6/15 probe not re-run; xcol wall probe viacgen_expis 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 -Unon-determinism;xb_appendnot a drop-in (NULL UB on=0shared strings, aliasing/moved-realloc hazard, empty-chain panic,capnot retained — every call stillreallocs, not exponential buffering);scan_dynper-linesymbol(only (DIM/SWAP already per-line) andscan_undimmedstill whole-s$— index-equivalent, not a line split, neither skipsstring("…:…");abs_float.irhas noarray_*/colon-in-literal so not a byte-equivalence witness; zapargv$storage=fixedcitation 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 ofxb_version_strvsT_totalto separatesrc$build from post-src$scans/emit +fwdDeclsBuffront-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 ofreflective-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).
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], holdinglabel_addrvalues), used before its DIM. Rust splits it into a scalar facetxb_var_Sub+ a dyn array facetxb_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_arrrename (a missed site →subscripted value is not an array); (3) provide typed facets —char*/char**for a string dual-use liketext$(the nested-fn demos hitredefinition 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 (adim X:integer[]name also used asfor X:) — verified empty on the selfhost corpus (sync stayed 45/45, byte-neutral) and correctly catches zap'si/o. The full_arrrouting (hoist scalar+array facets, ubound, access, assign, calloc via adua$(n$)suffix helper) was implemented and made zap's array facet emit cleanly. But it is 0-flip: zap's dual-usei/oare also by-ref array parameters ofCommandLine(i:integer[], …), so the emitted scalar facetintptr_t xb_var_i = 0;redefines the parameterxb_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 cleanfaithful=98, diverge=0state (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 DIMarrayas a 2-D dual-DIM) went from the scalar-vs-arrayredefinition of xb_var_arraycc-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 —arrayis a by-ref array param ofPrintArray, so the dyn-decl redefines the param). That param-skip re-introduced the by-ref-array-param regression: it madeaarraycompile but diverge (itsPrintArray(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'scollect_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_arrsplit, 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 namexb_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 namexb_var_X, so the scalar facet redefined the param — the exact cc-error that killed each attempt.- Concrete (aarray
PrintArray(array[]),arrayis 2-D dual-use): Rust emits fwdPrintArray(intptr_t *xb_var_array), defPrintArray(intptr_t *xb_var_array_arr), local scalarintptr_t xb_var_array = 0;, array facetxb_var_array_arr = calloc(…)(2-D flattened),UBOUND→sizeof(xb_var_array_arr)/…, call-sitePrintArray(&xb_var_array)(passes the scalar facet's address, a quirky but interp-faithful by-ref). cgen.x today emits the array decl asxb_var_array(collides, 458),UBOUNDsizeof(xb_var_array[0])on a scalar (526/535 subscript error), and the call asPrintArray(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 asxb_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.
Attempted a THIRD time and REVERTED (
d7e2249+ cgen.x mirror, reset toe62d03f). The prior two reverts are noted in the CGEN-BYREF-ARG row; this pass pins down why theemit_array_var_nameapproach keeps regressing, so it is not re-attempted a fourth time. The bug is synthetic.emit_call_argsemits&xfor a by-ref array arg. For a syntheticDIM a[3]; Fill(@a[]); arr[1]=42,ais a dual-use DIM'd array, so&xpasses its 1-slot scalar half →arr[1]runs OOB →42/<garbage>vs interp5/42. The bounded fix (route array params throughemit_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.x5/42). BUT it regresses real code:XBSourceLib/fgr/fgr.x+geo/geo.x(both faithful, gated bytests/cgen_demo_regression.rs::cgen_matches_interpreter_on_xbsourcelib) pass by-ref arrays that are undimmed dual-use / composite members / scalars, where the existing&xis exactly what the callee expects. The!is_undimmed_arraygate cut Rust fgr errors to 1 (a composite-member subtlety remained) and the cgen.xbyref(arm broke fgr with 20 cc errors. Root insight: real byref-array usage in this corpus is UNDIMMED (no_arrstorage) —&xis correct; the DIM'd-array-byref case thatemit_array_var_nametargets does not occur in real code (only in a synthetic repro). The full-workspace suite (not the demo differential — XBSourceLib is not indiff_cgenx.sh) is what catches this; always runcargo test --workspacebefore finalizing a byref change. Do not reopen without a real failing demo (none exists). cgen.x's ownbyref(gap (CGEN-EXPR-GAPS) is likewise a non-issue: its demos need undimmed&x, which the pre-existing default already approximates.
FIXED (2026-08-30):
XstErrorNumberToName(error, @error$)computes the error message intoxb_str_error_s(local string storage with_ssuffix) but the copy-out writeback read fromxb_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.errorInteger +error$String), the analyzer'sscan_body_collisionsdetects 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_scalarsrecords which byref String params have a$-suffixed hoisted scalar (viarecord_byref_str_s), andemit_byref_copy_outreads from the_svariable for those params (*xb_str_error_ref = xb_str_error_s). The_refpointer name stays unchanged (matching the signature). Non-collision cases like Kittedy's@reply$are unaffected (no_shoisted scalar → copy-out reads fromxb_str_replyas 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.
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 usingCHR$(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 inselfhost/compiler.xprevented it from processingcgen.x(5430 lines), causing an apparent OOM (exit 137 after 118s with only 728 lines of IR). Root causes and fixes:
- Underscore identifiers: tokenizer only allowed A-Z/a-z as identifier start, not
_(ch=95). Identifiers like_fdTab1were split into_(symbol) +fdTab1(ident), causing parser infinite loops. Fix: add ch=95 to identifier start check.- 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.- Single-line IF:
IF x=1 THEN y=10was treated as multi-line IF, causing incorrect nesting. Fix: detect single-line IF in stmtState=3; two-phasesingleLineIfflag (1→2) emitsend ifafter body statement; must popifStack(not just decrementifDepth).- MID$ assignment:
MID$(s$,pos,len)=valwas parsed ascompare(call MID$(...) = val)instead ofmid_assign. Fix: detect MID$ followed by(in statement parser; addCOMMA_OR_RPARENexpression 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_cgentest un-ignored and passes;compiler_self_compilation_produces_identical_irpasses; all workspace tests pass (0 failed, 0 ignored).
DONE (
823008f, sync 28/28, bootstrap fixed point intact, no demo regression): cgen.x now emits single-level nested functions as inlinexb_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 bycemitter_and_cgen_agree_on_nested_function(minimal non-GUI repro). The design as built, retained for reference:
The 19
function definition is not allowed herecc-fails are all single-level nested functions (verified: 0 demos nest deeper than one level). A nestedINTERNAL FUNCTION(e.g.Callback/CreateWindowinsideXitMain) 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 — whichxb_user_<name>(...) {for everyfunctiontoken — emits an illegal nested def.
Rust's scheme (the reference to mirror), from awindow's xb_user_XitMain:
- 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 ofxb_user_XitMain).
- 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.
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.
- 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-functionxb_gosub_base = xb_gosub_spso 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. Afunction
token while already
inFuncstarts a nested block (record the name in anestFns$set; appendxb_label_<name>:tonestBlocks$) instead ofnestBlocks$, and its locals/DIMs still feed the parent'susedSyms$/dimmedSyms$soemit_hoists$declares them at the parent top.
- A nested
end functionappends the gosub-return dispatch and clears the nest flag
(staying
inFunc); the parent'send functionemitshoists + parentBody + <return-dispatch> + nestBlocks$ + return + }.
emit_expr$'sfuncaddr(arm checksnestFns$: a nested-fn arg →((intptr_t)&&xb_label_<name>).- Adopt the per-function
xb_gosub_base(replacing the barexb_gosub_sp > 0guard) 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.
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 in6153215:#lineREDIM'd inside anIFbecame a block-scoped C VLA that later out-of-block uses couldn't see (→ CGEN-NESTED-DIM: nested-block array DIMs force to dyn), andtoken[]was an array param also read as a scalartoken = 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, socgen_cemitter_sync5/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):
xbasic$— String-array element type under a shared/local/type-conflict tangle.STRING #xbasic$[](a#-shared String array, src line 271) coexists with a localDIM xbasic$[#xuline](line 1671) and an Integerxbasic. The analyzer lowers the DIM asdim xbasic$:integer(wrong — it is a String array), so the dyn-array hoist emitsintptr_t* xb_var_xbasic_swhile element writes usechar** xb_str_xbasic_s→ undeclared-identifier +intptr_t*-vs-intcc 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.line— cross-function array flow.@line[]is a by-ref array param ofTranslateLine/TranslateStatement(src 105-106), butline[i]is also used in a function wherelineis neither a param nor a localDIM→ undeclaredxb_var_line. Fix needs shared/threaded array storage across the call chain (the interp shares one slot; the C backend lowers each function'slineindependently).
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.
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.
- 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.