Drop the workspaceRoot parameter from resolveToolchainPaths /
Toolchain.resolvePaths entirely. The old code resolved
\`<workspaceRoot>/native/dugite/bin/git\` as the git binary before
falling back to PATH — a malicious repo could commit an executable
at that path and clide would run it on the first auto-fired
git.status (which fires automatically on workspace open).
Dugite now resolves against trusted locations only:
1. CLIDE_DUGITE_DIR env var (dev override).
2. <exe-parent>/dugite/bin/git (production bundle).
3. <exe-parent>/lib/dugite/bin/git (alternate bundle layout).
Test plants `native/dugite/bin/git` in a temp workspace and asserts
the resolved git path is NOT inside the workspace.
Callers updated (8 sites): main.dart, backend_entry.dart twice,
test_app.dart three times (compute now wraps a no-arg call), plus
five test fixtures. backend.dart's now-vestigial hintRoot left in
the struct for cleanup under T-99.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`lib/kernel/src/toolchain.dart` is a `ChangeNotifier`, so it pulls in
`package:flutter/foundation.dart`. `GitClient` and `PqlClient` imported
it for the resolved binary paths, which leaked Flutter through the
`package:clide/clide.dart` barrel — breaking `dart test` on every core
subsystem suite (`ci/test_core.sh`), since pure Dart can't compile
Flutter packages.
Split the Flutter-free pieces into `toolchain_paths.dart`: `ResolvedPaths`,
`resolveToolchainPaths`, and a new read-only `ToolchainView` interface
with a `ToolchainView.resolved()` const factory. `Toolchain` now
implements `ToolchainView`; the clients depend on the interface. Core
test setups that built a `Toolchain` just to call `applyResolved`
switch to the factory.
Co-Authored-By: Claude <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 29s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m0s
23 integration tests in test/git/client_test.dart driving a real git
binary against per-test sandbox repos. Covers every GitClient public
method that wasn't already exercised through the daemon command
suite:
- Queries: status (clean + with upstream tracking ahead/behind),
diff with explicit paths, log, currentBranch, branches (with the
current-marker check), repoRoot (in-repo + non-repo paths).
- Mutations: unstage with paths, commit-returns-hash, discard,
discard with empty list, stash + stashPop with includeUntracked,
checkout, stageHunk + unstageHunk via _applyPatch.
- Error surface: mutations throw GitException on non-zero exit,
_applyPatch surfaces stderr from a bad patch, bad git binary path
→ ProcessException → GitException, queries return empty fallbacks
on a non-git workDir.
- parseLog standalone parser: empty input, short-record skip, full
record with body, 5-field record without body.
Coverage: src/git/client.dart 76/141 -> 124/141 (88%). The remaining
17 lines are the pull / push paths (would need a real remote with
matching credentials — out of scope) and the status GitException
catch branches that require an in-flight git process failure mid-call.
Total coverage 75.42% -> 76.04%; floor bumped to 76.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>