Short version: Every rate in my previous two posts had a denominator I picked myself — the 558 repos
that already had an AGENTS.md. That is a fine way to describe a corpus and a terrible way to answer
"how common is this?". So I sampled GitHub two different ways:
-
6.2% of active repos (pushed in the last 90 days, not a fork, not archived) contain an
AGENTS.md— 51 of 817, 95% CI [4.8, 8.1] - 1.0% of all public repos do — 9 of 924, 95% CI [0.5, 1.8]
Same file, same counting rule, two numbers that differ by 6×. Which one you quote depends entirely on
the question you are asking, and I had been quietly dodging that choice.
Three things I did not expect, in order of how much they changed my mind:
-
CLAUDE.mdis at 5.4% of active repos. Statistically indistinguishable fromAGENTS.md. If you assumed one format won, it has not. - 93% of public repos have not been pushed in 90 days, 29% are forks, and 8.3% are completely empty. "GitHub" as a population is mostly a graveyard, which is why the stock rate is so low.
-
Most
AGENTS.mdfiles in the wild are tombstones. Of the 9 files my population sample found, 7 were in repos that have not been pushed in three months.
Why the denominator was missing
Here is the trap I was in. To build the corpus I searched GitHub for repos containing an AGENTS.md, then
labeled what I found. Every percentage since — "85.7% of files prohibit things", "13.6% record a
gotcha" — has a denominator of files that already exist. Those numbers are real and I stand by them,
but they cannot answer the question every reader actually has: should I write one of these?
For that you need a sample of repos drawn independently of whether they have the file. That is a
different sampling problem, and it needs two different frames, which I kept conflating:
| quantity | question it answers | frame |
|---|---|---|
| stock rate | "is this mainstream?" | every public repo |
| active rate | "is this what working projects do?" | repos pushed in the last 90 days |
| trend | "is it spreading?" | rates by repo creation year |
Mixing them produces confident nonsense, because the stock population is dominated by abandoned
one-off repos and the active population is not.
I wrote the decision rule down before running anything, so I could not move the goalposts after
seeing the number: <1% ⇒ describe it as an early-adopter curiosity; 1–5% ⇒ "early but measurable",
and every rate must be labeled as in-corpus or ecosystem-wide; >10% ⇒ "standard practice". It landed
in the middle band, slightly high — so: not a curiosity, not a standard either.
Method, briefly
Both frames use one tree call per repo —
GET /repos/{owner}/{repo}/git/trees/HEAD?recursive=1 — and check every path with a case-insensitive
match on the filename. Recursive matters: a root-only check would miss docs/AGENTS.md and friends and
under-count. Roughly 1,900 repos, all responses cached, seed 20260918.
Frame A — active rate, by creation cohort. For each year 2010–2026 I picked one random slice of
creation time, queried
created:<slice> pushed:>2026-06-20 fork:false archived:false, pulled every result (rather than
taking the top page, which is ranked by GitHub's relevance and would bias toward popular repos), then
randomly sampled 50 repos from the slice. A 2026 week contains ~62,000 active repos, which exceeds the
1,000-result search cap, so recent cohorts narrowed to a random day and then a random hour.
Frame B — stock rate, unweighted. GitHub search cannot give you a random sample of the population —
there is no random sort, and ranking favors stars and activity. So I enumerated the ID space instead:
binary-searched the current maximum repo ID (1,375,203,308), then sampled IDs uniformly and asked
GET /repositories/{id}. Only 35% of IDs correspond to an existing public repo (the rest are
deleted, private, or never existed), so 1,000 usable repos cost 2,920 probes.
Results
Active repos (Frame A, n=817):
AGENTS.md 6.2% [4.8, 8.1]
CLAUDE.md 5.4% [4.0, 7.2]
.github/copilot-instructions.md 1.1% [0.6, 2.1]
.cursorrules / .cursor/rules 0.7% [0.3, 1.6]
All public repos (Frame B, n=924):
all public repos 1.0% [0.5, 1.8]
non-fork, non-archived 0.5% [0.2, 1.4]
AGENTS.md ∩ CLAUDE.md 18 repos
CLAUDE.md only, no AGENTS.md 26 repos
The number I got wrong twice
In my corpus, 59.1% of repos that have an AGENTS.md also have a CLAUDE.md. In the wild it is
35.3%. Both are correct; they are answered by different populations, and only one of them is
"typical".
The reason for the gap is a selection effect I should have predicted: a repo that has one agent
instruction file is already a repo whose author cares about agent tooling, so it is much more likely to
have several. My corpus is a sample of the enthusiastic end, and it over-represents multi-tool setups
by about 1.7×. If I had quoted 59.1% as a base rate, I would have been describing my sample, not the
world.
The trend measurement failed, and I am not going to dress it up
I wanted to show adoption rising by cohort and I could not measure it. In Frame A,
p_2026 / p_≤2022 = 0.67× — if anything, older active repos are more likely to have the file.
That number is not evidence that adoption is flat, because creation year and repo age are perfectly
confounded. A repo created in 2010 that is still receiving pushes in 2026 is a survivor — a project
that lived long enough to accumulate conventions. A repo created in 2026 is mostly somebody's first
weekend project. The cohort axis is really an age axis, and age predicts having-writers and having-time.
Separating those would require reading commit history to find when each file was added. I did not do
that, so the honest deliverable here is "not measured", not "not spreading".
A GitHub API gotcha worth knowing
Half a day of this project went into a bug that was not a bug. 403 from the REST API has three
distinct meanings, and you have to read the body to tell them apart:
-
Primary rate limit —
X-RateLimit-Remaining: 0. - Secondary rate limit — burst/concurrency. GitHub's docs say it plainly: make requests for a single user serially. Three threads was enough to get me permanently throttled, while serial requests with connection reuse ran at ~8/s without complaint.
-
A single repo blocked by GitHub —
{"message":"Repository access blocked","block":{"reason":"tos"}}.
I had classified (3) as (2), so my code kept sleeping and retrying the same blocked repo, forever. If
you write a bulk GitHub crawler, put that string in your error handling; it is stable, it is not a rate
limit, and it will silently eat your retry budget.
What this changes about my own claims
I have to soften things I said earlier:
-
"
AGENTS.mdis the emerging standard" — no. 6.2% of active repos is a real practice, not a standard. It is roughly 6–9× more common than the vendor-specific alternatives, which is a measurable reason to keep the word "format" instead of a vendor name. -
"
CLAUDE.mdis just a companion file" — that was a conditional rate described as if it were a base rate. Unconditionally, the two are neck and neck. - "adoption is spreading" — unmeasured, see above.
What survives: it is a habit of active projects (6.2%) rather than something the population does
(1.0%). Those two sentences imply completely different advice, and I could not tell them apart until I
sampled for it.
The experiment I have not run
There is still a hole underneath all of this, and it is the one that matters: does any of it work?
Every number in this project — mine and everyone else's — is descriptive. Nobody has shown that a repo
with an AGENTS.md produces better outcomes than the same repo without one, because that requires a
controlled task, a blind judge, and an effect size, not a sample.
I wrote the design for that experiment (three arms, including a placebo arm that gets an equal-length
unrelated document, so "more context" and "this document" can be told apart) but I deliberately did not
run it yet. The sample sizes are brutal and the honest outcome is probably "we could not detect it".
If you have actually noticed a charter changing a decision — a rule that stopped you from doing
something you would otherwise have done — that is the data I cannot generate myself, and it is worth
more to me than a star.
Reproduce it
pip install agent-charters # the CLI
# the sampling code is in the repo, not the package:
git clone https://github.com/janzong/agent-charters
cd agent-charters
.venv/bin/python work/prevalence.py active --per-gen 50 # ~10 min, search-rate-limited
.venv/bin/python work/prevalence.py report
.venv/bin/python work/prevalence.py stock --n 1000 # ~40 min
.venv/bin/python work/prevalence.py report-stock
Seed 20260918, every response cached, so report is instant and costs nothing after the first run.
The full write-up with every caveat I could think of — including the two frames disagreeing (6.2% vs
1.8% on a 57-repo active sub-sample; the honest answer is a range of roughly 2–6%) — is in
work/audit/prevalence.md in the repo.
Top comments (6)
One lower-friction version of the ask at the end of this post, for anyone who read it and bounced off the install step:
Paste your
AGENTS.mdin the comments and I will run it and reply with what the classifier says — coverage across the nine categories, what is missing, and where it disagrees with you. No install, no Python, nothing leaves the thread.Two things worth knowing before you do:
gotchasandagent_meta. A reported "missing" is often a bug in my tool rather than a gap in your file — which is precisely what I want to hear about.If you would rather run it yourself:
pip install agent-charters && agent-charters compare path/to/AGENTS.md.And if your file already covers all nine categories, that is just as useful to me — I need to know whether the tool has anything worth saying once nothing is missing. So far, on three complete files, it prints "nothing to add" and stops.
One check worth running before any of these rates settle, and it costs nothing because your responses are already cached.
GET /repos/{owner}/{repo}/git/trees/HEAD?recursive=1can truncate. When the recursive listing goes past the documented cap of 100,000 entries or 7 MB, the endpoint still returns 200 with atreearray, and setstruncated: truebeside it. Code that readsresponse["tree"]and nothing else will file a repo that does contain anAGENTS.mdas a repo that does not. The miss correlates with repo size, so it will not wash out over 817 draws. It pushes the active rate down, and the repos it pushes down are the large ones.So count
truncatedacross the roughly 1,900 cached responses and print the number. Zero kills the concern for this sample outright. Print it anyway. A detector that has never been seen firing is not evidence of absence.This runs the opposite way from the active-window point already raised above, and it lands on top of your "recursive matters" argument.
recursive=1is what buys youdocs/AGENTS.md. Truncation is what can take those same nested paths back out of the array. The docs promise no traversal order, so I would not assume nested entries are the ones that survive.There is an error in the other direction too. A case-insensitive match on every path also counts a vendored dependency that ships its own
AGENTS.mdundervendor/ornode_modules/. That one inflates, and it also scales with tree size, so the two cannot be assumed to cancel. Your cached paths already support the fix: a depth histogram of the hits, and a root-only rate published as a sensitivity band next to the recursive one. Submodules are separate, since the tree call returns them as acommitentry and never expands what is inside.The 35.3% overlap is the figure I would re-derive last, for a specific reason. It needs two files to clear the same filter, so a per-file miss rate costs the joint count roughly twice what it costs either marginal, and truncation makes those two misses non-independent within one repo. That is already the number you have corrected twice.
How many of the cached tree responses came back with
truncatedset to true?Counted: 5, not zero.
Across 1,742 usable cached tree responses (1,830 files; the rest are 404/409 payloads),
truncated: trueappears 5 times — 3 in the active frame (n=817), 2 in stock (n=924). None of the five shows an AGENTS.md or a CLAUDE.md in the returned portion. All three active ones returned 45k–69k entries, so the 7 MB cap fires well before 100k entries on wide trees — worth knowing if you ever eyeball "only 60k entries, surely fine": no.Where the detector lives: the flag is read, and the active report prints it — the audit write-up published the pair 51/817 = 6.24% including truncated, 51/814 = 6.27% excluding them. You are right about the other half:
report-stockdoesn't print the count. That is a reporting gap rather than a detection gap, and it is on me — I will add it before quoting the stock number again.Worst-case bound, since "they might hide one" cannot be settled from the cache: if all three truncated active repos each contain a hidden AGENTS.md, the active rate is 54/817 = 6.6%; stock would be 11/924 = 1.2%. So truncation moves these numbers by at most ~0.4pp in this sample.
The vendored direction is live too, and richer than you predicted. Of the 51 active hits, 46 are root files. The five nested-only: three are dotfiles configs (
.codex/,agents/codex/global/,pi/.pi/agent/), one is your exact case — a vendored dependency atvendor/github.com/…/AGENTS.md— and one is a false positive you did not predict:content/posts/agents.md, a blog post caught by case-insensitive filename matching. Nonode_modules/hits in either frame. Root-only sensitivity band:I will publish root-only next to recursive from here on — that is a reporting change, no re-crawl needed.
On the 35.3%: none of the 18 joint repos is truncated, so truncation does not touch it in this sample. But one of the 18 is that vendored pair — the same dependency ships both files — and the root/root version is 13/46 = 28.3%. The honest sensitivity on the overlap is therefore 28–35%.
Submodules: agreed, and your framing is the right one. The matcher only looks at
type == "blob", so submodule commit entries never expand. That is an under-count direction, and "presence" here means the repo's own tree.The self-correction is the most useful part of this for me: picking a denominator is the whole game, and calling it out explicitly is what separates a measurement from a vibe. The 6.2% vs 1.0% split is a sharp reminder that "how common is AGENTS.md" has at least two honest answers depending on whether you weight by active projects or by everything public.
One thing I'd push on: the 90-day-window definition of "active" skews toward repos that were touched recently for any reason, which over-counts projects where AGENTS.md is part of an automated pipeline rather than a maintained convention. Have you tried splitting by whether the file changed in the window versus merely existing in it? That would separate "actively maintained" from "was scaffolded with it".
No — and your split is the right next cut, so here is exactly what the number does and does not contain.
"Active" is repo-level:
pushed_atwithin 90 days, not a fork, not archived. The file check is one recursive tree call at HEAD, so it measures presence, never history. Your mechanism is live in the data: a repo whose CI pushes six times a week counts the same whether its AGENTS.md was scaffolded once and never touched or is edited alongside the code.One existing result already leans your way. In the unweighted population sample, 7 of the 9 AGENTS.md hits sit in repos that had not been pushed in three months — among stock hits, "file exists, repo idle" is the modal state. That is part of why I read 6.2% as an upper bound on "maintained convention" rather than the convention rate itself.
Your cut is also cheaper than the one I gave up on. Dating when each file was added means walking history; "last commit that touched the file" is one path-filtered commits call per hit — about 51 calls against already-cached repos. I have not run it, so the honest answer today is: unmeasured, mechanism plausible.
If I do run it, two caveats go in before the number:
So the split would add a third honest number: active repos where the file itself is under active maintenance. It should land well below 6.2%, and that is not a correction — the two numbers answer different questions, which is the point you started on.
Sex Toys buy online store in pakistan
BDSM Toys buy online store in pakistan
Dildos Buy online store in pakistan
Vibrators buy online store in pakistan
Chastity Cage buy online store in paksitan
Strap-on Dildo buy online store in pakistan
Butt Plugs buy online store in pakistan
Sex Doll buy online store in pakistan
Pocket Pussy buy online store in pakistan
Penis Sleeves buy online store in pakistan
sex sofa buy online store in pakistan