What happens
crates/dependable-core/src/parsers/gradle_catalog.rs reads Gradle's rich-version keys at line 234:
for key in ["strictly", "require", "prefer"] {
and positioned() at line 287 hardcodes kind: DependencyKind::Normal, with a real, non-zero-width span.
strictly is Gradle's forced-version mechanism — the standard way a security pin on a transitive dependency is written in a version catalog. Classified as Normal, it is an ordinary rewritable dependency to fix.
The consequence
gradle/libs.versions.toml:
[versions]
junit = { strictly = "5.10.0" }
A plain dependable fix, no flags, advances it. Traced: fix.rs's forced binding is kind == DependencyKind::Override && !overrides, which is false here, so the edit is pushed and commit writes it. rewrite_constraint has no guard either — Jvm's bare_version() is Minimum, a three-component version is not partial and carries no wildcard or tilde, so it falls through to the rewrite.
git grep "DependencyKind::Override" over the parsers returns only package_json.rs. So the forced-version protection is enforced on the kind tag, which only npm-family manifests ever set — not on the semantics of a forced version. Cargo [patch]/[replace], pnpm-workspace.yaml overrides: and Composer replace/conflict are safe only by omission: no parser reads them at all. Gradle rich versions are the one case that is read, misclassified, and writable.
Why it is being filed now
Found reviewing PR #122, which adds fix --overrides and a README section stating that npm-family overrides/resolutions are "the only maps dependable treats this way; nothing in Cargo, Go, Python, or the rest declares one", and that fix never rewrites one by default, --all included.
The code defect is pre-existing — #122 does not touch gradle_catalog.rs. What #122 contributes is a published guarantee a user would act on. That PR narrows its README wording to the ecosystems the guarantee actually covers; this issue is the code half.
Direction
Give positioned() a kind, and classify a strictly version as DependencyKind::Override — it is a forced version by Gradle's own definition. require and prefer are not: require is a minimum and prefer is a soft preference, so both should stay Normal.
That distinction is currently unavailable, because version_literal collapses all three keys into one literal before the caller sees which key it came from. Separating them is the first step, and is worth doing regardless — a prefer and a strictly presently produce identical items, which also makes issue #107's exact-pin narrowing read a soft preference as a hard pin.
Worth deciding at the same time: whether DependencyKind::Override is the right tag for a non-npm forced version, or whether the guard in fix.rs should key on a broader "this version was forced" predicate that both npm overrides and Gradle strictly satisfy.
What happens
crates/dependable-core/src/parsers/gradle_catalog.rsreads Gradle's rich-version keys at line 234:and
positioned()at line 287 hardcodeskind: DependencyKind::Normal, with a real, non-zero-width span.strictlyis Gradle's forced-version mechanism — the standard way a security pin on a transitive dependency is written in a version catalog. Classified asNormal, it is an ordinary rewritable dependency tofix.The consequence
gradle/libs.versions.toml:A plain
dependable fix, no flags, advances it. Traced:fix.rs'sforcedbinding iskind == DependencyKind::Override && !overrides, which isfalsehere, so the edit is pushed andcommitwrites it.rewrite_constrainthas no guard either —Jvm'sbare_version()isMinimum, a three-component version is not partial and carries no wildcard or tilde, so it falls through to the rewrite.git grep "DependencyKind::Override"over the parsers returns onlypackage_json.rs. So the forced-version protection is enforced on the kind tag, which only npm-family manifests ever set — not on the semantics of a forced version. Cargo[patch]/[replace],pnpm-workspace.yamloverrides:and Composerreplace/conflictare safe only by omission: no parser reads them at all. Gradle rich versions are the one case that is read, misclassified, and writable.Why it is being filed now
Found reviewing PR #122, which adds
fix --overridesand a README section stating that npm-familyoverrides/resolutionsare "the only mapsdependabletreats this way; nothing in Cargo, Go, Python, or the rest declares one", and thatfixnever rewrites one by default,--allincluded.The code defect is pre-existing — #122 does not touch
gradle_catalog.rs. What #122 contributes is a published guarantee a user would act on. That PR narrows its README wording to the ecosystems the guarantee actually covers; this issue is the code half.Direction
Give
positioned()a kind, and classify astrictlyversion asDependencyKind::Override— it is a forced version by Gradle's own definition.requireandpreferare not:requireis a minimum andpreferis a soft preference, so both should stayNormal.That distinction is currently unavailable, because
version_literalcollapses all three keys into one literal before the caller sees which key it came from. Separating them is the first step, and is worth doing regardless — apreferand astrictlypresently produce identical items, which also makes issue #107's exact-pin narrowing read a soft preference as a hard pin.Worth deciding at the same time: whether
DependencyKind::Overrideis the right tag for a non-npm forced version, or whether the guard infix.rsshould key on a broader "this version was forced" predicate that both npm overrides and Gradlestrictlysatisfy.