Skip to content

edtf-postgres promises rust-version 1.82 and pgrx requires 1.96 #197

Description

@CarlAllenn

Defect

edtf publishes edtf-postgres promising rust-version = "1.82", inherited
from [workspace.package], and that promise is false against edtf's own
lockfile
. Measured 2026-08-24 on edtf@2c2a1ed with the belt from #813's
branch, at the pinned 1.82.0 toolchain:

$ RUSTUP_TOOLCHAIN=1.82.0 cargo check -p edtf-postgres --locked \
    --no-default-features --features pg18
error: failed to parse manifest at `.../serde_spanned-1.1.1/Cargo.toml`
  feature `edition2024` is required
  ... not stabilized in this version of Cargo (1.82.0)

Walking the toolchain up finds two more walls and then the real one.
1.85.0 hits icu_collections/icu_normalizer/icu_properties@2.2.0 requires rustc 1.86, reached through the same build-script closure. And
1.86.0 hits the one that actually decides it:

error: rustc 1.86.0 is not supported by the following packages:
  pgrx@0.19.2 requires rustc 1.96
  pgrx-bindgen@0.19.2 requires rustc 1.96
  pgrx-macros@0.19.2 requires rustc 1.96
  pgrx-pg-config@0.19.2 requires rustc 1.96
  pgrx-pg-sys@0.19.2 requires rustc 1.96
  pgrx-sql-entity-graph@0.19.2 requires rustc 1.96

So the floor is not an obscure transitive detail at all: pgrx, the
crate's own DIRECT dependency, declares rust-version = "1.96".

edtf-postgres promises 1.82 while the library it is built on promises
1.96 — a fourteen-minor gap between a published promise and the thing it
depends on, sitting in plain sight in a manifest nobody was comparing.
The other five members compile clean at 1.82.0; only the extension is
affected.

Why nothing caught it until now. lint:msrv (canon #820) verifies
every crate the gate compiles, and edtf-postgres was not one: it sat in
CLIPPY_EXCLUDE, outside the gate's compile surface, which is the whole
defect canon #813 exists to end. The moment #813's branch widens the
surface to the publish path's, the MSRV check reaches the crate and the
promise fails on its first run. That is the two issues working exactly as
intended, and it is the reason this is filed rather than quietly patched.

Scope of the claim, stated precisely. This is --locked: it says
edtf's own lockfile cannot build the extension at 1.82. A downstream
consumer resolving fresh on 1.82 might land on older icu/toml versions
and succeed, because cargo's resolver is MSRV-aware. The org verifies the
locked form deliberately — it is the only deterministic one, and it is what
every other Rust check in the belt measures — so "our lockfile builds at
our declared minimum" is the promise being kept, and it is currently not.

Decided build

The declaration moves to the truth; the truth is not moved to the
declaration.

  • edtf-postgres declares its own rust-version rather than
    inheriting the workspace's — the crate genuinely has a different
    dependency closure from the five pure-Rust members, and cargo supports
    a per-package value for exactly this.
  • The value is measured, not guessed: the lowest toolchain at which
    cargo check -p edtf-postgres --locked --no-default-features --features pgNN passes. That is 1.96, which is what pgrx itself declares —
    confirmed by running: cargo check at the 1.96.0 pin finishes
    clean (3m57s), and at 1.86.0 it does not.
  • The repository pins that toolchain beside the 1.82.0 one, and canon
    lint:msrv then verifies both minimums, each at its own pin — the
    multi-MSRV path that check already supports and that no repository had
    exercised until now. Patch-exact, so a declared 1.96 wants a 1.96.0 pin.
  • A standing consequence worth writing down: this crate's minimum is
    pgrx's minimum, so it moves whenever the pgrx pin moves. The
    agreement check turns that from a silent drift into a red Renovate PR
    with an obvious one-line remedy, which is the right place for it.
  • [workspace.package] rust-version stays 1.82 for the five members that
    genuinely meet it. Raising the whole workspace to 1.86 to make one crate
    honest would be a false promise in the other direction, tightening a
    minimum five published crates actually keep.

Rejected, recorded: pinning older icu/serde_spanned in Cargo.lock to
hold the 1.82 line — it is dependency surgery on a transitive closure the
repository does not own, to keep a number nobody chose deliberately;
edtf-postgres inherited 1.82 because inheritance was the default, not
because 1.82 was ever measured for it. Also rejected: excluding the
extension from lint:msrv, which is the exclusion that hid this for forty
releases wearing a different hat.

Canon consequence

None to the belt: lint:msrv and gate-surface.py already do the right
thing, and finding this is them doing it. This is edtf's repo-side change
and rides its canon pin bump for #813. release-lab is unaffected — its
declared minimum is its build toolchain, so its lab-pg meets it by
construction.

One thing the canon should note either way: this is the first repository
in the org to need two MSRV toolchains, so it is the first real
exercise of msrv-plan.py's per-declaration grouping, which until now was
covered only by table tests.

Done when

  • the floor is measured by running — done: 1.96.0 compiles the crate
    clean, 1.86.0 does not, and pgrx@0.19.2 declares 1.96 itself
  • edtf-postgres declares that floor and the repository pins it
  • mise run lint:msrv on edtf is green and its output names both
    minimums, each compiled at its own pinned toolchain
  • the five other members still declare and still meet 1.82 — planted by
    re-running after the change, not assumed
  • the crates.io page for the next edtf-postgres release shows the new
    minimum, since it is a public promise that changed

Sequencing

After canon #813 (which found it) and its edtf pin bump; the change lands
in the same repo-side commit if that is still open, otherwise its own.
Refs .github#813, .github#820, edtf#172.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions