Audit every git repository under a root directory and emit one
merged.json conforming to the portfolio-audit data contract
(references/report-spec.md). The default renderer is the portfolio-audit
Next.js app (audits/<date>/merged.json in that repo); a standalone HTML
fallback is specced in the same reference.
Hard rules
- Read-only toward audited repos. Git commands limited to
log,show,diff,rev-parse,ls-files,branch -a. Never build, install, checkout, or write inside any audited repo. All output goes to a scratch directory and the portfolio-audit repo. - Run the scripts first and trust their inventory. Worktree dedup, tier
suggestions, stack detection, secrets flags, model attribution, and token
math live in
scripts/— deterministic and testable — not in agent judgment. Do not re-derive what they emit. - Every bug and improvement cites a real file (
path:linewhere possible). Uncited findings get cut. Defects are reported before praise. - No-churn stack rule. Recommend a migration only for a concrete payoff (cost, blocking limitation, EOL dependency). "Keep as-is" is the expected default and is stated explicitly.
- Scores come only from
references/rubrics.mdanchors. Read it before scoring. Honest gaps beat invented numbers: missing data is labeled "unknown", never guessed.
Workflow
- Scan. From this skill's directory:
Sanity-check before proceeding: worktrees collapsed into primaries, tier suggestions plausible, block coverage matches spot-checked repos.scripts/scan.sh <root> <since-date> > scratch/inventory.json node scripts/attribution.mjs --inventory scratch/inventory.json > scratch/attribution.json node scripts/tokens.mjs --projects-root <root> --inventory scratch/inventory.json > scratch/tokens.json - Tier the repos (inventory suggests; you may adjust with reason):
- Tier 1 deep (≥50 commits in window or revenue-touching): read entry points, key modules, recent diffs; 5-8 timeline milestones; ≥3 feature suggestions; 5-10 improvements; real bug hunt in hot files.
- Tier 2 standard (15-49): manifests + README + main entry; 3-5 milestones; 2-3 features; 3-5 improvements; bugs only if visible.
- Tier 3 light (<15): manifest + git log; ratings + verdict; 1-2 features. Repos with committed secrets always get a security writeup.
- Fleet groups: near-identical scaffolds (same template, ~same file count) become ONE entry — audit the shared scaffold once, then a per-repo delta table.
- Tier 4 legacy (no commits in window, or archived): one compact row — last active, stack, one-liner, one suggestion, verdict archive/revive/harvest. No scores, no bug hunts.
- Audit. Parallelize per-repo agents when orchestration is available (each gets the schema + rubrics and returns one record); otherwise go sequentially by tier. Timeline derivation: cluster commits into bursts (>14-day gap starts a new cluster), label each from dominant commit subjects.
- Synthesize. Merge records + attribution + tokens into
merged.json(contract inreferences/report-spec.md): portfolio totals, model mix, token series (estimated from commit blocks vs measured from transcripts, with per-repo completeness), AI time, security banner, consolidation notes, methodology + coverage notes. - Verify the numbers before delivering: spot-check commit counts against
raw
git log, token totals againststats-cache.json, monthly sums against yearly totals, and one repo's block coverage by hand.
Data sources — keep them honest
- Model attribution comes from
=== COMMIT SUMMARY ===Foundation Model lines, thenCo-Authored-Bytrailers, then bot author emails. Prose mentions of models in commit messages are NOT attribution. Repos without either are "unattributed" — show that bucket. - Estimated tokens/AI time come from commit-block
Estimated Token Cost/Time Takenlines (full history). Coverage varies by repo (0-100%) — always show block coverage next to estimates. - Measured tokens come from agent session transcripts (Claude Code JSONL
layout; directory configurable via
tokens.mjs --claude-dir), deduped by requestId. Transcript retention is limited — state the earliest surviving timestamp. When the agent keeps a stats cache (stats-cache.json), it provides a longer global daily series (no per-repo split).