fix: [#830] project_scan 32% failure rate — defensive spawn + degraded tracking
What does this PR do?
Fixes project_scan failing 32% of the time across 1,171 distinct users (telemetry-2026-05-21, 2,503 machines, 14 days). It's the first-interaction tool, so 32% failure at boot is a major conversion killer for new installs.
Two root causes (both addressed):
-
detectGit()had no try/catch aroundBun.spawnSync(["git", ...]). Bun's spawnSync THROWS when the binary is missing from PATH, not just when it exits non-zero. Any environment without git installed (minimal Docker images, fresh Windows installs without Git for Windows, some CI runners) crashed the entire tool — even though the other sub-detections didn't depend on git. -
The PII masker shredded the diagnostic. Bun's error
Executable not found in $PATH: "git"becameExecutable not found in $PATH: ?in telemetry (quoted strings →?), making the bug look like a generic shell failure and hiding it for two months.
Fix:
- New
safeSpawnSynchelper that catches the throw and returnsnullso callers see a deterministic "command failed" instead of an exception. Applied to everyBun.spawnSynccall indetectGit(). - Every detection in
ProjectScanTool.execute()now wraps with.catch()recording the failure to adegraded: string[]field and continuing with a sentinel result. - New "Degraded Detections" section in the formatted output +
degradedfield in metadata lets the LLM see exactly what was unavailable.
Type of change
- Bug fix (non-breaking change which fixes an issue)
Issue for this PR
Closes #830
How did you verify your code works?
tsgo --noEmit: pass, zero errorsbun test test/tool/project-scan.test.ts: 78 pass / 0 fail (1 new test)- New test "does not throw when git binary is missing from PATH" pins the regression — sets PATH to an empty directory, calls
detectGit(), asserts it returns{ isRepo: false }without throwing - Marker check: pass — no upstream-shared files touched
Checklist
- My code follows the project's code style
- I have performed a self-review of my own code
- My changes generate no new warnings
- I have added tests that prove my fix is effective
- New and existing unit tests pass locally with my changes
- Marker check passes
- Linked to issue #830
Files changed (2):
packages/opencode/src/altimate/tools/project-scan.ts—safeSpawnSynchelper,degradedtracking on every detection, Config.get() guarded, output surfaces degraded listpackages/opencode/test/tool/project-scan.test.ts— +1 test for the missing-git path
Expected telemetry impact: project_scan error rate drops from 32% to near-zero. Users without git get a successful scan with degraded: ["git"] instead of an opaque tool failure. Same defensive pattern protects against future Dispatcher/Config/Skill regressions that previously could crash the whole tool.
Related: Issue #827 / PR #828 fixed the finops_* 100% failure rate. This PR fixes the next P0 from the same triage doc (docs/internal/2026-05-21-p0-triage.md).
Summary by cubic
Make project_scan fail-safe by degrading partial detections instead of crashing, and now clearly distinguishes missing git, clean non-repo, and degraded git errors. Fixes #830 and should drop the 32% error rate to near-zero.
- Bug Fixes
- Added and exported
safeSpawnSync; routed allgitand tool-version checks through it withtimeoutsupport. detectGit()now returnsgitAvailableand optionalgitError; output shows "git not found" or "git error (exit N)" vs "Not a git repository";metadata.gitincludes both.- Wrapped all sub-detections and
Dispatchercalls with.catch(); track degraded as aSet, sort it, always emitmetadata.degraded, and append(<n> degraded)to the title; added a "Degraded Detections (n)" section in output. - Guarded
Config.get(); mark"skills","post-connect-suggestions","python-engine","warehouse.list","dbt.profiles","warehouse.discover","schema.cache_status", and"dbt.manifest"on failure. - Tests: added
safeSpawnSynccases (missing binary, success, non-zero, timeout) anddetectGitcontracts (gitAvailabletrue in normal runs; clean non-repo leavesgitErrorundefined); removed PATH-mutation approach.
- Added and exported
Summary by CodeRabbit
-
Bug Fixes
- Scan tolerates partial failures across sub-detections and records degraded items instead of aborting.
-
New Features
- Adds a "Degraded Detections" section, appends a degraded-count suffix to the title, and includes degraded details in scan metadata.
-
Improvements
- More clearly reports VCS availability vs. "not a repository" and treats failed tool checks as not-installed.
-
Tests
- Added tests for missing system binaries, non-zero exits, timeouts, and degraded-scan scenarios.