Repository navigation
feat(modgraph): read the tool directives of the main go.mod - #57
Conversation
Build parsed the main go.mod with ParseLax, and ParseLax drops the tool lines. So the graph had no edge to the module of a tool. vet then read such a module as unused, but go tool builds and runs its code with the build list of the module. Build now parses the main go.mod as the go command does. It links each tool package as an import of the main module from go.mod. A tool that no module holds makes the main module open. Version is 2, because the same input gives another graph. The oracle test adds the tools of the go.mod to go list -deps. A synthetic test serves a tool module from a proxy on the disk and checks the graph against go list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm Signed-off-by: Claude <noreply@anthropic.com>
The oracle test named the tools of go.mod in the go list -deps -test call. -test applies to each named package, so go list gave the test imports of a tool of a dependency. The graph never reads the test files of a dependency, so the oracle could fail on an edge that no build has. The test now lists the tools in a second go list call with no -test. The case for a tool of the main module passed without the fix. It now names a package in testdata, which the walk of the main module skips. A test file of the main module also imports the tool, and the tool edge must not be test-only. A go.mod with a directive that the go command rejects is now an error, and a case pins it. The docs say that the graph of a workspace module holds only the tools of its own go.mod. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm Signed-off-by: Claude <noreply@anthropic.com>
SafeDep Report SummaryNo dependency changes detected. Nothing to scan. This report is generated by SafeDep GitHub App |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Parse go.mod and link tool imports modgraph/graph.go, docs/modgraph.md |
Build uses strict parsing, records main-module tool directives as imports with go.mod evidence, and links them from the main module. The graph version changes to 2. The documentation describes tool directives and workspace graph scope. |
Check tool graph behavior modgraph/graph_test.go, modgraph/buildlist_test.go, modgraph/oracle_test.go |
Tests cover resolved and unresolved tools, graph edges, invalid go.mod input, and comparisons with go list. The oracle helper reads test and tool dependencies. |
Priority:
Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Feature
Sequence Diagram(s)
sequenceDiagram
participant Build
participant modfile.Parse
participant builder.run
participant PackageQueue
Build->>modfile.Parse: Parse the main go.mod
Build->>builder.run: Run graph construction with tool imports
builder.run->>PackageQueue: Link tool imports, then drain queued packages
Possibly related PRs
- safedep/code#56: Adds workspace build-list discovery; this PR describes how each workspace module graph handles its own tool directives.
- safedep/code#54: Introduces
Buildand the graph model that this PR updates to include tool-module edges.
Merge Risk: 
Minimal · up to 36085
-
The tool-graph change is ready to merge after normal checks.
Pre-merge checks: 
4 · 
1
-
Failed checks: 1 warning Passed checks: 4 passedFull details: Docstring Coverage
-
Explanation
Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 4 files. (1 skipped: 1 unsupported.)
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
-
Finishing Touches: 💡 1
-
📝 Generate docstrings: 💡
-
- Commit to this branch
- Create a new PR
🧪 Generate unit tests (beta)
-
- Commit to this branch
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
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 @coderabbitai help to get the list of available commands.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #57 +/- ##
==========================================
+ Coverage 77.23% 77.26% +0.02%
==========================================
Files 174 174
Lines 17129 17140 +11
==========================================
+ Hits 13230 13243 +13
+ Misses 3115 3114 -1
+ Partials 784 783 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
safedep/code#57 merged, so the pin moves from the head of its branch to the merge commit on main. The code is the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm
…ly roots (#844) * [FOU-589] Start the not-called check at the dependencies of the package Take a project whose code imports p, where p imports q and q calls the vulnerable function of p. vet reads no code of q, because the code does not import q. So the call search finds no path, and the check for another package that can call p started only at the imported packages. It found none and the finding read not-called. The check now also starts at the dependencies of p. A path from one of them back to p gives transitive. A path through an open package gives no-dependency-source. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm * [FOU-589] Take the test-only label of a Go root from its go.mod The module graph of a go.mod roots at the modules that its code imports. The reach rules took the test-only label of a root from the code usage, which covers the whole target. So a module that only the tests of one go.mod import read as a production import when the code of another go.mod imported it. The label of each path from that root was wrong too. modgraph marks an edge from the main module that only test files give. The conversion now marks a root that no other edge of the main module leads to as a test root. A local module that only tests import passes on test roots. The reach rules read the label of a root of a graph of imports from the graph. The scan file keeps the test roots in a new table, so the scan format is 5. codeusage.Version is 13. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm * [FOU-589] Count a tool directive of go.mod as an import modgraph read no tool lines of go.mod, so a module that only a tool directive names read unused. But go tool builds and runs the code of the tool with the build list of the module. safedep/code#57 links each tool package as an import of the main module. This pins safedep/code to the head of that branch. The acceptance script gives a go.mod whose only use of a module is a tool directive, and the module that the tool imports is imported through it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm * [FOU-589] Keep an open Go package from not-called, and a built test root from test-only A Fable review found two gaps in the functions that this branch changes. An open package can import a module that has no edge in the graph, and that module can call the package. The search for another caller started at the edges only, so an open package with source and no call path read not-called. An open package now gives no-dependency-source. A module that only test files import directly can also sit below a non-test root. The build then links it, but its finding read test-only. A test root now counts as test-only only when no path from a non-test root reaches it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm * [FOU-589] Take the test-only label of a Go package below a root from its path CodeRabbit found that a package below a root took the label from the code usage of the whole target. Take a go.mod whose code reaches a package only through a module that its tests import. When the code of another go.mod imports the package, the code usage marks it as a non-test import, so the finding of the first go.mod read not test-only. A package below a root of a graph of imports now takes the label of the path from the imported packages. The code usage gives the label only when the graph has no path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm * [FOU-589] Pin safedep/code to the merge of safedep/code#57 safedep/code#57 merged, so the pin moves from the head of its branch to the merge commit on main. The code is the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm --------- Co-authored-by: Claude <noreply@anthropic.com>



What was wrong
Buildparsed the maingo.modwithmodfile.ParseLax, andParseLaxdrops thetoollines. So the graph had no edge to the module of a tool. vet read such a module asunused. Butgo toolbuilds and runs the code of the tool with the build list of the module.What this changes
Buildparses the maingo.modwithmodfile.Parse, as the go command does.DiscoverandreadWorkspacealready parse the same file strictly, so theDiscoverandBuildpath fails on no new input. A caller that passes its own sources gets an error only for ago.modthat the go command rejects too.Buildlinks eachtoolpackage as an import of the main module, withgo.modand the line as the evidence. A tool edge is not test-only.Versionis2, because the same input gives another graph.go.mod.Tests
TestBuildReadsTheToolsOfTheMainModule: a tool of a dependency, a tool in atestdatadirectory of the main module, and a tool that no module holds. A test file also imports the tool, and the edge stays non-test.TestBuildRejectsABadInput: a directive that the go command rejects.TestBuildReachesTheModulesOfAToolserves a tool module from a proxy on the disk and checks the graph againstgo list.go.modin a secondgo list -depscall with no-test. On this repository it now covers thelefthooktool.main.A Fable sub-agent reviewed the change. It found an oracle that was too strict for a tool with test files, a test case that passed without the fix, and a gap in the workspace docs. The second commit fixes them.
Part of FOU-589. safedep/vet pins this after the merge.
Generated by Claude Code
Summary by CodeRabbit
New Features
tooldirectives in the main module’sgo.mod, along with their imported dependencies.Bug Fixes
go.modfiles now produce an error when they would be rejected by the Go command.Documentation