Keep CODEOWNERS aligned with reality.
$ checkowners --offline drift
severity: HIGH (Δmax=1.00)
CODEOWNERS Drift
┏━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Category ┃ Path ┃ Δ ┃ Reason ┃
┡━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ changed │ /payments/ │ 1.00 │ owners diverge on 2 of 2 covered path(s) │
│ │ │ │ (line 1) │
└──────────┴────────────┴──────┴───────────────────────────────────────────────┘
CheckOwners analyzes git and review history to infer who actually knows each part of your repository, then compares that evidence with your CODEOWNERS policy. Use it to detect stale or incorrect rules, find code with dangerously concentrated knowledge, recommend knowledgeable reviewers, and discover ownership gaps before they become operational risk.
Local-first. Git-native. No source-code upload. No LLM required. The core install is pure git. With no token, CheckOwners sends nothing anywhere. checkowners --offline opens no network connection. PyPI releases use Trusted Publishing. Sigstore signs each distribution.
This is a knowledge-risk tool, not a performance-measurement tool. Using it for individual evaluation is unsupported and harmful.
uvx checkowners driftRequires Python 3.11 through 3.14 and Git 2.23 or newer.
pip install checkowners # core CLI (pure git, zero API deps)
pip install "checkowners[graph]" # + networkx-backed graph / topology / onboard
pip install "checkowners[github]" # + GitHub API handle/team/review resolution
pip install "checkowners[all]" # everythingcheckowners --offline driftExit status is the same for every command: 0 clean, 1 internal error, 2 configuration or usage, 3 findings, 4 git or GitHub failure. checkowners --exit-zero hides findings only. See Exit codes.
Least privilege (job summary only; no pull-request comment):
name: checkowners
on: [pull_request]
jobs:
drift:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: smusali/checkowners@v0
with:
config: .github/checkowners.yml
comment_on_pr: falseSame-repo comments need pull-requests: write. The comment workflow and the pre-commit hooks are in GitHub Actions and Pre-commit.
CheckOwners treats code ownership as a confidence-scored spectrum rather than a static binary declaration.
No other open-source tool combines git-history inference, per-path ownership scores with evidence quality, pattern-aware drift with severity tiers, and knowledge-risk reporting behind a single CI-native JSON contract.
checkowners --offline drift on a sanitized two-person tree. The committed rule is /payments/ @platform.
The composite Action posts that finding as one comment on same-repo pull requests:
generate --force on the same tree writes examples/sample-CODEOWNERS:
# Generated by checkOwners. Do not edit manually.
/payments/ @alice @bob
analyze reads git log and git blame and caches an ownership map per repo under ~/.checkowners/.
- Noreply addresses become GitHub
@handleslocally. Other addresses use the GitHub API when a token is set. - One person with several emails counts once.
generatewrites CODEOWNERS and collapses a directory of identical owners into onedir/rule.driftmatches the committed file with GitHub's CODEOWNERS rules: directories, globs, last match wins.
Other reports use that same map. The table below lists them. In CI, the composite Action writes GITHUB_OUTPUT, a job summary, and one pull-request comment on same-repo pull requests.
Scoring commands accept --json, --as-of, and SOURCE_DATE_EPOCH. The default instant is the HEAD committer time. graph uses --export dot instead of --json.
The full pipeline is in docs/USAGE.md.
| Command | What it does |
|---|---|
checkowners analyze |
Infer ownership scores, qualified owner count, and continuity-risk warnings |
checkowners generate |
Write CODEOWNERS, ordered by ownership score; optional inline annotations |
checkowners explain-path <path> |
Show which CODEOWNERS rule owns a path, and the full match chain |
checkowners explain <path> |
Decompose inferred scores (signals, evidence, --why-not, --owner) |
checkowners owners <path> |
Minimal ranked owner list (who is an alias) |
checkowners print |
Print inferred ownership to stdout |
checkowners validate |
Validate existing CODEOWNERS syntax |
checkowners drift |
Compare inferred vs current; severity + max ownership-score delta |
checkowners baseline create |
Write an accepted-findings file so later runs fail only on new findings |
checkowners sync |
Open a pull request proposing CODEOWNERS changes (--commit commits locally) |
checkowners expertise <path> |
Evidence ranking for one path from cached analysis |
checkowners risk |
Rank paths by composite knowledge risk. bus-factor and decay are aliases |
checkowners graph [--export dot] |
Render the ownership graph |
checkowners qualified-owners [<path>] [--all] |
Per-path qualified owner count (capped by top_n_owners) with candidate backup reviewers |
checkowners simulate --remove @alice |
What breaks if those people leave, from the cached map |
checkowners topology |
Exploratory repository topology from commit co-occurrence |
checkowners suggest |
Recommend a reviewer from expertise and current load. balance is an alias |
checkowners onboard <path> |
Learning path from broadly shared paths to concentrated qualified ownership |
checkowners trends [--periods N] [--period-days D] |
Historical activity and qualified owner count over time |
checkowners github-action |
Run the full CI flow and write GITHUB_OUTPUT; used by the composite Action |
Removing 2 of 34 contributors:
Files losing their ONLY owner ......... 412 (3.4% of repo)
Files dropping to bus factor 1 ........ 1,203
Directories fully orphaned ............ 7
services/billing/ 182 files no remaining owner
infra/terraform/vpc/ 41 files no remaining owner
Repo truck factor ..................... 4 -> 2
Suggested transfers (candidate backup reviewers, by residual confidence):
services/billing/ -> @carol (0.61) @dave (0.44)
infra/terraform/vpc -> @erin (0.38) no strong second
Trimmed JSON from this repository is in examples/sample-output.md. Reference configs live under examples/.
Core inference is local git. checkowners --offline opens no network connection. With no token, CheckOwners sends nothing anywhere.
PyPI releases use Trusted Publishing. The publish workflow authenticates with a GitHub OIDC token and does not store a PyPI API token. Sigstore signs each distribution.
Each GitHub release also carries a CycloneDX SBOM and a signed build-provenance attestation.
This repository moved here from a previous GitHub organization; Sigstore attestations for 0.5.0 and earlier record that earlier publisher. 0.5.1 and later are published from smusali/checkowners.
Inference is deterministic git analysis; the scoring heuristics live in analyze.py and are auditable. The codebase has been built with agent assistance. Every AI-assisted change is human-reviewed, tested, and signed off.
What the tool reads, what leaves the machine, what the cache holds, how tokens are handled, and which permissions are required is in docs/PRIVACY.md.
- docs/USAGE.md: full configuration reference, ownership scoring formula, drift severity tiers, GitHub Actions integration, comparison table.
- docs/METHODOLOGY.md: formulas, prior art, terminology, and project principles.
- docs/GLOSSARY.md: one-sentence definitions of the terms used in reports.
- docs/limitations.md: when the tool can be wrong, and what repository evidence cannot prove.
- docs/FAQ.md: identity (usernames vs emails, teams + subteams), GitHub API access, file locations, tuning, troubleshooting.
- docs/PRIVACY.md: what is read, what leaves the machine, the cache,
cache purge, and the threat model. - docs/CONTRIBUTING.md: dev setup, commands, conventional commits, code conventions, PR workflow.
- Good first issues · Discussions
MIT

