How much of this codebase did coding agents write, and how sure can we be?
Repowise labels every commit agent, human_attested or unknown from
evidence the repository already holds, and reports every AI share beside its
coverage: the fraction of commits whose source is known. Below 30% coverage
the share is withheld.
| Label | Meaning |
|---|---|
agent | A marker or a captured edit ties the commit to an agent |
human_attested | An explicit human record: a git-ai note naming only human authors, or an Agent Trace record whose contributors are all human |
unknown | No marker either way. Unknown is never counted as human |
This is provenance, not a quality judgment. Repowise does not compare defect rates or any other outcome by label.
What is read by default
With no configuration, indexing reads public markers from local git history. No network calls, no model, no prompts.
| Marker | Example |
|---|---|
| Bot author or committer | copilot-swe-agent[bot], devin-ai-integration[bot], cursoragent@cursor.com |
| Message footer | Generated with Claude Code, the aider (aider) author suffix |
| Trailers | Co-authored-by: naming an agent service identity, Assisted-by:, Generated-by: |
| git-ai notes | Authorship notes on refs/notes/ai (git-ai standard), read in full, including human attestations |
| Agent Trace | .agent-trace/traces.jsonl records committed to the repository |
Every pattern is anchored to a service identity (a bot login, a service email,
an exact footer phrase), never a bare name, so a contributor who shares a name
with an agent stays unlabelled. Add your own bot or footer under
agent_provenance: in .repowise/config.yaml:
agent_provenance:
service_emails:
mybot@example.com: {agent: mybot, tier: 1}
footer_patterns:
- {pattern: "Generated by MyBot", agent: mybot}Coverage and the share
Coverage is the fraction of commits with a known source (agent plus
human_attested). The AI share is the agent fraction of those covered
commits, always printed with its coverage, for example "at least 40% of
covered commits, coverage 62%". Below 30% coverage only the count is shown.
Shares are reported per file, component and repository.
Edit-time capture (opt-in)
Capture records which lines an agent wrote as it writes them, so a commit carries a label even when nothing in its message says so. It is off until both the repository config and your user config turn it on.
| Agent | Capture |
|---|---|
| Claude Code | PostToolUse hook, installed with the agent hooks |
| Codex CLI | PostToolUse hook, installed with the agent hooks |
| Cursor | afterFileEdit hook: repowise agents add --target=cursor --scope=user |
The hook stores line hashes, the repo-relative path, an opaque session id, the
tool and the model in .repowise/attribution/events.jsonl. It never stores
prompts, code or identities. repowise update (which the post-commit hook
runs) matches those hashes to the lines new commits added and labels a matched
commit with the agent. An unmatched commit stays unknown.
| Level | Writes | Leaves the machine |
|---|---|---|
off (default) | Nothing | Nothing |
local | The gitignored sidecar under .repowise/attribution/ and labels in the local index | Nothing |
notes | Everything local writes, plus a git-ai v3 note per matched commit on refs/notes/ai | Only when you run git push origin refs/notes/ai. Repowise never pushes |
A note holds file paths, line ranges, the tool name, the model and a hashed session id. It never holds prompts, code, conversation ids or a person's identity. A commit that already has a note keeps it.
repowise attribution backfill --agent claude-code,codex binds past commits
from the transcripts these agents keep on this machine, under the same
consent. It keeps line hashes only and binds an edit to a commit made within
14 days after it.
Turning it on and off
# .repowise/config.yaml and ~/.repowise/config.yaml
attribution:
enabled: true # read public markers
capture: off # off, local or notes
agents: [] # agent target ids, e.g. claude-code, codex, cursorThe stricter value wins per key across the repository and user files:
enabled: falsein either file means nothing about AI is stored or shown on any surface: API, CLI, MCP, web or wiki.capturetakes the lower of the two levels; an unset key counts asoff.agentsis the set both files list.DO_NOT_TRACK=1keeps capture off, andinit --yesnever turns it on.
| Command | What it does |
|---|---|
repowise attribution status | Effective, repository and user values, capture per agent, and commits by label with coverage |
repowise attribution backfill --agent claude-code,codex | Binds past commits from local transcripts; prints coverage before and after |
repowise attribution purge | Deletes index labels, file rollups, the sidecar and the notes repowise wrote locally. Git history, trailers and other tools' notes stay |
repowise doctor | At capture: notes, checks the notes refspecs and notes.rewriteRef |
Where it shows
| Surface | What you see |
|---|---|
| Web | Commits page: agent badges, an authorship filter (agent, human, unknown) and a 12-month trend with coverage |
| REST | GET /api/repos/{id}/commits?authorship=agent, GET /api/repos/{id}/commits/agent-trend (counts per label, coverage_pct, agent_pct_of_covered) |
| MCP | get_context file cards carry one agent_share summary with its coverage; include=["provenance"] for the detail |
| Change Records | A three-state provenance item per change, usable in change policy |
Limits
- Squash merges that drop trailers lose those markers. PR-level signals (bot PR author, branch names, PR body) need forge APIs and are outside the local reader.
- Capture labels a commit only when a run of at least two consecutive substantive lines from one agent matches. One-line edits, lines repeated three or more times in a file, and lines two agents both claim stay unmatched.
- Pending capture events expire after 14 days, and each
updatebinds at most 50 new commits, resuming where it stopped. - Capture sees edits made on this machine. Teammates' work reaches you through their own capture or through notes they push.