diff --git a/ROADMAP.md b/ROADMAP.md index 94d2a39..da22180 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1407,3 +1407,36 @@ Original filing (2026-04-13): user requested a `-acp` parameter to support ACP p **Blocker.** None. Scope is ~30 lines of glue (helper + call-site sweep + one regression test). Breakage risk is low — the only consumers that deliberately read `DEFAULT_DATE` *as* today are the ones being fixed; `claw version` / `--version` keeps its honest compile-time meaning. **Source.** Jobdori dogfood 2026-04-17 against `/tmp/cd3` on main HEAD `e58c194` in response to Clawhip pinpoint nudge at `1494653681222811751`. Distinct from #80/#81/#82 (status/error surfaces lie about *static* runtime state): this is a surface that lies about *time itself*, and the lie is smeared into every live-agent system prompt, not just a single error string or status field. + +84. **`claw dump-manifests` default search path is the build machine's absolute filesystem path baked in at compile time — broken and information-leaking for any user running a distributed binary** — dogfooded 2026-04-17 on main HEAD `70a0f0c` from `/tmp/cd4` (fresh workspace). Running `claw dump-manifests` with no arguments emits: + ``` + error: Manifest source files are missing. + repo root: /Users/yeongyu/clawd/claw-code + missing: src/commands.ts, src/tools.ts, src/entrypoints/cli.tsx + Hint: set CLAUDE_CODE_UPSTREAM=/path/to/upstream or pass `claw dump-manifests --manifests-dir /path/to/upstream`. + ``` + `/Users/yeongyu/clawd/claw-code` is the *build machine's* absolute path (mine, in this dogfood; whoever compiled the binary, in the general case). The path is baked into the binary as a raw string: `strings rust/target/release/claw | grep '^/Users/'` → `/Users/yeongyu/clawd/claw-code/rust/crates/rusty-claude-cli../..`. JSON surface (`claw --output-format json dump-manifests`) leaks the same path verbatim. + + **Trace path — how the compile-time path becomes the default runtime search root.** + - `rust/crates/rusty-claude-cli/src/main.rs:2012-2018` — `dump_manifests()` computes `let workspace_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..");`. `env!` is *compile-time*: whatever `$CARGO_MANIFEST_DIR` was when `cargo build` ran gets baked in. On my machine that's `/Users/yeongyu/clawd/claw-code/rust/crates/rusty-claude-cli` → plus `../..` → `/Users/yeongyu/clawd/claw-code/rust`. + - `rust/crates/compat-harness/src/lib.rs:28-37` — `UpstreamPaths::from_workspace_dir(workspace_dir)` takes `workspace_dir.parent()` as `primary_repo_root` → `/Users/yeongyu/clawd/claw-code`. `resolve_upstream_repo_root` (lines 63-69 and 71-93) then walks a candidate list: the primary root itself, `CLAUDE_CODE_UPSTREAM` if set, ancestors' `claw-code`/`clawd-code` directories up to 4 levels, and `reference-source/claw-code` / `vendor/claw-code` under the primary root. If none contain `src/commands.ts`, it unwraps to the primary root. Result: on every user machine that is not the build machine, the default lookup targets a path that doesn't exist on that user's system. + - `rust/crates/rusty-claude-cli/src/main.rs:2044-2049` (`Manifest source directory does not exist`) and `:2062-2068` (`Manifest source files are missing. … repo root: …`) and `:2088-2093` (`failed to extract manifests: … looked in: …`) all format `source_root.display()` or `paths.repo_root().display()` into the error string. Since the source root came from `env!("CARGO_MANIFEST_DIR")` at compile time, that compile-time absolute path is what every user sees in the error. + + **Direct confirmation.** I rebuilt a fresh binary on the same machine (HEAD `70a0f0c`, build date `2026-04-17`) and reproduced cleanly: default `dump-manifests` says `repo root: /Users/yeongyu/clawd/claw-code`, `--manifests-dir=/tmp/fake-upstream` with the three expected `.ts` files succeeds (`commands: 0 tools: 0 bootstrap phases: 2`), and `--manifests-dir=/nonexistent` emits `Manifest source directory does not exist.\n looked in: /nonexistent` — so the override plumbing works *once the user already knows it exists.* The first-contact experience still dumps the build machine's path. + + **Why this is specifically a clawability gap and not just a cosmetic bug.** + 1. *Broken default for any distributed binary.* A claw or operator running a packaged/shipped `claw` binary on their own machine will see a path they do not own, cannot create, and cannot reason about. The error surface advertises a default behavior that is contingent on the end user having reconstructed the build machine's filesystem layout verbatim. + 2. *Privacy leak.* The build machine's absolute filesystem path — including the compiling user's `$HOME` segment (`/Users/yeongyu`) — is baked into the binary and surfaced to every recipient who ever runs `dump-manifests` without `--manifests-dir`. This lands in logs, CI output, transcripts, bug reports, the binary itself. For a tool that aspires to be embedded in clawhip / batch orchestrators this is a sharp edge. + 3. *Reproducibility violation.* Two binaries built from the same source at the same commit but on different machines produce different runtime behavior for the *default* `dump-manifests` invocation. This is the same reproducibility-breaking shape as ROADMAP #83 (build date injected as "today") — compile-time context leaking into runtime decisions. + 4. *Discovery gap.* The hint correctly names `CLAUDE_CODE_UPSTREAM` and `--manifests-dir`, but the user only learns about them *after* the default has already failed in a confusing way. A clawhip running this probe to detect whether an upstream manifest source is available cannot distinguish "user hasn't configured an upstream path yet" from "user's config is wrong" from "the binary was built on a different machine" — same error in all three cases. + + **Fix shape — three pieces, all small.** + 1. *Drop the compile-time default.* Remove `env!("CARGO_MANIFEST_DIR")` from the runtime default path in `main.rs:2016`. Replace with either (a) `env::current_dir()` as the starting point for `resolve_upstream_repo_root`, or (b) a hardcoded `None` that requires `CLAUDE_CODE_UPSTREAM` / `--manifests-dir` / a settings-file entry before any lookup happens. + 2. *When the default is missing, fail with a user-legible message — not a leaked absolute path.* Example: `dump-manifests requires an upstream Claude Code source checkout. Set CLAUDE_CODE_UPSTREAM or pass --manifests-dir /path/to/claude-code. No default path is configured for this binary.` No compile-time path, no `$HOME` leak, no confusing "missing files" message for a path the user never asked for. + 3. *Add a `claw config upstream` / `settings.json` `[upstream]` entry* so the upstream source path is a first-class, persisted piece of workspace config — not an env var or a command-line flag the user has to remember each time. Matches the settings-based approach used elsewhere (e.g. the `trusted_roots` gap called out in the 2026-04-08 startup-friction note). + + **Acceptance.** A `claw` binary built on machine A and run on machine B (same architecture, different filesystem layout) emits a default `dump-manifests` error that contains zero absolute path strings from machine A; the error names the required env var / flag / settings entry; `strings | grep '^/Users/'` and equivalent on Linux (`^/home/`) for the packaged binary returns empty. + + **Blocker.** None. Fix 1 + 2 is ≤20 lines in `rusty-claude-cli/src/main.rs:2010-2020` plus error-string rewording. Fix 3 is optional polish that can land separately; it is not required to close the information-leak / broken-default core. + + **Source.** Jobdori dogfood 2026-04-17 against `/tmp/cd4` on main HEAD `70a0f0c` (freshly rebuilt on the dogfood machine) in response to Clawhip pinpoint nudge at `1494661235336282248`. Sibling to #83 (build date → "today") and to the 2026-04-08 startup-friction note ("no default `trusted_roots` in settings"): all three are compile-time or batch-time context bleeding into a surface that should be either runtime-resolved or explicitly configured. Distinct from #80/#81/#82 (surfaces misrepresent runtime state) — here the runtime state being described does not even belong to the user in the first place.