From 51f0d6205b2ceb78c64ecbfc09ad2056de671a4a Mon Sep 17 00:00:00 2001 From: zinc Date: Wed, 14 Oct 2026 10:53:36 +0000 Subject: todo: record each agent's state at the end of the session and the merge order --- TODO.md | 193 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 170 insertions(+), 23 deletions(-) diff --git a/TODO.md b/TODO.md index 28ecdd4..210b4a3 100644 --- a/TODO.md +++ b/TODO.md @@ -247,6 +247,14 @@ the reasoning behind the open items survives the session. latency (microseconds per block against milliseconds for llvm), which matters for short programs and the language server. +- where to pick up (end of session): merge the unmerged agent work in this + order: line tables first (bounds checking and the conformance suite both + want positions in their failures), then bounds checking on top of it, then + the spec conformance suite, then the xref baseline and its ci check. test + blocks and destructors come after, since they are features rather than + safety nets. each agent's state and next step is in "agent work at the end + of the session" below. + ### feature decisions (owner's requirements, settled) - bounds checking is on by default; `unbounded` (used like `unchecked`: function prefix, block, top-level statement) turns it off; `o3` implies @@ -282,21 +290,160 @@ the reasoning behind the open items survives the session. phase files, `docs/SPEC.md`, standard-library api docs and `examples/std_tour.zc`, temporary notes removed, `PROPOSALS.md`. -### in flight when this was written (not on the branch; re-brief if missing) -these were running as agents in worktrees. if their branches were not merged -before the session ended, the work is lost and each item below must be started -again from its description in the open list. a zip of the repository with every -worktree is uploaded to the owner after each update. -- spec verification, restarted after an interruption. the first agent's - partial work (runner plus sections 1 to 3, about 375 programs) is committed - on the local branch `worktree-agent-a020917eebd018418` (3edfbaa) and the new - agent continues from it; see "verify both compilers". -- runtime line tables and backtraces, see "runtime source positions". -- bounds checking with `unbounded`, see "bounds checking". -- the cross-reference report, see "a cross-reference report". -- test blocks as a library (`src/lib/test.zc`, `zy --test`), see "test blocks". -- struct destructors (`fn drop()`) together with the `defer` single-statement follow-up, see "struct destructors". -- record and replay at the syscall gate (`ZYRECORD`/`ZYREPLAY`), see "record and replay". +### agent work at the end of the session (unmerged; read this first) +the owner stopped the session with seven agents running. each was asked to +commit what it had and report. their worktrees were copied next to the main +code in the final zip (`agents//`, with `AGENT-COMMITS.txt`, +`AGENT-CHANGES.diff` against the base they branched from, and +`AGENT-UNCOMMITTED.txt`). none of it is on this branch. to resume one, apply its +diff to a fresh branch (or re-create its worktree from the commits listed) and +continue from "next" below; the original architecture is in the matching item +of the open list. + +- line tables and backtraces (`agents/line-tables`, 3 commits, last 917bc34). + done and checked on zy, zym and the javascript vm: `lines on;`/`lines off;`, + `rt.trace()`, traces after the unchanged first line of `oob`, bad heap + pointer, use after free/double free, out of memory and frame overflow. per + function line blocks [next, entry, end, name, file, count, offset<<32|line...] + emitted into the code stream and chained from `rt.line_blocks`; compiler side + `lt_*` in base.zc plus hooks in stmt, function definition, closures and eval; + image.zc patches the image's own `rt.line_blocks`; check mode skips it. gate + op 56 returns the return-stack pointer (core.c `case 56: return cb_r;`, + micro.c from `r`, tinyvm.ts `this.rr`; wasm and baremetal go through micro.c; + jit and llvm paths untested). zc0 emits line tables for run-only images, via a + `//:lines NAME` marker that automap.sh puts before the program; the resident + compiler honours the same marker, so `build_image` images get tables too + (`runtime_image_test.py` has a case). stdin source is named ``. tests: + trace.zc, trace_more.zc, trace_run.zc with `.expected` files; test.sh fault + checks changed to `head -1`. genome grows 383672 to about 391049 words (+1.9%) + from code alone, with lines off. not done: README and SPEC section 8 docs, + the focused suites on the final build, the measurements (image sizes on/off, + mandel compile time, genome with lines on), the x86-64 jit path for op 56. + limits: o3 frames are not on the return stack; a function straddling code + segments is not recorded; core-level stack overflow messages have no trace; + the frame window (top 24 plus last 4) prints "... N more frames" with a raw + word count. merge note: main has the x86-64 jit since this branched; op 56 + must also work there (it reads `cb_r`, check the x64 hook sets it). +- bounds checking with `unbounded` (`agents/bounds-checking`, 2 commits, last + 46aa911). done, builds to the fixpoint: zc0 accepts and ignores `unbounded` + in all three positions; the compiler has the prefix (any order with + `unchecked`, `immediate`, `o3`; methods and closures), the block and the unit + form. fixed arrays with a variable index get an inline `outside` check and + `oob`; `for (i < K; i++)` with constant K proves a fixed array of at least K + (unverified that the check is really skipped). pointers get a one-instruction + `rt.bnd` enhancement test, then `rt.bchk` to report; interior pointers, stack + arrays and globals are not checked; constant index 0 and analyzer-known sizes + skip it. heap lengths are the buddy block capacity (`rt.new(4)` reports + length 11); exact lengths need a header change. `unbounded` and `o3` also drop + growable-array checks (changes `o3_bounds` and `bounds_hi`, which expected an + abort). `unbounded;` heads rt.zc, core.zc, base.zc, server.zc, run.zc, + build.zc; only actor.zc's header peeks in `encode` are marked. every program + is unchanged except `bounds_hi`, `o3_bounds`, `o3call`; errors, typeerrors, + langerrors, learn, icon and actor pass. cost: pointer kernel user time 0.58 s + to 0.76 s, fixed-array variable-index kernel about 35% slower. next: mark + `main` in tests/programs/o3call.zc `unbounded` (it reads `p[-1]` on purpose); + update the `o3_bounds` and `bounds_hi` expectations; add boundsrules.zc, + bounds_fixed.zc, bounds_heap.zc, zc0unbounded.zc with their test.sh lines; + docs (README, SPEC 2.6.1, 3.7.4, 3.13, keyword table, `kwtext`, vscode + grammar); time the self-compile (a baseline build is in the session + scratchpad `base/`, not in the zip); `make parity`. the 35% fixed-array cost + is worth a look before merging (hoist checks out of loops whose bound is the + array length, extend `proven`). +- spec conformance (`agents/spec-conformance`, last 808886b; the first, + interrupted attempt is `agents/spec-conformance-first-attempt`, already + folded in). done: sections 1 source (114 programs, zy 103 ok), 2 types + (142), 3 expressions (167, zy 165 ok). real self-hosted deviations found + (class a): a bad float or character literal in the first statement of a + function body is not reported (the lexer error is swallowed by lookahead); + `u*d` with `u`, `d` visible variables is taken as a recast (spec 1.6.3); the + bstr flag leaks into `int*` locals so `p[0]` reads a byte; the parameter + limit is 23 not 24 (decl.zc:326; closures 19, expr.zc:671); `void f() { + return 3; }` yields 3 not 0. spec issues (class c): 5.2.2 against 2.6.6 on + array parameters; `_` is an undocumented discard with a warning; an untyped + word stored into a float converts by value. zc0 (class b): mostly dropped + types (appendix a); b.1.11 literal saturation confirmed; new: INT_MIN / -1 + gives 0 and % gives INT_MIN in zc0. partly done: section 4 statements (129 + programs, zy 119 ok); six expectations are the agent's own mistakes to fix + (switch_break_in_loop_leaves_switch_only should be 5, + switch_expression_evaluated_once "hit1/", switch_label_forms calls t(-1), + t('a'), t(6), t(1), memdebug_report uses pn(rt.memreport()*100+7) substring + 107, memdebug_report_line line 9, asm_float_literal uses `word w`); real: + `memdebug scrub;` rejected (b.1.1/b.2.1 confirmed), top-level `o3 { }` + rejected (c), `case rect():` with fields possibly rejected (unresolved). + left: sections 5 to 8, the appendix b confirm/refute list, `known.txt`, the + `--zc0` runs, final counts, then add the runner to tests/test.sh. +- record and replay (no code; agent stopped during research). findings: every + syscall goes through `hostcall(op, a, b, c)` (core.c ~264) with three callers + (interpreter ~6324, jit `hook()` ~3617 which the x86-64 helpers also reach, + llvm ~5060): rename it `hostcall_real` and wrap it. stdin does not go through + the gate: the vm reads it with the Q opcode (io_linux.c; `J` is the end flag), + stdout with G, so log Q as (byte | J<<8) chunks flushed before each gate + record. the program source and its input share stdin (the compiler reads up + to the backtick with `srcend` in lex.zc), so replaying a modified program + needs a c mirror of `srcend`: serve the new source live, then answer the rest + from the log after skipping the old source. ops writing vm memory: 3 and 11 + (read), 5 and 13 (getenv), 22 and 24 (argv, env via `vecstr`), 9 sub-ops, 41 + (float format), 20 (scan), 43 to 45 (fork, channels); 18 and 19 are pure; + 14 to 17 (c abi) and 51 to 54 (contexts) hand out native handles: stop in + replay, and detect thread start (51, 43) in record. micro.c has its own gate. +- struct destructors and the `defer` single-statement form (no code; research + only). findings: `freevar(i)` (analyzer.zc ~611) is the only place an owned + local is freed, called from `exit_vars` (block end, return, break, continue, + deferred frees) and `fixups` (join paths); `borrow_cleanup_since` (~136) frees + fresh temporaries passed to borrow parameters (needs the argument type kept in + `emit_borrow_hold` to drop them); explicit `rt.free`/`rt.recycle` go through + `call()` in expr.zc, where `xty` is the argument type after `args()`; + reassignment never frees implicitly (`assigncheck` rejects overwriting a live + owner). a struct is `TY_STRUCT` with its name in `t[2]`, a pointer `TY_PTR` + with the struct in `t[4]`; find `drop` with `lookup(methname(name, "drop"))` + and `skind == K_FN`; reject generic, static, argument-taking or + result-returning `drop` in `kw_struct`. planned emission before the free, + when `fchk && !o3on && !dry`: `lit(0); op(O_PICK); skip = hole(); op(O_JZ); + lit(0); op(O_PICK); fnref(dropsym); op(O_CALL); op(O_DROP); setlit(skip, + here);`. the defer form: `defer_tokens`/`kw_defer` in stmt.zc. +- test blocks (no code; research only). findings: the native core never reads + a file argument (`zy foo.zc < src` keeps reading stdin; only the javascript + runner reads argv[2]), and any `--option` as argv[1] is rejected as unknown + in core.c main (~6743), micro/port_linux.c main (~375) and tinyvm.ts (~960), + so `--test` needs a small change in all three (accept it and keep it in + argv). `main` is called in `start()` in setup.zc (~187) through + `lookup(s_main)`, and `s_main` is a global of namespace `run`, so the library + can point it at `test.run_all` with no compiler change. plan: `src/lib/test.zc` + like actor.zc (`run.syntax("test", parse_test)`); without `--test` skip the + block by brace matching; with it compile the block through `run.tokens` and + `run.compile_list_context` as `test.case_N` and record [fn, name]; arm the + runner at include time; rewrite `expect(c)` to `if (!(c)) { test.fail(line, + text); return; }`. open: whether the keyword handler can decline when the + next token is not a string (so `test` stays usable as a name). diagnostics + print `zc: line N:` with no file name. +- compiler cross-reference report (`agents/xref-report`, one wip commit + e5d5136). done: `src/tools/xref.js` and `src/tools/xref.sh` (`--help`, + unknown option exits 2). the language server has no documentSymbol, so the + globals come from the tool's own parse of the joined compiler source (652 + globals, 628 functions, nothing left unparsed); writers and readers come from + one zclsp references request per global over 4 parallel servers. one full run + finished in 11m53s (about 2.5 s per request, about 30 s per server to check + the document); its output is in the zip at `agents/xref-report/xref-out/` + (report.txt, report.json), not yet reviewed by a person. top 15 globals by + number of writer functions, read off that json by the coordinator: xty 29, + xbor 23, xfloat 22, lv 21, xaf 21, lvk 20, xvk 19, ltag 17, xzero 17, + lstate 16, sflag 16, effect_value 15, fslots 13, panic 13, chkabort 12 (these + are the first candidates for the "group the parallel arrays" item). 60 + globals show no writer and 198 no reader (many are `_cap` twins); some of the + "no writer" ones are the known limits below, so check before trusting them. + written but never run: the `make xref` target, the README and ONBOARD.md + sections, the xref.sh entry in `tests/drivers/cli_help_test.py`. not done: + `tests/xref.baseline` (derive it from report.json, one line per global with + its sorted writers, or run `xref.sh --update` in the background), `--check` + and `--update` never run, the self-test (copy src/compiler to a scratch dir, + add one write to a global in another function, `xref.sh --compiler-dir DIR + --out SCRATCH --check` must exit 1), README says "minutes" where it should + say about 12 minutes. known limits: a store through a pointer (`*p = ...`), + an array written by a callee (`copy_w`, `memset`) and a global reached only + through its `_initial` array are not counted as writes. next: baseline, + `--check` (expect 0), self-test, `python3 tests/drivers/cli_help_test.py + --group scripts`, commit. a 12 minute run is too slow for every ci job; run + it nightly or only when `src/compiler/` changes. ### decisions - no structs in zc0 (owner's call): zc0 stays the small trusted root of the @@ -312,28 +459,28 @@ items with an architecture note carry the design worked out so far, enough to brief someone without re-reading the code. - [ ] remaining zc0 gaps: string literals over 4095 bytes, `embed` of an array type inside a function, `x as T` with more than a single name (`x as int*`), zc0 silently accepting `.u` on a parameter, and integer literals (`010` is 8 in zc0, 10 in the compiler; zc0 saturates on overflow). `docs/history/43-stdlib/notes.md` still says zc0 reserves u/m/d: add a pointer forward rather than rewriting history. -- [ ] (in flight) verify both compilers against `docs/SPEC.md` with a fresh agent that did not write it: expectations derived from the spec text before running anything, one program per rule under `tests/conformance/
/`, a runner `tests/conformance/run.sh`, each failure classified as self-hosted bug, zc0 bug, or spec error, and appendix b confirmed item by item. then add the runner to `tests/test.sh`. +- [ ] (partly done, see "agent work at the end of the session") verify both compilers against `docs/SPEC.md` with a fresh agent that did not write it: expectations derived from the spec text before running anything, one program per rule under `tests/conformance/
/`, a runner `tests/conformance/run.sh`, each failure classified as self-hosted bug, zc0 bug, or spec error, and appendix b confirmed item by item. then add the runner to `tests/test.sh`. - [ ] fix the implementation bugs listed in `docs/SPEC.md` appendix b, once confirmed: `memdebug scrub;` rejected; enum members can't reference earlier members; a plain function redefinition silently shadows the first; a prototype after the definition hides it; `?:` with one int and one float branch gives garbage; `bool` not normalised; float global initializers rejected; a global initializer naming another global uses its address; `include std;` inside a function body; syntax-error recovery hiding later errors; unterminated `/*` and string literals; `x as int * y`; prototypes needing parameter names; `len(char*)` with typecheck off; duplicate `case` labels; local `const` in constant expressions; constant expressions without precedence (`1+2*3` is 9). fix the prose items the appendix lists too. -- [ ] (in flight) a cross-reference report: for every global of the compiler, the functions that write it and the ones that only read it. build it from the language server's references (`textDocument/references` on `build/bin/zclsp`, so scoping is exact) and classify each reference as a write when an assignment operator, `++` or `--` follows it. output under `build/xref/`; a committed baseline and a `--check` mode that fails when a new writer of a global appears, so it can run in ci. +- [ ] (partly done, see "agent work at the end of the session") a cross-reference report: for every global of the compiler, the functions that write it and the ones that only read it. build it from the language server's references (`textDocument/references` on `build/bin/zclsp`, so scoping is exact) and classify each reference as a write when an assignment operator, `++` or `--` follows it. output under `build/xref/`; a committed baseline and a `--check` mode that fails when a new writer of a global appears, so it can run in ci. - [ ] optional, low priority: plain enums and `?:` in zc0 (both are small and produce ordinary code), so parity covers more programs. no structs in zc0 (decided). - [ ] group the compiler's parallel per-local arrays (`lname`, `ltype`, `lstate`, `lhome`, `lown`, `lslot`, `ltag`, `lpos`, `lnsz`, `lleaf`, ...) without structs: declare each family together in one place, with one comment block stating each array's meaning and invariant (for example when `lhome` is meaningful), route snapshot and restore of the family through one pair of functions, and use the cross-reference report to confirm nothing else writes them. then symbols and types the same way. fixpoint and analyzer behavior unchanged. - [ ] continue removing `unchecked` from the runtime. a reverted trial showed: `copy_w`/`fill_w` pass the analyzer when their gate stores go through `syscall(SYS_COPY, ...)`, `dgrow` with `own(w, it)` instead of `w[-2] = it`, `dpush` with `own` before the store, `dnew` as is; but the checked versions changed behavior (7 program checks failed, `tests/programs/share.zc` hung with a checked `cons`), so checked code inserts cleanup these functions can't afford. each needs its contract taught to the analyzer. `free`, `recycle`, `recycle_leaf`, `gc` are the allocator and collector the analyzer's own cleanup calls: document them as the irreducible escapes. `src/lib/actor.zc` has about 20 more. - [ ] revise diagnostics, comments and learning/reference prose using professional compiler documentation guidance, preserving lowercase and adding message regressions. touches messages everywhere, so do it when few other compiler changes are in flight. -- [ ] (in flight) test blocks: `test "name" { ... }` with `expect(cond)`, compiled only by `zy --test file.zc`, which runs each block and prints one line per test with the position of the first failed `expect`; skipped at parse time otherwise. build it as a library through `run.syntax` and `run.statement_hook`, like `src/lib/actor.zc`. -- [ ] (in flight) bounds checking on by default, `unbounded` to turn it off (function prefix, block, top-level `unbounded;`, mirroring `unchecked`), implied by `o3`. architecture: indexing is compiled in the postfix `[` handling of the expressions part; growable arrays already check (`dat`, `range_jz`, the `outside` enhancement, `proven()` removing checks a loop test already did). fixed arrays `T[n]` with a variable index get the same inline check against the constant `n`. pointers get a runtime call (for example `rt.bchk(p, i)`) that checks only when `p` is the start of a live heap block (header check word `p ^ MAGIC`, `paylen`), skipping constant index 0 and sizes the analyzer knows (`lnsz`). reports go through `oob`. the runtime, core, compiler parts, server, `run.zc` and `build.zc` start with `unbounded;`, which zc0 accepts and ignores (genome bytes unchanged). keep std, icon and actor checked unless a function touches raw memory on purpose. measure interpreter time before and after. +- [ ] (partly done, see "agent work at the end of the session") test blocks: `test "name" { ... }` with `expect(cond)`, compiled only by `zy --test file.zc`, which runs each block and prints one line per test with the position of the first failed `expect`; skipped at parse time otherwise. build it as a library through `run.syntax` and `run.statement_hook`, like `src/lib/actor.zc`. +- [ ] (partly done, see "agent work at the end of the session") bounds checking on by default, `unbounded` to turn it off (function prefix, block, top-level `unbounded;`, mirroring `unchecked`), implied by `o3`. architecture: indexing is compiled in the postfix `[` handling of the expressions part; growable arrays already check (`dat`, `range_jz`, the `outside` enhancement, `proven()` removing checks a loop test already did). fixed arrays `T[n]` with a variable index get the same inline check against the constant `n`. pointers get a runtime call (for example `rt.bchk(p, i)`) that checks only when `p` is the start of a live heap block (header check word `p ^ MAGIC`, `paylen`), skipping constant index 0 and sizes the analyzer knows (`lnsz`). reports go through `oob`. the runtime, core, compiler parts, server, `run.zc` and `build.zc` start with `unbounded;`, which zc0 accepts and ignores (genome bytes unchanged). keep std, icon and actor checked unless a function touches raw memory on purpose. measure interpreter time before and after. - [ ] after the fuzzing work, add more general functionality to the standard library. -- [ ] (in flight) struct destructors: a struct may declare `fn drop()`, called wherever the compiler frees an owned value of that type, before the memory is released. architecture: the compiler frees owned locals in `freevar` (analyzer part), called from `exit_vars`, reassignment and explicit `rt.free`/`rt.recycle` calls (`s_free`, `s_recycle` handling in the call code); when the local's static type is a pointer to a struct with a `Name.drop` method, emit a call to it first. objects freed by the collector or inside unchecked code do not run `drop`: document that. first users: file descriptors in `std.Host`, processes in `actor.zc`. -- [ ] (in flight) runtime source positions. architecture: at each statement start (`stmt()`) record (code address `here`, line); per function a permanent data block [entry, end, name, file, count, pairs...] linked into a runtime list (`rt.line_blocks`), or appended to run-only images by `build_image` like the symbol blob. `lines on;`/`lines off;` like `docs on;`; on for user programs, off for the genome unless the growth is small. a new syscall op returns the return-stack pointer (the return stack is in vm memory, 0x1A4100..0x1B40FF, growing up); implement it in `src/core/core.c`, `src/micro/micro.c`, `src/js/tinyvm.ts` and the wasm port. `rt.trace()` walks the return stack, printing ` at file:line in function` for words inside a known function range. the runtime's fatal reports (`oob`, bad heap pointer, double free, out of memory, frame overflow) print the trace after their unchanged first line. check mode skips all of it. +- [ ] (partly done, see "agent work at the end of the session") struct destructors: a struct may declare `fn drop()`, called wherever the compiler frees an owned value of that type, before the memory is released. architecture: the compiler frees owned locals in `freevar` (analyzer part), called from `exit_vars`, reassignment and explicit `rt.free`/`rt.recycle` calls (`s_free`, `s_recycle` handling in the call code); when the local's static type is a pointer to a struct with a `Name.drop` method, emit a call to it first. objects freed by the collector or inside unchecked code do not run `drop`: document that. first users: file descriptors in `std.Host`, processes in `actor.zc`. +- [ ] (partly done, see "agent work at the end of the session") runtime source positions. architecture: at each statement start (`stmt()`) record (code address `here`, line); per function a permanent data block [entry, end, name, file, count, pairs...] linked into a runtime list (`rt.line_blocks`), or appended to run-only images by `build_image` like the symbol blob. `lines on;`/`lines off;` like `docs on;`; on for user programs, off for the genome unless the growth is small. a new syscall op returns the return-stack pointer (the return stack is in vm memory, 0x1A4100..0x1B40FF, growing up); implement it in `src/core/core.c`, `src/micro/micro.c`, `src/js/tinyvm.ts` and the wasm port. `rt.trace()` walks the return stack, printing ` at file:line in function` for words inside a known function range. the runtime's fatal reports (`oob`, bad heap pointer, double free, out of memory, frame overflow) print the trace after their unchanged first line. check mode skips all of it. - [ ] borrow returns tied to an argument. inference already works for a callee defined before the call: `char* skip(char* s) { ...; return s + i; }` makes `let r = skip(t); rt.free(t); r[0]` an error. the gap: when the call comes before the callee's definition, nothing is reported and the program reads freed memory. extend the deferred effect requirements (`effect_require`, `effect_finish` at unit end) so a forward-called function whose return turns out to alias a parameter produces the same error. for bodyless declarations, externs and function types, add an unobtrusive spelling, for example a `borrows s` clause after the parameter list (`char* skip(char* s) borrows s;`), checked against the body when one exists. - [ ] variadic arguments and `std.print`. design: a pack is passed as one hidden argument, a caller-built read-only growable-array handle (`fixed T[?]` view: [len, cap, items, element size] in the caller's frame, so `len(a)` and `a[i]` work unchanged and nothing is allocated). `...int a` is a typed pack; several packs per function when their element types differ (`void f(...int a, ...char b)`): each extra argument goes to the pack whose element type matches its static type, an ambiguous match is an error, packs come after the fixed parameters. `...any args` packs each value with a kind code (int, float, char, string, bstr, pointer, ...) and its type record: `any` is a small built-in record { kind, value, type }. `...*any args` makes the call comma-free: after the fixed arguments, the parser reads unary expressions until `)`, commas optional, so `std.print("x = " name ", age " age "\n");` works and binary expressions must be parenthesized. `std.print(...*any args)` prints each argument by its kind (`put_n`, the float printer, `put_s`, `put_bs`, a character for `char`, hex for pointers). - [ ] x86-64 jit phase 2: compile o3 regions to x86-64 (port `rcompile` and the region register allocation; regions now run on the region interpreter and are 2 to 4 times slower than their plain versions on this jit) and inline enhancements (lower the optimized micro-ops to x86 registers as the arm64 path does, instead of calling `xrun`). validate with `tests/drivers/jit_x64_test.sh` and `tests/bench/bench.sh`, which now has an x86-64 jit column. add the driver to `tests/test.sh` on x86-64 linux. - [ ] `memdebug`'s `obj_base` searches only 64 words back for a block header, so under `memdebug on` an interior access more than 64 words into a block (`buf[255]` on a 256-word block) reports "bad heap pointer". find the header from the buddy allocator's size classes instead of a bounded scan, with a regression program. -- [ ] (in flight) `defer` follow-ups (small): accept `defer if (...) ...;` and other single statements besides an expression; decide whether deferred code may name an owner declared after the `defer` (now rejected by the lexical rule; one line in `defer_one`). +- [ ] (partly done, see "agent work at the end of the session") `defer` follow-ups (small): accept `defer if (...) ...;` and other single statements besides an expression; decide whether deferred code may name an owner declared after the `defer` (now rejected by the lexical rule; one line in `defer_one`). - [ ] build the llvm core by default when `llvm-config` is available (the makefile falls back to the interpreter otherwise), so x86 hosts get a native tier and the language server stops being interpreted; keep `ZYNOLLVM=1` and `-DZYINTERP` as the opt-outs. - [ ] stack-effect checking: every function and `asm` block declares or is inferred to have a fixed data-stack effect, and the compiler reports a block whose paths leave the stack unbalanced. the first step of a proving scaffold. - [ ] declared side effects at module boundaries (globals touched, syscalls or not), checked against the effect masks the compiler already computes. - [ ] `requires`/`ensures` contracts on functions, compiled to run-time checks under a switch, with the cases the value facts settle (constants, simple ranges) checked statically. -- [ ] (in flight) record and replay: a core option that logs every syscall gate result to a file and replays a run from the log exactly, for reproducing failures and adding tracing after the fact. +- [ ] (partly done, see "agent work at the end of the session") record and replay: a core option that logs every syscall gate result to a file and replays a run from the log exactly, for reproducing failures and adding tracing after the fact. - [ ] a debug adapter for vscode (breakpoints, stepping, locals) on the interpreter, built on the line tables. - [ ] reduce the cost of word-per-character strings: measure, then decide between making `bstr` the default string, unifying the two apis behind one name, or both. - [ ] continue fuzzing the compiler, analyzer, runtime, standard library, lsp, build tools and generated artifacts, including analyzer properties, memory-limit edges, compiler image mutation and long-running lsp memory use; feed generated programs to `make parity` for differential fuzzing between zc0 and the self-hosted compiler; fix discovered bugs. -- cgit v1.2.3