sigildocs

(sigil deps semver)

(sigil deps semver) - Semantic Versioning

Parses semver version strings and resolves version range constraints against lists of available versions (from git tags).

Range syntax (Cargo-style for pre-1.0): "^0.9.0" → >=0.9.0, <0.10.0 (caret = minor-compatible) "^0.13" → >=0.13.0, <0.14.0 (caret on 2-part = caret on .0) "^1.2.3" → >=1.2.3, <2.0.0 (caret = major-compatible for >=1.0) "~0.9.0" → >=0.9.0, <0.9.1 (tilde = patch only) "~1.2.3" → >=1.2.3, <1.3.0 (tilde = minor-compatible for >=1.0) "0.9.0" → =0.9.0 (exact match) ">=0.10" → >=0.10.0 (open-ended lower bound) "<1.0" → <1.0.0 (open-ended upper bound, min = 0.0.0) ">=0.10, <1.0" (compound range, comma-separated) "*" → any version

CORRECTION, 2026-08-25 (decisions/2026-08-25-sigil-field-is-a-floor): the range syntax above applies to dependency version: fields ONLY. The sigil: field in package.sgl is no longer a range — it is a bare minimum version ("0.16" means "needs at least Sigil 0.16", compared as resolved >= declared), and range operators there are refused at host validation — with ONE exception: a GRANDFATHERED caret (^X, floor below 0.20, until the compiler's major reaches 1) is tolerated, read as its bare floor, and warned about (decisions/2026-08-25-sigil-caret-grandfather). Manifests released before this date carry ^0.16-style values by the retired convention; readers interpret those as their floor (see sigil-field-floor below). That leniency is permanent for installed copies: old releases are frozen artifacts, and archived packages will never re-release.

Exports

parse-semverprocedure

Parse a semver string into a list of three integers. Returns (major minor patch) or #f if invalid. Handles optional "v" prefix: "v1.2.3" → (1 2 3). Two-part input is accepted and patch is defaulted to 0: "0.13" → (0 13 0). One-part "1" → (1 0 0). This relaxation supports sigil: field values like "^0.13".

Parse a version range string into a range record. Returns (min-version . max-version) where both are semver lists, or 'any for wildcard, or #f if invalid. min is inclusive, max is exclusive. Compound ranges separated by commas (>=0.10, <1.0) are intersected to a single interval.

Check if a version satisfies a range.

tag->versionprocedure

Strip "v" prefix from a tag to get a version string.

version->tagprocedure

Add "v" prefix to a version string.

Resolve a version range against a list of git tags. Returns the tag string for the highest matching version, or #f if no match found.

Find the highest semver version from a list of git tags. Returns the tag string for the highest version, or #f if none valid.

Bump a version range string to target a new version. Preserves the range operator (^, ~, or exact). Examples: (bump-version-range "^0.8.0" "0.9.1") → "^0.9.1" (bump-version-range "~1.2.0" "1.3.5") → "~1.3.5" (bump-version-range "0.8.0" "0.9.1") → "0.9.1" (bump-version-range "" "0.9.1") → ""

The range operator carried by a sigil: field value, or #f when the value is a bare version (or a well-formed supports record — the operator check recurses into its min:/max:). Returns the operator as a string: "^" "~" ">=" ">" "<=" "<" "=" "*" "," (compound) "v" (v-prefix), or "?" for a value that parses as nothing at all.

The floor a sigil: value declares — the minimum version it admits — as a semver list, or #f when none is derivable ("*", "<X", unparseable input).

Reads bare versions AND legacy operatored values: installed copies of releases cut before 2026-08-25 carry ^0.16-style values, and those manifests are frozen artifacts — archived packages will never re-release. Their floor is the minimum the old range admitted. Refusing operators is the HOST validation's job ((sigil deps installed)); this reader stays lenient by design.

The first version ABOVE the ceiling a sigil: value declares — exclusive — or #f when the value carries no enforceable ceiling.

bare "0.16" → (1 0 0) the IMPLICIT NEXT MAJOR: 1.0 and 2.0, never the semver-0.x pseudo-major (0.17), which is what the caret gave and what expired 869 declarations. There is deliberately no pre-1.0 special case — see sigil-next-major-string in (sigil package) for David's 2026-08-25 reasoning (1.0 is a flag day). (supports max: "0.20") → (0 20 0) a MEASURED ceiling grandfathered "^0.16" → (1 0 0) read as its stripped bare form (the caret tolerance, decisions/2026-08-25-sigil-caret- grandfather); a 1.0 ceiling cannot bind below 1.0, so this changes nothing until the flag day — at which point an old copy declaring ^0.16 compiled against sigil 1.x is honestly incompatible. other operators → #f floor-only leniency: old release manifests are frozen artifacts, and enforcing their declared ceilings is exactly the expired-declaration problem this design removes.

Does the RESOLVED Sigil version satisfy a sigil: declaration? The one comparison, used by host validation's callers, the coherence check's floor axis, and workspace member validation:

floor <= resolved (when a floor is derivable) resolved < ceiling (when a ceiling is enforceable — bare and supports values only; legacy operatored values are floor-only by design)

A value with neither bound ("*", unparseable) admits everything: refusing on an unreadable declaration is the ERA failure mode, and shape problems are the host validation's job.

Render a semver list back to a version string.

The bare replacement to suggest for an operatored sigil: value, or #f when none is derivable. For operators whose floor is the version they carry ("^0.16", ">=0.16", "=0.16", "~0.16", "v0.16") the replacement is the carried version text, so the suggestion matches what the author wrote ("0.16", not "0.16.0"). For ">" and compound ranges the floor is computed ("+1" exclusive bounds), so it is rendered in full. "*" and pure upper bounds have no derivable floor: the author must state the minimum they use.

Check if two version ranges can be satisfied by a single version. Returns #t if compatible, #f if conflicting.

Check whether two range strings have any overlapping versions. Convenience wrapper around ranges-compatible? that takes range strings (not parsed records). Returns #t when the ranges share at least one valid version, #f otherwise. Returns #f if either string fails to parse.

Used by the Sigil-version validation path: a host's sigil: range vs. a dependency's sigil: range — if they don't intersect, the dep doesn't support a Sigil version the host requires.

Check if two resolved versions for the same package conflict. Returns #f if they're the same, or a conflict description string.

Verify that the RESOLVED Sigil version satisfies a dependency's declared floor. Returns #t when resolved >= declared (or the dep declares nothing to compare), raises a clean diagnostic naming both sides otherwise.

REDEFINED 2026-08-25 (decisions/2026-08-25-sigil-field-is-a-floor). The previous definition — never wired into any validation since the 0.14.0 redesign (t-dc5d) — tested whether two declared RANGES intersect. That predicate measured declaration era, not compatibility: by it the monorepo could not build itself (20 members at ^0.17, 5 at ^0.18.6, with cross-deps between the two groups) while demonstrably building fine. The floor axis of the coherence check ((sigil deps coherence), axis D) is the wired production form of this comparison; this helper is the raising form for callers that want a hard stop.

resolved-version the Sigil version this project actually resolved (a version string, e.g. from the lock) dep-info (list name version sigil-field-value)

semver-majorvariable

(No description)

semver-minorvariable

(No description)

semver-patchvariable

(No description)

semver<?variable

(No description)

semver>?variable

(No description)

semver=?variable

(No description)

semver<=?variable

(No description)

semver>=?variable

(No description)