mirror of
https://github.com/ultraworkers/claw-code.git
synced 2026-05-30 03:35:20 +08:00
fix(#787): --resume with directory path returns session_path_is_directory kind + hint; wire fallback_hint_for_error_kind into both resume error emission sites
This commit is contained in:
parent
22b423b651
commit
113145a42a
@ -7739,3 +7739,5 @@ Original filing (2026-04-18): the session emitted `SessionStart hook (completed)
|
|||||||
785. **`claw dump` (typo/near-miss for dump-manifests) returned `error_kind:"unknown"` — no classifier arm for `"unknown subcommand:"` prose prefix** — dogfooded 2026-05-27 on `e628b4bb`. Any unknown top-level subcommand that triggers the suggestion path emitted `"unknown subcommand: <x>.\nDid you mean <y>"` but `classify_error_kind` had no arm for that prefix; all fell to the `"unknown"` catch-all. The hint was non-null (the suggestion text was extracted by `split_error_hint`) but `error_kind` was undifferentiated. Fix: added `starts_with("unknown subcommand:")` → `"unknown_subcommand"` arm. Unit test assertion + integration test `unknown_subcommand_returns_typed_kind_785` using `claw dump` as the trigger. 44 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori subcommand-classifier probe on `e628b4bb`, 2026-05-27.
|
785. **`claw dump` (typo/near-miss for dump-manifests) returned `error_kind:"unknown"` — no classifier arm for `"unknown subcommand:"` prose prefix** — dogfooded 2026-05-27 on `e628b4bb`. Any unknown top-level subcommand that triggers the suggestion path emitted `"unknown subcommand: <x>.\nDid you mean <y>"` but `classify_error_kind` had no arm for that prefix; all fell to the `"unknown"` catch-all. The hint was non-null (the suggestion text was extracted by `split_error_hint`) but `error_kind` was undifferentiated. Fix: added `starts_with("unknown subcommand:")` → `"unknown_subcommand"` arm. Unit test assertion + integration test `unknown_subcommand_returns_typed_kind_785` using `claw dump` as the trigger. 44 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori subcommand-classifier probe on `e628b4bb`, 2026-05-27.
|
||||||
|
|
||||||
786. **`claw dump-manifests --manifests-dir` (missing value) and `--manifests-dir=` (empty) both returned `error_kind:"unknown"` + `hint:null`** — dogfooded 2026-05-27 on `87f43347` (pinpoint by Gaebal-gajae). Both missing `--manifests-dir` branches in `parse_dump_manifests_args` emitted plain `"--manifests-dir requires a path"` with no typed prefix; `classify_error_kind` had no matching arm so they fell to `"unknown"`. Fix: both branches now use `missing_flag_value:` prefix + `\n` usage hint. Integration test `dump_manifests_missing_dir_has_typed_kind_and_hint_786` covers both cases. 45 CLI contract tests pass. [SCOPE: claw-code] Source: Gaebal-gajae pinpoint + Jobdori implementation on `87f43347`, 2026-05-27.
|
786. **`claw dump-manifests --manifests-dir` (missing value) and `--manifests-dir=` (empty) both returned `error_kind:"unknown"` + `hint:null`** — dogfooded 2026-05-27 on `87f43347` (pinpoint by Gaebal-gajae). Both missing `--manifests-dir` branches in `parse_dump_manifests_args` emitted plain `"--manifests-dir requires a path"` with no typed prefix; `classify_error_kind` had no matching arm so they fell to `"unknown"`. Fix: both branches now use `missing_flag_value:` prefix + `\n` usage hint. Integration test `dump_manifests_missing_dir_has_typed_kind_and_hint_786` covers both cases. 45 CLI contract tests pass. [SCOPE: claw-code] Source: Gaebal-gajae pinpoint + Jobdori implementation on `87f43347`, 2026-05-27.
|
||||||
|
|
||||||
|
787. **`claw --resume /tmp` (directory path) returned `error_kind:"session_load_failed"` + `hint:null`; resume error emission sites didn't apply `fallback_hint_for_error_kind`** — dogfooded 2026-05-27 on `22b423b6`. Two gaps: (1) the OS error `"Is a directory (os error 21)"` had no classifier arm, falling to generic `session_load_failed`; (2) both resume error emission paths (session load at line 3338, command execution at line 3484) called `split_error_hint` but not `fallback_hint_for_error_kind`, so API-layer errors with no `\n` always got `hint:null`. Fix: added `session_path_is_directory` classifier arm for `"Is a directory"` / `"os error 21"` messages; added `fallback_hint_for_error_kind` fallback to both resume error sites; added `session_path_is_directory` and `session_load_failed` to `fallback_hint_for_error_kind`. Unit test + integration test `resume_directory_path_returns_typed_kind_and_hint_787`. 46 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori resume-path probe on `22b423b6`, 2026-05-27.
|
||||||
|
|||||||
@ -283,6 +283,9 @@ fn classify_error_kind(message: &str) -> &'static str {
|
|||||||
// error message is "failed to restore session: legacy session is missing workspace
|
// error message is "failed to restore session: legacy session is missing workspace
|
||||||
// binding: ...", so the specific arm must be checked first.
|
// binding: ...", so the specific arm must be checked first.
|
||||||
"legacy_session_no_workspace_binding"
|
"legacy_session_no_workspace_binding"
|
||||||
|
} else if message.contains("Is a directory") || message.contains("os error 21") {
|
||||||
|
// #787: --resume given a directory path instead of a .jsonl file
|
||||||
|
"session_path_is_directory"
|
||||||
} else if message.contains("failed to restore session") {
|
} else if message.contains("failed to restore session") {
|
||||||
"session_load_failed"
|
"session_load_failed"
|
||||||
} else if message.contains("unsupported ACP invocation") {
|
} else if message.contains("unsupported ACP invocation") {
|
||||||
@ -401,6 +404,13 @@ fn fallback_hint_for_error_kind(kind: &str) -> Option<&'static str> {
|
|||||||
"missing_credentials" => {
|
"missing_credentials" => {
|
||||||
Some("Set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN before running claw.")
|
Some("Set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN before running claw.")
|
||||||
}
|
}
|
||||||
|
// #787: session load failures have no \n-delimited hint from the OS error path
|
||||||
|
"session_load_failed" => Some(
|
||||||
|
"Pass a path to a .jsonl session file, not a directory. Managed sessions live in .claw/sessions/.",
|
||||||
|
),
|
||||||
|
"session_path_is_directory" => Some(
|
||||||
|
"--resume expects a .jsonl session file path, not a directory. Run `claw --output-format json /session list` to list managed sessions.",
|
||||||
|
),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3327,7 +3337,10 @@ fn resume_session(session_path: &Path, commands: &[String], output_format: CliOu
|
|||||||
// #77: classify session load errors for downstream consumers
|
// #77: classify session load errors for downstream consumers
|
||||||
let full_message = format!("failed to restore session: {error}");
|
let full_message = format!("failed to restore session: {error}");
|
||||||
let kind = classify_error_kind(&full_message);
|
let kind = classify_error_kind(&full_message);
|
||||||
let (short_reason, hint) = split_error_hint(&full_message);
|
let (short_reason, inline_hint) = split_error_hint(&full_message);
|
||||||
|
// #787: fall back to kind-derived hint when message has no \n delimiter
|
||||||
|
let hint =
|
||||||
|
inline_hint.or_else(|| fallback_hint_for_error_kind(kind).map(String::from));
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{}",
|
"{}",
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
@ -3472,7 +3485,10 @@ fn resume_session(session_path: &Path, commands: &[String], output_format: CliOu
|
|||||||
// hardcoded "resume_command_error" + prose in the error field
|
// hardcoded "resume_command_error" + prose in the error field
|
||||||
let full_error = error.to_string();
|
let full_error = error.to_string();
|
||||||
let error_kind = classify_error_kind(&full_error);
|
let error_kind = classify_error_kind(&full_error);
|
||||||
let (short_reason, hint) = split_error_hint(&full_error);
|
let (short_reason, inline_hint) = split_error_hint(&full_error);
|
||||||
|
// #787: fall back to kind-derived hint when error has no \n delimiter
|
||||||
|
let hint = inline_hint
|
||||||
|
.or_else(|| fallback_hint_for_error_kind(error_kind).map(String::from));
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{}",
|
"{}",
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
@ -13009,6 +13025,11 @@ mod tests {
|
|||||||
classify_error_kind("failed to restore session: file not found"),
|
classify_error_kind("failed to restore session: file not found"),
|
||||||
"session_load_failed"
|
"session_load_failed"
|
||||||
);
|
);
|
||||||
|
// #787: directory-as-session-path gets its own kind (precedes generic session_load_failed)
|
||||||
|
assert_eq!(
|
||||||
|
classify_error_kind("failed to restore session: Is a directory (os error 21)"),
|
||||||
|
"session_path_is_directory"
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
classify_error_kind("unrecognized argument `--foo` for subcommand `doctor`"),
|
classify_error_kind("unrecognized argument `--foo` for subcommand `doctor`"),
|
||||||
"cli_parse"
|
"cli_parse"
|
||||||
|
|||||||
@ -2612,3 +2612,55 @@ fn dump_manifests_missing_dir_has_typed_kind_and_hint_786() {
|
|||||||
.expect("missing_flag_value must have hint (#786)");
|
.expect("missing_flag_value must have hint (#786)");
|
||||||
assert!(!h2.is_empty(), "hint must not be empty");
|
assert!(!h2.is_empty(), "hint must not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resume_directory_path_returns_typed_kind_and_hint_787() {
|
||||||
|
// #787: `claw --resume /tmp` (directory instead of .jsonl file) returned
|
||||||
|
// error_kind:"session_load_failed" + hint:null. The OS error "Is a directory (os error 21)"
|
||||||
|
// had no \n delimiter so split_error_hint returned None, and the resume error path
|
||||||
|
// didn't call fallback_hint_for_error_kind.
|
||||||
|
// Fix: (1) added session_path_is_directory classifier arm for os error 21;
|
||||||
|
// (2) wired fallback_hint_for_error_kind into both resume error emission sites.
|
||||||
|
let root = unique_temp_dir("resume-dir-787");
|
||||||
|
fs::create_dir_all(&root).expect("temp dir");
|
||||||
|
std::process::Command::new("git")
|
||||||
|
.args(["init", "-q"])
|
||||||
|
.current_dir(&root)
|
||||||
|
.output()
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
// Pass the root directory itself as the session path
|
||||||
|
let output = run_claw(
|
||||||
|
&root,
|
||||||
|
&[
|
||||||
|
"--output-format",
|
||||||
|
"json",
|
||||||
|
"--resume",
|
||||||
|
root.to_str().unwrap(),
|
||||||
|
"/status",
|
||||||
|
],
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!output.status.success(),
|
||||||
|
"resume with directory should fail"
|
||||||
|
);
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
let j: serde_json::Value = stderr
|
||||||
|
.lines()
|
||||||
|
.find(|l| l.trim_start().starts_with('{'))
|
||||||
|
.and_then(|l| serde_json::from_str(l).ok())
|
||||||
|
.expect("resume with directory should emit JSON error");
|
||||||
|
assert_eq!(
|
||||||
|
j["error_kind"], "session_path_is_directory",
|
||||||
|
"directory resume path should return session_path_is_directory, got {:?}",
|
||||||
|
j["error_kind"]
|
||||||
|
);
|
||||||
|
let hint = j["hint"]
|
||||||
|
.as_str()
|
||||||
|
.expect("session_path_is_directory must have hint (#787)");
|
||||||
|
assert!(
|
||||||
|
hint.contains(".jsonl") || hint.contains("session") || hint.contains("file"),
|
||||||
|
"hint should explain expected path format, got: {hint:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user