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:
parent
b42d1af184
commit
5e57d8da24
61
src/commands/doctor-verification-debt.ts
Normal file
61
src/commands/doctor-verification-debt.ts
Normal 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
|
||||
}
|
||||
}
|
||||
@ -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.");
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user