Sigil 0.19.3
An environment release. sigil env shell now hands your program the search paths its dependencies declare, not just the ones its top-level packages declare, so a tool finds the terminfo database and the helper binaries it was built against instead of quietly falling back to whatever the host happens to have.
Existing projects build without changes. Locks written by an older Sigil are still read, with a warning naming what they cannot express. Re-locking is the act that commits a project to 0.19.3 or newer.
sigil env shell exports what your dependencies declare
Here is the symptom that started this. A tmux built entirely from a Sigil channel, run under sigil env shell, drew its terminal description from the host's system profile rather than from the ncurses it was built against, and tic on PATH was the host's rather than the store's. Nothing failed and nothing warned; the wrong answer was indistinguishable from the right one until you looked at which file had actually been opened.
The consumer composed five hardcoded variables (PATH, MANPATH, INFOPATH, TERMINFO_DIRS, XDG_DATA_DIRS) over the lock's root packages only, so a dependency's paths were never exported at all. The lock now records the runtime closure instead. Any root package whose dependency provides a binary, a manual page or a data directory now finds it.
That is half of the problem. Honouring a package's own search-paths: declaration at runtime is a separate change and is not in this release.
A new lock format, and why it is a bump rather than an extension
env.lock version 2 adds three things: one row per runtime closure member keyed by package name, the base search paths moved out of consumer code, and a system: header key so that air-gapped and store-shared reification can fetch by bare hash.
There was no forward-compatible way to add them. Measured against 0.19.2, a new record type, an extra key on an existing record and an extra header key are all refused with exit 68, exactly as a version bump is. Since every route cost the same breakage, the bump was chosen for the diagnostic it produces: "unsupported env.lock version 3; this sigil reads 1, 2" tells you what happened, where "unknown env.lock record" reads like a corrupt file.
What a lock may contain
The new rows put variable names and subdirectories into lock content, and sigil env exports turns both into shell text. So the reader validates them where the lock is decoded rather than where the text is emitted: a variable name must be a shell identifier, and a subdirectory may not contain quotes, backslashes or control bytes, on top of the existing rules against absolute paths and ...
If you hand-edit an env.lock and it is refused, that is why.
sigil deps install leaves your lock alone
In a workspace, sigil deps install rewrote sigil.lock on every run, so the tree was never clean and no tag could produce a clean build. Committing the regenerated lock was itself what invalidated it, so there was no commit at which deps install did nothing. If you have been committing lock churn you did not cause, this is it.
A dependency that resolves back into your own project is no longer locked at all. Sigil's own lock went from 17 entries to 13, a pure deletion. Two smaller repairs ride along: building without a .git directory no longer records an empty sha: that breaks the next install, and a deps install that keeps a stale lock now says so instead of reporting success.
Publishing and locking survive a busy machine
sigil env lock and sigil channel publish evaluate your channel's own program inside a sandbox with a CPU budget. That budget was 5 CPU-seconds and one cold run costs about half of it, so any other work on the same machine pushed it over. It is now 30 CPU-seconds with a 120-second wall clock. If you have had a channel that publishes fine in the morning and refuses in the afternoon, that was this. One thing worth knowing before you try to tune around it: the cost is invoking the compiler at all, not the size of your channel, so trimming bindings will not help.
The message was wrong in both directions. A sandbox that ran out of CPU reported that your channel had failed, and an unbound variable in your channel.sgl was reported as "evaluation failed within resource boundary": a real defect in your code, blamed on your machine. Resource exhaustion is now a setup failure, exit 75, naming the limit it hit, and a program failure names the program and its exit status. Where the sandbox genuinely cannot tell the two apart, it says so rather than picking one.
A channel whose body calls (exit 0) without producing a result used to fail with cannot open file for reading: .../result.sgl. That is a named refusal now.