From 1b796f53e1e62fd8c99572d219086a6da8551a9f Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Sun, 5 Jul 2026 06:09:29 -0700 Subject: [PATCH 1/3] chore(A49): start plan --- .agents/plans/A49-release-docs-sync.md | 61 ++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 .agents/plans/A49-release-docs-sync.md diff --git a/.agents/plans/A49-release-docs-sync.md b/.agents/plans/A49-release-docs-sync.md new file mode 100644 index 0000000..abff63e --- /dev/null +++ b/.agents/plans/A49-release-docs-sync.md @@ -0,0 +1,61 @@ +--- +id: A49 +title: "Docs sync for 1.0.0: roadmap, changelog, guides, README true-up" +issue: null +pr: null +branch: docs/release-docs-sync +base: main +status: in_progress +direction: A +--- + +## Goal + +Bring every user-facing doc and status artifact in line with reality +(20/29 official suite files passing, `rc.3` → `1.0.0` pending) so the +`1.0.0` cut is documentation-clean. + +## Out of scope + +- Any source behavior change, except the test-warning fix (item 8) and + the doctest wiring (item 9). +- API changes — a sibling PR handles those. Unreleased CHANGELOG entries + are kept in clearly-labeled subsections to minimize the merge conflict. +- The `1.0.0` version bump and release CHANGELOG section (release-cut PR). +- Blog post, website. + +## Success criteria + +- [ ] `ROADMAP.md` Status reflects 20/29, dated 2026-07-05; suite gate marked MET. +- [ ] `CHANGELOG.md` Unreleased documents #376 and #379; suite-exclusion + doc entry lists the 9 exclusions by category; heading/link-ref + mismatch fixed; "Upgrading from 0.x" migration section added. +- [ ] `guides/sandboxing.md` CPU section leads with `:max_instructions`. +- [ ] `README.md`: `utf8` in coverage list; host-filesystem claim qualified; + `import Lua` shown in the Tour. +- [ ] `lib/lua.ex` `load_file!` links the Lua 5.3 manual. +- [ ] `lib/lua/runtime_exception.ex` moduledoc uses `` for default source. +- [ ] `guides/mix_tasks.md` sample output + skips-file reference updated. +- [ ] `test/lua_test.exs` encode-nil test no longer emits a type warning. +- [ ] `doctest Lua.Parser.Error` wired into a test file and passing. +- [ ] `.agents/plans/B17-*.md` and `A33-*.md` statuses reconciled with reality. +- [ ] `mix format`, `mix compile --warnings-as-errors`, `mix test`, + `mix docs --warnings-as-errors` all clean. + +## Implementation notes + +Verify each item still needs fixing before touching it — PR #378 shifted +line numbers and fixed some issues already. + +## Verification + +``` +mix format +mix compile --warnings-as-errors +mix test +mix docs --warnings-as-errors +``` + +## Risks + +- Concurrent API-PR CHANGELOG conflict — kept small via labeled subsections. From 544e33e86b9ec459f8d1774a91160c86f89ba747 Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Sun, 5 Jul 2026 06:18:01 -0700 Subject: [PATCH 2/3] docs(release): sync roadmap, changelog, guides, and README for 1.0.0 Bring every user-facing doc and status artifact in line with reality ahead of the 1.0.0 cut: ROADMAP status (20/29 suite, gate MET), CHANGELOG (#376/#379 entries, suite-exclusion doc, 0.x upgrade guide, heading/link-ref normalization), the sandboxing guide (:max_instructions as the primary CPU defense), README (utf8 coverage, qualified filesystem claim, ~LUA import), and the mix.suite guide (skips-file reality, current sample output). Also fixes the 5.4->5.3 manual link, the default-source docstring, a disjoint-type test warning, and wires up the Lua.Parser.Error doctest. Plan: A49 --- .agents/plans/A33-perf-baseline-luerl-gap.md | 6 +- .agents/plans/B17-vm-max-steps.md | 6 +- CHANGELOG.md | 88 ++++++++++++++++++-- README.md | 16 +++- ROADMAP.md | 32 +++---- guides/mix_tasks.md | 45 ++++++---- guides/sandboxing.md | 58 ++++++++++--- lib/lua.ex | 2 +- lib/lua/runtime_exception.ex | 2 +- tasks/lua.suite.ex | 26 +++--- test/lua/parser/error_unit_test.exs | 2 + test/lua_test.exs | 1 - 12 files changed, 208 insertions(+), 76 deletions(-) diff --git a/.agents/plans/A33-perf-baseline-luerl-gap.md b/.agents/plans/A33-perf-baseline-luerl-gap.md index 88073d2..11459e4 100644 --- a/.agents/plans/A33-perf-baseline-luerl-gap.md +++ b/.agents/plans/A33-perf-baseline-luerl-gap.md @@ -5,7 +5,7 @@ issue: null pr: null branch: perf/baseline-luerl-gap base: main -status: ready +status: merged direction: A unlocks: - per-workload perf fix plans @@ -145,4 +145,6 @@ this plan can use it. If not, run the scripts directly. ## Discoveries -(populated during measurement) +The baseline was captured and committed as +[`benchmarks/BASELINE.md`](../../benchmarks/BASELINE.md) (2026-06-15), +which supersedes the planned `bench_results/baseline-.md` path. diff --git a/.agents/plans/B17-vm-max-steps.md b/.agents/plans/B17-vm-max-steps.md index 308c92a..88d23e8 100644 --- a/.agents/plans/B17-vm-max-steps.md +++ b/.agents/plans/B17-vm-max-steps.md @@ -5,7 +5,7 @@ issue: 306 pr: 320 branch: feat/vm-max-steps base: main -status: review +status: merged direction: B unlocks: - deterministic CPU bound for library consumers calling Lua.eval!/2 without a host Task + timeout wrapper @@ -193,7 +193,9 @@ mix test --only lua53 ## What changed -PR #320. +PR #320 (merged). The option shipped **renamed** from the plan's working +name `:max_steps` to `:max_instructions` — the name used in `Lua.new/1`, +the docs, and the CHANGELOG. Everything else below matches what landed. Files touched: diff --git a/CHANGELOG.md b/CHANGELOG.md index 84876c1..100ccba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,59 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +> Upgrading from a Luerl-based `0.x` release? See +> [Upgrading from 0.x](#upgrading-from-0x-luerl-based-versions) below. + +## Upgrading from 0.x (Luerl-based versions) + +`1.0.0` replaces the Luerl backend with an Elixir-native Lua 5.3 VM, and +Luerl is no longer a runtime dependency. Most code built on the high-level +`Lua` API (`Lua.new/1`, `Lua.eval!/2`, `Lua.set!/3`, `deflua`, +`Lua.load_api/2`) keeps working unchanged. The breaking changes are all at +the value-encoding and error boundaries: + +- **Encoded table / userdata / function tags changed.** Values that carried + Luerl's internal tags now use the new VM's representation: tables are + `{:tref, integer()}` (was `:luerl.tref()`), userdata is + `{:udref, integer()}` (was `:luerl.usdref()`), and Elixir-defined Lua + callables are `{:native_func, fun}` (was `:luerl.erl_func()`); compiled Lua + functions are `{:lua_closure, _, _}`. If you pattern-matched the old + tuples, update the patterns — better still, treat encoded refs as opaque + and round-trip them through `Lua.decode!/2`. +- **MFA callback encoding was removed.** `Lua.encode!/2` no longer accepts the + `{module(), atom(), list()}` MFA tuple form. Replace it with a function + literal or a `deflua` callback. +- **Parser error messages have a new format.** The Luerl-style + `"Line 1: syntax error before: ';'"` is gone; the native parser produces + messages like `"Expected expression"`, with rich structured data available + via `Lua.Parser.parse_structured/1`. Assertions that string-matched the old + wording need updating. +- **64-bit integers wrap on overflow.** Arithmetic and bitwise ops follow Lua + 5.3 §3.4.1 (wrap-around at 2^63) instead of widening to bignums as Luerl + did. Code depending on arbitrary-precision integer results will now see + wrapped values. +- **Chunks are self-contained.** `Lua.Chunk` now holds a compiled prototype + and is reusable across `Lua.eval!/2` calls; there is no separate load step. +- **Exceptions are public.** `Lua.RuntimeException` and `Lua.CompilerException` + are documented, so user code can rescue and pattern-match them. + +Everything else — the default sandbox, `_G`/`_ENV` semantics, metatables, and +the standard-library surface — is compatible. The full breaking-change list +is in the [`1.0.0-rc.0`](#100-rc0---2026-05-26) entry below. + +## [Unreleased] + +### Changed +- Elixir callbacks now receive the public `Lua.t` (`%Lua{}`) as their state + argument **regardless of how they enter the VM**. Previously a two-arity + callback (`fn args, state -> {results, state} end`) got a `%Lua{}` when set + at a path or loaded via `deflua`/`Lua.load_api/2`, but the raw internal + `Lua.VM.State` when it reached the VM as an encoded value — nested inside a + value passed to `Lua.set!/3`, or produced by `Lua.encode!/2`. Those paths + now wrap the state consistently and validate the callback's return value, so + the same closure behaves identically everywhere. Code relying on the rc.3 + raw-state behaviour (or a `%Lua{state: raw}` workaround) should drop the + workaround (#379). ### Fixed - `Lua.encode!/2` now maps Elixir `nil` to Lua `nil` instead of the string @@ -14,6 +66,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 then ...` checks and breaking `return nil, "reason"` error patterns. The round trip `decode!(encode!(nil))` is now lossless, matching the existing behaviour for `nil` inside tables and function result lists (#374). +- `string.rep` now sizes its allocation to the actual result length instead of + the raw repeat count, so a large count paired with an empty or short string + (e.g. `string.rep("", 1e9)`) no longer over-allocates or trips the + string-size guard spuriously. The guard still refuses genuinely oversized + results (#376). + +### Documentation +- The Lua 5.3 official test suite now passes **20/29** files. The 9 excluded + files are deliberate, documented exclusions rather than open bugs: + - **Filesystem / subprocess non-goals** — `main`, `files`, `attrib`, + `verybig` (shell-out, file I/O, and filesystem `require`, which a + sandboxed embedded VM does not support). + - **Capability non-goals** — `coroutine`, `db` (the full + continuation/coroutine model and the full `debug` library). + - **Perf-bound, revisit in 1.0.x** — `big`, `closure` (run past the suite + timeout on the BEAM tuple-copy ceiling; the VM results are correct). + - **PUC error-wording divergence** — `errors` (our structured error + messages diverge from PUC-Lua's exact strings). ## [1.0.0-rc.3] - 2026-06-15 @@ -217,7 +287,7 @@ The public API is unchanged from rc.1. faster. This remains a deliberate safety/speed tradeoff for the RC and will be addressed before `1.0.0` final. -## [v1.0.0-rc.1] - 2026-06-02 +## [1.0.0-rc.1] - 2026-06-02 The second release candidate for `1.0.0`. It builds on rc.0 with a new `os` and `utf8` standard library, richer `debug` and error introspection, @@ -303,7 +373,7 @@ the same machine (Luerl and PUC-Lua used as drift controls, ±3%): are unaffected or faster. This is a deliberate safety/speed tradeoff for the RC and will be addressed before `1.0.0` final. -## [v1.0.0-rc.0] - 2026-05-26 +## [1.0.0-rc.0] - 2026-05-26 This is the first release candidate for `1.0.0`. The library has been rewritten on a new Elixir-native Lua 5.3 virtual machine, and the public @@ -414,7 +484,7 @@ API is intended to be stable. Please report any regressions before final. - Line number attribution for the first line of a chunk (#240). - `string.pack` no longer emits compile warnings (#224). -## [v0.4.0] - 2025-12-06 +## [0.4.0] - 2025-12-06 ### Changed - Upgrade to Luerl 1.5.1 @@ -422,7 +492,7 @@ API is intended to be stable. Please report any regressions before final. ### Fixed - Warnings on Elixir 1.19 -## [v0.3.0] - 2025-06-09 +## [0.3.0] - 2025-06-09 ### Added - Guards for encoded Lua values in `deflua` functions @@ -435,7 +505,7 @@ API is intended to be stable. Please report any regressions before final. ### Fixed - `deflua` function can now specify guards when using or not using state -## [v0.2.1] - 2025-05-14 +## [0.2.1] - 2025-05-14 ### Added - `Lua.encode_list!/2` and `Lua.decode_list!/2` for encoding and decoding function arguments and return values @@ -443,18 +513,18 @@ API is intended to be stable. Please report any regressions before final. ### Fixed - Ensure that list return values are properly encoded -## [v0.2.0] - 2025-05-14 +## [0.2.0] - 2025-05-14 ### Changed - Any data returned from a `deflua` function, or a function set by `Lua.set!/3` is now validated. If the data is not an identity value, or an encoded value, it will raise an exception. In the past, `Lua` and Luerl would happily accept bad values, causing downstream problems in the program. This led to unexpected behavior, where depending on if the data passed was decoded or not, the program would succeed or fail. -## [v0.1.1] - 2025-05-13 +## [0.1.1] - 2025-05-13 ### Added - `Lua.put_private/3`, `Lua.get_private/2`, `Lua.get_private!/2`, and `Lua.delete_private/2` for working with private state -## [v0.1.0] - 2025-05-12 +## [0.1.0] - 2025-05-12 ### Fixed diff --git a/README.md b/README.md index 01cc0f0..143d171 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,11 @@ read them back: Runtime errors raise `Lua.RuntimeException`, which carries the failing `:source` and `:line` so you can report exactly where a script broke: +The `~LUA` sigil used below (and elsewhere in this tour) needs `import Lua`: + ```elixir +import Lua + try do Lua.eval!(~LUA""" local x = 1 @@ -175,15 +179,19 @@ Lua object patterns work as written: `Lua` targets Lua 5.3. The lexer, parser, register-based VM, value encoding/decoding, varargs, multiple returns, `_G`/`_ENV`, metatables, the string-pattern engine (`find`/`match`/`gmatch`/`gsub`), and the `string`, -`table`, `math`, `os`, and `debug` standard libraries are implemented. +`table`, `math`, `os`, `utf8`, and `debug` standard libraries are implemented. As a sandboxed *embedded* VM, some standalone-interpreter behavior is a deliberate non-goal rather than a missing feature: - **Standalone interpreter / `os.execute`** — there is no shell-out to the host. -- **Host filesystem access** — `Lua` does not read your host filesystem. The - `io.*` library and `require`/`dofile` are sandboxed by default and raise - rather than touching disk; there is no host-OS file or module resolution. +- **Host filesystem access is deny-by-default** — `Lua` does not read your + host filesystem unless you opt in. The `io.*` library and `require`/`dofile` + are sandboxed by default and raise rather than touching disk, so out of the + box there is no host-OS file or module resolution. When you *want* it, you + enable it explicitly: `Lua.set_lua_paths/2` configures where `require` + resolves modules on disk, and `Lua.load_file!/2` loads a Lua file from the + host. - **Coroutines**, **garbage collection / weak tables**, and the **full `debug` library**. diff --git a/ROADMAP.md b/ROADMAP.md index 6e40d15..bca1d1f 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2,21 +2,25 @@ This is the strategic overview. For per-PR detail, see [`.agents/plans/`](.agents/plans). -## Status: 2026-06-15 - -- **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`, - `constructs`, `events`, `gc`, `goto`, `literals`, `locals`, `pm`). 4 - deliberate non-goals (`main`, `files`, `attrib`, `verybig`). 8 still - whole-file-skipped pending triage (`big`, `closure`, `coroutine`, - `db`, `errors`, `math`, `sort`, `strings`). +## Status: 2026-07-05 + +- **Version**: `1.0.0-rc.3` shipped (rc.0–rc.3 all released); `1.0.0` + final is pending the release cut. +- **Lua 5.3 official suite**: 20/29 files passing. 9 fully clean + (`api`, `bitwise`, `code`, `locals`, `nextvar`, `simple_test`, `tpack`, + `utf8`, `vararg`) plus 11 passing with documented skip-ranges (`all`, + `calls`, `constructs`, `events`, `gc`, `goto`, `literals`, `math`, + `pm`, `sort`, `strings`). The remaining 9 are the documented + exclusions: 5 whole-file skips (`big`, `closure` — perf-bound, revisit + in 1.0.x; `coroutine`, `db` — capability non-goals; `errors` — PUC + error-wording divergence) plus 4 deliberate non-goals (`main`, `files`, + `attrib`, `verybig` — filesystem/subprocess). `strings`, `sort`, and + `math` were promoted in #368. - **Current focus**: closing the `1.0.0` release gates in - [`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). + [`A13`](.agents/plans/A13-release-rc1.md) — the suite gate is **MET** + (20/29 passing with 9 documented exclusions), and the one-time + perf-gate check is satisfied (fib already at 1.03–1.11× Luerl after + #324/#360). Remaining work is the release-cut and docs true-up. ## Release sequencing (decided 2026-06-15) diff --git a/guides/mix_tasks.md b/guides/mix_tasks.md index d89cd1d..acfae83 100644 --- a/guides/mix_tasks.md +++ b/guides/mix_tasks.md @@ -52,10 +52,11 @@ to projects that depend on `:lua`. ### `mix lua.suite` — run the Lua 5.3 official suite -Runs every `.lua` file in `test/lua53_tests/` against this VM and -prints a pass / fail / timeout summary. Use it to spot newly-passing -suite files (candidates to promote into `@ready_tests`) or to -sanity-check the suite set during development. +Runs every `.lua` file in `test/lua53_tests/` **unmodified** against this +VM and prints a pass / fail / timeout summary. Because it applies no skip +ranges, its pass count is lower than the canonical suite count — it is a +triage and exploration tool for spotting files whose skip ranges in +`test/lua53_skips.exs` could be narrowed or removed. ```bash # Run all files @@ -73,35 +74,43 @@ mix lua.suite --verbose # Adjust per-file timeout (default 30s; long-running files like # `big.lua` and `closure.lua` need more, while CI may want less) mix lua.suite --timeout 60000 + +# Per-file conformance summary read from the skips file (no tests run) +mix lua.suite --status + +# Re-run each skip entry with it removed, flagging stale/promotable ranges +mix lua.suite --audit ``` -Sample output: +Sample output (raw run — no skip ranges applied): ``` -passing: 6 -failing: 21 -timeout: 2 +passing: 9 +failing: 17 +timeout: 3 -passing files: api, bitwise, code, simple_test, tpack, vararg +passing files: api, bitwise, code, locals, nextvar, simple_test, tpack, utf8, vararg failing files (top reason): - attrib.lua Lua runtime error: 'require' is sandboxed - big.lua Lua runtime error: attempt to compare a string with a number + all.lua Lua runtime error: loadfile(_) is sandboxed + math.lua Lua runtime error: bad argument in arithmetic expression ... timed out: - closure.lua > 30000ms - strings.lua > 30000ms + big.lua > 30000ms + closure.lua > 30000ms + constructs.lua > 30000ms ``` Each file is run in its own monitored task so an infinite loop in one file can't hang the run. -Unlike `mix test --only lua53`, this task does **not** consult the -hand-curated `@ready_tests` / `@deferred_permanent` lists in -`test/lua53_suite_test.exs`. It just runs everything. Use -`mix test --only lua53` for the canonical green-bar set, and use -`mix lua.suite` for exploration and triage. +Unlike `mix test --only lua53`, this task does **not** apply the per-file +skip ranges in `test/lua53_skips.exs` or the `@deferred_permanent` list in +`test/lua53_suite_test.exs` — it just runs everything raw. Use +`mix test --only lua53` for the canonical green-bar set (**20/29** files +passing, with the 9 documented exclusions), and use `mix lua.suite` (and +its `--status` / `--audit` modes) for exploration and triage. Exit status: diff --git a/guides/sandboxing.md b/guides/sandboxing.md index a4d76e3..befba9e 100644 --- a/guides/sandboxing.md +++ b/guides/sandboxing.md @@ -151,13 +151,47 @@ message =~ "stack overflow" > loops that PUC-Lua would run forever. Leave the default `:infinity` > if you rely on unbounded tail recursion. -## Limiting CPU time and total memory +## Limiting CPU work -The VM has **no internal wall-clock timeout** and does **not** cap the -host process's total memory. A script can still spin forever -(`while true do end`) or accumulate memory in ways the per-operation -guards above don't catch (for example, growing a table in a loop). These -limits have to be enforced by the BEAM, around the call. +The first defense against a runaway script (`while true do end`, a tight +numeric loop) is `:max_instructions` — an **in-VM instruction budget** +that terminates the evaluation deterministically, without a host `Task` +or wall-clock timeout: + +```elixir +lua = Lua.new(max_instructions: 1_000) + +{[false, message], _} = + Lua.eval!(lua, "return pcall(function() while true do end end)") + +message =~ "instruction budget exceeded" +#=> true +``` + +`:max_instructions` defaults to `:infinity` (no bound). A finite budget is +counted at loop back-edges and call boundaries — never per opcode, so the +default path carries no cost — and is fresh for each top-level +`Lua.eval!/2`. Exhausting it raises a catchable +`"instruction budget exceeded"` error, so a script recovers with `pcall` +just like it does from the allocation guards above. Because the budget is +enforced inside the VM, it needs no extra process and it bounds CPU work +even for code that never yields to the scheduler. + +> #### Instruction budget vs wall-clock time {: .info} +> `:max_instructions` bounds *work*, not *elapsed time* — a fixed budget +> executes the same number of instructions no matter how loaded the host +> is. For a hard wall-clock ceiling ("no script may run longer than one +> real second regardless of what it does"), pair it with the process +> wrapper below. + +## Limiting wall-clock time and total memory + +Two limits still live outside the VM: real elapsed time and the host +process's total memory. `:max_instructions` bounds work rather than +seconds, and the VM does **not** cap the host process's memory — a script +can still accumulate memory in ways the per-operation guards don't catch +(for example, growing a table in a loop). These limits have to be enforced +by the BEAM, around the call. Run untrusted scripts in a **separate, monitored process** with both a timeout and a heap ceiling. The key is to keep the wall-clock timeout and @@ -246,16 +280,18 @@ Two details make this robust: ## Putting it together A typical configuration for running untrusted scripts combines all three -layers — the default sandbox, a call-depth bound, and a process wrapper -for time and memory: +layers — the default sandbox, the in-VM recursion and instruction bounds, +and a process wrapper for time and memory: ```elixir -lua = Lua.new(max_call_depth: 200) +lua = Lua.new(max_call_depth: 200, max_instructions: 10_000_000) SafeLua.run(lua, untrusted_source) ``` The default sandbox blocks the OS/filesystem/loader surface, the built-in guards turn allocation bombs into catchable errors, `:max_call_depth` -bounds recursion, and `SafeLua.run/2` bounds wall-clock time and total -memory — with the host process surviving every one of those failures. +bounds recursion, `:max_instructions` bounds CPU work deterministically +inside the VM, and `SafeLua.run/2` adds a wall-clock timeout and a total +memory ceiling — with the host process surviving every one of those +failures. diff --git a/lib/lua.ex b/lib/lua.ex index f5429e3..161fed5 100644 --- a/lib/lua.ex +++ b/lib/lua.ex @@ -943,7 +943,7 @@ defmodule Lua do Loads a Lua file into the environment. Any values returned in the global scope are thrown away. - Mimics the functionality of Lua's [dofile](https://www.lua.org/manual/5.4/manual.html#pdf-dofile) + Mimics the functionality of Lua's [dofile](https://www.lua.org/manual/5.3/manual.html#pdf-dofile) """ @spec load_file!(t(), String.t()) :: t() def load_file!(%__MODULE__{} = lua, path) when is_binary(path) do diff --git a/lib/lua/runtime_exception.ex b/lib/lua/runtime_exception.ex index 2c9bae6..c341d20 100644 --- a/lib/lua/runtime_exception.ex +++ b/lib/lua/runtime_exception.ex @@ -10,7 +10,7 @@ defmodule Lua.RuntimeException do * `:original` — the underlying VM error term * `:state` — the internal VM state at the point of failure * `:line` — line number where the error was raised - * `:source` — source name (filename or ``) + * `:source` — source name (filename or the default ``) * `:call_stack` — list of Lua frames at failure """ alias Lua.Util diff --git a/tasks/lua.suite.ex b/tasks/lua.suite.ex index e8590f9..c9d0c0b 100644 --- a/tasks/lua.suite.ex +++ b/tasks/lua.suite.ex @@ -11,12 +11,12 @@ defmodule Mix.Tasks.Lua.Suite do > are downloaded into this repo via `mix lua.get_tests` and are not > part of the Hex package. - Unlike `mix test --only lua53`, this task does not consult the - hand-curated `@ready_tests` / `@deferred_permanent` lists in - `test/lua53_suite_test.exs`. It runs every file and reports - whatever happens, which makes it useful for spotting newly-passing - files (candidates to promote) and for sanity-checking the suite - set during development. + Unlike `mix test --only lua53`, this task does not apply the per-file + skip ranges in `test/lua53_skips.exs` or the `@deferred_permanent` + list in `test/lua53_suite_test.exs`. It runs every file raw and + reports whatever happens, which makes it useful for spotting files + whose skip ranges could be narrowed or removed, and for + sanity-checking the suite set during development. ## Usage @@ -45,16 +45,16 @@ defmodule Mix.Tasks.Lua.Suite do ## Output - A summary like: + A summary like (raw run — no skip ranges applied): - passing: 6 - failing: 23 - skipped: 0 + passing: 9 + failing: 17 + timeout: 3 - passing files: api, bitwise, code, simple_test, tpack, vararg + passing files: api, bitwise, code, locals, nextvar, simple_test, tpack, utf8, vararg failing files (top reason): - attrib.lua 'require' is sandboxed - big.lua attempt to compare a string with a number + all.lua loadfile(_) is sandboxed + math.lua bad argument in arithmetic expression ... ## Exit codes diff --git a/test/lua/parser/error_unit_test.exs b/test/lua/parser/error_unit_test.exs index 8bc321b..33fc9fc 100644 --- a/test/lua/parser/error_unit_test.exs +++ b/test/lua/parser/error_unit_test.exs @@ -3,6 +3,8 @@ defmodule Lua.Parser.ErrorUnitTest do alias Lua.Parser.Error + doctest Error + describe "new/4" do test "creates error with all fields" do position = %{line: 1, column: 5, byte_offset: 0} diff --git a/test/lua_test.exs b/test/lua_test.exs index 03090b1..0d62d64 100644 --- a/test/lua_test.exs +++ b/test/lua_test.exs @@ -728,7 +728,6 @@ defmodule LuaTest do # Top-level nil must map to Lua nil, not the truthy string "nil" assert {encoded, lua} = Lua.encode!(lua, nil) assert encoded == nil - refute encoded == "nil" # Round trip is lossless and symmetric with decode!/2 assert nil == Lua.decode!(lua, encoded) From d0449a121551e794e281e0b7c93242af5673eb79 Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Sun, 5 Jul 2026 06:18:49 -0700 Subject: [PATCH 3/3] chore(A49): mark plan as review --- .agents/plans/A49-release-docs-sync.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/.agents/plans/A49-release-docs-sync.md b/.agents/plans/A49-release-docs-sync.md index abff63e..6e88f66 100644 --- a/.agents/plans/A49-release-docs-sync.md +++ b/.agents/plans/A49-release-docs-sync.md @@ -2,10 +2,10 @@ id: A49 title: "Docs sync for 1.0.0: roadmap, changelog, guides, README true-up" issue: null -pr: null +pr: 381 branch: docs/release-docs-sync base: main -status: in_progress +status: review direction: A --- @@ -59,3 +59,19 @@ mix docs --warnings-as-errors ## Risks - Concurrent API-PR CHANGELOG conflict — kept small via labeled subsections. + +## What changed + +PR #381. Files touched: `ROADMAP.md`, `CHANGELOG.md`, `README.md`, +`guides/sandboxing.md`, `guides/mix_tasks.md`, `lib/lua.ex`, +`lib/lua/runtime_exception.ex`, `tasks/lua.suite.ex`, +`test/lua_test.exs`, `test/lua/parser/error_unit_test.exs`, and the +`B17` / `A33` plan files. + +Discoveries: +- The `mix lua.suite` task moduledoc (`tasks/lua.suite.ex`) carried the + same stale `@ready_tests` reference and sample output as the guide, so + it was fixed alongside item 7 — same doc-only class, no behavior change. +- The canonical suite count (20/29, skip ranges applied) differs from the + raw `mix lua.suite` count (9/17/3, no skips); the guide now distinguishes + them explicitly.