Skip to content

Dependency layers: the org has 14 real version divergences and only one of them is a decision #192

Description

@avrabe

Measured across the org on 2026-09-24: 43 public non-archived repos, 24 Rust, 18 first-party. Twelve declare workspace dependencies. Of 37 crates shared by two or more repos:

identical spec 12
cosmetic divergence ("1" vs "1.0" — same resolution) 11
real divergence (different compatible-version line) 14

Seven of the fourteen are one cluster — the WebAssembly toolchain:

wasmparser     loom=0.259  meld=0.246  scry=0.252  synth=0.256  witness=0.258
wasm-encoder   loom=0.259  meld=0.246  synth=0.258
wasmprinter    loom=0.259  meld=0.246
wit-component  loom=0.259  synth=0.258
wasmtime       rivet=47.0.4  scry=47  witness=48
wasmtime-wasi  rivet=47.0.4  scry=47  witness=48
wit-bindgen    kiln=0.41.0  relay=0.62.0

Also real, outside that cluster: sha2 0.10 (×4) vs 0.11 (×2) — a breaking digest-trait change; axum 0.7/0.8; reqwest 0.11/0.12; gimli 0.31/0.34; thiserror 1.0/2.x; rowan (rivet on a fork branch, spar on crates.io).

The defect is not the divergence — it is that you cannot tell drift from a decision

I read the comment context around 13 divergent pins. Exactly one carries a real reason:

relay  proptest  ">=1.7, <1.12"
       "--locked -> it picked 1.11.0 and Windows CI failed. 1.11.0 is CONFIRMED
        still broken; keep <1.11 until a fixed 1.11.x is verified."

That is a legitimate, evidenced hold. The other twelve are drift wearing a comment:

meld     wasmparser   "# WebAssembly parsing and encoding"   <- says what the crate IS, not why 0.246
loom     wasmparser   "# WebAssembly parsing and encoding"   <- same text, four versions newer
synth    wasmparser   "# WebAssembly tooling"
witness  wasmparser   <no comment>
witness  sha2         <no comment>
kiln     wit-bindgen  <no comment>
relay    wit-bindgen  <no comment>

Two repos carry the same comment at different versions. The wasmtime comments turn out to describe unrelated things (a fork pin being dropped, error handling) that merely sit above the line.

So the cost is not the version skew itself. It is that relay's real hold is indistinguishable from twelve cases of nobody-looked, and nothing anywhere records which is which.

varve already solved half of this, at the wrong level

varve-realms.toml carries a realm for exactly these tools:

# The realm that carries the bytecodealliance component-model tools —
# wasm-tools, wac, wkg, wit-bindgen-wrpc.
[realm.pulseengine-wasm]

wasm-tools the CLI is pinned, signed, and has a recorded trust decision — including the operator's reason for the three payloads ingested with no proof of origin. wasmparser the crate, from that same upstream release train, is at five versions across five repos with no decision recorded anywhere.

The layer already knows the right number. It does not reach Cargo.toml.

Proposal: a dependency layer, and cargo as a varve-provided tool

Layers as a dated timeline, not a forced pin. A layer carries a version set; each repo declares which layer it is on; being behind is legal and expected. What changes is that the gap becomes legible and dated — "meld is four layers back" — instead of invisible. The slowest repo still pins low, and still sees the others evolve.

A repo holding a crate below its own layer must declare it, with a reason. relay's proptest becomes a first-class declared exception; the twelve silent ones become failures until someone either upgrades or writes down why.

This is a pattern that already runs in rivet under another name: KNOWN_DIVERGENCES in the parser differential test — an undeclared divergence fails, a declared one carries its justification, and the list is expected to shrink. Absence of an entry is a failure, not a pass.

Packing cargo into varve is what makes it enforceable. A varve-supplied cargo that exits 1 when Cargo.toml diverges from the layer without a declared exception makes drift impossible to do silently — enforcement at every invocation, not just in CI. That is varve's existing idiom: a refusal is the feature.

One honest limit, stated up front

This cannot rewrite resolution, and should not pretend to. [patch.crates-io] requires semver compatibility, and 0.246 vs 0.259 are incompatible 0.x lines, so patching cannot silently unify what has already diverged. Repos still edit their own Cargo.toml. varve's job is to make the gap visible, dated and non-silent; the edit stays local. A tool that quietly changed which wasmparser you linked would be worse than the drift it fixed.

Scope it to clusters, not to everything

The wasm set must move together because wasmparser/wasm-encoder/wasmprinter/wit-component share types and a component-model binary format — that is where skew stops being cosmetic. anyhow does not need an org-wide pin and never will. Pinning everything would make the slowest repo gate everyone, which is the failure mode this exists to end.

Free benefit

Pinning cargo/rustc themselves kills a failure class the org has already hit: CI's @stable running a Rust version ahead of local, so a clean local clippy still fails CI on a newly-tightened lint.

Why now

RUSTSEC-2026-0308 (use-after-free through safe Rust APIs in salsa) was published on 2026-09-24 and affects every PulseEngine repo that links salsa. rivet assessed it — not applicable there, because rivet declares no interned struct and no tracked function takes a non-salsa argument — and that assessment lives in rivet's deny.toml, where no sibling repo can see it. Nobody did spar's. One advisory should produce one record, consumed N times; today it produces N assessments or none.

That is the duplicate work this proposal removes.


Measured and filed by Claude Opus 5 via Claude Code — https://claude.ai/code/session_015HMQUV3u86jN2hmCtXNTc9

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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