From 647ff379a4ea9c5efc4cc1b98019cb27cf18caa7 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Wed, 8 Apr 2026 15:30:04 +0900 Subject: [PATCH] docs(roadmap): file dev/rust plugin-validation host-home leak as backlog #27 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Filing per gaebal-gajae's status summary at message 1491322807026454579 in #clawcode-building-in-public, with corrected scope after re-running `cargo test -p rusty-claude-cli` against main HEAD (79da4b8): the 11 deterministic failures only reproduce on dev/rust, not main, so this is a dev/rust catchup item rather than a main regression. Two-layered root cause documented: 1. dev/rust `parse_args` eagerly validates user plugin hook scripts exist on disk before returning a CliAction 2. dev/rust test harness does not redirect $HOME/XDG_CONFIG_HOME to a fixture (no `env_lock` equivalent — main has 30+ env_lock hits, dev has zero) Together they make dev/rust `cargo test -p rusty-claude-cli` fail on any clean clone whose owner has a half-installed user plugin in ~/.claude/plugins/installed/. main has both the env_lock test isolation AND the parse_args/hook-validation decoupling already; dev/rust is just behind on the merge train. Action block in #27 spells out backporting env_lock + the parse_args decoupling so the next dev/rust release picks this up. --- ROADMAP.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ROADMAP.md b/ROADMAP.md index 86bc919..efde74d 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -327,6 +327,7 @@ Priority order: P0 = blocks CI/green state, P1 = blocks integration wiring, P2 = 24. **Plugin lifecycle init/shutdown test flakes under workspace-parallel execution** — dogfooding surfaced that `build_runtime_runs_plugin_lifecycle_init_and_shutdown` can fail under `cargo test --workspace` while passing in isolation because sibling tests race on tempdir-backed shell init script paths. This is test brittleness rather than a code-path regression, but it still destabilizes CI confidence and wastes diagnosis cycles. **Action:** isolate temp resources per test robustly (unique dirs + no shared cwd assumptions), audit cleanup timing, and add a regression guard so the plugin lifecycle test remains stable under parallel workspace execution. 25. **`plugins::hooks::collects_and_runs_hooks_from_enabled_plugins` flakes on Linux CI** — dogfooding on 2026-04-08 reproduced this twice (CI runs [24120271422](https://github.com/ultraworkers/claw-code/actions/runs/24120271422) and [24120538408](https://github.com/ultraworkers/claw-code/actions/runs/24120538408)), both times failing on first attempt and passing on rerun. Failure mode is `PostToolUse hook .../hooks/post.sh failed to start for "Read": Broken pipe (os error 32)` when spawning a child shell script. Passes consistently on macOS. **Suspected root cause:** `write_hook_plugin` in `rust/crates/plugins/src/hooks.rs` (~line 362) writes `pre.sh`, `post.sh`, and `failure.sh` via `fs::write` but never calls `fs::set_permissions` to add the execute bit. On Linux, directly `Command::new(path).spawn()`ing a shebang script without `+x` can succeed or fail depending on fork/exec ordering, which explains the flake pattern (pipe setup succeeds, exec fails after fork, yielding `Broken pipe` instead of a clean `Permission denied`). **Action:** in `write_hook_plugin`, after each `fs::write` of a `.sh` file, set mode `0o755` via `std::os::unix::fs::PermissionsExt` under `#[cfg(unix)]`, and add a regression guard that asserts the file is executable before the hook runner is invoked. 26. **Resumed local-command JSON parity gap** — **done**: direct `claw --output-format json` already had structured renderers for `sandbox`, `mcp`, `skills`, `version`, and `init`, but resumed `claw --output-format json --resume /…` paths still fell back to prose because resumed slash dispatch only emitted JSON for `/status`. Resumed `/sandbox`, `/mcp`, `/skills`, `/version`, and `/init` now reuse the same JSON envelopes as their direct CLI counterparts, with regression coverage in `rust/crates/rusty-claude-cli/tests/resume_slash_commands.rs` and `rust/crates/rusty-claude-cli/tests/output_format_contract.rs`. +27. **`dev/rust` `cargo test -p rusty-claude-cli` reads host `~/.claude/plugins/installed/` from real `$HOME` and fails parse-time on any half-installed user plugin** — dogfooding on 2026-04-08 (filed from gaebal-gajae's clawhip bullet at message `1491322807026454579` after the provider-matrix branch QA surfaced it) reproduced 11 deterministic failures on clean `dev/rust` HEAD of the form `panicked at crates/rusty-claude-cli/src/main.rs:3953:31: args should parse: "hook path \`/Users/yeongyu/.claude/plugins/installed/sample-hooks-bundled/./hooks/pre.sh\` does not exist; hook path \`...\post.sh\` does not exist"` covering `parses_prompt_subcommand`, `parses_permission_mode_flag`, `defaults_to_repl_when_no_args`, `parses_resume_flag_with_slash_command`, `parses_system_prompt_options`, `parses_bare_prompt_and_json_output_flag`, `rejects_unknown_allowed_tools`, `parses_resume_flag_with_multiple_slash_commands`, `resolves_model_aliases_in_args`, `parses_allowed_tools_flags_with_aliases_and_lists`, `parses_login_and_logout_subcommands`. **Same failures do NOT reproduce on `main`** (re-verified with `cargo test --release -p rusty-claude-cli` against `main` HEAD `79da4b8`, all 156 tests pass). **Root cause is two-layered.** First, on `dev/rust` `parse_args` eagerly walks user-installed plugin manifests under `~/.claude/plugins/installed/` and validates that every declared hook script exists on disk before returning a `CliAction`, so any half-installed plugin in the developer's real `$HOME` (in this case `~/.claude/plugins/installed/sample-hooks-bundled/` whose `.claude-plugin` manifest references `./hooks/pre.sh` and `./hooks/post.sh` but whose `hooks/` subdirectory was deleted) makes argv parsing itself fail. Second, the test harness on `dev/rust` does not redirect `$HOME` or `XDG_CONFIG_HOME` to a fixture for the duration of the test — there is no `env_lock`-style guard equivalent to the one `main` already uses (`grep -n env_lock rust/crates/rusty-claude-cli/src/main.rs` returns 0 hits on `dev/rust` and 30+ hits on `main`). Together those two gaps mean `dev/rust` `cargo test -p rusty-claude-cli` is non-deterministic on every clean clone whose owner happens to have any non-pristine plugin in `~/.claude/`. **Action (two parts).** (a) Backport the `env_lock`-based test isolation pattern from `main` into `dev/rust`'s `rusty-claude-cli` test module so each test runs against a temp `$HOME`/`XDG_CONFIG_HOME` and cannot read host plugin state. (b) Decouple `parse_args` from filesystem hook validation on `dev/rust` (the same decoupling already on `main`, where hook validation happens later in the lifecycle than argv parsing) so even outside tests a partially installed user plugin cannot break basic CLI invocation. **Branch scope.** This is a `dev/rust` catchup against `main`, not a `main` regression. Tracking it here so the dev/rust merge train picks it up before the next dev/rust release rather than rediscovering it in CI. 41. **Phantom completions root cause: global session store has no per-worktree isolation** —