From 15bea25597ca5656cdcfe660babe33d6b2877d2b Mon Sep 17 00:00:00 2001 From: Ryan Snodgrass Date: Thu, 30 Jul 2026 23:05:46 -0700 Subject: [PATCH] fix(agents): drop Aider from the instruction-file registry; unbreak AGENTS.md guide Co-Authored-By: SageOx SageOx-Session: https://sageox.ai/c/ses_019fb6a6-5bf9-76a3-88cb-283c84366844 --- cmd/ox/guides/agents-md.md | 2 +- cmd/ox/instruction_files.go | 15 +++++++-------- cmd/ox/instruction_files_test.go | 25 +++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/cmd/ox/guides/agents-md.md b/cmd/ox/guides/agents-md.md index 30464488..aa2b7cdd 100644 --- a/cmd/ox/guides/agents-md.md +++ b/cmd/ox/guides/agents-md.md @@ -6,7 +6,7 @@ audience: both # AGENTS.md and CLAUDE.md -AI coding agents read instruction files at session start to learn project conventions. Different tools historically named these files differently — `CLAUDE.md` (Claude Code), `.cursorrules` (Cursor), `.windsurfrules` (Windsurf), `GEMINI.md` (Gemini CLI), `CONVENTIONS.md` (Aider). The cross-tool convention has converged on **AGENTS.md at the repo root**, which Codex, OpenCode, Amp, Pi, Goose, and Gemini CLI all read. +AI coding agents read instruction files at session start to learn project conventions. Different tools historically named these files differently — `CLAUDE.md` (Claude Code), `.cursorrules` (Cursor), `.windsurfrules` (Windsurf), `GEMINI.md` (Gemini CLI), `CONVENTIONS.md` (Aider). The cross-tool convention has converged on **AGENTS.md at the repo root**, which Codex, OpenCode, Amp, Pi, Goose, and the OpenAI Agents SDK all read. SageOx supports both `AGENTS.md` and `CLAUDE.md` and follows the convention of treating them as siblings — typically `CLAUDE.md` is a symlink to `AGENTS.md` so both work without duplication. diff --git a/cmd/ox/instruction_files.go b/cmd/ox/instruction_files.go index b81d18c2..8ba57e43 100644 --- a/cmd/ox/instruction_files.go +++ b/cmd/ox/instruction_files.go @@ -21,7 +21,6 @@ const ( agentCline = "cline" agentOpenCode = "opencode" agentAmp = "amp" - agentAider = "aider" agentGoose = "goose" ) @@ -160,13 +159,13 @@ var InstructionFileRegistry = []InstructionFileSpec{ MarkerFormat: markerFormatMarkdown, DetectFn: nil, // shares AGENTS.md }, - { - AgentType: agentAider, - DisplayName: "Aider", - ProjectFiles: []string{"AGENTS.md"}, - MarkerFormat: markerFormatMarkdown, - DetectFn: nil, // shares AGENTS.md - }, + // Aider is deliberately absent. It reads whatever .aider.conf.yml's `read:` + // points at — CONVENTIONS.md by default — never AGENTS.md, so an AGENTS.md + // entry here marked a file Aider does not load while the docs promised + // CONVENTIONS.md. ox-adapter-aider owns CONVENTIONS.md end to end (install, + // check, uninstall, diagnose) under its own ox:prime:aider markers; adding a + // CONVENTIONS.md entry here would stack a second, differently-marked copy of + // the same prime instruction on top of the adapter's block. See ox-fbrm. { AgentType: agentGoose, DisplayName: "Goose", diff --git a/cmd/ox/instruction_files_test.go b/cmd/ox/instruction_files_test.go index 7f03e7b1..d4aba536 100644 --- a/cmd/ox/instruction_files_test.go +++ b/cmd/ox/instruction_files_test.go @@ -21,7 +21,7 @@ func TestDetectedInstructionFiles_EmptyDir(t *testing.T) { targets := DetectedInstructionFiles(root) - // agents with nil DetectFn (Codex, OpenCode, Amp, Aider) always pass detection, + // agents with nil DetectFn (Codex, OpenCode, Amp, Goose) always pass detection, // so AGENTS.md appears via them even in an empty dir names := targetRelPaths(targets) assert.Contains(t, names, "AGENTS.md", "AGENTS.md should appear (shared by nil-DetectFn agents)") @@ -98,7 +98,7 @@ func TestDetectedInstructionFiles_DeduplicatesSharedFiles(t *testing.T) { targets := DetectedInstructionFiles(root) - // AGENTS.md is listed by Claude Code, Codex, OpenCode, Amp, and Aider. + // AGENTS.md is listed by Claude Code, Codex, OpenCode, Amp, and Goose. // Dedup should ensure it appears only once. names := targetRelPaths(targets) count := 0 @@ -615,6 +615,27 @@ func TestInstructionFileRegistry_GooseSharesAgentsMd(t *testing.T) { assert.False(t, goose.Creatable, "AGENTS.md is a primary file; Creatable would be redundant") } +// TestInstructionFileRegistry_AiderIsAdapterOwned pins Aider OUT of this +// registry. Aider loads only what .aider.conf.yml's `read:` names — +// CONVENTIONS.md by default — so an AGENTS.md entry marked a file Aider never +// reads while README.md and docs/guides/agent-compatibility.md both promised +// CONVENTIONS.md. ox-adapter-aider owns CONVENTIONS.md end to end under its own +// ox:prime:aider markers, so a CONVENTIONS.md entry here is equally wrong: it +// would stack a second, differently-marked prime block on the adapter's. +// +// Failure prevented (the general class, not just Aider): a registry spec that +// names an instruction file its agent does not actually load, or that the +// agent's own adapter already owns — either way ox reports the agent as primed +// when it is not, or double-primes it. +func TestInstructionFileRegistry_AiderIsAdapterOwned(t *testing.T) { + for _, spec := range InstructionFileRegistry { + assert.NotEqual(t, "aider", spec.AgentType, + "Aider must not be in the instruction-file registry — ox-adapter-aider owns CONVENTIONS.md") + assert.NotContains(t, spec.ProjectFiles, "CONVENTIONS.md", + "spec %s lists CONVENTIONS.md, which ox-adapter-aider already writes under its own markers", spec.AgentType) + } +} + func TestInstructionFileRegistry_PrimaryFilesAreMarkdown(t *testing.T) { // AGENTS.md and CLAUDE.md must use markdown format -- they support HTML comments for _, spec := range InstructionFileRegistry {