Skip to content

Make the lint job actually lint, and clear what it finds - #4

Merged
packetloss404 merged 6 commits into
mainfrom
fix/golangci-v2-config
Sep 5, 2026
Merged

Make the lint job actually lint, and clear what it finds#4
packetloss404 merged 6 commits into
mainfrom
fix/golangci-v2-config

Conversation

@packetloss404

@packetloss404 packetloss404 commented Sep 5, 2026

Copy link
Copy Markdown
Owner

The lint job has been failing on every run, including on main, and not because of lint findings. It exited before linting anything:

Error: can't load config: unsupported version of the configuration: ""
golangci-lint exit with code 3

CI pins golangci-lint v2.9.0, which needs a version key and a restructured file, while .golangci.yml was still v1 format. So the repository had a lint job, a make lint target, and no lint coverage at all.

This makes the linter run, then clears everything it found. golangci-lint run now exits 0.

The config

The migration is the output of golangci-lint migrate run by v2.9.0 itself, not a hand conversion. All eight linters are preserved: errcheck, govet, ineffassign, misspell, staticcheck and unused stay linters, and gofmt and goimports move to the new top-level formatters section. The exclusion presets it adds reproduce v1's default exclusions, which were on before and would otherwise have silently switched off.

The one key it drops is run.timeout: 5m, and that is correct rather than lossy. v1 defaulted to a 1m timeout and 5m was raising it; v2 disables the timeout by default, so dropping it preserves the intent instead of reimposing a limit.

What the linter found

112 findings, not the 54 a first run suggests — golangci-lint truncates repeated issues by default, so the honest count needs --max-same-issues=0.

linter found how it was handled
errcheck 65 23 fixed, 2 production type assertions fixed, 40 test type assertions excluded by rule
staticcheck 29 all fixed
unused 18 all deleted
goimports 19 files all fixed

Things worth a second look

Error strings that legitimately start with a capital. All 15 ST1005 hits begin with a product name — Conduit, Sugar, Packet Computers — so lowercasing the first word would have been wrong. The messages lead with the condition instead: "Conduit run idempotency key is invalid" became "invalid Conduit run idempotency key". Only two of these strings are asserted anywhere in the tests ("integration is disabled" and "must use HTTPS") and both keep the asserted substring.

One deliberate policy choice. 40 of the 65 errcheck findings are unchecked type assertions in tests, reported because the original config set check-type-assertions: true. They look like:

assert.Equal(t, worktreeRoot, read.(*tools.ReadFileTool).Root)

Rewriting 40 of those into two-statement checked forms would cost real readability for no safety, because a panicking assertion in a test already fails the test and names the offending type. So check-type-assertions stays on for shipping code — and the two production sites in internal/procrun are fixed — while a scoped rule exempts type assertions in _test.go only. The rule matches errcheck's unnamed message, so unchecked errors remain enforced in tests too.

That is the one part of this PR that is a judgement rather than a fix, and it is one config block to reverse if the project would rather have the rewrites.

A real bug, small. cmd/packetcode/main.go registered a cleanup that called jobsMgr.Shutdown and then returned nil unconditionally, so a job manager that failed to stop cleanly reported success on the way out.

Dead code deleted rather than annotated. 67 lines: three test helpers that outlived their tests (approvalProvider with nine methods, scriptedAlias, bytesReader), two unused production members (App.startTurnDisplaying, App.err), one unused wrapper (Registry.loadDir), plus an if false block and a closure that locked and unlocked around nothing — both of which carried comments admitting they existed to silence a linter that was never running. This matches the call the audit made when it removed internal/tools/atomic.go.

Deprecated viewport API. Four SA1019 hits were bubbles methods renamed upstream: LineDown/LineUp/HalfViewDown/HalfViewUp are now ScrollDown/ScrollUp/HalfPageDown/HalfPageUp. Same behaviour, current names.

Import grouping. goimports on its own pulls a stray local import out of the stdlib block but parks it ahead of third-party, which is not what any of these files were reaching for — each already had a packetcode group at the bottom. Setting goimports.local-prefixes sorts them into that group, and the six files with a stranded single import have it merged in. The result is the convention the files already used: stdlib, third-party, packetcode.

Verification

Run on windows/amd64 against the branch tip, with golangci-lint v2.9.0, the version CI pins:

check result
golangci-lint run --max-issues-per-linter=0 --max-same-issues=0 ./... exit 0, 0 findings
golangci-lint fmt ./... idempotent, no changes
golangci-lint config verify valid
go build ./... exit 0
go vet ./... exit 0

internal/mcp's TestMcpTool_Execute_ToolTimeout failed once during this work while golangci-lint was saturating the machine, and passed 3 for 3 on its own afterwards; nothing references the helper deleted from that package.

Linting was run under GOOS=linux, darwin and windows, and all three report zero issues. That matters here: the first push of this branch still failed CI's lint job, because linting on Windows never compiles the !windows files and so never saw an unchecked type assertion in internal/procrun/process_posix.go -- the exact POSIX counterpart of one already fixed on the Windows side. That is fixed in b5b6351, and cross-GOOS linting is how it was caught rather than by another CI round trip.

CI

lint passes. So do all six build jobs, release dry run, and smoke on all three runners.

The jobs still red are the ones already red on main at 96865bb, the commit this branch forks from, and none of them are touched by this PR:

job failure same on main?
vulncheck x/crypto v0.43.0 advisories yes
TUI golden and protocol safety make tui-golden-check exits 1 yes
test (ubuntu-latest), test (macos-latest) TestApp_Undo_RestoreAndDepth, TestLoopSelfPaced_StartedWhileStreamingKeepsOwnership, TestKillTree* yes
test (windows-latest) TestRunUserPromptSubmit_CollectsStdout times out at 5s on a PowerShell hook yes, intermittently

internal/procrun deserves the explicit check, since this PR modifies it. Its two failures are byte-identical on main and on this branch -- same file, same process_posix_test.go:70, same exec: command with a non-nil Cancel was not created with CommandContext. That is a pre-existing problem in the test harness, not a consequence of these changes.

Not addressed here

The other jobs red on main are separate problems and untouched by this PR: vulncheck (x/crypto v0.43.0), the TUI golden check, and the platform test failures including the intermittent Windows PowerShell hook timeout.

🤖 Generated with Claude Code

packetloss404 and others added 5 commits September 5, 2026 10:39
The lint job has not linted anything for at least the last three runs on
main. It was not failing on findings, it was failing to start:

    can't load config: unsupported version of the configuration: ""

CI pins golangci-lint v2.9.0, which requires a version key and a
restructured file, while .golangci.yml was still v1 format. So the repo
has had a lint job, a Makefile target and no lint coverage.

This is the output of `golangci-lint migrate` run by v2.9.0 itself rather
than a hand conversion. It keeps all eight linters: errcheck, govet,
ineffassign, misspell, staticcheck and unused stay linters, and gofmt and
goimports move to the new top-level formatters section. The exclusion
presets it adds reproduce v1's default exclusions, which were on by
default before and would otherwise silently switch off.

The one key it drops is run.timeout: 5m. That is correct rather than
lossy: v1 defaulted to a 1m timeout and 5m was raising it, while v2
disables the timeout by default, so dropping it preserves the intent
instead of reimposing a limit.

This commit only makes the linter run. It reports 54 pre-existing findings
that nothing has ever enforced; the commits that follow clear them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The formatters now run, and goimports had findings in nineteen files where
a packetcode import had drifted into the stdlib block or sat against the
third-party block with no separator.

goimports on its own pulls a stray local import out of stdlib but leaves it
in a group of its own, ahead of third-party, which is not what any of these
files were reaching for: each already had a packetcode group at the bottom.
Setting goimports local-prefixes teaches the formatter that packetcode is
local, so it sorts those imports into the trailing group instead, and the
six files with a stranded single import have it merged into the group that
was already there.

The result is the convention the files were already using -- stdlib, then
third-party, then packetcode -- and running the formatter again changes
nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ipping code

Sixty-five errcheck findings, none of which anything had ever reported.

The real ones are fixed. cmd/packetcode/main.go registered a cleanup that
called jobsMgr.Shutdown and then returned nil unconditionally, so a job
manager that failed to stop cleanly reported success on the way out; it now
returns what Shutdown says. Deferred Shutdown and CloseHandle calls in
tests discard the result explicitly instead of silently. The setup writes
in mentions_test.go are asserted, because a test whose fixture failed to
write is not testing what it claims to.

Two production type assertions in internal/procrun were unchecked.
trackedJobs only ever holds a windows.Handle so neither can fail, but a
value of the wrong type now falls through to the same outcome as "no job
tracked" rather than panicking, which is the honest reading of an
impossible state.

The remaining forty are type assertions in tests, and those are excluded by
a scoped rule rather than rewritten. check-type-assertions stays on so that
shipping code is held to it, but in a test a panicking assertion already
fails the test and names the offending type, while turning forty inline
assertions like assert.Equal(t, root, tool.(*ReadFileTool).Root) into
two-statement checks would cost real readability for no safety. The rule
matches only errcheck's unnamed message, so unchecked errors stay enforced
in tests as well.

That last part is a policy choice rather than a fix, and it is one line to
reverse if the project would rather rewrite the assertions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eighteen unused findings, all of them genuinely dead rather than
false positives from build tags or reflection.

Three test helpers had outlived their tests: approvalProvider, a full
provider stub with nine methods; scriptedAlias, which existed to give the
scripted provider a second slug; and bytesReader, a hand-rolled io.Reader
whose comment explains it avoided importing bytes. Nothing refers to any of
them.

Two pieces of production code go with them. App.startTurnDisplaying was the
entry point for a turn whose transcript line differs from what the model
receives, but every caller reaches startTurnWith directly, so the wrapper
was never on a live path. The App.err field was never read. Registry.loadDir
is a two-argument wrapper around loadDirWith that nothing calls; loadScopeDir
is the live one.

Deleting rather than annotating is deliberate: this is the same call the
audit made when it removed internal/tools/atomic.go. Code that no longer has
a caller is easier to re-add from history than to keep explaining.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Twenty-nine findings, and the largest group needed care rather than the
obvious fix.

Fifteen ST1005 hits are error strings beginning with a capital, but every
one of them begins with a product name: Conduit, Sugar, Packet Computers.
Lowercasing the word would have been wrong, so the messages lead with the
condition instead -- "Conduit run idempotency key is invalid" becomes
"invalid Conduit run idempotency key". Only two of these strings are
asserted anywhere in the tests, and both keep the asserted substring.

Four SA1019 hits are bubbles viewport methods that were renamed:
LineDown/LineUp/HalfViewDown/HalfViewUp become
ScrollDown/ScrollUp/HalfPageDown/HalfPageUp, which are the same behaviour
under the current names.

The rest are simplifications that say what the code already meant: two
guards around TrimSuffix/TrimPrefix that those functions already perform, a
conditional assignment folded into its declaration, a De Morgan negation, a
struct literal that is a conversion, and two if/else chains over a single
value that are switches.

Two were dead scaffolding with comments admitting it: an `if false` block
"to silence unused-result lint", and a closure whose critical section
locked and unlocked around nothing before being discarded with
`_ = track // silence unused`.

One ST1018 hit is a test string holding real ESC bytes. It now spells them
\x1b, which is the same bytes and readable in a diff; the comment above it
explaining why the escapes must be literal still holds.

golangci-lint run now exits 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 5, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-05T16:31:16.988271Z f36af64 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f36af64ec7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cmd/packetcode/main.go
runtime.AddCleanup(func() error {
jobsMgr.Shutdown(5 * time.Second)
return nil
return jobsMgr.Shutdown(5 * time.Second)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Propagate the cleanup error past the deferred Close

When a job worker survives the five-second timeout or snapshot flushing fails, this cleanup now returns the error to packetRuntime.Close, but the TUI path invokes that method as defer runtime.Close() at line 290 and discards its result. Consequently, if the Bubble Tea run itself succeeds, run still returns nil and the process reports success—the shutdown failure this change intends to expose remains invisible.

Useful? React with 👍 / 👎.

releaseTree on POSIX asserted trackedGroups' value to int without checking
it, the exact counterpart of the two assertions already fixed in
process_windows.go. It was missed because linting on Windows never compiles
the !windows files, so the finding only appeared once CI ran on Linux.

Same treatment as the Windows side: the map only ever holds a pgid, so this
cannot fail, and a value of the wrong type now yields the same outcome as
no group being tracked rather than panicking.

Verified by running golangci-lint under GOOS=linux, darwin and windows,
which is what should have been done before the first push. All three report
zero issues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@packetloss404
packetloss404 merged commit ebb216f into main Sep 5, 2026
11 of 16 checks passed
@packetloss404
packetloss404 deleted the fix/golangci-v2-config branch September 5, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant