Skip to content

fix(doctor): checkDependencies and checkTrackedBakFiles read the code root - #4138

Open
Zoubeir23 wants to merge 1 commit into
career-ops-hq:mainfrom
Zoubeir23:Fix/3867-doctor-code-root-checks
Open

fix(doctor): checkDependencies and checkTrackedBakFiles read the code root#4138
Zoubeir23 wants to merge 1 commit into
career-ops-hq:mainfrom
Zoubeir23:Fix/3867-doctor-code-root-checks

Conversation

@Zoubeir23

@Zoubeir23 Zoubeir23 commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Fixes finding 6 of #3867.

checkDependencies() and checkTrackedBakFiles() both read projectRoot
(getCareerOpsRoot()), but node_modules and .git only ever live where
doctor.mjs itself sits. Under a split checkout (CAREER_OPS_ROOT /
CAREER_OPS_DATA_DIR, or the .career-ops-data marker) that data root is a
separate directory that never holds either.

jday013/maxmilian's proof on the issue: creating an empty node_modules/
inside the data root flips "Dependencies not installed" to "installed" even
though the real code checkout's own node_modules never moved. The inverse
holds for the .bak check — a data root with no .git silently skips a
warning that should have fired against the real code checkout.

Fix

Introduces codeRoot alongside projectRoot:

  • --target <path> keeps meaning "diagnose this whole other checkout" — both
    roots follow it, matching the existing coverage in
    tests/doctor-tracked-bak-files.test.mjs.
  • Otherwise codeRoot falls back to __dirname (where doctor.mjs lives).

Two call sites needed it, not one:

  • main()'s human-readable checks array.
  • The separate onboardingState() used by doctor.mjs --json — the form
    AGENTS.md has every agent run on the first message of every session — had
    its own, independently broken checkTrackedBakFiles(root) call. Found
    this while writing the test; it's the same bug hiding in a second place.

Tests

tests/doctor-code-root-checks.test.mjs (new): seeds a throwaway
CAREER_OPS_ROOT data root that is its own git repo with a tracked .bak
file and no node_modules, then asserts both doctor.mjs (plain) and
doctor.mjs --json report on the real code checkout instead. Mutation-checked
— reverting either call site independently reddens its assertion.

node test-all.mjs: 8729 passed, 0 failed.

Summary

doctor.mjs now separates codeRoot from projectRoot.

  • Dependency checks inspect codeRoot/node_modules.
  • Tracked .bak checks inspect the code checkout.
  • Plain-text and JSON output use the same corrected paths.
  • Split-checkout users no longer receive false warnings from the data root.

tests/doctor-code-root-checks.test.mjs:1-101 adds coverage for both output modes. Reported result: 8,729 passed and 0 failed.

Touched system files: doctor.mjs and tests/doctor-code-root-checks.test.mjs. No changes were reported for AGENTS.md, modes/, update-system.mjs, DATA_CONTRACT.md, providers/, or .github/.

… root

career-ops#3867 finding 6: both checks used projectRoot (getCareerOpsRoot()),
but node_modules and .git only ever live where doctor.mjs itself sits. Under a
split checkout (CAREER_OPS_ROOT/CAREER_OPS_DATA_DIR, or the .career-ops-data
marker) that data root is a separate directory that never holds either —
jday013/maxmilian's proof on the issue: an empty node_modules/ created inside
the data root flips 'Dependencies not installed' to 'installed', and a data
root with no .git silently skips a tracked-.bak warning that should have
fired against the real code checkout.

Introduces codeRoot alongside projectRoot: --target keeps meaning 'diagnose
this whole other checkout' (both roots follow it, matching the existing
tests/doctor-tracked-bak-files.test.mjs coverage), otherwise codeRoot falls
back to __dirname. Two call sites needed it, not one — main()'s checks array
AND the separate onboardingState() used by `doctor.mjs --json` (the form
AGENTS.md has every agent run on session start) each ran their own,
independently-broken checkTrackedBakFiles(root).

Adds tests/doctor-code-root-checks.test.mjs, mutation-verified: reverting
either call site reddens its assertion.

Credit: jday013 and maxmilian for the split-checkout reproduction on career-ops-hq#3867.
Copilot AI lite review requested due to automatic review settings September 13, 2026 07:20

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: ee0aaf5f-d82a-4fd7-9773-74f0121c2897

📥 Commits

Reviewing files that changed from the base of the PR and between cfc6b01 and 778bbd7.

📒 Files selected for processing (2)
  • doctor.mjs
  • tests/doctor-code-root-checks.test.mjs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • career-ops-hq/career-ops-docs (manual)

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

Changes

Doctor now separates the code checkout from the resolved data root. Dependency and tracked .bak checks use the code checkout in doctor.mjs:57-66, doctor.mjs:156, doctor.mjs:664, and doctor.mjs:832-837. New tests cover split-root plain-text and JSON output.

Code-root checks

Layer / File(s) Summary
Resolve code root and run code checks
doctor.mjs
doctor.mjs:57-66 adds codeRoot and explicitTarget. Checks at doctor.mjs:156, doctor.mjs:664, and doctor.mjs:832-837 use codeRoot.
Validate split-root behavior
tests/doctor-code-root-checks.test.mjs
tests/doctor-code-root-checks.test.mjs:1-101 creates a separate data checkout and verifies dependency and tracked .bak results in plain-text and JSON modes.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Suggested reviewers: scott-emberson, abankar1

Merge Risk: ⚪ Minimal · up to 778bb

The split-checkout doctor behavior is covered for plain and JSON output, with no actionable merge risk identified.

🚥 Pre-merge checks | ✅ 9
✅ Passed checks (9 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title uses the required Conventional Commits format, includes the appropriate doctor scope, and accurately describes the code-root fixes.
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.
User Layer Untouched ✅ Passed PASS: The authoritative PR range changes only doctor.mjs and tests/doctor-code-root-checks.test.mjs. Neither path is a user-layer path. DATA_CONTRACT.md:11-27 and DATA_CONTRACT.md:32-55 define…
No Personal Data ✅ Passed No real personal data appears in the diff. The only email-like value is the explicit test fixture test@example.com at tests/doctor-code-root-checks.test.mjs:60. The references to jday013/maxmilian
Shipped File Registered ✅ Passed No failure condition applies: the PR adds no new top-level file. The only added file is tests/doctor-code-root-checks.test.mjs, while doctor.mjs is modified, not added. doctor.mjs is already reg…
Provider Contract ✅ Passed PASS: The pull request changes only doctor.mjs and tests/doctor-code-root-checks.test.mjs. It adds or changes no .mjs provider under providers/, so the provider contract does not apply.
Agent-Operated Pr Disclosure ✅ Passed No disclosure warning applies: PR #4138 has head branch Fix/3867-doctor-code-root-checks, not copilot/*, and author Zoubeir23, not app/copilot-swe-agent. The PR label is 🔧 scripts, not `agen…
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • 🛠️ register-shipped-file
  • 🛠️ provider-test-scaffold
  • 🛠️ sync-language-mode
🚀 Post-Merge Actions
  • localization drift report
  • documentation drift report

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@Scott-Emberson Scott-Emberson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I reviewed the owned test tests/doctor-code-root-checks.test.mjs. It is sound and mutation-sensitive, and it drives the real code.

The test spawns the real doctor.mjs (execFileSync from the code root, with CAREER_OPS_ROOT pointed at a throwaway data root), so it exercises both driver paths: the plain-text main() checks and the separate onboardingState() path behind --json. No import of the functions, no reconstructed logic. It sets up the split scenario deliberately: the throwaway data root is its own git checkout carrying a tracked stray.md.bak and no node_modules, while the code root has node_modules and no such tracked file. Then it asserts, with literal expecteds, that the output does not say "Dependencies not installed" (so checkDependencies read the code root's node_modules), and that stray.md.bak appears in neither the plain output nor the parsed --json warnings[] (so checkTrackedBakFiles read the code root, not the data root). That discriminates code-root from data-root rather than just confirming the command ran.

I mutation-checked all three. Pointing checkDependencies at the project root reddens assertion 1, pointing main()'s checkTrackedBakFiles at the project root reddens assertion 2, and pointing onboardingState()'s call at the data root reddens assertion 3, each independently, green again on full revert. So both checks are covered in both callers and none of the three is a false-pass. The suite also has no process.exit( in it, so test-all runs it rather than refusing it, and it is hermetic (temp data root, isolated git config, cleaned in finally).

Owned test sound, not a false-pass. The substance is in doctor.mjs, so the routing and merge decision is the maintainer's, not mine to gate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants