mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-14 13:46:27 +00:00
Doctor: audit lint default selection (#100361)
Summary:
- audit Doctor lint default selection after the full lint-family backfill
- make legacy state, skills readiness, session transcripts, and session snapshots explicit-only for default lint
- document default lint vs --all/--only and keep plugin/SDK public contracts unchanged
Validation:
- Galin review: no blocking findings
- maintainer accepted the default-lint compatibility tradeoff in PR comment
- exact-head hosted gates passed for d5d88a0db1: CI#28976811444 and Workflow Sanity#28976811343
- local broad pnpm check was blocked by a shrinkwrap guard failure that reproduces on origin/main and is unrelated to this five-file Doctor diff
Co-authored-by: Gio Della-Libera <235387111+giodl73-repo@users.noreply.github.com>
This commit is contained in:
@@ -80,7 +80,9 @@ cat ~/.openclaw/openclaw.json
|
||||
|
||||
## Read-only lint mode
|
||||
|
||||
`openclaw doctor --lint` is the automation-friendly sibling of `openclaw doctor --fix`. Both run the same health checks; only the posture differs:
|
||||
`openclaw doctor --lint` is the automation-friendly sibling of
|
||||
`openclaw doctor --fix`. They share the same Doctor rule registry, but they do
|
||||
not select or act on rules in the same way:
|
||||
|
||||
| Mode | Prompts | Writes config/state | Output | Use it for |
|
||||
| ------------------------ | --------- | ----------------------- | ---------------------- | ------------------------------- |
|
||||
@@ -88,7 +90,27 @@ cat ~/.openclaw/openclaw.json
|
||||
| `openclaw doctor --fix` | sometimes | yes, with repair policy | friendly repair log | applying approved repairs |
|
||||
| `openclaw doctor --lint` | no | no | structured findings | CI, preflight, and review gates |
|
||||
|
||||
Health checks may provide an optional `repair()` implementation; `doctor --fix` applies it when present and falls back to the legacy doctor repair flow otherwise. The contract separates `detect()` (reports findings) from `repair()` (reports changes/diffs/side effects), which keeps a path open for a future `doctor --fix --dry-run` without turning lint checks into mutation planners.
|
||||
Default `doctor --lint` runs the broad-safe automation profile: checks that are
|
||||
static, local, and useful in CI or preflight output. It skips opt-in checks that
|
||||
are advisory, environment-sensitive, live-service dependent, account/workspace
|
||||
inventory, or historical cleanup. Use `doctor --lint --all` when you want the
|
||||
full registered lint audit, including those opt-in checks, or `--only <id>` for
|
||||
a targeted check.
|
||||
|
||||
`doctor --fix` does not use the lint default profile and does not accept
|
||||
`--all`. It runs Doctor's ordered repair path: modern health checks may provide
|
||||
an optional `repair()` implementation, and older areas still use their legacy
|
||||
Doctor repair flow. Some lint findings are intentionally diagnostic only, so a
|
||||
check appearing in `--lint --all` does not mean `--fix` will mutate that area.
|
||||
The contract separates `detect()` (reports findings) from `repair()` (reports
|
||||
changes/diffs/side effects), which keeps a path open for a future
|
||||
`doctor --fix --dry-run` without turning lint checks into mutation planners.
|
||||
|
||||
Some built-in checks are default-disabled internally so they stay available to
|
||||
`--all`, `--only`, and Doctor repair flows without becoming part of the default
|
||||
`doctor --lint` automation profile. Finding severity is still emitted per
|
||||
finding (`info`, `warning`, or `error`); default selection is not a severity
|
||||
level.
|
||||
|
||||
```bash
|
||||
openclaw doctor --lint
|
||||
@@ -115,7 +137,7 @@ Exit codes:
|
||||
Flags:
|
||||
|
||||
- `--severity-min info|warning|error` (default `warning`): controls both what prints and what causes a non-zero exit.
|
||||
- `--all`: runs every registered check, including opt-in checks excluded from the default automation set.
|
||||
- `--all`: runs every registered lint check, including opt-in checks excluded from the default automation set.
|
||||
- `--only <id>` (repeatable): run only the named check id(s); an unknown id is reported as an error finding.
|
||||
- `--skip <id>` (repeatable): exclude a check while keeping the rest of the run active.
|
||||
- `--json`, `--severity-min`, `--all`, `--only`, and `--skip` require `--lint`; plain `openclaw doctor` and `--fix` runs reject them.
|
||||
|
||||
@@ -357,6 +357,7 @@ describe("CORE_HEALTH_CHECKS", () => {
|
||||
"core/doctor/skills-readiness",
|
||||
);
|
||||
|
||||
expect(check).toMatchObject({ defaultEnabled: false });
|
||||
expect(check["repair"]).toBeTypeOf("function");
|
||||
|
||||
const findings = await check.detect({
|
||||
|
||||
@@ -422,11 +422,12 @@ const hooksModelCheck: HealthCheck = {
|
||||
},
|
||||
};
|
||||
|
||||
const legacyStateCheck: HealthCheck = {
|
||||
const legacyStateCheck: HealthCheck & { readonly defaultEnabled: false } = {
|
||||
id: "core/doctor/legacy-state",
|
||||
kind: "core",
|
||||
description: "Legacy sessions, agent state, and channel auth paths have been migrated.",
|
||||
source: "doctor",
|
||||
defaultEnabled: false,
|
||||
async detect(ctx) {
|
||||
const { detectLegacyStateMigrations } = await import("../commands/doctor-state-migrations.js");
|
||||
const detected = await detectLegacyStateMigrations({ cfg: ctx.cfg });
|
||||
@@ -874,12 +875,15 @@ const browserCheck: HealthCheck = {
|
||||
},
|
||||
};
|
||||
|
||||
function createSkillsReadinessCheck(deps: CoreHealthCheckDeps): HealthCheck {
|
||||
function createSkillsReadinessCheck(
|
||||
deps: CoreHealthCheckDeps,
|
||||
): HealthCheck & { readonly defaultEnabled: false } {
|
||||
return {
|
||||
id: "core/doctor/skills-readiness",
|
||||
kind: "core",
|
||||
description: "Allowed skills are usable in the current runtime environment.",
|
||||
source: "doctor",
|
||||
defaultEnabled: false,
|
||||
async detect(ctx, scope) {
|
||||
const unavailable = filterUnavailableSkillsForScope(
|
||||
await deps.detectUnavailableSkills(ctx.cfg),
|
||||
|
||||
@@ -1428,6 +1428,11 @@ describe("doctor health contributions", () => {
|
||||
|
||||
it("passes the active config into legacy state migration", async () => {
|
||||
const contribution = requireDoctorContribution("doctor:legacy-state");
|
||||
const legacyStateCheck = CORE_HEALTH_CHECKS.find(
|
||||
(check) => check.id === "core/doctor/legacy-state",
|
||||
);
|
||||
expect(legacyStateCheck).toMatchObject({ defaultEnabled: false });
|
||||
|
||||
const cfg = { session: { store: "/tmp/shared-sessions.json" } };
|
||||
const detected = { preview: ["legacy sessions"], warnings: [] };
|
||||
mocks.detectLegacyStateMigrations.mockResolvedValue(detected);
|
||||
@@ -1570,6 +1575,12 @@ describe("doctor health contributions", () => {
|
||||
expect(contributionIds).toContain("core/doctor/config-audit-scrub");
|
||||
expect(contributionIds).toContain("core/doctor/session-transcripts");
|
||||
expect(contributionIds).toContain("core/doctor/session-snapshots");
|
||||
expect(
|
||||
contributionChecks.find((check) => check.id === "core/doctor/session-transcripts"),
|
||||
).toMatchObject({ defaultEnabled: false });
|
||||
expect(
|
||||
contributionChecks.find((check) => check.id === "core/doctor/session-snapshots"),
|
||||
).toMatchObject({ defaultEnabled: false });
|
||||
expect(contributionIds).toContain("core/doctor/plugin-registry");
|
||||
expect(contributionIds).toContain("core/doctor/configured-plugin-installs");
|
||||
expect(contributionIds).toContain("core/doctor/legacy-plugin-dependencies");
|
||||
|
||||
@@ -1769,6 +1769,7 @@ export function resolveDoctorHealthContributions(): DoctorHealthContribution[] {
|
||||
healthChecks: {
|
||||
id: "core/doctor/session-transcripts",
|
||||
description: "Legacy or branchy session transcript files are represented as findings.",
|
||||
defaultEnabled: false,
|
||||
async detect() {
|
||||
const { detectSessionTranscriptHealthIssues, sessionTranscriptIssueToHealthFinding } =
|
||||
await import("../commands/doctor-session-transcripts.js");
|
||||
@@ -1801,6 +1802,7 @@ export function resolveDoctorHealthContributions(): DoctorHealthContribution[] {
|
||||
healthChecks: {
|
||||
id: "core/doctor/session-snapshots",
|
||||
description: "Stale cached session snapshot paths are represented as findings.",
|
||||
defaultEnabled: false,
|
||||
async detect(ctx) {
|
||||
const { detectSessionSnapshotHealthIssues, sessionSnapshotIssueToHealthFinding } =
|
||||
await import("../commands/doctor-session-snapshots.js");
|
||||
|
||||
Reference in New Issue
Block a user