Sigil 0.22.1

release

A cache-correctness and test-reliability release. The package cache now proves what it reuses: it hashes the bytes of every input and output it depends on, binds the compiler's own identity into the cache key, and declines to reuse anything it cannot fully account for. The test runner stops counting other checkouts' tests as your own, and its file summary can finally report a shortfall. An opt-in evidence journal records what a test run actually loaded and executed, for qualifying a release rather than trusting a green suite.

Download 0.22.1Get started

Existing projects build without changes. No dependency-wide version bumps are required; a declaration such as sigil: "0.22" already admits 0.22.1. The first build after upgrading rebuilds packages whose manifests predate the new schema, and later builds reuse as before. No store reset is needed.

The package cache proves what it reuses

Package reuse used to trust a manifest whose inputs matched by size and modification time. Sigil now hashes the bytes of every input and every required output before reusing a package, so a same-size edit with a restored timestamp, a broken link, or a missing documentation sidecar rebuilds instead of restoring a stale result. Older manifests miss safely and are replaced after the next complete build.

The compiler's own identity is part of the cache key too: the executable's bytes and its startup libraries, not the version it reports. A rebuilt toolchain that still says 0.22.0 no longer reuses bytecode from the previous one. Full contract in the build cache reference.

Test discovery stays inside your checkout

In a repository without a test/ directory, discovery walks the whole tree and used to descend into other checkouts underneath it. One repository counted 117 of 319 per-file results from .worktrees/ and .claude/worktrees/, including a test that passed in the real tree and failed in a stale copy. Discovery now stops at any directory containing a .git and prints a SKIP line saying so.

The Files: summary line could not report a shortfall: a run in which nothing executed still printed Files: 4 of 4 run. It now reads X of Y loaded, so 0 of 1 loaded, 1 failed to load beside Tests: 1 passed tells you exactly what happened.

Opt-in test execution evidence

Set SIGIL_TEST_EVIDENCE to a new JSONL path and SIGIL_TEST_RUN_ID to a run token before starting Sigil, and the runtime journals every input buffer it acquired (with its SHA-256), every module initialization, and the runner's selection and completion records. scripts/observe-test-evidence.py runs one sigil test --sgl invocation under an external observer and reconciles the journal against the expected files and module bytes; it exits 2 for incomplete evidence, and a missing or truncated journal never qualifies as a pass. Inconsistent evidence is rejected, partial-load fail-fast is honored, and large journals stream during validation. This is POSIX-only and experimental; Windows and browser builds refuse the opt-in with exit 74. See runtime evidence and test execution evidence.

Runtime and process fixes

Async readers and writers are preserved when the opposite direction becomes ready on the same socket or descriptor, so duplex connections no longer freeze under backpressure; the visible symptom was terminal scrollback replay stalling on reconnect. Native closures stay rooted across allocating metadata initialization, closing a collection window that WebAssembly builds could hit. PTY children on macOS and BSD now receive the requested environment, so a TERM override reaches the shell as it already did on Linux. The build store retries a content lock that its previous owner releases mid-contention instead of reporting an unwritable store. Compiler summary export lookups are indexed, development CLI entrypoints read startup library roots the same way the bundled CLI does, and the release Scheme observation timeout is configurable.