Resolve a bare numeric `cards <id>` to a clear error instead of silent exit-0
The defect
basecamp cards 12345 — a bare numeric positional on the group noun cards — returned exit 0 and the group help. To an agent that meant basecamp cards show 12345, that reads as success while nothing ran: a silent no-op masked as OK.
This is the residual from #416 ("Remove shortcut commands that shadow group nouns"). That PR fixed the top-level basecamp card 12345 case — it now errors non-zero with unknown command "card" … Did you mean cards?. But the same footgun survived one level down on the group nouns themselves.
Investigation showed it is systemic, not cards-only: cards, todos, messages, and comments all exited 0 + help on a bare positional. Root cause — Cobra's default legacyArgs rejects an unknown first arg only for the root command; every non-root group parent (no RunE) falls through to help with a zero exit.
Convention matched
The CLI already has a bare-group seam: isBareGroupWithFlags converts basecamp cards --in X (a group invoked with stray flags, no subcommand) into a non-zero usage error, and the root help function suppresses help output so Execute() can emit it. This change extends that exact seam to cover a stray positional the same way it already covers stray flags — mirroring #416's top-level fix one level down, and honoring the Agent Accessibility principle: no silent exit-0 that hides a no-op. Bare cards (no args) still shows help with exit 0; nothing about the command surface changes.
The fix
isBareGroupWithUnknownArg— a non-runnable group carrying a leftover positional (parallel toisBareGroupWithFlags). The root help function suppresses help for it;Execute()converts it to a usage error.unknownSubcommandErrorreturnsoutput.ErrUsageHint(codeusage, exit 1) that names the unknown command and points at the canonical path: for a numeric id it spells outDid you mean "basecamp cards show 12345"?; otherwise it offers Cobra's spelling suggestions, falling back to--help.
Because the fix lives at the shared seam, it resolves cards (the residual) and every sibling group noun uniformly.
$ basecamp cards 12345
{ "ok": false, "error": "unknown command \"12345\" for \"basecamp cards\"",
"code": "usage", "hint": "Did you mean \"basecamp cards show 12345\"?" } # exit 1
$ basecamp cards # unchanged: help, exit 0
$ basecamp cards show 123 # unchanged: routes to the show subcommand
The error is classified usage (exit 1), not api_error — an agent branching on the code sees a usage mistake it must not retry, per the same reasoning behind TestTransformCobraErrorClassifiesArityAsUsage.
Test coverage
internal/cli/help_test.go:isBareGroupWithUnknownArgdetectscards 12345/todos 999/comments 55/ a typo, with help suppressed;unknownSubcommandErrorasserts code, message, and both hint variants (numeric→show, typo→help); a routing test confirmscards show 123resolves past the guard to the runnable leaf.e2e/errors.bats:basecamp cards 12345andbasecamp messages shwoexit non-zero with the usage envelope and hint.
Tracking: Agent Accessibility card 10265932727 (residual from #416).
Summary by cubic
Group commands previously treated stray positional arguments as successful help; they now return a non-zero usage error. For example, basecamp cards 12345 suggests cards show 12345 instead of masking a no-op as success.
- Applies to
cards,todos,messages,comments,chat, and nested groups such ascards columnandcards step. - Numeric hints target only identifier-taking
showcommands; singleton groups such asaccountsfall back to--help. - Typos suggest the nearest subcommand, including aliases, while scoped calls such as
cards 12345 --in myprojectretain the specific hint. - Bare groups and explicit
--helpremain unchanged;--help=falsestill reports the stray argument. - Adds unit and end-to-end coverage for the usage envelope and routing behavior.