Skip to content

feat(mcp): let doctor --exit-code gate CI with a non-zero exit on failure#1444

Merged
JSONbored merged 3 commits into
JSONbored:mainfrom
glorydavid03023:feat/mcp-doctor-exit-code-v2
Jun 28, 2026
Merged

feat(mcp): let doctor --exit-code gate CI with a non-zero exit on failure#1444
JSONbored merged 3 commits into
JSONbored:mainfrom
glorydavid03023:feat/mcp-doctor-exit-code-v2

Conversation

@glorydavid03023

Copy link
Copy Markdown
Contributor

Summary

Replacement for #359 (closed). Rebased cleanly onto current main — it adds only the doctor --exit-code behavior and does not drop any of the existing MCP CLI coverage (config provenance, completion, version, profile, etc. are all untouched).

gittensory-mcp doctor computes a status (ok / warnings / needs_attention) but always exits 0, so it cannot gate a CI step or pre-commit hook:

gittensory-mcp doctor --exit-code   # exits 1 if the local MCP setup is broken

When a check fails (status: "needs_attention"), the process exits 1. The diagnostic report is still printed — only the exit code changes.

Closes #1443.

Behavior / compatibility

  • Default unchanged. Without --exit-code, doctor still exits 0, so existing scripts keep working.
  • Warnings stay exit 0. Only hard failures (needs_attention) produce a non-zero code.

Implementation

runCli's command result now carries an optional numeric exit code, and the entry point uses it: process.exit(typeof exitCode === "number" ? exitCode : 0). doctor is the only command that returns a number; every other command resolves to undefined and still exits 0.

Changes (only-adds, no removed coverage)

  • packages/gittensory-mcp/bin/gittensory-mcp.js — thread an optional exit code through the entry point; doctor returns 1 only under --exit-code on needs_attention; help-text flag.
  • test/unit/mcp-cli-doctor.test.tsadds three focused tests (default exit 0 on failure; non-zero under --exit-code on failure; exit 0 under --exit-code when passing). Uses the shared mcp-cli-harness.
  • packages/gittensory-mcp/README.md — document the flag.

Validation

Run locally on Node v22.22.0 (matches the project's engine):

  • npm run build:mcp → pass
  • test/unit/mcp-cli-doctor.test.ts → my three --exit-code cases pass:
    • keeps doctor exit code 0 by default even when a check fails
    • exits non-zero from doctor --exit-code when a check fails
    • keeps doctor --exit-code at 0 when checks pass
  • gittensory-mcp doctor --exit-code help/smoke verified.

Honest caveats from my local box (both unrelated to this change):

  • A full tsc --noEmit could not complete locally because my local node_modules is missing some newly-added deps (pg, ioredis, pngjs, @cloudflare/puppeteer, pixelmatch) used by src/selfhost/*, src/visual-agent/*, etc. There are zero type errors in the files this PR touches. CI runs a clean install and gives the authoritative result.
  • One pre-existing doctor test (runs doctor against a local health/session fixture, a local-scorer fixture assertion, flaky/retry x1) fails locally; it is untouched by this change (which only adds a return at the end of doctor).

Note re #359: it was rebased onto a main snapshot that predated the config merge, so its diff appeared to delete config tests. This branch is cut from current main, so the diff is strictly additive.

Security / privacy

No auth, cookie, CORS, GitHub App output, identity, or contributor-evidence changes. Only an exit-code signal is added; no new data is printed or transmitted.

…lure

`gittensory-mcp doctor` computes a status (ok / warnings / needs_attention)
but always exits 0, so it cannot gate a CI step or pre-commit hook. Add an
opt-in `--exit-code` flag: when a diagnostic check fails (status
"needs_attention"), the process exits 1. The diagnostic report is still
printed; only the exit code changes.

The default is unchanged — without `--exit-code`, doctor still exits 0 — so
existing scripts keep working. Warnings also stay at exit 0; only hard
failures are surfaced as a non-zero code.

runCli's command result now carries an optional numeric exit code (doctor is
the only command that returns one); every other command still resolves to
exit 0.

Tests (test/unit/mcp-cli-doctor.test.ts) cover the default exit-0-on-failure
behavior, the non-zero exit under --exit-code on failure, and exit 0 under
--exit-code when checks pass.

Closes JSONbored#1443

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dosubot dosubot Bot added the size:XS label Jun 26, 2026
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.51%. Comparing base (7ddaaf0) to head (31611c8).
⚠️ Report is 17 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1444   +/-   ##
=======================================
  Coverage   95.51%   95.51%           
=======================================
  Files         204      204           
  Lines       22205    22205           
  Branches     8028     8028           
=======================================
  Hits        21210    21210           
  Misses        415      415           
  Partials      580      580           
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@loopover-orb

loopover-orb Bot commented Jun 27, 2026

Copy link
Copy Markdown

Warning

🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨

⏸️ Gittensory review — held for maintainer review

3 files · 1 AI reviewers · no blockers · readiness 55/100 · CI green · clean

⏸️ Held for maintainer review — This is a first-time contribution to this repo, so the gate stays advisory rather than blocking. The findings remain visible, and the gate will apply normally once this author has merge history here.

Review summary
The CLI change correctly threads an optional numeric result from runCli into process.exit and has doctor return 1 only for --exit-code plus needs_attention. The doctor status is computed from the existing check list, so warnings keep the default zero exit and no other command gets a nonzero path from this diff. The notable weakness is test isolation around ambient auth environment variables, not the implementation itself.

Signal Result Evidence
Code review ✅ No blockers 1 reviewers, synthesized
Linked issue ✅ Linked #1443
Related work ⚠️ 3 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Review load ❌ 8/20 Readiness component derived from cached public PR metadata and labels; size label size:XS.
Validation evidence ❌ 5/25 Cached preflight status is hold.
Open PR queue ❌ 3/10 17 open PR(s), 12 likely reviewable, 5 unlinked.
Contributor context ✅ Confirmed Gittensor contributor glorydavid03023; Gittensor profile; 188 PR(s), 5 issue(s).
Gate result ⚠️ Not blocking Advisory; not blocking this PR.
Nits — 6 non-blocking
  • nit: test/unit/mcp-cli-doctor.test.ts:417 and test/unit/mcp-cli-doctor.test.ts:431 rely on "no token configured" while the harness merges process.env, so clear GITTENSORY_API_TOKEN, GITTENSORY_MCP_TOKEN, and GITTENSORY_TOKEN in these failure-path cases like the earlier first-run auth test does.
  • nit: test/unit/mcp-cli-doctor.test.ts:456 says checks pass, but the assertion accepts warnings; rename this to "when there are no failing checks" or assert the checks array contains no fail statuses.
  • nit: packages/gittensory-mcp/bin/gittensory-mcp.js:1709 leaves shell completion's generic option list without --exit-code, so tab completion will not surface the new doctor flag.
  • In test/unit/mcp-cli-doctor.test.ts:417 and test/unit/mcp-cli-doctor.test.ts:431, add empty values for all supported token env vars so the tests prove the intended no-auth path independent of a developer shell.
  • In test/unit/mcp-cli-doctor.test.ts:456, either assert the status is exactly ok by configuring away warning-producing checks, or rename the test to match the actual contract: --exit-code exits 0 for ok and warnings.
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.
Review context
Contributor next steps
  • Review top overlaps.
  • Add scope summary.
  • Fix blocker.
  • Expect slower review.
  • Refresh registry data or choose a registered active repo.
  • Check active issues and PRs before submitting.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Review load = cached public PR metadata such as size labels, changed paths, and preflight status.
  • Open PR queue = repo-wide review pressure; it is not a PR quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
Review details

Generated from public PR metadata and the diff. Advisory only; deterministic signals remain authoritative.

The CLI change correctly threads an optional numeric result from runCli into process.exit and has doctor return 1 only for --exit-code plus needs_attention. The doctor status is computed from the existing check list, so warnings keep the default zero exit and no other command gets a nonzero path from this diff. The notable weakness is test isolation around ambient auth environment variables, not the implementation itself.

Nits (5)

  • nit: test/unit/mcp-cli-doctor.test.ts:417 and test/unit/mcp-cli-doctor.test.ts:431 rely on "no token configured" while the harness merges process.env, so clear GITTENSORY_API_TOKEN, GITTENSORY_MCP_TOKEN, and GITTENSORY_TOKEN in these failure-path cases like the earlier first-run auth test does.
  • nit: test/unit/mcp-cli-doctor.test.ts:456 says checks pass, but the assertion accepts warnings; rename this to "when there are no failing checks" or assert the checks array contains no fail statuses.
  • nit: packages/gittensory-mcp/bin/gittensory-mcp.js:1709 leaves shell completion's generic option list without --exit-code, so tab completion will not surface the new doctor flag.
  • In test/unit/mcp-cli-doctor.test.ts:417 and test/unit/mcp-cli-doctor.test.ts:431, add empty values for all supported token env vars so the tests prove the intended no-auth path independent of a developer shell.
  • In test/unit/mcp-cli-doctor.test.ts:456, either assert the status is exactly ok by configuring away warning-producing checks, or rename the test to match the actual contract: --exit-code exits 0 for ok and warnings.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.

  • Re-run Gittensory review

@dosubot dosubot Bot added the lgtm label Jun 28, 2026
@JSONbored
JSONbored merged commit 007f9ce into JSONbored:main Jun 28, 2026
17 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in gittensory - v1 roadmap Jun 28, 2026
@github-actions github-actions Bot mentioned this pull request Jun 29, 2026
12 tasks
galuis116 pushed a commit to galuis116/gittensory that referenced this pull request Jul 18, 2026
Root cause traced as far as possible: release-please's own createReleases()
(the step that actually tags a merged Release PR) sometimes finds nothing
to tag, and createPullRequests() then aborts every subsequent run without
ever completing the tag -- confirmed live across two separate
linked-versions release cycles this session (JSONbored#7127, JSONbored#7133), both requiring
a manual dispatch of each publish-*.yml workflow plus manually flipping
the merged PR's autorelease:pending label. Matches multiple long-open,
unresolved upstream issues (googleapis/release-please#1946/JSONbored#1444/JSONbored#1406),
so it isn't something fixable from this repo's config alone.

Adds a reconcile-stale-releases job that runs after every release-please
attempt, independent of its outputs: compares each package's committed
package.json version against what's actually live on npm, dispatches
that package's publish workflow whenever they disagree (the same
documented human-override path, automated), and flips any merged PR's
autorelease label once every package is confirmed live -- unsticking
release-please's own tracking for the next run. Also cleaned up one
ancient, silently-stuck instance of the exact same bug (engine v1.0.0,
autorelease:pending since long before this session).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

MCP CLI: let doctor gate CI/pre-commit with a non-zero exit code

2 participants