Cargo: Fix transitive dev-dependency classification#1692
Cargo: Fix transitive dev-dependency classification#1692
Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bf1a46b to
e999111
Compare
…ssification Instead of labeling each transitive dependency based on the kind of the single edge pointing at it, propagate labels from workspace roots: BFS from production roots (normal-kind direct deps) labels reachable packages as Production, BFS from development roots (dev/build-kind direct deps) labels them as Development. This fixes transitive deps in dev-dep subtrees being mislabeled as Production when intermediate edges happen to be normal-kind. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add two test cases: 1. Transitive deps of dev-deps (approx->num-traits->autocfg) are all labeled Development, while prod deps (itoa) remain Production. 2. A dep reachable from both prod and dev roots gets both labels. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the explicit reachability BFS with workspace-member edge labeling plus hydrateDepEnvs propagation. Same correctness fix as before — transitive deps reached only via dev-dep roots are labeled Development, multi-label preserved — but relies on the existing graph hydration helper instead of bespoke adjacency/BFS code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Promote workspaceMembers from [PackageId] to Set PackageId so the per-node membership check is O(log n) instead of O(n). Also expand the kindToLabel comment to document the "build"/"dev"/null mapping. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous hydrateDepEnvs-based approach propagated Production
downward through any edge, which mislabeled build-script utilities
(autocfg, cc, version_check, etc.) as Production whenever they
appeared as [build-dependencies] of a normal dep. Verified on sparkle
(+16 false Production deps pre-fix) and ficus (+5 pre-fix).
Replace with two explicit reachability passes:
* Production: reachable from workspace members via null-kind edges
only. A build or dev edge breaks the release chain.
* Development: anything reachable (via any edge) from the target of
a non-null-kind edge — the build/dev subtree and its transitives.
Cargo does not resolve transitive dev-dependencies (only workspace-
member dev-deps), so the only non-null kind on non-workspace edges is
"build". Comment captures this so future readers know why we don't
need a separate propagation rule for dev.
Adds two regression tests covering build-dep subtrees and the multi-
label case where a dep is reachable via both a prod-normal path and
a prod→build edge.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per CLAUDE.md style guide, avoid list comprehensions. Refactor the reachability adjacency builders to use map/filter/concatMap, and factor the edge-kind predicates into isProdEdge/isDevEdge helpers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Other strategies (pnpm, yarn) propagate EnvProduction/EnvDevelopment via the shared hydrateDepEnvs helper. Cargo cannot, because hydrateDepEnvs has no edge-kind awareness and would incorrectly tag packages reached only through "dev"- or "build"-kind edges as Production. Call this out explicitly so future readers don't "simplify" back to hydration and reintroduce the bug this PR fixes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8b058a0 to
9f6664f
Compare
Rebase onto master picked up the 3.17.2 release section. Consolidate the Cargo entry there (3.17.2 is not yet released) and remove the duplicate 3.17.1 heading. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9f6664f to
6bf3943
Compare
Per hlint suggestion — single traversal instead of two. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WalkthroughThis pull request refactors the Cargo strategy to correctly classify dependencies reachable only via dev-dependencies or build-dependencies as "Development" rather than "Production". The implementation replaces per-edge labeling with a graph reachability analysis approach. It introduces adjacency maps to distinguish production-only paths from paths including development dependencies, computes reachable node sets for each classification, and labels packages based on their membership in these sets. A package can receive both labels when reachable via both path types. Test coverage is added for transitive dev-dependency labeling scenarios and packages with dual reachability paths. 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/Strategy/Cargo.hs`:
- Around line 560-568: The function reachable uses guarded equations in the
helper go (the clause "go visited (x : xs) | Set.member x visited = ..." and the
otherwise branch); replace that guard-based pattern with a single if-then-else
expression inside the "go visited (x : xs)" equation: compute children as
before, then use if Set.member x visited then go visited xs else go (Set.insert
x visited) (children ++ xs). Keep the same names (reachable, go, visited,
children) and preserve semantics and types.
In `@test/Cargo/MetadataSpec.hs`:
- Around line 209-340: Add a new test in MetadataSpec (near
devDepTransitiveLabelingSpec) that constructs a multi-member workspace using
CargoMetadata with two workspace members (e.g., member-a and member-b) where
member-a has a NodeDependency on member-b and member-b depends on an external
crate; call buildGraph False meta and assert that the external crate appears in
expectDeps/expectDirect but the internal workspace member (member-b) does not
surface as an external dependency. Use the existing helpers/constructors from
the file (PackageId, mkPkgId, ResolveNode, NodeDependency, devKind/nullKind,
mkDep, expectDeps, expectDirect) so the test covers the regression of labeling
before shrinkRoots.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 86ddddd6-6146-422b-9f41-2800c9ccba59
📒 Files selected for processing (3)
Changelog.mdsrc/Strategy/Cargo.hstest/Cargo/MetadataSpec.hs
| reachable :: Map.Map PackageId [PackageId] -> Set PackageId -> Set PackageId | ||
| reachable adj = go Set.empty . Set.toList | ||
| where | ||
| go visited [] = visited | ||
| go visited (x : xs) | ||
| | Set.member x visited = go visited xs | ||
| | otherwise = | ||
| let children = fromMaybe [] (Map.lookup x adj) | ||
| in go (Set.insert x visited) (children ++ xs) |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
# Check if the file exists and examine the specific lines
if [ -f "src/Strategy/Cargo.hs" ]; then
echo "=== File exists. Checking lines 560-568 ==="
sed -n '560,568p' src/Strategy/Cargo.hs
else
echo "File not found at src/Strategy/Cargo.hs"
# Try to find it
find . -name "Cargo.hs" -type f 2>/dev/null | head -5
fiRepository: fossas/fossa-cli
Length of output: 461
🏁 Script executed:
# Get more context around lines 560-568
sed -n '555,575p' src/Strategy/Cargo.hsRepository: fossas/fossa-cli
Length of output: 765
🏁 Script executed:
# Check the overall structure of the file to understand imports and pattern
head -100 src/Strategy/Cargo.hs | tail -50Repository: fossas/fossa-cli
Length of output: 1368
🏁 Script executed:
# Search for any Haskell coding guidelines in the repo
find . -type f \( -name "*.md" -o -name "*.txt" -o -name "GUIDELINES*" -o -name ".editorconfig" \) | xargs grep -l "match guard\|pattern guard\|avoid.*guard" 2>/dev/null | head -10Repository: fossas/fossa-cli
Length of output: 106
🏁 Script executed:
# Check the coding guidelines files
cat ./CLAUDE.mdRepository: fossas/fossa-cli
Length of output: 1497
🏁 Script executed:
# Also check the style guide
cat ./docs/contributing/STYLE-GUIDE.mdRepository: fossas/fossa-cli
Length of output: 12249
🏁 Script executed:
# Verify the proposed fix would be syntactically correct by checking if similar patterns exist elsewhere
rg "if.*then.*else" src/Strategy/Cargo.hs | head -10Repository: fossas/fossa-cli
Length of output: 42
Replace the guarded equations in reachable with an if-then-else expression.
This code uses match guards, which the repository explicitly avoids per the style guide. Replace with conditional expressions as shown below.
♻️ Proposed fix
reachable :: Map.Map PackageId [PackageId] -> Set PackageId -> Set PackageId
reachable adj = go Set.empty . Set.toList
where
go visited [] = visited
- go visited (x : xs)
- | Set.member x visited = go visited xs
- | otherwise =
- let children = fromMaybe [] (Map.lookup x adj)
- in go (Set.insert x visited) (children ++ xs)
+ go visited (x : xs) =
+ if Set.member x visited
+ then go visited xs
+ else
+ let children = fromMaybe [] (Map.lookup x adj)
+ in go (Set.insert x visited) (children ++ xs)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| reachable :: Map.Map PackageId [PackageId] -> Set PackageId -> Set PackageId | |
| reachable adj = go Set.empty . Set.toList | |
| where | |
| go visited [] = visited | |
| go visited (x : xs) | |
| | Set.member x visited = go visited xs | |
| | otherwise = | |
| let children = fromMaybe [] (Map.lookup x adj) | |
| in go (Set.insert x visited) (children ++ xs) | |
| reachable :: Map.Map PackageId [PackageId] -> Set PackageId -> Set PackageId | |
| reachable adj = go Set.empty . Set.toList | |
| where | |
| go visited [] = visited | |
| go visited (x : xs) = | |
| if Set.member x visited | |
| then go visited xs | |
| else | |
| let children = fromMaybe [] (Map.lookup x adj) | |
| in go (Set.insert x visited) (children ++ xs) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Strategy/Cargo.hs` around lines 560 - 568, The function reachable uses
guarded equations in the helper go (the clause "go visited (x : xs) | Set.member
x visited = ..." and the otherwise branch); replace that guard-based pattern
with a single if-then-else expression inside the "go visited (x : xs)" equation:
compute children as before, then use if Set.member x visited then go visited xs
else go (Set.insert x visited) (children ++ xs). Keep the same names (reachable,
go, visited, children) and preserve semantics and types.
| devDepTransitiveLabelingSpec :: Test.Spec | ||
| devDepTransitiveLabelingSpec = | ||
| Test.describe "dev-dep transitive labeling" $ do | ||
| Test.it "transitive deps of dev-deps are labeled Development" $ do | ||
| let wsId = PackageId "myapp" "0.1.0" "path+file:///path/to/myapp" | ||
| itoaId = mkPkgId "itoa" "1.0.18" | ||
| approxId = mkPkgId "approx" "0.5.1" | ||
| numTraitsId = mkPkgId "num-traits" "0.2.19" | ||
| autocfgId = mkPkgId "autocfg" "1.4.0" | ||
|
|
||
| wsNode = | ||
| ResolveNode | ||
| wsId | ||
| [ NodeDependency itoaId [nullKind] | ||
| , NodeDependency approxId [devKind "dev"] | ||
| ] | ||
| approxNode = | ||
| ResolveNode | ||
| approxId | ||
| [ NodeDependency numTraitsId [nullKind] | ||
| ] | ||
| numTraitsNode = | ||
| ResolveNode | ||
| numTraitsId | ||
| [ NodeDependency autocfgId [devKind "build"] | ||
| ] | ||
| itoaNode = ResolveNode itoaId [] | ||
| autocfgNode = ResolveNode autocfgId [] | ||
|
|
||
| meta = CargoMetadata [] [wsId] $ Resolve [wsNode, approxNode, numTraitsNode, itoaNode, autocfgNode] | ||
| graph = buildGraph False meta | ||
|
|
||
| itoaDep = mkDep "itoa" "1.0.18" CargoType [EnvProduction] | ||
| approxDep = mkDep "approx" "0.5.1" CargoType [EnvDevelopment] | ||
| numTraitsDep = mkDep "num-traits" "0.2.19" CargoType [EnvDevelopment] | ||
| autocfgDep = mkDep "autocfg" "1.4.0" CargoType [EnvDevelopment] | ||
|
|
||
| expectDeps [itoaDep, approxDep, numTraitsDep, autocfgDep] graph | ||
| expectDirect [itoaDep, approxDep] graph | ||
|
|
||
| Test.it "dep reachable from both prod and dev roots gets both labels" $ do | ||
| let wsId = PackageId "myapp" "0.1.0" "path+file:///path/to/myapp" | ||
| aId = mkPkgId "A" "1.0.0" | ||
| bId = mkPkgId "B" "1.0.0" | ||
| cId = mkPkgId "C" "1.0.0" | ||
|
|
||
| wsNode = | ||
| ResolveNode | ||
| wsId | ||
| [ NodeDependency aId [nullKind] | ||
| , NodeDependency bId [devKind "dev"] | ||
| ] | ||
| aNode = ResolveNode aId [NodeDependency cId [nullKind]] | ||
| bNode = ResolveNode bId [NodeDependency cId [nullKind]] | ||
| cNode = ResolveNode cId [] | ||
|
|
||
| meta = CargoMetadata [] [wsId] $ Resolve [wsNode, aNode, bNode, cNode] | ||
| graph = buildGraph False meta | ||
|
|
||
| aDep = mkDep "A" "1.0.0" CargoType [EnvProduction] | ||
| bDep = mkDep "B" "1.0.0" CargoType [EnvDevelopment] | ||
| cDep = mkDep "C" "1.0.0" CargoType [EnvProduction, EnvDevelopment] | ||
|
|
||
| expectDeps [aDep, bDep, cDep] graph | ||
| expectDirect [aDep, bDep] graph | ||
|
|
||
| Test.it "direct dep declared as both normal and dev gets both labels" $ do | ||
| -- Cargo emits multiple NodeDepKinds when a dep is declared in both | ||
| -- [dependencies] and [dev-dependencies] on the same package. | ||
| let wsId = PackageId "myapp" "0.1.0" "path+file:///path/to/myapp" | ||
| dualId = mkPkgId "dual" "1.0.0" | ||
| transId = mkPkgId "trans" "1.0.0" | ||
|
|
||
| wsNode = ResolveNode wsId [NodeDependency dualId [nullKind, devKind "dev"]] | ||
| dualNode = ResolveNode dualId [NodeDependency transId [nullKind]] | ||
| transNode = ResolveNode transId [] | ||
|
|
||
| meta = CargoMetadata [] [wsId] $ Resolve [wsNode, dualNode, transNode] | ||
| graph = buildGraph False meta | ||
|
|
||
| dualDep = mkDep "dual" "1.0.0" CargoType [EnvProduction, EnvDevelopment] | ||
| transDep = mkDep "trans" "1.0.0" CargoType [EnvProduction, EnvDevelopment] | ||
|
|
||
| expectDeps [dualDep, transDep] graph | ||
| expectDirect [dualDep] graph | ||
|
|
||
| Test.it "build-deps of prod-deps and their transitives are Development" $ do | ||
| -- Regression guard: build-script utilities (autocfg, cc, version_check, | ||
| -- etc.) are declared as [build-dependencies] of normal deps. They are | ||
| -- compiled during the build but never linked into the release artifact, | ||
| -- so they should be labeled Development, not Production — even though | ||
| -- their parent is a production dep. | ||
| let wsId = PackageId "myapp" "0.1.0" "path+file:///path/to/myapp" | ||
| serdeId = mkPkgId "serde" "1.0.0" | ||
| autocfgId = mkPkgId "autocfg" "1.0.0" | ||
| autocfgChildId = mkPkgId "autocfg-child" "1.0.0" | ||
|
|
||
| wsNode = ResolveNode wsId [NodeDependency serdeId [nullKind]] | ||
| serdeNode = ResolveNode serdeId [NodeDependency autocfgId [devKind "build"]] | ||
| autocfgNode = ResolveNode autocfgId [NodeDependency autocfgChildId [nullKind]] | ||
| childNode = ResolveNode autocfgChildId [] | ||
|
|
||
| meta = CargoMetadata [] [wsId] $ Resolve [wsNode, serdeNode, autocfgNode, childNode] | ||
| graph = buildGraph False meta | ||
|
|
||
| serdeDep = mkDep "serde" "1.0.0" CargoType [EnvProduction] | ||
| autocfgDep = mkDep "autocfg" "1.0.0" CargoType [EnvDevelopment] | ||
| childDep = mkDep "autocfg-child" "1.0.0" CargoType [EnvDevelopment] | ||
|
|
||
| expectDeps [serdeDep, autocfgDep, childDep] graph | ||
| expectDirect [serdeDep] graph | ||
|
|
||
| Test.it "dep reachable via a normal prod path and a build-edge gets both labels" $ do | ||
| -- X is a direct normal dep of the workspace (Production) AND a build-dep | ||
| -- of a prod dep (Development via the build-edge split). Both labels | ||
| -- should be applied. | ||
| let wsId = PackageId "myapp" "0.1.0" "path+file:///path/to/myapp" | ||
| aId = mkPkgId "A" "1.0.0" | ||
| xId = mkPkgId "X" "1.0.0" | ||
|
|
||
| wsNode = ResolveNode wsId [NodeDependency aId [nullKind], NodeDependency xId [nullKind]] | ||
| aNode = ResolveNode aId [NodeDependency xId [devKind "build"]] | ||
| xNode = ResolveNode xId [] | ||
|
|
||
| meta = CargoMetadata [] [wsId] $ Resolve [wsNode, aNode, xNode] | ||
| graph = buildGraph False meta | ||
|
|
||
| aDep = mkDep "A" "1.0.0" CargoType [EnvProduction] | ||
| xDep = mkDep "X" "1.0.0" CargoType [EnvProduction, EnvDevelopment] | ||
|
|
||
| expectDeps [aDep, xDep] graph | ||
| expectDirect [aDep, xDep] graph |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Add a multi-member workspace regression case.
These scenarios all use a single workspace root. Since buildGraph now labels workspace members before shrinkRoots, please add a member-a -> member-b -> dep case to ensure internal workspace crates still do not surface as dependencies.
🧪 Suggested coverage
Test.it "dep reachable via a normal prod path and a build-edge gets both labels" $ do
-- X is a direct normal dep of the workspace (Production) AND a build-dep
-- of a prod dep (Development via the build-edge split). Both labels
-- should be applied.
@@
expectDeps [aDep, xDep] graph
expectDirect [aDep, xDep] graph
+
+ Test.it "workspace members do not surface as dependencies" $ do
+ let appId = PackageId "app" "0.1.0" "path+file:///path/to/app"
+ libId = PackageId "lib" "0.1.0" "path+file:///path/to/lib"
+ depId = mkPkgId "dep" "1.0.0"
+
+ appNode = ResolveNode appId [NodeDependency libId [nullKind]]
+ libNode = ResolveNode libId [NodeDependency depId [nullKind]]
+ depNode = ResolveNode depId []
+
+ meta = CargoMetadata [] [appId, libId] $ Resolve [appNode, libNode, depNode]
+ graph = buildGraph False meta
+
+ dep = mkDep "dep" "1.0.0" CargoType [EnvProduction]
+ libDep = mkDep "/path/to/lib" "0.1.0" UnresolvedPathType [EnvProduction]
+
+ expectDeps [dep] graph
+ expectDirect [dep] graph
+ Graphing.vertexList graph `shouldSatisfy` notElem libDepAs per coding guidelines, **/*.{hs,rs}: Code should be thoroughly tested with appropriate unit tests.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/Cargo/MetadataSpec.hs` around lines 209 - 340, Add a new test in
MetadataSpec (near devDepTransitiveLabelingSpec) that constructs a multi-member
workspace using CargoMetadata with two workspace members (e.g., member-a and
member-b) where member-a has a NodeDependency on member-b and member-b depends
on an external crate; call buildGraph False meta and assert that the external
crate appears in expectDeps/expectDirect but the internal workspace member
(member-b) does not surface as an external dependency. Use the existing
helpers/constructors from the file (PackageId, mkPkgId, ResolveNode,
NodeDependency, devKind/nullKind, mkDep, expectDeps, expectDirect) so the test
covers the regression of labeling before shrinkRoots.
Style guide avoids match guards. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Overview
The cargo strategy was labeling transitive dependencies by the kind of the single edge directly pointing at them. This caused deps reachable only via a dev-dep subtree to be reported as
Productionwhenever the intermediate edges happened to be normal-kind — for exampleworkspace → approx (dev) → num-traits (normal)leakednum-traitsinto Production.The fix in
src/Strategy/Cargo.hsreplaces per-edge labeling with two edge-kind-aware reachability passes over the resolved graph:"build"or"dev"edge breaks the release chain, so its target (and everything transitively below) is not Production via that path.A package reachable by both kinds of paths carries both labels —
LabeledGrapheralready supports multi-labels, so no structural changes were needed.Why we don't need a separate rule for transitive dev-deps
Cargo only resolves
[dev-dependencies]for workspace members. Dev-deps declared inside a non-workspace crate are not part ofcargo metadata'sresolvegraph, so the only non-null kind we ever see on a non-workspace edge is"build". The reachability rule above handles it uniformly.Acceptance criteria
Development(previously leaked toProductionwhen intermediate edges were normal-kind).autocfg,cc,version_check) and their transitives are labeledDevelopment— they're needed to compile the project but are never linked into the release artifact.Testing plan
Minimal reproduction
The
Cargo.tomlincludes two dependencies.itoahas no transitive deps. It is included as a prod dependency, so it should be the only production dependency in the graph.approxis being included as a dev dependency. It has two transitive dependencies:approxrequiresnum-traitsas a normal dependency, andnum-traitsrequiresautocfgas a build-script dependency.Before this fix,
fossa analyzereported bothitoaandnum-traitsas production dependencies. After the fix, it correctly reports onlyitoaas a production dependency.Create
/tmp/cargo-devdep-test/Cargo.toml:Create
/tmp/cargo-devdep-test/src/lib.rs:Generate the lockfile:
Run fossa (released) and fossa-dev (this branch):
Compare the two outputs. The output from
fossa-devonly includescargo+itoa. The output fromfossaincludes bothcargo+itoaandcargo+num-traits.Run again with
--include-unused-deps. All four deps appear in the graph; the difference is thatapprox,num-traits, andautocfgare now labeled Development instead of leaking into Production.Real-world verification: sparkle and ficus
This is where the build-dep correctness is visible — the minimal
num-traits-style repro above doesn't differentiate the fix from releasedfossa(both classifyautocfgas Development, just by different paths). The sparkle/ficus scans are the evidence that the fix handles the build-dep case at scale without regressing any Production deps.I scanned both projects with released
fossa(3.16.6) andfossa-devfrom this branch, then diffedsourceUnits.Build.Dependencies:All movement is Production → Development — no deps incorrectly leaked the other direction. The reclassified deps are exactly the categories you'd expect:
proptest,wiremock,tokio-test,axum-test,simple_test_case,test-log,tracing-test-macro,assert-json-diffautocfg,cc,cfg_aliases,pkg-config,rustc_version,vcpkg,version_check,jobserver,shlex,find-msvc-toolsxshell,xshell-macros,cargo_metadata,cargo-platform,env_logger,env_filter,tempfilewinapi-i686-pc-windows-gnu,winapi-x86_64-pc-windows-gnu,hermit-abiAutomated coverage
New
MetadataSpeccases:Risks
Correctness fix with a visible behavior change: Production dep sets will shrink for any project that has dev-dep or build-dep trees with non-trivial transitive closures. This was the intent — previously those trees were partially leaking into Production. Flagged in the changelog. No deps move in the opposite direction (Dev → Prod), so there's no risk of over-reporting.
Metrics
Not tracked.
References
cargo-dev-prod-labeling-fix.mdChecklist
docs/.docs/README.msand gave consideration to how discoverable or not my documentation is.Changelog.md. If this PR did not mark a release, I added my changes into an## Unreleasedsection at the top..fossa.ymlorfossa-deps.{json.yml}, I updateddocs/references/files/*.schema.jsonAND I have updated example files used byfossa initcommand. You may also need to update these if you have added/removed new dependency type (e.g.pip) or analysis target type (e.g.poetry).docs/references/subcommands/<subcommand>.md.