dependable-tui's package lookup evaluates freshness with check_version on
untranslated strings on both sides of the comparison: the current version it
was given, and the whole list of versions the registry published. The CLI path
translates both before comparing and translates the answer back afterwards. For
Python, C#, and the JVM the two paths therefore answer different questions about
the same package.
Found while reviewing #120 (feat/107-exact-pin-version), which is where the
consequence surfaced. It is pre-existing and separate from that PR's own
defect, which was in exact_pin's guard; #120 fixes the guard and does not
touch this.
Mechanism
crates/dependable-tui/src/data.rs, in lookup:
match checker.fetch_versions(ecosystem, name).await {
Ok(versions) => {
let evaluation = check_version("*", &versions, Some(version));
versions is whatever the registry served, in its own dialect, and version is
the Node::version off the selected row. check_version
(crates/dependable-core/src/semver/checker.rs) does:
let mut parsed: Vec<Version> = versions.iter().filter_map(|v| Version::parse(v).ok()).collect();
...
let locked = locked_at.and_then(|s| Version::parse(s).ok());
so any string in either position that is not strict semver is silently
dropped, not reported.
Compare evaluate_item in crates/dependable-fetch/src/check.rs, which for the
same inputs runs to_semver_versions over the list (per-ecosystem:
pep440_to_semver, nuget_to_semver, maven_to_semver), runs
to_semver_constraint over the constraint, compares in semver, and then maps the
reported versions back to their registry-native spelling through native_for /
in_native_versions — with a comment explaining precisely why the round-trip
matters ("several natives can translate to one semver").
Consequences, both directions
Why it is worth filing rather than fixing in passing
The machinery the TUI needs is private to dependable-fetch:
to_semver_versions, in_native_versions, and native_for are all fn, not
pub fn, inside check.rs. Closing this is therefore a choice between two real
options, and the choice is the substance of the issue:
- Publish it. Expose a translating evaluation entry point from
dependable-fetch (something like evaluate_versions(ecosystem, &versions, current)) and have both the CLI and the TUI call it. Correct by construction,
but it is a public-API commitment for a crate whose surface is the documented
integration point for external consumers.
- Duplicate it. Reimplement the translate/compare/map-back sequence in
data.rs. No API commitment, and two copies of a lossy translation that
already needed a long comment to explain why the mapping back is by parsed
equality rather than by text — they will drift.
Whichever is chosen, it should be one call, not a patch to the current-version
side alone: both sides of the comparison are affected, and repairing one leaves
the other.
Acceptance
- A TUI-level test over a JVM (or NuGet, or PEP 440) package whose published list
contains versions that are not strict semver asserts the pane reports the
registry's real newest release.
- A TUI-level test asserts a current version in the ecosystem's own dialect is
compared as that ecosystem reads it, rather than dropped.
- The decision between publishing and duplicating is recorded in the PR that
closes this.
Related: #96 (the same false ok from a version never read), #113 (the CLI-side
NuGet reading), #120 (where this was found).
dependable-tui's package lookup evaluates freshness withcheck_versiononuntranslated strings on both sides of the comparison: the current version it
was given, and the whole list of versions the registry published. The CLI path
translates both before comparing and translates the answer back afterwards. For
Python, C#, and the JVM the two paths therefore answer different questions about
the same package.
Found while reviewing #120 (
feat/107-exact-pin-version), which is where theconsequence surfaced. It is pre-existing and separate from that PR's own
defect, which was in
exact_pin's guard; #120 fixes the guard and does nottouch this.
Mechanism
crates/dependable-tui/src/data.rs, inlookup:versionsis whatever the registry served, in its own dialect, andversionisthe
Node::versionoff the selected row.check_version(
crates/dependable-core/src/semver/checker.rs) does:so any string in either position that is not strict semver is silently
dropped, not reported.
Compare
evaluate_itemincrates/dependable-fetch/src/check.rs, which for thesame inputs runs
to_semver_versionsover the list (per-ecosystem:pep440_to_semver,nuget_to_semver,maven_to_semver), runsto_semver_constraintover the constraint, compares in semver, and then maps thereported versions back to their registry-native spelling through
native_for/in_native_versions— with a comment explaining precisely why the round-tripmatters ("several natives can translate to one semver").
Consequences, both directions
4.11,4.12,4.13,4.13.1,4.13.2reaches the TUI'scheck_versionas five strings of whichonly two parse.
latest_availablebecomes4.13.2here only by luck; for anartifact whose newest releases are two-segment (or
6.4.4.Final, or afour-segment NuGet
8.0.32.1) the newest release is invisible and the panereports a stale "latest". If nothing parses the status is
Error("no parseable versions").version, so
currentfalls back tolatest_compatibleand the armSome(cur) if *cur >= latest_available => UpToDateis taken unconditionally —a green
okregardless of the registry. This is why feat(fetch): report a dependency pinned to an exact version rather than leaving it unknown #120's guard defectproduced a false
okrather than an error, and it is the same shape as fix(tui): a dependency whose version was never read renders as up to date #96 andfix(core): a bare NuGet Version reports UpToDate whatever the registry publishes #113.
Why it is worth filing rather than fixing in passing
The machinery the TUI needs is private to
dependable-fetch:to_semver_versions,in_native_versions, andnative_forare allfn, notpub fn, insidecheck.rs. Closing this is therefore a choice between two realoptions, and the choice is the substance of the issue:
dependable-fetch(something likeevaluate_versions(ecosystem, &versions, current)) and have both the CLI and the TUI call it. Correct by construction,but it is a public-API commitment for a crate whose surface is the documented
integration point for external consumers.
data.rs. No API commitment, and two copies of a lossy translation thatalready needed a long comment to explain why the mapping back is by parsed
equality rather than by text — they will drift.
Whichever is chosen, it should be one call, not a patch to the current-version
side alone: both sides of the comparison are affected, and repairing one leaves
the other.
Acceptance
contains versions that are not strict semver asserts the pane reports the
registry's real newest release.
compared as that ecosystem reads it, rather than dropped.
closes this.
Related: #96 (the same false
okfrom a version never read), #113 (the CLI-sideNuGet reading), #120 (where this was found).