Skip to content

Commit 22376d8

Browse files
kasangyongclaudevincentkoc
authored
fix(doctor): honor per-agent bootstrap profile in size check (#84424)
* fix(doctor): honor per-agent bootstrap profile in size check * fix(doctor): thread defaultAgentId through structured bootstrap-size health check The noteBootstrapFileSize note path was fixed in the previous commit. This commit applies the same defaultAgentId threading to the registered core/doctor/bootstrap-size health check in doctor-core-checks.ts, which is used by doctor --lint and repair flows. Adds an integration regression test that places a 15 000-char AGENTS.md between the per-agent budget (10 000) and the defaults budget (20 000) and asserts that a truncation warning is emitted -- the test fails on main (defaults budget wins, no warning) and passes after this patch (per-agent budget wins, warning). Also updates three fixHint strings in the health check to name the per-agent knob before the defaults fallback, matching the note-path tip text from the previous commit. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix(doctor): preserve total-budget remediation guidance --------- Co-authored-by: Claude Sonnet 4.6 <[email protected]> Co-authored-by: Vincent Koc <[email protected]>
1 parent 827f2c4 commit 22376d8

4 files changed

Lines changed: 78 additions & 12 deletions

File tree

src/commands/doctor-bootstrap-size.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,25 @@ describe("noteBootstrapFileSize", () => {
6262
"Total bootstrap injected chars: 20,000 (13% of max/total 150,000).",
6363
"Total bootstrap raw chars (before truncation): 25,000.",
6464
"",
65-
"- Tip: tune `agents.defaults.bootstrapMaxChars` for per-file limits.",
65+
"- Tip: tune `agents.list[].bootstrapMaxChars` for this agent, or `agents.defaults.bootstrapMaxChars` as fallback, for per-file limits.",
6666
].join("\n"),
6767
);
6868
});
6969

70+
it("threads the default agent id through bootstrap size resolution", async () => {
71+
resolveDefaultAgentId.mockReturnValueOnce("custom-agent");
72+
resolveBootstrapContextForRun.mockResolvedValue({
73+
bootstrapFiles: [],
74+
contextFiles: [],
75+
});
76+
await noteBootstrapFileSize({} as OpenClawConfig);
77+
expect(resolveBootstrapMaxChars).toHaveBeenCalledWith(expect.anything(), "custom-agent");
78+
expect(resolveBootstrapTotalMaxChars).toHaveBeenCalledWith(expect.anything(), "custom-agent");
79+
expect(resolveBootstrapContextForRun).toHaveBeenCalledWith(
80+
expect.objectContaining({ agentId: "custom-agent" }),
81+
);
82+
});
83+
7084
it("stays silent when files are comfortably within limits", async () => {
7185
resolveBootstrapContextForRun.mockResolvedValue({
7286
bootstrapFiles: [

src/commands/doctor-bootstrap-size.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@ function formatCauses(causes: Array<"per-file-limit" | "total-limit">): string {
3737
* Returns the raw budget analysis for tests and callers that need structured evidence.
3838
*/
3939
export async function noteBootstrapFileSize(cfg: OpenClawConfig) {
40-
const workspaceDir = resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));
41-
const bootstrapMaxChars = resolveBootstrapMaxChars(cfg);
42-
const bootstrapTotalMaxChars = resolveBootstrapTotalMaxChars(cfg);
40+
const defaultAgentId = resolveDefaultAgentId(cfg);
41+
const workspaceDir = resolveAgentWorkspaceDir(cfg, defaultAgentId);
42+
const bootstrapMaxChars = resolveBootstrapMaxChars(cfg, defaultAgentId);
43+
const bootstrapTotalMaxChars = resolveBootstrapTotalMaxChars(cfg, defaultAgentId);
4344
const { bootstrapFiles, contextFiles } = await resolveBootstrapContextForRun({
4445
workspaceDir,
4546
config: cfg,
47+
agentId: defaultAgentId,
4648
});
4749
const stats = buildBootstrapInjectionStats({
4850
bootstrapFiles,
@@ -96,10 +98,14 @@ export async function noteBootstrapFileSize(cfg: OpenClawConfig) {
9698
lines.push("");
9799
}
98100
if (needsPerFileTip) {
99-
lines.push("- Tip: tune `agents.defaults.bootstrapMaxChars` for per-file limits.");
101+
lines.push(
102+
"- Tip: tune `agents.list[].bootstrapMaxChars` for this agent, or `agents.defaults.bootstrapMaxChars` as fallback, for per-file limits.",
103+
);
100104
}
101105
if (needsTotalTip) {
102-
lines.push("- Tip: tune `agents.defaults.bootstrapTotalMaxChars` for total-budget limits.");
106+
lines.push(
107+
"- Tip: tune `agents.list[].bootstrapTotalMaxChars` for this agent, or `agents.defaults.bootstrapTotalMaxChars` as fallback, for total-budget limits.",
108+
);
103109
}
104110

105111
note(lines.join("\n"), "Bootstrap file size");

src/flows/doctor-core-checks.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,3 +878,44 @@ describe("CORE_HEALTH_CHECKS", () => {
878878
);
879879
});
880880
});
881+
882+
describe("core/doctor/bootstrap-size", () => {
883+
let tmp: string | undefined;
884+
885+
afterEach(async () => {
886+
if (tmp !== undefined) {
887+
await fs.rm(tmp, { recursive: true, force: true });
888+
tmp = undefined;
889+
}
890+
});
891+
892+
it("honors the per-agent bootstrapMaxChars override in health findings", async () => {
893+
tmp = await fs.mkdtemp(join(tmpdir(), "openclaw-health-bootstrap-"));
894+
// This size fits the global default but exceeds the default agent's effective budget.
895+
await fs.writeFile(join(tmp, "AGENTS.md"), "a".repeat(15_000), "utf-8");
896+
897+
const check = getCheck(CORE_HEALTH_CHECKS, "core/doctor/bootstrap-size");
898+
const findings = await check.detect({
899+
mode: "lint",
900+
runtime,
901+
cfg: {
902+
agents: {
903+
defaults: {
904+
workspace: tmp,
905+
bootstrapMaxChars: 20_000,
906+
},
907+
list: [{ id: "custom-agent", default: true, bootstrapMaxChars: 10_000 }],
908+
},
909+
},
910+
});
911+
912+
expect(findings).toContainEqual(
913+
expect.objectContaining({
914+
checkId: "core/doctor/bootstrap-size",
915+
severity: "warning",
916+
message: expect.stringContaining("AGENTS.md"),
917+
fixHint: expect.stringContaining("agents.list[].bootstrapMaxChars"),
918+
}),
919+
);
920+
});
921+
});

src/flows/doctor-core-checks.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -464,18 +464,20 @@ const bootstrapSizeCheck: HealthCheck = {
464464
const { resolveBootstrapContextForRun } = await import("../agents/bootstrap-files.js");
465465
const { resolveBootstrapMaxChars, resolveBootstrapTotalMaxChars } =
466466
await import("../agents/embedded-agent-helpers.js");
467-
const workspaceDir = resolveAgentWorkspaceDir(ctx.cfg, resolveDefaultAgentId(ctx.cfg));
467+
const defaultAgentId = resolveDefaultAgentId(ctx.cfg);
468+
const workspaceDir = resolveAgentWorkspaceDir(ctx.cfg, defaultAgentId);
468469
const { bootstrapFiles, contextFiles } = await resolveBootstrapContextForRun({
469470
workspaceDir,
470471
config: ctx.cfg,
472+
agentId: defaultAgentId,
471473
});
472474
const analysis = analyzeBootstrapBudget({
473475
files: buildBootstrapInjectionStats({
474476
bootstrapFiles,
475477
injectedFiles: contextFiles,
476478
}),
477-
bootstrapMaxChars: resolveBootstrapMaxChars(ctx.cfg),
478-
bootstrapTotalMaxChars: resolveBootstrapTotalMaxChars(ctx.cfg),
479+
bootstrapMaxChars: resolveBootstrapMaxChars(ctx.cfg, defaultAgentId),
480+
bootstrapTotalMaxChars: resolveBootstrapTotalMaxChars(ctx.cfg, defaultAgentId),
479481
});
480482
const findings: HealthFinding[] = [];
481483
for (const file of analysis.truncatedFiles) {
@@ -484,7 +486,8 @@ const bootstrapSizeCheck: HealthCheck = {
484486
severity: "warning",
485487
message: `${file.name} exceeds bootstrap limits and will be truncated.`,
486488
path: file.path,
487-
fixHint: "Reduce the file size or tune agents.defaults.bootstrapMaxChars/TotalMaxChars.",
489+
fixHint:
490+
"Reduce the file size or tune `agents.list[].bootstrapMaxChars` / `bootstrapTotalMaxChars` for this agent, or the corresponding `agents.defaults.*` fallback.",
488491
});
489492
}
490493
for (const file of analysis.nearLimitFiles) {
@@ -496,7 +499,8 @@ const bootstrapSizeCheck: HealthCheck = {
496499
severity: "info",
497500
message: `${file.name} is near the configured bootstrap file limit.`,
498501
path: file.path,
499-
fixHint: "Reduce the file size or tune agents.defaults.bootstrapMaxChars.",
502+
fixHint:
503+
"Reduce the file size or tune `agents.list[].bootstrapMaxChars` for this agent, or `agents.defaults.bootstrapMaxChars` as fallback, for per-file limits.",
500504
});
501505
}
502506
if (analysis.totalNearLimit) {
@@ -505,7 +509,8 @@ const bootstrapSizeCheck: HealthCheck = {
505509
severity: analysis.hasTruncation ? "warning" : "info",
506510
message: "Total bootstrap context is near the configured total limit.",
507511
path: workspaceDir,
508-
fixHint: "Reduce bootstrap file sizes or tune agents.defaults.bootstrapTotalMaxChars.",
512+
fixHint:
513+
"Reduce bootstrap file sizes or tune `agents.list[].bootstrapTotalMaxChars` for this agent, or `agents.defaults.bootstrapTotalMaxChars` as fallback.",
509514
});
510515
}
511516
return findings;

0 commit comments

Comments
 (0)