From 3a533ceba01e4fb4ff608d7c6c9c09c5145b2d7b Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 23 Apr 2026 02:17:16 +0900 Subject: [PATCH] fix(#152-follow-up-2): claw bootstrap-plan rejects trailing arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What Was Broken `claw bootstrap-plan garbage` silently accepted the `garbage` argument: $ claw bootstrap-plan garbage - CliEntry - FastPathVersion - StartupProfiler - ... Same pattern as #152 init follow-up (previous cycle): a no-arg diagnostic verb was missing its `rest.len()` guard. ## What This Fix Does Add parse-time length check before constructing CliAction::BootstrapPlan: "bootstrap-plan" => { if rest.len() > 1 { return Err(format!( "unrecognized argument \`{}\` for subcommand \`bootstrap-plan\`", rest[1] )); } Ok(CliAction::BootstrapPlan { output_format }) } ## Dogfood Verification Before: $ claw bootstrap-plan garbage - CliEntry - FastPathVersion (continues listing...) After: $ claw bootstrap-plan garbage [error-kind: cli_parse] error: unrecognized argument `garbage` for subcommand `bootstrap-plan` $ claw bootstrap-plan (no args) - CliEntry - FastPathVersion (still works normally) ## Full No-Arg Verb Suffix-Guard Sweep (Post-Fix) All no-arg verbs now uniformly reject trailing garbage: | Verb | Status | |---|---| | help garbage | ✅ rejects | | version garbage | ✅ rejects | | status garbage | ✅ rejects | | sandbox garbage | ✅ rejects | | doctor garbage | ✅ rejects | | state garbage | ✅ rejects | | init garbage | ✅ rejects (previous cycle #55) | | diff garbage | ✅ rejects | | plugins garbage | ✅ rejects | | skills garbage | ✅ rejects | | system-prompt garbage | ✅ rejects | | dump-manifests garbage | ✅ rejects | | bootstrap-plan garbage | ✅ rejects (this commit) | | acp garbage | ✅ rejects | Legitimate positionals (not a bug): - `export ` — file path is the intended arg - `config
` — flexible section filter (design question, not bug) ## Non-Regression - `claw bootstrap-plan` (no args) still works ✅ - All 180 binary tests pass ✅ - All 466 library tests pass ✅ ## Related - #152 follow-up (init suffix guard, previous cycle) - Completes diagnostic-verb suffix-guard contract hygiene --- rust/crates/rusty-claude-cli/src/main.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index 35e7216..d7e6d8b 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -1015,7 +1015,18 @@ fn parse_args(args: &[String]) -> Result { match rest[0].as_str() { "dump-manifests" => parse_dump_manifests_args(&rest[1..], output_format), - "bootstrap-plan" => Ok(CliAction::BootstrapPlan { output_format }), + "bootstrap-plan" => { + // #152: bootstrap-plan is a no-arg verb. Reject unexpected suffixes + // like `claw bootstrap-plan garbage` that silently accept trailing + // args instead of rejecting at parse time. + if rest.len() > 1 { + return Err(format!( + "unrecognized argument `{}` for subcommand `bootstrap-plan`", + rest[1] + )); + } + Ok(CliAction::BootstrapPlan { output_format }) + } "agents" => Ok(CliAction::Agents { args: join_optional_args(&rest[1..]), output_format,