fix(inspector): encode_cwd réplique l'encodage exact de Claude Code (cause racine du wedge no-reply)
`encode_cwd` ne remplaçait que `/` et `\` par `-`, laissant le `.` intact. Or Claude Code aplatit le cwd via `replace(/[^a-zA-Z0-9]/g, '-')` : tout caractère non alphanumérique devient `-`, y compris le `.`. Pour un run dir isolé `…/IdeA/.ideai/run/<uuid>`, le `/.` doit collapser en double dash (`…-IdeA--ideai-run-<uuid>`). L'ancienne version calculait `…-IdeA-.ideai-run-…`, un dossier inexistant sur disque : le turn-watcher voyait un répertoire vide (baseline 0, conversation <none>) et ne déclenchait jamais `turn_ended`, ce qui wedgeait le backstop no-reply du rendez-vous inter-agents. Validé EN LIVE : demandeur libéré ~grâce 2s (au lieu du timeout 600s), preuves dans idea.log. Test de non-régression `encode_cwd_encodes_dot_in_run_dir_to_double_dash` + maj test Windows (`C:\Users\me` -> `C--Users-me`). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -23,18 +23,20 @@ use domain::project::ProjectPath;
|
||||
|
||||
/// Encodes a cwd the way Claude Code names its per-project transcript folder.
|
||||
///
|
||||
/// **Assumption (documented, isolated, and unit-tested):** Claude flattens the
|
||||
/// absolute cwd into a single directory segment by replacing each path separator
|
||||
/// (`/` or `\`) — and the leading separator — with `-`. So
|
||||
/// `/home/anthony/Documents/Projects/IdeA` becomes
|
||||
/// `-home-anthony-Documents-Projects-IdeA`. We keep this convention in one small,
|
||||
/// tested function so that if Claude's exact encoding differs in some edge case, only
|
||||
/// this seam needs to change. We do not attempt to encode `.` or other characters, as
|
||||
/// the common case (clean absolute project paths) is covered and over-encoding would
|
||||
/// risk pointing at the wrong folder.
|
||||
/// **Verified against real `~/.claude/projects/` folders:** Claude flattens the absolute
|
||||
/// cwd into a single directory segment by replacing **every character that is not an ASCII
|
||||
/// alphanumeric** (`[a-zA-Z0-9]`) with `-` — this is Claude Code's `replace(/[^a-zA-Z0-9]/g,
|
||||
/// '-')`. Crucially that means **`.` is encoded too**, not just path separators: a run dir
|
||||
/// like `/home/anthony/Documents/Projects/IdeA/.ideai/run/<uuid>` becomes
|
||||
/// `-home-anthony-Documents-Projects-IdeA--ideai-run-<uuid>` (the `/.` collapses to a
|
||||
/// **double** dash). The earlier version only replaced `/` and `\`, leaving the `.`, so it
|
||||
/// computed `-IdeA-.ideai-run-…` — a folder that never exists on disk; the turn-watcher
|
||||
/// then saw an empty dir (baseline 0, conversation `<none>`) and never fired `turn_ended`,
|
||||
/// wedging the no-reply backstop. We keep this convention in one small, tested seam so a
|
||||
/// future Claude encoding change touches only here.
|
||||
pub(crate) fn encode_cwd(cwd: &str) -> String {
|
||||
cwd.chars()
|
||||
.map(|c| if c == '/' || c == '\\' { '-' } else { c })
|
||||
.map(|c| if c.is_ascii_alphanumeric() { c } else { '-' })
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -83,8 +85,26 @@ mod tests {
|
||||
"-home-anthony-Documents-Projects-IdeA"
|
||||
);
|
||||
assert_eq!(encode_cwd("/a/b"), "-a-b");
|
||||
// Windows-style separators flatten too.
|
||||
assert_eq!(encode_cwd("C:\\Users\\me"), "C:-Users-me");
|
||||
// Every non-alphanumeric flattens to `-`, including `:` and `\` on Windows paths.
|
||||
assert_eq!(encode_cwd("C:\\Users\\me"), "C--Users-me");
|
||||
}
|
||||
|
||||
/// Non-regression for the no-reply backstop wedge: an IdeA agent's isolated run dir
|
||||
/// contains a `.` (`/.ideai/`), which Claude encodes as `-` just like `/`. The `/.`
|
||||
/// must therefore collapse to a **double** dash, matching the real on-disk folder
|
||||
/// `-home-anthony-Documents-Projects-IdeA--ideai-run-<uuid>`. The earlier `/`-only
|
||||
/// encoding produced `-IdeA-.ideai-run-…` (nonexistent) and broke the turn watcher.
|
||||
#[test]
|
||||
fn encode_cwd_encodes_dot_in_run_dir_to_double_dash() {
|
||||
let uuid = "73c853d1-c0fd-463b-ad17-1d24fefa371f";
|
||||
let cwd = format!("/home/anthony/Documents/Projects/IdeA/.ideai/run/{uuid}");
|
||||
assert_eq!(
|
||||
encode_cwd(&cwd),
|
||||
format!("-home-anthony-Documents-Projects-IdeA--ideai-run-{uuid}")
|
||||
);
|
||||
// The hyphens already present inside the UUID survive (they are non-alphanumeric,
|
||||
// re-encoded to the same `-`), so the segment matches byte-for-byte.
|
||||
assert!(encode_cwd(&cwd).ends_with(uuid));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user