The sigil: field
The sigil: field in package.sgl declares the minimum Sigil version a package needs — a bare version string, no range operators:
(package
name: "my-app"
version: "0.1.0"
sigil: "0.19" ; needs at least Sigil 0.19
...)Compatibility is resolved >= declared, with an implicit ceiling at the next major version: sigil: "0.19" admits [0.19, 1.0), and sigil: "1.2" admits [1.2, 2.0). Majors are 1.0 and 2.0 — never the semver-0.x pseudo-major, so a 0.x declaration does not expire when 0.(x+1) ships.
Declaring a measured ceiling
When a package has been tested to break on a newer Sigil, the ceiling can be named explicitly with the supports form:
sigil: (supports min: "0.16" max: "0.20") ; max is EXCLUSIVEWriting a supports form is a claim that someone measured the incompatibility — it is deliberately heavier than the string so it looks deliberate at the use site. If you have no measured ceiling, use the bare string.
What the field controls
- Validation.
sigil build,sigil deps install, and every other command-entry path refuse a host manifest whosesigil:field is missing, carries a range operator, or has a malformedsupportsform (exit 78,EX_CONFIG— retrying unchanged cannot succeed). The diagnostic names the operator and the replacement. The one exception is a grandfathered caret — see the grandfather clause below — which warns instead of refusing. - Runtime resolution. The implicit
sigil-stdlib/sigil-libdependencies resolve inside the admitted window. The lockfile decides the point inside the window:sigil deps installkeeps the locked runtime; a no-lock install takes the newest admitted tag;sigil deps updatere-resolves ordinary dependencies but holds the runtime at its locked version unless you pass--upgrade-sigil. Nobody gets moved to a new runtime by relocking. - The coherence check. The floor axis of
SIGIL_BUILD_COHERENCEcompares the resolved Sigil against every installed dependency's declared window, and can refuse a build compiling a dependency against a runtime it declared it cannot run on.
Range operators (^, ~, >=, >, <=, <, =, *, compound ranges) remain valid for dependency version: fields. Only sigil: is a bare minimum.
The caret grandfather clause (tolerate-but-warn, self-deleting)
The estate-wide convention until 2026-08-25 was a caret (sigil: "^0.16") — every one of the 2858 operatored declarations measured that day was one. A grandfathered caret is tolerated: read as its bare floor (the caret is ignored, not honoured), with one compact warning per build. Tolerance is bounded by two conditions in the code — deliberately not by an environment switch or a future decision, because deferred flips in this estate historically do not happen:
- Grandfather cutoff. Only
^Xwith a floor below 0.20 (the line at which the convention retired) is tolerated.^0.20,^1.2, and every non-caret operator refuse — so new caret debt cannot accumulate, and the tolerated set can only shrink. - Flag-day end. When the running compiler's major version reaches 1, tolerance ends and every operator refuses.
^0.16read as floor 0.16 must not admit 1.0.
When to actually migrate a caret — this matters: rewrite ^0.16 to "0.16" only once every Sigil toolchain that builds the repo is at least the version the warning names. Compilers predating this change read a bare sigil: value as an EXACT version range; measured on a real repo, that evicts the lockfile's runtime pin and silently downgrades (exit 0) on the next sigil deps install. The warning therefore names the minimum version in its own text. Until then the caret spelling keeps working — swept and unswept manifests resolve identically under any compiler carrying this change.
The retired convention (pre-2026-08-25)
Until 2026-08-25 the field was documented as a semver range, and the estate-wide convention was a caret: sigil: "^0.16". A caret on a 0.x version pins the minor (^0.16 = [0.16.0, 0.17.0)), so every such declaration expired by construction the moment the next Sigil minor shipped: measured on 2026-08-25, 808 declaration-era findings across 149 checkouts — including the Sigil monorepo, which failed the range predicate while building itself fine — against zero actual floor violations across 274 installed dependencies.
The ruling (decisions/2026-08-25-sigil-field-is-a-floor, David): a floor is a fact about what a package uses; an upper bound is a prediction about software that does not exist yet. Predictions expire; facts do not. Rust's rust-version and Go's go directive made the same choice.
If you meet a sigil: "^0.16" in an old branch or an installed dependency copy: it is read leniently as its floor (0.16). Installed copies of old releases are frozen artifacts and are never refused for carrying the old convention; only host manifests (the project being built and its workspace members) are validated. To migrate one, strip the operator and keep the version — "^0.16" becomes "0.16". Do not raise the floor while migrating; raising a floor is a behavioural claim that needs its own evidence.
There is deliberately no special case letting pre-1.0 declarations roll into 1.0: 1.0 is when breaking changes are most likely — the last moment before compatibility promises bind — so it is exactly the transition the ceiling exists to gate. 1.0 is a flag day: crossing it costs one mechanical sweep over the estate's declarations, once.