Lom (Language of Machine) — an AI-native programming language designed around the LLM repair loop: when a model writes broken Lom, the language itself is built to diagnose it precisely and fix it mechanically.
LLM-coding-native first, workloads later. Built in Rust.
Most languages treat "an AI wrote broken code" as someone else's problem. Lom inverts that: repair is the language's reason for existing. Every rejection carries a structured diagnostic code and, where a mechanical fix is safe, lom fix applies it for you — the same interface an LLM agent uses. The whole toolchain (diagnostics → lom fix plans → applied patches) is machine-readable by design.
| Claim | Receipt |
|---|---|
| The repair loop works on real models | On the then-full 128-task set (10 categories; the suite has since grown to 129 via task 130), deepseek-v4-pro+thinking and glm-5.3 score 98.3% / 98.5% pass@1 (2 models × 10 samples @ t=1.0; pass@5 99.2% / 98.8%) — the only sub-100 tasks are a known prompt-ambiguity anchor and one deliberately hard cross-compiler repair — report |
| The compiler compiles itself — provably | self_comp.lom (12,671 lines of Lom) compiles its own source to WASM; the result is byte-identical from both sides (a 272,925-byte strong quine, re-verified on every CI run) |
| A trustable core | Zero third-party crates, zero unsafe, 600 unit + 40 integration tests, three-OS CI; the language surface (syntax / 20 keywords / diagnostic codes / 43 builtins) is frozen — changes require an RFC |
hello.lom — you (or your model) forgot the import:
fn main() -> Unit
println(len("hello"))
end
Run it, and the diagnostic tells you exactly what to do — then let the language do it:
$ lom hello.lom
[runtime] error (0:0): [RUNTIME002] 符号 'len' 未导入。需在文件顶部声明:from string import {len}
$ lom fix hello.lom --apply
lom apply: hello.lom(迭代 2 轮)
round 1: applied 1, skipped 0
[1:1] insert (NAM005) — 在文件顶部插入 'from string import {len}'
最终诊断(修复后源码): 0 错误 / 0 警告
$ lom hello.lom
5
That insert is a high-confidence mechanical action; ambiguous cases surface as hints instead of silent rewrites. The same lom fix plan format (lom-fix/v1) is what LLM agents consume — see SPEC_FOR_AI.md.
cargo build --release # builds target/release/lom (zero dependencies)
./target/release/lom examples/fib.lom # run a programA minimal Lom program (fn + tail-expression return; blocks close with end):
fn main() -> Unit
println("hello, lom")
end
Save as hello.lom, run lom hello.lom. When a run fails at run time, lom hello.lom --trace prints a call-level JSONL event stream to stderr (--trace-out <file> to redirect, --trace-max-events <n> to cap, --trace-builtins to include builtin calls) — see SPEC_FOR_AI.md §11g; AOT lom build artifacts never pass through the interpreter, so --trace does not apply to them. New here? Read SPEC_FOR_AI.md (the LLM-facing language spec). AI maintainers start with the copy-ready handoff prompt, the maintenance protocol, then docs/HANDOVER.md — internal governance and working history, maintained in Chinese (see docs/README.md for the map of English vs Chinese surfaces). 中文读者:README.zh-CN.md。
- Current: v1.14.0 (2026-10-21). Language surface frozen since v1.0 (2026-09-02) — zero surface changes since; every release since has been tooling/evidence work (the agent execution trace
--trace, online playground incl. the in-browser self-hosting demo and share links, structured diagnostics, remediation). Full numbers and the per-release log: see Release log and Milestone history below. - The L2 self-hosted subset compiler (
examples/selfhost/self_comp.lom) is a supported face as of v1.8.0 (2026-10-08; promoted from experimental by user adjudication after four consecutive A- review rounds, a zero-open ledger, and the v1.7.2 line-number batch completing its repair loop): it compiles a strict subset of Lom to WASM, rejects some programs the host accepts (each rejection carries an[L2xxx]code and a repair hint vialom l2fix/ tools/l2fix.py; rejections whose message carries a quoted name also carry aln:clsource position — the registered 71/102 coverage boundary with 31 exempt type-string cases), and self-hosts provably. - The 38 review rounds are in-system adversarial reviews (agent-run, evidence-based) — good hygiene, not external peer review; grades never extrapolate across baselines.
- Online playground — run Lom, see structured diagnostics, and apply
lom fix --applywith a line diff, entirely in your browser (the full host toolchain compiled towasm32-wasip1, zero third-party JS). Pressing Run quietly folds your program into the URL hash (UTF-8-safe), so opening the shared link restores your exact editor content.
Status: v1.0 — language surface FROZEN (2026-09-02). Frozen: syntax / 20 reserved words / diagnostic codes / 43 builtins (spec §14). Changes require a new RFC. Current release: v1.16.0 (2026-10-28; the iterative-repair task category, per designs/0027, user-adjudicated with all points as recommended; versioned minor per the decision table evaluation-surface-expansion row — the v1.5.0/v1.11.0 precedent; zero src/, frozen surface untouched) — RepairLoopBench gains an eleventh category, iterative_repair: 9 tasks (ids 131-139) measuring how many rounds a model needs to fix a program, not just whether it fixes it in one shot. The protocol is a single-step feedback chain: round 1 is the standard prompt (so C(1) = pass@1, calibrating against the published numbers); each later round feeds back the candidate real diagnostics (static check --json and/or runtime output, captured verbatim); scoring stops at first pass or censors at R=5. The headline measurable hypothesis ships as task 131 (a verbatim copy of the known-hard 127: single-round 1/10 on both models, 18/20 candidates missing the same Option-unwrap step — does round-2 diagnostics close it?); three new tasks (137-139) are round-2-solvable forms — statically clean, runtime-broken; the three no-op trap tasks are excluded. New tooling: eval/iter_eval.py (trajectory collection; --dry-run self-test green incl. censoring) + eval/iter_summarize.py (round distribution / censoring rate / C(N) curve). Task count 129 to 138; the doc_audit category regex now counts categories mechanically. No convergence numbers are claimed yet — the glm-5.3 zero-marginal-cost first sampling run is a separate batch. Baseline unchanged: quine 272925, verify_selfcomp 368/368, Rust 600+40.
Previous repository milestone: v1.15.0 (2026-10-28; ACRI native conformance +
lom explain, per designs/0027, user-adjudicated with all points as recommended; versioned minor per the decision table's user-visible-new-capability row; frozen language surface untouched) — thecheck --jsonenvelope now carries a nestedpositionobject (line/col/anchor) alongside the retained top-level pair (append-only dual write per ACRI's forward-compatibility axiom;anchoris"exact"for real coordinates and"none"at 0:0 — never fabricated; the upgrade is constructively proven by a one-line diff against the prior envelope), making Lom ACRI L0 native-conformant (dual-write transition). New subcommandlom explain <code>(the ACRI §2.1 OPTIONAL clause): per-code long-form documentation — semantics / common causes / repair direction — for all 46 registered codes, from the single in-tree source with a compile-time count lock; human-readable by default,--jsonemits anlom-explain/v1envelope; unknown codes get a structured error with same-family neighbors (exit ≥2). Unit tests 593→600 (+2 envelope, +5 explain), integration 40 unchanged; baseline unchanged by construction: quine 272925 byte-identical, verify_selfcomp 368/368, eval 129/129 both backends (the 138-task iterative-repair category lands in v1.16.0).Previous repository milestone: v1.14.0 (2026-10-21; agent execution trace per designs/0026, user-adjudicated with all four decision points resolved as recommended; versioned minor per the decision table's user-visible-new-capability row; pure CLI flag family + interpreter instrumentation — zero language-surface change; repository tag at
b07c6d1, CI run39646107026seven jobs green — first run green; not externally published) — the run mode gains--trace: a call-level JSONL event stream (lom-trace/v1) for agents. Each user-function/closure entry emits acallevent (name,ln:clsite, framedepth, arguments) and each exit areturnorcall-errorevent — the latter carrying the verbatim[RUNTIMExxx]code, so a failed run now shows which call layer, with what arguments, not just the final error. The flag family:--trace(default off; the off state costs exactly twoOptionchecks per user call),--trace-out <file>(default sink stderr, stdout stays the program's product; the file sink is NDJSON append-write),--trace-max-events <n>(default 10000,0= unlimited, cap-hit streams end withtruncated:true),--trace-builtins(default off — builtin calls enter the trace only on demand). Value serialization isto_displaysemantics plus trace-only truncation (120 chars / nesting depth 3, overflow becomes shape annotations like[List len=20000 …]); theto_displaycore is untouched andprintlnoutput is byte-identical. Applicability boundary: the interpreter run mode only — AOTlom buildartifacts never pass through the interpreter and produce no trace. Baseline unchanged by construction: quine 272925 byte-identical, verify_selfcomp 368/368, Rust 581→593 (+12 trace unit tests; 40 integration unchanged), eval 129/129 both backends; trace-off A/B bench (call_density n=10^6 / lookup n=800) zero regression. Previous repository milestone: v1.13.0 (2026-10-20; playground share links, user-adjudicated; versioned minor per the decision table's user-visible-new-capability row; zerosrc/, zero.lom, zero language-surface change — pureplayground/+ docs + version pairing; repository tag ate171488, CI run39431280200seven jobs green; not externally published) — the online playground gains share links: pressing Run (also Fix or Ctrl/Cmd+Enter) silently folds the editor content into the URL hash as UTF-8-safe base64url, and opening such a link restores the editor automatically (an invalid hash falls back to the default example, never a broken page). The codec lives in the new pure-function moduleplayground/sharelink.js— base64url hand-rolled overTextEncoderbytes becausebtoathrows on non-Latin-1 text (zero third-party JS; the module is imported by both the browserapp.jsand nodesmoke.mjs, one source of truth).history.replaceStatekeeps the back-button clean; zero new UI elements. Programs longer than a 6,000-code-unit threshold skip the update with a console note (URL-length safety; probe-measured expansion at the threshold: ASCII 8k / CJK worst-case 24k URL chars —target/probes/sharelink/).smoke.mjsgains an 8th share section locking the codec's round-trip (CJK+emoji / CRLF / empty / over-long gate both sides, 8 assertions; 31→39 checks) in CI's playground job with zero workflow changes. Baseline unchanged by construction: quine 272925, verify_selfcomp 368/368, Rust 581+40, eval 129/129 both backends. gh-pages deployment is a separate user-adjudicated step (not in this batch). Previous repository milestone: v1.12.1 (2026-10-16; wasm file-builtins fix per designs/0025, user-adjudicated on all four points; zerosrc/, zero.lom, zero language-surface change; repository tag atbdfd187, CI run38893522801seven jobs green; not externally published) — the playground engine's two real file-builtin defects are fixed at the WASI-harness layer (playground/harness.jsonly; desktop behavior byte-identical):file_exists("playground.lom")now returnstrue— it had silently returnedfalse(the harness lackedpath_filestat_get, so the ENOSYS error folded throughPath::exists()into a wrong value with zero diagnostics; the builtin's dispatch arm has no Err exit — the designs/0019 misjudgment, now annotated in place) — andfile_appendon the mapped file now appends at tail — it had overwritten the file head (thepath_openfdflags APPEND bit was dropped and every write landed at offset 0). The fix adds apath_filestat_getstub succeeding only for the mapped name (everything else ENOENT — keeping the/lom.tomldiscovery gate dark, probe-locked) plus strict WASI p1 append semantics (per-fd flag, seek-to-end before each write). Unmapped read/write/append forms keep their structured[RUNTIME000]errors — the registered virtual-single-file boundary, now worded precisely across all four builtins.smoke.mjsgains a 14-assertion file section locked by CI's playground job (17→31 checks). Baseline unchanged by construction: quine 272925, verify_selfcomp 368/368, Rust 581+40, eval 129/129 both backends. gh-pages deployment is a separate user-adjudicated step. Previous repository milestone: v1.12.0 (2026-10-15; playground self-hosting demo — phase 2 B-only per designs/0024, user-adjudicated; zerosrc/, zero.lom, zero language-surface change; repository tag at4a64471, CI run38688935472seven jobs green; not externally published) — the online playground gains a "Self-hosting demo" section: one button runs, entirely inside the visitor's browser, the four-step chain that grounds the self-hosting claim — the L2-subset compiler (shipped asselfcomp.wasm, itself produced by compilingself_comp.lomwith the host) compilesself_comp.lom; the emitted hex is decoded by a browser-side hex2wasm; the resulting wasm compilesself_comp.lomagain; and that output is compared character-by-character with a preset baseline, landing on QUINE achieved: 272925 bytes, byte-identical (browser-tested locally in ~26.5 s; a dedicated 60 s watchdog — separate from the main engine's 5 s one — bounds the chain). A scope note states plainly that the self-hosting engine is the L2 strict subset while full-language programs belong in the main engine above. New files:playground/selfboot.js(env-contract harness mirroringrun_selfcomp.mjsitem-for-item),playground/selfboot-worker.js,tools/build_selfboot.py(builds the three distribution artifacts with aCOMPILED 272925hard gate);smoke.mjsgrows a six-assertion self-hosting segment; CI's playground job builds the artifacts before smoking. Phase-1 files are append-only (zero deletions). Baseline unchanged by construction: quine 272925, verify_selfcomp 368/368, Rust 581+40, eval 129/129 both backends. gh-pages deployment is a separate user-adjudicated step. Previous repository milestone: v1.11.0 (2026-10-13; RepairLoopBench runtime-diagnostic task + v1.10.3 claims precision-correction, user-adjudicated; versioned minor per the decision table's "evaluation-surface expansion" row — the v1.5.0 task-expansion precedent; zero language-surface change; repository tag ate83177b, CI run38576018355seven jobs green; not externally published) — task 130 adds a third task-source face to the benchmark: a statically-clean deep-recursion program whose failure exists only at run time (the v1.10.3 structured[RUNTIME000]guard). Its prompt embeds two separately collected real outputs — the genuinely-empty--check --jsonblock and the verbatim runtime stderr (ordering after the诊断 JSONblock is a hard constraint, probe-verified againsteval_prompt_check's extraction anchors); the reference solution adds the base case, verified on both backends. Suite 128→129 (error_repair 31→32);eval_prompt_check32/32, both backends 129/129. A supply-pass finding is corrected in place across five claim sites: the fix pipeline is static-diagnostic-driven and runtime errors never enter it, so v1.10.3's message-aware RUNTIME000 hint has no CLI arrival path for runtime-raised diagnostics (dispatcher entry real and unit-locked; boundary registered in eval/README's new runtime-diagnostic task discipline). quine 272925 byte-identical; Rust 581+40 unchanged. Previous repository milestone: v1.10.3 (2026-10-13; runtime repair-loop deepening — plan 乙, user-adjudicated; zero language-surface change; repository tag at838b5f0, CI run38507453120seven jobs green; not externally published) — the deep-recursion guard is re-calibrated 80,000→60,000: frame size varies with function shape, and the measured deep-frame (countdown-style) physical overflow sits at ≈67,000-68,000 layers, so the old guard was unreachable for that shape (raw process abort, exit 127, zero diagnostics — a live claim-vs-reality gap in the v1.1.2 "structured instead of aborts" wording); at 60,000 the structured[RUNTIME000]diagnostic (code, position, repair advice, exit 1) precedes the physical overflow for measured shapes (probe n=68,000: exit-127-silent before → structured exit 1 after; 50,000-layer legal recursion still passes). The RUNTIME000 fix suggestion becomes message-aware (depth-limit messages get a Medium add-a-base-case / rewrite-as-whilehint instead of the generic Low one — precision-corrected in v1.11.0: the fix pipeline is static-diagnostic-driven, so this hint has no CLI arrival path for runtime-raised diagnostics; boundary registered in eval/README), and the wasm/300 playground guard message no longer hard-codes "256MB stack" (stack-context note parameterized per target). Rust 580→581; quine 272925 byte-identical, verify_selfcomp 368/368. Previous repository milestone: v1.10.2 (2026-10-13; fix-engine PKG-family coverage closure, user-adjudicated; zero language-surface change — advisory hints for existing codes only, no new codes, no auto-apply; repository tag at664be97, CI run38463443495seven jobs green; not externally published) — the fix dispatcher table now covers all 38 host codes: the sevenPKG001-PKG007entries (previously falling into the generic "unknown code" bucket — the same gap NAM006/MUT002 closed in v1.5.0) are Medium hint-grade suggestions: package errors root in manifests/dependency graphs/cross-file structure, so none is auto-applied (the safety axiom is untouched;PKG007is an informative collision notice in the NAM006 family). Rust 578→580 (two new tests: per-code hint form + the never-applied negative across all seven); quine 272925 byte-identical, verify_selfcomp 368/368, eval 128/128 both backends. Previous repository milestone: v1.10.1 (2026-10-13; English-surface closure for the external evidence chain, user-adjudicated; comments and docs only — zero behavior change, zero language-surface change; repository tag ataeb38da, CI run38379975886seven jobs green; not externally published) — each of the six eval reports under eval/ now opens with an English TL;DR block (Chinese bodies unchanged; appended-box convention);src/fix.rsgains an English module note stating the auto-apply safety axiom (Chinese comments retained); docs/README.md is added as an English map of the repository's English vs Chinese document surfaces; the human tutorial (docs/lom-project-guide.html) gains an English pointer to the English references; the stale "Current: v1.8.0" line in Status & boundaries is refreshed to this version. Baseline unchanged by construction: quine 272925 bytes, verify_selfcomp 368/368, Rust 578 unit + 40 integration, eval 128/128 both backends. Previous repository milestone: v1.10.0 (2026-10-12; the β-2 engineering batch of designs/0023 — closing ACRI's two L1 conformance gaps, user-adjudicated "execute in order"; repository tag atb3bb9f5, CI run38168342829seven jobs green; not externally published)) — three deliverables (Rust 578 unit = 573+5, integration 40 = 37+3 in a newtests/beta2_exit_codes.rs; quine 272925 byte-identical, verify_selfcomp 368/368, eval 128/128 both backends, six selfhost modes — all green): ①lom codes [--json]— the machine-readable diagnostic-code registry ACRI §2.1 requires: 46 entries (38 host codes across the 9 namespaces + the 8 L2 families) with code/severity/category/summary; severity cross-checked against the runtime classifiers by unit test. ②l2-fix/v1→l2-fix/v2(append-only): the L2 codegenln:clprefix is lifted into a structuredpositionfield in both implementations (rustlom l2fixand the python CI oracle) — python↔rust parity 30/30 zero field differences,--self-check6/6. ③ exit-code bands (ACRI §2.2): tool-self failures (unreadable input, usage errors, internal errors) move from 1 to ≥2 across 20 paths, separating them from diagnostic errors (=1);pkg-expand's mixed diagnostic path stays 1 (carries PKG codes) and the LSP protocol band is untouched (r58-locked) — both registered inspecs/acri/lom-conformance.md. With this batch Lom is L1-conformant per its own conformance map (L0 conformant, L1 conformant with the two registered deviations, L2 documented). Zero language-surface change (new CLI surface + schema addition + exit-code band; frozen syntax/keywords/codes/builtins untouched). Previous repository milestone: v1.9.5 (2026-10-10; performance-engineering knife 2 of designs/0022 — the value-representation deep-water surgery, user-adjudicated "full Rc-ification of all five families"; repository tag at973d020, CI run38049184858seven jobs green; not externally published) — A2: five immutable Value families go Rc-shared (6 files, +126/−102):Str(String)→Str(Rc<str>),Closure{params, body}→Rc-shared (closure creation stops deep-cloning the whole body AST),Enum{variant, args}→Rc<str>+Rc<Vec<Value>>(Ok/Some construction stops allocating a variant-name String),Record/Tuplefield/element lists →Rc<Vec<…>>. Variable reads, argument passing, and closure creation become O(1) pointer clones. Same-machine full-30-point A/B vs the knife-1 binary: call_density 0.69-0.83×, map_lookup 0.72-0.88×, closure_loop 0.84-0.94×, lookup/recurse all <0.94×;--bootstrapwall time 108.8→43.1 s (planner-run; cumulative 471.5→43.1 s ≈ 10.9× across both knives). Registered trade-off, stated plainly (figures corrected per R139's interleaved A/B re-measurement): string-construction-heavy loads pay one extra copy into the Rc — the mechanism is real (read-verified; construction-site annotation inValue::Str's doc comment), but the batch-time short-tier figures (1.31×/1.16× at n=10k/25k) proved time-of-day noise (same-build interleaved re-runs: 1.002/0.990, neutral); the 50k tier's 1.03× reproduces (the quadratic copy dominates the extra hop there). Behavior locked byte-identical: quine 272925 bytes md5-identical, 10-program × 3-run probe set all MATCH, verify_selfcomp 368/368, eval 128/128 both backends, six selfhost modes, fuzz/diff/probes green, structural equality semantics preserved (Rc PartialEq compares contents). Zero rollbacks, zero bypasses — five families landed per the design table; no new tests (573+37 via mechanical adaptation of ~57 sites). Zero language-surface change. Previous repository milestone: v1.9.4 (2026-10-10; repository tag at4c555ad, CI run38045249710seven jobs green; not externally published) — performance-engineering knife 1 (designs/0022, user-adjudicated on all three points — split-knife delivery / value-representation Rc-ification reserved for knife 2 / bench assets in-tree with manual-batch acceptance): interpreter hot-path A1: the function table goes Rc-shared (HashMap<String, FnDecl>→HashMap<String, Rc<FnDecl>>, +8/−7 insrc/interpreter.rs): every user-function call previously deep-cloned the entire FnDecl (name + params + the whole body AST —eval_call's two.get(name).cloned()sites); a call now clones a pointer. Same-machine measured effects (medians; raw values on record in designs/0022 and HANDOVER §10): call_density 1.96× (1263.9→644.7 ms @ n=1M — probe-carrier figure, the supply-probe three-function file that exercises exactly this path; the same load on the in-tree bench.lom carrier measured 1.27×, the two carriers converging at v1.9.5 ≈627 ms — R140 carrier disclosure), the L2 self-compile 515.3→123.5 s (4.17×),--bootstrapwall time 471.5→108.8 s; the arith/closure probes at 1.14×/1.16× sit inside the ±8-13% run-to-run band and are not claimed as gains. Behavior locked byte-identical: quine 272925 bytes byte-identical (md5 match), 5-program × 3-run probe set 15/15 identical, verify_selfcomp 368/368, eval 128/128 on both backends, six selfhost modes green, clippy/fmt zero. Scope note: A3 (loop-scope micro-opt) was read-verified to have no zero-risk implementable gain (each iteration's fresh scope owns its name String by construction; hoisting the Scope would change closure-capture observability — prohibited) and was honestly skipped, zero code change. Also in this batch: bench assets in-tree —examples/bench.lomgrows six loads (arith_loop / call_density / closure_loop / list_get_nth / map_hit_far / str_concat; the four legacy loads untouched) andtools/bench.pyjoins the repo (10-load matrix, quick/full tiers, Python-side expected-value self-check,--baselinecompare; not in CI, per adjudication). Zero language-surface change (pure implementation optimization). Previous repository milestone: v1.9.3 (2026-10-10; repository tag at22a7008, CI run37951486401seven jobs green; not externally published) — thirty-second-review remediation (R132-R135, user-adjudicated "continue"), all four P3 documentation-precision items from review-2026-10-09-4 closed in one batch: R132 README.zh-CN evidence items refreshed to current values (561+31 → 573+37; the model-verification bullet now carries the current headline criterion — full 128-task set pass@1 98.3%/98.5% — replacing the 31-task-era wording); R133 the TODO registered-boundaries ledger regains the "L2 package-expansion units skip same-file duplicate-fn detection" item (declared registered at v1.6.1, never actually entered — now recorded); R134 the HANDOVER §1 Rust-test row's arithmetic contradiction fixed (35 → 37, attribution split v1.9.1 31→35 + v1.9.2 locking +2); R135 designs/0021's §1 subtraction formula and thecheck_and_report_filedoc comment updated to the R128 unified-subtraction form (the old text described the pkg-side-only subtraction that R128 superseded — the src comment touch is why this batch is versioned patch). Zero behavior change (comment-only src touch); counts 573+37, quine 272925, 368/368 unchanged; doc_audit 72→73 with the post-tag R136 mirror-check interlock (unversioned tool batch, R136/R137 remediation). Previous repository milestone: v1.9.2 (2026-10-09; repository tag atec2ebf0, CI run37895385249seven jobs green; not externally published) — post-tag remediation bookkeeping (user-adjudicated "execute"; versions the two behavior-fix commits that landed after the v1.9.1 tag, per the maintenance policy's "behavior fix → patch" rule): R125 (thirtieth-review,b614037): root-path subtraction — no-manifestlom buildnow checks each root-directory.lomfile against root_externals minus the file's own top-level symbols, mirroring the package path (a root file's same-file duplicate fn colliding with a package-exported name stays NAM002 error instead of downgrading to NAM006); R128 (thirty-first-review,d513894): unified subtraction —check_and_report_filesubtracts the file's own symbols from the final externals vector (base ∪ pkg) as a whole, closing the package-path edge where a same-file duplicate fn colliding with a dependency-closure symbol (base_externals side) downgraded to NAM006; +2 integration tests locking both (R125/R128 negative forms; 35→37 integration). Bookkept in the same version: R127/R129/R130 documentation counters (TODO baseline row, README evidence-table & zh-CN stale values, handoff boundary-count sentence) and R131 — doc_audit's new J+ integration-reality interlock (71→72 items: tests/#[test]actuals vs documented counts). Rust 578 + 40 integration; verify_selfcomp 368/368, eval 128/128, quine 272925 unchanged (self_comp untouched). Zero language-surface change (existing diagnostic codes on existing paths). Previous repository milestone: v1.9.1 (2026-10-09; repository tag at7c2403c, CI run37767137349seven jobs green; not externally published) — build-gaps remediation (designs/0021; user-adjudicated "execute per your judgment"; three registered build-flow boundaries closed): ① multi-file package intra-package cross-reference false positive —lom build(manifest-driven) now checks each package file againstexternals_map[P] ∪ (P.public_symbols − the file's own top-level symbols)— the subtraction prevents flipping same-file duplicate-fn NAM002 errors to NAM006 warnings (single-file packages: subtraction = empty set, zero behavior change, build_closure_externals 3/3 unchanged); ② PKG007 on manifest-driven builds —warn_public_symbol_clashesis now called on the human-readable path (stderr, JSON path untouched); ③ main-file diagnostics — the root directory's.lomfiles (conventionallymain.lom) are now checked with full-graph externals, symmetric with package-file behavior (root's dependency closure = all packages). A misplaced CLI doc block (cli.rs "rc 1 for package errors" on the wrong function, contradicted by the rc=0 implementation) corrected in the same sweep. 4 new integration tests (build_gaps_remediation.rs: cross-ref passes / duplicate still NAM002-no-NAM006 / PKG007 on stderr / main-file NAM003). Rust 573 + 31→35 integration; verify_selfcomp 368/368, eval 128/128, quine 272925 unchanged (self_comp untouched). The frozen language surface remains unchanged (NAM003 false-positive removal + existing codes on new paths, no new codes). Previous repository milestone: v1.9.0 (2026-10-08; repository tag at9751011, CI run37680423135seven jobs green; not externally published) — l2fix CLI subcommand (designs/0018 adjudication-1-甲 follow-through, enabled by the v1.8.0 promotion): the L2 rejection-text code-line parser and seven-family repair-suggestion mapper move into the host binary aslom l2fix <file|-> [--json] [--text ...](stdin-piped real usage:lom self_comp.lom -- bad.lom out.hex 2>&1 | lom l2fix -). Rust-native (src/l2fix.rs, 828 lines, zero regex), the 43-name/8-module lookup wired directly tointerpreter::module_of(single source of truth, same-crate); python↔rust field-for-field parity verified: 18 synthetic pairs + 12 real negatives through the full chain = zero differences; JSON envelope byte-identical. 12 new Rust tests (parse/suggest/all-families/JSON roundtrip/e2e) → 561→573 unit, integration 31 unchanged; quine 272925 bytes (self_comp untouched); verify_selfcomp 368/368. l2fix.py stays in CI as the python parity oracle (doc-gates self-check, R120). Frozen language surface untouched. Previous repository milestone: v1.8.0 (2026-10-08; repository tag at88d91dc, CI run37675620476seven jobs green; not externally published) — L2 promotion (user-adjudicated "execute" on the graduation menu after the 28th review's A- left all five designs/0020 §4 criteria met): the L2 self-hosted subset compiler is promoted from experimental to a supported face — the README boundary section, the playground footer, and eval/README now say so; the strict-subset semantics,[L2xxx]rejection codes, source positions (v1.7.2), and l2fix repair hints are unchanged (this is a claims-face batch: zerosrc/, zero.lom, zero eval-task changes — the 127-129 prompts keep their verbatim capture-time wording, annotated in eval/README). Graduation basis on record: four consecutive A- review rounds (25th-28th), zero-open ledger at promotion, the repair loop complete (codes v1.6.0 / l2fix / line numbers v1.7.2), L2-face eval tasks stable under both models. Review-round count on the README boundary line refreshed 26→28. The frozen language surface remains unchanged. Previous repository milestone: v1.7.2 (2026-10-08; repository tag atca31691, CI run37652195871seven jobs green; not externally published) — L2 diagnostic line numbers (designs/0020, user-adjudicated on all four points; zerosrc/changes, frozen language surface untouched): the L2 subset compiler'scodegen errorchannel now carries aln:clposition prefix before the code (unified with the existing lex/parse channels) whenever the message contains a quoted name resolvable through a new token-level name-anchor table —build_name_pos, a linear prescan over the token stream recording each identifier's first occurrence, built only on the error path so successful compilations stay byte-identical (all 204 positive-case hex outputs old-vs-new identical; the strong quine re-derived at 272925 bytes, both sides). Position semantics = the name's first occurrence, not a precise span (registered boundary); messages without a quoted name, or whose first quoted name never appears as an identifier token (internal type strings like 'i64', dotted paths like 'json.json_dump'), keep the bare format — 31 such negatives on the corpus, an explicit machine-checked exemption list. Coverage measured on the corpus: 71 of 102 quoted-name negative lines now carry a position. Chain updates: l2fix's codegen regex gains the optional position group (message untouched, seven-family mapping unchanged); verify_selfcomp adds a "quoted-name ⇒ non-empty position" summary assertion over the 164 negatives (the 148 existing dual locks untouched, zero breakage by construction — substring matching); eval task 129's embedded L2 text re-captured verbatim (codegen error: 5:13 [L2U001] …— line:col matching the host NAM005 diagnostic), 127/128 byte-identical (no quoted names). L2 graduation criteria inventoried in designs/0020 §4 (graduation itself remains a separate adjudication). Baseline: verify_selfcomp 368/368 (with the new assertion), bootstrap 14/14 quine 272925, self_comp 12591→12671 lines, Rust 561+31 and eval 128/128 per backend unchanged, doc gates 71/71. The frozen language surface remains unchanged. Previous repository milestone: v1.7.1 (2026-10-07; repository tag at7739959, CI run37526137995seven jobs green; not externally published) — closes the twenty-seventh review's two openings R117/R118 (playground edge hardening; zerosrc/changes, frozen language surface untouched): R117 — the README registered-boundaries sentence and designs/0019 now quantify source-code nesting depth as its own path (the parser/compile-side recursion, distinct from the interpreter call-depth path guarded at 300): it shares the engine-stack ceiling with no structured guard — node-measured ≈500 nested expressions trap while desktop's 256 MB stack handles 3000+ — surfaced as a translated trap (three-layer fallback) rather than a structured diagnostic; the app/worker trap wording is neutralized from "栈上限" to "引擎执行限制" (measured trap forms includememory access out of boundsandunreachable, not only stack exhaustion). R118 —harness.js run()now falls back to the default argv on an emptyargsarray (previously[]passed straight through and the host trapped reading argv[1]; an A/B probe confirms the pre-fixRuntimeError: unreachableis gone, and the depth-probe path still traps — never swallowed); the args contract is documented in the harness header.node playground/smoke.mjsALL PASS (11 items). Baseline unchanged by construction (zerosrc/changes): verify_selfcomp 368/368, bootstrap 14/14 quine 272293, Rust 561+31, eval 128/128 per backend, doc_audit 71/71. The frozen language surface remains unchanged. Previous repository milestone: v1.7.0 (2026-10-06; repository tag at7685d64, CI run37348863077#267 seven jobs green; published as a GitHub pre-release on 2026-10-07 — the first external release checkpoint; the deployed playground carries the v1.7.1 hardening) — the online playground (designs/0019, user-adjudicated; frozen language surface untouched, desktop behavior byte-identical): the full host toolchain now compiles towasm32-wasip1(a new[profile.wasm-release], ~1.7 MB, still zero third-party crates and zerounsafeinsrc/— the wasm-sideunsafelives only in the Rust toolchain's own std/WASI shims, exactly as desktop builds have always relied onstdinternals) and runs in the browser behind a hand-written zero-dependency WASI binding (playground/harness.js): args/stdout/stderr capture, a minimal preopen over a single read-write virtual file/playground.lom— so the page can run programs, show structured diagnostics, and applylom fix --applywith a line diff, entirely client-side. Twocfg(target_family = "wasm")-gated spots insrc/(skip the 256MB-stack worker thread; call-depth guard calibrated to 300 — node-measured engine stack ≈ 500 Lom frames, structured diagnostics fire before the trap). A node smoke suite joins CI as a seventh job (arithmetic/fib byte-parity with desktop, the four-step repair loop, depth guard,--check); the desktop baseline is untouched (verify_selfcomp 368/368, quine 272293, stock hex of all positive cases identical by construction). Registered boundaries: browser recursion ≪ desktop (wasm-side call-depth guard 300); source-code nesting depth — the parser/compile-side recursion path, distinct from the interpreter call-depth path — likewise shares the engine-stack ceiling with no structured guard (node-measured ≈500 nested expressions trap; desktop's 256 MB stack handles 3000+), surfaced as a translated trap rather than a structured diagnostic; thefilemodule is a virtual single file (v1.12.1 precision note:file_existsnow answers true for the mapped file andfile_appendappends at tail — harnesspath_filestat_get+ strict APPEND semantics per designs/0025; creates outside the mapped name remain structured ENOENT errors, the registered boundary);env::args()sees only the virtual script path. The frozen language surface remains unchanged. Previous repository milestone: v1.6.1 (2026-10-03; repository tag ate6b807b, CI run37125504909#259 six jobs green; not externally published) — closes the twenty-sixth review's three P3 openings R114/R115/R116 (RFC-0004 revision 54; user-adjudicated "execute then stand by"; hostsrc/untouched): R115 — the experimental L2 subset compiler now rejects same-file duplicate userfndefinitions in single-file mode (codegen error: [L2P001] L2.2 子集不支持: 函数 'go' 重复定义(同文件唯一命名,请重命名其一)), mirroring the host's NAM002 error face and the existing enum/variant-duplication precedent; the check is gated to single-file mode because file boundaries are unrecoverable in package-expanded units (cross-package same-name fns are the host's NAM006 deterministic-order legal surface, locked by pkg cases 106-108), and it only counts prior user fns (idx≥0) so fn-over-import-alias "local wins" semantics are preserved;classify_l2gains the "重复定义" keyword (L2P family). R114 — the v1.6.0 "141 messages classify, 0% fallback" claim is scope-annotated at every claim site (corpus scope: ~29 further combination messages outside the negative corpus constructively reach the L2G fallback — code nonempty, message intact, l2fix low-confidence and non-misleading); the l2fix fallback comment and help text are corrected; the ~29 keywords are deliberately NOT added (denser keywords raise misclassification risk on unseen combinations). R116 — SECURITY.md's CI supply-chain version references refreshed (@v5, Node 24; remaining annotations are Ubuntu 26 migration notices only, per the 2026-10-03 pre-unfreeze audit). New negativeneg_l2_dup_fn.lom; verify_selfcomp 367→368 (164 negatives); the quine at 272293 bytes and the stock hex of all positive cases stayed identical by construction; self_comp 12572→12591 lines; Rust 561+31 and eval 128/128 per backend unchanged. A same-sweep fix: the status paragraph's milestone line carried a stale batch attribution (v1.6.0 tagged but v1.5.0's description — R111's recurrence type), now corrected. The frozen language surface and external publication freeze remain unchanged. Previous repository milestone: v1.6.0 (2026-10-01; repository tag at6227a89, CI run36878845005#249 six jobs green; not externally published) — L2 diagnostic structuring prerequisite (designs/0017, RFC-0004 revision 52; user-adjudicated on all three points; hostsrc/untouched): the L2 subset compiler'scodegen errorchannel now carries a structured code on every rejection —codegen error: [L2T001] <original message>— via an output-layer keyword classifier (classify_l2, 7 families L2E/P/S/V/C/U/T + L2G fallback) modeled on the host'sclassify_runtime_error; all 263subset_errcall sites and every message body stay byte-identical (the 145 existing negative-lock assertions pass unchanged), all 141 distinct negative messages classify with a 0% fallback rate (corpus scope — annotated per R114 in v1.6.1). A pre-existing latent bug in the L2lex errorchannel is fixed on the spot (its error records lacked thecodefield the reporter reads — previously a RUNTIME000 crash; nowlex error 4:15 [LEX005] 意外字符 '@', with 2 new lex negatives). The negative-lock table is upgraded wholesale to code+message dual locks (147 entries) with a code-nonempty assertion; the three L2-face error_repair tasks (127-129) re-capture their embedded L2 text verbatim from the new output (solutions/expected unchanged). verify_selfcomp 365→367 (163 negatives); the quine moves to 271989 bytes (byte-identical both sides, +2434 from the classifier code); self_comp 12544→12572 lines; the stock hex of all positive cases stays identical by construction; selfhost six modes unaffected; eval 128/128 per backend unchanged. Line numbers are deliberately deferred (full coverage would need AST spans, conflicting with the registered "no-span" front-end design; a ~118-site main-cluster pass is a future batch). The frozen language surface and external publication freeze remain unchanged. Previous repository milestone: v1.5.1 (2026-10-01; repository tag at142a118, CI run36859203394#246 six jobs green; not externally published) — closes R112 (P2) (RFC-0004 revision 51; user-delegated to the maintainer's judgment): assigning to a captured outermutbinding from inside a closure body used to panic the interpreter (RefCell already borrowed, rc=1, guarded). The named-closure call path held itsenv.borrow()Ref across the whole closure-body execution, so the body's assignment re-enteredborrow_muton the same Scope — the fix takes the closure value out first (+9/−1). Method note: the planner initially suggested an explicit runtime error, but reading the code showed MUT002's registered semantics already say "interpreter = shared scope" — the panic was an implementation bug blocking that semantics, so the fix releases the borrow and the assignment now executes per shared scope (probe:6, outer variable visible; MUT002 warning still emitted), joining the read form in the same flagged divergence family; host-WASM and L2 still compile-reject the form. An old-vs-new A/B over 247 repo files shows zero diffs. Rust 561 unit + 28→31 integration (newtests/r112_closure_assign.rs×3); verify_selfcomp 365/365, the quine, and eval 128/128 unchanged. R112 closed — the ledger is empty. The frozen language surface and external publication freeze remain unchanged. Previous repository milestone: v1.5.0 (2026-10-01; repository tag atb61182f, CI run36841096349#242 six jobs green; not externally published) — repair-loop deepening batch 1 (designs/0016, RFC-0004 revision 50; user-adjudicated on all three design points; zero language-surface change): the fix engine's dispatch table no longer falls through to "unknown error code" forNAM006(collision notice — behavior already deterministic, rename only to remove ambiguity) andMUT002(closure captured amutbinding — don't rely on captured mut state), both now proper Medium hints that are never auto-applied; SPEC_FOR_AI's code table and fix-strategy table gain the rows they were missing; fix_corpus grows 11→13 pairs (informative warnings deliberately not auto-rewritten, per pair 04's precedent); and error_repair grows 24→31 tasks (repo-wide 121→128), including a new L2-face genre (127-129): code the host accepts but the experimental L2 subset compiler rejects, with the prompt embedding both the real host--check --jsonand the real L2codegen errortext, and reference solutions verified on three chains (host interp / host WASM / full L2 chain). A pre-existing interpreter panic was found and opened as R112 (P2, not fixed here): assigning to a captured outermutfrom inside a closure body hitsRefCell already borrowed(rc=1, guarded) while the other two backends reject outright. Rust 559→561 unit (+2); eval 128/128 per backend; eval_prompt_check 31/31; verify_selfcomp 365/365 and the quine unchanged (self_comp untouched). The frozen language surface and external publication freeze remain unchanged. Previous repository milestone: v1.4.15 (2026-10-01; repository tag atcf73de2, CI run36829534116#238 six jobs green; not externally published) — closes the R110 adjacent surface (RFC-0004 revision 48; user-adjudicated per the maintainer menu; designs/0015), completing the three-step block-shadowing generalization: for-variables (v1.4.13) → block-levellet(v1.4.14) → block-level destructuringlet (x, y)(this batch). The v1.4.14 snapshot matched onlyStmt::Let, so a block-level destructuring (whose bindings are always immutable) shadowing an outerlet mut xdrew a spurious MUT001 on post-block assignment; the snapshot condition now covers both statement forms name-by-name (the restore loop is unchanged). The twenty-fourth review's p13 probe goes from one false warning to clean across--check/run/host-WASM (output 10/20/5, runtime always correct); the negative (outerlet x) keeps its genuine warning with hint positioning, and the R75 fix engine keeps its conservative demotion (applied=0, no rewrite). A whole-repo old-vs-new--checksweep over all 4,300.lomfiles shows zero MUT001-surface diffs (10 lines each side, all genuine immutable reassignments). Rust 554→559 unit (+5 tests); verify_selfcomp 365/365 and the quine unchanged (self_comp untouched). §5.4's shadowing sentence is reworded to attribute NAM006/PKG007 surfacing to import/package collisions only. The frozen language surface and external publication freeze remain unchanged. Previous repository milestone: v1.4.14 (2026-10-01; repository tag ate8b099e, CI run36770326768#232 six jobs green; not externally published) — R110 remediation (user-adjudicated block-level snapshot/restore, the v1.4.13 fix generalized), closing the twenty-third review's P2: a block-levelletinsideif/while/forused to overwrite the same-named outer binding's mutability flag in the typechecker's function-level env and never restore it — failing both ways (a spurious MUT001 afterlet mut x+ blocklet x+ post-blockx = 5, and a missed one afterlet x+ blocklet mut x) while all three runtimes agree on block scoping.check_blocknow snapshots each first-seen block-levelletname's current-layer entry and restores it after the block (shadowed entries restored both ways; the registered no-shadow leak divergence deliberately untouched per the v1.4.13 precedent; for-var restore stacks independently; match arms on child envs unaffected). A whole-repo old-vs-new--checksweep over all 4,300.lomfiles shows exactly one diff — the checked-in R79 case's long-standing false MUT001 cleared, runtime byte-identical. Rust 548→554 unit (+6 tests); verify_selfcomp 365/365 and the quine unchanged. R110 closed — the ledger is empty. The frozen language surface and external publication freeze remain unchanged. Previous repository milestone: v1.4.13 (2026-10-01; repository tag at7a48810, CI run36753460493#228 six jobs green; not externally published) — closes the two remaining registered ledger items (pure diagnostics/tooling scope; RFC-0004 revision 46): the for-variable mutability quirk (registered since v1.2.3) —let mut x = 1; for x in 1..3 …; x = 5no longer draws a spurious MUT001 warning that was byte-identical to the genuine immutable case (the typechecker now snapshots and restores the shadowed current-layer entry around the loop; the read-after-loop divergence surface is deliberately untouched), and the manifest-lesslom buildper-file NAM003 (registered since v1.4.9) — package sources now typecheck against their own dependency-closure externals, clearing all cross-package false errors (105: three, 102: four) while out-of-closure symbols still fail (negative-locked). Rust 543→548 unit + 25→28 integration; verify_selfcomp 365/365 and the quine unchanged (self_comp untouched). Both items closed; the ledger is empty. The frozen language surface and external publication freeze remain unchanged. Previous repository milestone: v1.4.12 (2026-09-30; repository tag at11cdc68, CI run36723102998#225 six jobs green; not externally published) — R107 remediation (user-adjudicated: later-import-wins + NAM006 variant), closing the last open ledger item: when two packages import different real names under the same alias, all three implementations now resolve to the later import declaration (the interpreter/host-WASM already did; the L2's alias registry kept the first — its registration now overwrites, superseding designs/0008's "keep the existing summary" note), and the collision surfaces as a new NAM006 variant ("import alias 'g' duplicated — the later declaration wins (shadowing libr::origin)") on run/--check/build without blocking; no-alias imports stay exempt per v1.4.11. Newtests/r107_alias_clash.rs×4 (integration 21→25) and pkg_cases 108 (verify_selfcomp 364→365 = 196 + 8 + 161); the stock 203-pair comparison is byte-identical with the one new case as the intended diff (1 byte, fn-index swap — the old hex printed 4, the new prints 103 matching the host); self_comp 12529→12544 lines, quine 269555, stock hex identical except the one intended case. R107 closed — open ledger empty. The frozen language surface and external publication freeze remain unchanged. Previous repository milestone: v1.4.11 (2026-09-30; repository tag at13a4cf4, CI run36714193624#223 six jobs green; not externally published) — twenty-second-review remediation closing R108/R109 (RFC-0004 revision 44), pure typecheck-diagnostics scope: package enum variants no longer fail--checkwith false NAM003 errors (the Ident path now releases external-symbols names exactly like the function-call path — the checked-in 103 case goes from rc=1/three errors to rc=0/clean, misspellings still rejected), and the NAM006 shadowing warning now fires only for true aliases, eliminating v1.4.10's same-origin merged-unit noise on plain single-package imports (101 case: three misleading warnings → zero) while genuine alias-vs-local shadowing keeps its warning (r104 tests green) and main-file-fn-vs-package-fn collisions still surface through the externals-aware duplicate branch. Newtests/r108_variant_externals.rs×5 (integration 16→21); verify_selfcomp stays 364/364; self_comp/codegen untouched (quine 269521). R108/R109 closed; R107 (alias-vs-alias cross-backend divergence) remains open for adjudication. The twenty-third review (2026-10-01, baseline 21b3f26) confirmed all five closures loss-free and rated the repository B+, opening R110 (P2, pre-existing: block-scopedletshadowing leaks mutability flags in the typechecker — spurious and missed MUT001 both ways while all three runtimes agree on block scoping). The twenty-fourth review (2026-10-01, baseline 35df007) confirmed the R110 closure loss-free across 27 probes and re-rated the repository A- (rebound per the nineteenth→twentieth precedent), opening R111 (P3: two stale-documentation spots). The frozen language surface and external publication freeze remain unchanged. Previous repository milestone: v1.4.10 (2026-09-30; repository tag atce8c731, CI run36694182024#219 six jobs green; not externally published) — twenty-first-review remediation closing R104/R105 (designs/0014, RFC-0004 revision 43; user-adjudicated local-definition-wins + two warning codes in the freeze's safe zone): the interpreter's package loader iterated aHashMap, so two packages exporting the same symbol resolved nondeterministically across runs of the same backend (10-run probes mixed outputs; WASM/L2 were already deterministic) — registration now sorts by package-root path, making all three sides agree on "the path-later package wins"; and the interpreter's call path resolved callee names through the import-alias map before the user-function table, silently bypassing a localfnthat shadowed an import alias (WASM/L2 ran the local one) — the local name is now checked first, aligning all three sides to local-definition-wins. Collisions now surface instead of resolving silently:NAM006(warning) for alias/local and merged-unit shadowing — order-insensitive, fixing the old behavior where import-then-fn raised a false non-blocking NAM002 "duplicate" error and fn-then-import said nothing (genuine same-file duplicates keep NAM002) — andPKG007(warning) names both packages and the path-order winner (run/--check/build). Packagefnauto-publicity (real names callable without import) is now documented in §8.1 as the registered design it always was. Newtests/r104_dedup_order.rs(6 integration cases with hard determinism assertions — 10→16 total) and pkg_cases 106/107 lock three-side behavior; verify_selfcomp 362→364 (196 + 7 + 161); self_comp and codegen untouched (quine 269521, stock hex identical by construction). R104/R105 closed; open ledger empty. The frozen language surface and external publication freeze remain unchanged. Previous repository milestone: v1.4.9 (2026-09-30; repository tag ate71749e, CI run36677072901#216 six jobs green; not externally published) — R94 ledger batch 2 (designs/0013, RFC-0004 revision 42), closing the host ledger family:map_removeunified toBoolper SPEC §9.7's frozen claim (the interpreter/typechecker already returned Bool; the host-WASM helper dropped the hit information intoV_UNITand v1.2.13's adjudication A had aligned the then-experimental L2 to that Unit side — the helper now tags the probe result likemap_has, and the L2 verdicts flip to Bool, superseding that adjudication in favor of the frozen spec text; all three implementations now printtrue/false/true, an e2e Rust test locks it, and the stock compiler comparison shows 198 pairs byte-identical with 2 intentional diffs — cases 81/84, helper-signature bytes only). In-packageasaliases fixed on the host: package-source imports now register their alias→real-name mappings in the interpreter (also unblocking the in-package stdlib-alias form, test-covered), andlom build <file> --target wasmtypechecks with package externals — the 105 alias-chain case now agrees on all three sides (18/18) and the 3 false NAM003 errors are gone (the eighteenth review's "2 errors" was a miscount, corrected). Registered unfixed boundary: the manifest-drivenlom buildflow's per-file view still reports NAM003 inside the package source. Baseline: verify_selfcomp 362/362 (196 + 5 + 161;neg_map_remove_unit_binddeleted,neg_println_voidrewritten overmap_set, new positive 201), bootstrap 14/14 with the quine at 269521 bytes, self_comp 12529 lines, Rust 543 unit + 10 integration (newtests/r94_pkg_alias.rs×2), eval 121/121 per backend. R94 fully closed; no open ledger items. The v1.0 language surface (implementation aligned to already-frozen claims, not changed) and external publication freeze remain unchanged. Previous repository milestone: v1.4.8 (2026-09-30; repository tag at8c86a60, CI run36668306358#214 six jobs green; not externally published) — R94 ledger batch 1 (designs/0013, RFC-0004 revision 41), zerosrc/changes: the eval harness's only Map-key sort (the json_stringify mediation path) now uses UTF-8 byte order (Buffer.compare, aligned with the interpreter's RuststrOrd and the wasm-inlinedrt_str_cmpbehindmap_keys) — an astral-key probe (U+E000 + U+1F600) that previously ordered keys differently on the two backends now agrees byte-for-byte, closing R94's json key-order face; existing cases are unaffected (all checked-in Map keys are ASCII/BMP). Two negatives promote the twentieth review's §6-①② L2 rejection messages into the verifier (neg_user_fn_named_println,neg_match_scalar_on_list) and §6-③'s trust-boundary line joins the handoff checklist — verify_selfcomp 360→362 (195 + 5 + 162; self_comp untouched so all existing pairs are unchanged by construction, quine still 269510 bytes). Planner-found stale documentation (R103, four spots) fixed on the spot; a residual "#209" run-number typo in this banner from the R102 fix is corrected (the run is #210). The v1.0 language surface and external publication remain frozen. Previous repository milestone: v1.4.7 (2026-09-30; tag at22a37a6after six-job CI run36612026578#210 passed; not externally published) — R101 host fix: the WASM codegen'sand/orshort-circuit now pushesLabel::If(W-2 precedent), so areturnactually executed on the right side no longer traps the host-WASM backend — all three implementations agree (0/9/1/8, rc=0) and a Rust e2e test locks it (541→542). A pure codegen bug fix; no language-surface change. With v1.4.6 (E甲 expansion + R100) this completed user-adjudicated items 1–2; the twentieth review followed and rated the six-batch increment A-. R94 remained the only open ledger item at this tag. The v1.0 language surface and external publication remain frozen. Previous repository milestone: v1.4.6 (2026-09-30; tag at96c26b6after six-job CI run36608219947#207 passed; not externally published) — E甲 expansion + R100: the Bool display prescan gains the three blind-spot fixes (abbindknowledge table layered like D甲'scbindso the R85 param-direct boundary does not flip; a sharedmatch_bool_dispentry covering statement-level matches; strict seeding forms),for-iteration variables gain element-container knowledge (@ec:prefix), and the heap prescan's open-heap whitelist adds the five load-emitting read builtins — the formerly uninstantiable empty-List module now compiles validly. All five §11 gaps flip to positives; 16 pre-existing negatives re-verified unflipped. Delivered baseline: 360/360 selfcomp (195 single-file + 5 package + 160 negatives), 14/14 bootstrap, 269510-byte strong quine, 12525-line compiler, all 193 v1.4.5 pairs hex-identical. R95/R96/R97/R99/R100 closed; R94/R101 remain open (R101 next). The v1.0 language surface and external publication remain frozen. Previous repository milestone: v1.4.5 (2026-09-29; tag atdf7f83bafter six-job CI run36594643927#204 passed; not externally published) — R97 D甲: the display prescan tracks container payloads bound by match-arm binders (strictly AST-only tables: user-enum payloads, annotated container-returning fns, per-function container-binding knowledge; per-arm table copies with an unspellable@arm_localsentinel; a plannerload_cont_exprrefinement keeps pure-scalar-payload enums unmarked). No-String-literalmatch Some(xs) => println(xs)now compiles via direct-chain, parameter-annotation, and helper-return flows; cross-function direct containerprintlnwithout a binder stays rejected (negatives unchanged). Delivered baseline: 352/352 selfcomp (188 single-file + 5 package + 159 negatives), 14/14 bootstrap, 266073-byte strong quine, 12199-line compiler, all 183 v1.4.4 pairs hex-identical. R95/R99/R96/R97 are closed; R94/R100/R101 remain open. The v1.0 language surface and external publication remain frozen. Previous repository milestone: v1.4.4 (2026-09-29; tag at1851964after six-job CI run36578254262#202 passed; not externally published) — R96 C甲: closures call preludeprintln/ioimport (parent-env first, two-name exemption); 340/340 selfcomp, all 175 v1.4.3 pairs hex-identical, 262552-byte quine, 11938-line compiler. R96 closed at this tag. Previous repository milestone: v1.4.2 (2026-09-29; repository version, tag at 8012c38 after six-job CI run 36549057031 passed; not externally published) — user-approved R99 A甲 safety stage (designs/0012, RFC-0004 revision 34): value-position if/match expressions whose normal paths all terminate now get a compile-time rejection before hex emission, closing the known uninstantiable-WASM route. The nineteenth-review d2 L2 runtime claim was disproved after that review and corrected on the report front page; its historical B+ grade is not re-rated. R95's host-legal arithmetic shape remains an L2 compile-time rejection; R96/C甲, R97/D甲 and E甲 are still unimplemented. R100 P2 (empty-List read builtins can emit memory operations without a memory section) is open for user adjudication. Local verifier: 298/298 = 137 single-file behavior pairs + 5 package projects + 156 negative rejections; the 139 pre-A甲 behavior pairs produced byte-identical hex under an independent old/new compiler comparison (139/139, including the v1.4.0 subset 127/127); bootstrap 14/14 with a new 249786-byte strong quine; self_comp.lom 11469 lines; Rust 541 unit + 8 integration; eval 121/121 on each backend. Language surface and external publication remain frozen. Previous repository milestone: v1.4.1 (2026-09-28; repository version, not externally published) — the container-display batch closes the L2 compiler's last long-deferred display surface (designs/0011, user-approved; RFC-0004 revision 32):println/[1, a, true], Maps{a: 1, 中: 2}(byte-order keys), records{a: 1, b: x}(declaration order), tuples(1, a)incl. the single-element(a,)form, enumsNode(Leaf, 1, Leaf)(per-instance variant-name select chains; recursive enums and enum↔list mutual recursion self-reference through a placeholder-patched disp section), and closures<闭包>(flipping the previously registered divergence); ascan_has_displayprescan + ibase fallback guards deep flows; nine deferred negatives flip to positives, four new deep-flow negatives lock the fallback. On top of v1.4.0's RFC-0004 completion: the L2.4 bootstrap closure lands with a strong quine. The self-hosted compiler compiles its own source to a 234734-byte wasm module; that wasm re-compiles a representative case subset with byte-identical outputs, and re-compiles the compiler's own source in 1.1s to a module byte-identical with the host build (same sha256). Nine self-compilation gates were cleared (eight equivalent source rewrites — including a hand-written digit parser replacingstring_to_int's union — and one genuine gap: bottom-typed return-terminated arms in value-position if/match now merge via a?placeholder, flipping the R91 Form-B/if boundary into agreement; an inline return arm in a value-position if is likewise covered, while a return inside a Form-A single-line match arm is rejected identically by both sides at the syntax layer (single-return-arm shapes agree on both sides; the nineteenth review opened R95 for the both-arms-return shape where the host warns-and-runs and L2 rejects — adjudication pending)). A--bootstraptier (14/14, with a--ci-smokesub-tier in CI) guards the closure. On top of v1.3.2's records and tuples (designs/0010 stage one, RFC-0004 revision 28):vt rc{name:vt}carries field names (records are structural types — set/name/order mismatches are compile-time rejections) over an unpadded[n][8B-slots]heap layout, with field access as a compile-time offset under untagged static typing; the file/env builtins run through five harness imports (json-batch mediation precedent, harness argv pass-through forargs), math compiles inline, and io imports pass through as no-ops. The seventeenth review's R90 is closed in the same release —pkg-expandkeeps package-sourceas-alias imports in place so the L2 pipeline matches the host WASM on alias chains (the host interpreter's own rejection there stays a registered divergence, +2 Rust tests), and R91 widens the value-context return-arm boundary registration to bothifandmatch. On top of v1.3.1's statement-surface finale: the return batch (designs/0009, user-approved; RFC-0004 revision 26) compilesreturnstatements and?propagation via the wasmreturninstruction — zero depth tracking, no function-body wrapping (byte-identical output for programs without them), and the host's three historical label-depth bug families are structurally impossible.?unpacks Ok/Some payloads (width-specialized reads) and early-returns the original Err/None value, with stricter-than-host compile-time context checking (family + Err-payload compatibility); closure bodies save/restore their own return context. Eleven new pairs cover returns inside for/while/match arms/closures/recursion and?in tail-if position including the f64-payload validator-exemption shape; a registered boundary keeps a value-positionifwith a return arm a compile-time rejection (pre-existing host divergence). On top of v1.3.0's multi-file package projects (designs/0008, user-approved hybrid route; RFC-0004 revision 24). A new host tool,lom pkg-expand, expands a package project into a single compilation unit using the host's own dependency resolution as the single source of truth (packages ordered exactly as the host merge; package-internal top-level imports stripped;--listprints package names; manifest-less projects expand idempotently); the L2 side accepts an optional package-name list as a third argument so imports dispatch like the host — builtin modules, then known packages (imported names checked against the merged symbol tables,asaliases riding the real-name summaries) — and unknown modules are compile-time errors instead of silently dropped. Enum clashes across packages stay explicit rejections (registered trust boundary). Four multi-file package projects (single package / three-package chain / alias + enum / enum payloads) join the verifier at 238/238 with the 100 pre-existing pairs byte-identical. On top of v1.2.15's json batch (designs/0007, user-approved hybrid-mediation route + B1+B2 consumption + JS-value number splitting; RFC-0004 revision 22):json_parse/json_stringifyrun through two harness imports (JS semantics = the L2 comparison baseline; the module gains them plus an alloc export only under ahas_jsonprescan) over an opaquevt jsJSON-node tree (objects keep insertion order as kv sequences — not Map, avoiding key-reordering divergence; arrays materialize asls{js}cons chains);data.fieldcompiles to a key-matching helper that traps on miss like the host, js values flow through list builtins asls{js}with a kind guard,println(js)dispatches by node kind, and stringify accepts scalars/Lists/Maps via in-product js_of converters (enum/closure values rejected);from json importwith unknown names now errors. On top of v1.2.14's sixteenth-review remediation (R86-R89) and v1.2.13's Map batch (designs/0006, user-approved B1+B2 + strictness package): all eight map builtins withvt mp{V}(keys always String),map_getreturningOptionconsumed by existing match patterns, key-sortedmap_keys/map_values, structural==/!=via per-value-type recursive specialization, reference semantics shared with the host (let aliases see in-place writes), and rehash/tombstone behavior ported from the host hash table;map_removereturns Unit per adjudication A (a documented host interpreter-vs-WASM divergence stays host-side);from map importwith unknown names now errors instead of being silently ignored. On top of v1.2.12's fifteenth-review remediation (list_fold accumulator specialization, println(Bool) prescan) and v1.2.11's List batch. Compile-time strictness: non-Map receivers, non-String keys, value-type mismatches, and baremp{?}reads are explicit rejections. Tests: 541 Rust unit + 8 integration; L2 verifier 286/286 (139 behavior pairs (134 single-file + 5 multi-file package projects) + 147 negative rejections; the 127 pre-record pairs produce byte-identical hex); eval 121/121 on both host backends. The nineteenth review graded B+ (its own baseline only; not extrapolated) — the container-display claims verified loss-free, with R95 (P2: both-arms-return value-if divergence) through R98 opened for adjudication. (spec §13 changelog, RFC-0004 revision 22). v1.2.2 (2026-09-21) — Ninth-review remediation release (frozen language surface unchanged; bug fixes toward the frozen grammar and documented contracts): R55 High auto-fix safety (MUT001 scoped to the enclosing function, EFF001 at the signature end line with same-function effect merging,lom-apply/v1reportsfinaldiagnostics andok= applied>0 and zero final errors); R56 panic maps to exit 1 andi64::MIN / -1/% -1are structured RUNTIME000; R57 a missing finalendis a PARSE001 error (was silently accepted at EOF); R58 LSP JSON-RPC goes through the standard JSON parser (spaced/escaped/nested payloads work; real stdio e2e); R59json_parserejects unescaped control characters (RFC 8259, three implementations aligned); R60 CI gates hardened (zero-dependency double layer, type/pattern depth guards, eval prompt diagnostic check 24/24); R61 repo-wide rustfmt + CI actions on Node 24. Tests 487 → 513 + 3 integration; eval 121/121 both backends. v1.1.0 (2026-09-03) — remediation release: newMUT002warning (closure capturing amutbinding — interpreter/WASM capture semantics differ),NAM003false-positive fix (self-referentialletclosures),inf/NaNdisplay unified across both backends.Maintenance audit status (2026-10-06): external publication was unfrozen by user decision on 2026-10-04; the first public artifacts (README rework, pre-release v1.6.1, the online playground) are live. The twenty-sixth system-internal independent review graded its baseline A- — three delivery claims re-verified with zero distortion, a security-surface pass with zero P1/P2, and three P3 registry items (R114-R116, all closed in v1.6.1). This is a system-internal agent review, not external peer audit; grades never extrapolate across baselines.
lom fix --applyreports afinaldiagnostics block andokreflects the post-patch state — reviewing--dry-rundiffs for logic-bearing edits is still recommended. See TODO and the copy-ready handoff prompt.
✅ Phase 1 — Minimal Interpreter (completed; 历史里程碑快照,非现状——现状见上文 Status & boundaries 段与 eval/README)
- 13/13
.lomexamples pass (examples/*.lom) - 20/20 Rust unit tests pass (
cargo test)(Phase 1 时点快照,2026-08 初;现值见下方状态段) - Implements:
fn/let/let mut/if/elif/else/while/for/return, Int/Float/Bool/String/Unit, closures (first-class), builtins (println/print/int_to_string/string_to_int/len)
✅ Phase 2 — LLM-coding-native Core (completed)
- ✅ 2.1.1
match+enum+ Result/Option + pattern matching - ✅ 2.1.2
?error propagation (Result/Option) - ✅ 2.1.3
|>pipeline operator - ✅ 2.1.4 structural records
{x: Int, y: Int}+ tuples - ✅ 2.1.5 explicit imports
from mod import {name as alias}(stdlib: io/string/math) - ✅ 2.2 tolerant parser with holey AST (
Stmt::Hole, sync-point recovery, all errors collected) - ✅ 2.3 structured JSON diagnostics (
lom-diag/v1schema,--json/--check/--helpCLI, LEX/PARSE/RUNTIME error codes) - ✅ 2.4 gradual type checker (two-pass: signature collection + body check; TYPE/MAT/NAM error codes;
--checkruns type check,--jsonemits structured type diagnostics; progressive: type errors are warnings, dynamic run still works) - ✅ 2.5 explicit effect system (
! [IO, Clock]annotation;EFF001warning when pure functions call effectful functions;mainimplicitly has all effects; closures inherit enclosing effects; stdlibprintln/printdeclare[IO]) - ✅ 2.6
lom info --jsontype info export (lom-info/v1schema — functions/enums/imports;infosubcommand with--json; no type-check, no run; parse failure falls back tolom-diag/v1) - ✅ 2.7
lom fix --plan --jsonAI repair plan (lom-fix/v1schema — per-diagnostic plans withinsert/delete/hintactions,textsnippets for EFF001/MAT001,retryflag,confidencelevels; covers 20+ error codes across LEX/PARSE/TYPE/MAT/NAM/EFF/RUNTIME;fixsubcommand with--json) - ✅ 2.8
eval/100-task benchmark suite (10 categories × {arithmetic, control_flow, types, closures, match_enum, pipeline, records_tuples, effects, modules, error_repair}; per-task{id, prompt, solution, expected, notes}; PowerShell + Bash runners with--verifyreference-solution smoke test and--candidates-dirLLM evaluation; 100/100 reference solutions pass; LLM 实测 99/100 (99%) — error_repair 15/15, 0 syntax/import errors; see eval/REPORT.md) - ✅ 3.1
lom fix --applyrepair execution (lom-apply/v1schema — appliesconfidence=High+action≠Hintfixes to source;--dry-runpreview,--jsonstructured output; text patching via line/col offsets, reverse-order application to avoid drift; EFF001 upgraded from Hint to precise Insert — pure functions get! [E]appended, partial-effect functions get, Einserted before]) - ✅ 3.2 AST span-based diagnostic positioning (
Spantype onFnDecl/EnumDecl; parser recordsfn/enumkeyword position + signature end viaprev_token_pos; typechecker usescurrent_fn_spanfor EFF001/TYPE010/NAM002 instead of(0,0); removes Phase 3.1find_fn_linesource-scanning hack; end-to-end verified —effects_bad.lomEFF001 now points to10:1/21:1(as of 2026-08-22)) - ✅ 3.3 Standard library expansion —
list+jsonmodules (Value::Listvariant with immutable semantics;listmodule:list_empty/list_length/list_get/list_is_empty/list_head/list_tail/list_cons;jsonmodule: hand-written zero-dependencyjson_parse/json_stringifywith JSON↔Lom Value mapping — object→Record, array→List, string→Str, number→Int/Float, true/false→Bool, null→Unit; supports surrogate pair escapes; examples: examples/list_demo.lom + examples/json_demo.lom) - ✅ 3.4 Standard library expansion —
filemodule +stringextensions (filemodule:file_read/file_write/file_append/file_existswith[IO]effect annotation — file system read/write/append/existence;stringextensions:split/contains/replace/starts_with/ends_with— split returnsList<String>(empty sep splits by char); all string extensions are pure functions; examples: examples/file_demo.lom + examples/string_demo.lom) - ✅ 3.5 CLI demo +
envmodule — Phase 3 exit criterion met:envmodule (args()returnsList<String>); CLI--separator passes args to Lom program; todo list CLI (examples/todo.lom) — add/list/done/remove/help with JSON persistence, recursive list traversal, effect-correct (! [IO]annotations); end-to-end verified all commands
Phase 4 — LLM-repair-native + toolchain (direction adjusted 2026-08-07):
- ✅ 4.1.1 NAM003 spelling suggestion via Levenshtein (
levenshtein()≤2 edit distance; candidates = scope vars + top-level fns + stdlib + enum variants; tie-break prefers longer name;fix_nam_undefinedemits Hint with "Did you mean X?"; e2e tests:printl→println,lengt→length, no-similar-name path) - ✅ 4.1.2 MAT001 Result/Option auto-variant-insert (
MatchExpr::end_linerecordsendkeyword line; typechecker fillsend_lineinto MAT001 diagnostic;fix_mat_non_exhaustiveupgraded to graded fix — builtin variantsOk(_)/Err(_)/Some(_)/Noneget High-confidence precise Insert beforeend; user enum variants stay Medium Hint (params unknown, safe boundary); e2e tests: Result-missing-Err, Option-missing-None, user-enum-not-auto-applied) - ✅ 4.1.3 Fix history record (src/fix_history.rs) —
.lom/fix-history.jsonlNDJSON format (one JSON object per line);lom fix --applyauto-appends entry after successful write (timestamp/file/applied/skipped/changes with diagnostic_code);lom fix --history [--json]reads and displays past fixes (lom-fix-history/v1schema); zero-dependency ISO 8601 UTC timestamp via epoch calc;AppliedChangenow carriesdiagnostic_codefor history tracking; history write failure does not block apply success; 15 unit tests + 2 e2e tests
Phase 4.2 — REPL (interactive trial-and-error, LLM-friendly):
- ✅ 4.2 REPL interactive session (src/repl.rs) —
lom replsubcommand;is_input_complete()multiline completeness check (paren/brace/bracket depth + string state + block keyword/end pairing;enumis single-line decl,fn/if/while/for/matchneedend);ReplSessionwith context-preserving incremental execution (exec_item()registers fn/enum/import,exec_repl_block()runs stmt/expr in globals scope soletbinds persist); special commands:q/:help/:reset/:show; errors converted to output text (REPL never crashes on bad input);lom>/..>prompts for multiline; EOF (Ctrl+D) exits cleanly; 26 tests (8 completeness + 18 e2e)
Phase 4.3 — Simple LSP (IDE integration):
- 4.3 LSP (src/lsp.rs) — hover/completion/diagnostics over real stdio JSON-RPC. Since the R58 fix (2026-09-21) message parsing goes through the standard in-crate JSON parser (whitespace-tolerant, escape-decoding, nested-object safe) and all outbound strings are fully escaped; since the R63 fix (2026-09-21) the transport is bounded and standards-shaped —
Content-Lengthis capped at 16 MiB (oversized headers exit 1 instead of an allocation abort) and malformed payloads get-32700/-32600error responses (id:null) instead of silent drops. A real-process e2e (tests/r58_lsp_process.rs) covers initialize→didOpen multiline→diagnostics→hover→didChange→shutdown plus the R63 boundary pair. - ✅ 4.4 Package manager prototype (src/package.rs) —
lom buildsubcommand +lom.tomlmanifest (name/version/[dependencies]); local path dependencies (Dependency::Path);resolve_dependencies()DFS recursive resolution with cycle detection (CircularDep);collect_public_symbols()auto-exposes top-levelfn/enum;process_import()resolves external packages (PKG005 unknown package / PKG006 symbol not exported); runtimeload_packages()registers packagefn/enumvariants in interpreter; 16 unit tests (manifest parse, cycle detection, symbol collection, path resolution); example: examples/pkg_demo/ —mathliblocal dep withsquare/cube/factorial
RepairLoopBench — the 129-task repair-suite benchmark (eval/README.md is its benchmark card): a contamination-safe, dual-backend benchmark for the repair loop — its error_repair tasks hand models real structured diagnostics (verbatim host --check --json plus the L2 cross-compiler's [L2xxx] channel) and score convergence through the advisory fix loop; a scope statement distinguishing it from SWE-bench-family suites is on the card. Headline: full-set pass@1 = 98.3% / 98.5% across two frontier models (10 samples each, unbiased estimator, 2026-10-07 — full-set report). For citations, see the Citing section of the benchmark card (cite the tag, not main).
Verify the claims (from a fresh clone; ~5 minutes — the full third-party reproduction guide, including the LLM pass-rate experiments and what a fresh clone can and cannot verify, is docs/REPRODUCING.md):
cargo build --release && cargo test --release # 600 unit + 40 integration tests
python tools/verify_selfcomp.py # L2 self-compilation: 368/368
python tools/verify_selfcomp.py --bootstrap # three-layer bootstrap 14/14; the self-hosted
# compiler regenerates a byte-identical 272,925-byte quine
powershell -ExecutionPolicy Bypass -File eval/runner/run.ps1 -Verify -LomBin ./target/release/lom.exe # eval 129/129
./target/release/lom.exe codes --json | python -c "import json,sys; print(json.load(sys.stdin)['total'])" # 46600/600 Rust unit tests pass (plus 40 process-level integration tests: CLI exit codes, LSP stdio e2e incl. the R63 transport-boundary pair and the R71/R72 transport-corner trio, the R94 in-package-alias pair, the R104 same-name-package determinism ×6 in tests/r104_dedup_order.rs, and the R108 variant-externals ×5 in tests/r108_variant_externals.rs, and the R107 alias-clash ×4 in tests/r107_alias_clash.rs, and the closure-externals ×3 in tests/build_closure_externals.rs). 38 .lom files (29 examples + 4 bootstrap self-hosting files + 2 in the pkg_demo package + 3 self-hosted sources in examples/selfhost/ — the 5727-line reference interpreter self_interp.lom, the L2.1 encoder spike l2_spike.lom, and the 12671-line L2.3 subset compiler (v1.7.2; tagged v1.6.0 source was 12572 lines) self_comp.lom; apply_test.lom is an intentionally-broken lom fix demo). eval/ 138/138 reference solutions pass on both backends (tree-walking interpreter and WASM; ./eval/runner/run.ps1 -Verify -LomBin target/release/lom.exe, -Backend wasm for the WASM side). LLM generation pass-rate: 99.1-100% across 4 models (deepseek-v4-pro+thinking / v4-flash / glm-4.7 / glm-5.3, 2026-08-31, full 113-task set as it stood then — multimodel report); original 99/100 baseline: 2026-08-03, 100-task set (REPORT.md). pass@k retest (2026-09-07): 2 models × 10 samples @ temperature=1.0 on the 116-task set — pass@1 = pass@5 = pass@10 = 99.1% for both deepseek-v4-pro+thinking and glm-5.3 (unbiased estimator; only task 078, the prompt-ambiguity anchor, fails systematically — 0/10 both models) — pass@k report. full-set pass@k (2026-10-07, the current headline evidence): the same 2 models × 10 samples @ temperature=1.0 on the full 128-task set (first multi-sample measurement for the v1.5.0-era additions; tasks 126/127 tightened the same day per the ambiguity-fix lineage 078→118→126) — pass@1 = 98.3% (deepseek-v4-pro+thinking) / 98.5% (glm-5.3), pass@5 = 99.2% / 98.8%, pass@10 = 100.0% / 99.2%; sub-100 tasks fully attributed: 078 (known prompt-ambiguity anchor, its explicit twin 118 passes 10/10 both models) and 127 (deliberately hard L2-cross-compiler repair where 18/20 candidates miss one Option-unwrapping step) — full-set report. error_repair hot-patch sampling (2026-09-16): the same 2 models × 10 samples @ temperature=1.0 on all 24 error_repair tasks (including the four warning-level repair tasks 119/120/122 and LEX005 task 121) — 24/24 tasks × both models = 24/24 each, zero failures — patch report. iterative-repair first sampling (2026-10-28, the new category's debut): both models × 10 samples, single-step feedback chain, R=5 — C(2) = 100% for both (glm-5.3 C(1) 83.3% → C(2) 100%; deepseek-v4-pro+thinking 91.1% → 100%; zero censored at R=5); the headline hypothesis confirmed on task 131 (the known-hard 127 copy): single-round 30% on both models, and all 14 first-round failures converge in round 2 once fed the real diagnostics (14/14) — against the historical no-feedback baseline where 18/20 candidates miss the same Option-unwrap step: the repair loop's closure value, quantified — first iterative report. The latest committed main milestone is v1.16.0 (the iterative-repair task category — 9 new tasks measuring how many rounds a fix takes; task count 129 to 138), preceded by v1.15.0 (the ACRI native-conformance + lom explain batch — a nested position object in the check envelope and per-code long-form documentation; 7 new unit tests), preceded by v1.14.0 (the agent execution-trace batch — --trace call-level JSONL events for the interpreter run mode; 12 new unit tests), preceded by v1.13.0 (the playground share-links batch — Run folds the program into the URL hash and shared links restore the editor; suite 129 tasks), preceded by v1.12.1 (the wasm file-builtins fix — the playground engine's file_exists/file_append defects repaired at the WASI-harness layer per designs/0025; suite 129 tasks), preceded by v1.12.0 (the playground self-hosting demo — an in-browser four-step chain producing the 272925-byte quine byte-identically; tag 4a64471), preceded by v1.11.0 (the RepairLoopBench runtime-diagnostic task 130 + the v1.10.3 claims precision-correction; tag e83177b). The twentieth in-system adversarial review (2026-09-30, covering the v1.4.2→v1.4.7 six-batch increment) rated the repository A- (zero claim distortion, 22 probes zero silent miscompiles); the twenty-first review (same day, covering the v1.4.8/v1.4.9 R94-closure batches) verified both batches' claims loss-free with the new behavior surfaces probe-clean, and opened R104/R105 (P1: interpreter cross-run nondeterminism on same-name package symbols, and opposite shadowing direction for alias-vs-user-fn — both pre-existing name-collision surfaces awaiting adjudication) plus R106 (P3 documentation staleness, fixed on the spot). The twenty-second review (same day, baseline 92502e2) confirmed the R104/R105 closure conditions loss-free and rated the repository B+ (rebound per trajectory), opening R107 (P1, pre-existing: two packages importing the same alias with different real names resolve differently across backends, silently) / R108 (P2, pre-existing: package enum variants falsely rejected by --check) / R109 (P3, introduced by v1.4.10: merged-unit NAM006 noise on plain single-package imports) for adjudication.
Phase 4's feature surface is implemented; the R55/R58 reopening blockers (High auto-fix boundaries, LSP transport conformance) were closed by remediation on 2026-09-21. REPL and the package prototype remain available. Direction adjusted per 2026-08-07 retrospective — original "workload-native" (tensor/autodiff/MLIR) dropped: Mojo acquired by Qualcomm makes the AI-compute lane crowded; Lom focuses on the repair loop after these blockers are closed. See §2.5 retrospective.
Phase 5 — Ecosystem & Bootstrapping (complete 2026-08-19 — closing evaluation in §2.7):
- ✅ 5.0 Self-host feasibility verified (examples/bootstrap/) — mini interpreter written in Lom itself:
String → split("") → lex → List<Token> → recursive descent parse → AST → eval. Correct operator precedence & left-associativity ("3+4*2"→11,"1+2*3+4"→11). Recursive enum (AST nodes) + match +split(s,"")char scan sufficient for compiler core 3 stages. Language gaps found: no tuple destructure (let (a,b)=...), no char type, no HashMap — see Phase 5 notes. Fixed tree-walking stack limit by running interpreter in 256MB-stack thread. - ✅ 5.1
lettuple destructuring —let (a, b, ...) = exprbinds tuple elements to names (src/ast.rsStmt::LetDestruct; src/parser.rsparse_let_destructwith trailing-comma tolerance &mutrejection; src/interpreter.rs runtime tuple/count checks; src/typechecker/ per-element type binding orUnknownfallback). Motivation: the #1 "LLM writes it naturally but language rejects it" pattern discovered during self-hosting (PARSExxx "expected identifier, got LParen"); now examples/bootstrap/mini_interp.lom parses with natural(Expr, List<Token>)tuple returns — no record workaround. 5 unit tests + 287/287 total. - ✅ 5.2 statement-layer self-hosting — examples/bootstrap/stmt_interp.lom adds Stmt layer (
SLet/SExpr/SIf) over the expression interpreter: let bindings, if/else, blocks, comparisons; functional env viaList<(String, Int)>+ cons shadowing; built on 5.1 tuple destructuring - ✅ 5.3
foroverList<T>(v0.4.1 P0 gap fix) —for x in xsnow iterates Lists (previously String/Int only,RUNTIME000); interpreter gains aValue::Listiteration branch (element binding,returnpropagates), typechecker derives element type (List<T>→ x: T); 4 new tests (291/291 total), eval task 101 added (101/101 pass) - ✅ 5.4 string concat promotion (v0.4.1 P0 gap fix) — if either operand of
+is a String, the other is promoted viato_display():"n = " + 42works (previouslyRUNTIMEerror, requiredint_to_string); typechecker result type is String, non-promotable combos (Int+Bool) still warn TYPE001; 3 new tests (294/294 total), eval task 102 added (102/102 pass) - ✅ 5.5 compound assignment (v0.4.1 P0 gap fix) —
+=-=*=/=; lexer gains 4 tokens (maximal munch, distinct from->), parser desugars tox = x op ewith newline guard, so interpreter/typechecker reuse the full Assign pipeline unchanged (NAM003/TYPE001 checks included);+=composes with 5.4 concat promotion; 5 new tests (299/299 total), eval task 103 added (103/103 pass). v0.4.1 P0 gap trilogy complete. - ✅ 5.6 range expression (v0.4.2 P1 gap fix) —
a..b(left-inclusive right-exclusive) evaluates toList<Int>, reusing 5.3's for-in-List and the whole list module with zero new runtime machinery (list_length(1..4)works); lexer gainsDotDot(distinct from Float3.14and tuple indext.0), parser adds lowest-precedenceparse_range(belowor, non-associative, newline-guarded), typechecker recordsList<Int>and warns TYPE001 on known non-Int ends; resolves LANGUAGE_SPEC open question #1 (Rust-stylea..bchosen,a..=brejected — two range operators are a known LLM confusion source); 8 new tests (307/307 total), eval task 104 added (104/104 pass) - ✅ 5.7 match guard (v0.4.2 P1 gap fix) —
pattern if cond => body: arm wins only when pattern matches AND guard (Bool) is true, otherwise falls through; guard may reference pattern-bound variables; typechecker checks guard is Bool (TYPE002) and guarded arms don't count toward exhaustiveness (Rust semantics — guard truth is runtime-only); 6 new tests (313/313 total), eval task 105 added (105/105 pass) - ✅ 5.8 named functions as first-class values (v0.4.2 P1 gap fix) —
let f = doublewraps a named function as a closure value (env = globals, identical tocall_function's parent env; recursion unaffected since in-body name calls still go through the functions table); typechecker already allowed function references (gradual Unknown) so zero changes there; 3 new tests (316/316 total), eval task 106 added (106/106 pass). Language prerequisite forlist_map/list_filter-style higher-order stdlib. - ✅ 5.9 higher-order list stdlib (v0.4.3) —
list_map(f, xs)/list_filter(f, xs)/list_fold(f, init, xs);call_builtinwidened from&selfto&mut selfso builtins can call back into closures;faccepts closure literals or named functions (5.8 wrapped values); composes naturally with range (list_map(double, 1..6)) and concat promotion (list_foldstring building); typechecker signatures registered (f: Fn); 4 new tests (320/320 total), eval task 107 added (107/107 pass). Also backfilled SPEC_FOR_AI's missinglistmodule table (doc debt since Phase 3.3) - ✅ 5.10 bootstrap deepening — examples/bootstrap/stmt_interp.lom gains
SWhile(source-level while loops;let mut+ Lom while threads the functional env across iterations, cons shadowing makes rebinding work);exec_stmtsrewritten from hand recursion tolist_fold+ closure — real dogfooding of 5.8/5.9 inside self-host code; new test program 3 (while countdown sum = 6), outputs11 / 10 / 6all correct; effect annotation chain completed (! [IO]) and TYPE010 eliminated —--checknow reports 0 diagnostics (was 2 historical warnings) - ✅ 5.11 bootstrap functions — source language gains
fn name(p1, p2) ... enddefinitions and calls with lexical scoping (fresh param env per call);Decllayer (DFn/DStmt) with collect-fns-first execution so forward references and mutual recursion work (program 5:collatz_lencalls later-definedis_odd, 8 recursion steps correct). Key refactor: statement-value semantics — naive "last SExpr is the return value" broke atif(values can't escape branches; in-function SExpr mis-prints), rebuilt asexec_stmt → (env, value)whereSIf's value is the taken block's value (if-as-expression), block value = tail statement value,in_fnflag separates top-level printing from in-function evaluation. Debug finding recorded: mini-language has no==(lexer silently drops=, causing infinite recursion + stack overflow — equality now expressed arithmetically). Outputs11/10/6/49/8all correct,--check0 diagnostics - ✅ 5.12 bootstrap value system — values grow from Int-only to
Val = VInt | VStr | VBool(recursive enum, feasibility proven in 5.0); source language gains string literals,==comparison, lowercasetrue/falseBool printing;Addmixing mirrors Lom v0.4.1 (either-side-String promotes viashow()); conditions unified undertruthy(). The 5.11 trap is fixed at the root:=/==are now explicit tokens (TAssign/TEq) instead of being silently dropped. 9 test programs all correct (incl.hello, lom,true,ababab),--check0 diagnostics. Value-system prerequisite for a self-host diagnostics layer - ✅ 5.13 bootstrap diagnostics layer — the whole evaluator moves from "silently yield 0 on error" to
Result<_, String>+?propagation: Lom's errors-as-values philosophy implemented in Lom. Undefined variables/functions, arity mismatches, type errors, division by zero all report explicitly (program 8's four diagnostics verified verbatim);?threads through while conditions and loop bodies; exec_stmts folds a Result (Err short-circuits); error messages concat Ints via v0.4.1 promotion. The typechecker's TYPE020?-compatibility rules got a real stress test. 13 programs correct,--check0 diagnostics - ✅ 5.14 bootstrap
VListrecursive values —ValgainsVList(List<Val>)(first recursive value type in the self-hosted runtime); source language gains list literals[1, 2], chainable indexingxs[i], and alen()builtin (works on List and String — first builtin mechanism in the mini-language); literal element evaluation reuseseval_args; show/values_eq/truthy extend recursively ([[1, 2], [3]]prints correctly); out-of-bounds/negative-index/type errors go through the 5.13 diagnostics channel (original index preserved in messages — first version reported the decremented counter, fixed). 9 programs / 18 outputs verified verbatim,--check0 diagnostics - ✅ 5.15 bootstrap fault-tolerant parsing — mirrors the host's holey-AST philosophy: parse errors become AST nodes (
EError/SError) reported via the 5.13 diagnostics channel at execution; lexer unknown chars yieldTUnknowntokens instead of silent drops; parser is now total (peek_tokOption guards replace all barelist_head— empty/truncated/unknown input never crashes). Program 10: statements before the error still run (7printed), thenparse error: unexpected unknown character '@'; truncated1 +and empty input are safe. Debug log: missingendin a Form B arm containing an inner match (parse_call_args — the §4.1 trap again), an over-correction addingendto a Form A arm (Form A needs none — the A/B asymmetry was the root confusion), and two latentlist_consarg-order bugs caught by the typechecker's TYPE003. 10 programs / 21 outputs verified,--check0 diagnostics - ✅ 5.16 bootstrap intrinsics — the 5.14
lenspecial case graduates to a proper dispatch:try_intrinsic(Option<Result<Val, String>>, None falls through to user functions; intrinsics take precedence). New:split(String→List via list_map+closure),contains,trim,to_string(reusesshow),push(append — complement to host's cons-prepend, hand-recursed). All parameter checks go through the diagnostics channel. Program 11's 6 outputs verified verbatim ([a, b, c],3,true,x,[1, 2, 3],42!); 27 total outputs correct,--check0 diagnostics - ✅ 5.17 bootstrap positioned diagnostics — tokens become records
{t: Token, ln: Int}(putting the host's structural records to work); the lexer threads line numbers per function; parser side is isolated behind two helpers (peek_kind/peek_line) so every existing match is untouched and only error messages gainat line N. Program 12:@reports line 4, malformed let/fn report their own lines. Mini-language diagnostics now match the host's shape (stage + message + position; only the JSON schema differs). Noted: the Form-B-arm-with-inner-match missing-endtrap fired a third time (its "期望表达式,得到 FatArrow" signature is now a known fingerprint). 31 outputs verified verbatim,--check0 diagnostics - ✅ 5.18 performance baseline — new examples/bench.lom (
lom bench.lom -- <bench> <n>, external wall-clock; Lom has no clock builtin). Real measurements (Windows, release, single runs): list_build 1000→8000: 55→494ms (superlinear, cons copies O(n) each); lookup 200→800: 2383→86875ms (~4.5×/doubling, near-cubic —list_tailcopieselems[1..]per call, so scanning is O(n²)); recurse: 10000 OK (82ms), 100000 overflows the 256MB stack thread (~2.6KB Rust stack per Lom frame → safe depth ~10⁴). Data-driven P2 revision: the real self-hosting bottleneck is theVec-backedValue::Listrepresentation (functional head/tail/cons processing degenerates to O(n²)), NOT the missing HashMap — P2 should prioritize an Rc cons-cell / persistent list, HashMap second; recursion depth limit documented as a known limitation (a real fix needs an explicit stack or trampoline, deferred to the compiler phase) - ✅ 5.19 List as Rc cons cells (v0.5.0, P2-①) —
Value::List { elems: Vec<Value> }→Value::List(ListVal)persistent list (Nil | Cons(Rc<ConsNode>)): cons/tail O(1) (Rc clone, structural sharing), head O(1); trade-off: random-accesslist_getbecomes an O(n) walk (fine for traversal-style code). All 31 use sites migrated (interpreter 24 + json 7); public API: cons/head/tail/len/get/is_empty/from_vec/iter. Before/after, same machine same benchmarks: lookup n=200: 2383→184ms (13×), n=400: 10784→559ms (19×), n=800: 86875→1893ms (46×), n=1600: infeasible (~10min) → 7.3s; list_build n=8000: 494→39ms (12×+, now startup-dominated). lookup's residual quadratic growth is the algorithm's inherent linear scan — that's where a future HashMap belongs. Full regression: 320/320 tests, eval 107/107, all 31 bootstrap outputs unchanged - ✅ 5.20 map module (v0.5.1, P2-②) —
Value::Map(Rc<RefCell<HashMap<String, Value>>>)+mapstdlib module (map_empty/map_set/map_get→Option/map_has/map_remove/map_keys/map_values/map_size); reference semantics with interior mutability (map_set mutates in place O(1),letaliases share — deliberately unlike List's immutable persistence; copy-on-write was rejected because the args slice always holds an Rc soRc::get_mutnever succeeds); keys/values return sorted for deterministic output; json_stringify serializes Map as a JSON object (sorted keys). Real measurements (same machine): lookup(List) n=2000 → 18136ms vs map_lookup n=2000 → 55ms (~330× wall-clock; ~48ms of that is interpreter startup, so compute-only ≈ 2500×); map_lookup is flat ~55-66ms across n=500/1000/2000 (O(n) total). 5 new tests (325/325 total), eval task 086 added (108/108 pass) - ✅ 5.21 bootstrap env switched to Map (v0.5.1 dogfooding) — stmt_interp.lom's variable env goes from
List<(String, Val)>assoc list (linear scan, functional threading) to the host's Map (O(1) lookup, in-place mutation):env_lookupcollapses from 10-line recursion to a 4-linemap_getmatch;bind_paramsbecomes a while +map_setloop;exec_stmt/exec_stmtsno longer return env (mutation replaces threading) — signatures drop fromResult<(Env, Val), String>toResult<Val, String>. Lexical scoping preserved: callee gets a freshmap_empty(). All 31 outputs verbatim identical,--check0 diagnostics. Real measurements (stress program: 100 lets + N-loop re-lookup): N=200 → 842→226ms (3.7×), N=400 → 2436→292ms (8.3×; old grows ×2.9/doubling, new ×1.3 — quadratic vs linear, as predicted) - ✅ 5.22 bootstrap static check pass — the mini-language gains a check phase between parse and eval, mirroring the host's three-stage architecture:
check_programcollects undefined-variable / undefined-function / arity diagnostics asList<String>(mini NAM003) andrun_programrefuses to execute on any diagnostic. Scope is a host Map (dogfooding 5.20/5.21 again); fn bodies see only params (lexical scope, same as runtime); SLet checks RHS before binding (let x = xcorrectly errors). Call checking is split intocheck_call/check_call_fnhelpers to dodge the §4.1 nested-match end-counting trap. Program 8's undefined-variable and arity errors upgrade from runtime to check-time (check error:prefix); new program 13 covers RHS-before-bind, intrinsic arity, and check-pass-then-run. 34 outputs verified verbatim,--check0 diagnostics - ✅ 5.23 bootstrap wrap-up: fns table → Map + operator completion — the function table goes from
List<Decl>linear scan toMap<Decl>(collect_fns builds it directly, find_fn is a 4-linemap_getmatch); the mini-language gains<<=>=!=(lexer lookahead for>/>=,</<=,!=/bare-!— same explicit-token pattern as=/==; evaluation unified in aneval_cmp_intskeleton with byte-identical error messages;!=negatesvalues_eqfor all value types incl. lists). New program 14 (5 outputs: true/true/false/true/true). 39 outputs verified verbatim,--check0 diagnostics. Every name lookup in the bootstrap interpreter is now O(1). - ✅ 5.24 char type decision (docs-only) — LANGUAGE_SPEC open question #3 closed: no separate
Chartype, single-char Strings are Char (Python/JS model). Rationale: Python (the language LLMs know best) has no char; Rust's'a'/"a"split is a known LLM confusion source; the measured bottlenecks were never char (5.19-5.21 fixed the real ones). Recorded in LANGUAGE_SPEC §11 and DESIGN_RATIONALE §11.7. All three P2 items (List representation, HashMap, char) are now closed.
Phase 6 — Production-grade (engineering surface complete 2026-08-22; closing evaluation in §2.8):
- ✅ 6.1 Semantic versioning — Cargo.toml aligned to
0.5.1(was 0.1.0 while milestones said otherwise), first git tagv0.5.1, newlom --versionflag (CARGO_PKG_VERSION as single source of truth), version policy in HANDOVER - ✅ 6.2 Community governance — CONTRIBUTING.md (zero-dep rule, no-fabricated-data rule, regression trilogy, milestone-docs pairing), CODE_OF_CONDUCT.md (Contributor Covenant 2.1), RFC template with a mandatory LLM-impact analysis section
- ✅ 6.3 Multi-platform CI — .github/workflows/ci.yml: build +
cargo test+ bootstrap regression (39-output floor) + eval verify on Linux/macOS/Windows - ✅ 6.4
lom doc(src/doc.rs) — API doc generator: signatures from AST, doc comments recovered from source (consecutive#lines above each item; the lexer drops comments so they can only come from source text); Markdown by default,lom-doc/v1JSON with--json; 8 tests - ✅ 6.5
lom fmt(src/fmt.rs) — token-stream-driven reindenter (not AST round-trip, which would lose comments); 4-space canonical indentation, comments/strings byte-preserved, single-lineenum X = A | Bspecial-cased, paren-continuation lines untouched;--applyin-place /--checkCI gate; 12 tests; all 33 valid example files verified idempotent - ✅ 6.6 Security audit — SECURITY.md: trusted-program threat model, zero-dependency supply chain, hardening inventory (grep-verified zero
unsafe, total parser, checked arithmetic — 更正 2026-09-03:checked 算术从未实现,i64 溢出在 release 下静默回绕,现如实记为 SECURITY.md 已知限制第 4 条), accepted risks honestly listed - Honest note on the exit criterion: "a third-party project deployed to production with Lom" cannot be self-certified — the engineering surface is done; adoption remains the long-term north star
- ✅ 6.7 Review-driven rectification (2026-08-22, after an external read-only review) — type checking is now visible on the default run path (diagnostics on stderr, never blocks execution — the gradual-typing promise is unchanged; previously
lom fileskipped the checker entirely solet x: Int = "hello"ran silently); eval runners compare stdout only and now require exit code 0; CI gains three gates (bootstrap golden-file verbatim diff viastmt_interp.expected.txt,lom fmt --checkover all examples, zero-dependency enforcement making SECURITY.md's promise real); doc-rot sweep (HANDOVER/eval-README/guide anchors/SPEC'spubmarking/stale EFF001 line numbers); all 19 build warnings cleaned (genuine misuse removed, intentional API surface annotated); v0.6.0 released + tagged (doc/fmt were user-visible additions that hadn't bumped the version)
Phase 7 — WASM compiler backend (complete 2026-08-23, RFC-0002):
- ✅ 7.1 emitter skeleton (src/wasm.rs) — hand-written zero-dependency WASM binary writer (LEB128, type/import/func/memory/global/export/code/data sections); byte-level golden tests + real Node instantiation smoke test
- ✅ 7.2 dynamic-semantics codegen (src/wasm_codegen.rs) — tagged-i64 value representation (Int/Bool/Unit inline, Float boxed, Str in static data); functions (recursion/mutual recursion OK),
let/let mutwith block scoping, if/elif/else (statement + expression), while, for-over-Int, earlyreturn, arithmetic with Int→Float promotion, comparisons,and/orshort-circuit, string literals + string==; runtime tag dispatch in hand-written WASM helper functions; CLIlom build <file> --target wasm [-o out.wasm]; host imports via eval/runner/run_wasm.mjs (Node.js) - ✅ 7.3 closures & named-function values — closure objects
[table_idx][env_ptr](tag 5), free-variable analysis with value-copy capture at creation,call_indirectdispatch via funcref table, shims for named functions as values, recursive closures via pre-bind + env slot patch. Verified byte-identical vs interpreter on closures/hof/logical/nested_calls + 11/12 eval category 04 - ✅ 7.4 strings & stdlib (v0.9.0) — concat promotion (
"n = " + 42), itoa/ftoa, len/trim/upper/lower/contains/replace/starts_with/ends_with/string_to_int, string ordering comparisons, for-over-String (UTF-8 chars), sqrt/abs/min/max, import aliases. Verified on 18 examples + 48/52 eval tasks in categories 01/02/03/04/09 (rest need List/Map → 7.6) - ✅ 7.5 enum/match/
?(v0.10.0) — enum heap objects[variant_idx][n_args][args...], match arm chains with guards,?unwraps Ok/Some and early-returns Err/None; recursive enum equality/printing - ✅ 7.6a Record/Tuple/List (v0.11.0) — tuples, records (order-insensitive equality), cons-cell lists,
a..branges, for-over-List,split,list_map/filter/fold(closure callbacks viacall_indirect). Value representation migrated to 4-bit tags (3-bit mask truncated tags 8+) - ✅ 7.6b Map + memory.grow (v0.12.0) — hand-written open-addressing hash table (FNV-1a, linear probe, tombstones, 2x rehash), sorted deterministic output; bump allocator auto-grows memory pages. eval 108/108 byte-identical on BOTH backends; the ~1400-line self-hosted mini-interpreter (
stmt_interp.lom) compiles to WASM and matches its golden file verbatim - ✅ 7.7 json module via host mediation (v0.13.0) —
lom_json_parse/lom_json_stringifyhost imports (harness implements with JS JSON + value layout read/write;lom_alloc+lom_variant_tableexports for host-side materialization). Documented divergence: Int/Float split of JSON numbers decided by JS value, not source syntax - ✅ 7.8 file/env + package linking (v0.14.0) — file_read/write/append/exists + env::args() via host imports;
lom buildmerges lom.toml dependency sources into one WASM module (later definition wins on name clash, matching the interpreter). All stdlib modules now compile to WASM - ✅ 7.9 golden acceptance + CI wasm gate (v0.15.0) — eval runner
-Backend wasmmode (108/108 on WASM); CI gains WASM parity steps (eval suite + stmt_interp golden via node on all 3 platforms); type checking runs before wasm compilation (stderr, non-blocking — same gradual-typing promise as the interpreter) - ✅ 7.10 performance comparison + recursion-depth settlement (stretch) — same-machine benchmarks (HANDOVER §10): WASM vs tree-walking ~49× compute-only on the compute-heavy bootstrap-style lookup workload (wall-clock 20×; Node's ~98ms startup dominates small loads); recursion ceiling moved from a hard ~10⁴ (tree-walking) to host-stack-tunable (10⁵ verified with
node --stack-size) — RFC-0002 criterion 4 settled as "partially met (tunable to 10⁵)", a definitive fix (explicit stack machine/trampoline) remains an option. All Phase 7 milestones (7.1–7.10) closed; exit criteria 1/2/3/5 met, 4 partially met.
Fix engine deepening (started 2026-08-25 — strengthening the LLM-repair-native core):
- ✅ M1 spelling-repair
replaceactions (v0.16.0) — NAM003/NAM004 diagnostics with a did-you-mean suggestion now emit concretereplacefix actions (NAM004 gains the suggestion itself: record fields and enum variants reuse the Phase 4.1.1 Levenshtein machinery). Since typechecker diagnostics carry no expression-level spans, occurrences are located by whole-token source scanning (strings/comments excluded; record fields require a.prefix so same-named variables are untouched) — superseded by Phase 3.2b (v0.21.0): single-point replace from diagnostic spans, scanning kept only as fallback. Confidence is medium — guessed repairs are never auto-applied bylom fix --apply(explicit safety policy: only 100%-certain fixes auto-apply). The schema'sreplaceaction, reserved since Phase 2.7, is now produced by real rules. 11 new tests (406/406 total) - ✅ M2 iterative
--applyrepair loop (v0.17.0) —lom fix --applyupgrades from single-pass to a closed loop: re-diagnose → apply → repeat until no high-confidence fixes remain or the source stops changing (max 5 rounds as an anti-oscillation brake). The classic "fix A exposes B" case now converges in one command (e.g. deleting an unexpected char lets parsing succeed, which surfaces EFF001 for round 2). Output gains per-round grouping (human) androunds+ per-changeroundfields (JSON, schema stayslom-apply/v1); fix history records each applied round as its own entry (old entries withoutroundread as round 1 — backward compatible). Single-roundto_json/to_humanin apply.rs retired into the multi-round versions; exit-code semantics unchanged. 5 new tests (411/411 total) - ✅ M3 targeted PARSE001 repairs (v0.18.0) — the most common LLM syntax error class graduates from hint-only to concrete fixes, with an evidence-based twist: probing the real parser showed missing
endforfn/if/whileis silently accepted by the tolerant parser (no diagnostic at all), so the only end-class error is期望 'end' 闭合 match. Rules:期望 ')'+ offending token at line start (new statement /end/ EOF) → insert)at the end of the previous non-empty line (high, auto-appliable — eval 086/088's exact case, verified end-to-end: fixed file prints 7);期望 ')'+ mid-line token → insert at error position (medium — could be a missing comma instead);期望 'end'(unclosed match) → medium insert. Zero parser changes needed — the diagnostics already carry everything. 5 new/updated tests (416/416 total) - ✅ M4 error_repair expansion +
--applygolden corpus (v0.19.0) — eval error_repair grows 15 → 20 tasks (multi-error files, NAM003/NAM004 spelling-suggestion tasks matching M1, effect chains, unclosed match); all diagnosis JSONs in prompts are reallom --jsonoutput, and every reference solution is executed to verify its expected output. Neweval/fix_corpus/— (bad, fixed) source pairs driven end-to-end through the iterative--applyloop in a Rust test, the repair-native claim's regression net. Corpus composition is the honest coverage metric: 3/4 cases fully auto-repaired, 1/4 advisory-only by design (medium-confidence spelling fix, never auto-applied). Finding recorded: LEX001's line-end quote insertion mis-composes when the unclosed string swallowed a trailing)(the paren ends up inside the string) — documented as a known limitation, not silently "fixed" with a guessy heuristic. 1 new test (417/417 at the time; 418/418 now after the eval-ID uniqueness test) - ✅ MUT001 immutable-reassignment check (v0.20.0, 2026-08-31) —
let x = 3; x = 4used to pass silently (spec said "rejected", implementation had zero checks — a round-4 review self-finding, then a pending decision). Now the typechecker tracks binding mutability (TypeEnv.mutables) and emits aMUT001warning for assignment to immutable bindings: plainlet, function parameters,forloop variables,matchpattern bindings, and compound+=/-=targets (they desugar to=). Warning-level per the gradual-typing philosophy — stderr only, never blocks execution, the interpreter stays permissive;let mutand same-nameletshadowing stay silent. Message/hint deliberately avoid claiming "let" for parameter targets (Lom has nomutparams). 8 new tests (426/426 total) - ✅ Phase 3.2b expression-level spans (v0.21.0, 2026-08-31) — the last positioning debt is paid.
Exprbecomesstruct { kind: ExprKind, span: Span }(the enum is nowExprKind);Stmt::Let/Stmt::Assigngain spans; the parser fills every node (span_since/span_fromhelpers, left-assoc chains inherit the left operand's start, desugared nodes reuse the target's span). Diagnostics leave(0,0): NAM003 points at the identifier use, NAM004 at the field-name token (theFieldspan'send), MUT001 at the assignment target, TYPE001/002/003/020 at the expression.lom fixNAM003/NAM004 spelling repairs upgrade from whole-token scanning to single-point replace (scan retained as a sanity-checked fallback for span-less positions, e.g. variant names in patterns —Patternhas no span). Caveat encoded infix.rs: lexer columns are 1-based byte columns, fix actions are char columns —precise_occurrenceconverts and refuses mismatches. Interpreter/WASM backends behavior-neutral (match &expr.kind). 11 new tests (437/437 total) - ✅ Phase 8 readiness (v0.22.0, 2026-08-31) — RFC-0003 (full self-hosting) accepted by user decision and committed; its two pre-8.1 sub-questions settled: host AST dump =
lom <file> --dump-ast(new; deterministic indentation tree overProgram, spans excluded — host byte-column vs bootstrap char-column divergence would poison verbatim diffs; format-is-contract, golden-tested), and the 8.2 static-check depth = the NAM/arity/EFF/MAT subset. CI audit: the golden verbatim gates (interpreter + WASM) already in place since Phase 7.9 cover the machinery the 8.4 three-layer golden needs — one more gate step at 8.4, no new infra. 5 new tests (442/442 total)
Phase 8 — Full self-hosting (complete 2026-09-02, RFC-0003; technical archive = its 24 revision records):
- ✅ 8.1 frontend self-hosting (v0.23.0) — examples/selfhost/self_interp.lom: complete lexer + Pratt parser + AST dump written in Lom; acceptance vs host on 146+ files (dump/tokens/diags verbatim). Found and fixed a real host bug en route:
?/returnearly-exit was swallowed in tail-if-expression and match Form B arm blocks (+2 regression tests) - ✅ 8.2 static-check self-hosting (v0.24.0) — NAM003 / TYPE003-arity / EFF001 / MAT001 in Lom (
--checkmode); 42-builtin signature table exported from a host test as the sync sentinel - ✅ 8.3 evaluator self-hosting (v0.25.0) — tree-walking evaluator in Lom (11-variant value system, env chains,
?channel); three-layer self-certification: the self-hosted interpreter runsstmt_interp.lomand matches the 39-line golden verbatim (host → selfhost → selfhost → program); eval 113/113 stdout-aligned - ◐ 8.4 WASM-carrier self-certification (v0.26.0, partial — honestly recorded) — self_interp compiles to WASM (220KB) and is semantically correct on small files, but an unlocated nondeterministic OOB (>~6.7KB target programs) blocks the wasm-carrier three-layer golden; six classes of decisive experiments excluded (RFC-0003 revision 21) — deep-dive deferred to the post-1.0 ledger. CI selfhost gate covers the five stable modes instead. (Resolved in v1.1.1, 2026-09-07: the OOB was root-caused as heap-layout-sensitive eager payload loads and fixed in three codegen spots; 8.4 was re-judged complete and the wasm layer rejoined CI — see RFC-0003 revisions 25-29 and the W workpackage in docs/TODO.md)
- ✅ json dual-builtin adjudication (v0.27.0) — user decision landed:
char_from_codeadded (builtins 42→43;type_ofrejected), unblocking the self-hosted interpreter'sjson_parse/json_stringify(Part H, ~340 lines: char-wise recursive-descent parse incl.\uXXXXsurrogate pairs + an 11-variant stringify mirror) and removing the L2 self-hosted compiler's byte-construction blocker.argsargv passthrough so CLI programs (todo/bench) run under the self-hosted interpreter; verify tool gains a--runmode (31/31 incl. the three-layer golden) and joins the CI gate; eval task 115 added
v1.0 — language-surface freeze (2026-09-02, v1.0.0):
- ✅ Freeze declaration (LANGUAGE_SPEC §14): syntax / 20 reserved words / diagnostic codes (warning-only additions allowed) / 43 builtins; any change requires a new RFC. Adjudications recorded: north star "third-party production use" is not a v1.0 gate (long-term north star; v1.0 certifies engineering completeness, not market validation)
- ✅ Freeze-cut fix: the host lexer now decodes non-ASCII string literals (and generalized
\escapes) as complete UTF-8 characters — the per-byte Latin-1 expansion had corrupted Chinese literals since Phase 3 (ASCII-only programs byte-identical); the tolerant lexer reports one error per character for non-ASCII identifier positions. 454/454 tests, eval 114/114 dual backend, self-host six-mode acceptance green with Latin-1 fold counts now zero
See docs/lom-project-guide.html for the full project guide (positioning, design philosophy, 8-phase roadmap + v1.0 freeze, target LLM strategy, risk mitigation, repo governance).
Lom is a progressively fused AI-native programming language:
- Phase 0-3 — LLM-coding-native: tolerant parser, structured JSON diagnostics, linear pipeline syntax, structural types, gradual typing, explicit effect system. Target: LLMs write Lom with low error rate and easy recovery. (Done — original 99/100 single-model baseline 2026-08-03 REPORT.md; multi-model validation followed 2026-08-31: 99.1-100% across 4 models on the full 113-task set multimodel report; single-run sampling closed by the 2026-09-07 pass@k retest — 2 models × 10 samples @ temperature=1.0, pass@1/5/10 = 99.1% each (pass@k report); the cross-language control group remains open, stated as such)
- Phase 4 — LLM-repair-native + toolchain (direction adjusted 2026-08-07): expand
lom fixin-place auto-repair (differentiation — MoonBit doesn't do in-place repair), add REPL / LSP / package manager. Original "workload-native" (tensor/autodiff/MLIR) dropped — Mojo (acquired by Qualcomm) saturates the AI-compute lane. - Phase 5-7 — ecosystem & backends (done 2026-08): bootstrap self-hosting feasibility (a 1450-line mini-interpreter written in Lom), production engineering (semver, 3-platform CI,
lom doc/fmt), and a hand-written zero-dependency WASM compiler backend (lom build --target wasm) with byte-identical dual-backend output. Since 2026-08-25: fix-engine deepening (M1-M4 — spelling-repairreplaceactions, iterative--applyloop, targeted PARSE001 fixes, expanded error_repair eval).
The "AI-native language" space (2026-08 retrospective):
| Direction | Leaders | Status |
|---|---|---|
| Workload-native (tensor / autodiff / GPU) | Mojo (acquired by Qualcomm $3.9B), Bend | Saturated. Mojo binds to Qualcomm hardware strategy. Not Lom's lane. |
| LLM-coding-native (write-it-right) | MoonBit (IEEE TSE 2026 paper: 1/7 corpus but 2x AI gen rate vs Gleam; ~400k users; 1.0 imminent Sep 2026) | MoonBit leads. Lom's 99% eval validates the same thesis. |
| LLM-repair-native (fix-it-fast) — Lom's lane | Lom (lom fix --apply iterative in-place repair, tolerant parse, gradual typing) |
Empty. MoonBit regenerates on error; Lom patches in-place. Complementary, not competitive. |
Lom takes the differentiated route: occupy the empty LLM-repair-native lane — in-place auto-repair (lom fix --apply), tolerant parsing, confidence-graded fixes — complementary to MoonBit's write-it-right approach.
- Locality > Globality — declarations and usage must close within the same view.
- Tolerance > Strictness — syntax errors non-fatal, type errors degradable.
- Linear > Nested — pipeline, early return, guard clauses over deep nesting.
- Self-describing > Implicit convention — intent in code (executable docstrings, schema-as-type).
| Phase | Name | Duration | Exit criteria |
|---|---|---|---|
| 0 | Language Design & Spec | 1-2 w | LLM reads spec, generates valid code >80% |
| 1 | Minimal Interpreter | 4-6 w | 10+ test cases pass with LLM-generated code |
| 2 | LLM-coding-native Core | 6-10 w | LLM generation pass-rate eval set meets baseline |
| 3 | Usable MVP | 8-12 w | Write a complete CLI tool or simple web service |
| 4 | LLM-repair-native + toolchain (adjusted 2026-08) | — | lom fix covers main diagnostic auto-repair + confidence model + REPL + LSP + package manager |
| 5 | Ecosystem & Bootstrapping | done 2026-08-19 | Bootstrap feasibility proven (mini-interpreter in Lom; full self-hosting = RFC-0003 draft, parked) |
| 6 | Production engineering | done 2026-08-22 | Semver, 3-platform CI, governance, lom doc/fmt, SECURITY.md |
| 7 | WASM compiler backend | done 2026-08-23 (v0.15.0) | lom build --target wasm; dual-backend byte-identical golden + eval 113/113×2(时点值,2026-08-23;现值见 eval/README) |
| 8 | Full self-hosting | done 2026-09-02 (v0.23-v0.26) | Frontend + static checks + evaluator in Lom (self_interp.lom); interpreter-carrier three-layer self-certification; wasm-carrier golden blocked by an unresolved OOB bug (RFC-0003 rev 21, post-1.0 ledger) |
| v1.0 | Language-surface freeze | 2026-09-02 (v1.0.0) | Frozen: syntax / 20 reserved words / diagnostic codes / 43 builtins (spec §14). Post-1.0 ledger: wasm OOB deep-dive, L2 self-hosted compiler (blocker resolved), Pattern spans, package registry, debugger. Long-term north star: third-party production use — explicitly not a v1.0 gate |
See the project guide for details.
| Priority | LLM | Phase | Adaptation focus |
|---|---|---|---|
| P0 | DeepSeek | Phase 0 | SPEC_FOR_AI readability, error JSON field response, error pattern sampling |
| P1 | Claude / GPT | Phase 2 | Cross-model consistency baseline |
| P2 | Kimi / GLM / Gemini | Phase 3+ | Coverage expansion |
- Host language: Rust
- Phase 1+: hand-written lexer / recursive-descent parser / tree-walking interpreter / WASM emitter — zero third-party dependencies (CI-enforced). The crate is a binary, not a library — a deliberate keep-it-readable decision.
- Phase 4: reuse existing interpreter/typechecker/diagnostics — no new low-level deps (no Cranelift/MLIR). Adds
lom fixrule expansion, REPL, LSP server, package manager.
Apache-2.0. See LICENSE.
- GitHub: lom-lang/lom
- Issues: https://github.com/lom-lang/lom/issues