Skip to content

Commit 7859222

Browse files
committed
ROADMAP ultraworkers#119: claw <slash-only verb> + any arg silently falls through to Prompt; bare_slash_command_guidance gated by rest.len() != 1; 9 known verbs affected
Dogfooded 2026-04-18 on main HEAD 3848ea6 from /tmp/cdUU. The 'this is a slash command' helpful-error only fires when invoked EXACTLY bare. Adding ANY argument silently falls through to Prompt dispatch and burns billable tokens. $ claw --output-format json hooks {"error":"`claw hooks` is a slash command. Use `claw --resume SESSION.jsonl /hooks`..."} # clean error $ claw --output-format json hooks --help {"error":"missing Anthropic credentials; ..."} # Prompt fallthrough. The CLI tried to send 'hooks --help' # to the LLM as a user prompt. 9 known slash-only verbs affected: hooks, plan, theme, tasks, subagent, agent, providers, tokens, cache All exhibit identical pattern: bare verb → clean error verb + any arg (--help, list, on, off, --json, etc) → Prompt fallthrough, billable LLM call User pattern: 'claw status --help' prints usage. So users naturally try 'claw hooks --help' expecting same. Gets charged for prompt 'hooks --help' to LLM instead. Trace: main.rs:745-763 entry point: if rest.len() != 1 { return None; } <-- THE BUG match rest[0].as_str() { 'help' => ..., 'version' => ..., other => bare_slash_command_guidance(other).map(Err), } main.rs:765-793 bare_slash_command_guidance: looks up command in slash_command_specs() returns helpful error string WORKS CORRECTLY — just never called when args present Claude Code convention: 'claude hooks --help' prints usage, 'claude hooks list' lists hooks. claw-code silently charges. Compare sibling bugs: ultraworkers#108 typo'd verb + args → Prompt (typo path) ultraworkers#117 -p 'text' --arg → Prompt with swallowed flags (greedy -p) ultraworkers#119 known slash-verb + any arg → Prompt (too-narrow guidance) All three are silent-billable-token-burn. Same underlying cause: too-narrow parser detection + greedy Prompt dispatch. Fix shape (~35 lines): - Remove rest.len() != 1 gate. Widen to: if rest.is_empty() { return None; } let first = rest[0].as_str(); if rest.len() == 1 { // existing bare-verb handling } if let Some(guidance) = bare_slash_command_guidance(first) { return Some(Err(format!( '{} The extra argument `{}` was not recognized.', guidance, rest[1..].join(' ') ))); } None - Subcommand --help support: catch --help for all recognized slash verbs, print SlashCommandSpec.description - Regression tests: 'claw <verb> --help' prints help, 'claw <verb> any arg' prints guidance, no Prompt fallthrough Joins Silent-flag/documented-but-unenforced (ultraworkers#96-ultraworkers#101, ultraworkers#104, ultraworkers#108, ultraworkers#111, ultraworkers#115, ultraworkers#116, ultraworkers#117, ultraworkers#118) as 14th. Joins Claude Code migration parity (ultraworkers#103, ultraworkers#109, ultraworkers#116, ultraworkers#117) as 5th — muscle memory from claude <verb> --help burns tokens. Joins Truth-audit — 'missing credentials' is a lie; real cause is CLI invocation was interpreted as chat prompt. Cross-cluster with Parallel-entry-point asymmetry — slash-verb with args is another entry point differing from bare form. Natural bundles: ultraworkers#108 + ultraworkers#117 + ultraworkers#119 — billable-token silent-burn triangle: typo fallthrough (ultraworkers#108) + flag swallow (ultraworkers#117) + known-slash-verb fallthrough (ultraworkers#119) ultraworkers#108 + ultraworkers#111 + ultraworkers#118 + ultraworkers#119 — parser-level trust gap quartet: typo fallthrough + 2-way collapse + 3-way collapse + known-verb fallthrough Filed in response to Clawhip pinpoint nudge 1494948121099243550 in #clawcode-building-in-public.
1 parent 3848ea6 commit 7859222

1 file changed

Lines changed: 99 additions & 0 deletions

File tree

ROADMAP.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3882,3 +3882,102 @@ ear], /color [scheme], /effort [low|medium|high], /fast, /summary, /tag [label],
38823882
**Blocker.** Product decision: is the 3-way collapse intentional (one command, three synonyms) or accidental (three commands, one implementation)? Help docs suggest the latter. Either path is fine, as long as behavior matches documentation.
38833883
38843884
**Source.** Jobdori dogfood 2026-04-18 against `/tmp/cdTT` on main HEAD `b9331ae` in response to Clawhip pinpoint nudge at `1494940571385593958`. Joins **Silent-flag / documented-but-unenforced** (#96–#101, #104, #108, #111, #115, #116, #117) as 13th member — more severe than #111 (3-way collapse vs 2-way). Joins **Truth-audit / diagnostic-integrity** on the help-vs-implementation-mismatch axis. Cross-cluster with **Parallel-entry-point asymmetry** (#91, #101, #104, #105, #108, #114, #117) on the "multiple surfaces with distinct-advertised-but-identical-implemented behavior" axis. Natural bundle: **#111 + #118** — dispatch-collapse pair: `/providers` → `Doctor` (2-way) + `/stats`+`/tokens`+`/cache` → `Stats` (3-way). Complete parser-dispatch audit shape. Also **#108 + #111 + #118** — parser-level trust gaps: typo fallthrough (#108) + 2-way collapse (#111) + 3-way collapse (#118). Session tally: ROADMAP #118.
3885+
3886+
119. **The "this is a slash command, use `--resume`" helpful-error path only triggers for EXACTLY-bare slash verbs (`claw hooks`, `claw plan`) — any argument after the verb (`claw hooks --help`, `claw plan list`, `claw theme dark`, `claw tokens --json`, `claw providers --output-format json`) silently falls through to Prompt dispatch and burns billable tokens on a nonsensical "hooks --help" user-prompt. The helpful-error function at `main.rs:765` (`bare_slash_command_guidance`) is gated by `if rest.len() != 1 { return None; }` at `main.rs:746`. Nine known slash-only verbs (`hooks`, `plan`, `theme`, `tasks`, `subagent`, `agent`, `providers`, `tokens`, `cache`) ALL exhibit this: bare → clean error; +any-arg → billable LLM call. Users discovering `claw hooks` by pattern-following from `claw status --help` get silently charged** — dogfooded 2026-04-18 on main HEAD `3848ea6` from `/tmp/cdUU`.
3887+
3888+
**Concrete repro.**
3889+
```
3890+
# BARE invocation — clean error:
3891+
$ claw --output-format json hooks
3892+
{"type":"error","error":"`claw hooks` is a slash command. Use `claw --resume SESSION.jsonl /hooks` or start `claw` and run `/hooks`."}
3893+
3894+
# Same command + --help — PROMPT FALLTHROUGH:
3895+
$ claw --output-format json hooks --help
3896+
{"type":"error","error":"missing Anthropic credentials; ..."}
3897+
# The CLI tried to send "hooks --help" to the LLM as a user prompt.
3898+
3899+
# Same for all 9 known slash-only verbs:
3900+
$ claw --output-format json plan on
3901+
{"error":"missing Anthropic credentials; ..."} # should be: /plan is slash-only
3902+
3903+
$ claw --output-format json theme dark
3904+
{"error":"missing Anthropic credentials; ..."} # should be: /theme is slash-only
3905+
3906+
$ claw --output-format json tasks list
3907+
{"error":"missing Anthropic credentials; ..."} # should be: /tasks is slash-only
3908+
3909+
$ claw --output-format json subagent list
3910+
{"error":"missing Anthropic credentials; ..."} # should be: /subagent is slash-only
3911+
3912+
$ claw --output-format json tokens --json
3913+
{"error":"missing Anthropic credentials; ..."} # should be: /tokens is slash-only
3914+
3915+
# With real credentials: each of these is a billed LLM call with prompts like
3916+
# "hooks --help", "plan on", "theme dark" — the LLM interprets them as user requests.
3917+
```
3918+
3919+
**Trace path.**
3920+
- `rust/crates/rusty-claude-cli/src/main.rs:745-763` — the bare-slash-guidance entry point:
3921+
```rust
3922+
) -> Option<Result<CliAction, String>> {
3923+
if rest.len() != 1 {
3924+
return None; // <-- THE BUG
3925+
}
3926+
match rest[0].as_str() {
3927+
"help" => ...,
3928+
"version" => ...,
3929+
// etc.
3930+
other => bare_slash_command_guidance(other).map(Err),
3931+
}
3932+
}
3933+
```
3934+
The `rest.len() != 1` gate means any invocation with more than one positional arg is skipped. If the first arg IS a known slash-verb but there's ANY second arg, the guidance never fires.
3935+
- `rust/crates/rusty-claude-cli/src/main.rs:765-793` — `bare_slash_command_guidance` implementation. Looks up the command in `slash_command_specs()`, returns a helpful error string. Works correctly — but only gets called from the gated path.
3936+
- Downstream dispatch: if guidance doesn't match, args fall through to the Prompt action, which sends them to the LLM (billed).
3937+
- Compare #108 (subcommand typos fall through to Prompt): typo'd verb + any args → Prompt. **#119 is the known-verb analog**: KNOWN slash-only verb + any arg → same Prompt fall-through. Both bugs share the same underlying dispatch shape; #119 is particularly insidious because users are following a valid pattern.
3938+
- Claude Code convention: `claude hooks --help`, `claude hooks list`, `claude plan on` all print usage or structured output. Users migrating expect parity.
3939+
3940+
**Why this is specifically a clawability gap.**
3941+
1. *User pattern from other subcommands is "verb + --help" → usage info.* `claw status --help` prints usage. `claw doctor --help` prints usage. `claw mcp --help` prints usage. A user who learns `claw hooks` exists and types `claw hooks --help` to see what args it takes... burns tokens on a prompt "hooks --help".
3942+
2. *--help short-circuit is universal CLI convention.* Every modern CLI guarantees `--help` shows help, period. `argparse`, `clap`, `click`, etc. all implement this. claw-code's per-subcommand inconsistency (some subcommands accept --help, some fall through to Prompt, some explicitly reject) breaks the convention.
3943+
3. *Billable-token silent-burn.* Same problem as #108 and #117, but triggered by a discovery pattern rather than a typo. Users who don't know a verb is slash-only burn tokens learning.
3944+
4. *Joins truth-audit.* `claw hooks` says "this is a slash command, use --resume." Adding --help changes the error to "missing credentials" — the tool is LYING about what's happening. No indication that the user prompt was absorbed.
3945+
5. *Pairs with #108 and #117.* Three-way bug shape: #108 (typo'd verb + args → Prompt), #117 (`-p "prompt" --arg` → Prompt with swallowed args), **#119 (known slash-only verb + any arg → Prompt)**. All three are silent-billable-token-burn surface errors where parser gates don't cover the realistic user-pattern space.
3946+
6. *Joins Claude Code migration parity.* Users coming from Claude Code assume `claude hooks --help` semantics. claw-code silently charges them.
3947+
7. *Also inconsistent with subcommands that have --help support.* `status/doctor/mcp/agents/skills/init/export/prompt` all handle --help gracefully. `hooks/plan/theme/tasks/subagent/agent/providers/tokens/cache` don't. No documentation of the distinction.
3948+
3949+
**Fix shape — widen the guidance check to cover slash-verb + any args.**
3950+
1. *Remove the `rest.len() != 1` gate, or widen it to handle the slash-verb-first case.* ~10 lines:
3951+
```rust
3952+
) -> Option<Result<CliAction, String>> {
3953+
if rest.is_empty() {
3954+
return None;
3955+
}
3956+
3957+
let first = rest[0].as_str();
3958+
3959+
// Bare slash verb with no args — existing behavior:
3960+
if rest.len() == 1 {
3961+
match first {
3962+
"help" => return Some(Ok(CliAction::Help { output_format })),
3963+
// ... other bare-allowed verbs ...
3964+
other => return bare_slash_command_guidance(other).map(Err),
3965+
}
3966+
}
3967+
3968+
// Slash verb with args — emit guidance if the verb is slash-only:
3969+
if let Some(guidance) = bare_slash_command_guidance(first) {
3970+
return Some(Err(format!("{} The extra argument `{}` was not recognized.", guidance, rest[1..].join(" "))));
3971+
}
3972+
None // fall through for truly unknown commands
3973+
}
3974+
```
3975+
2. *Widen the allow-list at `:767-777`.* Some subcommands (`mcp`, `agents`, `skills`, `system-prompt`, etc.) legitimately take positional args. Leave those excluded from the guidance. Add a explicit list of slash-only verbs that should always trigger guidance regardless of arg count: `hooks`, `plan`, `theme`, `tasks`, `subagent`, `agent`, `providers`, `tokens`, `cache`. ~5 lines.
3976+
3. *Subcommand --help support.* For every subcommand that the parser recognizes, catch `--help` / `-h` explicitly and print the registered `SlashCommandSpec.description`. Or: route all slash-verb `--help` invocations to a shared "slash-command help" handler that prints the spec description + resume-safety annotation. ~20 lines.
3977+
4. *Regression tests per verb.* For each of the 9 verbs, assert that `claw <verb> --help` produces help output (not "missing credentials"), and `claw <verb> any arg` produces the slash-only guidance (not fallthrough).
3978+
3979+
**Acceptance.** `claw hooks --help`, `claw plan list`, `claw theme dark`, `claw tokens --json`, `claw providers --output-format json` all produce the structured slash-only guidance error with recognition of the provided args. No billable LLM call for any invocation of a known slash-only verb, regardless of positional/flag args. `claw <verb> --help` specifically prints the subcommand's documented purpose and usage hint.
3980+
3981+
**Blocker.** None. The fix is a localized parser change (`main.rs:745-763`). Downstream tests are additive.
3982+
3983+
**Source.** Jobdori dogfood 2026-04-18 against `/tmp/cdUU` on main HEAD `3848ea6` in response to Clawhip pinpoint nudge at `1494948121099243550`. Joins **Silent-flag / documented-but-unenforced** (#96–#101, #104, #108, #111, #115, #116, #117, #118) as 14th member — the fall-through to Prompt is silent. Joins **Claude Code migration parity** (#103, #109, #116, #117) as 5th member — users coming from Claude Code muscle-memory for `claude <verb> --help` get silently billed. Joins **Truth-audit / diagnostic-integrity** — the CLI claims "missing credentials" but the true cause is "your CLI invocation was interpreted as a chat prompt." Cross-cluster with **Parallel-entry-point asymmetry** (#91, #101, #104, #105, #108, #114, #117) — another entry point (slash-verb + args) that differs from the same verb bare. Natural bundle: **#108 + #117 + #119** — billable-token silent-burn triangle: typo fallthrough (#108) + flag swallow (#117) + known-slash-verb-with-args fallthrough (#119). All three are silent-money-burn failure modes with the same underlying cause: too-narrow parser detection + greedy Prompt dispatch. Also **#108 + #111 + #118 + #119** — parser-level trust gap quartet: typo fallthrough (#108) + 2-way slash collapse (#111) + 3-way slash collapse (#118) + known-slash-verb fallthrough (#119). Session tally: ROADMAP #119.

0 commit comments

Comments
 (0)