cli: one declaration for the commands, and the documents held to it - #101
Merged
Conversation
The block tfg --help prints was a raw string sitting beside the switch that dispatched, and nothing compared the two. The website copied that block by hand. The flag table in README was typed out separately from the flag set generate builds. Three lists saying one thing, no two of them compared, and all three green - README was missing --damage, added to the program the same day. Run and the help now come from one table in internal/cli/commands.go, so the first of those cannot drift at all. The output is byte for byte what it was: the same sha256 across the help, the version and an unknown command. The declaration keeps the parts apart because they are different questions. Verb is what the dispatch matches; Aliases reach the same place and are not printed, which is how four spellings of licence exist without the help becoming a spelling lesson; Shown replaces the verb where they differ, and only recipe does, since the dispatch branches on "recipe" while a person types "recipe fmt"; an empty summary means reachable but unlisted, which is help itself. That shape came from measuring first. The dispatch offered eight verbs taking arguments and the help printed ten, with all four differences legitimate - a guard comparing the two lists would have raised four false alarms. The other two lists are documents, which no declaration can generate, so they get guards asking a rule rather than carrying a list of exceptions. Every command the help lists must be a verb the tool answers to, and every command it does not list must do nothing except print that help - which tells help apart from a verb somebody forgot to describe without naming either. The README table is compared against the flag set generate really builds, both directions. The English page must repeat the program's own sentences word for word. TestEveryCommandTakingItsOwnArgumentsIsWatchedForHelp read the dispatch out of cli.go with go/ast, and refused to pass when that switch disappeared - which is what it was built to do. It asks behaviour now: a command with its own flag set refuses an unknown flag with ExitUsage and one that ignores what follows answers normally, splitting all eleven verbs eight to three with no exception to write down. Five mutations were invalidated by the move and are repointed. Three came back NOT CAUGHT or BROKEN first, and one of those was a real hole in a new guard: it asked the declaration about its own verb rather than about the first word of the name the help prints, so an entry printing one word and dispatching on another passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes O201. Three lists said the same thing, no two of them were compared, and all three were green.
tfg --helpprints was a raw string beside the switch that dispatchedgeneratebuildsREADME was missing
--damage, added to the program the same day, and nothing said so.Measured before writing anything
The naive fix would not have worked, and this is why:
generate validate verify cleanup recipe preset formats damageCommands:blockrecipe fmt, plusversionandlicenseAll four differences are legitimate -
recipebranches on one word and prints as two, andversion/licensedo not takeargs[1:]. A guard comparing the two lists would have raised four false alarms. That is what shaped the declaration into separate fields rather than one name.The change
Runand the help both come from one table ininternal/cli/commands.go.The output is byte for byte what it was - the same sha256 across
--help,--versionand an unknown command. This is a refactor, not something a user of the tool can see.The fields are apart because they answer different questions.
Verbis what the dispatch matches.Aliasesreach the same place and are not printed, which is how four spellings of licence exist without the help turning into a spelling lesson.Shownreplaces the verb in the help where they differ, and onlyrecipedoes. An empty summary means reachable but unlisted, which ishelpitself.Guards
The other two lists are documents, which no declaration can generate. Each guard asks a rule, not a list of exceptions:
helpapart from a verb somebody forgot to describe, without naming eithergeneratereally builds, both directions, with a shape check first so a change inflag.PrintDefaultscannot leave the map empty and every comparison trivially trueTestEveryCommandTakingItsOwnArgumentsIsWatchedForHelpread the dispatch withgo/astand refused to pass when that switch disappeared - exactly what it was built to do. It asks behaviour now: a command with its own flag set refuses an unknown flag withExitUsage, one that ignores what follows answers normally. All eleven verbs, eight to three, no exception to write down.What this cost
Five mutations were invalidated at once - four by code moving out of
cli.go, one by my own pattern written against a line I then reflowed. All five reportedSKIP, which reads as proven, and onlystaleness.pysaid otherwise. Repointed and rerun.Three came back
NOT CAUGHTorBROKEN, each for a different reason, and one was a real hole in a new guard: it asked the declaration about its own verb rather than about the printed name, so an entry printing one word and dispatching on another passed. The fix is a repair, not a mutation tweak.internal/guard/site_test.gocrossed into the crowding band, answered with a cut rather than a larger number - the command knowledge moved to the new file where it belongs.Verification
Full suite green,
gofmtfrom$(go env GOROOT)/binandgo vetclean,journey.py300 of 300,staleness.pyreports every pattern occurring exactly once across 958 entries, ten mutations run and all caught.🤖 Generated with Claude Code