Skip to content

fix(fasl): replay a nested (load) once on a warm hit (#92; stacked on #89) - #94

Merged
pyrex41 merged 3 commits into
mainfrom
capcov/fix-92-fasl-nested-load
Oct 19, 2026
Merged

pyrex41 merged 3 commits into
mainfrom
capcov/fix-92-fasl-nested-load

Conversation

@pyrex41

@pyrex41 pyrex41 commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

Fixes #92

Stacked on #89 (capcov/fix-86-87-home-dir-cache @ 7ad7ae4). The fix doesn't depend on #89: the boot.lua and test changes cherry-pick onto main cleanly. The only conflict is a textual one in CHANGELOG.md, where this entry sits next to #89's. Only the f-modelcheck acceptance run needs #89, because that driver does (cd ...) and then loads relative paths. If #89's branch is merged and deleted, GitHub retargets this PR to main.

Root cause

splice_into_outer (boot.lua, install_fasl) copies a nested load's fasl records into the enclosing recording. The splice exists so that an outer stream has no hole for a nested load. It also ran when the nested load happened inside one of the outer file's chunks: a top-level (load ...) form, or a function that calls load. That load is already in the outer stream as the chunk that calls it. On a warm hit, replaying that chunk ran the nested load again, from its own fasl, and the spliced copy replayed it a second time. Every nested file's output doubled.

Fix

  • Skip the splice when the outer span is in_chunk. deps propagation and splicing for loads made outside a chunk are unchanged.
  • Bump FASL_FORMAT to SHENFASL7. The fasl key does not cover boot.lua, so caches written by older builds already contain the duplicate. Without the bump, an upgraded build still hit them and printed 36 MC lines. Now they miss once and are rewritten.
  • Regression test in test/cli_spec.lua covers two cases: a top-level nested load, and a load from a function. Each is checked cold, warm (asserted to be a fasl hit), warm again, and with SHEN_FASL=off. All runs must print BEGIN LIB LIB END. At 7ad7ae4 the warm checks fail (4 fails); at head they pass.

Evidence (LuaJIT 2.1.1785763465, macOS arm64, private TMPDIR/XDG_CACHE_HOME/SHEN_KERNEL_CACHE/SHEN_FASL_DIR)

Issue repro (--hush-load, cold → warm → warm, then SHEN_FASL=off):

base 7ad7ae4  main.shen   BEGIN|LIB|LIB|END|  BEGIN|LIB|LIB|LIB|LIB|END|  BEGIN|LIB|LIB|LIB|LIB|END|  off: BEGIN|LIB|LIB|END|
head bf3fd90  main.shen   BEGIN|LIB|LIB|END|  BEGIN|LIB|LIB|END|          BEGIN|LIB|LIB|END|          off: BEGIN|LIB|LIB|END|
head bf3fd90  main2.shen  BEGIN|LIB|LIB|END|  BEGIN|LIB|LIB|END|          BEGIN|LIB|LIB|END|          off: BEGIN|LIB|LIB|END|
shen-go 1f587de (load echo filtered)  BEGIN|LIB|LIB|END|  (both files)

capcov f-modelcheck, one fasl dir, run 0 cold then runs 1–3 warm (22 fasl hits, 0 misses):

base 7ad7ae4:  25, 36, 36, 36 MC lines
head bf3fd90:  25, 25, 25, 25 MC lines; payload byte-identical to the shen-go 1f587de reference on every run
head over a fasl dir warmed by base (upgrade path): 25, 25, 25, 25

Suites:

make test  base 7ad7ae4: TOTAL: 1225 pass, 0 fail across 23 specs
make test  head bf3fd90: TOTAL: 1237 pass, 0 fail across 23 specs   (cli_spec 54 -> 66: the new #92 checks)
certify    base 7ad7ae4: passed ... 154  failed ... 0  pass rate ... 100%
certify    head bf3fd90: passed ... 154  failed ... 0  pass rate ... 100%

🤖 Generated with Claude Code

pyrex41 and others added 2 commits October 19, 2026 06:38
When a recorded load ran another load from inside one of its chunks (a
top-level (load ...) form, or a function that loads), splice_into_outer
also copied the inner load's records into the outer fasl. On a hit the
outer chunk re-ran the load and the spliced copy replayed it again, so
the inner file's output printed twice on every warm run.

Splice only when the outer span is not in a chunk, which is the case the
splice exists for (a load whose effects no outer record reproduces). Deps
are still propagated either way.

Fixes #92

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A fasl written before the previous commit already holds the spliced
copy of an in-chunk nested load, and the cache key does not cover
boot.lua, so an upgraded build still hit it and printed the nested
output twice. A new header makes fasl_parse reject those files: they
are recompiled once, then cached correctly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pyrex41
pyrex41 changed the base branch from capcov/fix-86-87-home-dir-cache to main October 19, 2026 12:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Warm fasl cache duplicates the output of nested (load)s

1 participant