Skip to main content
ClaudeWave
Skill510 repo starsupdated today

fork-skill-digest

fork-skill-digest compares how configured forks diverge from upstream defaults across enabled status, variables, model assignments, and schedules for each skill. Run this weekly to identify peer-learning signals, where fleet-wide customization patterns suggest upstream defaults need adjustment, ranked by divergence frequency and per-fork customization fingerprints highlighting the heaviest customizers.

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

SKILL.md

> **${var}** — Target repo to scan forks of (e.g. "owner/aeon"). If empty, reads `memory/watched-repos.md` and uses the first entry.

Today is ${today}. Generate the weekly **divergence digest** — where the configured fork fleet systematically disagrees with upstream defaults on `enabled`, `var`, `model`, or `schedule`, and a per-fork customization fingerprint for the heaviest customizers.

## Why this exists

`skill-leaderboard` ranks **what's popular** (top 15 by enabled count). `fork-fleet` surfaces **per-fork unique work** (commits, new skills). Neither answers: **where do operators consistently disagree with upstream defaults?** That's the peer-learning signal — if 6 out of 8 configured forks enable a skill upstream defaults off, upstream is shipping the wrong default. If 5 out of 8 disable a skill upstream defaults on, that skill is noise. This skill surfaces those signals weekly so the operator can flip defaults that the fleet has already voted on.

## Steps

### 1. Determine the target repo

If `${var}` is set, use that as `TARGET_REPO`. Otherwise read `memory/watched-repos.md` and use the first non-comment, non-empty line. If neither yields a value, log `FORK_SKILL_DIGEST_NO_TARGET` to `memory/logs/${today}.md` and stop (no notification).

### 2. Snapshot upstream defaults

Read this running instance's local `aeon.yml` once. Build:

- `UPSTREAM_DEFAULTS`: dict `{skill_name -> {enabled: bool, model: str|null, var: str, schedule: str|null}}` for every skill entry under `skills:`.
- `UPSTREAM_SKILLS`: set of skill directory names from `skills/` (use `ls skills/`).
- `UPSTREAM_TAGS`: dict `{skill_name -> [tags]}` parsed from each `skills/<name>/SKILL.md` frontmatter (best-effort; missing frontmatter → `[]`).

These are the comparison baselines — never mutated.

### 3. Fetch active forks

```bash
CUTOFF=$(date -u -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-30d +%Y-%m-%dT%H:%M:%SZ)
gh api "repos/${TARGET_REPO}/forks?per_page=100" --paginate \
  --jq "[.[] | select(.pushed_at > \"$CUTOFF\") | select(.archived == false) | select(.disabled == false) | {owner: .owner.login, full_name: .full_name, pushed_at, stargazers_count, default_branch}]"
```

If zero active forks: log `FORK_SKILL_DIGEST_NO_FORKS` and stop (no notification).

### 4. Per-fork enumeration (one tree call + one yml fetch each)

For each active fork, run **one** recursive git-tree call to enumerate files (cheaper than per-path contents):

```bash
gh api "repos/${FORK_FULL}/git/trees/HEAD?recursive=1" --jq '[.tree[] | select(.type == "blob") | .path]'
```

Then fetch the fork's `aeon.yml` only if the tree contains it:

```bash
gh api "repos/${FORK_FULL}/contents/aeon.yml?ref=${FORK_DEFAULT_BRANCH}" --jq '.content' | base64 -d
```

Error handling:
- 404 / 409 (empty repo): mark `status: "no_tree"`, skip aeon.yml extraction, continue.
- 403 with `X-RateLimit-Remaining: 0`: sleep 60s, retry once. If still failing, mark `status: "rate_limited"` and continue.
- Tree contains aeon.yml but contents call 404s: mark `status: "yml_unreadable"`, continue.
- aeon.yml present but YAML parse fails: mark `status: "yml_invalid"`, continue.

For each readable `aeon.yml`, extract per-skill `{enabled, model, var, schedule}`. Treat missing keys as inheriting the upstream default (do NOT count those as overrides).

Detect **fork-only skills**: directory names matching `skills/<name>/SKILL.md` in the fork's tree where `<name>` is NOT in `UPSTREAM_SKILLS`. Record `{fork_full_name, skill_name, path}` for each.

### 5. Tier each fork

For each fork compute a divergence signal vector vs `UPSTREAM_DEFAULTS`:
- `enabled_diff`: count of skills where the fork's `enabled` differs from upstream
- `var_overrides`: count of skills with non-empty `var:` where upstream's was empty (or different non-empty value)
- `model_overrides`: count of skills with `model:` differing from upstream
- `schedule_overrides`: count of skills with `schedule:` differing from upstream
- `fork_only_skill_count`: count from step 4

Tier the fork:
- **CONFIGURED**: any signal ≥1 (the fork actively diverged)
- **TEMPLATE**: aeon.yml readable but every signal is 0 — exclude from divergence math
- **UNREADABLE**: no_tree / no aeon.yml / yml_unreadable / yml_invalid / rate_limited — tracked in source-status footer

### 6. Aggregate divergence (the core analysis)

Let `N_CONFIGURED` = count of forks tiered CONFIGURED. If `N_CONFIGURED < 2`: log `FORK_SKILL_DIGEST_TEMPLATE_FLEET` with active/template/unreadable counts, write a stub article noting the conversion rate, and **skip notification**. Stop.

For each skill name in `UPSTREAM_SKILLS`, compute four divergence dimensions:

**Enable divergence:**
- `forks_enabled_count`: number of CONFIGURED forks with `enabled: true` for this skill
- `forks_disabled_count`: number of CONFIGURED forks with `enabled: false` for this skill (explicitly set, not inherited)
- `upstream_enabled`: bool from UPSTREAM_DEFAULTS
- `divergence_pct`:
  - If upstream `enabled: false`: `forks_enabled_count / N_CONFIGURED` (how many forks disagree by enabling)
  - If upstream `enabled: true`: `forks_disabled_count / N_CONFIGURED` (how many forks disagree by disabling)
- `direction`: `"ENABLE_UPWARD"` (upstream off, forks turn on) or `"DISABLE_DOWNWARD"` (upstream on, forks turn off)

**Var divergence:**
- `var_override_count`: number of CONFIGURED forks where `var:` differs from upstream
- `top_var_value`: most common non-empty fork value (with count) — only if ≥2 forks share it

**Model divergence:**
- `model_override_count`: number of forks with non-null model differing from upstream
- `top_model_value`: most common fork model (with count) — only if ≥2 forks share it (signals fleet consensus on a cheaper/different model)

**Schedule divergence:**
- `schedule_override_count`: number of forks with schedule differing from upstream
- `top_schedule_value`: most common fork schedule (with count) — only if ≥2 forks share it

### 7. Categorize divergent