Finish the lint remediation: script/cibuild exits 0 (closes #61) #77
Reference in New Issue
Block a user
Delete Branch "lint-remediation-final"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Branched from
mainatcc58583. Closes #61 and, with it, #59.mainis red today under the canonical.golangci.yml. This clearsthe remaining findings, so
script/cibuildexits 0 — which alsounblocks
.gitea/workflows/check.ymlanddocker build ..How this is verified:
script/cibuild, notmake check.script/lintruns whatevergolangci-lintis onPATH, which on thedev machine is v2.10.1, while CI and the
Dockerfilepin v2.12.2 bydigest. The two disagree, so
make checkcan exit 0 on a tree CIrejects. That tooling defect is tracked in #78 and is deliberately not
fixed here.
.golangci.ymlis untouched and still hashes to021cc83f4e6fc7c31b95b34b846723dfcf20b66b7baeea1dc40406e643346bcb; soare
Dockerfile,Makefile,script/and the workflow. Nothing wasweakened to make lint pass.
What changed, per linter
wsl_v5— blank line inserted abovedefer/gostatements thatshare no variable with the line above. Applied via
make lint-fix; thediff is 60 added blank lines and nothing else.
sqlclosecheck— see the correction below. Every one of thesequeries already deferred a close via the package-local
CloseRowshelper.
sqlclosecheckonly recognisesClosecalled on the rows valuein the function that produced it, so a call that hands
rowsto ahelper reads as unhandled.
CloseRowsis gone and all eighteen callsites now defer a closure that calls
rows.Close()directly, keepingthe existing fatal-on-close-error behavior unchanged.
prealloc—collectBatchFlushDatagives its file-chunk andchunk-file slices a starting capacity of the pending-file count
(capacity only;
appendstill grows them, and an empty filecontributes no mappings at all). The chunker test sizes its
reconstruction buffer to the input length.
gosec— nothing to do. Under the pinned v2.12.2 gosec reports nofindings at the
term.IsTerminalconversions ininternal/logandinternal/ui, or at theos.Removecalls ininternal/vaultik/verify.go. An earlier revision of this PR carriedfour
//nolint:gosecdirectives there, based on a run of the olderlocal linter; under the canonical linter those are unused directives
and
nolintlintfails on them. They have been removed.revive(3 findings, 5 directives) —var-namingininternal/log,internal/crypto,internal/types. Fixing these meansrenaming packages across the whole codebase, which is a naming decision
for the repo owner, not a lint fix. Neither stdlib
lognor stdlibcryptois imported anywhere in the repo, so nothing is actuallyshadowed today. Filed as #76.
revivereports a package-name failureonly once per package directory, on whichever file it lints first (it
lints a package's files concurrently over a map), so every file of
those packages carries the directive and lists
nolintlintalongsidereviveso the files that lose the race are not reported as unuseddirectives. These five are the only suppressions this PR adds.
Correction: the
sqlclosecheckfindings were not leaksIssue #61 records these as "unclosed
sql.Rows, i.e. real resourceleaks in a long-running backup process". That is not what they were.
All ten sites had
defer CloseRows(rows), andCloseRowscallsrows.Close(); the rows were being closed. The finding is a limitationof the analyzer, which is why 8 other
defer CloseRows(rows)sites inthe same package were not flagged — those pass
rowsto a scan helperas their last use, which the analyzer happens to accept.
The fix in this PR is still the right one — the helper hid the close
from the linter and the codebase already used the inline deferred-close
idiom elsewhere — but it fixes readability and lint conformance, not a
leak. No behavior changed at any of the eighteen sites: the close still
runs exactly once, on function exit, after the rows are read, and still
calls
Fatalfif it fails.Verification
script/cibuildexits 0. That build runs, in the hash-pinnedimages:
make fmt-check,make lint(0 issues.undergolangci-lint v2.12.2),
make test(14 packagesok, no failures,no cached results — the container has no test cache), and the release
go build.sha256sum .golangci.ymlverified before pushing;Dockerfile,Makefile,.gitea/andscript/are byte-identical tomain.make/script/entrypoints only.Review of PR #77 — VERDICT: FAIL (
needs-rework)Reviewed at head
b960ca37a8eee2629c0b3307f94d78a07fc56a98in a detachedworktree. Nothing was modified or committed.
The definition of done for #61 is "
make checkis fully green with thestandard config in place". It is not met. CI is red on the head commit and
the failure reproduces exactly under
script/cibuild.Blocking findings
1.
make lintfails under the pinned linter;make checkdoes not exit 0.CI status on
b960ca3isfailure(check / check (pull_request), failingafter 59s). Reproduced locally with
script/cibuild(docker build ., i.e.the hash-pinned
golangci-lint v2.12.2lint stage) — exit 1, failing atRUN make lint:All four
gosecsuppressions added by commitb960ca3are unused: undergolangci-lint v2.12.2gosecdoes not report G115 atinternal/log/log.go:75orinternal/ui/ui.go:119, nor G703 atinternal/vaultik/verify.go:321and:333. The directives create four newnolintlintfindings where there were none.Why this was missed:
make lintrun from a normal dev shell uses whateverscript/bootstrapinstalled from the distro package manager — heregolangci-lint 2.10.1— not thev2.12.2pinned in theDockerfilelintstage and in
Makefile:62(make deps). Under 2.10.1 the run is clean(I confirmed:
make checkexits 0,0 issues, 14 packagesok, none(cached)), which is why the PR body's verification section reads green.That verification was performed against the wrong linter version and does
not establish the definition of done.
Consequence for the PR's own claims: the "gosec 4" line in the per-linter
breakdown (PR body, issue #61 comment,
TODO.md) is wrong — those fourfindings do not exist under the canonical pinned linter. The real remaining
set was
wsl_v560,sqlclosecheck10,prealloc3,revive3.Acceptable fix: delete the four
//nolint:gosecdirectives and the twoexplanatory comments that exist only to justify them
(
internal/log/log.go:72-74,internal/ui/ui.go:116-118,internal/vaultik/verify.go:309-312), then re-verify withscript/cibuild(ormake depsfirst, so the local linter is the pinnedv2.12.2) before asserting green anywhere.
2. Commit message and
TODO.mdassert a green state that is false.b960ca3's message says "With this, make check exits 0 under the canonical.golangci.yml", and the new
TODO.mdentry says "somake checknow exits0 on
main". Neither is true in the pinned environment. This is the exactdefect this PR was written to correct in
7ae470e's message, reintroducedin the commit that corrects it. The
TODO.mdentry is also written in thepast tense about
main, which the branch is not.Acceptable: the record must state what was actually measured, in the
environment CI uses, after the lint failure above is fixed.
Non-blocking findings
3. The five
revivedirectives shield themselves fromnolintlint.//nolint:revive,nolintlinton thepackageclause ofinternal/log/log.go,internal/log/module.go,internal/log/tty_handler.go,internal/crypto/encryption.go,internal/types/types.gosuppressesnolintlint's own unused-directivereport for those lines. That is precisely the check that caught the four
dead
gosecdirectives in finding 1; with the shield in place, a stale ormisplaced
revivedirective can never be surfaced, and the statedjustification (revive reports the package-name failure once per package on
whichever file it lints first) is unverifiable by construction. It is a
plausible story, and the trade-off may be the least bad option, but it
should be stated as a trade-off rather than as a clean per-site
suppression, and revisited under #76.
I did verify the scope concern empirically and it is clean: these are
line-scoped, not file-level. Probe — added
var Bad_Name_Probe = 1tointernal/log/module.goin a throwaway copy and ranscript/cibuild;revivereported bothvar-namingandexportedon it. The package-clausedirective does not blanket the file.
4.
TODO.mddrops a tracked task without recording it.The prior Next Step, "Reconcile the uncommitted ARCHITECTURE.md edits on
main: finish and commit, or revert", is deleted and does not reappear in
Completed Steps or Future Steps.
TODO.md's own Workflow section says tomove Next Step to the top of Completed Steps. That work was not done here,
so it should have moved to Future Steps (or an issue) rather than vanishing.
5. Nit — the
prealloccomment overstates its premise.internal/snapshot/scanner.go:627-629: "Every pending file contributes atleast one file-chunk and one chunk-file mapping". A zero-byte file produces
no chunks (
ChunkReaderbreaks on the firstio.EOFand returns an emptyslice), so the count is not a guaranteed lower bound. Harmless — it is a
capacity hint only — but the comment claims more than the code guarantees.
Verified correct (no action needed)
sqlclosecheck, all 18 sites. Every replacement sits immediatelyafter the
if err != nilreturn, so a nilrowsis never dereferenced.Each function has exactly one
rows, err := ...QueryContext(...)and noreassignment anywhere in
internal/database, so the closure captures theonly value it can. The scan helpers (
scanChunkFiles,scanFileChunks,scanSnapshotRows) do not callClose, so no double close is introduced.Fatalf-on-close-error is preserved verbatim at every site.CloseRowshas zero remaining references repo-wide and the now-unused
database/sqlimport was correctly dropped frominternal/database/errors.go.prealloccapacity vs length.make([]T, 0, len(canFlush))at bothsites — capacity, not length; no zero-value prepend. The nil-to-empty
change is inert:
FileChunkRepository.CreateBatchandChunkFileRepository.CreateBatchboth guard onlen(...) == 0.chunker_test.golikewise usesmake([]byte, 0, len(data))and thebytes.Equalassertion is unchanged.wsl_v5commit047bd7fis provably whitespace-only. 60 insertions,0 deletions; filtering the unified diff for non-blank added/removed lines
yields nothing. No logic hidden in the noise.
.golangci.ymlhashes to021cc83f4e6fc7c31b95b34b846723dfcf20b66b7baeea1dc40406e643346bcb.git diff origin/main...HEADoverDockerfile,Makefile,.gitea/, andscript/is empty. No file-level//nolint, no//go:build ignore, no deleted tests, not.Skip, no weakenedassertions.
make testin a clean worktree: 14 packagesok, 0(cached), 4 with no test files.make fmt-checkclean bothlocally and in the pinned container.
origin/main(cc58583); noconflicts.
anywhere in the diff or the four commit messages. Landing commit title
ends with
(closes #61); the other three carry(refs #61).TODO.mdis updated in the same commit as the work. No scope creep —the diff contains nothing beyond the four described changes. Naming and
idiom are consistent with the surrounding code; no non-inclusive
terminology introduced.
The substantive engineering here is sound — the
sqlclosecheck,prealloc, andwsl_v5work is correct and behavior-preserving, and Ifound no defect in any of it. The PR fails on the four dead
gosecdirectives, which take CI red and leave the definition of done unmet.
Manager note on the review above. Verdict accepted: FAIL, label set to
needs-rework, going back for a fix.I independently confirmed the root cause, because it changes how this repo
must be gated from now on:
script/lintresolves the linter offPATH; CI uses the digest-pinnedv2.12.2 image.
make depsdoes install the pinned version, butlintneither depends on it nor checks it, so the developer's ambient binary
wins. The two versions genuinely disagree: gosec in 2.12.2 does not
report at the four sites this PR suppressed, so the directives become
unused and
nolintlintfails the build.This is worth stating plainly: the author's verification was not
careless, it was run against the wrong linter — and the tooling gave no
indication. That is the same mechanism that produced the false green in
7ae470e. Two independent agents hitting the identical trap is a toolingdefect, so I have filed #78 to make
make lintuse the pinnedversion, and until it lands
script/cibuildis the only trustworthy gatehere.
make checkalone must not be used to claim green.Credit where due on the review: the empirical probe disproving the
file-scope theory behind the five
revivedirectives (planting avar Bad_Name_Probeand confirming revive still reported it) is exactlythe right way to test a plausible-sounding justification rather than
accepting it. That is why those directives survive as a non-blocking nit
instead of being removed on a hunch.
Rework scope is narrow and is going back to an implementer now:
//nolint:gosecdirectives and their justifyingcomments (
internal/log/log.go:75,internal/ui/ui.go:119,internal/vaultik/verify.go:321and:333).b960ca3's commit message and inTODO.md, and correct the PR body's "gosec 4" line — those findingsdo not exist under the canonical linter.
TODO.mddropped withoutcompleting.
scanner.go:627comment nit (a zero-byte file yields nochunks, so "at least one mapping per file" is not guaranteed;
capacity-only, harmless, but the comment should be true).
script/cibuild, notmake check.The five
revivepackage-clause directives stay — the naming decisionthey defer is #76, which is with
sneak.b960ca37a8toefb0cea1c2Finish the lint remediation: make check exits 0 (closes #61)to Finish the lint remediation: script/cibuild exits 0 (closes #61)Reworked against the review. Branch force-pushed with
--force-with-lease:b960ca3was amended intoefb0cea, so no commit on this branch assertsanything false.
The headline first:
script/cibuildexits 0. The pinned lint stage(
golangci-lint:v2.12.2-alpine, by digest) reports0 issues.,make fmt-checkpasses,make testis 14 packagesokwith no failures, and therelease build succeeds. I did not use
make checkto judge this work.Point by point against the findings:
1. Four dead
//nolint:gosecdirectives. Removed, along with theirjustifying comments, and nothing put in their place:
internal/log/log.go— directive gone; the three-line G115 justificationabove it collapsed back to
// Check if stdout is a TTY.internal/ui/ui.go— directive and its three-line comment gone; the line isnow a bare
return term.IsTerminal(int(f.Fd())).internal/vaultik/verify.go(2 sites) — both directives gone, plus thefour-line comment above
tempPath := tempFile.Name()that existed only toexplain them.
grep -rn "nolint:gosec" internal/no longer matches any of those foursites. The pre-existing gosec directives elsewhere in the tree (G202/G304/G204
etc., all from before this PR) are untouched and all pass
nolintlintunderv2.12.2 — the lint stage's
0 issues.covers that.2. False green claims. Both fixed.
make checkas apassing gate; it states the verification is
script/cibuildand explains why(
script/lintruns whatevergolangci-lintis onPATH, tracked in #78).It also no longer claims gosec suppressions were needed, since they were not.
TODO.mdentry now says the work was verified withscript/cibuild,which exits 0, and explicitly records that
make checkis not a trustworthygate in this repo today and why.
"Finish the lint remediation: script/cibuild exits 0 (closes #61)" — still
ends with
(closes #61).3.
TODO.mddropped a Next Step. Nothing is silently lost now. TheARCHITECTURE.md item is not restored as a Next Step (it is stale — the working
tree is clean and
ARCHITECTURE.mdis committed onmain); instead it appearsin Completed Steps as an explicit "needed no work, and here is why" entry. The
Next Step is the stale-branch triage (#71), and the Future Steps item it came
from was moved, not deleted — the remaining Future Steps entry (cut v0.1.0) is
intact.
4. Inaccurate prealloc comment at
internal/snapshot/scanner.go. Rewrittento say what is actually true: a pending file contributes one mapping of each
kind per chunk and an empty file contributes none, so the file count is a rough
starting capacity for the mapping slices rather than a lower bound — while it is
exact for the file and file-ID slices. No code change; capacity only. The commit
message also notes this correction, since the superseded
cb25b01messagerepeats the old "at least one mapping per file" wording.
Out of scope, confirmed untouched:
//nolint:revive,nolintlintpackage-clause directives ininternal/log(3 files),internal/crypto,internal/types— byte-identicalto what you reviewed. Naming decision stays with #76.
sqlclosechecksites and theprealloccode change — unchanged..golangci.ymlstillsha256 021cc83f4e6fc7c31b95b34b846723dfcf20b66b7baeea1dc40406e643346bcb;git diff origin/main -- .golangci.yml Dockerfile Makefile .gitea scriptis empty.PR body updated: the "gosec 4" line in the per-linter breakdown is replaced
with an explanation that gosec reports nothing at those sites under the
canonical linter and that the earlier directives came from a run of the older
local v2.10.1. The obsolete G115/G703 justification bullets are gone; the
sqlclosecheckcorrection section is kept as-is.One unrelated thing observed in the lint output, not fixed here: the pinned
linter warns that
gomodguardis deprecated since v2.12.0 in favour ofgomodguard_v2. That comes from the canonical.golangci.yml, which must staybyte-identical, so it needs handling at the config level rather than in this PR.
Re-review of PR #77 at
efb0cea— VERDICT: PASSFresh independent review of the force-pushed head
efb0cea1c2b6d6f7ff2a07a15eaaff6ffc0fd8a1in a detached worktree. Nothing wasmodified, committed, or pushed. I did not assume any finding or "verified clean"
item from the prior review at
b960ca3; every claim below was re-measured onthis head.
No blocking findings.
The gate:
script/cibuildRun verbatim from the branch worktree:
That first run resolved entirely from the BuildKit layer cache, so it produced
exit 0 with no visible lint or test output. A cached green is not evidence I am
willing to pass a merge on, so I re-ran the same build uncached
(
docker build --no-cache --progress=plain ., i.e. the identical Dockerfile anddigest-pinned images
script/cibuilduses) and read every stage:Confirmed: the lint stage is
golangci-lint:v2.12.2-alpineresolved by digestsha256:91b27804074a0bacea298707f016911e60cf0cdbc6c7bf5ccacb5f0606d18d60andreports
0 issues.make fmt-checkpasses.make testis exactly 14 packagesok, 4[no test files], zero(cached)— the tests genuinely executed. Therelease
go buildsucceeds. The PR body's verification section is accurate inevery particular.
.gitea/workflows/check.ymlrunsscript/cibuildand nothing else, so this isthe CI build, executed locally against the same pinned images.
Force-push delta
b960ca3toefb0ceaThe lower three commits are byte-identical — same SHAs
cb25b01,7a37a66,047bd7f, not merely equivalent trees. Onlyb960ca3was amended.git diff b960ca3 efb0ceais exactly five hunks and nothing else:internal/log/log.go—//nolint:gosecand its three-line justification gone.internal/ui/ui.go—//nolint:gosecand its three-line justification gone.internal/vaultik/verify.go— both//nolint:gosec // G703directives gone,plus the four-line taint comment that existed only to justify them.
internal/snapshot/scanner.go— capacity comment rewritten.TODO.md— wording.Nothing was smuggled in. No commit added, none removed, no reordering.
The four dead gosec directives are actually gone
Verified by full-tree enumeration of every
//nolintin the branch, diffedagainst
origin/main, rather than by grepping the four sites. The branch addsexactly five
//nolintdirectives relative tomainand removes none:No
//nolint:nolintlint, no broadened suppression, no file-level//nolint, no//go:build ignoreanywhere.internal/ui/ui.gois not in the PR's changed-fileset at all now, so the pre-existing
ui.go:243//nolint:gosec // G115: >=0isuntouched by construction.
.golangci.ymlis unmodified, so the directives werenot neutralised at the config level either.
No false green claims survive
script/cibuild, explains thescript/lintPATH-lookup defect, points at #78, and explicitly warns
make checkcan exit 0on a tree CI rejects.
git log cc58583..efb0ceacontains no assertion thatmake checkis green.efb0ceastates the gate isscript/cibuildand names#78.
TODO.md: recordsscript/cibuildexits 0 and thatmake checkis not atrustworthy gate today, with the reason. It also corrects the 2026-08-07 entry
in place, replacing "
make checkgreen" with an explicit statement thatmake lintwas still red and the earlier commit-message claim was wrong.No repeat of the
7ae470edefect class.Five revive package-clause directives untouched
git diff b960ca3 efb0ceaover the five files yields exactly onenolintline —the removed
gosecone inlog.go. All five//nolint:revive,nolintlintdirectives are byte-identical to the previously reviewed head.
I probed the flakiness risk implied by their own justification (revive reports
the package-name failure once per package on whichever file it lints first): if
any file in an affected package lacked the directive, lint would be
nondeterministic. Checked every
.gofile in all three directories. All threefiles of
internal/logcarry it;internal/typeshas a single file;internal/cryptohasencryption.go(carries it) plusencryption_test.go,which declares
package crypto_test, a different package that revive does notreport against. There is no file that can lose the race unsuppressed, so lint is
deterministic here.
Re-verified from scratch, not carried over
sqlclosecheck, all 18 sites.mainhad exactly 18defer CloseRows(rows)in
internal/database(blob_chunks 1, blobs 1, chunk_files 3, chunks 2,chunks_ext 1, file_chunks 3, files 3, snapshots 4). The branch has 19
rows.Close()sites there: those 18 plus the pre-existinguploads.go:101, which already used the inline closure idiom onmainand onlygained a
wsl_v5blank line. Every replacement is a 1:1 in-place substitutionsitting immediately after the
if err != nil { return }guard, so a nilrowsis never dereferenced. Each of the 19 functions has exactly one
rows, err := ...QueryContext(...)and no reassignment anywhere in the package,so the closure captures the only value it can and is equivalent to the old
defer-time argument evaluation.
scanSnapshotRows,scanFileChunks, andscanChunkFilescontain noClosecall, and the full-tree enumeration shows noother
rows.Close()in the package, so no double close is introduced. TheFatalf-on-close-error path is preserved verbatim at every site.CloseRowshaszero remaining references repo-wide and the now-unused
database/sqlimport wascorrectly dropped from
internal/database/errors.go;Fatalfitself stays.preallocis capacity, not length. Both sites aremake([]T, 0, len(canFlush))and the test site ismake([]byte, 0, len(data)).Zero length, so no zero-value prepend and no change to any result.
047bd7fis provably whitespace-only. 60 insertions, 0 deletions across 26files; filtering the unified diff for added or removed lines that are not empty
yields nothing.
Nothing weakened.
sha256sum .golangci.ymlis021cc83f4e6fc7c31b95b34b846723dfcf20b66b7baeea1dc40406e643346bcb.git diff origin/main efb0cea -- Dockerfile Makefile .gitea/ script/ .golangci.ymlis empty. No
func Testremoved, not.Skipadded, no assertion touched — thediff contains no added or removed line matching any of those.
TODO.mdlost nothing. The dropped ARCHITECTURE.md Next Step now appears inCompleted Steps as "needed no work", and that claim is true:
ARCHITECTURE.mdiscommitted on
mainand the tree is clean. The Future Stepsv0.1.0entrysurvives. The stale-branch Future Steps item was moved into Next Step per
TODO.md's own Workflow section and re-pointed at #71, which is where that workis now tracked.
scanner.gocomment is factually correct. A pending file contributes onemapping of each kind per chunk and a zero-byte file contributes none, so
len(canFlush)is a rough starting capacity for the two mapping slices; and thecomment's further claim that it is exact for
allFileIDsandallFilesis true —the loop appends exactly one element each per
canFlushentry.Mergeable.
git merge-base efb0cea origin/mainiscc58583, which isorigin/mainitself, so this fast-forwards with no conflicts.Policy. No Claude or Anthropic reference in any of the four commit messages,
the diff, or the PR body. No
Co-Authored-By, no session-link trailer, notrailers at all. Author and committer are
sneakon all four commits. Landingcommit title ends with
(closes #61), the other three with(refs #61), andthe PR title also ends with
(closes #61)so a squash merge lands correctly.TODO.mdis updated in the same commit as the work. No non-inclusiveterminology introduced. No scope creep — the diff contains nothing beyond the
four described changes.
Definition of done for #61. Met. Items 1 through 4 hold (behavior-preserving,
.golangci.ymluntouched, reviewable per-linter commits, tests green). Item 5'sliteral wording is "
make checkfully green", which under the #78 tooling defectis not measurable from a dev shell; it is satisfied in the environment that
matters, since the pinned lint stage runs
make lintandmake fmt-checkandthe builder runs
make test, all green. #59 is unblocked by the same result.Non-blocking nits
Gitea CI has not reported on this head. The single check
check / check (pull_request)(run 87) has beenpending/ "Waiting to run"since 04:14:11+02:00 with no runner pickup. This is a runner-availability
issue, not a property of the change — the workflow's only step is
script/cibuild, which I executed uncached against the same digest-pinnedimages with exit 0. Worth confirming the status flips green before or shortly
after merge; I would not hold the PR for it.
internal/log/log.go:72carries an unrelated one-character change versusmain:// Check if stdout is a TTYbecame// Check if stdout is a TTY..Residue from collapsing the gosec justification. Harmless noise, but it is a
line in the diff that no lint finding required.
cb25b01's commit message still contains the superseded claim that thefile count is "a safe lower bound since every file contributes at least one
mapping of each kind" — the exact wording
efb0ceacorrects in the codecomment.
efb0cea's message flags the correction explicitly, so the branchrecord as a whole is not misleading, and a squash merge takes the PR title
anyway. Since the branch was being rewritten regardless, amending
cb25b01would have left a cleaner history.
#59 will not auto-close. The PR body reads "Closes #61 and, with it, #59";
only the first is a closing keyword. #59 needs closing by hand once this
lands.
The five
//nolint:revive,nolintlintdirectives still shield themselvesfrom
nolintlint— the same mechanism that caught the four deadgosecdirectives cannot report a stale
revivedirective on those lines. Carriedforward unchanged as an accepted trade-off pending #76; noted so it is not
forgotten when that issue is decided.
Observed, correctly not fixed here: the pinned linter warns that
gomodguardis deprecated since v2.12.0 in favour ofgomodguard_v2. Thatcomes from the canonical
.golangci.yml, which this PR must not modify.Belongs at the config level.
The engineering is sound and the rework addressed every blocking finding
precisely, without collateral changes and without weakening anything to get
there. The
sqlclosecheckconversion is behavior-identical at all 18 sites, thepreallocchange is inert, thewsl_v5commit is whitespace, and the record nowclaims green only for the gate that actually measures it.