Skip to main content
ClaudeWave
Skill510 repo starsupdated today

Fork Health

Fork Health synthesizes push recency, enabled skill count, and 30-day PR activity into four health tiers (ACTIVE, WARM, STALE, QUIET) for each fork in a fleet, computing a fleet-wide health ratio and surfacing the top 10 ACTIVE forks as a ranked leaderboard. Use this skill weekly to answer "how many forks in the fleet are genuinely running" with a single stat and identify the most productive instances worth highlighting to external audiences.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/aaronjmars/aeon /tmp/fork-health && cp -r /tmp/fork-health/skills/fork-health ~/.claude/skills/fork-health
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

> **${var}** — Optional. Pass `dry-run` to skip notify (state and article still write). Pass `owner/repo` to override the parent repo. Combine with a space (`dry-run owner/repo`) for both.

Today is ${today}. With the parent repo past 132 forks and the fleet-intelligence stack now answering "who's alive" (`fork-cohort`), "what's missing" (`skill-gap`), "what's running" (`skill-adoption`), and "who contributes back" (`contributor-spotlight`), one synthesis question is still unanswered at the per-fork level: **how healthy is each fork**, as a single tier? `fork-cohort` buckets on workflow-run recency alone; this skill blends three independent signals (push recency, configured skill count, PR throughput) into one rank so an operator scanning the fleet can answer "X of 132 forks are ACTIVE" in one number — and surface the top-10 ACTIVE forks as a leaderboard worth pointing strangers at.

## Why this exists

A fork that pushed today but has zero enabled skills is a placeholder. A fork that enabled 30 skills but hasn't pushed in 60 days is a museum piece. A fork that's pushing AND enabling AND merging its own PRs is a real, running instance. `fork-cohort` collapses all three into "did Actions run lately?"; `skill-adoption` aggregates across the cohort, not per-fork; `contributor-spotlight` picks one named operator a week. None of them answer "give me the per-fork ranked list."

That ranked list is the missing public number. "9 of 132 forks are ACTIVE" is the kind of single-line stat that lands in a tweet, a Show HN comment, or a deck slide. This skill exists to compute it — once a week, gated to notify only when the ratio moves materially.

This is a **measurement skill**. It never opens PRs, never comments on forks, never edits any fork's files. The output is one article + one optional notification + one state file. Same pattern as `fork-cohort` / `skill-gap` / `skill-adoption`.

## Scope and inputs

Reads from two places, with graceful degradation:

1. **`memory/topics/fork-cohort-state.json`** (primary) — gives the full fork list with last-run timestamps. When present and fresh (≤8 days), the cohort cache is the input source — saves one round-trip to `gh api repos/{parent}/forks --paginate`.
2. **`gh api repos/{parent}/forks --paginate`** (fallback / first run) — when cohort state is absent or stale, fetch the live forks list.
3. **Per fork: `gh api repos/{fork}`** — pulls `pushed_at`, `default_branch`, `stargazers_count`, `owner.login`, `owner.type`.
4. **Per fork: `gh api repos/{fork}/contents/aeon.yml?ref={default_branch}`** — base64-decoded, parsed for `enabled: true` slugs (same inline-object grep + Python YAML fallback as `skill-adoption`).
5. **Per fork: `gh api repos/{fork}/pulls?state=closed&base={default_branch}&per_page=100`** — count PRs merged into the fork's own default branch in the last 30 days. (Not PRs back upstream — that's `contributor-spotlight`'s territory. This measures the fork's *internal* PR throughput as a sign of active development against its own main.)

Writes:
- `memory/topics/fork-health-state.json` — per-fork tier, score components, rolling 8-week history
- `articles/fork-health-${today}.md` — leaderboard article (every non-error run, including QUIET)
- `memory/logs/${today}.md` — one log block per run
- Notification via `./notify` — only when the ACTIVE ratio drops ≥10 percentage points week-over-week, the top-10 changes, or it's the first baseline run (see step 8)

## Steps

### 0. Bootstrap

```bash
mkdir -p memory/topics articles
[ -f memory/topics/fork-health-state.json ] || cat > memory/topics/fork-health-state.json <<'EOF'
{"parent":null,"last_run":null,"last_status":null,"audited_count":null,"readable_count":null,"buckets":null,"history":[],"forks":{}}
EOF
```

If `jq empty` fails on the state file (corrupt JSON from an aborted write), back it up to `.bak`, reset to the empty template above, and tag the run `STATE_CORRUPT`. Continue — a fresh state file means no prior week to diff, which is the correct post-corruption behaviour (WoW deltas are simply omitted).

`forks` is a map keyed by `owner/repo`: `{tier, pushed_days, enabled_count, prs_30d, score, last_seen}`. `history` is a rolling list (cap 8 entries) of `{date, audited, readable, buckets:{ACTIVE,WARM,STALE,QUIET}, top10:[fork]}` used for WoW comparison.

### 1. Parse var

- Split `${var}` on whitespace. Tokens: `dry-run`, anything matching `^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$` (treated as `PARENT_OVERRIDE`), anything else.
- If any unknown token is present → log `FORK_HEALTH_SCORE_BAD_VAR: ${var}` and exit (no notify).
- `MODE=dry-run` if the `dry-run` token is present, else `execute`.

### 2. Resolve parent repo

```bash
if [ -n "$PARENT_OVERRIDE" ]; then
  PARENT_REPO="$PARENT_OVERRIDE"
else
  PARENT_REPO=$(gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)" --jq '.parent.full_name // .full_name')
fi
```

If `state.parent` is set and differs from the resolved `PARENT_REPO` → log `FORK_HEALTH_SCORE_PARENT_CHANGED`, reset `forks` and `history` to empty, update `state.parent`. (A different parent means a different fleet; old scores are meaningless.)

### 3. Build the fork audit list

Try the cached path first (identical freshness logic to `skill-gap` and `skill-adoption` so the three skills agree on the fork universe):

```bash
COHORT_STATE=memory/topics/fork-cohort-state.json
COHORT_FRESH=false
if [ -f "$COHORT_STATE" ]; then
  COHORT_DATE=$(jq -r '.last_run // empty' "$COHORT_STATE")
  if [ -n "$COHORT_DATE" ]; then
    AGE_DAYS=$(( ($(date -u +%s) - $(date -u -d "$COHORT_DATE" +%s)) / 86400 ))
    [ "$AGE_DAYS" -le 8 ] && COHORT_FRESH=true
  fi
fi
```

- `COHORT_FRESH=true`: read the full fork list from `state.forks` keys. Set `fork_source=cohort`.
- `COHORT_FRESH=false`: fall back to `gh api "repos/${PARENT_REPO}/forks" --paginate --jq '.[].full_name'`. Set `fork_source=live`. Retry-once-then-skip on 403/5xx.

**Bot owner allowlist** (same as `skill-adoption`): `dependabot[b