mirror of
https://github.com/instructkr/claw-code.git
synced 2026-04-27 04:34:59 +08:00
ROADMAP #247: classify_error_kind() misses prompt-related parse errors; hint dropped in JSON envelope
Cycle #33 dogfood finding from direct probe of Rust claw binary:
## The Pinpoint
Two related contract drifts in the typed-error envelope:
### 1. Error-kind misclassification
`classify_error_kind()` at main.rs:246-280 uses substring matching but
does NOT match two common parse error messages:
- "prompt subcommand requires a prompt string" → classified as 'unknown'
- "empty prompt: provide a subcommand..." → classified as 'unknown'
The §4.44 typed-error contract specifies 'parse | usage | unknown' as
DISTINCT classes. Known parse errors should be 'cli_parse', not 'unknown'.
### 2. Hint lost in JSON mode
Text mode appends 'Run `claw --help` for usage.' to parse errors.
JSON mode emits 'hint: null'. The trailer is added at the stderr-print
stage AFTER split_error_hint() has already serialized the envelope, so
JSON consumers never see it.
## Repro
Dogfooded on main HEAD dd0993c (cycle #33):
$ claw --output-format json prompt
{"error":"prompt subcommand requires a prompt string","hint":null,"kind":"unknown","type":"error"}
Expected: kind="cli_parse" + hint="Run \\`claw --help\\` for usage."
## Impact
- Claws dispatching on typed error.kind fall back to substring matching
- JSON consumers lose actionable hint that text-mode users see
- Joins JSON envelope field-quality family (#90, #91, #92, #110, #115,
#116, #130, #179, #181, #247)
## Fix Shape
1. Add prompt-pattern clauses to classify_error_kind() (~4 lines)
2. Move hint plumbing to BEFORE JSON envelope serialization (~15 lines)
3. Add golden-fixture regression tests per cycle #30 pattern
Not a red-state bug (error IS surfaced, exit code IS correct), but real
contract drift. Deferred for implementation; filed per Clawhip nudge
to 'add one concrete follow-up to ROADMAP.md'.
Per cycle #24 calibration:
- Red-state bug? ✗ (errors exit 1 correctly)
- Real friction? ✓ (typed-error contract drift)
- Evidence-backed? ✓ (dogfood probe + code trace identified both leaks)
- Implementation cost? ~20 lines Rust (bounded)
- Demand signal needed? Medium — any claw doing error.kind dispatch on
prompt-path errors is affected
Source: Jobdori cycle #33 direct dogfood 2026-04-22 22:30 KST in response
to Clawhip pinpoint nudge at msg 1496503374621970583.
This commit is contained in:
86
ROADMAP.md
86
ROADMAP.md
@@ -6561,3 +6561,89 @@ main.py: error: the following arguments are required: command
|
|||||||
- #179 (stderr hygiene + real message): quality contract — envelope carries real error message
|
- #179 (stderr hygiene + real message): quality contract — envelope carries real error message
|
||||||
- #180 (this): discoverability contract — claws can enumerate the surface before dispatching
|
- #180 (this): discoverability contract — claws can enumerate the surface before dispatching
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pinpoint #247. `classify_error_kind()` misses prompt-related parse errors — "empty prompt" and "prompt subcommand requires" classified as `unknown` instead of `cli_parse`; JSON envelope also drops the `Run claw --help for usage` hint
|
||||||
|
|
||||||
|
**Gap.** Typed-error contract (§4.44) specifies an enumerated error kind set: `filesystem | auth | session | parse | runtime | mcp | delivery | usage | policy | unknown`. The `classify_error_kind()` function at `rust/crates/rusty-claude-cli/src/main.rs:246-280` uses substring matching to map error messages to these kinds. Two common prompt-related parse errors are NOT matched and fall through to `unknown`:
|
||||||
|
|
||||||
|
1. **"prompt subcommand requires a prompt string"** (from `claw prompt` with no argument) — should be `cli_parse` or `missing_argument`
|
||||||
|
2. **"empty prompt: provide a subcommand..."** (from `claw ""` or `claw " "`) — should be `cli_parse` or `usage`
|
||||||
|
|
||||||
|
Separately, the JSON envelope loses the hint trailer. Text mode appends "Run `claw --help` for usage." to parse errors; JSON mode emits `"hint": null`. The hint is added at the print stage (main.rs:228-243) AFTER `split_error_hint()` has already run on the raw message, so the JSON envelope never sees it.
|
||||||
|
|
||||||
|
**Repro.** Dogfooded 2026-04-22 on main HEAD `dd0993c` (cycle #33) from `/Users/yeongyu/clawd/claw-code/rust`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Text mode (correct hint, wrong kind):
|
||||||
|
$ claw prompt
|
||||||
|
[error-kind: unknown]
|
||||||
|
error: prompt subcommand requires a prompt string
|
||||||
|
|
||||||
|
Run `claw --help` for usage.
|
||||||
|
$ echo $?
|
||||||
|
1
|
||||||
|
# Observation: error-kind is "unknown", should be "cli_parse" or "missing_argument".
|
||||||
|
# The hint "Run claw --help for usage." IS present in text output.
|
||||||
|
|
||||||
|
# JSON mode (wrong kind AND missing hint):
|
||||||
|
$ claw --output-format json prompt
|
||||||
|
{"error":"prompt subcommand requires a prompt string","hint":null,"kind":"unknown","type":"error"}
|
||||||
|
$ echo $?
|
||||||
|
1
|
||||||
|
# Observation: "kind": "unknown" (wrong), "hint": null (hint dropped).
|
||||||
|
# A claw switching on error kind has no way to distinguish this from genuine "unknown" errors.
|
||||||
|
|
||||||
|
# Same pattern for empty prompt:
|
||||||
|
$ claw ""
|
||||||
|
[error-kind: unknown]
|
||||||
|
error: empty prompt: provide a subcommand (run `claw --help`) or a non-empty prompt string
|
||||||
|
$ echo $?
|
||||||
|
1
|
||||||
|
|
||||||
|
$ claw --output-format json ""
|
||||||
|
{"error":"empty prompt: provide a subcommand (run `claw --help`) or a non-empty prompt string","hint":null,"kind":"unknown","type":"error"}
|
||||||
|
$ echo $?
|
||||||
|
1
|
||||||
|
```
|
||||||
|
|
||||||
|
**Impact.**
|
||||||
|
1. **Error-kind contract drift.** Typed error contract (§4.44) enumerates `parse | usage | unknown` as distinct classes. Classifying known parse errors as `unknown` means any claw dispatching on `error.kind == "cli_parse"` misses the prompt-subcommand and empty-prompt paths. Claws have to either fall back to substring matching the `error` prose (defeating the point of typed errors) or over-match on `unknown` (losing the distinction between "we know this is a parse error" and "we have no idea what this error is").
|
||||||
|
|
||||||
|
2. **Hint field asymmetry.** Text mode users see the actionable hint. JSON mode consumers (the primary audience for typed errors) do not. A claw parsing the JSON envelope and deciding how to surface the error to its operator loses the "Run `claw --help` for usage." pointer entirely.
|
||||||
|
|
||||||
|
3. **Joins error-quality family** (#179, #181, §4.44 typed envelope): each of those cycles locked in that errors should be truthful + complete + consistent across channels. This pinpoint shows two unfixed leaks: (a) the classifier's keyword list is incomplete, (b) the hint-appending code path bypasses the envelope.
|
||||||
|
|
||||||
|
**Recommended fix shape.**
|
||||||
|
|
||||||
|
Two atomic changes:
|
||||||
|
|
||||||
|
1. **Add prompt-related patterns to `classify_error_kind()`** (main.rs:246-280):
|
||||||
|
```rust
|
||||||
|
} else if message.contains("prompt subcommand requires") {
|
||||||
|
"cli_parse" // or "missing_argument"
|
||||||
|
} else if message.contains("empty prompt:") {
|
||||||
|
"cli_parse" // or "usage"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Unify hint plumbing.** Move the "Run `claw --help` for usage." trailer logic into the shared error-rendering path BEFORE the JSON envelope is built, so `split_error_hint()` can capture it. Currently the trailer is added only in the text-mode stderr write at main.rs:234-242.
|
||||||
|
|
||||||
|
**Regression.** Add golden-fixture tests for:
|
||||||
|
- `claw prompt` → JSON envelope has `kind: "cli_parse"` (or chosen class), `hint` non-null
|
||||||
|
- `claw ""` → same
|
||||||
|
- `claw " "` → same
|
||||||
|
- Cross-mode parity: text mode and JSON mode carry the same hint content (different wrapping OK)
|
||||||
|
|
||||||
|
**Blocker.** None. ~20 lines Rust, straightforward.
|
||||||
|
|
||||||
|
**Priority.** Medium. Not red-state (errors ARE surfaced and exit codes ARE correct), but real contract drift that defeats the typed-error promise. Any claw doing typed-error dispatch on prompt-path errors currently falls back to substring matching.
|
||||||
|
|
||||||
|
**Source.** Jobdori cycle #33 proactive dogfood 2026-04-22 22:30 KST in response to Clawhip pinpoint nudge. Probed empty-prompt and prompt-subcommand error paths; found classifier gap + hint drop. Joins §4.44 typed-envelope contract gap family (#90, #91, #92, #110, #115, #116, #130, #179, #181). Natural bundle: **#130 + #179 + #181 + #247** — JSON envelope field-quality quartet: #130 (export errno strings lose context), #179 (parse errors need real messages), #181 (exit_code must match process), **#247 (error-kind classification + hint plumbing incomplete)**.
|
||||||
|
|
||||||
|
**Related prior work.**
|
||||||
|
- §4.44 typed error envelope contract (drafted 2026-04-20 jointly with gaebal-gajae)
|
||||||
|
- #179 (parse-error real message quality) — claws consuming envelope expect truthful error
|
||||||
|
- #181 (envelope.exit_code matches process exit) — cross-channel truthfulness
|
||||||
|
- #30 (cycle #30: OPT_OUT rejection tests) — classification contracts deserve regression tests
|
||||||
|
|||||||
Reference in New Issue
Block a user