docs(governance): D-106 per-workspace PATH preset (T-511)

Decides FR-1: a machine-local, user-scope list of dirs prepended to the
PATH of every spawned shell, keyed by repo identity so linked worktrees
(.worktrees/) share their main repo's preset. Prepend-over-login-shell
semantics, D-6 CLI parity (env path verbs), and a security fence: the
preset never feeds clide's own binary resolution (T-98 lesson).

Also corrects D-104's cross-references: the bundled-pql decision is
D-92, not D-58 (D-58 is format engines) — the anchor was dead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 17:40:28 +02:00
co-authored by Claude Fable 5
parent bd3acf38e9
commit 1ac60d1fe7
4 changed files with 28 additions and 2 deletions
+1
View File
@@ -147,6 +147,7 @@ You might also want, project-permitting:
- [D-103: Drawing-card primitive layer is SVG; the SVG renderer is the engine](decisions/architecture.md#d-103-drawing-card-primitive-layer-is-svg-the-svg-renderer-is-the-engine) — _architecture_
- [D-104: Explicit supporter-binary path overrides in user-scope settings](decisions/tooling.md#d-104-explicit-supporter-binary-path-overrides-in-user-scope-settings) — _tooling_
- [D-105: Support Vibe CLI as opt-in alternative to Claude Code CLI](decisions/llm.md#d-105-support-vibe-cli-as-opt-in-alternative-to-claude-code-cli) — _llm_
- [D-106: Per-workspace PATH preset prepended at spawn](decisions/tooling.md#d-106-per-workspace-path-preset-prepended-at-spawn) — _tooling_
## Open questions
+10 -2
View File
@@ -113,8 +113,16 @@ Toolchain, supply chain, CI, ignore strategy.
### D-104: Explicit supporter-binary path overrides in user-scope settings
- **Date:** 2026-06-28
- **Decision:** clide resolves each **external** supporter binary (`claude`, `d2`, and future supporter tools — **not** bundled `pql`/`git`, which keep [D-58]/[D-59]) through an **explicit `tools:` map in user-scope settings**: tool-name → absolute path. The map holds concrete, user-visible, user-editable paths and is the **first** step in resolution — if an entry is set, clide uses that exact path (honest error if it is missing / not executable, falling **down the chain with a warning** rather than hard-failing). On **first run**, clide auto-detects each tool **once** — probing the login-shell PATH (T-439) plus well-known dirs (`~/.local/bin`, `/usr/local/bin`, `/opt/homebrew`, Homebrew-on-Linux `/home/linuxbrew/.linuxbrew/bin`) — and **writes the discovered absolute paths into the map**, so detection is a one-time *populate*, not a per-launch heuristic; thereafter the pinned explicit value wins. A **re-detect** action re-runs the probe (e.g. after installing a tool). Unset/undetected tools fall back to the existing chain (bundled/pinned per [D-58]/[D-59], then login-shell PATH). **User-scope only, keyed by machine ([D-93]) — never committed** (absolute paths are machine-specific). Generalizes [D-58]'s `CLIDE_PQL_BIN` override to every supporter tool; surfaced in the Config tab and the Problems panel for unresolved tools.
- **Decision:** clide resolves each **external** supporter binary (`claude`, `d2`, and future supporter tools — **not** bundled `pql`/`git`, which keep [D-92]/[D-59]) through an **explicit `tools:` map in user-scope settings**: tool-name → absolute path. The map holds concrete, user-visible, user-editable paths and is the **first** step in resolution — if an entry is set, clide uses that exact path (honest error if it is missing / not executable, falling **down the chain with a warning** rather than hard-failing). On **first run**, clide auto-detects each tool **once** — probing the login-shell PATH (T-439) plus well-known dirs (`~/.local/bin`, `/usr/local/bin`, `/opt/homebrew`, Homebrew-on-Linux `/home/linuxbrew/.linuxbrew/bin`) — and **writes the discovered absolute paths into the map**, so detection is a one-time *populate*, not a per-launch heuristic; thereafter the pinned explicit value wins. A **re-detect** action re-runs the probe (e.g. after installing a tool). Unset/undetected tools fall back to the existing chain (bundled/pinned per [D-92]/[D-59], then login-shell PATH). **User-scope only, keyed by machine ([D-93]) — never committed** (absolute paths are machine-specific). Generalizes [D-58]'s `CLIDE_PQL_BIN` override to every supporter tool; surfaced in the Config tab and the Problems panel for unresolved tools.
- **Rationale:** PATH-probing (T-439) is adaptive but brittle on non-standard installs — a login-shell probe misses Homebrew when `brew shellenv` lives only in `~/.bashrc` (login shells source `.bash_profile`/`.profile`, not `.bashrc`). Materializing resolution into explicit, pinned paths makes it deterministic and debuggable: "tool not found" becomes a one-line settings fix, and the value is *visible* rather than recomputed by heuristic each launch. First-run auto-detect keeps it zero-config for standard installs; **pinning the result instead of re-probing is what makes it explicit** — escaping the heuristic fragility while keeping the convenience.
- **Cost:** A first-run detection pass plus a small settings surface. Stale pins (a tool moved on a brew upgrade) must fall back + warn, not hard-fail. The map is per-machine, so it does not travel with the repo — each machine detects once.
- **Cross-reference:** [D-58](#d-58-ship-pql-bundled-with-clide) (pql resolution + `CLIDE_PQL_BIN` override), [D-59](#d-59-bundled-git-via-dugite-native), [D-93](architecture.md#d-93-clide-writes-no-directories-of-its-own-into-the-workspace) (user-scope state), T-439 (login-shell-derived PATH), T-494 (the d2 template — first consumer).
- **Cross-reference:** [D-92](#d-92-ship-pql-bundled-with-clide) (pql resolution + `CLIDE_PQL_BIN` override), [D-59](#d-59-bundled-git-via-dugite-native), [D-93](architecture.md#d-93-clide-writes-no-directories-of-its-own-into-the-workspace) (user-scope state), T-439 (login-shell-derived PATH), T-494 (the d2 template — first consumer).
- **Raised by:** 2026-06-28 — user, during the drawing-card SVG/d2 work after the login-shell PATH probe was seen to miss linuxbrew: "should we just add the hard paths to the supporting binaries to the clide settings file?" + "auto detect on first run sounds solid" — explicit pinned paths, populated by a one-time first-run probe.
### D-106: Per-workspace PATH preset prepended at spawn
- **Date:** 2026-07-13
- **Decision:** Each workspace can carry a **preset list of directories prepended to the PATH** clide hands to everything it spawns a shell for: the **hosted Claude session** (so the agent's Bash tool sees it — the [D-83](architecture.md#d-83-dogfood-agent-model--hosted-stream-json-session-primary-external-cli-driver-secondary) bootstrap seam that already injects `CLIDE_SOCK`/`CLIDE_WORKSPACE`) and **terminal PTY panes**. Prepend semantics: preset dirs win over the resolved login-shell PATH (T-439), applied at spawn time — no profile sourcing, no login-shell requirement, and only NEW spawns see an edit. Storage is **user-scope, keyed by workspace hash ([D-93]) — never committed** (`app.env.pathPrepend.<hash>`; absolute paths are machine-specific, and a committed preset would let a cloned repo inject PATH). The hash keys off the **repo identity, not the literal directory**: a workspace that is a linked git worktree (its `.git` is a `gitdir:` pointer file, e.g. under an in-repo `.worktrees/` dir — supported, and gitignored by the scaffold) resolves to the **main repo root**, so every worktree shares its repo's preset. **Security fence:** the preset feeds only spawned-shell environments — clide's own binary resolution (pinned `tools:` per [D-104](#d-104-explicit-supporter-binary-path-overrides-in-user-scope-settings), bundled pql/git per [D-92](#d-92-ship-pql-bundled-with-clide)/[D-59](#d-59-bundled-git-via-dugite-native), the toolchain probe) never consults it (the T-98 dugite lesson). Parity ([D-6](architecture.md#d-6-cli-and-event-surface-contract)): `clide env path list|set|add|remove|clear|capture` mirrors the settings-panel editor; `capture` suggests entries by diffing the login-shell PATH against the process PATH — "set once by either party".
- **Rationale:** The T-439 login-shell probe is a best-effort global heuristic with a known residual hole — a login-but-non-interactive probe misses interactive-only profile additions (brew `shellenv` in `~/.bashrc`), which is exactly the observed failure (Go toolchain invisible to the agent in the pql repo, 2026-07-08). [D-104] made single binaries explicit; this extends the same explicit-over-heuristic philosophy to whole directories, per repo, visible and editable by both the user and the agent instead of re-discovered per session.
- **Cost:** Per-machine — a preset does not travel with the repo and re-keys (resets) when a repo moves ([D-93] trade-off, same as account bindings). One more env layer every spawn site must route through the single `shell_env` resolver; already-running shells keep their env until respawned.
- **Cross-reference:** [D-104](#d-104-explicit-supporter-binary-path-overrides-in-user-scope-settings), [D-93](architecture.md#d-93-clide-writes-no-directories-of-its-own-into-the-workspace), [D-83](architecture.md#d-83-dogfood-agent-model--hosted-stream-json-session-primary-external-cli-driver-secondary), [D-6](architecture.md#d-6-cli-and-event-surface-contract), [D-92](#d-92-ship-pql-bundled-with-clide), [D-59](#d-59-bundled-git-via-dugite-native), T-439 (login-shell PATH resolver), T-484 (spawn-time `CLAUDE_CONFIG_DIR` injection — the per-workspace spawn-env precedent).
- **Raised by:** 2026-07-08 — FR-1, logged from a pql session where the agent shell could not find the Go toolchain: "a settings-panel option (per repo/workspace): a list of PATH entries to prepend, set once, then injected everywhere clide spawns a shell"; worktree keying added 2026-07-13 — user: "make sure the PATH settings take usage of worktrees inside a .worktrees dir inside the repo into account".