Skip to content

Rename CLAUDE.md to AGENTS.md org-wide, with CLAUDE.md as a symlink #51

Description

@lesnik512

What

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.

Activity

  1. lesnik512 commented on Sep 6, 2026

    @lesnik512
    MemberAuthor

    This was generated by AI during triage.

    Agent Brief

    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 replaces CLAUDE.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:

    gh repo list modern-python --limit 60 --json name,isArchived \
      --jq '.[] | select(.isArchived | not) | .name'
    

    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.
    • 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.

  2. lesnik512 commented on Sep 6, 2026

    @lesnik512
    MemberAuthor

    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.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.

    This issue closes when they land.

  3. lesnik512 commented on Sep 6, 2026

    @lesnik512
    MemberAuthor

    This was generated by AI during triage.

    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.

  4. lesnik512 commented on Sep 6, 2026

    @lesnik512
    MemberAuthor

    This was generated by AI during triage.

    Complete

    Verified across all 28 non-archived org repos:

    • 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.

  5. added a commit that references this issue on Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationenhancementNew feature or requestready-for-agentFully specified, ready for an AFK agent

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions