From 834b0a91fee317526562e10ea2e5abeb2848de2b Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 23 Apr 2026 07:03:40 +0900 Subject: [PATCH] fix(#169): classify invalid/missing CLI flag values as cli_parse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pinpoint #169: typed-error classifier gap discovered during dogfood probe. `claw --output-format json --output-format xml doctor` was emitting: {"error": "unsupported value for --output-format: xml ...", "hint": null, "kind": "unknown", "type": "error"} After fix: {"error": "unsupported value for --output-format: xml ...", "hint": "Run `claw --help` for usage.", "kind": "cli_parse", "type": "error"} The change adds two new classifier branches to `classify_error_kind`: 1. `unsupported value for --` → cli_parse 2. `missing value for --` → cli_parse Covers all `CliOutputFormat::parse` / `parse_permission_mode_arg` rejections and any future flag-value validation messages using the same pattern. Side benefit: the #247 hint synthesizer ("Run `claw --help` for usage.") now triggers automatically because the error is now correctly classified as cli_parse. Consumers get both correct kind AND helpful hint. Test added: - `classify_error_kind_covers_flag_value_parse_errors_169` (4 positive + 1 sanity case) Tests: 224/224 pass (+1 from #169). Discovered during dogfood probe 2026-04-23 07:00 Seoul, cycle #94. Refs: #169, typed-error family (#121, #127, #129, #130, #164, #247) --- rust/crates/rusty-claude-cli/src/main.rs | 50 ++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index d7bc4e5..071c9d4 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -292,6 +292,17 @@ fn classify_error_kind(message: &str) -> &'static str { } else if message.starts_with("empty prompt:") { // #247: `claw ""` or `claw " "` — a parse error, not `unknown`. "cli_parse" + } else if message.contains("unsupported value for --") { + // #169: Invalid CLI flag values (e.g., `--output-format xml`, + // `--permission-mode bogus`) are parse errors, not `unknown`. + // This covers all `CliOutputFormat::parse` / `parse_permission_mode_arg` + // rejections and any future `unsupported value for --: ` + // messages emitted from the parse_args dispatcher. + "cli_parse" + } else if message.contains("missing value for --") { + // #169: Missing required flag values (e.g., `--output-format` with no + // trailing argument) are parse errors, same family as above. + "cli_parse" } else if message.contains("invalid model syntax") { "invalid_model_syntax" } else if message.contains("is not yet implemented") { @@ -11100,6 +11111,45 @@ mod tests { ); } + #[test] + fn classify_error_kind_covers_flag_value_parse_errors_169() { + // #169: Invalid CLI flag values must classify as `cli_parse`, + // not fall through to `unknown`. Regression guard found during + // dogfood probe 2026-04-23: `claw --output-format xml doctor` + // emitted `{"kind":"unknown"}` envelope instead of `cli_parse`. + assert_eq!( + classify_error_kind( + "unsupported value for --output-format: xml (expected text or json)" + ), + "cli_parse", + "invalid --output-format value must classify as cli_parse" + ); + assert_eq!( + classify_error_kind( + "unsupported value for --permission-mode: bogus (expected ...)" + ), + "cli_parse", + "invalid --permission-mode value must classify as cli_parse" + ); + assert_eq!( + classify_error_kind("missing value for --output-format"), + "cli_parse", + "missing --output-format value must classify as cli_parse" + ); + assert_eq!( + classify_error_kind("missing value for --permission-mode"), + "cli_parse", + "missing --permission-mode value must classify as cli_parse" + ); + // Sanity: must not hijack genuinely unknown errors that happen to + // contain the word `unsupported` or `missing`. + assert_eq!( + classify_error_kind("some unsupported runtime condition we don't recognize"), + "unknown", + "generic `unsupported` text should still fall through to unknown" + ); + } + #[test] fn split_error_hint_separates_reason_from_runbook() { // #77: short reason / hint separation for JSON error payloads