I'd like to work on this — planning a small CLI-only fix.
Scope: backend/internal/cli/ only — wrap arg-count validators for ao review submit and ao preview (including ao preview clear) so misuse returns usageError and exits 2, matching AGENTS.md and the rest of the CLI.
Approach:
- Add/reuse an
atMostOneArg helper (same pattern as noArgs in root.go)
- Wire it into
review submit and preview
- Use
noArgs for preview clear instead of bare cobra.NoArgs
- Add table tests asserting exit code 2 for too-many-args cases
No daemon/API/OpenAPI changes. I'll open a PR once tests pass locally.
---
## Issue description (full body)
### Summary
`AGENTS.md` documents a CLI convention: **usage errors exit 2; runtime/daemon failures exit 1**. Two commands break that convention for **argument-count misuse** — they exit **1** instead of **2** when too many positional args are passed.
Affected commands:
- `ao review submit`
- `ao preview`
- `ao preview clear`
### Expected behavior
When a user passes the wrong number of positional arguments, the process should exit **2** (usage error), consistent with other commands like `ao session get`, `ao completion`, and `ao project add`.
### Actual behavior
Passing too many args exits **1** (generic failure), which misleads scripts and wrappers that distinguish usage misuse from daemon/runtime errors.
### Reproduction
```bash
$ ao review submit mer-1 mer-2
Error: accepts at most 1 arg(s), received 2
$ echo $?
1 # expected 2
$ ao preview http://localhost:5173 extra
Error: accepts at most 1 arg(s), received 2
$ echo $?
1 # expected 2
$ ao preview clear extra
Error: unknown command "extra" for "ao preview clear"
# (or similar arg-count error — should exit 2, not 1)
$ echo $?
1 # expected 2
Root cause
In backend/internal/cli/:
| File |
Current validator |
review.go |
Args: cobra.MaximumNArgs(1) on review submit |
preview.go |
Args: cobra.MaximumNArgs(1) on preview |
preview.go |
Args: cobra.NoArgs on preview clear |
Cobra's built-in arg validators return plain errors.New(...). The root command's SetFlagErrorFunc only wraps flag parse errors as usageError, not Args validation errors. So arg-count failures are not recognized as usage errors and ExitCode() returns 1.
Other commands already follow the correct pattern — they wrap Cobra validators so misuse becomes usageError:
noArgs in root.go (used by ao start, ao stop, etc.)
oneSessionIDArg, sessionRenameArgs in session.go
- Inline wrappers in
project.go, completion.go
Existing tests for these commands already assert exit 2 for other usage errors (e.g. TestReviewSubmitMissingVerdictIsUsageError, TestPreview_MissingSessionIDIsUsageError), but no test covers arg-count misuse for review/preview.
Proposed fix (CLI-only)
-
Add a shared helper in root.go:
func atMostOneArg(cmd *cobra.Command, args []string) error {
if err := cobra.MaximumNArgs(1)(cmd, args); err != nil {
return usageError{err}
}
return nil
}
-
Use atMostOneArg for review submit and preview.
-
Use existing noArgs for preview clear instead of cobra.NoArgs.
-
Add tests:
TestReviewSubmitTooManyArgsIsUsageError
TestPreview_TooManyArgsIsUsageError
TestPreviewClear_TooManyArgsIsUsageError
Out of scope
- No daemon, HTTP, SQLite, or OpenAPI changes
- No new CLI commands or flags
- No changes to flag-validation paths (those already work)
Test plan
cd backend
go test ./internal/cli/ -run 'TestReviewSubmit|TestPreview' -count=1
go test ./internal/cli/ -count=1
Manual smoke:
ao review submit a b; echo $? # should print 2
ao preview a b; echo $? # should print 2
ao preview clear x; echo $? # should print 2
References
- Contributor convention:
AGENTS.md — "Return usage errors as usageError so CLI misuse exits 2"
- Exit code mapping:
backend/internal/cli/root.go — usageError + ExitCode()
- Prior art:
noArgs, oneSessionIDArg, completion.go arg wrapper
Files to change
| File |
Change |
backend/internal/cli/root.go |
Add atMostOneArg helper |
backend/internal/cli/review.go |
Args: atMostOneArg on review submit |
backend/internal/cli/preview.go |
Args: atMostOneArg on preview; Args: noArgs on preview clear |
backend/internal/cli/review_test.go |
TestReviewSubmitTooManyArgsIsUsageError |
backend/internal/cli/preview_test.go |
TestPreview_TooManyArgsIsUsageError, TestPreviewClear_TooManyArgsIsUsageError |
I'd like to work on this — planning a small CLI-only fix.
Scope:
backend/internal/cli/only — wrap arg-count validators forao review submitandao preview(includingao preview clear) so misuse returnsusageErrorand exits 2, matchingAGENTS.mdand the rest of the CLI.Approach:
atMostOneArghelper (same pattern asnoArgsinroot.go)review submitandpreviewnoArgsforpreview clearinstead of barecobra.NoArgsNo daemon/API/OpenAPI changes. I'll open a PR once tests pass locally.
Root cause
In
backend/internal/cli/:review.goArgs: cobra.MaximumNArgs(1)onreview submitpreview.goArgs: cobra.MaximumNArgs(1)onpreviewpreview.goArgs: cobra.NoArgsonpreview clearCobra's built-in arg validators return plain
errors.New(...). The root command'sSetFlagErrorFunconly wraps flag parse errors asusageError, not Args validation errors. So arg-count failures are not recognized as usage errors andExitCode()returns 1.Other commands already follow the correct pattern — they wrap Cobra validators so misuse becomes
usageError:noArgsinroot.go(used byao start,ao stop, etc.)oneSessionIDArg,sessionRenameArgsinsession.goproject.go,completion.goExisting tests for these commands already assert exit 2 for other usage errors (e.g.
TestReviewSubmitMissingVerdictIsUsageError,TestPreview_MissingSessionIDIsUsageError), but no test covers arg-count misuse for review/preview.Proposed fix (CLI-only)
Add a shared helper in
root.go:Use
atMostOneArgforreview submitandpreview.Use existing
noArgsforpreview clearinstead ofcobra.NoArgs.Add tests:
TestReviewSubmitTooManyArgsIsUsageErrorTestPreview_TooManyArgsIsUsageErrorTestPreviewClear_TooManyArgsIsUsageErrorOut of scope
Test plan
Manual smoke:
References
AGENTS.md— "Return usage errors as usageError so CLI misuse exits 2"backend/internal/cli/root.go—usageError+ExitCode()noArgs,oneSessionIDArg,completion.goarg wrapperFiles to change
backend/internal/cli/root.goatMostOneArghelperbackend/internal/cli/review.goArgs: atMostOneArgonreview submitbackend/internal/cli/preview.goArgs: atMostOneArgonpreview;Args: noArgsonpreview clearbackend/internal/cli/review_test.goTestReviewSubmitTooManyArgsIsUsageErrorbackend/internal/cli/preview_test.goTestPreview_TooManyArgsIsUsageError,TestPreviewClear_TooManyArgsIsUsageError