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.
|
/// Encodes a cwd the way Claude Code names its per-project transcript folder.
|
||||||
///
|
///
|
||||||
/// **Assumption (documented, isolated, and unit-tested):** Claude flattens the
|
/// **Verified against real `~/.claude/projects/` folders:** Claude flattens the absolute
|
||||||
/// absolute cwd into a single directory segment by replacing each path separator
|
/// cwd into a single directory segment by replacing **every character that is not an ASCII
|
||||||
/// (`/` or `\`) — and the leading separator — with `-`. So
|
/// alphanumeric** (`[a-zA-Z0-9]`) with `-` — this is Claude Code's `replace(/[^a-zA-Z0-9]/g,
|
||||||
/// `/home/anthony/Documents/Projects/IdeA` becomes
|
/// '-')`. Crucially that means **`.` is encoded too**, not just path separators: a run dir
|
||||||
/// `-home-anthony-Documents-Projects-IdeA`. We keep this convention in one small,
|
/// like `/home/anthony/Documents/Projects/IdeA/.ideai/run/<uuid>` becomes
|
||||||
/// tested function so that if Claude's exact encoding differs in some edge case, only
|
/// `-home-anthony-Documents-Projects-IdeA--ideai-run-<uuid>` (the `/.` collapses to a
|
||||||
/// this seam needs to change. We do not attempt to encode `.` or other characters, as
|
/// **double** dash). The earlier version only replaced `/` and `\`, leaving the `.`, so it
|
||||||
/// the common case (clean absolute project paths) is covered and over-encoding would
|
/// computed `-IdeA-.ideai-run-…` — a folder that never exists on disk; the turn-watcher
|
||||||
/// risk pointing at the wrong folder.
|
/// 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 {
|
pub(crate) fn encode_cwd(cwd: &str) -> String {
|
||||||
cwd.chars()
|
cwd.chars()
|
||||||
.map(|c| if c == '/' || c == '\\' { '-' } else { c })
|
.map(|c| if c.is_ascii_alphanumeric() { c } else { '-' })
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,8 +85,26 @@ mod tests {
|
|||||||
"-home-anthony-Documents-Projects-IdeA"
|
"-home-anthony-Documents-Projects-IdeA"
|
||||||
);
|
);
|
||||||
assert_eq!(encode_cwd("/a/b"), "-a-b");
|
assert_eq!(encode_cwd("/a/b"), "-a-b");
|
||||||
// Windows-style separators flatten too.
|
// Every non-alphanumeric flattens to `-`, including `:` and `\` on Windows paths.
|
||||||
assert_eq!(encode_cwd("C:\\Users\\me"), "C:-Users-me");
|
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]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user