LoC Dashboard
Paths.
analysis-results/…andprogress-tracker/…in this skill are the default workspace layout. They resolve throughlocations.yamlin$TRAUST_CONFIG_HOME(docs/setup.md, Storage locations); substitute your configured roots.
Estimate and visualise the lines-of-code coverage of the security-audit campaign by querying the GitHub Languages API for every repository that already has a completed audit report, then rendering an interactive single-file HTML dashboard.
Input
$ARGUMENTS is optional and may be any combination of:
| Token | Meaning |
|---|---|
| (none) | Incremental run — fetch only repos missing from the cache, rebuild dashboard. |
refresh |
Ignore the cache; refetch every repo from the GitHub API. |
no-fetch |
Skip the API entirely; rebuild the dashboard from the existing cache. |
open |
Open the resulting dashboard in the default browser when done. |
summary |
After building, print a plain-text summary table to the conversation. |
<path> |
Override the analysis-results root (directory containing findings/ and oss-findings/). |
Procedure
Step 1 — Locate the analysis-results root
The harness needs the directory that contains findings/_manifest/*.csv and oss-findings/.
Resolution order:
- A path passed in
$ARGUMENTS. $AUDIT_RESULTS_ROOTenvironment variable.../analysis-resultsrelative to thetraustcheckout (the normal sibling layout).- Walk upward from
$PWDlooking for a directory that containsfindings/_manifest/audit-manifest.csv.
If none resolve, ask the user for the path.
Step 2 — Run the harness script
The skill ships build_loc_dashboard.py alongside this file. Invoke it with Bash:
python3 "$SKILL_DIR/scripts/build_loc_dashboard.py" \
--results-root <resolved-root> \
[--refresh | --no-fetch] [--open] [--workers 20]
What the script does:
- Reads the four manifest CSVs in
findings/_manifest/:audit-manifest.csv,services-manifest.csv,gap-manifest.csv,oss-manifest.csv. - Filters to rows whose
canonical_pathreport (.jsonor.md) already exists on disk — so the dashboard reflects completed audits and grows as the campaign progresses. - Deduplicates by GitHub URL. Counts internal-GitLab repos separately (they are reported as "not estimated").
- For each unique GitHub repo not already in
gh-languages-cache.jsonl, callsgh api repos/{owner}/{repo}/languages(parallel, default 20 workers). Linguist already excludesvendor/,node_modules/, generated code, and binary blobs. - Converts per-language byte counts → estimated LoC using the
BPLtable in the script. - Writes:
findings/_manifest/gh-languages-cache.jsonl— one JSON object per repo (sorted, diffable).progress-tracker/metrics/dashboards/loc-dashboard.html— self-contained dashboard (no CDN deps).
Step 3 — Report back
Always tell the user:
- The output paths (relative to the results root).
- Headline numbers: code-LoC, total-LoC, calibrated first-party LoC (÷
FIRST_PARTY_DIVISOR), repo counts, GitLab-excluded count. - How many repos were freshly fetched vs cache-hit.
If summary was requested, also print the per-bucket and top-10-language tables to the conversation.
Step 4 — Interpretation note (include once per session)
The GitHub Languages API counts all non-vendored source including tests, fixtures, and tooling. Audit reports that hand-state LoC typically cite first-party non-test code only. Spot-checking against reports that record both gives a median ratio of ~`FIRST_PARTY_DIVISOR`× — the dashboard surfaces both figures.
Outputs
| File | Purpose |
|---|---|
findings/_manifest/gh-languages-cache.jsonl |
Raw per-repo Linguist byte counts. Incremental cache; commit it so re-runs only fetch new repos. |
progress-tracker/metrics/dashboards/loc-dashboard.html |
Interactive dashboard: summary cards, bucket bars, language breakdown, sortable/filterable repo table with language-mix strips. |
The dashboard embeds the standard population block (python3 -m traust.cli corpus) as a final panel for cross-dashboard reconciliation.
Operational notes
- Auth: uses the
ghCLI; the user must begh auth login-ed. No tokens are read or written by the skill. - Rate limit: a full
--refreshof ~2 k repos consumes ~2 k of the 5 k/hr core limit. Incremental runs cost only<new repos>calls. - Idempotent: safe to wire into
finalize_batch.shor a/loop— incremental mode is the default. - Tuning: edit the constants at the top of
build_loc_dashboard.py:BPL— bytes-per-line ratios per languageDOC_LANGS— languages excluded from the "code-only" totalFIRST_PARTY_DIVISOR— calibration factor for the first-party headline
- GitLab repos (any GitLab host in the inventory) are counted but not sized — the GitHub API can't reach them. If a GitLab MCP/token is available the script can be extended to call
GET /projects/:id/languages.
Failure modes
| Symptom | Likely cause | Fix |
|---|---|---|
gh: command not found |
GitHub CLI not installed | brew install gh / dnf install gh |
Many error: HTTP 404 rows |
Repo renamed/deleted/private | Expected; they appear in the dashboard with status err and contribute 0 LoC. |
rate limit exceeded |
>5 k fetches in an hour | Re-run later; cache means only the failed tail re-fetches. |
| Dashboard shows 0 repos | Wrong --results-root |
Pass the directory that contains findings/_manifest/. |
Trends
Each run appends total_loc / repos_with_loc / languages (source
loc-dashboard) to the central metrics ledger (python3 -m traust.cli metrics history,
no-op when unchanged) and injects a trend-vs-previous banner into the
dashboard HTML.