# Project health (/docs/infra/project-health)



[`lpm doctor`](/docs/infra/doctor) is a structured environment check. Each check produces a `(name, severity, detail)` row; the run finishes with a summary and an exit code (0 if every fail is green, 1 otherwise). This page covers the design — what severity levels mean, how `--fix` decides what to repair, and how the parallel probe layer keeps a full doctor run under a second on a healthy project.

For the CLI flags, see [`lpm doctor`](/docs/infra/doctor).

## Severity model [#severity-model]

Three levels:

| Severity        | Meaning                                                                          | Affects exit code? |
| --------------- | -------------------------------------------------------------------------------- | ------------------ |
| `pass` (green)  | The check succeeded                                                              | No                 |
| `warn` (yellow) | Suboptimal but not broken — e.g., outdated tool, expired-but-renewable cert      | No                 |
| `fail` (red)    | Broken — e.g., missing required dep, unreachable registry, corrupted store entry | Yes (exit 1)       |

A `warn` is informational — `lpm doctor` exits 0 even if every check warns. A `fail` exits 1, which is what CI gates on.

## Fast vs full run [#fast-vs-full-run]

By default, `lpm doctor` runs a **fast local-only preset** — no network round-trips, no subprocess spawns. The full check set runs under `--all`.

Under `--all`, two infrastructure checks run **concurrently** at the start of the doctor run:

* **Registry health** — `client.health_check()` against the configured registry URL.
* **Auth check** — `client.whoami()` if a token exists in the keychain.

Both are network round-trips. Running them in parallel via `tokio::join!` keeps the worst case to a single round-trip rather than two.

After those two land, the rest of the checks run inline (they're filesystem-bound and fast).

## Check inventory (representative) [#check-inventory-representative]

The authoritative live catalog is [`lpm doctor list`](/docs/infra/doctor#lpm-doctor-list) (or `--json` for machine-readable). Highlights:

| Group             | Checks                                                                                                                                                                                                                                                   |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Registry**      | Reachable from this network? Latency reasonable?                                                                                                                                                                                                         |
| **Auth**          | Token present? Token valid (via `whoami`)? Expiry within warning threshold?                                                                                                                                                                              |
| **Runtime**       | Managed runtime detected? Detected version installed? PATH includes the right `bin/`?                                                                                                                                                                    |
| **Project state** | `package.json` present and parseable? `lpm.lock` matches `package.json`? `node_modules/` matches lockfile?                                                                                                                                               |
| **Store**         | `~/.lpm/store/` exists and accessible? Lockfile-referenced entries present? Any integrity drift?                                                                                                                                                         |
| **Sandbox**       | Seatbelt available (macOS) or landlock available (Linux)?                                                                                                                                                                                                |
| **Local HTTPS**   | If `lpm.json > https: true` or a project cert exists — CA installed? Cert valid?                                                                                                                                                                         |
| **Tools**         | If `lpm.json > tools` declares pins — those plugin versions present?                                                                                                                                                                                     |
| **TypeScript**    | For each `tsconfig.json` in the workspace, can `tsc` be resolved? Healthy when the project-local `node_modules/.bin/tsc` exists. Warns when only a system-PATH `tsc` is reachable (editor and CI may diverge). Fails when `tsc` is not reachable at all. |

Each check produces one `Check` row. Use `--json` to get the full structured set (every check ID, severity, detail, remediation hint).

## Auto-fix mode [#auto-fix-mode]

```bash
lpm doctor --fix              # prompt before each remediation
lpm doctor --fix -y           # skip prompts (CI-friendly)
lpm doctor -y                 # implies --fix
lpm doctor --all --fix        # full sweep + repair (pair --all with --fix to repair every catalog row)
```

`--fix` only repairs what was actually checked — running `--fix` without `--all` cannot fix rows that the fast preset never ran.

For each `fail` (and some `warn`s), the remediator either has a known fix or doesn't. Examples of fixable checks:

| Check fail                             | Fix action                                                               |
| -------------------------------------- | ------------------------------------------------------------------------ |
| Detected managed runtime not installed | Run [`lpm use node@<version>` or `lpm use bun@<version>`](/docs/dev/use) |
| `node_modules/` doesn't match lockfile | Run `lpm install`                                                        |
| Local CA not installed in trust store  | Run [`lpm cert trust`](/docs/infra/cert)                                 |
| Store integrity issue                  | Run [`lpm store verify --fix`](/docs/infra/store)                        |
| Project formatting drifted             | Run [`lpm fmt`](/docs/dev/fmt)                                           |
| Plugin version pinned but not present  | Run [`lpm plugin update <name>`](/docs/dev/plugin)                       |

Examples of unfixable checks:

| Check fail                     | Why unfixable                                                                                |
| ------------------------------ | -------------------------------------------------------------------------------------------- |
| Registry unreachable           | Network problem — outside doctor's scope                                                     |
| Token expired                  | User has to log in (`lpm login`) — interactive                                               |
| Sandbox unavailable on this OS | OS-level limitation; nothing doctor can install                                              |
| TypeScript not installed       | Doctor doesn't auto-install random packages — `lpm install -D typescript` is the user's call |

Unfixable failures are reported with the recommended next step but no auto-action.

## `lpm doctor` vs `lpm health` [#lpm-doctor-vs-lpm-health]

```bash
lpm doctor          # project + environment health (this page)
lpm health          # registry connectivity check (different command)
```

|                       | `lpm doctor`                                                      | `lpm health`                                     |
| --------------------- | ----------------------------------------------------------------- | ------------------------------------------------ |
| Scope                 | Project + environment                                             | Just the registry                                |
| Reads `package.json`? | Yes                                                               | No                                               |
| Hits the network?     | Only under `--all` (1–2 calls); default fast preset is local-only | Yes (1 call)                                     |
| Auto-fix?             | Yes (`--fix`)                                                     | No                                               |
| Use case              | "Is my project in a good state?"                                  | "Is the registry reachable from this CI runner?" |

`lpm health` is the lighter-weight CI smoke test — no project context needed.

## JSON output [#json-output]

```bash
lpm doctor --json
```

Structured array of check results:

```json
{
  "success": true,
  "mode": "fast",
  "no_failures": false,
  "clean": false,
  "has_warnings": false,
  "passed": 2,
  "failed": 1,
  "warnings": 0,
  "checks": [
    { "code": "registry_reachable", "check": "Registry", "passed": true, "severity": "pass", "detail": "lpm.dev OK (87ms)" },
    { "code": "node_managed_match", "check": "Node version", "passed": true, "severity": "pass", "detail": "22.12.0 from lpm.json" },
    { "code": "deps_sync_drift", "check": "Dependencies", "passed": false, "severity": "fail", "detail": "drifted from lpm.lock — run `lpm install`" }
  ],
  "fixes_applied": ["lpm install"]
}
```

Top-level fields:

| Field                            | Meaning                                                                               |
| -------------------------------- | ------------------------------------------------------------------------------------- |
| `mode`                           | `"fast"` (default, local-only) or `"all"` (under `--all`)                             |
| `no_failures`                    | `true` when every check passed (warnings don't break this)                            |
| `has_warnings`                   | `true` when at least one check reported `warn`                                        |
| `clean`                          | `no_failures && !has_warnings` — the "everything green" shorthand                     |
| `passed` / `failed` / `warnings` | Per-severity counts                                                                   |
| `fixes_applied`                  | Array of remediation actions that ran under `--fix`; empty when `--fix` wasn't passed |

Full envelope shape (per-check fields) is documented on the [`lpm doctor`](/docs/infra/doctor#machine-readable-output---json) page. Useful for CI dashboards or agents that want to dispatch on specific failures rather than parse human output.

## When to run [#when-to-run]

* **In CI**: as a pre-test step. Fast, catches "the lockfile is stale because the PR forgot to commit it" before the test suite spends 10 minutes failing for a downstream reason.
* **After upgrading LPM CLI**: confirms nothing in the new version broke your local state.
* **When something's wrong and you don't know what**: doctor's broad-strokes inventory often surfaces it.
* **In onboarding scripts**: bundle `lpm install && lpm doctor --fix -y && lpm dev` for a "clone-to-running" experience.

## See also [#see-also]

* [`lpm doctor`](/docs/infra/doctor) — CLI command reference
* [`lpm health`](/docs/infra/doctor) — registry-only health check
* [`lpm store verify`](/docs/infra/store) — deeper store integrity check
* [`lpm cert status`](/docs/infra/cert) — CA + project cert detail
* [Exit codes](/docs/reference/exit-codes) — what doctor's exit codes mean for CI gates
