Skip to content

[luv-286] fix: detect PATH-shadowed failproofai installs at install + run time#286

Merged
NiveditJain merged 2 commits into
mainfrom
luv-286
May 5, 2026
Merged

[luv-286] fix: detect PATH-shadowed failproofai installs at install + run time#286
NiveditJain merged 2 commits into
mainfrom
luv-286

Conversation

@NiveditJain

@NiveditJain NiveditJain commented May 5, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds scripts/install-diagnosis.mjs (Node built-ins only, never throws) that resolves the PATH-first failproofai via command -v / where, compares it against the running install, and emits a copy-pasteable cleanup command for the offending install (bun-side cleanup targets ~/.bun/bin/failproofai and the bun globals dir; npm-side uses npm rm -g failproofai).
  • Wires diagnosis into scripts/postinstall.mjs (warns at install time when the just-installed copy is shadowed) and scripts/launch.ts (rewrites the existing Cannot find server.js at: runtime error to name the actual stale install when the cause is a PATH shadow rather than a broken build).
  • 7 new unit tests in __tests__/scripts/install-diagnosis.test.ts covering same-install / bun-link shadow / bun-global shadow / secondary-npm shadow / nothing on PATH / corrupt package.json / npm root -g failure.

Why

Customers who previously ran bun install -g failproofai (or any contributor whose prior bun run dev/start triggered bun link via predev/prestart) end up with ~/.bun/bin/failproofai shadowing later npm install -g failproofai@<new> updates because ~/.bun/bin sorts ahead of npm's prefix on PATH. The current runtime error blames "missing build output" and recommends npm install -g failproofai@latest — which doesn't help when the new install is itself being shadowed. Concrete repro this fix targets:

$ npm install -g failproofai@0.0.10-beta.0
$ failproofai
... v0.0.9-beta.3 ...
Error: Cannot find server.js at:
  /home/nivedit/prs/failproofai-281/.next/standalone/server.js

After this change: postinstall warns immediately at install time naming both paths + cleanup command; if the customer misses that warning, the runtime error explains the real cause + exact fix instead of recommending a no-op upgrade.

Test plan

  • bun run test:run (1451 unit tests pass, incl. 7 new)
  • bun run lint (clean)
  • bunx tsc --noEmit (clean)
  • bun run build:cli (bundles cleanly)
  • Manual repro: from a checkout, bun run build:cli && bun link, then npm install -g failproofai@<latest beta> from elsewhere; verify postinstall prints shadow warning and failproofai then prints the shadow-shaped error
  • bun run test:e2e
  • Docker clean-install test per CLAUDE.md "Testing protocol" step 3 - postinstall should exit 0 in the no-shadow case

Generated with Claude Code.

Summary by CodeRabbit

  • Bug Fixes

    • Improved detection and error messaging for cases where an older failproofai installation on the system's PATH shadows the newly installed version.
    • Installation and startup now provide specific cleanup recommendations (e.g., npm rm -g or bun unlink commands) when shadowing is detected.
  • Tests

    • Added comprehensive test suite for shadow installation detection logic.

A stale `bun link` from a contributor's prior `bun run dev` (or a
`bun install -g failproofai` whose ~/.bun/bin sorts ahead of npm's
prefix) shadows later `npm install -g failproofai@<new>` updates.
Customers then see a confusing "Cannot find server.js at:
<old-dev-tree>/.next/standalone/server.js" runtime error pointing at
the shadowed install — and the recommended fix
(`npm install -g failproofai@latest`) doesn't help when the new
install is itself being shadowed.

New scripts/install-diagnosis.mjs resolves the PATH-first failproofai
via `command -v` (POSIX) / `where` (Win32), compares its package root
+ version against the running install, and emits a copy-pasteable
cleanup command. Surfaced in two places: scripts/postinstall.mjs
warns at install time before any broken run, and scripts/launch.ts
rewrites the existing missing-server.js error to name the actual
stale install when the cause is a shadow rather than a broken build.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@NiveditJain has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 54 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ffb816a5-8663-421f-afb6-f2ec83feb696

📥 Commits

Reviewing files that changed from the base of the PR and between e1e40e9 and 669e818.

📒 Files selected for processing (3)
  • __tests__/scripts/install-diagnosis.test.ts
  • scripts/install-diagnosis.mjs
  • scripts/launch.ts
📝 Walkthrough

Walkthrough

This PR introduces automatic detection of PATH-shadowed failproofai installations. A new diagnostic module probes the system PATH, npm, and bun global installs to identify when an older version shadows the running copy, then integrates warnings into postinstall and launch flows with actionable cleanup commands.

Changes

Path Shadow Detection and Recovery

Layer / File(s) Summary
Core Diagnostic Logic
scripts/install-diagnosis.mjs
Exports diagnoseShadow() function that probes PATH via command -v/where, locates package roots, reads versions from package.json, and detects npm/bun global installs. Returns structured diagnostics with shadowed flag, path/version details, and cleanup recommendation.
Postinstall Integration
scripts/postinstall.mjs
Calls diagnoseShadow() during install and prints warning with paths/versions plus uninstall recommendation if shadowing is detected; does not fail the install on errors.
Launch Error Handling
scripts/launch.ts
When server.js is missing in start mode, calls diagnoseShadow() to provide detailed error message with shadow details and cleanup command, falling back to generic error if no shadow is found.
Tests & Documentation
__tests__/scripts/install-diagnosis.test.ts, CHANGELOG.md
Comprehensive test suite covering 7 scenarios: same install, bun shadow, npm shadow, no shadow, corrupted package.json, npm root -g failure, and version mismatches. CHANGELOG documents the new troubleshooting flow.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A rabbit finds a shadow on the PATH,
An older friend blocking the newest bath!
Command -v discovers the hidden imposter,
Then nudges the user toward a prompt fix—
npm rm -g the ghost, no more tricks! 🔧

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main change: detecting PATH-shadowed failproofai installs at install and runtime.
Description check ✅ Passed The description covers all required template sections (description, type of change, checklist) with comprehensive detail about what was added and why, and includes test status.
Docstring Coverage ✅ Passed Docstring coverage is 90.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/launch.ts (1)

53-78: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add a launch-level unit test for the new stale-shadow error path.

This TypeScript file introduces new runtime behavior but there’s no direct test shown for message selection when server.js is missing and diagnostics indicate an alternate install.

As per coding guidelines, **/*.ts: Always add unit tests in __tests__/ for new behavior; never modify existing tests just to make them pass.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/launch.ts` around lines 53 - 78, Add a unit test in __tests__ that
exercises the new stale-shadow path by stubbing diagnoseShadow to return an
object with shadowed: true and populated fields (pathFirstPath,
pathFirstVersion, npmGlobalPath/bunGlobalPath,
npmGlobalVersion/bunGlobalVersion, recommendation), then invoke the launch
behavior that computes shadowMessage (the code path using diagnoseShadow,
packageRoot, version, serverJsPath) and assert that console.error is called with
the shadowMessage (not the fallback serverJsPath message) and that
process.exit(1) is invoked; use spies/mocks for console.error and process.exit
and import or require the module entrypoint so the test runs the same logic in
scripts/launch.ts (reference symbols: diagnoseShadow, shadowMessage,
serverJsPath, packageRoot, version).
🧹 Nitpick comments (1)
__tests__/scripts/install-diagnosis.test.ts (1)

88-129: ⚡ Quick win

Add an explicit assertion for bun-link remediation output in this scenario.

This case is the highest-risk path, but it currently doesn’t assert diag.recommendation. Adding that assertion would lock in the expected cleanup command and prevent regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@__tests__/scripts/install-diagnosis.test.ts` around lines 88 - 129, Add an
assertion that diag.recommendation explicitly suggests remediating the
bun-linked dev tree in this shadowing scenario: after calling diagnoseShadow
(and existing expects), assert that diag.recommendation contains the
BUN_LINKED_PKG identifier (and optionally NPM_GLOBAL_PKG) so the message
recommends removing or unlinking the bun-linked dev tree (i.e., the expected
cleanup command/path is locked in); reference diagnoseShadow and the
diag.recommendation field when adding this expectation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/install-diagnosis.mjs`:
- Around line 108-116: The cleanup command selection in buildRecommendation
currently only checks pathFirstPackageRoot and misses bun-link cases; update
buildRecommendation (function name) to detect bun installations by also checking
for the existence of bun shadow files/dirs (e.g.,
homedir()+"/.bun/bin/"+PKG_NAME and
homedir()+"/.bun/install/global/node_modules/"+PKG_NAME) or whether PATH
contains homedir()+"/.bun/bin" and treat those as isBun true; implement this by
importing fs and using fs.existsSync on those bun paths (and optionally inspect
process.env.PATH), then return the same bun cleanup string when found so
bun-link shadows get the correct rm -f and rm -rf commands.

In `@scripts/launch.ts`:
- Around line 59-68: The current guard uses only diag.shadowed so it misses the
case where the PATH-first binary lives in the same package root but is a stale
runtime; update the condition around the diagnoseShadow result to also detect a
mismatched PATH-first version or missing build when diag.pathFirstPath ===
diag.selfPackageRoot: e.g. change the if to `if (diag.shadowed ||
(diag.pathFirstPath === diag.selfPackageRoot && diag.pathFirstVersion &&
diag.pathFirstVersion !== version))` (or the equivalent using
packageRoot/version variables) so the stale-install message is shown when the
PATH-first copy is the same root but has a different/older version; keep using
diagnoseShadow, diag.pathFirstPath, diag.selfPackageRoot, diag.pathFirstVersion,
packageRoot and version to locate the logic and produce shadowMessage.

---

Outside diff comments:
In `@scripts/launch.ts`:
- Around line 53-78: Add a unit test in __tests__ that exercises the new
stale-shadow path by stubbing diagnoseShadow to return an object with shadowed:
true and populated fields (pathFirstPath, pathFirstVersion,
npmGlobalPath/bunGlobalPath, npmGlobalVersion/bunGlobalVersion, recommendation),
then invoke the launch behavior that computes shadowMessage (the code path using
diagnoseShadow, packageRoot, version, serverJsPath) and assert that
console.error is called with the shadowMessage (not the fallback serverJsPath
message) and that process.exit(1) is invoked; use spies/mocks for console.error
and process.exit and import or require the module entrypoint so the test runs
the same logic in scripts/launch.ts (reference symbols: diagnoseShadow,
shadowMessage, serverJsPath, packageRoot, version).

---

Nitpick comments:
In `@__tests__/scripts/install-diagnosis.test.ts`:
- Around line 88-129: Add an assertion that diag.recommendation explicitly
suggests remediating the bun-linked dev tree in this shadowing scenario: after
calling diagnoseShadow (and existing expects), assert that diag.recommendation
contains the BUN_LINKED_PKG identifier (and optionally NPM_GLOBAL_PKG) so the
message recommends removing or unlinking the bun-linked dev tree (i.e., the
expected cleanup command/path is locked in); reference diagnoseShadow and the
diag.recommendation field when adding this expectation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5de8bc8f-4401-4f5a-8b7f-4f64ce76ed78

📥 Commits

Reviewing files that changed from the base of the PR and between 0217c09 and e1e40e9.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • __tests__/scripts/install-diagnosis.test.ts
  • scripts/install-diagnosis.mjs
  • scripts/launch.ts
  • scripts/postinstall.mjs

Comment thread scripts/install-diagnosis.mjs Outdated
Comment thread scripts/launch.ts
(1) buildRecommendation now keys on `pathFirstBin` rather than the
realpath'd package root. In bun-link cases, realpath unwraps the link
into the dev-tree path (not under ~/.bun/), so the previous root-based
isBun check mis-classified bun-link shadows as npm and produced a
useless `npm rm -g` recommendation. Trusting the un-resolved binary
path catches both bun-link and bun-install shadows correctly.

(2) Shadow detection now also fires when `selfPackageRoot` matches
`pathFirstPath` but a different failproofai exists at the npm or bun
global. This is the runtime stale-binary case the diagnosis was
designed for: the user is running the shadow, so the two roots agree,
and the previous "self != path-first" check returned false there. The
launch.ts caller now picks the alternate install (npm or bun global,
whichever differs from path-first) for the "Newer copy:" line so we
never point the user back at the same binary they're already running.

Adds an explicit recommendation assertion to the existing bun-link
test plus a new test covering the runtime stale-binary scenario.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@NiveditJain NiveditJain merged commit 3e9a209 into main May 5, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant