How StackHealth scores a repo
Formula version v1.0. Every weight and threshold is defined below. The same formula lives in code at the formula spec and runs locally in our worker.
Overall score
Each scan produces a 0–100 composite score and an A+ to F letter grade. The composite weights four dimensions:
| Security | 30% |
| Quality | 25% |
| Hygiene | 25% |
| Community | 20% |
overall = round( 0.30 * security + 0.25 * quality + 0.25 * hygiene + 0.20 * community )
Grade boundaries: A+ ≥95 · A ≥90 · A- ≥85 · B+ ≥80 · B ≥75 · B- ≥70 · C+ ≥65 · C ≥60 · C- ≥55 · D ≥50 · F <50
1. Security (30%)
| OpenSSF Scorecard | 40% of security · 12% overall |
| Semgrep p/security-audit | 40% of security · 12% overall |
| Trivy dependency CVEs | 20% of security · 6% overall |
Semgrep findings are LoC-normalised so a 100-finding repo at 1M LoC isn't penalised the same as a 100-finding repo at 1k LoC. Dependency CVEs are absolute: one critical vulnerability is one critical vulnerability regardless of project size.
2. Quality (25%)
| Cyclomatic complexity (lizard) | 30% · avg ≤5 → 100 |
| Lint density (ruff / eslint / golangci) | 25% · issues per kLoC |
| Code duplication (jscpd) | 20% · 0% dup → 100 |
| Test signal (no execution) | 15% · directories + CI runners |
| File size | 10% · penalises files >1000 LoC |
3. Hygiene (25%)
A binary checklist. Total possible: 100 points.
| README.md exists, >300 chars | 15 |
| LICENSE present | 15 |
| LICENSE is OSI-approved | 5 |
| CONTRIBUTING.md | 8 |
| CODE_OF_CONDUCT.md | 5 |
| SECURITY.md | 7 |
| .github/workflows/ or .gitlab-ci.yml | 10 |
| A workflow triggers on pull_request | 5 |
| tests/ directory | 10 |
| .gitignore | 3 |
| Repo description on GitHub | 5 |
| Repo topics on GitHub | 5 |
| Pushed within last 365 days | 7 |
4. Community (20%)
| Activity (recent commits) | 35% of community · 7% overall |
| Contributors (last 365d, log2 scale) | 25% · 5% overall |
| Popularity (stars, log10 scale) | 20% · 4% overall |
| Responsiveness (median time-to-first-response) | 20% · 4% overall |
Popularity is capped at 4% of overall so a repo cannot earn an A just by being popular.
Reproducibility
Every scan stores:
- The formula version (e.g.
v1.0) - Tool versions (
semgrep@…,trivy@…, etc.) - The exact commit SHA that was scanned
- Raw JSON outputs from every analyzer
If you can fetch the raw outputs and the published formula doesn't reproduce the score, that's a bug we'll fix.
What we do NOT score
We deliberately exclude code aesthetics (no AI prose review), documentation depth (only README presence), runtime performance, stars growth rate, author identity, and language choice. These are either gameable, subjective, or unfair across projects.
How the formula evolves
Patch (v1.0 → v1.0.1) — bug fixes only, same inputs → same score. Minor (v1.0 → v1.1) — threshold tweaks; old scans aren't re-scored. Major (v1 → v2) — structural changes, always preceded by a 30-day public RFC.
All changes are pull requests against the public formula repo.