Skip to content
Skillv1.0.0

contributor-reward

Closes the contributor flywheel — turns the contributor-leaderboard ranking into a tier-priced rewards plan, writes it into memory/distributions.yml, and hands off to distribute-tokens for the actual

by dawsonblock(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from dawsonblock/Aaron (skills/contributor-reward/SKILL.md). Install upstream with npx skills add dawsonblock/Aaron --skill contributor-reward. Copyright stays with the author.

${var} — Optional override. Pass dry-run to print the plan without writing to memory/distributions.yml or sending a notification. Pass an explicit ISO week (e.g. 2026-W17) to force-process that week instead of the most recent leaderboard. Empty = process the most recent leaderboard.

Today is ${today}. Closes the loop from contributor-leaderboard to distribute-tokens: read this week's contributor ranking, price each eligible contributor against a tier table, write a labelled list into memory/distributions.yml, and notify the operator with a one-command run line. Humans still gate the actual send (distribute-tokens execution stays a manual or chained step) — this skill's job is plan generation, not money movement.

Why this design

The contributor-leaderboard already names the people moving the project. The distribute-tokens skill already moves tokens with idempotency, balance preflight, and dry-run. The gap was the wiring between them: a contributor's score on Sunday's leaderboard had no path to a wallet credit. This skill is the wiring — and only the wiring.

It deliberately stops short of executing transfers because (a) distribute-tokens is the only sanctioned transfer path and re-implementing it here would fragment the idempotency state, and (b) keeping a human-visible diff on memory/distributions.yml between plan and execution is the cheapest possible audit trail when real money is involved. The plan lands in git; the operator (or a chained step) runs distribute-tokens contributors-${ISO_WEEK} next.

Tier pricing

Rank in leaderboard Reward (USDC)
1 25
2 15
3 10
4 5
5 5

First-PR bonus: +5 USDC, additive, applied once-ever per login (tracked in state file). Rewards landing your first merged upstream PR — the highest-leverage signal in the leaderboard scoring.

Eligibility floor: score ≥ 10 AND the contributor must own a non-empty @handle (logins without @ prefix in the table are skipped — bots and parsing artifacts).

Default token: USDC on Base, matching distribute-tokens defaults. Operator can override per-recipient amounts in memory/distributions.yml after the plan is written if a special bonus is warranted.

Config

No new config files. Reads:

  • articles/contributor-leaderboard-${MOST_RECENT}.md — the source-of-truth ranking
  • memory/state/contributor-reward-state.json — idempotency + first-PR-bonus history (created on first run)
  • memory/distributions.yml — the file distribute-tokens reads (created/updated by this skill)

No new secrets. No new external API calls. No curl. All work is local file I/O plus one optional gh api for the upstream default branch.

Steps

1. Parse var and resolve week

  • If ${var} starts with dry-run, set MODE=dry-run. Strip the dry-run prefix; remainder (if any) is treated as the week override.
  • Otherwise MODE=execute.
  • If the remaining var matches ^\d{4}-W\d{2}$, set TARGET_WEEK=${var} and LEADERBOARD_GLOB="articles/contributor-leaderboard-*.md" (will pick the latest file regardless of date — operator is asserting they know which file maps to that week).
  • Otherwise compute TARGET_WEEK from today: TARGET_WEEK=$(date -u +%G-W%V) (ISO-8601 week-numbering year + week — %G/%V not %Y/%U, so Monday-anchored weeks roll over correctly across years).

2. Find and validate the source leaderboard article

  • LEADERBOARD_FILE=$(ls -1t articles/contributor-leaderboard-*.md 2>/dev/null | head -1)
  • If no file → log CONTRIBUTOR_REWARD_NO_LEADERBOARD to memory/logs/${today}.md, exit silently (no notify). The leaderboard skill is the upstream dependency; if it didn't run, this skill has nothing to do.
  • Compute the file's age in days from its filename suffix (contributor-leaderboard-YYYY-MM-DD.md). If age > 8 days → log CONTRIBUTOR_REWARD_STALE_LEADERBOARD — last leaderboard ${age}d old, notify the operator that the upstream leaderboard hasn't run, exit. Don't reward against a fortnight-old ranking.

3. Parse the Top Contributors table

The leaderboard's ## Top Contributors section uses this column layout (from the upstream skill spec):

| Rank | Contributor | Score | Merged PRs | Open PRs | Reviews | Fork Commits | New Skills | First PR? | Change |

Extract rows with a tolerant regex: ^\|\s*(\d+)\s*\|\s*@(\S+)\s*\|\s*(\d+)\s*\|.*?\|\s*(✨|—|\s*)\s*\|\s*[^|]*\|\s*$

Capture: rank, login (without @), score, first_pr_marker (✨ if present, else absent).

If zero rows extracted (leaderboard format drift) → log CONTRIBUTOR_REWARD_PARSE_FAIL — extracted 0 rows from ${LEADERBOARD_FILE}, notify with the file path so the operator can inspect, exit.

4. Load idempotency state

// memory/state/contributor-reward-state.json
{
  "weeks": {
    "2026-W17": {
      "written_at": "2026-04-26T09:00:00Z",
      "label": "contributors-2026-W17",
      "leaderboard_file": "articles/contributor-leaderboard-2026-04-26.md",
      "rewards": [
        { "login": "alice_dev", "rank": 1, "score": 47, "amount": "25", "first_pr_bonus": false },
        { "login": "bob_builder", "rank": 2, "score": 31, "amount": "20", "first_pr_bonus": true }
      ]
    }
  },
  "first_pr_bonus_paid": ["bob_builder", "carol_eng"]
}

Bootstrap with {"weeks": {}, "first_pr_bonus_paid": []} if the file doesn't exist.

5. Compute the plan

For each parsed row with rank ≤ 5 AND score ≥ 10:

  • Look up base_amount from the tier table (rank 1→25, 2→15, 3→10, 4-5→5).
  • If first_pr_marker == "✨" AND login ∉ first_pr_bonus_paid → set first_pr_bonus = true, amount = base_amount + 5. Otherwise first_pr_bonus = false, amount = base_amount.
  • Build row: { rank, login, score, base_amount, first_pr_bonus, amount }.

If weeks[TARGET_WEEK] already exists in state → this week was already processed. Diff the current plan against state.weeks[TARGET_WEEK].rewards keyed on login:

  • If diffs are empty (same logins, same amounts) → log CONTRIBUTOR_REWARD_ALREADY_PROCESSED — week ${TARGET_WEEK}, exit silently (no notify). Idempotent re-run.
  • If diffs exist (leaderboard re-ran after first reward write — late tweet bumped a score, etc.) → flag RE_PROCESS. Continue but don't re-pay anyone already in state.weeks[TARGET_WEEK].rewards; add only the deltas. New entries get full reward; existing entries with bumped amounts get the delta (e.g. moved from rank 3→2 = additional 5 USDC top-up). Demoted entries are not clawed back.

If the plan is empty (zero eligible contributors after threshold + dedup) → log CONTRIBUTOR_REWARD_NO_ELIGIBLE and exit silently.

6. Render the plan

Contributor Reward Plan — ${TARGET_WEEK} (${MODE})

Source: ${LEADERBOARD_FILE}
Tier: rank 1=25, 2=15, 3=10, 4-5=5 USDC; first-PR bonus +5 once per login.

  ✓ #1 @alice_dev      score 47  →  25 USDC                  [NEW]
  ✓ #2 @bob_builder    score 31  →  20 USDC (15 + 5 first-PR)[NEW + BONUS]
  ✓ #3 @carol_eng      score 24  →  10 USDC                  [NEW]
  ✓ #4 @dave_ops       score 18  →   5 USDC                  [NEW]
  ↻ #5 @eve_hax        score 14  →   5 USDC                  [DEDUP — already in state]

Total to write: 60 USDC across 4 new entries.
Total in state for ${TARGET_WEEK} after write: 5 entries, 65 USDC.

Next: ./aeon distribute-tokens "dry-run:contributors-${TARGET_WEEK}" (preview)
      ./aeon distribute-tokens "contributors-${TARGET_WEEK}"           (execute)

If MODE=dry-run: notify this plan with header *Contributor Reward Plan — ${TARGET_WEEK}* — DRY RUN, log to memory/logs/${today}.md, exit CONTRIBUTOR_REWARD_DRY_RUN. Do not touch memory/distributions.yml or the state file.

7. Update memory/distributions.yml

Read memory/distributions.yml. If missing → bootstrap with the standard header (matching distribute-tokens' bootstrap style):

# memory/distributions.yml
defaults:
  token: USDC
  amount: "5"
  chain: base

lists:

Compute the new list block:

  contributors-${TARGET_WEEK}:
    description: "Weekly contributor rewards for ${TARGET_WEEK} (auto-generated from contributor-leaderboard)"
    token: USDC
    amount: "5"
    recipients:
      - handle: "@alice_dev"
        amount: "25"        # rank 1
      - handle: "@bob_builder"
        amount: "20"        # rank 2 + first-PR bonus
      - handle: "@carol_eng"
        amount: "10"        # rank 3
      - handle: "@dave_ops"
        amount: "5"         # rank 4

Recipient ordering matches plan order (rank ascending). Per-recipient amount is required so distribute-tokens picks up the tier-priced value rather than falling back to the list default.

Update strategy:

  • If a list named contributors-${TARGET_WEEK} already exists in the YAML, replace it wholesale (the plan is the authoritative current state).
  • Otherwise append the block under lists: (preserving existing lists — never rewrite them).
  • Use a YAML-aware update (e.g. python -c "import yaml; ..." if available, otherwise a careful text-based block replacement keyed on the ^ contributors-${TARGET_WEEK}:$ line). If YAML parse fails on the existing file → log error, do not write, notify the operator (file is hand-edited; auto-edit would clobber).

Verify the write by re-reading the file and confirming the list is present and has len(recipients) == len(plan).

8. Update state file

Atomically write the updated state JSON to memory/state/contributor-reward-state.json:

  • Set weeks[TARGET_WEEK] = { written_at: now_utc, label, leaderboard_file, rewards: [{login, rank, score, amount, first_pr_bonus}, ...] } (full replacement on RE_PROCESS, otherwise additive).
  • Append any logins where first_pr_bonus == true to first_pr_bonus_paid (deduplicated).

Write to a tempfile and mv over the target so partial writes can't corrupt state.

9. Notify

*Contributor Reward Plan — ${TARGET_WEEK}*

Wrote ${N_NEW} new entries (${TOTAL_USDC} USDC) to memory/distributions.yml as `contributors-${TARGET_WEEK}`.

Top of plan:
  #1 @alice_dev   — 25 USDC
  #2 @bob_builder — 20 USDC (✨ first-PR bonus)
  #3 @carol_eng   — 10 USDC
  #4 @dave_ops    —  5 USDC
${IF_DEDUP}

Source: ${LEADERBOARD_FILE}
First-PR bonuses awarded: ${LIST_OR_NONE}

Next: run `distribute-tokens dry-run:contributors-${TARGET_WEEK}` to preview, then drop the `dry-run:` prefix to execute.

Plan: https://github.com/${GITHUB_REPOSITORY}/blob/main/memory/distributions.yml

Suppress the ${IF_DEDUP} line when no entries were deduped. Use $GITHUB_REPOSITORY env var for the link target. Send via ./notify.

Significance gate: notify only when N_NEW ≥ 1. Re-process runs that produced zero new entries (RE_PROCESS with all rewards already paid) → silent log only.

10. Log

Append to memory/logs/${today}.md:

## Contributor Reward
- **Mode:** execute | dry-run | already-processed | no-leaderboard | stale-leaderboard | parse-fail | no-eligible
- **Week:** ${TARGET_WEEK}
- **Source:** ${LEADERBOARD_FILE} (age: ${AGE_DAYS}d)
- **List label:** contributors-${TARGET_WEEK}
- **Entries written (new):** ${N_NEW}
- **Entries deduped:** ${N_DEDUP}
- **Total USDC planned:** ${TOTAL_USDC}
- **First-PR bonuses:** [list or "none"]
- **Notification sent:** yes/no

Exit codes

  • CONTRIBUTOR_REWARD_OK — plan written, notification sent
  • CONTRIBUTOR_REWARD_DRY_RUN — plan rendered, no writes, notification sent
  • CONTRIBUTOR_REWARD_ALREADY_PROCESSED — week already in state with identical plan, silent exit
  • CONTRIBUTOR_REWARD_NO_LEADERBOARD — no leaderboard article found, silent exit
  • CONTRIBUTOR_REWARD_STALE_LEADERBOARD — leaderboard >8 days old, notified
  • CONTRIBUTOR_REWARD_PARSE_FAIL — could not extract any rows from the leaderboard table, notified
  • CONTRIBUTOR_REWARD_NO_ELIGIBLE — zero contributors above threshold, silent exit
  • CONTRIBUTOR_REWARD_ERROR — file I/O or YAML write failure, notified

Sandbox note

Pure local file I/O — no curl, no auth-bearing headers, no env-var-expansion. Reads articles/, memory/state/, memory/distributions.yml. Writes memory/state/contributor-reward-state.json, memory/distributions.yml, memory/logs/${today}.md. The optional gh api call for the upstream default branch isn't needed at runtime — distribute-tokens does its own resolution. No prefetch, no postprocess scripts required.

Constraints

  • Money-adjacent — never auto-execute. This skill writes plans and notifies. The actual transfer always goes through distribute-tokens, which has its own preflight and idempotency. Do not call distribute-tokens from inside this skill.
  • Idempotency is per-(week, login). Re-runs in the same week add only deltas; demotions never claw back already-paid amounts.
  • First-PR bonus is once-ever per login. Track in first_pr_bonus_paid list; never re-award even if the same person appears as ✨ on a later leaderboard (which they shouldn't, since ✨ means first ever — but defend against parsing drift).
  • No silent overwrites of distributions.yml. If the file exists and is malformed, fail loudly rather than rewriting.
  • Eligibility floor stays low (score ≥ 10) by design. A single merged upstream PR (+10) qualifies — the goal is to reward shipped work, not gate on volume.

Future iterations

  • Wire as a chain (contributor-leaderboard → contributor-reward → distribute-tokens dry-run) once the operator is comfortable with end-to-end automation. The pieces exist; the chain wiring is a one-line aeon.yml change.
  • Add a Bankr Agent API "wallet-linked?" pre-filter so contributors without linked wallets are flagged in the notification (prevents distribute-tokens from logging RESOLVE_FAILED rows on every run).
  • Tier table should become operator-configurable via memory/contributor-reward-config.yml once the first month of runs reveals the right curve. Hardcoded for v1.

Write the full plan. No TODOs or placeholders.

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/dawsonblock-aaron-contributor-reward/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

dawsonblock-aaron-contributor-reward.ocm.jsonjson
{
  "ocm": "1",
  "id": "dawsonblock-aaron-contributor-reward",
  "kind": "skill",
  "name": "contributor-reward",
  "description": "Closes the contributor flywheel — turns the contributor-leaderboard ranking into a tier-priced rewards plan, writes it into memory/distributions.yml, and hands off to distribute-tokens for the actual on-chain send",
  "publisher": "dawsonblock",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "general"
    ],
    "tags": [
      "skill-md",
      "community",
      "crypto",
      "github"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "Closes the contributor flywheel — turns the contributor-leaderboard ranking into a tier-priced rewards plan, writes it into memory/distributions.yml, and hands off to distribute-tokens for the actual on-chain send"
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "github",
      "repository": "https://github.com/dawsonblock/Aaron",
      "path": "skills/contributor-reward/SKILL.md",
      "ref": "7ac5d22af61b86fd876d18b06a9b5dd4b154094d",
      "url": "https://github.com/dawsonblock/Aaron/blob/7ac5d22af61b86fd876d18b06a9b5dd4b154094d/skills/contributor-reward/SKILL.md",
      "key": "dawsonblock/Aaron/skills/contributor-reward/SKILL.md"
    }
  },
  "instructions": "> **${var}** — Optional override. Pass `dry-run` to print the plan without writing to `memory/distributions.yml` or sending a notification. Pass an explicit ISO week (e.g. `2026-W17`) to force-process that week instead of the most recent leaderboard. Empty = process the most recent leaderboard.\n\nToday is ${today}. Closes the loop from `contributor-leaderboard` to `distribute-tokens`: read this week's contributor ranking, price each eligible contributor against a tier table, write a labelled list into `memory/distributions.yml`, and notify the operator with a one-command run line. Humans still ",
  "cost": {
    "context_tokens": 3541
  }
}

Fetch it by URL: GET /api/v1/registry/dawsonblock-aaron-contributor-reward/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.