feat(forensics): knowledge-concentration / key-person (bus-factor) ownership check - #128
Merged
Merged
Conversation
β¦nership check (#127) Adds the PEOPLE axis to the git forensics. hotspots/coupling/bug-fix measure the code (what/where is risky); `ownership` measures who holds it β contribution concentration, the literal bus factor (authors to reach 50%/80%), sole-authored files, single-owned areas, and orphaned knowledge (sole-owned code whose only author has gone inactive). Pure `git log`, no new tooling; a natural fit for a deterministic localiser (tech due diligence + team prioritisation, ADR-0009). Design (ADR-0010): - Identity via git's own mailmap resolution (%aN/%aE), coalesced by email β we never guess aliases. The summary always carries an identity caveat. This is the headline correctness risk: without a .mailmap an author's two emails split them in two and the concentration reads falsely low. - Commit-touch ownership is primary (robust to one-off codemods); lines-added informs the concentration headline. Generated/vendored files are dropped up front by reusing the source inventory, so a committed bundle can't crown its committer. - Recency drives the orphaned-knowledge signal (absence-is-signal). - warn, never fail; non-git / shallow clone / thin history β honest skip. - Optional CHECKUP_OWNERSHIP_ANON=1 anonymises names for shared reports. Thresholds (keyperson %, sole-author %, orphan months) are tunable via .checkup.yml thresholds + env, defaulting to the literals so an absent block is byte-identical. The renderer picks the record up automatically (contract). - lib/ownership.jq: pure, unit-testable transform (rows β record) - bin/checkup.sh: section 23 β git log + awk normalise, feeds the transform - lib/config.sh + .checkup.yml.example: threshold keys - test/ownership.test.sh: 28 assertions on the transform (maths, thresholds, identity coalescing, orphaned recency, anonymise, honest skip); wired into CI - ADR-0010 + README/architecture docs Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VxHXw3RKcV6SBKary5iwcC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #127.
Adds the people axis to the git forensics.
git-hotspots,change-coupling,bug-fix-densitymeasure the code (what/where is risky). This addsownershipβ who holds it: contribution concentration, the literal bus factor (authors to reach 50%/80%), sole-authored files, single-owned areas, and orphaned knowledge (sole-owned code whose only author has gone inactive). Puregit log, zero new tooling.Design β see ADR-0010
%aN/%aE, coalesced by email β we never invent alias heuristics. The summary always carries an identity caveat. Demonstrated on a fixture repo: without a.mailmapan author's two emails split them in two and a different contributor falsely reads as the key person (bus factor looks like a healthy 2); with.mailmapthey coalesce and the real key-person surfaces at 50% (bus factor 1).warn, neverfail; non-git / shallow clone / thin history β honestskip(ADR-0003).CHECKUP_OWNERSHIP_ANON=1anonymises names for shared reports.ownership_keyperson_pct_warn,ownership_sole_author_pct_warn,ownership_orphan_months) tunable via.checkup.yml thresholds+ env; literals as defaults so an absent block is byte-identical.Contents
lib/ownership.jqβ pure, unit-testable transform (authorship rows β record)bin/checkup.shβ section 23:git log --numstat+ awk normalise β transform βwrite_parsedlib/config.sh+.checkup.yml.exampleβ threshold keystest/ownership.test.shβ 28 assertions (maths, thresholds, identity coalescing, orphaned recency, anonymise, honest skip), wired into CIVerification
checkup.shagainst a synthetic multi-author repo (rename + dual-email author + inactive owner): emits contract-conformantparsed/ownership.json; the renderer surfaces it automatically in Top Problems, the by-file focus map, and its own section β no renderer changes.shellcheck/bash -nclean.Acceptance criteria (#127)
parsed/ownership.jsonemitted, contract-conformant, absent-git βskiptop[]of single-owner areas.mailmaprespected; identity caveat surfaced in the summaryskip.checkup.yml.examplePer ADR-0007 I don't self-merge β leaving this for a human to squash-merge once CI is green.
π€ Generated with Claude Code