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.
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-digestSKILL.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 divergentMention/keyword sweep on social platforms for [REPLACE: KEYWORDS] — trends, sentiment, top posts
5 concrete real-life actions, leverage-scored against open loops with specificity and anti-fluff gates
Curated AI-agent tweets, clustered into narratives with insight summaries
Tracker of AI agent substitution signals — which roles, companies, and industries show real headcount displacement. Named roles + real deployments only.
Competitive-intelligence digest on the AI agent framework space — momentum, releases, breaking changes across a curated watchlist
Cross-domain market pulse from AIXBT's free grounding endpoint — crypto, macro, tradfi, geopolitics. Refreshes taxonomy references (clusters, chains) as a bonus.
Pre-batch API provider health check — detects credit exhaustion or auth failure for every configured provider key before the scheduled batch runs, giving the operator a window to act before skills degrade
List a wallet's live ERC-20 token approvals on Base and flag unlimited / risky spender grants. Keyless via Base RPC (eth_getLogs + eth_call) — no explorer key needed.