Skip to content

Follow the coding rules, close the test holes, and guard the flake - #23

Merged
siliconwitch merged 3 commits into
mainfrom
dev
Aug 21, 2026
Merged

Follow the coding rules, close the test holes, and guard the flake#23
siliconwitch merged 3 commits into
mainfrom
dev

Conversation

@siliconwitch

Copy link
Copy Markdown
Member

The third and last of the cleanup cycles, plus the five findings the review of #22 confirmed.

Coding rules

The --json path of every list command assigned nothing before it returned, two commands built an HTTP client above the guards that can refuse the call, and three constants had one use each. The two five-second values in the login poll are literals now, each carrying the RFC 8628 section it comes from. Request and AuthenticatedRequest take a reader named reader; the session keeps In and Out, which docs/cli.md records as the one place a reader is not called reader.

takeServerFlag had one production call site and was kept alive by a unit test written against it, which is both of the rules that say not to. Its thirty lines now run where they always ran, and the table that covered them drives Dispatch instead: a synthetic command records the base it was handed and the arguments left over.

Tests

Nothing invoked main, so none of its exit paths were covered. A subprocess table runs six of them, including the two that must exit zero. fleet list, key list and account balance had no server-refusal case, FetchDevices had no failure test of its own, member remove never reached its unknown-fleet guard, and nothing read the four lines login prints.

Carried over from the review of #22

The account delete probe asked GET /, which superstack-server/internal/api/auth.go:25-29 exempts from authentication, so the not-logged-in guard added last cycle could never fire against the real server and its test built a server shape that does not exist. It asks GET /fleets now. logout treated an already-removed login as a failure while account delete tolerated it, so a second logout printed an instruction naming a file that was not there. maximumBody had one use. The comment on the bound named only the device list as uncapped, and the member list is uncapped too. Two error sentences said the login had "nowhere to live", which is a metaphor that names no action.

The flake guard

CLAUDE.md states that vendorHash = null holds only while the module has no third-party dependencies, and nothing checked it: adding one leaves gofmt, go mod tidy and the GoReleaser snapshot green while nix build fails on inconsistent vendoring.

TestTheModuleStaysDependencyFree reads that condition out of go.mod and go.sum, so it costs nothing and runs everywhere the suite does. The new flake workflow covers what a Go build cannot see, the version regex in flake.nix and the postInstall rename, by building the package and checking that the version main.go declares, the version the flake reads out of it, and the version the built binary reports all agree.

It runs on tags and on pull requests touching flake.nix, flake.lock, main.go or go.mod, not on every pull request, because a cold runner downloads a 257 MiB Go toolchain to do it against a build job that finishes in about a minute. Measured locally: 45 seconds of compile once the store is warm. The paths list includes the workflow itself, so this pull request runs it rather than merging it untested.

Evidence

  • gofmt -l . clean, go mod tidy leaves the module files unchanged, go vet ./... clean, go test -count=1 ./... all packages pass with no skips.
  • Every new guard was checked against the mutation that breaks it. Two came back missed on the first attempt and both were fixed rather than reported as caught: the fs.ErrNotExist tolerance in logout had no test, and the member-remove row served an empty fleet list so the loop never ran under mutation.
  • The workflow's script was run against a real build before being written down. All three versions agreed and the binary landed at bin/superstack, which is the rename working.
  • The pinned action SHA was resolved with gh api, not guessed. actionlint, which carries shellcheck, passes on all three workflows.
  • A stray 10 MB api.test binary was sitting untracked in the tree, so *.test is ignored now.

An adversarial review over this diff is still running. If it confirms anything, the fix lands as a further commit on this branch before merge.

No version change, and nothing under flake.nix, .goreleaser.yaml or go.mod is touched. Paired with a documentation change in superstack-server.

The --json path of every list command assigned nothing before it returned, two
commands built an HTTP client above the guards that can refuse the call, and
three constants had one use each. The two five-second values in the login poll
are literals now, each carrying the RFC 8628 section it comes from.

Request and AuthenticatedRequest take a reader named reader. The session keeps
In and Out, and the parameters that fill them match, which docs/cli.md records
as the one place a reader is not called reader.

--server trimmed slashes but not spaces, so a quoted empty shell variable
reported a connection failure rather than saying what was wrong.

The KEY column's hardcoded width is a cross-repo invariant: it holds only while
the server sends a five-character suffix, and nothing on either side said so.

Six holes in the tests close. Nothing invoked main, so none of its exit paths
were covered; a subprocess table now runs six of them, including the two that
must exit zero. fleet list, key list and account balance had no server-refusal
case, FetchDevices had no failure test of its own, member remove never reached
its unknown-fleet guard, and nothing read the four lines login prints.

Five findings from the review of the previous cycle land here rather than in a
cycle of their own. The account delete probe asked GET /, which the server
exempts from authentication, so the not-logged-in guard added last cycle could
never fire against the real server and its test built a server shape that does
not exist; it asks GET /fleets now. logout treated an already-removed login as
a failure while account delete tolerated it, so a second logout printed an
instruction naming a file that was not there. maximumBody had one use. The
comment on the bound named only the device list as uncapped, and the member
list is uncapped too. Two error sentences said the login had "nowhere to live",
which is a metaphor that names no action.

Every new guard was checked against the mutation that breaks it.

No version change, and nothing under .github, flake.nix, .goreleaser.yaml or
go.mod is touched.
takeServerFlag had one production call site and was kept alive by a unit test
written against it, which is both of the rules that say not to. Its thirty
lines now run where they always ran, at the top of Dispatch, and the table that
covered them drives Dispatch instead: a synthetic command records the base it
was handed and the arguments left over, so the flag is tested through the
surface the program actually uses.

Four mutations were checked against it: dropping the whitespace trim, dropping
the empty-address guard, leaking the flag into the command's arguments, and
mishandling the value that follows it.

go test leaves a compiled binary behind when a run is interrupted, and one had
been sitting untracked in the tree, so *.test is ignored now.
CLAUDE.md states that vendorHash = null holds only while the module has no
third-party dependencies, and nothing checked it: adding one leaves gofmt, go
mod tidy and the GoReleaser snapshot all green while nix build fails on
inconsistent vendoring.

TestTheModuleStaysDependencyFree reads that condition straight out of go.mod
and go.sum, so it costs nothing and runs everywhere the suite does. Checked
against a bare require, a grouped require block, and a go.sum appearing.

The flake workflow covers what a Go build cannot see: the regex in flake.nix
that parses the version out of main.go, and the postInstall rename that exists
because Go names the binary after the module path. It builds the package, then
checks that the version main.go declares, the version the flake reads out of
it, and the version the built binary reports all agree.

It runs on tags and on pull requests touching flake.nix, flake.lock, main.go
or go.mod, not on every pull request, because a cold runner downloads a 257 MiB
Go toolchain to do it. Measured here: 45 seconds of compile once the store is
warm. The paths list includes the workflow itself, so this pull request runs
it.

The script was run against a real build before being written down, and
actionlint, which carries shellcheck, passes on all three workflows.

No version change, and nothing under flake.nix, .goreleaser.yaml or go.mod is
touched.
@siliconwitch
siliconwitch merged commit 09fda59 into main Aug 21, 2026
2 checks passed
@siliconwitch
siliconwitch deleted the dev branch August 21, 2026 09:38
siliconwitch added a commit that referenced this pull request Aug 21, 2026
The job rename you asked for, plus the two findings the adversarial
review of #23 confirmed. It found four, of which three were the same
documentation defect filed by three lenses.

## The rename

`ci.yml` and `flake.yml` both called their job `build`, so #23 showed
two checks with the same name and no way to tell which had failed. The
three jobs are `release`, `build` and `flake` now.

## The probe guard

The review found that nothing held the widened condition on `account
delete`'s probe. Reverting `!= http.StatusOK` to the `==
http.StatusUnauthorized` it replaced left the whole package green,
because every test answered the probe with 200 or 401 and both take the
same branch either way.

I verified that myself before acting on it, and it is right. #23's
commit message said every new guard had been checked against the
mutation that breaks it. That was true only of the coarse mutation which
deletes the guard outright; the boundary survived. The claim was
stronger than the check behind it.

The two standalone probe tests become one table over 401, 503 and 426,
so a refusal of any kind stops the delete, suppresses the question, and
relays the server's own sentence. The 503 case is reachable against the
real server: `superstack-server/internal/api/auth.go:52` answers 503
when the login lookup fails, and `/fleets` is not on the exemption list.

## Evidence

- `gofmt -l .` clean, `go mod tidy` leaves the module files unchanged,
`go vet ./...` clean, `go test -count=1 ./...` all packages pass.
- Three mutations checked against the new table: narrowing the condition
back to 401, removing it, and pointing the probe back at the health
check the server does not authenticate. All caught, including the one
that previously survived.
- `actionlint` passes on the renamed workflow.

Two of my own mutation runs during this work reported results from a
setup that had silently failed, once because a copy omitted an untracked
file and once because an anchor was not unique. Both were discarded and
re-run rather than reported.

No version change. Paired with a documentation change in
`superstack-server`.
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