method · v4 · this page changes only when the method does
Method
Applies to re audit in this source. Sections 1–4 are the rules this source computes (method v4). Section 5 is what v2 changed, section 6 what v3 added, and section 6b the one rule v4 changed. Published reports stay on the method that produced them. Reports #1 and #2 are method v2. Reports #3 and #4, and any audit produced by the 0.3.0 release, are method v3. Report #4 is 10.5281/zenodo.23196011. This source does not recompute them.
Causari publishes counts about repositories that are not ours. The only honest way to do that is to publish, next to every number, exactly how it was obtained, what it cannot see, and how to reproduce or contest it. This page is that.
1What is counted
For a repository at a given HEAD, re audit walks git log --no-merges
and classifies each commit from machine-readable metadata only: the commit
message trailers and the author identity. No model, no heuristic on the diff, no guess from
style. Merge commits are excluded.
| Signal | Class |
|---|---|
git-ai note under refs/notes/ai that names a non-empty tool in sessions or prompts | VERIFIED · that tool |
Trailers Drafted-With, Executed-By, AI-Model, AI-Agent, AI-Tool, AI-Assisted: yes and the AI-* family with a positive value | VERIFIED |
Co-Authored-By naming a known agent identity (Claude, Copilot, aider, Codex, ChatGPT, OpenHands; Devin, Jules, Gemini and Cursor only with a bot identity or vendor domain) | VERIFIED |
Author identity of a known agent (aider, noreply@anthropic.com, devin-ai, openhands, cursoragent, google-labs-jules) | VERIFIED |
Message contains (aider) or Generated with Claude Code | PROBABLE |
| Anything else | UNKNOWN · never counted |
VERIFIED and PROBABLE are never summed. Published measurements use VERIFIED only.
A note that has only schema_version, only a human declaration, or a tool that is empty or whitespace is not a signal. Detection continues with the other rows. Absence of a qualifying signal is UNKNOWN. UNKNOWN is not human.
2How survival is computed
Introduced = lines added by each classified commit (git log --numstat),
text files only, excluding lockfiles, vendored and generated paths. Still at HEAD = for
every tracked file, git blame --line-porcelain HEAD; a line counts for the commit
blame attributes it to, capped at that commit's introduced count. The ratio is the sum over all
classified commits, so it is line-weighted.
The ratio is the persistence of lines that this method associates with its tagged cohort. It does not, by itself, establish code quality, developer productivity, total AI usage, that a model wrote the line, security, correctness, or a causal benefit or harm of AI.
3What it cannot see
- Inline completions. Copilot, Cursor Tab, Windsurf, Continue, Zed AI, JetBrains AI leave no trace in git. Those lines land in the untagged baseline. "No git signal" means no metadata, not no AI, and not a finding that a human wrote them.
- Untagged agent commits. Claude Code's trailer can be disabled; many teams strip trailers on squash. Those commits are UNKNOWN. UNKNOWN is not human.
- Mixed commits. Granularity is the commit. A qualifying signal tags the commit, and the survival count is that commit's introduced and surviving lines. A git-ai range over lines 20–30 of a 100-line commit classifies the commit; it does not mean those 100 lines were proven AI-authored. A human's review edits inside an AI-tagged commit are counted with that commit. AI code committed without a tag is untagged, which is not a finding that a human wrote it.
- Several tools in one note. The audit stores one agent per commit. Sessions are read before prompts. Within one object the lexicographically first key wins. That choice is deterministic and is not a finding that the selected tool wrote the commit. Disagreement is not represented.
- Notes are not the commit.
refs/notes/aiis not part of the commit object. The commit SHA can stay the same while a note is added or removed and the classification changes. A note is metadata the repository can rewrite. - Paraphrase and rewrite. A line rewritten by a human with the same meaning is a death; a line the AI copied from elsewhere is an introduction.
4Known ways the number misleads (method v1)
Bulk commits dominate
Because the ratio is line-weighted, one commit that vendors a tree, snapshots documentation or checks in a generated bundle can hold nearly every introduced line of a repository. The published row then measures that commit's fate, not the project's. Example in the current data: one documentation-versioning commit of 3.2 million lines determines a repository's entire ratio. We publish it with the mark "one agent ≥ 90 %" because hiding it would be worse than showing it, and because the fix belongs in the method, not in the table.
re audit owner/repo --json
# largest_commit_share, capped_survival_rate
Formatting and moves count as death
git blame runs without -w, -M,
-C: a formatter pass, a re-indent, or moving a function to another file
re-attributes those lines.
git blame -w -M -C --line-porcelain HEAD
# what method v2 runs instead
Small samples
Below 5 AI-tagged commits no ratio is published: one commit can dominate. The counts are still printed, with the coverage block saying so.
re audit --json
# "coverage": { "small_sample": true, "sample_floor": 5 }
Age
Survival decays with time since the commit. Early adopters look different from last week's adopters. Method v1 does not normalise for age; compare a repository with itself over time instead.
re audit --save # append a snapshot to .causari/audits/
# method v3: "baseline": { "by_age": [...], "age_matched": {...} }
A cleared or rewritten repository
When a project empties its tree and starts over, or ports everything in one pass, no line older than that day survives, tagged or not. The AI-tagged ratio then measures the rewrite. Example in the current data: one repository was ported in April 2026 and cleared in July; 6,584 of its commits, 1,529 of them AI-tagged, predate the oldest line still at HEAD; its ratio of 30 % over all AI-tagged lines is 53 % over the lines that postdate that day. Methods v1 and v2 cannot tell; v3 names the oldest surviving line and counts what lies behind it.
re audit --json
# "baseline": { "oldest_surviving": { "date", "commits_before", ... } }
5Method v2
Method v2 caps per-commit weight (a commit weighs at most the
95th percentile of per-commit introduced counts in its repository, never more than 10,000 lines)
and reports the capped ratio, the median per-commit ratio and the largest commit's share
alongside the line-weighted ratio; runs blame with -w -M -C and honours
.git-blame-ignore-revs; parses the trailer block with git's own rules; reads
Assisted-by:; refuses shallow clones; and writes a coverage block into
every result. The weekly Survival Report adds bootstrap intervals
over the sampled repositories. Every published number carries the method version that produced
it.
6Method v3: the same repository as the baseline
An AI-tagged ratio on its own answers "how much of this code is still there", not "compared
with what". Method v3 answers the second question with the only baseline that shares the
repository's conventions, reviewers, formatter passes and rewrites: the untagged lines
of the same repository. Every UNKNOWN commit (no machine-readable AI signal: human-written,
inline-completed and untagged-agent code alike) enters a baseline
block. It costs nothing extra; git log --numstat already walks every
commit and git blame already names the owner of every line. PROBABLE
commits are in neither cohort.
baseline.untagged: the same figures asverified(sums, line-weighted, capped, median, largest commit's share) over the untagged commits.baseline.by_age: six windows of line age, 0–30, 30–90, 90–180, 180–365, 365–730 and 730+ days, each holding the AI-tagged and the untagged cohort side by side. Age is the time from a commit's committer date to the committer date of HEAD, so the table depends on the repository alone, not on when the audit ran.baseline.age_matched: the AI-tagged line-weighted ratio against the untagged ratio re-weighted to the age mix of the AI-tagged lines (direct standardisation), over the windows where both cohorts hold at least 5 commits. The block states how many windows were used and what share of the AI-tagged lines they hold. "AI code is newer" cannot by itself produce a gap.baseline.oldest_surviving: the oldest commit that still owns a line at HEAD, with the number of commits, lines and AI-tagged commits older than it. Many commits before it means the repository was cleared or rewritten; nothing from before that date survives, tagged or not.
The gap is a count from one repository's own history, not a verdict on a tool: the untagged
cohort includes AI code that carried no tag, and the granularity is still the commit. It is
tagged_rate minus untagged_rate, printed in
percentage points directly under the survival percentage. Positive means the metadata-matched
lines have the higher line-weighted survival in those windows. The windows are age buckets, not identical timestamps. When no age window holds at least 5 commits of
both kinds, the audit says the gap is unavailable. It is also in
--summary and in the JSON; the Survival
Report shows it per repository from the first report built with v3. Three repositories measured
while writing this section: −6.0 points (the cleared one above), −4.7 and +5.4, the last with one
commit holding 70 % of its AI-tagged lines. Five commits is the comparability floor, not a
reliability guarantee.
re audit owner/repo
# Age-matched gap: -4.7 percentage points (AI-tagged 62.4% minus untagged 67.1% in the matched age windows). Positive means the metadata-matched lines have the higher line-weighted survival in those windows, over 1 matched age window holding …% of the AI-tagged lines. Lines outside those windows are not in this gap.
# Baseline: untagged lines of the same repository
# line age AI-tagged untagged
# 90-180 d 83.2% (33 commits) 96.1% (674 commits)
6bMethod v4: a git-ai note needs a named tool
Method v4 keeps the v3 baseline and every other v3 rule. It changes one classification.
Under v3, any parseable git-ai note under refs/notes/ai was VERIFIED,
and a note with no tool was recorded as agent ai at confidence 1.0.
Under v4 that note is not AI evidence. A non-empty tool in sessions or
prompts still is, and it is recorded as that tool. An empty tool does
not hide a later named tool, and a note with no qualifying tool does not hide an independent
trailer. Named-tool notes stay. The ref is not dropped.
The same frozen repository can therefore change number between v3 and v4. On the corpus of
Survival Report #3 the schema-only fallback was two commits in
cloudflare/workers-sdk. Removing only those changed the aggregate from
61.3921% to 61.3898% (−0.0023 percentage points). The published median gap did not move, and
no repository left the cohort. Report #3 remains the v3 measurement. It is not recomputed.
Report #4 (10.5281/zenodo.23196011) is also
method v3, produced by the 0.3.0 binary, and is not recomputed.
Commit granularity is unchanged. A qualifying signal still tags the commit, not the line range inside the note.
7Reproduce or contest any number
curl -fsSL https://causari.dev/install.sh | sh
re audit owner/repo --json # counts, coverage.method, blame flags, repository.head
re audit owner/repo --summary # the same reading, as Markdown
re audit owner/repo --seal # those JSON bytes, signed for that commit and method
re seal verify audit.seal.json # or drop the file on causari.dev/verify
--json and the seal store the counts, repository.head,
coverage.method, coverage.blame_flags, and the other
coverage flags. They do not store refs/notes/ai. Naming that ref is not the
same as keeping its bytes: it is not part of the commit, so adding or removing it can change the
class while the stored SHA stays. A repository that has moved has a different commit, and this
export does not reproduce the published number.
--badge and --card write SVGs.
re report reads the local ledger; it does not export an audit.
If your reproduction differs, or a row is wrong, open an issue with the JSON. Corrections
are made in public and recorded in the data history. Maintainers who do not want their
repository measured add one line to
.github/survival-optout.txt;
the next weekly run drops the row and it is not re-added.
8How repositories are selected
The Survival Report measures the repositories listed in
.github/survival-repos.txt:
a hand-picked part, added by pull request, and a part filled every week, the day before the report, by
scripts/survival_discover.py
from the GitHub commit search API. The script looks for public, non-merge commits that carry the
same signals section 1 classifies as VERIFIED (the Co-Authored-By identities, the
provenance trailers, the agent author identities) and samples the most recent ones per signal.
For every repository sampled at least twice it reads the star count first. The repository-wide
commit count is requested only for repositories that still could enter the list: at least 100 stars,
not a fork, not archived, not opted out, still on GitHub. Counting the others first is what made the
run of 2026-10-11 hit its two-hour limit and write nothing; 383 of the 400 candidates on 2026-09-27
were under 100 stars.
Repositories with at least 5 such commits (the sample floor) and at least 100 stars are kept;
forks, archived repositories and every line of .github/survival-optout.txt
are dropped; the remaining ones are ordered by stars, then by that count, then by name, and the list
is filled up to 100 with the hand-picked repositories kept; when fewer clear the floor, the list is
shorter, not padded. The sample is a moving window (the most recent commits per signal), so a
repository found one week can miss it the next without anything having changed there: every
repository discovered in an earlier week that the sample missed is counted again repository-wide,
with the signals it was found with, and stays while it passes the same floors. A repository leaves
the list through the floors, an opt-out or its disappearance from GitHub, never through the luck of
the sample; the discovery record names the ones retained that way and the ones that left. Stars first, because a raw commit count
selects contribution-graph painters and mirrors of large projects, which say nothing about how AI code
fares in software people use. That order exists only to choose the sample: the list file and every
report page stay alphabetical. The counts behind each selection are published in
.github/survival-discovery.json
and each report states how its list was selected. The date in the list header is the last run that
finished. A run that stops early does not replace the list; it records last_attempt
in that same file, and the report says the list was not replaced.
Limits: commit search reads the default branch only, returns at most 1,000 results per query and matches text, so a message that merely mentions a trailer is a hit; the audit of the clone, not the search, decides what is counted. A commit carrying two signals counts twice in the selection count. The selection favours popular repositories where AI-attributed commits are visible; it is not a random sample of open source, and the report says so.
GITHUB_TOKEN=… python3 scripts/survival_discover.py --dry-run
# the same list, the same counts, from your own token
9Evidence classes in the ledger
Two paths, not one proof chain:
git history → re audit → survival counts
metadata, commit-level, method version on the JSON
hooks / proxy / record → .causari/ ledger → re why, trace, impact, lens
local, gitignored; declared or correlated, not authorship
A seal signs the audit JSON for one commit and one method version. It does not join the
ledger to the audit, and it does not make either one a proof of who typed the code.
The audit reads git. The ledger (re init, re hook,
re proxy, re watch) records agent actions as they happen, and
every answer it gives names one of two evidence classes.
Declared
Hooks, MCP, re record: the agent stated what it did. Exact prompt and
path. With Claude Code the hook snapshots the tree right before the edit, so a human change
between two agent edits is reported separately instead of absorbed.
re hook claude-code # every prompt and edit, exactly
Correlated
Proxy + watch: the lines you inserted are searched inside completions captured moments before. A score, not a fact. The adversarial harness gives the measured numbers: 100 % on a clean write, 50 % after a formatter pass, wrong per-line attribution when two prompts touch one file in the same window.
re proxy & re watch
# model, tokens, cost; file changes joined by content
10What we do not do
- No rank, no colour, no "healthy" or "high churn" wording, no badge about repositories we do not own.
- No audit of a third-party repository on request through our infrastructure. Run it yourself; the tool is free.
- No estimate. Where git carries no signal, the row says so.
- No public bot that audits your project unasked. Why: a measurement with a verdict attached stops being a measurement. The reasoning is in the manifesto; the plan is in the roadmap, with exit criteria per phase.
Context: "Will It Survive?", arXiv 2601.16809 measures the same question on 201 projects with a different detector and finds agent-authored code is modified less often than human code. Our counts should be read next to it, not instead of it.