refactor(agents): retire TOOLS.md into an AGENTS.md section with a doctor migration (#113966)

* fix(agents): stop retired attestation hashes from faking a vanished workspace

* feat(agents): migrate TOOLS.md content into the AGENTS.md tools section

* refactor(agents): drop TOOLS.md from the workspace bootstrap set

* refactor(policy): read tool policy entries from AGENTS.md

* docs: describe local tool notes as an AGENTS.md section

* test(codex): drop TOOLS.md developer-instruction coverage with the removed path

* test(agents): cover the TOOLS.md doctor migration behaviors

* refactor(doctor): satisfy TOOLS.md migration lint

* test(agents): split workspace attestation survival coverage

* refactor(codex): simplify workspace context basenames

* refactor(doctor): keep TOOLS.md migration helpers module-local

* docs: regenerate docs map

* fix(doctor): keep migration claims fresh

* fix(doctor): preserve nested tool notes

* refactor(doctor): split tools migration helpers

* refactor(doctor): limit TOOLS.md migration to workspace root

* style(doctor): keep migration under line limit

* fix(doctor): recover interrupted AGENTS publish

* docs(hooks): describe root-only TOOLS.md migration accurately

* fix(doctor): preserve migrated tool guidance visibility

* test(agents): regenerate prompt snapshots after rebase

* fix(policy): block evaluation while TOOLS.md is unmigrated

* fix(doctor): check merged bootstrap budget per agent

* fix(doctor): keep budget helper internal

* refactor(doctor): keep migration budget helpers to their consumers
This commit is contained in:
Peter Steinberger
2026-07-27 03:56:30 -04:00
committed by GitHub
parent 445774d85e
commit 669db2968f
80 changed files with 2262 additions and 501 deletions
@@ -240,13 +240,6 @@ const CODEX_WORKSPACE_BOOTSTRAP_CONTEXT_FILES = [
},
] as const;
const CODEX_WORKSPACE_THREAD_DEVELOPER_CONTEXT_FILES = [
{
path: path.join(WORKSPACE_DIR, "TOOLS.md"),
content: "<TOOLS.md contents will be here>",
},
] as const;
const CODEX_WORKSPACE_TURN_SCOPED_DEVELOPER_CONTEXT_FILES = [
{
path: path.join(WORKSPACE_DIR, "IDENTITY.md"),
@@ -268,7 +261,7 @@ const CODEX_HEARTBEAT_CONTEXT_FILE = {
} as const;
const CODEX_WORKSPACE_BOOTSTRAP_PROMPT_CONTEXT = [
"OpenClaw loaded these user-editable workspace files for the current turn. Codex loads AGENTS.md natively. TOOLS.md is provided as inherited Codex developer instructions. SOUL.md, IDENTITY.md, and USER.md are provided as turn-scoped collaboration instructions so native Codex subagents do not inherit them. HEARTBEAT.md is handled by heartbeat collaboration-mode guidance. Those files are not repeated here.",
"OpenClaw loaded these user-editable workspace files for the current turn. Codex loads AGENTS.md natively. SOUL.md, IDENTITY.md, and USER.md are provided as turn-scoped collaboration instructions so native Codex subagents do not inherit them. HEARTBEAT.md is handled by heartbeat collaboration-mode guidance. Those files are not repeated here.",
"",
"# Project Context",
"",
@@ -284,21 +277,6 @@ const CODEX_WORKSPACE_BOOTSTRAP_PROMPT_CONTEXT = [
.join("\n")
.trim();
const CODEX_WORKSPACE_THREAD_DEVELOPER_INSTRUCTIONS = [
"## OpenClaw Workspace Instructions",
"",
"OpenClaw loaded these workspace instruction files from the active agent workspace. Internalize and follow them accordingly.",
"",
...CODEX_WORKSPACE_THREAD_DEVELOPER_CONTEXT_FILES.flatMap((file) => [
`### ${file.path}`,
"",
file.content,
"",
]),
]
.join("\n")
.trim();
const CODEX_WORKSPACE_TURN_SCOPED_DEVELOPER_INSTRUCTIONS = [
"## OpenClaw Agent Soul",
"",
@@ -903,7 +881,6 @@ function renderScenarioSnapshot(
appServer,
config: CODEX_PROMPT_SNAPSHOT_THREAD_CONFIG,
promptText: codexTurnPromptText,
developerInstructionAdditions: CODEX_WORKSPACE_THREAD_DEVELOPER_INSTRUCTIONS,
turnScopedDeveloperInstructions: CODEX_WORKSPACE_TURN_SCOPED_DEVELOPER_INSTRUCTIONS,
heartbeatCollaborationInstructions:
scenario.trigger === "heartbeat" ? CODEX_HEARTBEAT_COLLABORATION_INSTRUCTIONS : undefined,
@@ -922,7 +899,7 @@ function renderScenarioSnapshot(
"",
...scenario.notes.map((note) => `- ${note}`),
"- This captures the OpenClaw-owned Codex app-server inputs and reconstructs the stable Codex model/permission layers from committed Codex prompt fixtures.",
"- This also simulates Codex workspace bootstrap routing: `TOOLS.md` as inherited developer instructions, `SOUL.md`, `IDENTITY.md`, and `USER.md` as turn-scoped collaboration instructions, `MEMORY.md` in turn input, and `HEARTBEAT.md` as a heartbeat-only file pointer.",
"- This also simulates Codex workspace bootstrap routing: `AGENTS.md` through native project-doc discovery, `SOUL.md`, `IDENTITY.md`, and `USER.md` as turn-scoped collaboration instructions, `MEMORY.md` in turn input, and `HEARTBEAT.md` as a heartbeat-only file pointer.",
"",
"## Scenario Metadata",
"",
@@ -942,8 +919,6 @@ function renderScenarioSnapshot(
simulatedWorkspaceBootstrapFiles: CODEX_WORKSPACE_BOOTSTRAP_CONTEXT_FILES.map(
(file) => file.path,
),
simulatedWorkspaceDeveloperInstructionFiles:
CODEX_WORKSPACE_THREAD_DEVELOPER_CONTEXT_FILES.map((file) => file.path),
simulatedWorkspaceTurnScopedDeveloperInstructionFiles:
CODEX_WORKSPACE_TURN_SCOPED_DEVELOPER_CONTEXT_FILES.map((file) => file.path),
simulatedHeartbeatWorkspaceFile: CODEX_HEARTBEAT_CONTEXT_FILE.path,
@@ -504,7 +504,7 @@ async function createToolRichScenario(workspaceDir: string): Promise<PromptScena
"<skill><name>release</name><description>Release OpenClaw safely.</description><location>/skills/release/SKILL.md</location></skill>",
"</available_skills>",
].join("\n");
const contextFiles = await readContextFiles(workspaceDir, ["AGENTS.md", "TOOLS.md", "SOUL.md"]);
const contextFiles = await readContextFiles(workspaceDir, ["AGENTS.md", "SOUL.md"]);
const systemPrompt = buildToolRichSystemPrompt({
workspaceDir,
skillsPrompt,
@@ -573,9 +573,9 @@ async function createBootstrapWarningScenario(workspaceDir: string): Promise<Pro
},
} satisfies OpenClawConfig;
const largeAgents = "# AGENTS.md\n\n" + "Rules.\n".repeat(5_000);
const largeTools = "# TOOLS.md\n\n" + "Notes.\n".repeat(3_000);
const largeSoul = "# SOUL.md\n\n" + "Notes.\n".repeat(3_000);
await writeWorkspaceFile({ dir: workspaceDir, name: "AGENTS.md", content: largeAgents });
await writeWorkspaceFile({ dir: workspaceDir, name: "TOOLS.md", content: largeTools });
await writeWorkspaceFile({ dir: workspaceDir, name: "SOUL.md", content: largeSoul });
const { bootstrapFiles, contextFiles } = await resolveBootstrapContextForRun({
workspaceDir,
config: bootstrapConfig,
@@ -668,7 +668,7 @@ async function createMaintenanceScenario(workspaceDir: string): Promise<PromptSc
const memoryFlushPrompt = [
"Pre-compaction memory flush.",
"Store durable memories only in memory/2026-03-15.md (create memory/ if needed).",
"Treat workspace bootstrap/reference files such as MEMORY.md, SOUL.md, TOOLS.md, and AGENTS.md as read-only during this flush; never overwrite, replace, or edit them.",
"Treat workspace bootstrap/reference files such as MEMORY.md, SOUL.md, and AGENTS.md as read-only during this flush; never overwrite, replace, or edit them.",
"If nothing to store, reply with NO_REPLY.",
"Current time: Sunday, March 15th, 2026 - 9:30 PM (America/Los_Angeles)",
"Reference UTC: 2026-03-16 04:30 UTC",
@@ -751,17 +751,15 @@ async function createWorkspaceWithPromptCompositionFiles(): Promise<string> {
"# AGENTS.md",
"",
"## Session Startup",
"Read AGENTS.md and TOOLS.md before making changes.",
"Read AGENTS.md before making changes.",
"",
"## Tools",
"Use rg before grep.",
"",
"## Red Lines",
"Do not rewrite user commits.",
].join("\n"),
});
await writeWorkspaceFile({
dir: workspaceDir,
name: "TOOLS.md",
content: "# TOOLS.md\n\nUse rg before grep.\n",
});
await writeWorkspaceFile({
dir: workspaceDir,
name: "SOUL.md",