fix!: a reserved task name no longer takes down the CLI - #45
Merged
Conversation
A project task named after a built-in threw during discovery, and discovery runs
while the container is built - so the exception reached every invocation:
$ sputnik list Error: Task name 'init' is reserved by a built-in command
$ sputnik deploy Error: Task name 'init' is reserved by a built-in command
One name collision and nothing ran, not even `list`, and the message named
neither the file nor a way out.
Two changes:
The names that carry the CLI - run, list, help, completion, context:switch,
context:list - stay reserved, but a collision now skips that one task and keeps
going. The warning names the file, because with several task directories the
class alone leaves you guessing:
Skipped task 'list' in /project/sputnik/ListTask.php: the name is reserved by
a built-in command - rename the task or give it a group prefix
An alias that collides drops just the alias; taking the whole task away over a
secondary name would help nobody.
`init` becomes shadowable. Scaffolding a project happens once, a project command
named init may well be a daily one, so the project wins and the built-in says it
is gone. Symfony's addCommand already replaces by name and tasks register after
the core commands, so this needed no ordering change.
Warnings travel through withPreloadedData into the compiled container. Discovery
only runs when that cache is cold, so a warning that lived in the discovery pass
alone would appear once and then vanish while the task stayed skipped - verified
that it survives a warm cache.
BREAKING: a task named `init` used to be an error and now silently wins over the
built-in scaffold, with a warning. Reserved-name collisions no longer abort.
This was referenced Aug 20, 2026
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.
The second accepted finding from external review of 0.2.0. Reproduced against the binary before changing anything:
Discovery threw, and discovery runs while the container is built — so a single name collision reached every invocation. No task ran, not even
list, and the message named neither the file nor a way out. With several task directories configured, the class name alone leaves you guessing which file to edit.Structural names: skip the task, keep the CLI
run,list,help,completion,context:switch,context:liststay reserved — losinglistwould leave no way to reach anything else. A collision now skips that one task:Verified on the binary:
listworks, and an unrelateddeploytask in the same directory still runs with exit 0.A colliding alias drops just the alias. Removing an otherwise valid task over a secondary name would help nobody.
initbecomes shadowableThe reviewer's argument holds: a one-off scaffold should not outrank a daily project command. So a project task named
initwins, and the built-in says it is gone:This needed no ordering change — Symfony's
addCommand()replaces by name, and task commands already register after the core ones.The part that decided the design
Discovery results are baked into the compiled container via
withPreloadedData, so discovery only runs when that cache is cold. A warning collected in the discovery pass alone would appear once and then disappear while the task stayed silently skipped — worse than no warning, because the user would remember seeing it and assume it was resolved.So warnings travel through
withPreloadedDatainto the container, andKernel::createApplication()writes them to the sharedOutputChannelfrom #32. Verified against the binary that the warning is still there on the second run, with a warm cache.Tests
The reserved-name path had no test at all before this — the behaviour that broke a whole CLI was unverified. Now:
init, and the shadowing is reportedlistexits 0 with the warning whiledeploystill runs; and a projectinitruns instead of the scaffold (asserting the built-in's own output is absent)The old E2E test asserted the exact behaviour being removed — that
listfails when a task is namedinit. Rewritten into the two cases above rather than deleted.Breaking
A task named
initused to be an error and now silently wins over the built-in scaffold, with a warning. Reserved-name collisions no longer abort — a task that used to break the CLI loudly now goes missing with a warning, which is the trade this makes deliberately.🤖 Generated with Claude Code
https://claude.ai/code/session_018CTvnzcNYmFgm2HQcm821A