Skip to content

Commit d9844cf

Browse files
committed
fix(#780): classifier arm ordering bug — legacy_session_no_workspace_binding and no_managed_sessions shadowed by generic session_load_failed arm
1 parent 364e790 commit d9844cf

2 files changed

Lines changed: 20 additions & 2 deletions

File tree

ROADMAP.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7725,3 +7725,5 @@ Original filing (2026-04-18): the session emitted `SessionStart hook (completed)
77257725
778. **`claw doctor --output-format json` check objects had no `hint` field — all warn/fail remediation was buried in `details_prose`** — dogfooded 2026-05-27 on `e0203036`. Automation had to parse prose strings to find remediation text instead of reading a stable `hint` field. `DiagnosticCheck.json_value()` never emitted a `hint` field. Fix: added `hint: Option<String>` field to `DiagnosticCheck`, added `with_hint()` builder, populated for all warn/fail cases (auth: set env var; config: fix JSON syntax; workspace: git init; boot_preflight: install missing binaries; sandbox: expected on non-Linux). Empty hint string collapses to `null` (ok checks). 39 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori doctor-envelope probe on `e0203036`, 2026-05-27.
77267726

77277727
779. **Resumed `/skills <skill>` invocation returned bare prose → `error_kind:"unknown"` + `hint:null` after #776** — dogfooded 2026-05-27 on `fded4f6b` (pinpoint by Gaebal-gajae). Sibling of #777: the `/skills` invoke-dispatch guard emitted a single-line prose error identical in structure to the pre-#777 plugins mutation guard. After #776's classify/split it fell to `unknown+null` because no `interactive_only:` prefix was present. Fix: replaced with `interactive_only: /skills {skill_name} invocation requires a live session.\n...hint...` format. Integration test `resume_skills_invocation_is_typed_interactive_only_779` added. 40 CLI contract tests pass. [SCOPE: claw-code] Source: Gaebal-gajae pinpoint + Jobdori implementation on `fded4f6b`, 2026-05-27.
7728+
7729+
780. **`classify_error_kind` arm ordering bug: `"failed to restore session: legacy session is missing workspace binding: ..."` classified as `session_load_failed` instead of `legacy_session_no_workspace_binding`** — dogfooded 2026-05-27 on `364e7909`. The full error message from `resume_session` prepends `"failed to restore session: "` before `"legacy session is missing workspace binding: ..."`. The `contains("failed to restore session")` arm at line 278 matched first, returning `session_load_failed`; the more specific `legacy_session_no_workspace_binding` arm at line 282 was never reached. Same shadowing existed for `no_managed_sessions`. Fix: reordered the three arms — specific cases (`no_managed_sessions`, `legacy_session_no_workspace_binding`) before the generic `session_load_failed` catch-all. Unit test updated to assert corrected discriminants, plus new assertion covering the full prefixed message that exposed the bug. 40 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori classifier-ordering probe on `364e7909`, 2026-05-27.

rust/crates/rusty-claude-cli/src/main.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,12 +274,15 @@ fn classify_error_kind(message: &str) -> &'static str {
274274
"missing_worker_state"
275275
} else if message.contains("session not found") {
276276
"session_not_found"
277-
} else if message.contains("failed to restore session") {
278-
"session_load_failed"
279277
} else if message.contains("no managed sessions found") {
280278
"no_managed_sessions"
281279
} else if message.contains("legacy session is missing workspace binding") {
280+
// #780: must precede the generic "failed to restore session" arm — the full
281+
// error message is "failed to restore session: legacy session is missing workspace
282+
// binding: ...", so the specific arm must be checked first.
282283
"legacy_session_no_workspace_binding"
284+
} else if message.contains("failed to restore session") {
285+
"session_load_failed"
283286
} else if message.contains("unsupported ACP invocation") {
284287
"unsupported_acp_invocation"
285288
} else if message.contains("unsupported skills action") {
@@ -12942,8 +12945,15 @@ mod tests {
1294212945
classify_error_kind("session not found: abc123"),
1294312946
"session_not_found"
1294412947
);
12948+
// #780: "no managed sessions found" is more specific than generic "failed to restore"
12949+
// session_load_failed; the reordered classifier now correctly returns no_managed_sessions.
1294512950
assert_eq!(
1294612951
classify_error_kind("failed to restore session: no managed sessions found"),
12952+
"no_managed_sessions"
12953+
);
12954+
// Bare session load failures that aren't no_managed_sessions or legacy_binding still map here
12955+
assert_eq!(
12956+
classify_error_kind("failed to restore session: file not found"),
1294712957
"session_load_failed"
1294812958
);
1294912959
assert_eq!(
@@ -12996,6 +13006,12 @@ mod tests {
1299613006
classify_error_kind("legacy session is missing workspace binding"),
1299713007
"legacy_session_no_workspace_binding"
1299813008
);
13009+
// #780: full error string produced by resume_session includes the
13010+
// "failed to restore session: " prefix — the specific arm must win.
13011+
assert_eq!(
13012+
classify_error_kind("failed to restore session: legacy session is missing workspace binding: /path/to/session.jsonl"),
13013+
"legacy_session_no_workspace_binding"
13014+
);
1299913015
assert_eq!(
1300013016
classify_error_kind("unsupported skills action: bogus. Supported actions: list"),
1300113017
"unsupported_skills_action"

0 commit comments

Comments
 (0)