Repository navigation
Rename CLAUDE.md to AGENTS.md org-wide, with CLAUDE.md as a symlink #51
Description
Activity
- addeddocumentationImprovements or additions to documentationImprovements or additions to documentationenhancementNew feature or requestNew feature or request
on Sep 5, 2026 This was generated by AI during triage.
Agent Brief
Category: enhancement
Summary: RenameCLAUDE.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 inCLAUDE.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:
gh repo list modern-python --limit 60 --json name,isArchived \ --jq '.[] | select(.isArchived | not) | .name'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:
- The file's own H1. At least
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.- Prose mentions in tests.
faststream-outboxhas one in its client-contract test that names the file as the home of a documented invariant. - ADR bodies.
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:
- Every non-archived org repo that had a root
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. - In every touched repo, a case-insensitive search for
CLAUDE.mdacross 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 mirrorAGENTS.md, in both its prose and its checklist. - Each repo's own lint/test gate passes unchanged —
just lint-ciwhere present, plus the repo's test recipe. - Opening a session in a renamed repo loads the instructions (verifiable with
claude -pagainst a fact stated only in that file).
Out of scope:
- Creating a
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.- Editing the content of any instructions file beyond its own heading and stale self-references. This is a rename, not a rewrite.
- The PR-template convention question — that is Host the PR-body-as-spec template org-wide; adopt the convention here #50 and its follow-up. If a repo's PR template mentions the file, update the name only; leave its wording alone.
- Archived repos.
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.
- The file's own H1. At least
- addedready-for-agentFully specified, ready for an AFK agentFully specified, ready for an AFK agent
on Sep 6, 2026 This was generated by AI during triage.
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:
- In every touched repo, a case-insensitive search for
CLAUDE.mdacross 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 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.- Both recorded as git renames, so
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.- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile #54
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile chat-app#12
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile compose2pod#84
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile db-retry#33
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile eof-fixer#31
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile fastapi-sqlalchemy-template#63
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile faststream-concurrent-aiokafka#60
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile faststream-outbox#162
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile faststream-redis-timers#63
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile httpware#115
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile lite-bootstrap#168
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile litestar-sqlalchemy-template#38
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile modern-di#451
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile modern-di-aiogram#11
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile modern-di-aiohttp#17
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile modern-di-arq#11
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile modern-di-celery#12
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile modern-di-fastapi#40
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile modern-di-faststream#40
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile modern-di-flask#12
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile modern-di-grpc#11
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile modern-di-litestar#42
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile modern-di-pytest#34
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile modern-di-starlette#16
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile modern-di-taskiq#10
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile modern-di-typer#34
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile semvertag#55
- chore: rename CLAUDE.md to AGENTS.md and Justfile to justfile that-depends#237
This issue closes when they land.
- In every touched repo, a case-insensitive search for
This was generated by AI during triage.
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:
Claude Code reads
CLAUDE.md, notAGENTS.md.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:Fixture Result AGENTS.mdaloneNONE — not in context CLAUDE.mdreal file (control)codeword returned — loaded AGENTS.md+CLAUDE.mdsymlinkcodeword returned — loaded AGENTS.md+CLAUDE.mdcontaining@AGENTS.mdcodeword 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.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:@AGENTS.mdAn 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.This was generated by AI during triage.
Complete
Verified across all 28 non-archived org repos:
- 27 repos carry
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.- All 28 now use a lowercase
justfile. - Recent
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.- 27 repos carry
- added a commit that references this issue
on Sep 6, 2026
What
Every repo in the org keeps its agent instructions in
CLAUDE.md— this repo included.AGENTS.mdis 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.mdin each repo and leaveCLAUDE.mdas a symlink to it, soClaude Code keeps loading it with no change on its side.
Scope
Repos with a
CLAUDE.mdtoday include this one,modern-di,faststream-outbox, and themodern-di-*integrations. Worth doing in one pass rather than per repo — half-migrated is worsethan either end state, because a reader cannot tell which file is authoritative.
Per-repo checklist
git mv CLAUDE.md AGENTS.mdln -s AGENTS.md CLAUDE.md(git stores the symlink as a mode-120000 blob; no config needed)faststream-outboxthat is two:.github/PULL_REQUEST_TEMPLATE.mdand a prose mention in
tests/test_client_contract.py. Other repos will have their own.Worth checking before doing it
core.symlinks=trueanddeveloper 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.
AGENTS.mdas the real file andCLAUDE.mdas the link is the right way round —the generic name should be the content — but it means any tool that writes to
CLAUDE.mdwritesthrough the link, which is fine, and any tool that replaces it breaks the link silently.
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 onCLAUDE.md, so renaming in onerepo alone would have introduced the drift this issue exists to avoid.