Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 25 additions & 12 deletions .agents/plans/A13-release-rc1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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).
Expand Down
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
44 changes: 35 additions & 9 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
Expand All @@ -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

Expand Down
59 changes: 59 additions & 0 deletions benchmarks/BASELINE.md
Original file line number Diff line number Diff line change
@@ -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/<workload>.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.
Loading