Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Analyze git repositories to build a security ownership topology (people-to-file), compute bus factor and sensitive-code ownership, and export CSV/JSON for graph databases and visualization. Use when the user explicitly wants a security-oriented ownership or bus-factor analysis grounded in git history (for example: orphaned sensitive code, security maintainers, CODEOWNERS reality checks for risk, sensitive hotspots, or ownership clusters). Do NOT use for general maintainer lists, non-security own
.claude/skills/tech-leads-club-security-ownership-map/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 18% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 1889% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 82% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 129% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 102% | 0% |
Build a bipartite graph of people and files from git history, then compute ownership risk and export graph artifacts for Neo4j/Gephi. Also build a file co-change graph (Jaccard similarity on shared commits) to cluster files by how they move together while ignoring large, noisy commits.
networkx (required; community detection is enabled by default)Install with:
bashpip install networkx
--since/--until).scripts/run_ownership_map.py (co-change graph is on by default; use --cochange-max-files to ignore supernode commits).--graphml).scripts/query_ownership.py for bounded JSON slices.references/neo4j-import.md).By default, the co-change graph ignores common “glue” files (lockfiles, .github/*, editor config) so clusters reflect actual code movement instead of shared infra edits. Override with --cochange-exclude or --no-default-cochange-excludes. Dependabot commits are excluded by default; override with --no-default-author-excludes or add patterns via --author-exclude-regex.
If you want to exclude Linux build glue like Kbuild from co-change clustering, pass:
bashpython skills/skills/security-ownership-map/scripts/run_ownership_map.py \ --repo /path/to/linux \ --out ownership-map-out \ --cochange-exclude "**/Kbuild"
Run from the repo root:
bashpython skills/skills/security-ownership-map/scripts/run_ownership_map.py \ --repo . \ --out ownership-map-out \ --since "12 months ago" \ --emit-commits
Defaults: author identity, author date, and merge commits excluded. Use --identity committer, --date-field committer, or --include-merges if needed.
Example (override co-change excludes):
bashpython skills/skills/security-ownership-map/scripts/run_ownership_map.py \ --repo . \ --out ownership-map-out \ --cochange-exclude "**/Cargo.lock" \ --cochange-exclude "**/.github/**" \ --no-default-cochange-excludes
Communities are computed by default. To disable:
bashpython skills/skills/security-ownership-map/scripts/run_ownership_map.py \ --repo . \ --out ownership-map-out \ --no-communities
By default, the script flags common auth/crypto/secret paths. Override by providing a CSV file:
# pattern,tag,weight
**/auth/**,auth,1.0
**/crypto/**,crypto,1.0
**/*.pem,secrets,1.0Use it with --sensitive-config path/to/sensitive.csv.
ownership-map-out/ contains:
people.csv (nodes: people)files.csv (nodes: files)edges.csv (edges: touches)cochange_edges.csv (file-to-file co-change edges with Jaccard weight; omitted with --no-cochange)summary.json (security ownership findings)commits.jsonl (optional, if --emit-commits)communities.json (computed by default from co-change edges when available; includes maintainers per community; disable with --no-communities)cochange.graph.json (NetworkX node-link JSON with community_id + community_maintainers; falls back to ownership.graph.json if no co-change edges)ownership.graphml / cochange.graphml (optional, if --graphml)people.csv includes timezone detection based on author commit offsets: primary_tz_offset, primary_tz_minutes, and timezone_offsets.
Use scripts/query_ownership.py to return small, JSON-bounded slices without loading the full graph into context.
Examples:
bashpython skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out people --limit 10 python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out files --tag auth --bus-factor-max 1 python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out person --person alice@corp --limit 10 python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out file --file crypto/tls python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out cochange --file crypto/tls --limit 10 python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out summary --section orphaned_sensitive_code python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out community --id 3
Use --community-top-owners 5 (default) to control how many maintainers are stored per community.
Run these to answer common security ownership questions with bounded output:
bash# Orphaned sensitive code (stale + low bus factor) python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out summary --section orphaned_sensitive_code # Hidden owners for sensitive tags python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out summary --section hidden_owners # Sensitive hotspots with low bus factor python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out summary --section bus_factor_hotspots # Auth/crypto files with bus factor <= 1 python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out files --tag auth --bus-factor-max 1 python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out files --tag crypto --bus-factor-max 1 # Who is touching sensitive code the most python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out people --sort sensitive_touches --limit 10 # Co-change neighbors (cluster hints for ownership drift) python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out cochange --file path/to/file --min-jaccard 0.05 --limit 20 # Community maintainers (for a cluster) python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out community --id 3 # Monthly maintainers for the community containing a file python skills/skills/security-ownership-map/scripts/community_maintainers.py \ --data-dir ownership-map-out \ --file network/card.c \ --since 2025-01-01 \ --top 5 # Quarterly buckets instead of monthly python skills/skills/security-ownership-map/scripts/community_maintainers.py \ --data-dir ownership-map-out \ --file network/card.c \ --since 2025-01-01 \ --bucket quarter \ --top 5
Notes:
--touch-mode file to count per-file touches.--window-days 90 or --weight recency --half-life-days 180 to smooth churn.--ignore-author-regex '(bot|dependabot)'.--min-share 0.1 to show stable maintainers only.--bucket quarter for calendar quarter groupings.--identity committer or --date-field committer to switch from author attribution.--include-merges to include merge commits (excluded by default).Use this structure, add fields if needed:
json{ "orphaned_sensitive_code": [ { "path": "crypto/tls/handshake.rs", "last_security_touch": "2023-03-12T18:10:04+00:00", "bus_factor": 1 } ], "hidden_owners": [ { "person": "alice@corp", "controls": "63% of auth code" } ] }
Use references/neo4j-import.md when you need to load the CSVs into Neo4j. It includes constraints, import Cypher, and visualization tips.
bus_factor_hotspots in summary.json lists sensitive files with low bus factor; orphaned_sensitive_code is the stale subset.git log is too large, narrow with --since or --until.summary.json against CODEOWNERS to highlight ownership drift.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-04 | fail→pass | 54,237 | 3,301 | -94% | 1 | 1 | 0% | 2,507 | 2,965 | +18% | 0 | 0 | — |
case-01 | fail→fail | 27,133 | 16,874 | -38% | 1 | 1 | 0% | 5,890 | 2,551 | -57% | 0 | 0 | — |
case-02 | fail→fail | 4,885 | 6,868 | +41% | 1 | 1 | 0% | 228 | 2,751 | +1107% | 0 | 0 | — |
case-03 | fail→pass | 3,375 | 11,197 | +232% | 1 | 1 | 0% | 200 | 3,977 | +1889% | 0 | 0 | — |
case-05 | fail→pass | 8,587 | 2,721 | -68% | 1 | 1 | 0% | 1,552 | 2,825 | +82% | 0 | 0 | — |
case-06 | fail→pass | 17,663 | 2,706 | -85% | 1 | 1 | 0% | 1,254 | 2,871 | +129% | 0 | 0 | — |
case-07 | fail→pass | 19,283 | 3,557 | -82% | 1 | 1 | 0% | 1,361 | 2,750 | +102% | 0 | 0 | — |
case-08 | fail→pass | 9,153 | 1,828 | -80% | 1 | 1 | 0% | 1,554 | 2,614 | +68% | 0 | 0 | — |
case-13 | fail→pass | 11,639 | 55,751 | +379% | 1 | 1 | 0% | 2,189 | 2,674 | +22% | 0 | 0 | — |
case-09 | fail→pass | 13,131 | 3,497 | -73% | 1 | 1 | 0% | 2,135 | 2,968 | +39% | 0 | 0 | — |
case-10 | fail→pass | 54,129 | 3,572 | -93% | 1 | 1 | 0% | 1,397 | 2,576 | +84% | 0 | 0 | — |
case-11 | fail→pass | 9,136 | 3,153 | -65% | 1 | 1 | 0% | 1,551 | 2,576 | +66% | 0 | 0 | — |
case-12 | pass→pass | 7,721 | 1,599 | -79% | 1 | 1 | 0% | 1,377 | 2,624 | +91% | 0 | 0 | — |
case-14 | fail→pass | 7,744 | 2,078 | -73% | 1 | 1 | 0% | 1,462 | 2,674 | +83% | 0 | 0 | — |
case-15 | fail→pass | 12,818 | 2,115 | -83% | 1 | 1 | 0% | 2,421 | 2,689 | +11% | 0 | 0 | — |
case-16 | pass→pass | 10,296 | 2,656 | -74% | 1 | 1 | 0% | 1,826 | 2,690 | +47% | 0 | 0 | — |
case-17 | fail→pass | 15,710 | 1,404 | -91% | 1 | 1 | 0% | 2,892 | 2,520 | -13% | 0 | 0 | — |
case-18 | fail→fail | 11,126 | 3,314 | -70% | 1 | 1 | 0% | 1,841 | 2,824 | +53% | 0 | 0 | — |
case-19 | fail→pass | 20,892 | 2,359 | -89% | 1 | 1 | 0% | 1,873 | 2,705 | +44% | 0 | 0 | — |
case-20 | fail→pass | 16,363 | 2,668 | -84% | 1 | 1 | 0% | 2,839 | 2,776 | -2% | 0 | 0 | — |
case-21 | fail→pass | 13,902 | 3,418 | -75% | 1 | 1 | 0% | 2,413 | 2,943 | +22% | 0 | 0 | — |
case-22 | pass→pass | 18,801 | 20,889 | +11% | 1 | 1 | 0% | 3,983 | 6,434 | +62% | 0 | 0 | — |
case-23 | pass→pass | 5,851 | 6,427 | +10% | 1 | 1 | 0% | 1,058 | 3,443 | +225% | 0 | 0 | — |
case-24 | pass→pass | 14,295 | 22,727 | +59% | 1 | 1 | 0% | 2,801 | 6,628 | +137% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 24 cases were attempted, and 20 counted toward the lift figure. The other 4 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +67 percentage points is the difference between those two pass rates over the 20 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.