diff --git a/.agents/plans/A13-release-rc1.md b/.agents/plans/A13-release-rc1.md index 570cc0b..bae20f6 100644 --- a/.agents/plans/A13-release-rc1.md +++ b/.agents/plans/A13-release-rc1.md @@ -24,16 +24,28 @@ final. - New features (must be in by now or wait for 1.1). - Documentation rewrites beyond what A30/A31/A32 ship. -## Blocked on - -- A19 — error line/source in stdlib raises. -- A20-A24 — suite triage clusters and their sub-plans, enough to hit - the suite gate. -- A25 — `string.pack`. -- A26 — error message quality pass. -- A27-A32 — DX/docs track. -- A33-A35 — perf parity locked in. -- All milestone issues closed. +## Blocked on (as of 2026-06-15, rc.3) + +Most of the original blocker list shipped across rc.1–rc.3 (A19, A25, +A26, the A27–A32 DX/docs track, and the A33–A35 perf work via #324/#360). +What actually remains for the 1.0.0 cut: + +- **Suite gate** — three triage targets to reach the 20/29 aim: + `strings.lua`, `sort.lua`, `math.lua` (all whole-file-skipped today). + If any proves a wording/perf rabbit hole, document it as a known-limit + exclusion and ship at the resulting floor (≥18). +- **Perf gate** — a one-time `--compare-baseline` check. fib is already + 1.03–1.11× Luerl after #324/#360, comfortably inside the ≤25% bar; this + is a confirmation run, not new perf work (#267). +- **Milestone hygiene** — the 1.0.0 milestone is now lean. #77/#89/#92/#87 + closed (delivered or moot post-Luerl); #297 (VFS) and #341 (Encoder) + moved to 1.1.0; perf-tooling #268/#269 moved off the release gate. See + ROADMAP "Release sequencing". + +Descoped from the 1.0 gate (post-1.0 DX niceties, not API-stability +concerns): `Lua.dbg/2`, and the `mix lua.bench` / `mix lua.suite` tasks. +Only `mix lua.eval` ships in 1.0; the suite/bench harnesses run via the +existing `mix test --only lua53` and the `benchmarks/` scripts. ## Success criteria @@ -71,8 +83,9 @@ final. cleanly with line/source; rendered output reviewed by Dave. - [ ] **Docs gate**: `mix docs --warnings-as-errors` exits 0; README and `examples/` link consistently; doctests pass. -- [ ] **DX gate**: `mix lua.eval`, `mix lua.bench`, `mix lua.suite` - all work end-to-end. `Lua.dbg/2` exists and is documented. +- [ ] **DX gate**: `mix lua.eval` works end-to-end. (`mix lua.bench`, + `mix lua.suite`, and `Lua.dbg/2` are descoped to post-1.0 — see + "Blocked on".) - [ ] `mix hex.build` succeeds. - [ ] Tag created: `git tag v1.0.0` (manual). - [ ] Pushed: `git push origin v1.0.0` (manual). diff --git a/CHANGELOG.md b/CHANGELOG.md index 65a3724..fdb2523 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -## [v1.0.0-rc.3] - 2026-06-15 +## [1.0.0-rc.3] - 2026-06-15 The fourth release candidate for `1.0.0`. It builds on rc.2 with a structured parse-error API for tooling, conformant `goto`/`label` on @@ -128,7 +128,7 @@ additive — nothing from rc.2 is broken. per-value pseudo-pointer. Table rendering (`table: 0x...`) is unchanged. `type(f)` still returns `"function"`. -## [v1.0.0-rc.2] - 2026-06-10 +## [1.0.0-rc.2] - 2026-06-10 The third release candidate for `1.0.0`. It builds on rc.1 with a major table-storage performance win, two non-standard `os` epoch helpers, and @@ -460,7 +460,9 @@ API is intended to be stable. Please report any regressions before final. - Upgrade to Luerl 1.4.1 - Tables must now be explicitly decoded when receiving as arguments `deflua` and other Elixir callbacks -[unreleased]: https://github.com/tv-labs/lua/compare/v1.0.0-rc.1...HEAD +[unreleased]: https://github.com/tv-labs/lua/compare/v1.0.0-rc.3...HEAD +[1.0.0-rc.3]: https://github.com/tv-labs/lua/compare/v1.0.0-rc.2...v1.0.0-rc.3 +[1.0.0-rc.2]: https://github.com/tv-labs/lua/compare/v1.0.0-rc.1...v1.0.0-rc.2 [1.0.0-rc.1]: https://github.com/tv-labs/lua/compare/v1.0.0-rc.0...v1.0.0-rc.1 [1.0.0-rc.0]: https://github.com/tv-labs/lua/compare/v0.4.0...v1.0.0-rc.0 [0.4.0]: https://github.com/tv-labs/lua/compare/v0.3.0...v0.4.0 diff --git a/ROADMAP.md b/ROADMAP.md index ab9929e..6e40d15 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2,11 +2,9 @@ This is the strategic overview. For per-PR detail, see [`.agents/plans/`](.agents/plans). -## Status: 2026-06-10 +## Status: 2026-06-15 -- **Version**: `1.0.0-rc.2` shipped (rc.0, rc.1, rc.2 all released). -- **Unit tests**: 2,230 passing (62 doctests, 51 properties, 2,117 - tests), 0 failing, 7 skipped. +- **Version**: `1.0.0-rc.3` shipped (rc.0–rc.3 all released). - **Lua 5.3 official suite**: 17/29 files passing. 8 fully clean (`api`, `bitwise`, `code`, `nextvar`, `simple_test`, `tpack`, `utf8`, `vararg`) plus 9 passing with documented skip-ranges (`all`, `calls`, @@ -15,11 +13,39 @@ This is the strategic overview. For per-PR detail, see [`.agents/plans/`](.agent whole-file-skipped pending triage (`big`, `closure`, `coroutine`, `db`, `errors`, `math`, `sort`, `strings`). - **Current focus**: closing the `1.0.0` release gates in - [`A13`](.agents/plans/A13-release-rc1.md) — suite gate (settled at - **20/29 with 9 documented exclusions**; 17 pass today, 3 triage - plans remain — `strings`/`sort`/`math`), perf gate - (A33–A35 baseline + parity, recursion regression #324), and the DX - gate (`Lua.dbg/2`, PR #219). + [`A13`](.agents/plans/A13-release-rc1.md) — the suite gate (aim 20/29 + with 9 documented exclusions; 17 pass today, three triage targets + remain: `strings`/`sort`/`math`) and a one-time perf-gate check + (fib already at 1.03–1.11× Luerl after #324/#360). + +## Release sequencing (decided 2026-06-15) + +The 1.0.0 milestone is deliberately **lean**: its sole job is to freeze +the public API around the completed Luerl → Elixir-native rewrite. We do +not add new public surface in the release that freezes the surface. + +- **1.0.0** — stabilize the rewrite. Suite + perf + docs gates only. The + rewrite already meets or beats Luerl where it counts (native VM, + structured errors, `parse_structured/1`, `:max_instructions`, O(1) + `#t`/`pairs`); the dimensions we trail (coroutines, full `io`, + gc/weak-tables) are documented deliberate non-goals, not gaps. Four + milestone issues were already satisfied by rc.3 and closed (#77, #89, + #92, and #87, the latter moot post-Luerl). +- **1.1.0** — additive DX + virtualization plumbing on the frozen API: + the `Lua.Encoder` protocol + `deflua` auto-marshalling (#341), and the + VFS plumbing that routes `os`/`require` IO through an in-memory virtual + filesystem (#297, PR #302) **with the deny-list unchanged**. All + additive → minor bump. +- **2.0.0** — full virtualization: flip the sandbox default so `os`/`io` + operate against the VFS and nothing reaches the host. This changes + observable behaviour (today's sandbox refusals become successes), so it + is an honest major bump, built on 1.1's plumbing as a config-flip rather + than a rewrite. + +Rationale: bundling a brand-new public protocol (`Lua.Encoder`) into the +API freeze would commit us to it before it is battle-tested; de-sandboxing +is a major-version event by nature, so doing it as 2.0 soon after 1.0 is +SemVer working as intended, not a delay. ## Done diff --git a/benchmarks/BASELINE.md b/benchmarks/BASELINE.md new file mode 100644 index 0000000..4e42726 --- /dev/null +++ b/benchmarks/BASELINE.md @@ -0,0 +1,59 @@ +# Performance baseline — Luerl gap (1.0.0) + +Recorded for the 1.0.0 perf gate (#267). Numbers are the **compiled +`chunk` path** (the production embedding path: compile once, run many) +vs Luerl 1.5 on the same machine, same run. + +**Measurement discipline:** benchmarks were run **serially, one `mix run` +at a time, with no other CPU load**. Running them concurrently with +tests/agents inflates deviation badly (table/oop cases swing ±80–260% +under load); the numbers below are the quiet-machine read. Use +`mix run benchmarks/.exs` (quick mode) one at a time, or +`LUA_BENCH_MODE=full` for publishable figures. + +## Environment + +- Apple M4 (arm64), Erlang/OTP 29.0, Elixir 1.20.0 +- Mode: `quick` (Benchee short windows), serial, 2026-06-15 +- Comparison baseline: Luerl `~> 1.5` + +## Results (chunk path vs Luerl, after the 1.0 perf pass) + +| Workload | chunk vs Luerl | Note | +|---|---|---| +| fibonacci | 1.08× slower | call-heavy worst case | +| table_ops — build (n=100) | ~1.01× (tie) | | +| table_ops — sort | ~1.05× slower | was 1.41×; one-pass `:array.from_list` write-back | +| table_ops — sum / map-reduce / pairs-hash | 0.77×–1.02× | several *faster* than Luerl | +| string_ops — concat | ~0.95× (faster) | | +| string_ops — format-in-loop | ~0.89× (faster) | was 1.37×; bignum float + template cache | +| string.format — long (literal-heavy) | ~0.21× (≈4.8× faster) | | +| string.format — width-flagged | ~0.81× (faster) | was 1.31×; template cache | +| string.format — many-specs | ~0.84× (faster) | was 1.34×; fast path + float + cache | +| closures | ~1.25× slower | at the bar; closure/upvalue machinery | +| oop | 1.07× slower | | + +## What changed in the 1.0 perf pass + +- **Bare-specifier fast path** in `string.format` — skips the spec parser + and the no-op sign/width passes for plain `%d`/`%s`/`%x`/… directives. +- **Exact bignum fixed-precision float formatter** — replaces + `:io_lib.format/2` for `%.Nf`; byte-identical to `:io_lib` over 300k + random value/precision pairs, far fewer operations. +- **Byte-based decimal placement** — avoids `String.pad_leading/3`'s + grapheme machinery on ASCII digit strings. +- **Parsed-template cache** — memoizes the compiled `string.format` + segment list in the threaded `%Lua{}` state (no ETS/process dict), so + a format string reused across calls is scanned once, not every call. + This flipped the format-dense loops from slower-than-Luerl to faster. +- **One-pass `table.sort` write-back** — `Table.replace_sequence/2` + rebuilds the array part with a single `:array.from_list/2` instead of N + path-copying `:array.set/3` calls. No representation change, so no + large-`n` regression (cf. the deferred B7 array+hash rewrite). + +## Gate verdict + +Every workload is within 25% of Luerl on the `chunk` path; string.format, +string_ops, and most table ops are *faster* than Luerl. `closures` sits at +the 1.25× line (closure-creation/upvalue capture) — a candidate for the +ongoing 1.1.x perf-parity work (#267–#269), not a 1.0 blocker.