fix(#171): classify unexpected extra arguments errors as cli_parse

Pinpoint #171: typed-error classifier gap discovered during #141 probe cycle #97.

`claw list-sessions --help` emits:
  error: unexpected extra arguments after `claw list-sessions`: --help

This format is used by multiple verbs that reject trailing positional args:
- list-sessions
- plugins (subcommands)
- config (subcommands)
- diff
- load-session

Before fix:
  {"error": "unexpected extra arguments after `claw list-sessions`: --help",
   "hint": null,
   "kind": "unknown",
   "type": "error"}

After fix:
  {"error": "unexpected extra arguments after `claw list-sessions`: --help",
   "hint": "Run `claw --help` for usage.",
   "kind": "cli_parse",
   "type": "error"}

The pattern `unexpected extra arguments after \`claw` is specific enough
that it won't hijack generic prose mentioning "unexpected extra arguments"
in other contexts (sanity test included).

Side benefit: like #169/#170, correctly classified cli_parse errors now
auto-trigger the #247 hint synthesizer.

Related #141 gap not yet closed: `claw list-sessions --help` still errors
instead of showing help (requires separate parser fix to recognize --help
as a distinct path). This classifier fix at least makes the error surface
typed correctly so consumers can distinguish "parse failure" from "unknown"
and potentially retry without the --help flag.

Test added:
- `classify_error_kind_covers_unexpected_extra_args_171` (4 positive cases
  + 1 sanity guard)

Tests: 226/226 pass (+1 from #171).

Typed-error family: #121, #127, #129, #130, #164, #169, #170, #247.
This commit is contained in:
YeonGyu-Kim 2026-04-23 08:02:12 +09:00
parent 5736f364a9
commit fbb0ab4be7

View File

@ -321,6 +321,17 @@ fn classify_error_kind(message: &str) -> &'static str {
// misuse — more specific than cli_parse, give it its own kind so
// consumers can offer REPL-launch guidance.
"slash_command_requires_repl"
} else if message.contains("unexpected extra arguments after `claw") {
// #171: `claw <verb> --help` where <verb> doesn't accept suffix args
// (e.g., `claw list-sessions --help`, `claw plugins list --foo`).
// This is a parse error. Message template:
// `unexpected extra arguments after \`claw <verb>\`: <args>`
//
// Covers: plugins, config, diff, list-sessions, load-session.
// #141-related: `claw list-sessions --help` specifically fails here
// instead of showing help. Classifier fix unblocks typed-error
// handling even while the help-for-that-verb gap remains open.
"cli_parse"
} else if message.contains("invalid model syntax") {
"invalid_model_syntax"
} else if message.contains("is not yet implemented") {
@ -11215,6 +11226,54 @@ mod tests {
);
}
#[test]
fn classify_error_kind_covers_unexpected_extra_args_171() {
// #171: `claw <verb> <extra-args>` where the verb doesn't accept
// trailing positional args (e.g., `claw list-sessions --help`,
// `claw plugins list --foo`). Message template is:
// `unexpected extra arguments after \`claw <verb>\`: <args>`
//
// Affects: list-sessions, plugins, config, diff, load-session.
// Before #171, these were classified `unknown`, breaking typed-error
// consumer dispatch on what is clearly a CLI parse error.
assert_eq!(
classify_error_kind(
"unexpected extra arguments after `claw list-sessions`: --help"
),
"cli_parse",
"list-sessions extra args must classify as cli_parse"
);
assert_eq!(
classify_error_kind(
"unexpected extra arguments after `claw plugins list`: --foo"
),
"cli_parse",
"plugins subcommand extra args must classify as cli_parse"
);
assert_eq!(
classify_error_kind("unexpected extra arguments after `claw diff`: --bar"),
"cli_parse",
"diff extra args must classify as cli_parse"
);
assert_eq!(
classify_error_kind(
"unexpected extra arguments after `claw config show`: --baz"
),
"cli_parse",
"config subcommand extra args must classify as cli_parse"
);
// Sanity: the pattern requires the exact `after \`claw` prefix to
// match, so unrelated prose with "unexpected extra arguments" in a
// different structure falls through.
assert_eq!(
classify_error_kind(
"the API returned unexpected extra arguments in some response"
),
"unknown",
"generic prose with 'unexpected extra arguments' should fall through"
);
}
#[test]
fn split_error_hint_separates_reason_from_runbook() {
// #77: short reason / hint separation for JSON error payloads