Summary
ci.yml's complexity check walks the filesystem:
# .github/workflows/ci.yml (lint job)
COMPLEXITY_ISSUES=$(gocyclo -over 10 -ignore "_test\.go" . 2>&1 || true)
frontend/node_modules contains vendored Go source — e.g. frontend/node_modules/flatted/golang/pkg/flatted/flatted.go ships Stringify at complexity 32 and Parse at 16. Any run of that command in a workspace where npm dependencies are installed reports those as findings.
CI is NOT currently affected — established, not assumed
I checked both jobs that run gocyclo before filing:
| workflow / job |
gocyclo invocation |
npm install in the same job? |
affected |
ci.yml / lint |
gocyclo -over 10 -ignore "_test\.go" . (filesystem walk) |
no (no setup-node, no npm ci) |
no — node_modules never exists in that workspace |
pre-commit.yml / pre-commit |
via the gocyclo hook: gocyclo -over 10 $(git ls-files "*.go" | grep -v _test.go | grep -v vendor/) |
yes (npm ci at line 233, before pre-commit run --all-files at line 279) |
no — git ls-files only lists tracked files, and frontend/node_modules/ is gitignored (.gitignore:97) |
So the two invocations are safe today for opposite reasons: one has no node_modules, the other cannot see it.
Why file it anyway
1. It is a live trap for local runs and agents. The documented, copy-pasteable command is the ci.yml one, and anyone who runs it after npm ci in the same checkout gets findings that belong to a third-party npm package. I hit this during review of PR LeanerCloud/cloud-commitments-cli#1758 and initially attributed 2 findings to the PR that were my own — which is exactly how it would present to anyone else, including a CI-reproduction agent trying to match the lint job locally.
2. The two invocations disagree about scope, and only one of them is robust. ci.yml's . walk is safe purely because that job happens not to install npm dependencies. Adding setup-node + npm ci to the lint job — or a cache restore that materialises node_modules — breaks it immediately, with a failure pointing at a vendored file nobody in this repo owns. The pre-commit form is structurally immune because it derives its file list from git.
Note ci.yml's form also lacks the vendor/ exclusion the pre-commit form has. That costs nothing today (git ls-files "*/vendor/*.go" "vendor/*.go" returns 0 — no tracked vendor tree), but it is the same latent gap.
Suggested fix
Make ci.yml derive its file list the same way the pre-commit hook does, so the two checks agree and neither depends on what happens to be on disk:
COMPLEXITY_ISSUES=$(gocyclo -over 10 $(git ls-files "*.go" | grep -v _test.go | grep -v vendor/) 2>&1 || true)
Or, minimally, extend the ignore pattern: -ignore "_test\.go|node_modules|vendor/".
Reproduction
cd frontend && npm ci && cd ..
gocyclo -over 10 -ignore "_test\.go" .
32 flatted Stringify frontend/node_modules/flatted/golang/pkg/flatted/flatted.go:16:1
16 flatted Parse frontend/node_modules/flatted/golang/pkg/flatted/flatted.go:145:1
gocyclo -over 10 -ignore "_test\.go|node_modules" .
(no output, exit 0)
Found during independent adversarial review of PR LeanerCloud/cloud-commitments-cli#1758.
Findings from the 2026-09-02 codebase audit
Added by an automated audit of 3c0f8ac94048a2c36fce5ccddee54e6c4849a5cd (tip of origin/main). Each item below was reported by one reviewer and independently confirmed by a second that did not write it. Full report: docs/audits/codebase-audit-2026-09-02.md.
A15-003 (medium)
The table in this issue clears pre-commit.yml on the grounds that its gocyclo hook enumerates via git ls-files and so cannot see node_modules. That holds for gocyclo but not for the Go hooks beside it. .pre-commit-config.yaml:16-21 defines the go-vet hook as bash -c 'go vet ./...', and Go's ./... skips only testdata and paths beginning with . or _, never node_modules. Reproduced in a clean worktree: after npm ci, go list ./... emits github.com/LeanerCloud/CUDly/frontend/node_modules/flatted/golang/pkg/flatted, because frontend/node_modules/flatted/golang/pkg/flatted/flatted.go has no go.mod of its own and is absorbed into the CUDly module. In .github/workflows/pre-commit.yml the "Install frontend deps" step at :235 runs before "Run pre-commit" at :241, so this fires in CI. go vet happens to pass on that package today, so nothing is broken; the exposure is that a future flatted release reddens this repo's own vet, test and govulncheck gates on code it does not own. Same fix shape as the gocyclo one: have the Go ./... invocations enumerate real packages, e.g. go vet $(go list ./... | grep -v /node_modules/). Audit finding A15-003.
Summary
ci.yml's complexity check walks the filesystem:frontend/node_modulescontains vendored Go source — e.g.frontend/node_modules/flatted/golang/pkg/flatted/flatted.goshipsStringifyat complexity 32 andParseat 16. Any run of that command in a workspace where npm dependencies are installed reports those as findings.CI is NOT currently affected — established, not assumed
I checked both jobs that run gocyclo before filing:
ci.yml/lintgocyclo -over 10 -ignore "_test\.go" .(filesystem walk)setup-node, nonpm ci)node_modulesnever exists in that workspacepre-commit.yml/pre-commitgocyclohook:gocyclo -over 10 $(git ls-files "*.go" | grep -v _test.go | grep -v vendor/)npm ciat line 233, beforepre-commit run --all-filesat line 279)git ls-filesonly lists tracked files, andfrontend/node_modules/is gitignored (.gitignore:97)So the two invocations are safe today for opposite reasons: one has no
node_modules, the other cannot see it.Why file it anyway
1. It is a live trap for local runs and agents. The documented, copy-pasteable command is the
ci.ymlone, and anyone who runs it afternpm ciin the same checkout gets findings that belong to a third-party npm package. I hit this during review of PR LeanerCloud/cloud-commitments-cli#1758 and initially attributed 2 findings to the PR that were my own — which is exactly how it would present to anyone else, including a CI-reproduction agent trying to match the lint job locally.2. The two invocations disagree about scope, and only one of them is robust.
ci.yml's.walk is safe purely because that job happens not to install npm dependencies. Addingsetup-node+npm cito thelintjob — or a cache restore that materialisesnode_modules— breaks it immediately, with a failure pointing at a vendored file nobody in this repo owns. The pre-commit form is structurally immune because it derives its file list from git.Note
ci.yml's form also lacks thevendor/exclusion the pre-commit form has. That costs nothing today (git ls-files "*/vendor/*.go" "vendor/*.go"returns 0 — no tracked vendor tree), but it is the same latent gap.Suggested fix
Make
ci.ymlderive its file list the same way the pre-commit hook does, so the two checks agree and neither depends on what happens to be on disk:COMPLEXITY_ISSUES=$(gocyclo -over 10 $(git ls-files "*.go" | grep -v _test.go | grep -v vendor/) 2>&1 || true)Or, minimally, extend the ignore pattern:
-ignore "_test\.go|node_modules|vendor/".Reproduction
Found during independent adversarial review of PR LeanerCloud/cloud-commitments-cli#1758.
Findings from the 2026-09-02 codebase audit
Added by an automated audit of
3c0f8ac94048a2c36fce5ccddee54e6c4849a5cd(tip oforigin/main). Each item below was reported by one reviewer and independently confirmed by a second that did not write it. Full report:docs/audits/codebase-audit-2026-09-02.md.A15-003 (medium)
The table in this issue clears
pre-commit.ymlon the grounds that its gocyclo hook enumerates viagit ls-filesand so cannot seenode_modules. That holds for gocyclo but not for the Go hooks beside it..pre-commit-config.yaml:16-21defines the go-vet hook asbash -c 'go vet ./...', and Go's./...skips onlytestdataand paths beginning with.or_, nevernode_modules. Reproduced in a clean worktree: afternpm ci,go list ./...emitsgithub.com/LeanerCloud/CUDly/frontend/node_modules/flatted/golang/pkg/flatted, becausefrontend/node_modules/flatted/golang/pkg/flatted/flatted.gohas nogo.modof its own and is absorbed into the CUDly module. In.github/workflows/pre-commit.ymlthe "Install frontend deps" step at :235 runs before "Run pre-commit" at :241, so this fires in CI.go vethappens to pass on that package today, so nothing is broken; the exposure is that a futureflattedrelease reddens this repo's own vet, test and govulncheck gates on code it does not own. Same fix shape as the gocyclo one: have the Go./...invocations enumerate real packages, e.g.go vet $(go list ./... | grep -v /node_modules/). Audit finding A15-003.