Skip to main content
ClaudeWave
Skill510 estrellas del repoactualizado today

Fork Firstrun

Fork Firstrun detects when a forked repository completes its first workflow run and immediately sends a named alert to operators, closing the six-day gap that the weekly fork-cohort snapshot creates. Use this when you need same-day visibility into new fork activations to enable timely outreach while the fork owner is still in setup flow, rather than waiting until the next Sunday cohort run flags the transition.

Instalar en Claude Code
Copiar
git clone --depth 1 https://github.com/aaronjmars/aeon /tmp/fork-firstrun && cp -r /tmp/fork-firstrun/skills/fork-firstrun ~/.claude/skills/fork-firstrun
Después abre una sesión nueva de Claude Code; el skill carga automáticamente.

SKILL.md

> **${var}** — Optional. `dry-run` skips notify (state still updates). `owner/repo` overrides the parent repo. Empty = normal run.

Today is ${today}. A new fork completing its very first workflow run is the highest-signal community event Aeon emits — someone deployed, configured secrets, and actually ran the agent. `fork-cohort` already names this transition (`NEW_ACTIVE`) but only fires on Sundays. Mid-week activations sit in the void for up to 6 days until the next cohort run. This skill catches them the day they happen.

## Why this exists

`fork-cohort` (Sunday 19:00 UTC) bucketises every fork as COLD / STALE / ACTIVE / POWER and flags weekly transitions — but a fork that activates Monday morning waits 6 days before anyone notices. Two costs:

- **Operator** loses the chance to reach out while the new fork owner is still in setup-flow with the agent open in another tab.
- **New operator** doesn't feel seen — Aeon's "you matter to us" loop runs on a weekly cadence when activation itself is a same-day event.

This skill closes the gap with a daily cron that diffs the cohort's ACTIVE set against a persistent seen-list and emits per-fork named alerts the day each fork first runs.

## Two-sided value

| Side | What they get |
|------|---------------|
| Operator (@aaronjmars + community ops) | Same-day named ping — "Fork `speend/aeon` just ran its first skill" — with link, run count if detectable, fork stargazers |
| New fork owner | The community sees them on day one rather than waiting through a six-day silent cohort cycle |

## Inputs

Reads in this order:

| Source | Purpose | Required? |
|--------|---------|-----------|
| `memory/topics/fork-cohort-state.json` | Cached ACTIVE/POWER list — preferred fast-path, no API hits per fork | Optional |
| `memory/topics/fork-first-run-state.json` | Persistent seen-list of forks already alerted | Auto-created on first run |
| `gh api repos/{parent}/forks?per_page=100 --paginate` | Live fallback when cohort state is missing or >8 days stale | Fallback only |
| `gh api repos/{fork}/actions/runs?per_page=1` | Per new-active fork — fetch most-recent-run metadata for the alert | Per new fork |

Writes:
- `memory/topics/fork-first-run-state.json` — updated seen-list every run.
- `memory/logs/${today}.md` — one log block per run, even on `QUIET`.
- Notification via `./notify` — only when a gate fires.

No new secrets. Uses `gh api` exclusively (auth via `GITHUB_TOKEN`).

## State schema

`memory/topics/fork-first-run-state.json`:

```json
{
  "parent_repo": "aaronjmars/aeon",
  "last_run": "2026-05-17",
  "last_status": "FORK_FIRST_RUN_ALERT_OK",
  "seen": {
    "speend/aeon": {
      "first_seen_active_at": "2026-05-15",
      "first_seen_active_run_at": "2026-05-14T18:32:00Z",
      "announced_at": "2026-05-15",
      "stargazers": 0
    }
  }
}
```

Key invariants:
- `seen[fork]` is set the first run the fork shows up as ACTIVE/POWER. Once present, it is never re-announced.
- `first_seen_active_run_at` is the fork's most-recent-workflow-run timestamp at announce time — informational, not used for gating.
- LRU cap: 500 entries. When the cap is hit, drop the oldest by `announced_at` to keep the file bounded for long-running deployments.

## Steps

### 1. Parse var

- If `${var}` matches `^dry-run` → `MODE=dry-run`. Strip the prefix; remainder is treated as the parent override.
- Otherwise `MODE=execute`.
- If the remainder matches `^[a-z0-9][a-z0-9-]*/[a-zA-Z0-9._-]+$` (case-insensitive owner/repo) → `PARENT_OVERRIDE` is set.
- Otherwise the remainder must be empty; non-empty unparseable → log `FORK_FIRST_RUN_ALERT_BAD_VAR: ${var}` and exit (no notify).

### 2. Resolve parent repo

```bash
mkdir -p memory/topics
[ -f memory/topics/fork-first-run-state.json ] || echo '{"parent_repo":null,"last_run":null,"last_status":null,"seen":{}}' > memory/topics/fork-first-run-state.json

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 `PARENT_REPO` changes vs the value already stored in state, treat the seen-list as scoped to the prior parent and **reset it** for the new parent (a manual var override with a different upstream is a new tracking universe). Write a `_Reset: parent changed from {old} to {new}_` marker in the log block.

### 3. Source the active-fork list

Prefer the cached cohort state when fresh:

```bash
COHORT_STATE="memory/topics/fork-cohort-state.json"
COHORT_AGE_DAYS=99
if [ -f "${COHORT_STATE}" ]; then
  COHORT_LAST_RUN=$(jq -r '.last_run // empty' "${COHORT_STATE}")
  if [ -n "${COHORT_LAST_RUN}" ]; then
    COHORT_AGE_DAYS=$(( ( $(date -u +%s) - $(date -u -d "${COHORT_LAST_RUN}" +%s) ) / 86400 ))
  fi
fi
```

| Condition | Source for ACTIVE list |
|-----------|------------------------|
| Cohort state exists AND `COHORT_AGE_DAYS <= 8` AND its `parent_repo` matches | Read `forks` object, filter `bucket ∈ {ACTIVE, POWER}` |
| Otherwise | Live fallback (step 3a) |

Eight days is one cohort cycle plus a one-day grace — within that window the cohort's ACTIVE list is still the ground truth and is cheaper than per-fork API calls.

#### 3a. Live fallback

```bash
gh api "repos/${PARENT_REPO}/forks" --paginate \
  --jq '[.[] | select(.archived != true and .disabled != true) | {full_name, owner: .owner.login, stargazers_count, created_at, pushed_at}]' \
  > /tmp/forks.json
```

Per fork (cap at 80 — same budget guard as fork-cohort):

```bash
LAST_RUN=$(gh api "repos/${FORK_FULL_NAME}/actions/runs?per_page=1" \
  --jq '.workflow_runs[0].updated_at // empty' 2>/dev/null)
```

A fork is ACTIVE if `LAST_RUN` is non-empty AND `(now - LAST_RUN) < 7 days`. 403 → retry once after 60s; persistent → skip the fork (log `unreadable`). 404 (Actions disabled) → treat as not-active. 5xx → retry once after 10s; persistent → skip.

If the live fallback itself fails to list forks after retry → exit `FORK_FIR