# description
Machine-readable output of `sm plugins doctor --json`. Aggregates per-status counts across built-in and drop-in plugins plus the structured issue / warning lists the human renderer produces, and the runtime contribution rejections persisted by the last scan. The `elapsedMs` top-level field is the command's own wall-clock (see `cli-contract.md` §Elapsed time).

# properties.ok.description
Always `true` on the happy path. Error envelopes use the shared `{ ok: false, error: { code, message } }` shape from `cli-contract.md` §Error envelope and do NOT carry this schema's other fields. A doctor run that surfaces issues still returns `ok: true` (the verb succeeded; the issues live under `issues[]` and gate the exit code).

# properties.kind.description
Discriminator pinning this envelope to the plugins-doctor verb.

# properties.counts.description
Aggregate counts across both built-in and drop-in plugins. `loaded` is the count of plugins whose runtime imported cleanly (status=enabled); `incompatible` folds the `incompatible-spec` and `incompatible-catalog` buckets; `invalid` is `invalid-manifest`; `loadError` is `load-error` + `id-collision`. The split mirrors the human table without committing to its label catalog.

# properties.issues.description
One entry per plugin in a non-success state (`incompatible-spec`, `incompatible-catalog`, `invalid-manifest`, `load-error`, `id-collision`). Empty when every plugin is `enabled` or intentionally `disabled`. Iteration order matches the human renderer.

# properties.issues.items.properties.id.description
Qualified extension id `<plugin>/<ext>`. The bare plugin id form is reserved for the bundle (aggregate) macro path in CLI / BFF requests and never appears in persisted state.

# properties.issues.items.properties.status.description
Failure mode. Matches the `IDiscoveredPlugin.status` enum minus `enabled` / `disabled` (those never land in `issues[]`).

# properties.issues.items.properties.reason.description
Sanitised human-readable explanation. May be empty when the loader supplied none.

# properties.warnings.description
Informational warnings that do NOT gate the exit code: `precondition-kind-unknown` (an extractor declares a `precondition.kind` no installed Provider emits), `unknown-slot` (a view contribution targets a slot id absent from the closed catalog), `unknown-input-type` (a declared setting picks an input-type id absent from the closed catalog; AJV rejects it at load for drop-ins, this is the catalog-drift defence-in-depth path mirroring `unknown-slot`), and `recommended-action-missing` (an action's `precondition.analyzerIds` names an analyzer no loaded plugin declares). Iteration order matches the human renderer.

# properties.warnings.items.properties.id.description
Qualified extension id (`<plugin>/<ext>`) for `precondition-kind-unknown` and `recommended-action-missing`, `<plugin>/<ext>/<contributionId>` for `unknown-slot`, or `<plugin>/<ext>/<settingId>` for `unknown-input-type`.

# properties.contributionErrors.description
View contributions the last persisted scan REJECTED at emit time (the "off-shape visible" follow-up): an `ctx.emitContribution(...)` call whose ref was not a declared contribution, or whose payload failed the target slot's payload schema. Read from `scan_contribution_errors`; empty when the last scan had no rejected emissions, or when no scan has run yet (fresh project / missing DB). Each entry gates the exit code (any contribution error → exit 1). Iteration order matches the human renderer (`pluginId`, `extensionId`, `nodePath`, `emittedAt` ASC).

# properties.contributionErrors.items.properties.pluginId.description
Plugin id of the extension whose emission was rejected.

# properties.contributionErrors.items.properties.extensionId.description
Extension id (within the plugin) that emitted the rejected contribution.

# properties.contributionErrors.items.properties.nodePath.description
Target node path the contribution was emitted against.

# properties.contributionErrors.items.properties.reason.description
Rejection reason: the literal `undeclared-contribution-ref`, or the AJV error string when the payload failed the slot's payload schema.

# properties.contributionErrors.items.properties.message.description
Sanitised human-readable diagnostic (mirrors the ephemeral `extension.error` event of kind `contribution-rejected`).

# properties.contributionErrors.items.properties.contributionId.description
Resolved contribution id. Absent for the `undeclared-contribution-ref` shape (no contribution was resolved).

# properties.contributionErrors.items.properties.slot.description
Resolved target slot. Absent for the `undeclared-contribution-ref` shape.

# properties.elapsedMs.description
Command's own wall-clock duration in milliseconds (see `cli-contract.md` §Elapsed time).