Skip to content

feat(fetch): report a dependency pinned to an exact version rather than leaving it unknown #107

Description

@justin13888

A tree/TUI node built from a manifest carries no version, because a manifest declares a constraint and nothing resolved it. That is the right answer for a range — ^1.2 names a set of versions, not one — and #96 / #104 make the tool say unknown rather than guess.

But a narrow subset of declared constraints is not a range at all:

  • a *.csproj <PackageReference Include="Foo" Version="1.2.3" /> — NuGet's bare Version is a minimum, but in practice with a lockfile-less restore it is the version that lands
  • a Gradle version catalog pin, foo = { module = "g:a", version = "1.2.3" }
  • any ecosystem where the declared string parses cleanly as an exact semver::Version rather than a VersionReq with more than one member

For those, Node::version = None understates what the manifest actually said: there is exactly one version the constraint admits, and reporting it would give a correct freshness verdict where today the row reads unknown.

Why this was deliberately left out of #104

#96 is about not fabricating a verdict from an unread version, and #104 fixes that by making an unread version unrepresentable. The exact-pin case is the opposite problem — a version that was read and is being discarded — and the "a constraint is not a resolution" argument that justifies None for ^1.2 does not cover 1.2.3. Folding it into #104 would have widened that PR past its issue, so it is filed here instead.

What needs deciding

  1. Which ecosystems' exact pins count. NuGet's Version="1.2.3" is formally [1.2.3,), so treating it as resolved is a judgement call about what users mean, not a parse fact.
  2. Where the narrowing lives. dependable-core already parses constraints; the natural shape is a helper that answers "does this constraint admit exactly one version, and which?" and is used by direct_graph / shallow_graph in crates/dependable-fetch/src/tree.rs.
  3. Whether the resulting node is distinguishable from a lockfile-resolved one. A pin is a declaration, and the TUI currently draws no distinction between "resolved" and "declared and unambiguous".

Out of scope

Ranges of any kind, dist-tags, and git/path references. Those stay unknown.

Split out of #104's review; see decision (e) there.

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

    coredependable-core (pure types/parsers/semver)enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions