Skip to content
lom-langPublic

About

Lom (Language of Machine) — A programming language designed for the LLM repair loop — structured diagnostics, mechanical lom fix, self-hosting strong quine, zero-dependency Rust core.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Lom

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.

CI deps unsafe license

Why Lom exists

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.

The evidence

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

Feel the repair loop in 60 seconds

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.

Quick start

cargo build --release            # builds target/release/lom (zero dependencies)
./target/release/lom examples/fib.lom    # run a program

A 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。

Status & boundaries (honest version)

  • 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 via lom l2fix / tools/l2fix.py; rejections whose message carries a quoted name also carry a ln:cl source 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 --apply with a line diff, entirely in your browser (the full host toolchain compiled to wasm32-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.

Release log

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) — the check --json envelope now carries a nested position object (line/col/anchor) alongside the retained top-level pair (append-only dual write per ACRI's forward-compatibility axiom; anchor is "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 subcommand lom 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, --json emits an lom-explain/v1 envelope; 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 run 39646107026 seven 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 a call event (name, ln:cl site, frame depth, arguments) and each exit a return or call-error event — 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 two Option checks 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 with truncated:true), --trace-builtins (default off — builtin calls enter the trace only on demand). Value serialization is to_display semantics plus trace-only truncation (120 chars / nesting depth 3, overflow becomes shape annotations like [List len=20000 …]); the to_display core is untouched and println output is byte-identical. Applicability boundary: the interpreter run mode only — AOT lom build artifacts 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; zero src/, zero .lom, zero language-surface change — pure playground/ + docs + version pairing; repository tag at e171488, CI run 39431280200 seven 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 module playground/sharelink.js — base64url hand-rolled over TextEncoder bytes because btoa throws on non-Latin-1 text (zero third-party JS; the module is imported by both the browser app.js and node smoke.mjs, one source of truth). history.replaceState keeps 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.mjs gains 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; zero src/, zero .lom, zero language-surface change; repository tag at bdfd187, CI run 38893522801 seven jobs green; not externally published) — the playground engine's two real file-builtin defects are fixed at the WASI-harness layer (playground/harness.js only; desktop behavior byte-identical): file_exists("playground.lom") now returns true — it had silently returned false (the harness lacked path_filestat_get, so the ENOSYS error folded through Path::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) — and file_append on the mapped file now appends at tail — it had overwritten the file head (the path_open fdflags APPEND bit was dropped and every write landed at offset 0). The fix adds a path_filestat_get stub succeeding only for the mapped name (everything else ENOENT — keeping the /lom.toml discovery 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.mjs gains 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; zero src/, zero .lom, zero language-surface change; repository tag at 4a64471, CI run 38688935472 seven 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 as selfcomp.wasm, itself produced by compiling self_comp.lom with the host) compiles self_comp.lom; the emitted hex is decoded by a browser-side hex2wasm; the resulting wasm compiles self_comp.lom again; 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 mirroring run_selfcomp.mjs item-for-item), playground/selfboot-worker.js, tools/build_selfboot.py (builds the three distribution artifacts with a COMPILED 272925 hard gate); smoke.mjs grows 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 at e83177b, CI run 38576018355 seven 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 --json block and the verbatim runtime stderr (ordering after the 诊断 JSON block is a hard constraint, probe-verified against eval_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_check 32/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 at 838b5f0, CI run 38507453120 seven 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-while hint 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 at 664be97, CI run 38463443495 seven jobs green; not externally published) — the fix dispatcher table now covers all 38 host codes: the seven PKG001-PKG007 entries (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; PKG007 is 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 at aeb38da, CI run 38379975886 seven 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.rs gains 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 at b3bb9f5, CI run 38168342829 seven jobs green; not externally published)) — three deliverables (Rust 578 unit = 573+5, integration 40 = 37+3 in a new tests/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 codegen ln:cl prefix is lifted into a structured position field in both implementations (rust lom l2fix and the python CI oracle) — python↔rust parity 30/30 zero field differences, --self-check 6/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 in specs/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 at 973d020, CI run 38049184858 seven 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/Tuple field/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×; --bootstrap wall 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 in Value::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 at 4c555ad, CI run 38045249710 seven 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 in src/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×), --bootstrap wall 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.lom grows six loads (arith_loop / call_density / closure_loop / list_get_nth / map_hit_far / str_concat; the four legacy loads untouched) and tools/bench.py joins the repo (10-load matrix, quick/full tiers, Python-side expected-value self-check, --baseline compare; not in CI, per adjudication). Zero language-surface change (pure implementation optimization). Previous repository milestone: v1.9.3 (2026-10-10; repository tag at 22a7008, CI run 37951486401 seven 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 the check_and_report_file doc 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 at ec2ebf0, CI run 37895385249 seven 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-manifest lom build now checks each root-directory .lom file 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_file subtracts 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 at 7c2403c, CI run 37767137349 seven 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 against externals_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_clashes is now called on the human-readable path (stderr, JSON path untouched); ③ main-file diagnostics — the root directory's .lom files (conventionally main.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 at 9751011, CI run 37680423135 seven 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 as lom 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 to interpreter::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 at 88d91dc, CI run 37675620476 seven 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: zero src/, 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 at ca31691, CI run 37652195871 seven jobs green; not externally published) — L2 diagnostic line numbers (designs/0020, user-adjudicated on all four points; zero src/ changes, frozen language surface untouched): the L2 subset compiler's codegen error channel now carries a ln:cl position 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 at 7739959, CI run 37526137995 seven jobs green; not externally published) — closes the twenty-seventh review's two openings R117/R118 (playground edge hardening; zero src/ 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 include memory access out of bounds and unreachable, not only stack exhaustion). R118 — harness.js run() now falls back to the default argv on an empty args array (previously [] passed straight through and the host trapped reading argv[1]; an A/B probe confirms the pre-fix RuntimeError: unreachable is gone, and the depth-probe path still traps — never swallowed); the args contract is documented in the harness header. node playground/smoke.mjs ALL PASS (11 items). Baseline unchanged by construction (zero src/ 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 at 7685d64, CI run 37348863077 #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 to wasm32-wasip1 (a new [profile.wasm-release], ~1.7 MB, still zero third-party crates and zero unsafe in src/ — the wasm-side unsafe lives only in the Rust toolchain's own std/WASI shims, exactly as desktop builds have always relied on std internals) 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 apply lom fix --apply with a line diff, entirely client-side. Two cfg(target_family = "wasm")-gated spots in src/ (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; the file module is a virtual single file (v1.12.1 precision note: file_exists now answers true for the mapped file and file_append appends at tail — harness path_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 at e6b807b, CI run 37125504909 #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"; host src/ untouched): R115 — the experimental L2 subset compiler now rejects same-file duplicate user fn definitions 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_l2 gains 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 negative neg_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 at 6227a89, CI run 36878845005 #249 six jobs green; not externally published) — L2 diagnostic structuring prerequisite (designs/0017, RFC-0004 revision 52; user-adjudicated on all three points; host src/ untouched): the L2 subset compiler's codegen error channel 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's classify_runtime_error; all 263 subset_err call 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 L2 lex error channel is fixed on the spot (its error records lacked the code field the reporter reads — previously a RUNTIME000 crash; now lex 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 at 142a118, CI run 36859203394 #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 outer mut binding from inside a closure body used to panic the interpreter (RefCell already borrowed, rc=1, guarded). The named-closure call path held its env.borrow() Ref across the whole closure-body execution, so the body's assignment re-entered borrow_mut on 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 (new tests/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 at b61182f, CI run 36841096349 #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" for NAM006 (collision notice — behavior already deterministic, rename only to remove ambiguity) and MUT002 (closure captured a mut binding — 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 --json and the real L2 codegen error text, 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 outer mut from inside a closure body hits RefCell 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 at cf73de2, CI run 36829534116 #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-level let (v1.4.14) → block-level destructuring let (x, y) (this batch). The v1.4.14 snapshot matched only Stmt::Let, so a block-level destructuring (whose bindings are always immutable) shadowing an outer let mut x drew 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 (outer let 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 --check sweep over all 4,300 .lom files 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 at e8b099e, CI run 36770326768 #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-level let inside if/while/for used 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 after let mut x + block let x + post-block x = 5, and a missed one after let x + block let mut x) while all three runtimes agree on block scoping. check_block now snapshots each first-seen block-level let name'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 --check sweep over all 4,300 .lom files 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 at 7a48810, CI run 36753460493 #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 = 5 no 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-less lom build per-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 at 11cdc68, CI run 36723102998 #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. New tests/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 at 13a4cf4, CI run 36714193624 #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 --check with 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. New tests/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-scoped let shadowing 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 at ce8c731, CI run 36694182024 #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 a HashMap, 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 local fn that 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) — and PKG007 (warning) names both packages and the path-order winner (run/--check/build). Package fn auto-publicity (real names callable without import) is now documented in §8.1 as the registered design it always was. New tests/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 at e71749e, CI run 36677072901 #216 six jobs green; not externally published) — R94 ledger batch 2 (designs/0013, RFC-0004 revision 42), closing the host ledger family: map_remove unified to Bool per SPEC §9.7's frozen claim (the interpreter/typechecker already returned Bool; the host-WASM helper dropped the hit information into V_UNIT and v1.2.13's adjudication A had aligned the then-experimental L2 to that Unit side — the helper now tags the probe result like map_has, and the L2 verdicts flip to Bool, superseding that adjudication in favor of the frozen spec text; all three implementations now print true/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-package as aliases 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), and lom build <file> --target wasm typechecks 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-driven lom build flow's per-file view still reports NAM003 inside the package source. Baseline: verify_selfcomp 362/362 (196 + 5 + 161; neg_map_remove_unit_bind deleted, neg_println_void rewritten over map_set, new positive 201), bootstrap 14/14 with the quine at 269521 bytes, self_comp 12529 lines, Rust 543 unit + 10 integration (new tests/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 at 8c86a60, CI run 36668306358 #214 six jobs green; not externally published) — R94 ledger batch 1 (designs/0013, RFC-0004 revision 41), zero src/ 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 Rust str Ord and the wasm-inlined rt_str_cmp behind map_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 at 22a37a6 after six-job CI run 36612026578 #210 passed; not externally published) — R101 host fix: the WASM codegen's and/or short-circuit now pushes Label::If (W-2 precedent), so a return actually 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 at 96c26b6 after six-job CI run 36608219947 #207 passed; not externally published) — E甲 expansion + R100: the Bool display prescan gains the three blind-spot fixes (a bbind knowledge table layered like D甲's cbind so the R85 param-direct boundary does not flip; a shared match_bool_disp entry 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 at df7f83b after six-job CI run 36594643927 #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_local sentinel; a planner load_cont_expr refinement keeps pure-scalar-payload enums unmarked). No-String-literal match Some(xs) => println(xs) now compiles via direct-chain, parameter-annotation, and helper-return flows; cross-function direct container println without 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 at 1851964 after six-job CI run 36578254262 #202 passed; not externally published) — R96 C甲: closures call prelude println/print without io import (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/print and concat promotion now display all six container kinds with byte-identical host/L2 output — Lists [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, enums Node(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); a scan_has_display prescan + 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 replacing string_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 --bootstrap tier (14/14, with a --ci-smoke sub-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 for args), math compiles inline, and io imports pass through as no-ops. The seventeenth review's R90 is closed in the same release — pkg-expand keeps package-source as-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 both if and match. On top of v1.3.1's statement-surface finale: the return batch (designs/0009, user-approved; RFC-0004 revision 26) compiles return statements and ? propagation via the wasm return instruction — 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-position if with 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; --list prints 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, as aliases 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_stringify run through two harness imports (JS semantics = the L2 comparison baseline; the module gains them plus an alloc export only under a has_json prescan) over an opaque vt js JSON-node tree (objects keep insertion order as kv sequences — not Map, avoiding key-reordering divergence; arrays materialize as ls{js} cons chains); data.field compiles to a key-matching helper that traps on miss like the host, js values flow through list builtins as ls{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 import with 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 with vt mp{V} (keys always String), map_get returning Option consumed by existing match patterns, key-sorted map_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_remove returns Unit per adjudication A (a documented host interpreter-vs-WASM divergence stays host-side); from map import with 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 bare mp{?} 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/v1 reports final diagnostics and ok = applied>0 and zero final errors); R56 panic maps to exit 1 and i64::MIN / -1 / % -1 are structured RUNTIME000; R57 a missing final end is 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); R59 json_parse rejects 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: new MUT002 warning (closure capturing a mut binding — interpreter/WASM capture semantics differ), NAM003 false-positive fix (self-referential let closures), inf/NaN display 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 --apply reports a final diagnostics block and ok reflects the post-patch state — reviewing --dry-run diffs for logic-bearing edits is still recommended. See TODO and the copy-ready handoff prompt.

Status (milestone history & live numbers)

✅ Phase 1 — Minimal Interpreter (completed; 历史里程碑快照,非现状——现状见上文 Status & boundaries 段与 eval/README)

  • 13/13 .lom examples 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/v1 schema, --json / --check / --help CLI, LEX/PARSE/RUNTIME error codes)
  • ✅ 2.4 gradual type checker (two-pass: signature collection + body check; TYPE/MAT/NAM error codes; --check runs type check, --json emits structured type diagnostics; progressive: type errors are warnings, dynamic run still works)
  • ✅ 2.5 explicit effect system (! [IO, Clock] annotation; EFF001 warning when pure functions call effectful functions; main implicitly has all effects; closures inherit enclosing effects; stdlib println/print declare [IO])
  • ✅ 2.6 lom info --json type info export (lom-info/v1 schema — functions/enums/imports; info subcommand with --json; no type-check, no run; parse failure falls back to lom-diag/v1)
  • ✅ 2.7 lom fix --plan --json AI repair plan (lom-fix/v1 schema — per-diagnostic plans with insert/delete/hint actions, text snippets for EFF001/MAT001, retry flag, confidence levels; covers 20+ error codes across LEX/PARSE/TYPE/MAT/NAM/EFF/RUNTIME; fix subcommand 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 --verify reference-solution smoke test and --candidates-dir LLM 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 --apply repair execution (lom-apply/v1 schema — applies confidence=High + action≠Hint fixes to source; --dry-run preview, --json structured 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 , E inserted before ])
  • ✅ 3.2 AST span-based diagnostic positioning (Span type on FnDecl/EnumDecl; parser records fn/enum keyword position + signature end via prev_token_pos; typechecker uses current_fn_span for EFF001/TYPE010/NAM002 instead of (0,0); removes Phase 3.1 find_fn_line source-scanning hack; end-to-end verified — effects_bad.lom EFF001 now points to 10:1/21:1 (as of 2026-08-22))
  • ✅ 3.3 Standard library expansion — list + json modules (Value::List variant with immutable semantics; list module: list_empty/list_length/list_get/list_is_empty/list_head/list_tail/list_cons; json module: hand-written zero-dependency json_parse/json_stringify with 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 — file module + string extensions (file module: file_read/file_write/file_append/file_exists with [IO] effect annotation — file system read/write/append/existence; string extensions: split/contains/replace/starts_with/ends_with — split returns List<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 + env module — Phase 3 exit criterion met: env module (args() returns List<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_undefined emits 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_line records end keyword line; typechecker fills end_line into MAT001 diagnostic; fix_mat_non_exhaustive upgraded to graded fix — builtin variants Ok(_)/Err(_)/Some(_)/None get High-confidence precise Insert before end; 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.jsonl NDJSON format (one JSON object per line); lom fix --apply auto-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/v1 schema); zero-dependency ISO 8601 UTC timestamp via epoch calc; AppliedChange now carries diagnostic_code for 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 repl subcommand; is_input_complete() multiline completeness check (paren/brace/bracket depth + string state + block keyword/end pairing; enum is single-line decl, fn/if/while/for/match need end); ReplSession with context-preserving incremental execution (exec_item() registers fn/enum/import, exec_repl_block() runs stmt/expr in globals scope so let binds 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-Length is capped at 16 MiB (oversized headers exit 1 instead of an allocation abort) and malformed payloads get -32700/-32600 error 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 build subcommand + lom.toml manifest (name/version/[dependencies]); local path dependencies (Dependency::Path); resolve_dependencies() DFS recursive resolution with cycle detection (CircularDep); collect_public_symbols() auto-exposes top-level fn/enum; process_import() resolves external packages (PKG005 unknown package / PKG006 symbol not exported); runtime load_packages() registers package fn/enum variants in interpreter; 16 unit tests (manifest parse, cycle detection, symbol collection, path resolution); example: examples/pkg_demo/ — mathlib local dep with square/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'])"  # 46

600/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 let tuple destructuring — let (a, b, ...) = expr binds tuple elements to names (src/ast.rs Stmt::LetDestruct; src/parser.rs parse_let_destruct with trailing-comma tolerance & mut rejection; src/interpreter.rs runtime tuple/count checks; src/typechecker/ per-element type binding or Unknown fallback). 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 via List<(String, Int)> + cons shadowing; built on 5.1 tuple destructuring
  • ✅ 5.3 for over List<T> (v0.4.1 P0 gap fix) — for x in xs now iterates Lists (previously String/Int only, RUNTIME000); interpreter gains a Value::List iteration branch (element binding, return propagates), 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 via to_display(): "n = " + 42 works (previously RUNTIME error, required int_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 to x = x op e with 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 to List<Int>, reusing 5.3's for-in-List and the whole list module with zero new runtime machinery (list_length(1..4) works); lexer gains DotDot (distinct from Float 3.14 and tuple index t.0), parser adds lowest-precedence parse_range (below or, non-associative, newline-guarded), typechecker records List<Int> and warns TYPE001 on known non-Int ends; resolves LANGUAGE_SPEC open question #1 (Rust-style a..b chosen, a..=b rejected — 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 = double wraps a named function as a closure value (env = globals, identical to call_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 for list_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_builtin widened from &self to &mut self so builtins can call back into closures; f accepts closure literals or named functions (5.8 wrapped values); composes naturally with range (list_map(double, 1..6)) and concat promotion (list_fold string 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 missing list module 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_stmts rewritten from hand recursion to list_fold + closure — real dogfooding of 5.8/5.9 inside self-host code; new test program 3 (while countdown sum = 6), outputs 11 / 10 / 6 all correct; effect annotation chain completed (! [IO]) and TYPE010 eliminated — --check now reports 0 diagnostics (was 2 historical warnings)
  • ✅ 5.11 bootstrap functions — source language gains fn name(p1, p2) ... end definitions and calls with lexical scoping (fresh param env per call); Decl layer (DFn/DStmt) with collect-fns-first execution so forward references and mutual recursion work (program 5: collatz_len calls later-defined is_odd, 8 recursion steps correct). Key refactor: statement-value semantics — naive "last SExpr is the return value" broke at if (values can't escape branches; in-function SExpr mis-prints), rebuilt as exec_stmt → (env, value) where SIf's value is the taken block's value (if-as-expression), block value = tail statement value, in_fn flag 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). Outputs 11/10/6/49/8 all correct, --check 0 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, lowercase true/false Bool printing; Add mixing mirrors Lom v0.4.1 (either-side-String promotes via show()); conditions unified under truthy(). 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), --check 0 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, --check 0 diagnostics
  • ✅ 5.14 bootstrap VList recursive values — Val gains VList(List<Val>) (first recursive value type in the self-hosted runtime); source language gains list literals [1, 2], chainable indexing xs[i], and a len() builtin (works on List and String — first builtin mechanism in the mini-language); literal element evaluation reuses eval_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, --check 0 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 yield TUnknown tokens instead of silent drops; parser is now total (peek_tok Option guards replace all bare list_head — empty/truncated/unknown input never crashes). Program 10: statements before the error still run (7 printed), then parse error: unexpected unknown character '@'; truncated 1 + and empty input are safe. Debug log: missing end in a Form B arm containing an inner match (parse_call_args — the §4.1 trap again), an over-correction adding end to a Form A arm (Form A needs none — the A/B asymmetry was the root confusion), and two latent list_cons arg-order bugs caught by the typechecker's TYPE003. 10 programs / 21 outputs verified, --check 0 diagnostics
  • ✅ 5.16 bootstrap intrinsics — the 5.14 len special 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 (reuses show), 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, --check 0 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 gain at 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-end trap fired a third time (its "期望表达式,得到 FatArrow" signature is now a known fingerprint). 31 outputs verified verbatim, --check 0 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_tail copies elems[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 the Vec-backed Value::List representation (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-access list_get becomes 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>>>) + map stdlib 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), let aliases share — deliberately unlike List's immutable persistence; copy-on-write was rejected because the args slice always holds an Rc so Rc::get_mut never 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_lookup collapses from 10-line recursion to a 4-line map_get match; bind_params becomes a while + map_set loop; exec_stmt/exec_stmts no longer return env (mutation replaces threading) — signatures drop from Result<(Env, Val), String> to Result<Val, String>. Lexical scoping preserved: callee gets a fresh map_empty(). All 31 outputs verbatim identical, --check 0 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_program collects undefined-variable / undefined-function / arity diagnostics as List<String> (mini NAM003) and run_program refuses 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 = x correctly errors). Call checking is split into check_call/check_call_fn helpers 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, --check 0 diagnostics
  • ✅ 5.23 bootstrap wrap-up: fns table → Map + operator completion — the function table goes from List<Decl> linear scan to Map<Decl> (collect_fns builds it directly, find_fn is a 4-line map_get match); the mini-language gains < <= >= != (lexer lookahead for >/>=, </<=, !=/bare-! — same explicit-token pattern as =/==; evaluation unified in an eval_cmp_int skeleton with byte-identical error messages; != negates values_eq for all value types incl. lists). New program 14 (5 outputs: true/true/false/true/true). 39 outputs verified verbatim, --check 0 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 Char type, 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 tag v0.5.1, new lom --version flag (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/v1 JSON 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-line enum X = A | B special-cased, paren-continuation lines untouched; --apply in-place / --check CI 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 file skipped the checker entirely so let 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 via stmt_interp.expected.txt, lom fmt --check over all examples, zero-dependency enforcement making SECURITY.md's promise real); doc-rot sweep (HANDOVER/eval-README/guide anchors/SPEC's pub marking/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 mut with block scoping, if/elif/else (statement + expression), while, for-over-Int, early return, arithmetic with Int→Float promotion, comparisons, and/or short-circuit, string literals + string ==; runtime tag dispatch in hand-written WASM helper functions; CLI lom 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_indirect dispatch 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..b ranges, for-over-List, split, list_map/filter/fold (closure callbacks via call_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_stringify host imports (harness implements with JS JSON + value layout read/write; lom_alloc + lom_variant_table exports 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 build merges 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 wasm mode (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 replace actions (v0.16.0) — NAM003/NAM004 diagnostics with a did-you-mean suggestion now emit concrete replace fix 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 by lom fix --apply (explicit safety policy: only 100%-certain fixes auto-apply). The schema's replace action, reserved since Phase 2.7, is now produced by real rules. 11 new tests (406/406 total)
  • ✅ M2 iterative --apply repair loop (v0.17.0) — lom fix --apply upgrades 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) and rounds + per-change round fields (JSON, schema stays lom-apply/v1); fix history records each applied round as its own entry (old entries without round read as round 1 — backward compatible). Single-round to_json/to_human in 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 end for fn/if/while is 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 + --apply golden 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 real lom --json output, and every reference solution is executed to verify its expected output. New eval/fix_corpus/ — (bad, fixed) source pairs driven end-to-end through the iterative --apply loop 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 = 4 used 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 a MUT001 warning for assignment to immutable bindings: plain let, function parameters, for loop variables, match pattern bindings, and compound +=/-= targets (they desugar to =). Warning-level per the gradual-typing philosophy — stderr only, never blocks execution, the interpreter stays permissive; let mut and same-name let shadowing stay silent. Message/hint deliberately avoid claiming "let" for parameter targets (Lom has no mut params). 8 new tests (426/426 total)
  • ✅ Phase 3.2b expression-level spans (v0.21.0, 2026-08-31) — the last positioning debt is paid. Expr becomes struct { kind: ExprKind, span: Span } (the enum is now ExprKind); Stmt::Let/Stmt::Assign gain spans; the parser fills every node (span_since/span_from helpers, 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 (the Field span's end), MUT001 at the assignment target, TYPE001/002/003/020 at the expression. lom fix NAM003/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 — Pattern has no span). Caveat encoded in fix.rs: lexer columns are 1-based byte columns, fix actions are char columns — precise_occurrence converts 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 over Program, 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: ?/return early-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 (--check mode); 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 runs stmt_interp.lom and 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_code added (builtins 42→43; type_of rejected), unblocking the self-hosted interpreter's json_parse/json_stringify (Part H, ~340 lines: char-wise recursive-descent parse incl. \uXXXX surrogate pairs + an 11-variant stringify mirror) and removing the L2 self-hosted compiler's byte-construction blocker. args argv passthrough so CLI programs (todo/bench) run under the self-hosted interpreter; verify tool gains a --run mode (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).

What is Lom

Lom is a progressively fused AI-native programming language:

  1. 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)
  2. Phase 4 — LLM-repair-native + toolchain (direction adjusted 2026-08-07): expand lom fix in-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.
  3. 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-repair replace actions, iterative --apply loop, targeted PARSE001 fixes, expanded error_repair eval).

Why Lom

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.

Design Philosophy

  1. Locality > Globality — declarations and usage must close within the same view.
  2. Tolerance > Strictness — syntax errors non-fatal, type errors degradable.
  3. Linear > Nested — pipeline, early return, guard clauses over deep nesting.
  4. Self-describing > Implicit convention — intent in code (executable docstrings, schema-as-type).

Roadmap (summary)

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.

Target LLM Priority

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

Tech Stack

  • 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 fix rule expansion, REPL, LSP server, package manager.

License

Apache-2.0. See LICENSE.

Contact

About

Lom (Language of Machine) — A programming language designed for the LLM repair loop — structured diagnostics, mechanical lom fix, self-hosting strong quine, zero-dependency Rust core.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages