Sigil 0.22.8
A correctness release. Release and web builds could drop the guard of a small helper procedure, comparisons with NaN answered true, and sigil mcp serve showed no tools in new Claude Code sessions. It also fixes a web app that doesn't import (sigil core), a from-path dependency that was silently left out, macros that allocated on every use in native code, and modules reused after SIGIL_FEATURES changed. More of R7RS now works without an import.
Existing projects build without changes. Rebuild your release and web builds to pick up the first fix.
Release and web builds keep a helper's guard
Since 0.16.1, release and web builds replaced a small helper with a fast path when its body could end in a struct field read, an index calculation (+ x (* y (cols g))) or a vector lookup (vector-ref (items s) i), even when the helper did something else first. (if (< i 0) 'none (vector-ref (items s) i)) crashed on the guarded branch, a guarded index helper returned the unguarded sum, and a counter bumped before the lookup never moved. Dev builds were right, so tests passed. The fast path now applies only when the whole helper is that one expression.
Comparisons with NaN are false
(<= nan 1.0), (>= nan 1.0) and (= nan nan) answered #t, so a tolerance check like (<= (abs (- a b)) eps) let a NaN through, and zero? said #t for NaN. Every comparison with NaN is now false, on every backend, and min and max return NaN when an argument is NaN.
Sigil's MCP tools appear again in new Claude Code sessions
Since 2026-09-25, Claude Code 2.1.282 negotiates MCP protocol revision 2026-07-28 with any server that offers it, and sigil mcp serve offered it without answering in that revision's shape. Claude Code checks that a tool listing says how long it may be cached (ttlMs and cacheScope). The listing from sigil mcp serve had neither, so the client retried a few times and then registered no tools at all, with no error shown. This release takes sigil-mcp 0.18.1, which adds both fields whenever the client speaks 2026-07-28, and older clients get exactly the answers they got before. Sessions that are already running keep the server they started with, so restart them after upgrading.
A web app that doesn't import (sigil core) starts
An app whose modules never import (sigil core) died in the browser before running anything, with unbound variable '%set-spec!'. The core library is now always set up first.
A from-path dependency is installed and built
A project's (from-path dir: "../lib") without package: was left out: sigil deps install didn't link it and sigil build finished without it, with only a "failed to load import" warning. It's now linked and built like any other dependency, and one that doesn't resolve stops both commands with its directory named. When --redirects points a dependency elsewhere than its .sigil/deps link, the build says which tree it used.
Macros cost nothing at run time in native code
A syntax-rules macro whose expansion calls a procedure or reads a variable of the macro's own library looked the name up, and allocated, on every use in release and web builds: 27 allocations for one vector operation in sigil-geometry. Each use now finds the binding once, and still sees it change after a set!.
Modules are recompiled when SIGIL_FEATURES changes
sigil eval, sigil run and sigil test keep compiled copies of the modules they build from source, and reused a copy compiled without a feature when that feature was set, so a cond-expand kept its old branch. Each feature set now has its own copies.
More of R7RS works without an import
A library that imports only (sigil core), and every test file, now has these (scheme base) procedures:
- the math procedures
floor,ceiling,round,truncate,max,min,quotient,gcd,lcm,expt,square,exact,inexact,exact?,inexact?,exact-integer?and thefloor/andtruncate/families, which needed(sigil math); string<?,string>?,string<=?andstring>=?, which needed(sigil string)and now take more than two strings;list-tail,list-set!,list-copy,vector-append,boolean=?,symbol=?,exact-integer-sqrt,input-port-open?,output-port-open?,rational?,real?,complex?,numerator,denominator,rationalize,exact->inexactandinexact->exact, which didn't exist. Sigil has no exact fractions, sonumerator,denominatorandrationalizereturn floats for a fractional argument, andrationalizecomputes in floating point.
(sigil math) and (sigil string) still export the names they had. A library or program that defines one of these names itself keeps its own definition. abs, modulo, remainder and call/cc can now be passed as values: (map abs xs) raised "unbound variable". Importing (sigil string) no longer swaps string-upcase and string-downcase for its own ASCII-only versions; they are core's.
The R7RS compatibility page now says which module each name is in and lists the gaps that remain.
Smaller fixes
sigil cli use v0.22.7works; the leadingvof a tag name is accepted.sigil deps install --helplists the options, including--redirectsand--upgrade-sigil.string-indexandstring-index-righttake a character as well as a predicate:(string-index "a,b" #\,)is 1.sigil testshows each file's time in its per-file line, and--reportlists every file with its outcome, counts and time in milliseconds.- A module can export 1024 names, up from 512. The web bridge packages (browser, audio, WebGL, DOM, canvas, network) add their natives to
(sigil core), and a web app that linked several of them could run core out of room. A module past the limit now reports each name it can't export instead of dropping it silently.
Known issues
Release builds of desktop apps run the standard library and dependency packages as bytecode; only the app's own modules are native. Web builds run everything native. SIGIL_NATIVE_DEPS=1 makes a release build compile the standard library natively, at the cost of a much larger binary, since every standard library module is included. A proper fix is coming.
string-upcase and string-downcase convert non-ASCII letters with the C library's case tables, so their results for non-ASCII text can differ between systems: the Linux release build turns "λ" into "Λ", and a build against glibc may leave it unchanged. Before 0.22.8 a library that imported (sigil string) got ASCII-only versions whenever it passed these procedures as values.