# lpm doctor / lpm health (/docs/infra/doctor)



```bash
lpm doctor [--all] [--fix] [-y]
lpm health
```

Two complementary diagnostic commands:

* **`lpm doctor`** — checks the **project + environment**: managed runtime detection, `lpm install` state, global store accessibility, vault-storage, local policy extension posture, and Sigstore posture, plus the broader `--all` sweep for registry/auth/tunnel/tooling/sandbox state. The big "is everything OK here?" command.
* **`lpm health`** — checks the **registry**: connectivity, latency, the registry's own health endpoint. The "is the service up?" command.

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

`lpm doctor` runs a **fast local-only preset by default**: no registry probe, no `whoami`, no tunnel lookup, no lint / fmt subprocess, no plugin reachability fetch, no sandbox probe, no manifest-compat sweep. It does include local policy extension config and executable availability checks. It's the "why is this project broken right now?" pass — milliseconds.

Pass `--all` to run **every catalog row**, including the network-touching and subprocess-spawning ones. Use `--all` in CI gates and when you actually want the broad sweep; stick with the default for quick local checks.

## Examples [#examples]

```bash
lpm doctor                       # fast preset — local-only checks
lpm doctor --all                 # full sweep — every catalog row
lpm doctor --fix                 # auto-fix what it can, prompt where unclear
lpm doctor --fix -y              # auto-fix, no prompts (CI-friendly)
lpm doctor --all --json          # full sweep, structured for tooling

lpm health                       # registry connectivity check
lpm health --json
```

`--fix` only dispatches remediations for rows that were actually checked. Some of those remediations, especially `lpm install`, can incidentally repair adjacent state as a side effect, so treat `fixes_applied` as the authoritative list of what doctor intentionally ran. Pair `--all --fix` for the full repair pass.

## What `lpm doctor` checks [#what-lpm-doctor-checks]

The check set is intentionally broad — every signal that "this project is in a known-good state to develop in." For the canonical inventory, run:

```bash
lpm doctor list                          # full catalog (human)
lpm doctor list --json                   # full catalog (machine-readable)
lpm doctor list --code <code>            # one entry
lpm doctor list --category <substring>   # filter by group
```

The catalog drives the runtime: `lpm doctor`'s `Check` constructors take a `&'static CheckEntry` reference, so a runtime row cannot carry a code that isn't a registered static — that part is by construction. Catalog completeness (every static reaches `lpm doctor list`) is enforced by a unit-test drift guard rather than the type system, so adding a new static without registering it fails CI rather than the build. The [Catalog](#catalog) section below mirrors what `lpm doctor list` prints today.

`--json` returns the full per-check result set; `--verbose` adds remediation hints inline.

## Machine-readable output (`--json`) [#machine-readable-output---json]

`lpm doctor --json` is the structured surface every CI pipeline, MCP server, or scripting tool should consume — it's a contract, not a debug aid. The shape is stable; consumers should match on `code`, never on the human-readable `check` or `detail` strings.

```json
{
  "success": true,
  "mode": "all",
  "no_failures": false,
  "clean": false,
  "has_warnings": true,
  "passed": 12,
  "failed": 1,
  "warnings": 2,
  "fixes_applied": [],
  "checks": [
    {
      "code": "registry_reachable",
      "check": "Registry",
      "passed": true,
      "severity": "pass",
      "detail": "https://lpm.dev"
    },
    {
      "code": "auth_invalid",
      "check": "Authentication",
      "passed": false,
      "severity": "fail",
      "detail": "token exists but invalid — run: lpm login"
    }
  ]
}
```

Top-level fields:

| Field                            | Type       | Notes                                                                                                                       |
| -------------------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------- |
| `mode`                           | `string`   | `"fast"` (default preset) or `"all"` (under `--all`). Agents need this to distinguish what was actually checked vs skipped. |
| `no_failures`                    | `bool`     | `true` when no `severity = "fail"` checks fired (warnings tolerated).                                                       |
| `clean`                          | `bool`     | `true` when no failures AND no warnings.                                                                                    |
| `has_warnings`                   | `bool`     | `true` when at least one `severity = "warn"` check fired.                                                                   |
| `passed` / `failed` / `warnings` | `number`   | Counts.                                                                                                                     |
| `fixes_applied`                  | `string[]` | Remediations `--fix` ran. Empty in read-only mode.                                                                          |
| `checks[]`                       | `array`    | Per-check rows — see below.                                                                                                 |

Per-check fields:

| Field      | Type     | Notes                                                                                                                                    |
| ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `code`     | `string` | Stable snake\_case identifier. Examples: `registry_unreachable`, `node_modules_missing`, `pnpm_overrides_drift`. &#x2A;*Match on this.** |
| `check`    | `string` | Human-readable label. Wording can change.                                                                                                |
| `passed`   | `bool`   | `true` for `pass` and `warn`; `false` for `fail`.                                                                                        |
| `severity` | `string` | One of `pass` \| `fail` \| `warn`.                                                                                                       |
| `detail`   | `string` | Human-readable detail with remediation. May be empty.                                                                                    |

`severity = "warn"` carries `passed = true` so that warning-only runs don't trip strict CI gates that key off `no_failures`. Use `has_warnings` (top-level) or filter `checks[].severity == "warn"` if you want to surface warnings explicitly.

Exit code: `0` when no `severity = "fail"` checks fired (warnings are tolerated), `1` otherwise.

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

```bash
lpm doctor --fix
```

Walks the failing checks and asks before each remediation. Common fixes include:

* Install the missing managed runtime
* Run `lpm install` to reconcile `node_modules/`
* Run `lpm fmt` to reconcile formatting
* Reconcile `lpm.lockb` with `lpm.lock` when the graph fits the binary format, or remove stale binary bytes for TOML-only graphs
* Update `.gitattributes` so `lpm.lockb` is marked as binary

Pass `-y` (which implies `--fix`) to skip every prompt — useful in CI bootstrap scripts.

## What `lpm health` checks [#what-lpm-health-checks]

```bash
lpm health
lpm health --json
```

Hits the registry's health endpoint with a **single round-trip**, reports up/down and the response time in milliseconds. Exits non-zero when the registry is unreachable, so the command works as a self-gating CI smoke test on its own — no need to wrap it.

```json
{
  "success": true,
  "healthy": true,
  "registry_url": "https://lpm.dev",
  "response_time_ms": 87
}
```

`lpm health` does not aggregate percentiles or sample repeatedly — `response_time_ms` is one measurement. For load-testing or latency distribution, drive it from your own benchmarking harness.

`lpm health` does not check your project — only the registry. For project + environment checks, use `lpm doctor`.

## Flags [#flags]

### `lpm doctor` [#lpm-doctor]

| Flag          | Effect                                                                                                                                              |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--all`       | Run every catalog row (including registry / auth / tunnel / lint / fmt / plugin / sandbox / manifest-compat). Default is the fast local-only preset |
| `--fix`       | Auto-fix issues with prompts. Only repairs what was actually checked — pair with `--all` for the full repair pass                                   |
| `-y`, `--yes` | Skip prompts (implies `--fix`)                                                                                                                      |

### `lpm doctor list` [#lpm-doctor-list]

| Flag                     | Effect                                                       |
| ------------------------ | ------------------------------------------------------------ |
| `--code <code>`          | Filter to a single catalog entry by exact code match         |
| `--category <substring>` | Case-insensitive substring filter against the category label |

`--json` (the global flag) toggles structured output for both `lpm doctor` and `lpm doctor list`.

### `lpm health` [#lpm-health]

No specific flags besides the [global flags](/docs/commands#global-flags) — `--json` and `--verbose` are useful.

## Catalog [#catalog]

Every check `lpm doctor` can emit, grouped by category. The `code` column is the load-bearing identifier — match on it in automation. Wording in the description and remediation columns may evolve, but codes never change once shipped.

This table is hand-maintained alongside the catalog source in [`crates/lpm-cli/src/doctor_catalog.rs`](https://github.com/lpm-dev/rust-client/blob/main/crates/lpm-cli/src/doctor_catalog.rs). For the live, machine-readable inventory, run `lpm doctor list --json` — that's the authoritative surface. Drift between this table and the runtime catalog is a docs bug; please file an issue if you spot it.

### Infrastructure [#infrastructure]

| Code                        | Severity | Description                                                                                                                                                                    |
| --------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `registry_reachable`        | pass     | The configured registry responds to its health endpoint.                                                                                                                       |
| `registry_unreachable`      | fail     | The configured registry could not be reached. &#x2A;*Fix:** Check network connectivity, firewall rules, or the registry status page.                                           |
| `global_store_accessible`   | pass     | The shared content-addressable store at `~/.lpm/store/` resolves.                                                                                                              |
| `global_store_inaccessible` | fail     | The shared content-addressable store could not be located or read. &#x2A;*Fix:** Verify `$HOME` is set and that `~/.lpm/store/` is writable. Re-run `lpm install` to recreate. |

### Auth [#auth]

| Code                        | Severity | Description                                                                                                                                                                                                                                                                         |
| --------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `auth_valid`                | pass     | A registry auth token is present and `whoami` succeeds.                                                                                                                                                                                                                             |
| `auth_invalid`              | fail     | A token is stored but the registry rejected it. &#x2A;*Fix:** Re-authenticate with `lpm login`.                                                                                                                                                                                     |
| `auth_missing`              | fail     | No registry auth token is configured. &#x2A;*Fix:** Run `lpm login`.                                                                                                                                                                                                                |
| `vault_storage_keychain`    | pass     | On macOS, vault secrets are unlocked through the OS Keychain.                                                                                                                                                                                                                       |
| `vault_storage_native`      | pass     | On Linux/Windows, vault blobs are encrypted on disk and the local data key is protected by Secret Service-compatible storage or Credential Manager.                                                                                                                                 |
| `vault_storage_fallback`    | warn     | Linux/Windows vault secrets are using the encrypted-file fallback key at `~/.lpm/.vault-fallback-key`; any same-UID process can read that key file. &#x2A;*Fix:** unlock or repair the OS secure store so LPM CLI can promote the data key.                                         |
| `vault_storage_unavailable` | fail     | Encrypted vault files exist locally, but LPM CLI cannot access the OS-protected data key or the legacy fallback key. &#x2A;*Fix:** unlock Secret Service/Credential Manager, repair the secure store, or restore the fallback key from backup if this machine was not promoted yet. |

### Policy extensions [#policy-extensions]

| Code                                   | Severity | Description                                                                                                                                          |
| -------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `policy_extensions_not_configured`     | pass     | No active install-time policy extensions are configured in `~/.lpm/config.toml`.                                                                     |
| `policy_extensions_configured`         | pass     | One or more active install-time policy extensions are configured. &#x2A;*Inspect:** Run `lpm policy status` or `lpm policy doctor`.                  |
| `policy_extension_report_mode`         | warn     | A configured policy extension is report-only. &#x2A;*Fix:** Switch to `mode = "enforce"` when the policy is ready to block installs.                 |
| `policy_extension_command_unavailable` | fail     | A configured policy extension command cannot be found or executed. &#x2A;*Fix:** Install the executable or fix `[policy.extensions.<name>].command`. |
| `policy_extension_config_invalid`      | fail     | Policy extension configuration is malformed. &#x2A;*Fix:** Repair the `[policy.extensions]` table, then rerun `lpm policy doctor`.                   |

### Project state [#project-state]

| Code                            | Severity | Description                                                                                                                                                                                                                     |
| ------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `package_json_present`          | pass     | A readable `package.json` exists in the project directory.                                                                                                                                                                      |
| `package_json_missing`          | fail     | No `package.json` exists in the project directory. &#x2A;*Fix:** Run `lpm init`, or `cd` into your project root before running doctor.                                                                                          |
| `linker_mode_resolved`          | pass     | Reports the linker mode the install pipeline would resolve to plus which surface produced it (CLI flag / `~/.lpm/config.toml` / `LPM_LINKER` / `package.json > lpm > linker` / workspace auto-detect / default). Informational. |
| `node_modules_isolated_healthy` | pass     | `node_modules/` exists and is backed by an isolated `.lpm/wrappers/` store.                                                                                                                                                     |
| `node_modules_hoisted_healthy`  | pass     | `node_modules/` exists and uses the hoisted layout.                                                                                                                                                                             |
| `node_modules_virtual_healthy`  | pass     | `node_modules/` symlinks point into the virtual store at `~/.lpm/store/v2/links/`.                                                                                                                                              |
| `v2_store_orphans`              | warn     | The v2 store at `~/.lpm/store/v2/` has link entries or objects no longer reachable from any registered project. &#x2A;*Fix:** `lpm cache prune` (dry-run) then `lpm cache prune --apply`.                                       |
| `node_modules_mixed_layout`     | warn     | Both isolated and hoisted layout state are present in `node_modules/`. &#x2A;*Fix:** Re-run `lpm install` to converge on the configured linker layout.                                                                          |
| `node_modules_no_store`         | warn     | `node_modules/` exists but no LPM CLI-owned store is present. &#x2A;*Fix:** Run `lpm install` to rebuild the layout under LPM CLI ownership.                                                                                    |
| `node_modules_legacy_layout`    | warn     | An older LPM CLI layout is on disk and a one-time migration is pending. &#x2A;*Fix:** Run `lpm install` to migrate to the current layout.                                                                                       |
| `node_modules_missing`          | fail     | `node_modules/` is missing — dependencies have not been installed. &#x2A;*Fix:** Run `lpm install`.                                                                                                                             |
| `lockfile_present`              | pass     | `lpm.lock` is present at the project root.                                                                                                                                                                                      |
| `lockfile_missing`              | warn     | No `lpm.lock` was found at the project root. &#x2A;*Fix:** Run `lpm install` — it generates the lockfile alongside `node_modules/`.                                                                                             |
| `lockfile_binary_valid`         | pass     | `lpm.lockb` matches `lpm.lock` and parses cleanly.                                                                                                                                                                              |
| `lockfile_binary_missing`       | warn     | `lpm.lockb` is missing while `lpm.lock` is present and the graph fits the binary format. &#x2A;*Fix:** Run `lpm doctor --fix` to regenerate, or run `lpm install`.                                                              |
| `lockfile_binary_stale`         | warn     | `lpm.lockb` does not match the contents of `lpm.lock`, or stale binary bytes remain for a TOML-only graph. &#x2A;*Fix:** Run `lpm doctor --fix` to reconcile, or run `lpm install`.                                             |
| `lockfile_binary_corrupt`       | warn     | `lpm.lockb` does not parse as a valid binary lockfile. &#x2A;*Fix:** Run `lpm doctor --fix` to reconcile from `lpm.lock`.                                                                                                       |
| `gitattributes_lockb_marked`    | pass     | `.gitattributes` marks `lpm.lockb` as binary.                                                                                                                                                                                   |
| `gitattributes_lockb_unmarked`  | warn     | `.gitattributes` exists but does not mark `lpm.lockb` as binary. &#x2A;*Fix:** Add `lpm.lockb binary` to `.gitattributes` so git diffs treat the file correctly.                                                                |
| `gitattributes_missing`         | warn     | No `.gitattributes` found — `lpm.lockb` may be diffed as text. &#x2A;*Fix:** Run `lpm init` or add `lpm.lockb binary` to a new `.gitattributes`.                                                                                |
| `deps_sync_clean`               | pass     | `lpm.lock` and `package.json` agree on the declared dependency set.                                                                                                                                                             |
| `deps_sync_drift`               | warn     | `lpm.lock` and `package.json` disagree — manifest changes have not been resolved. &#x2A;*Fix:** Run `lpm install` to reconcile.                                                                                                 |
| `local_source_dir_ok`           | pass     | A `file:` / `link:` dependency points at a directory with a readable `package.json`.                                                                                                                                            |
| `local_source_tarball_ok`       | pass     | A `file:` dependency points at a readable tarball.                                                                                                                                                                              |
| `local_source_dir_no_pkg`       | fail     | A `file:` / `link:` dependency points at a directory with no `package.json`. &#x2A;*Fix:** Add `package.json` to the local path or update the dep target.                                                                       |
| `local_source_invalid_type`     | fail     | A `file:` / `link:` dependency points at an unexpected file type. &#x2A;*Fix:** Re-target the dependency at a directory or a tarball file.                                                                                      |
| `local_source_link_to_file`     | fail     | A `link:` dependency points at a regular file rather than a directory. &#x2A;*Fix:** `link:` must reference a project directory; switch to `file:` for tarballs.                                                                |
| `local_source_unreadable`       | fail     | A `file:` / `link:` dependency target could not be read. &#x2A;*Fix:** Restore the target path or fix permissions; rerun `lpm install`.                                                                                         |

### lpm.json [#lpmjson]

| Code                       | Severity | Description                                                                                                                                 |
| -------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `lpm_json_valid`           | pass     | `lpm.json` exists and parses against the strict schema.                                                                                     |
| `lpm_json_schema_warnings` | warn     | `lpm.json` parsed but contained unknown or off-schema fields. &#x2A;*Fix:** Inspect the listed fields and align with the documented schema. |
| `lpm_json_invalid_syntax`  | fail     | `lpm.json` is not valid JSON. &#x2A;*Fix:** Fix the JSON syntax error reported in `detail`.                                                 |
| `lpm_json_not_object`      | fail     | `lpm.json`'s top-level value is not an object. &#x2A;*Fix:** Replace with a JSON object literal `{ ... }`.                                  |
| `lpm_json_unreadable`      | fail     | `lpm.json` could not be read from disk. &#x2A;*Fix:** Fix file permissions; rerun doctor.                                                   |

### Runtime [#runtime]

| Code                    | Severity | Description                                                                                                                                                     |
| ----------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `node_managed_match`    | pass     | A managed Node install matches the pinned spec.                                                                                                                 |
| `node_pinned_unmet`     | warn     | Project pins a Node version but only a system Node satisfies (or none does). &#x2A;*Fix:** Run `lpm use node@<version>` to install and pin the managed version. |
| `node_missing_pinned`   | fail     | Project pins a Node version and no Node is reachable. &#x2A;*Fix:** Run `lpm use node@<version>` to install the pinned version.                                 |
| `node_system_unpinned`  | pass     | No Node version is pinned; a system Node was found. &#x2A;*Fix:** Optional — pin a Node version via `lpm.json > runtime.node` for reproducibility.              |
| `node_missing_unpinned` | fail     | No Node version is pinned and no Node is reachable. &#x2A;*Fix:** Install Node via `lpm use node@22` (or your preferred version).                               |
| `bun_managed_match`     | pass     | A managed Bun install matches `lpm.json > runtime.bun`.                                                                                                         |
| `bun_pinned_unmet`      | warn     | Project pins a Bun version but no managed Bun install matches. &#x2A;*Fix:** Run `lpm use bun@<version>` to install and pin the managed version.                |
| `bun_missing_pinned`    | fail     | Project pins a Bun version and no Bun is reachable. &#x2A;*Fix:** Run `lpm use bun@<version>` to install the pinned version.                                    |

### Tunnel [#tunnel]

| Code                           | Severity | Description                                                                                                                                                                                      |
| ------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `tunnel_active`                | pass     | A tunnel domain is configured and the registry confirms ownership.                                                                                                                               |
| `tunnel_idle`                  | pass     | No tunnel domain is configured for this project.                                                                                                                                                 |
| `tunnel_unauthenticated`       | pass     | A tunnel domain is configured but the user is not authenticated to verify ownership. &#x2A;*Fix:** Run `lpm login` to verify ownership.                                                          |
| `tunnel_unverified`            | pass     | Ownership of the tunnel domain could not be verified due to a transient registry error. &#x2A;*Fix:** Retry; check `lpm health`.                                                                 |
| `tunnel_not_claimed`           | warn     | The configured tunnel domain is not claimed by this account. &#x2A;*Fix:** Run `lpm tunnel claim <domain>` (Pro/Org) or change the domain.                                                       |
| `tunnel_owned_by_other`        | warn     | The configured tunnel domain is claimed by a different account. &#x2A;*Fix:** Choose a different domain; the current claim is held elsewhere.                                                    |
| `tunnel_unreachable`           | warn     | The registry could not be reached to verify the tunnel claim. &#x2A;*Fix:** Check network; retry `lpm doctor`.                                                                                   |
| `tunnel_unknown_base`          | warn     | Configured tunnel domain uses a base domain the LPM.dev Registry does not recognize. &#x2A;*Fix:** Use one of the supported base domains, or contact the LPM.dev Registry team to add a new one. |
| `tunnel_domain_no_dot`         | warn     | Configured tunnel domain has no dot separating subdomain from base. &#x2A;*Fix:** Set the full domain: `<subdomain>.lpm.fyi` or `<subdomain>.lpm.llc`.                                           |
| `tunnel_domain_empty_label`    | warn     | Configured tunnel domain has an empty label (e.g., `..lpm.fyi`). &#x2A;*Fix:** Remove the empty label.                                                                                           |
| `tunnel_domain_label_too_long` | warn     | A label in the configured tunnel domain exceeds 63 characters (DNS limit). &#x2A;*Fix:** Shorten the offending label.                                                                            |
| `tunnel_domain_too_long`       | warn     | Total tunnel domain length exceeds the DNS limit. &#x2A;*Fix:** Shorten the domain.                                                                                                              |
| `tunnel_subdomain_length`      | warn     | Tunnel subdomain length is outside the allowed range. &#x2A;*Fix:** Pick a subdomain between the documented bounds.                                                                              |
| `tunnel_subdomain_chars`       | warn     | Tunnel subdomain contains characters outside the allowed alphabet. &#x2A;*Fix:** Use only lowercase letters, digits, and hyphens.                                                                |
| `tunnel_subdomain_hyphen`      | warn     | Tunnel subdomain starts or ends with a hyphen. &#x2A;*Fix:** Subdomains must start and end with an alphanumeric character.                                                                       |

### Code quality [#code-quality]

| Code               | Severity | Description                                                                                                                           |
| ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `lint_clean`       | pass     | Oxlint reports no issues for the project.                                                                                             |
| `lint_warnings`    | warn     | Oxlint reported warnings for the project. &#x2A;*Fix:** Run `lpm lint` to inspect, then address.                                      |
| `lint_errors`      | fail     | Oxlint reported errors for the project. &#x2A;*Fix:** Run `lpm lint` and fix the reported errors.                                     |
| `lint_unparseable` | warn     | Oxlint output could not be parsed by doctor. &#x2A;*Fix:** Run `lpm lint` directly to see the raw output.                             |
| `fmt_clean`        | pass     | Biome reports the project formatting is clean.                                                                                        |
| `fmt_unformatted`  | warn     | Biome found files that need reformatting. &#x2A;*Fix:** Run `lpm fmt` to apply formatting.                                            |
| `fmt_other_issue`  | warn     | Biome reported a non-format issue (parse error, config error, etc.). &#x2A;*Fix:** Run `lpm fmt --check` to see the raw biome output. |

### TypeScript [#typescript]

| Code                              | Severity | Description                                                                                                                                                          |
| --------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `typescript_healthy`              | pass     | Project-local `tsc` resolves through the `node_modules/.bin` chain.                                                                                                  |
| `typescript_missing_for_tsconfig` | warn     | `tsc` is reachable only via the system `PATH`; no project-local install. &#x2A;*Fix:** Run `lpm install -D typescript` so editor + CI use the same version.          |
| `typescript_unavailable`          | fail     | `tsc` is not reachable for a `tsconfig.json`-owning directory. &#x2A;*Fix:** Run `lpm install -D typescript` (or `lpm install` if `typescript` is already declared). |

### Plugin [#plugin]

| Code                      | Severity | Description                                                                                                           |
| ------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------- |
| `plugin_up_to_date`       | pass     | An installed plugin is at the latest known version.                                                                   |
| `plugin_update_available` | warn     | A newer version of an installed plugin is available upstream. &#x2A;*Fix:** Run `lpm plugin update <name>` to update. |

### Workspace [#workspace]

| Code                | Severity | Description                                                                                                                                |
| ------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `workspace_acyclic` | pass     | No dependency cycles among workspace members.                                                                                              |
| `workspace_cycle`   | fail     | A dependency cycle exists among workspace members. &#x2A;*Fix:** Break the cycle by removing or restructuring the offending workspace dep. |

### Global installs [#global-installs]

| Code                             | Severity | Description                                                                                                                                                       |
| -------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `global_manifest_valid`          | pass     | `~/.lpm/global/manifest.json` parses and is structurally valid.                                                                                                   |
| `global_manifest_absent`         | pass     | No global install manifest is present (no global installs yet).                                                                                                   |
| `global_manifest_corrupt`        | fail     | `~/.lpm/global/manifest.json` is unreadable or malformed. &#x2A;*Fix:** Inspect and repair, or reinstall affected globals.                                        |
| `global_bin_on_path`             | pass     | `~/.lpm/bin` is on the user's `PATH`.                                                                                                                             |
| `global_bin_off_path`            | warn     | `~/.lpm/bin` is not on the user's `PATH`. &#x2A;*Fix:** Add `~/.lpm/bin` to your shell PATH (see `lpm install --global` notes).                                   |
| `global_shims_clean`             | pass     | Every shim in `~/.lpm/bin` belongs to a recorded global install.                                                                                                  |
| `global_shims_no_dir`            | pass     | Global bin directory does not yet exist.                                                                                                                          |
| `global_shims_orphans`           | warn     | Files exist in `~/.lpm/bin` without a matching manifest entry. &#x2A;*Fix:** Remove the orphan files, or run `lpm install --global` to re-register.               |
| `global_shims_unreadable`        | warn     | Global bin directory could not be enumerated. &#x2A;*Fix:** Fix permissions; rerun doctor.                                                                        |
| `global_install_roots_empty`     | pass     | No global installs are recorded.                                                                                                                                  |
| `global_install_roots_healthy`   | pass     | Every global install root exists and carries a ready marker.                                                                                                      |
| `global_install_roots_unhealthy` | fail     | One or more global install roots are missing or incomplete. &#x2A;*Fix:** Reinstall the affected globals (`lpm install --global <pkg>`).                          |
| `global_trusted_deps_valid`      | pass     | `~/.lpm/global/trusted-dependencies.json` parses cleanly.                                                                                                         |
| `global_trusted_deps_absent`     | pass     | No host-global trusted-dependencies file is present yet.                                                                                                          |
| `global_trusted_deps_corrupt`    | fail     | `~/.lpm/global/trusted-dependencies.json` is unreadable, malformed, or uses a newer schema. &#x2A;*Fix:** Repair or delete it, then re-approve globals as needed. |

### Sandbox + scripts [#sandbox--scripts]

| Code                           | Severity | Description                                                                                                                                                                                                                                                                                |
| ------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `sandbox_available`            | pass     | The OS sandbox backend used by lifecycle scripts is available.                                                                                                                                                                                                                             |
| `sandbox_helper_missing`       | warn     | (Windows) `lpm-sandbox-helper.exe` is not located next to `lpm.exe`, so strict-mode AppContainer containment falls back to the Low IL backend (no outbound-network denial). &#x2A;*Fix:** Reinstall `@lpm-registry/cli`, or set `LPM_SANDBOX_HELPER=<path>` if the helper lives elsewhere. |
| `sandbox_degraded`             | warn     | Strict mode is engaged but the host kernel forced the V1 fallback (filesystem containment only — no outbound network denial). &#x2A;*Fix:** Upgrade to kernel 6.7+ and unset `[sandbox] allow-degraded`, or drop back to default via `lpm config sandbox --set default`.                   |
| `sandbox_disabled_by_user`     | warn     | Sandbox is persistently disabled via `[sandbox] mode = "none"`. Lifecycle scripts run with no containment, including credential env vars. &#x2A;*Fix:** Restore via `lpm config sandbox --set default` (or `--set strict`).                                                                |
| `sandbox_kernel_too_old`       | warn     | Linux kernel is too old to support Landlock at the required ABI. &#x2A;*Fix:** Upgrade the kernel, or accept that lifecycle scripts run unsandboxed.                                                                                                                                       |
| `sandbox_unsupported_platform` | warn     | No supported sandbox backend exists for this platform. &#x2A;*Fix:** Lifecycle scripts will not be sandboxed on this platform — review with `lpm approve-scripts`.                                                                                                                         |
| `sandbox_probe_failed`         | fail     | The sandbox probe errored unexpectedly. &#x2A;*Fix:** File a bug with the `detail` text.                                                                                                                                                                                                   |
| `policy_force_security_floor`  | warn     | An override is in effect that lowers the default script-policy floor. &#x2A;*Fix:** Review the override and confirm it matches the project's threat model.                                                                                                                                 |

### Sigstore provenance [#sigstore-provenance]

| Code                        | Severity | Description                                                                                                                                                                |
| --------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sigstore_verify_enforced`  | pass     | Sigstore provenance verification is fail-closed. Rejected attestations refuse the install or approval.                                                                     |
| `sigstore_verify_warn_mode` | warn     | Sigstore verification only warns and does not block installs. &#x2A;*Fix:** Unset `LPM_PROVENANCE_ENFORCE` (or set it to `deny`), or run `lpm config sigstore --set deny`. |
| `sigstore_verify_disabled`  | warn     | Sigstore verification is disabled entirely. &#x2A;*Fix:** Re-enable deny mode via `LPM_PROVENANCE_ENFORCE=deny` or `lpm config sigstore --set deny`.                       |

### Manifest compat [#manifest-compat]

| Code                    | Severity | Description                                                                                                                                                                                                                                                                                       |
| ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pnpm_overrides_drift`  | warn     | Entries in `pnpm.overrides` that LPM CLI is not honoring through `lpm.overrides`, top-level `overrides`, or `resolutions`. &#x2A;*Fix:** Run `lpm migrate` to translate, or mirror the entries verbatim in `lpm.overrides`.                                                                       |
| `pnpm_patches_drift`    | warn     | Entries in `pnpm.patchedDependencies` whose patch files LPM CLI cannot bind to via `lpm.patchedDependencies`. &#x2A;*Fix:** Re-author with `lpm patch` or mirror the entries into `lpm.patchedDependencies` after verifying the patch applies cleanly under LPM CLI's stricter integrity binding. |
| `pnpm_peer_rules_drift` | warn     | Sub-key entries (`ignoreMissing`, `allowedVersions`, `allowAny`) that LPM CLI is not honoring via `lpm.peerDependencyRules`. &#x2A;*Fix:** Run `lpm migrate` (the planner translates selector keys 1:1) or mirror the rules under `lpm.peerDependencyRules`.                                      |
| `engines_npm_ignored`   | warn     | `engines.npm` is declared but LPM CLI is not the npm CLI and does not enforce its constraint. &#x2A;*Fix:** Remove the field, or accept that LPM CLI ignores it. `engines.node` and `engines.lpm` are enforced.                                                                                   |
| `engines_pnpm_ignored`  | warn     | `engines.pnpm` is declared but LPM CLI does not enforce pnpm's version constraint. &#x2A;*Fix:** Remove the field, or accept that LPM CLI ignores it. `engines.node` and `engines.lpm` are enforced.                                                                                              |
| `engines_yarn_ignored`  | warn     | `engines.yarn` is declared but LPM CLI does not enforce yarn's version constraint. &#x2A;*Fix:** Remove the field, or accept that LPM CLI ignores it. `engines.node` and `engines.lpm` are enforced.                                                                                              |
| `engines_bun_ignored`   | warn     | `engines.bun` is declared but LPM CLI does not enforce bun's version constraint. &#x2A;*Fix:** Use `lpm.json > runtime.bun` when the project needs managed Bun on `PATH`; `engines.node` and `engines.lpm` are enforced.                                                                          |

## See also [#see-also]

* [`lpm doctor list`](#catalog) — the live inventory surface; mirrors this table
* [`lpm policy`](/docs/infra/policy) — inspect and test local policy extensions
* [`lpm store verify`](/docs/infra/store) — deeper store integrity check
* [`lpm cert status`](/docs/infra/cert) — CA + project cert detail
* [Project health](/docs/infra/project-health) — design and check inventory
