You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Rename CLAUDE.md to AGENTS.md org-wide, with CLAUDE.md as a symlink #51
Every repo in the org keeps its agent instructions in CLAUDE.md — this repo included. AGENTS.md
is the cross-agent filename other coding agents look for, so the instructions are invisible to
anything that is not Claude Code.
Proposal: rename the file to AGENTS.md in each repo and leave CLAUDE.md as a symlink to it, so
Claude Code keeps loading it with no change on its side.
Scope
Repos with a CLAUDE.md today include this one, modern-di, faststream-outbox, and the modern-di-* integrations. Worth doing in one pass rather than per repo — half-migrated is worse
than either end state, because a reader cannot tell which file is authoritative.
Per-repo checklist
git mv CLAUDE.md AGENTS.md
ln -s AGENTS.md CLAUDE.md (git stores the symlink as a mode-120000 blob; no config needed)
Update inbound references. In faststream-outbox that is two: .github/PULL_REQUEST_TEMPLATE.md
and a prose mention in tests/test_client_contract.py. Other repos will have their own.
Worth checking before doing it
Windows checkouts. Git creates symlinks on Windows only with core.symlinks=true and
developer mode or elevation; otherwise the working tree gets a plain text file containing the
target path. If anyone develops on Windows, a committed duplicate with a one-line pointer may beat
a symlink.
Direction.AGENTS.md as the real file and CLAUDE.md as the link is the right way round —
the generic name should be the content — but it means any tool that writes to CLAUDE.md writes
through the link, which is fine, and any tool that replaces it breaks the link silently.
Tools that resolve vs. read links. Worth a quick check that the agents in use follow a symlink
rather than skipping it.
Context
Raised in review on modern-python/faststream-outbox#159, which deliberately did not do the rename:
that PR's premise is aligning with modern-di, which is still on CLAUDE.md, so renaming in one
repo alone would have introduced the drift this issue exists to avoid.
Category: enhancement Summary: Rename CLAUDE.md to AGENTS.md in every org repo that has one, as a plain rename with no symlink, updating inbound references in the same pass.
Verified before writing this brief. Claude Code 2.1.261 discovers AGENTS.md natively: a fixture containing only AGENTS.md and no CLAUDE.md loaded correctly. A second fixture with AGENTS.md plus a CLAUDE.md symlink also loaded, once, with no double-read. The CLI bundle states it directly — "Claude Code hardcodes CLAUDE.md / AGENTS.md discovery."
This settles the issue's open question. The symlink was proposed so "Claude Code keeps loading it with no change on its side"; that is already true without one. Doing the plain rename removes the two risks the body flags — Windows checkouts needing core.symlinks and developer mode, and any tool that replacesCLAUDE.md silently breaking the link. Do not create the symlink.
Current behavior:
Agent instructions live in CLAUDE.md. Across the org's non-archived repos, 27 of 28 have one and none has an AGENTS.md, so any coding agent that looks for the cross-agent filename finds nothing. One repo (that-depends) has neither.
Desired behavior: AGENTS.md is the real, and only, instructions file in every repo that has one today. No CLAUDE.md remains — not as a file, not as a symlink. Every inbound reference points at the new name, and each file's own top-level heading no longer says CLAUDE.md.
Scope — derive it, don't trust a list. The set changes; compute it at run time rather than working from a snapshot:
For each, act only if CLAUDE.md exists at the repo root. As of triage that was every non-archived repo except that-depends.
Inbound references — find them per repo, don't assume a count. In each repo, after renaming, search the whole worktree for the old name and fix every hit:
The file's own H1. At least modern-di and faststream-outbox open with # CLAUDE.md, which becomes wrong on rename.
.github/PULL_REQUEST_TEMPLATE.md in the repos that have a local one — it links to the file for the "Where a fact goes" section.
Prose mentions in tests. faststream-outbox has one in its client-contract test that names the file as the home of a documented invariant.
ADR bodies. modern-di has at least one ADR citing the file by name.
modern-di's integration-authoring guide is the load-bearing one. It instructs new integration authors to mirror modern-di's CLAUDE.md, and appears in both its prose and its checklist. Left stale, it propagates the old name into every future integration repo.
Acceptance criteria:
Every non-archived org repo that had a root CLAUDE.md now has AGENTS.md and no CLAUDE.md — no file, no symlink.
git log --follow on the new path shows the pre-rename history, i.e. the change was a rename and not a delete-plus-add.
In every touched repo, a case-insensitive search for CLAUDE.md across the worktree returns no hits outside .git/.
Each renamed file's top-level heading names the new file.
modern-di's integration-authoring guide tells authors to mirror AGENTS.md, in both its prose and its checklist.
Each repo's own lint/test gate passes unchanged — just lint-ci where present, plus the repo's test recipe.
Opening a session in a renamed repo loads the instructions (verifiable with claude -p against a fact stated only in that file).
Out of scope:
Creating a CLAUDE.md symlink or stub. Explicitly rejected above; do not add one "just in case".
that-depends. It has no agent instructions at all. Whether it should is a separate decision, not part of a rename.
Editing the content of any instructions file beyond its own heading and stale self-references. This is a rename, not a rewrite.
Sequencing note: the body is right that half-migrated is worse than either end state, because a reader cannot tell which file is authoritative. Prefer one pass over all repos. If that is not possible in a single run, finish each repo completely — rename plus all its inbound references — before starting the next, so no repo is ever left in a mixed state.
Done as a plain rename with no symlink, per the brief. One PR per repo; each is complete on its own, so no repo is ever left half-migrated.
Correction to the brief above
One acceptance criterion I wrote is wrong and was not applied:
In every touched repo, a case-insensitive search for CLAUDE.md across the worktree returns no hits outside .git/.
That fails for any repo with a planning/ tree. Most hits live in frozen historical records — planning/changes/, planning/releases/, planning/retros/, planning/audits/ — which narrate what was done at a time when the file genuinely was called CLAUDE.md. Rewriting them would falsify the record. semvertag alone has 46 such references across 9 change files and a release note; all were left untouched.
The rule actually applied: live pointers are updated, frozen narrative is not. Live means the instructions file itself, README, .github/, docs/ (including ADRs and docs/agents/), architecture/, planning/deferred.md, planning/decisions/, source and tests. Decision records were treated as live, since a reader following an ADR's pointer today should land on the file that exists.
Scope, as executed
CLAUDE.md → AGENTS.md: 27 repos.that-depends had none.
Justfile → justfile: 24 repos.modern-di, faststream-outbox, chat-app and compose2pod were already lowercase. This second rename is not from this issue — it was requested alongside, and bundled so each repo is touched once. just resolves either casing, so nothing changes operationally.
Both recorded as git renames, so git log --follow keeps history.
The references predicted in the brief were all present and are all fixed — notably modern-di's integration-authoring guide, in both its prose and its checklist, so new integration repos will be told to mirror AGENTS.md.
Status
All 28 PRs pass their repo's own CI. 27 are CLEAN; litestar-sqlalchemy-template#38 is BLOCKED solely because branch protection requires one approving review — its lint and pytest checks both pass.
Correction: the premise of the plain rename was wrong
The brief above claimed Claude Code discovers AGENTS.md natively, and the rename was executed on that basis without a CLAUDE.md. That was wrong, and it left 27 repos with no project instructions loading at all until it was fixed.
What the evidence actually says
The documentation is explicit:
Claude Code reads CLAUDE.md, not AGENTS.md.
The test that appeared to prove otherwise was invalid. It ran claude -p with file tools available, so the model simply read AGENTS.md off disk and reported the value. That demonstrates the file exists, not that it was loaded as context. Re-run with file tools denied:
Fixture
Result
AGENTS.md alone
NONE — not in context
CLAUDE.md real file (control)
codeword returned — loaded
AGENTS.md + CLAUDE.md symlink
codeword returned — loaded
AGENTS.md + CLAUDE.md containing @AGENTS.md
codeword returned — loaded
The CLI-bundle string cited as corroboration — "Claude Code hardcodes CLAUDE.md / AGENTS.md discovery" — was over-read. In context it belongs to the Codex importer, explaining why it maps Codex's AGENTS.md onto Claude's CLAUDE.md. That is evidence for the opposite conclusion.
The original proposal was right
This issue's body specified AGENTS.md as the real file with CLAUDE.md alongside it, and listed "tools that resolve vs. read links" as worth checking before doing it. That check was the correct instinct, and the answer is that the second file is required, not optional.
What was done
Each of the 27 repos now carries a one-line CLAUDE.md:
@AGENTS.md
An import rather than the symlink this issue proposed, for the Windows reason the body itself raises: a symlink needs Administrator or Developer Mode, and without them the checkout gets a plain text file containing the target path. The import is also what the documentation recommends, and it leaves room to append Claude-specific instructions below it. AGENTS.md stays the single home of the content, so the two cannot drift.
Verified end-to-end on modern-di with file tools denied: the instructions load, reported as "AGENTS.md, imported via CLAUDE.md".
Status
26 of 27 restored and merged. modern-python/litestar-sqlalchemy-template#39 is open — branch protection requires one approving review. That repo currently has AGENTS.md and no CLAUDE.md, so it loads no instructions until that PR lands.
Correction to the acceptance criteria
The criterion "no CLAUDE.md remains — no file, no symlink" is wrong and should read: every repo keeps a CLAUDE.md that imports AGENTS.md.
27 repos carry AGENTS.md as the real file, each with a CLAUDE.md whose entire contents are @AGENTS.md. Checked by content, not just existence — no repo has a stray or divergent CLAUDE.md.
that-depends has neither, correctly: it never had agent instructions. Whether it should is a separate question.
All 28 now use a lowercase justfile.
Recent main runs are green in every repo; no PRs from this work remain open.
Both renames were recorded as git renames, so git log --follow reaches the pre-rename history in each case.
Historical records in planning/changes/, planning/releases/, planning/retros/ and planning/audits/ were deliberately left naming CLAUDE.md and Justfile, since they narrate what was true at the time. Live pointers were updated, including modern-di's integration-authoring guide, so new integration repos will be told to mirror AGENTS.md.
Closing. The correction above stands as the record of why CLAUDE.md is still present: Claude Code does not read AGENTS.md, and the import is what makes the cross-agent filename work for both.
Activity
lesnik512 commentedon Sep 6, 2026
Agent Brief
Category: enhancement
Summary: Rename
CLAUDE.mdtoAGENTS.mdin every org repo that has one, as a plain rename with no symlink, updating inbound references in the same pass.Verified before writing this brief. Claude Code 2.1.261 discovers
AGENTS.mdnatively: a fixture containing onlyAGENTS.mdand noCLAUDE.mdloaded correctly. A second fixture withAGENTS.mdplus aCLAUDE.mdsymlink also loaded, once, with no double-read. The CLI bundle states it directly — "Claude Code hardcodes CLAUDE.md / AGENTS.md discovery."This settles the issue's open question. The symlink was proposed so "Claude Code keeps loading it with no change on its side"; that is already true without one. Doing the plain rename removes the two risks the body flags — Windows checkouts needing
core.symlinksand developer mode, and any tool that replacesCLAUDE.mdsilently breaking the link. Do not create the symlink.Current behavior:
Agent instructions live in
CLAUDE.md. Across the org's non-archived repos, 27 of 28 have one and none has anAGENTS.md, so any coding agent that looks for the cross-agent filename finds nothing. One repo (that-depends) has neither.Desired behavior:
AGENTS.mdis the real, and only, instructions file in every repo that has one today. NoCLAUDE.mdremains — not as a file, not as a symlink. Every inbound reference points at the new name, and each file's own top-level heading no longer saysCLAUDE.md.Scope — derive it, don't trust a list. The set changes; compute it at run time rather than working from a snapshot:
For each, act only if
CLAUDE.mdexists at the repo root. As of triage that was every non-archived repo exceptthat-depends.Inbound references — find them per repo, don't assume a count. In each repo, after renaming, search the whole worktree for the old name and fix every hit:
modern-diandfaststream-outboxopen with# CLAUDE.md, which becomes wrong on rename..github/PULL_REQUEST_TEMPLATE.mdin the repos that have a local one — it links to the file for the "Where a fact goes" section.faststream-outboxhas one in its client-contract test that names the file as the home of a documented invariant.modern-dihas at least one ADR citing the file by name.modern-di's integration-authoring guide is the load-bearing one. It instructs new integration authors to mirrormodern-di'sCLAUDE.md, and appears in both its prose and its checklist. Left stale, it propagates the old name into every future integration repo.Acceptance criteria:
CLAUDE.mdnow hasAGENTS.mdand noCLAUDE.md— no file, no symlink.git log --followon the new path shows the pre-rename history, i.e. the change was a rename and not a delete-plus-add.CLAUDE.mdacross the worktree returns no hits outside.git/.modern-di's integration-authoring guide tells authors to mirrorAGENTS.md, in both its prose and its checklist.just lint-ciwhere present, plus the repo's test recipe.claude -pagainst a fact stated only in that file).Out of scope:
CLAUDE.mdsymlink or stub. Explicitly rejected above; do not add one "just in case".that-depends. It has no agent instructions at all. Whether it should is a separate decision, not part of a rename.Sequencing note: the body is right that half-migrated is worse than either end state, because a reader cannot tell which file is authoritative. Prefer one pass over all repos. If that is not possible in a single run, finish each repo completely — rename plus all its inbound references — before starting the next, so no repo is ever left in a mixed state.
lesnik512 commentedon Sep 6, 2026
Executed — 28 PRs open, all CI green
Done as a plain rename with no symlink, per the brief. One PR per repo; each is complete on its own, so no repo is ever left half-migrated.
Correction to the brief above
One acceptance criterion I wrote is wrong and was not applied:
That fails for any repo with a
planning/tree. Most hits live in frozen historical records —planning/changes/,planning/releases/,planning/retros/,planning/audits/— which narrate what was done at a time when the file genuinely was calledCLAUDE.md. Rewriting them would falsify the record.semvertagalone has 46 such references across 9 change files and a release note; all were left untouched.The rule actually applied: live pointers are updated, frozen narrative is not. Live means the instructions file itself,
README,.github/,docs/(including ADRs anddocs/agents/),architecture/,planning/deferred.md,planning/decisions/, source and tests. Decision records were treated as live, since a reader following an ADR's pointer today should land on the file that exists.Scope, as executed
CLAUDE.md→AGENTS.md: 27 repos.that-dependshad none.Justfile→justfile: 24 repos.modern-di,faststream-outbox,chat-appandcompose2podwere already lowercase. This second rename is not from this issue — it was requested alongside, and bundled so each repo is touched once.justresolves either casing, so nothing changes operationally.git log --followkeeps history.The references predicted in the brief were all present and are all fixed — notably
modern-di's integration-authoring guide, in both its prose and its checklist, so new integration repos will be told to mirrorAGENTS.md.Status
All 28 PRs pass their repo's own CI. 27 are
CLEAN;litestar-sqlalchemy-template#38isBLOCKEDsolely because branch protection requires one approving review — itslintandpytestchecks both pass.This issue closes when they land.
lesnik512 commentedon Sep 6, 2026
Correction: the premise of the plain rename was wrong
The brief above claimed Claude Code discovers
AGENTS.mdnatively, and the rename was executed on that basis without aCLAUDE.md. That was wrong, and it left 27 repos with no project instructions loading at all until it was fixed.What the evidence actually says
The documentation is explicit:
The test that appeared to prove otherwise was invalid. It ran
claude -pwith file tools available, so the model simply readAGENTS.mdoff disk and reported the value. That demonstrates the file exists, not that it was loaded as context. Re-run with file tools denied:AGENTS.mdaloneCLAUDE.mdreal file (control)AGENTS.md+CLAUDE.mdsymlinkAGENTS.md+CLAUDE.mdcontaining@AGENTS.mdThe CLI-bundle string cited as corroboration — "Claude Code hardcodes CLAUDE.md / AGENTS.md discovery" — was over-read. In context it belongs to the Codex importer, explaining why it maps Codex's
AGENTS.mdonto Claude'sCLAUDE.md. That is evidence for the opposite conclusion.The original proposal was right
This issue's body specified
AGENTS.mdas the real file withCLAUDE.mdalongside it, and listed "tools that resolve vs. read links" as worth checking before doing it. That check was the correct instinct, and the answer is that the second file is required, not optional.What was done
Each of the 27 repos now carries a one-line
CLAUDE.md:An import rather than the symlink this issue proposed, for the Windows reason the body itself raises: a symlink needs Administrator or Developer Mode, and without them the checkout gets a plain text file containing the target path. The import is also what the documentation recommends, and it leaves room to append Claude-specific instructions below it.
AGENTS.mdstays the single home of the content, so the two cannot drift.Verified end-to-end on
modern-diwith file tools denied: the instructions load, reported as "AGENTS.md, imported via CLAUDE.md".Status
26 of 27 restored and merged.
modern-python/litestar-sqlalchemy-template#39is open — branch protection requires one approving review. That repo currently hasAGENTS.mdand noCLAUDE.md, so it loads no instructions until that PR lands.Correction to the acceptance criteria
The criterion "no
CLAUDE.mdremains — no file, no symlink" is wrong and should read: every repo keeps aCLAUDE.mdthat importsAGENTS.md.lesnik512 commentedon Sep 6, 2026
Complete
Verified across all 28 non-archived org repos:
AGENTS.mdas the real file, each with aCLAUDE.mdwhose entire contents are@AGENTS.md. Checked by content, not just existence — no repo has a stray or divergentCLAUDE.md.that-dependshas neither, correctly: it never had agent instructions. Whether it should is a separate question.justfile.mainruns are green in every repo; no PRs from this work remain open.Both renames were recorded as git renames, so
git log --followreaches the pre-rename history in each case.Historical records in
planning/changes/,planning/releases/,planning/retros/andplanning/audits/were deliberately left namingCLAUDE.mdandJustfile, since they narrate what was true at the time. Live pointers were updated, includingmodern-di's integration-authoring guide, so new integration repos will be told to mirrorAGENTS.md.Closing. The correction above stands as the record of why
CLAUDE.mdis still present: Claude Code does not readAGENTS.md, and the import is what makes the cross-agent filename work for both.docs: add AGENTS.md and CLAUDE.md (#238)