From 3e2d56469bef84a2275f2be717bd1136b2bbcf8f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 4 Jun 2026 11:53:40 -0400 Subject: [PATCH] docs: document doctor stale warning helpers --- .../doctor/shared/plugin-tool-allowlist-warnings.ts | 2 ++ src/commands/doctor/shared/preview-warnings.ts | 8 ++++++++ .../doctor/shared/release-configured-plugin-installs.ts | 4 ++++ src/commands/doctor/shared/stale-oauth-profile-shadows.ts | 4 ++++ src/commands/doctor/shared/stale-plugin-config.ts | 5 +++++ src/commands/doctor/shared/stale-subagent-allowlist.ts | 7 +++++++ src/commands/doctor/shared/update-phase.ts | 1 + 7 files changed, 31 insertions(+) diff --git a/src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts b/src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts index 1a3d8fb4d44e..119ed0871cbe 100644 --- a/src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts +++ b/src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts @@ -1,3 +1,4 @@ +// Doctor warnings for plugin allowlists that make configured tool policies ineffective. import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id"; import { isRecord as hasRecord } from "@openclaw/normalization-core/record-coerce"; import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce"; @@ -573,6 +574,7 @@ function addIssue(issues: Map>, key: string, sourceLabel: st issues.set(key, sources); } +/** Collect warnings when plugin allowlists block tools referenced by active tool policies. */ export function collectPluginToolAllowlistWarnings(params: { cfg: OpenClawConfig; env?: NodeJS.ProcessEnv; diff --git a/src/commands/doctor/shared/preview-warnings.ts b/src/commands/doctor/shared/preview-warnings.ts index bd81e837889e..a7bc8e487ccb 100644 --- a/src/commands/doctor/shared/preview-warnings.ts +++ b/src/commands/doctor/shared/preview-warnings.ts @@ -1,3 +1,4 @@ +// Doctor preview warning aggregation for config that can surprise users before repair. import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id"; import { isRecord as hasRecord } from "@openclaw/normalization-core/record-coerce"; import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce"; @@ -305,6 +306,7 @@ function formatTargets(targets: string[]): string { return `${targets.slice(0, 2).join(", ")}, and ${targets.length - 2} more`; } +/** Warn when visible-reply policy selects message_tool but message is unavailable. */ export function collectVisibleReplyToolPolicyWarnings(cfg: OpenClawConfig): string[] { const groupPolicy = resolveGroupVisibleReplyProvenance(cfg); const warnings: string[] = []; @@ -345,6 +347,7 @@ function formatChannelList(channels: string[]): string { .join(", ")}, and ${channels.length - 2} more`; } +/** Warn when routed channel agents lack the message tool required for channel actions. */ export function collectChannelBoundMessageToolPolicyWarnings(cfg: OpenClawConfig): string[] { return collectChannelRouteTargets(cfg).flatMap((target) => { const agentTools = resolveAgentConfig(cfg, target.agentId)?.tools; @@ -686,6 +689,7 @@ function collectInheritedByProviderConfiguredToolSectionWarnings(params: { }); } +/** Warn when configured tool sections no longer widen restrictive tool profiles. */ export function collectProfileConfiguredToolSectionWarnings(cfg: OpenClawConfig): string[] { const warnings: string[] = []; const globalTools = hasRecord(cfg.tools) ? cfg.tools : undefined; @@ -754,10 +758,13 @@ export function collectProfileConfiguredToolSectionWarnings(cfg: OpenClawConfig) } export type DoctorPreviewNotes = { + /** Non-warning doctor notes shown during preview. */ infoNotes: string[]; + /** Warning notes shown during preview. */ warningNotes: string[]; }; +/** Collect info and warning notes for doctor preview mode. */ export async function collectDoctorPreviewNotes(params: { cfg: OpenClawConfig; doctorFixCommand: string; @@ -959,6 +966,7 @@ export async function collectDoctorPreviewNotes(params: { return { infoNotes, warningNotes: warnings }; } +/** Collect warning notes only for callers that do not display info notes. */ export async function collectDoctorPreviewWarnings(params: { cfg: OpenClawConfig; doctorFixCommand: string; diff --git a/src/commands/doctor/shared/release-configured-plugin-installs.ts b/src/commands/doctor/shared/release-configured-plugin-installs.ts index 3aabf4c437bf..9ea7029f4b7d 100644 --- a/src/commands/doctor/shared/release-configured-plugin-installs.ts +++ b/src/commands/doctor/shared/release-configured-plugin-installs.ts @@ -1,3 +1,4 @@ +// Release-era repair for configs that imply official plugin installs before install records existed. import { collectConfiguredModelRefs } from "@openclaw/model-catalog-core/configured-model-refs"; import { normalizeNullableString as normalizeId } from "@openclaw/normalization-core/string-coerce"; import { collectConfiguredAgentHarnessRuntimes } from "../../../agents/harness-runtimes.js"; @@ -256,6 +257,7 @@ function addEligiblePluginId(cfg: OpenClawConfig, pluginIds: Set, plugin pluginIds.add(normalized); } +/** Return true when this config has not yet crossed the configured-plugin install release gate. */ export function shouldRunConfiguredPluginInstallReleaseStep(params: { currentVersion?: string | null; touchedVersion?: string | null; @@ -273,6 +275,7 @@ export function shouldRunConfiguredPluginInstallReleaseStep(params: { return touchedComparedToRelease === null || touchedComparedToRelease < 0; } +/** Collect plugin/channel ids implied by config for the release install backfill step. */ export function collectReleaseConfiguredPluginIds(params: { cfg: OpenClawConfig; env?: NodeJS.ProcessEnv; @@ -327,6 +330,7 @@ export function collectReleaseConfiguredPluginIds(params: { }; } +/** Run the configured-plugin install release backfill when the config still needs it. */ export async function maybeRunConfiguredPluginInstallReleaseStep(params: { cfg: OpenClawConfig; env?: NodeJS.ProcessEnv; diff --git a/src/commands/doctor/shared/stale-oauth-profile-shadows.ts b/src/commands/doctor/shared/stale-oauth-profile-shadows.ts index b7ec6f4e7260..a57c5c118741 100644 --- a/src/commands/doctor/shared/stale-oauth-profile-shadows.ts +++ b/src/commands/doctor/shared/stale-oauth-profile-shadows.ts @@ -1,3 +1,4 @@ +// Doctor cleanup for per-agent OAuth profiles shadowing fresher main-agent credentials. import fs from "node:fs/promises"; import path from "node:path"; import { isRecord } from "@openclaw/normalization-core/record-coerce"; @@ -106,6 +107,7 @@ function shouldRemoveLocalOAuthShadow(params: { return mainExpires >= localExpires; } +/** Find local OAuth profiles that safely inherit fresher main-agent credentials instead. */ export async function scanStaleOAuthProfileShadows(params: { cfg: OpenClawConfig; env?: NodeJS.ProcessEnv; @@ -263,6 +265,7 @@ async function repairStaleOAuthProfilesForAgent(params: { : { status: "unchanged" }; } +/** Format warnings for stale per-agent OAuth profile shadows. */ export function collectStaleOAuthProfileShadowWarnings(params: { hits: StaleOAuthProfileShadow[]; doctorFixCommand: string; @@ -273,6 +276,7 @@ export function collectStaleOAuthProfileShadowWarnings(params: { ); } +/** Remove stale per-agent OAuth profile shadows after rechecking each locked store. */ export async function repairStaleOAuthProfileShadows(params: { cfg: OpenClawConfig; env?: NodeJS.ProcessEnv; diff --git a/src/commands/doctor/shared/stale-plugin-config.ts b/src/commands/doctor/shared/stale-plugin-config.ts index 0e47079578ec..8ce58b23ac3f 100644 --- a/src/commands/doctor/shared/stale-plugin-config.ts +++ b/src/commands/doctor/shared/stale-plugin-config.ts @@ -1,3 +1,4 @@ +// Doctor scanner and repair for plugin/channel config that references missing plugins. import { sanitizeForLog } from "../../../../packages/terminal-core/src/ansi.js"; import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../../agents/agent-scope.js"; import { CHANNEL_IDS } from "../../../channels/ids.js"; @@ -79,6 +80,7 @@ function collectPluginRegistryState( }; } +/** Return true when plugin discovery errors should pause stale-plugin auto-removal. */ export function isStalePluginAutoRepairBlocked( cfg: OpenClawConfig, env?: NodeJS.ProcessEnv, @@ -89,6 +91,7 @@ export function isStalePluginAutoRepairBlocked( return collectPluginRegistryState(cfg, env).hasDiscoveryErrors; } +/** Scan plugin/channel config surfaces for ids no longer present in manifests or installs. */ export function scanStalePluginConfig( cfg: OpenClawConfig, env?: NodeJS.ProcessEnv, @@ -305,6 +308,7 @@ function formatStalePluginHitWarning(hit: StalePluginConfigHit): string { return `- ${hit.pathLabel}: model override references missing channel plugin "${hit.pluginId}".`; } +/** Format warnings for stale plugin config hits. */ export function collectStalePluginConfigWarnings(params: { hits: StalePluginConfigHit[]; doctorFixCommand: string; @@ -326,6 +330,7 @@ export function collectStalePluginConfigWarnings(params: { return lines.map((line) => sanitizeForLog(line)); } +/** Remove stale plugin ids and dangling channel references when discovery is healthy. */ export function maybeRepairStalePluginConfig( cfg: OpenClawConfig, env?: NodeJS.ProcessEnv, diff --git a/src/commands/doctor/shared/stale-subagent-allowlist.ts b/src/commands/doctor/shared/stale-subagent-allowlist.ts index 526784867a03..0e085450b71a 100644 --- a/src/commands/doctor/shared/stale-subagent-allowlist.ts +++ b/src/commands/doctor/shared/stale-subagent-allowlist.ts @@ -1,11 +1,15 @@ +// Doctor scanner and repair for subagent allowlists that reference missing agents. import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; import { listAgentIds } from "../../../agents/agent-scope-config.js"; import type { OpenClawConfig } from "../../../config/types.openclaw.js"; import { normalizeAgentId } from "../../../routing/session-key.js"; export type StaleSubagentAllowlistHit = { + /** Config path containing the stale allowAgents entry. */ pathLabel: string; + /** Original configured agent id. */ agentId: string; + /** Normalized agent id used for matching configured targets. */ normalizedAgentId: string; }; @@ -80,6 +84,7 @@ function collectStaleAllowlistEntries(params: { return hits; } +/** Find subagent allowlist entries not backed by configured agent or ACP targets. */ export function scanStaleSubagentAllowlistReferences( cfg: OpenClawConfig, ): StaleSubagentAllowlistHit[] { @@ -105,6 +110,7 @@ export function scanStaleSubagentAllowlistReferences( return hits; } +/** Format warnings for stale subagent allowlist entries. */ export function collectStaleSubagentAllowlistWarnings(params: { hits: readonly StaleSubagentAllowlistHit[]; doctorFixCommand: string; @@ -131,6 +137,7 @@ function filterAllowAgents(params: { }); } +/** Remove stale subagent allowlist entries while preserving valid targets and wildcards. */ export function maybeRepairStaleSubagentAllowlists(cfg: OpenClawConfig): { config: OpenClawConfig; changes: string[]; diff --git a/src/commands/doctor/shared/update-phase.ts b/src/commands/doctor/shared/update-phase.ts index 03da23915ad2..b21d0e10741d 100644 --- a/src/commands/doctor/shared/update-phase.ts +++ b/src/commands/doctor/shared/update-phase.ts @@ -1,3 +1,4 @@ +// Update-phase helpers that gate doctor repairs during package swaps and convergence. import { isTruthyEnvValue } from "../../../infra/env.js"; export const UPDATE_IN_PROGRESS_ENV = "OPENCLAW_UPDATE_IN_PROGRESS";