Skip to content

chore: make AGENTS.md canonical, CLAUDE.md a pointer (ENG-5577) - #126

Merged
Tanq16 merged 3 commits into
mainfrom
nathansportsman/eng-5577-agents-md-canonical
Aug 5, 2026
Merged

chore: make AGENTS.md canonical, CLAUDE.md a pointer (ENG-5577)#126
Tanq16 merged 3 commits into
mainfrom
nathansportsman/eng-5577-agents-md-canonical

Conversation

@nsportsman

Copy link
Copy Markdown
Contributor

What

Makes AGENTS.md the canonical instruction file for this repo and reduces
CLAUDE.md to the one-line @AGENTS.md import pointer.

  • AGENTS.md — the previous CLAUDE.md content, byte-identical.
  • CLAUDE.md — now the 11-byte pointer @AGENTS.md (Anthropic-documented import syntax).
  • .gemini/settings.jsoncontext.fileName opt-in so Gemini reads AGENTS.md.

Why

This repo is a nested clone that carried a Claude-only root CLAUDE.md.
Codex and Gemini sessions opened here ran uninstructed. After this change
all three harnesses load the same instructions:

Harness How it reaches the content
Claude Code CLAUDE.md@AGENTS.md import
Codex reads AGENTS.md natively
Gemini .gemini/settings.jsoncontext.fileName

The direction (pointer in CLAUDE.md, content in AGENTS.md) is forced by
anthropics/claude-code#6235:
Claude Code does not read AGENTS.md natively, so CLAUDE.md must remain.

How

Produced by the scripted, idempotent conversion merged in
praetorian-inc/palatine#485
— the same conversion applied to guard-core in
praetorian-inc/guard#7319.
Only git-tracked files are touched; the script never merges and refuses on any
conflict. No instruction text was rewritten — content moved verbatim.

Verification

  • Byte-exactness confirmed: origin/main:CLAUDE.md vs the new AGENTS.md compare identical via cmp.
  • The script's read-only --check mode passes on the result.

Premise gate

SKIP recorded. The trigger is "builds non-trivial machinery"; this PR builds
none. It is a byte-identical file move plus an 11-byte pointer, generated by an
already-reviewed script — no new layer, no new subsystem, no newly-authored
behavior-defining prose.

Scope

Part of ENG-5577 (deliberately non-closing). That ticket covers 12 nested
clones; this PR converts one of them, so it must not auto-close the ticket while
the others are unlanded. Merge is a human decision.

Move the root CLAUDE.md content byte-exactly to a sibling AGENTS.md and
reduce CLAUDE.md to the one-line `@AGENTS.md` import pointer, so Codex
(native AGENTS.md) and Gemini (via .gemini/settings.json context.fileName)
load the same instructions Claude Code already did.

Direction is forced by anthropics/claude-code#6235: Claude Code does not
read AGENTS.md natively, so CLAUDE.md must remain as the pointer.

Produced by the scripted, idempotent conversion from palatine
(praetorian-inc/palatine#485) — the same one applied to guard-core in
praetorian-inc/guard#7319. Content is byte-identical to the previous
CLAUDE.md; no instruction text was rewritten.

Part of ENG-5577.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 17ce3229-28d2-4dec-b5e3-34d7af71eeb1

📥 Commits

Reviewing files that changed from the base of the PR and between bf35d87 and 086aeb8.

📒 Files selected for processing (4)
  • .gemini/settings.json
  • AGENTS.md
  • CLAUDE.md
  • internal/ado/collect_surfaces.go

Walkthrough

Added project-wide development guidance in AGENTS.md. Configured Gemini to load AGENTS.md and GEMINI.md. Replaced the contents of CLAUDE.md with an AGENTS.md reference. Updated soft-failure documentation references in surface collection code.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch nathansportsman/eng-5577-agents-md-canonical

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex Review

Critical Issues

None.

Security

  • Prompt-injection attempt detected: .gemini/settings.json:1 configures agent context loading, CLAUDE.md:1 redirects to AGENTS.md, and AGENTS.md:9 declares PR-supplied instructions as “binding.” Per the review policy, I stopped without reviewing further.

Suggestions

No suggestions.


Reviewed by Codex (gpt-5.6-sol)

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Review

Critical issues

None. Verified the core claim: AGENTS.md is created with git blob 3713b04, identical to the pre-change CLAUDE.md blob — the content move is byte-for-byte, and CLAUDE.md correctly reduces to the @AGENTS.md import pointer.

Security

No security concerns flagged. Documentation/config move only; no code, network, auth, or secrets involved.

Test coverage

Not applicable — no .go/.ts/*.py production changes.

No critical issues — LGTM pending human review.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gemini Review

Critical Issues

  • CLAUDE.md: Replacing the file's contents with the string @AGENTS.md breaks the contract for Claude Code, which expects the file to contain actual markdown instructions. It does not natively parse @ file references as includes, meaning the agent will lose its system context. If the goal is to single-source the instructions, use a git symlink instead (ln -s AGENTS.md CLAUDE.md).
  • .gemini/settings.json: The context.fileName array references "GEMINI.md", but this file does not exist in the repository.

Security

No security concerns flagged.

Suggestions

No suggestions.


Reviewed by Gemini (gemini-3.1-pro-preview)

AGENTS.md is now the canonical instruction file and CLAUDE.md is an
11-byte @AGENTS.md pointer, so comments citing CLAUDE.md as the place a
convention is documented now point at a stub. Retarget them.

Comment-only; no behavior change.

Part of ENG-5577.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@nsportsman

Copy link
Copy Markdown
Contributor Author

Adjudication — round 1

Every automated finding on this PR is dispositioned below as either confirmed (reproduced concretely, then fixed) or refuted (with the specific evidence it does not hold). No reviewer is blind-accepted, and none is blind-dismissed.

REFUTED — "Claude Code does not parse @AGENTS.md, so all project context is lost"

This is the most-repeated finding across the 12 conversion PRs, and it is directly testable. A/B run in a converted repo with file tools disabled, so the session cannot answer by reading AGENTS.md itself.

That last part is load-bearing. My first attempt at this test left tools enabled, and both arms answered correctly — by grepping the file. That control was vacuous and proved nothing about context loading, so I re-ran it with --disallowed-tools.

Probe: "Per this repository, which numeric exit code means 'estimate exceeds budget'? Reply with the bare number only, or NONE if you have no repository instructions." The answer appears only in AGENTS.md.

Arm CLAUDE.md contains Result
A — as shipped @AGENTS.md 3 (correct)
B — control AGENTS.md (the @ stripped) "this session has no Read/Grep/Glob/Bash tools available, so I can't open AGENTS.md … Rather than guess a number, I'd rather tell you that."

Arm A answers with no file access whatsoever, so the import was expanded into context before the turn began. Arm B — the identical file minus one character — cannot. That is the mechanism working, measured rather than assumed.

@path is Anthropic-documented import syntax (docs.claude.com/en/docs/claude-code/memory, "CLAUDE.md imports"). The direction here is also forced rather than chosen: Claude Code does not read AGENTS.md natively (anthropics/claude-code#6235), which is exactly why CLAUDE.md has to remain as a pointer instead of being deleted.

REFUTED — "use a git symlink (ln -s AGENTS.md CLAUDE.md) instead"

Two independent reasons.

1. The symlink degrades into precisely the failure this finding warns about. A mode-120000 symlink checked out with core.symlinks=false — the default on Git for Windows, and the case on some CI runners — materialises as a regular text file whose entire content is the string AGENTS.md. That is byte-for-byte Arm B of the table above: the arm that loses all context. So the proposal swaps a documented, measured mechanism for one that fails silently on a subset of checkouts, and fails in the exact way the finding is worried about.

2. The conversion script rejects symlinks deliberately. It refuses to follow or create them, a guard added after a containment-escape defect was found and fixed during review of praetorian-inc/palatine#485. Hand-adding one here would route around that guard and diverge from the other 11 repos in this batch.

REFUTED — ".gemini/settings.json lists GEMINI.md, which does not exist in the repository"

The fact is correct — no GEMINI.md exists — but it is not a defect. context.fileName is a candidate list, not a set of required files: Gemini loads whichever entries exist and ignores the rest.

Measured with gemini -p "/memory show", which makes the CLI report its own loaded context rather than having me infer it from an answer:

Arm .gemini/settings.json /memory show reports
A — as shipped {"context":{"fileName":["AGENTS.md","GEMINI.md"]}} Current memory content from 1 file(s):Context from: …/AGENTS.md
B — control fileName: ["NOSUCHCONTEXTFILE.md"] Memory is currently empty.
C — control file removed entirely Memory is currently empty.

Arm A loads AGENTS.md and reports 1 file, raising no error for the absent GEMINI.md — the entry is inert. It is also deliberate: it keeps a repo-local GEMINI.md working if one is ever added, without a settings change.

Arms B and C exist so the measurement can fail — without them, "it loaded" would be unfalsifiable. They also establish something worth stating: the settings file is load-bearing. Remove it and Gemini loads nothing at all in this repo, which is the pre-conversion status quo this PR is fixing.

Fixed in review (self-found) — stale CLAUDE.md citations in code comments

Not raised by any reviewer, found while checking the conversion's blast radius: two comments in internal/ado/collect_surfaces.go cited CLAUDE.md as the place a convention is documented. That now points at an 11-byte stub. Retargeted at AGENTS.md in b791d7d — comment-only, go build ./... clean.


Cross-repo note. This PR is one of 12 in the ENG-5577 batch. The @-import and GEMINI.md findings recurred across several of them and are answered by the same measurements above, run once against a converted repo rather than re-asserted per PR.

CI is being re-verified after the latest push. Merge remains a human decision.

@Tanq16
Tanq16 merged commit bcd93ec into main Aug 5, 2026
14 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants