feat: integrate verification debt into openclaw doctor

Adds debt score display to doctor command output:
- Shows total/unresolved debt count
- Highlights high-risk items (score >= 7)
- Breaks down by category
- Suggests remediation command

New file: src/commands/doctor-verification-debt.ts (~60 lines)
Modified: src/commands/doctor.ts (import + call)
This commit is contained in:
OpenClaw Explorer 2026-03-02 07:07:53 +08:00
parent b42d1af184
commit 5e57d8da24
2 changed files with 64 additions and 0 deletions

View File

@ -0,0 +1,61 @@
import type { OpenClawConfig } from "../config/config.js";
import type { DoctorCommandOptions } from "./doctor.js";
import { note } from "../terminal/note.js";
import { resolveDefaultAgentId } from "../config/agent-id.js";
import { resolveAgentWorkspaceDir } from "../config/paths.js";
import {
loadVerificationDebt,
calculateDebtScore,
getDebtSummary,
} from "../security/verification-debt.js";
export async function noteVerificationDebt(
cfg: OpenClawConfig,
options: DoctorCommandOptions,
): Promise<void> {
const workspaceDir = resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));
try {
const state = await loadVerificationDebt({ workspaceDir });
const score = calculateDebtScore(state);
const summary = getDebtSummary(state);
if (summary.unresolved === 0) {
note("✓ No verification debt — all security checks up to date.", "Security");
return;
}
const lines: string[] = [];
lines.push(`Verification Debt Score: ${score}`);
lines.push("");
lines.push(`Unresolved: ${summary.unresolved} / ${summary.total}`);
lines.push("");
if (summary.highRisk.length > 0) {
lines.push("⚠️ High-risk items (score ≥7):");
for (const item of summary.highRisk.slice(0, 5)) {
const ageDays = Math.floor((Date.now() - item.skippedAt) / (24 * 60 * 60 * 1000));
lines.push(` - [${item.category}] ${item.description} (risk: ${item.riskScore}, ${ageDays}d)`);
}
if (summary.highRisk.length > 5) {
lines.push(` ... and ${summary.highRisk.length - 5} more`);
}
lines.push("");
}
lines.push("By category:");
for (const [cat, count] of Object.entries(summary.byCategory)) {
if (count > 0) {
lines.push(` - ${cat}: ${count}`);
}
}
lines.push("");
lines.push("Run 'openclaw security audit --deep' to address security debt.");
const severity = score >= 20 ? "error" : score >= 10 ? "warn" : "info";
note(lines.join("\n"), "Security", severity);
} catch {
// Debt file doesn't exist yet — that's fine, first run will create it
}
}

View File

@ -53,6 +53,7 @@ import {
import { maybeRepairUiProtocolFreshness } from "./doctor-ui.js";
import { maybeOfferUpdateBeforeDoctor } from "./doctor-update.js";
import { noteWorkspaceStatus } from "./doctor-workspace-status.js";
import { noteVerificationDebt } from "./doctor-verification-debt.js";
import { MEMORY_SYSTEM_PROMPT, shouldSuggestMemorySystem } from "./doctor-workspace.js";
import { applyWizardMetadata, printWizardHeader, randomToken } from "./onboard-helpers.js";
import { ensureSystemdUserLingerInteractive } from "./systemd-linger.js";
@ -322,5 +323,7 @@ export async function doctorCommand(
}
}
await noteVerificationDebt(cfg, options);
outro("Doctor complete.");
}