Skip to content

refactor(cfcli): replace the bare exit-code ints with a named type - #17

Merged
exadmin merged 2 commits into
exadmin:mainfrom
vlsi:refactor/cfcli-exit-codes
Aug 13, 2026
Merged

refactor(cfcli): replace the bare exit-code ints with a named type#17
exadmin merged 2 commits into
exadmin:mainfrom
vlsi:refactor/cfcli-exit-codes

Conversation

@vlsi

@vlsi vlsi commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Stacked on #16. GitHub cannot base a cross-fork pull request on a branch that lives in the fork, so the diff below currently shows #16's commit as well. Merge #16 first and this one collapses to the second commit — ae18c71, 9 files. Review that commit alone in the meantime.

Why

runWithDependencies returned an untyped int. All 21 return sites spelled their exit code as a bare literal, and the only place that said what those literals meant was a four-row table in a doc comment:

//   - 0: the scan found nothing, or the exclude subcommand succeeded
//   - 1: any other failure — bad arguments, the dictionary, ...
//   - 2: the scan reported at least one finding that no allow rule or exclusion covered
//   - 3: a dictionary expression does not compile under Go's RE2 engine

That table is exactly the kind of comment #16 argues against writing when there is an alternative: it restates values the code already holds, nothing checks it, and it goes stale the moment a fifth code appears or a fourth changes meaning. The honest answer to comment drift is to encode what the type system can hold and comment only the remainder — so this removes the table rather than maintaining it.

The tests made the same point from the other side. if exitCode != 3 says nothing about what 3 is, and a reader debugging a red build had to go find the table to learn it.

What

exitStatus and its four values, in a new cfcli/exit_status.go. Each constant carries the condition it reports, so the description now sits next to the number and the compiler keeps them together.

  • run, runWithDependencies, and runExcludeCommand return exitStatus instead of int. The signature now says what the value is, which was the complaint int invited.
  • main converts once, at os.Exit.
  • 21 return sites use the names.
  • 21 test comparisons use the names, and the five failure messages that hard-coded want 1 / want 0 now format the constant instead, so a renumbering cannot leave a lying message behind.

README.md's exit-0 line gains the exclude command, which it had always omitted — the constant's comment and the README now describe the same condition.

How to verify

Behavior is unchanged: the same four numbers reach the process exit under the same conditions, and no test expectation was relaxed to make this pass.

cd cfcli
gofmt -l .        # prints nothing
go vet ./...      # clean
go test ./...     # passes

The exit codes are a published interface — scripts branch on them — so the check that matters is that the numeric values did not move. They are pinned twice: by the explicit = 0= 3 in the const block rather than iota, and by the tests, which still assert the same outcomes through the new names.

What this deliberately does not do

A distinct type does not make the set closed — return 7 still compiles, because an untyped constant converts. Go offers no way to prevent that, and pretending otherwise in a comment would be the same mistake this PR removes. The win here is naming and locality, not enforcement.

🤖 Generated with Claude Code

vlsi and others added 2 commits August 11, 2026 11:04
cfcli carried 2 comment lines across 3764 lines of Go, so every fact not
expressed by a type had to be re-derived from the code on each reading.
This adds doc comments to the declarations whose contract is not evident
from the signature, and inline comments where a line is surprising.

The facts recovered here are the ones that cost the most to re-derive:
nil-tolerance of appDependencies.getenv and .now, the same-filesystem
constraint replaceFile inherits from os.Rename, the four exit codes
behind runWithDependencies' bare int return, and the format coupling
between encryptDictionaryForTest and decryptDictionary.

Comments only: the packages parse to identical ASTs with comments
dropped, and gofmt, go vet, and go test are unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
runWithDependencies returned an untyped int, so each of the 21 return
sites spelled its exit code as a literal and the only description of
what those literals meant was a four-row table in a doc comment. A table
in a comment drifts; a constant does not.

exitStatus and its four values move that vocabulary into the code, where
each value carries the condition it reports and the compiler keeps the
name next to the number. run, runWithDependencies, and runExcludeCommand
return the type instead of int, main converts once at os.Exit, and the
tests compare against the names rather than 0 through 3.

Behavior is unchanged: the same four numbers reach the process exit in
the same conditions. README.md's exit-0 line gains the exclude command,
which it had always omitted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@exadmin
exadmin merged commit 2c73c9e into exadmin:main Aug 13, 2026
1 of 2 checks passed
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.

2 participants