Skip to content

Commit d6e7404

Browse files
authored
Merge pull request #6948 from luciferlive112116/docs/ams-contribution-signal-inventory-6794
docs(miner): inventory real-world contribution eligibility signals for the AMS profile
2 parents 38efdac + 8d7fdfe commit d6e7404

1 file changed

Lines changed: 142 additions & 0 deletions

File tree

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# AMS contribution-signal inventory — what real repos actually expose as eligibility signals
2+
3+
Research spike for **#6794**, grounding the `ContributionProfile` schema design (#6795) in an audit of real
4+
repos rather than an assumption about what repos "usually" do. Every number below was read from the live
5+
GitHub REST API (`GET /repos/{owner}/{repo}/labels`, `GET /repos/{owner}/{repo}/contents/{path}`) against the
6+
sample on 2026-07-17. **Research and writeup only — no code, no schema, no `discover` wiring here.**
7+
8+
## Summary
9+
10+
**Three findings should shape the schema, and all three argue for treating extraction as best-effort and
11+
per-signal rather than as a reliable parse:**
12+
13+
1. **There is no universal eligibility label.** `good first issue` / `help wanted` exist in only **6 of 10**
14+
sampled repos. The three largest (rust, deno, kubernetes) use their own taxonomies entirely — rust's is
15+
`E-easy` / `E-medium` / `E-hard` / `E-help-wanted`. A profile that name-matches a fixed list silently
16+
returns "no eligible issues" on exactly the repos with the most contributor activity.
17+
2. **Label _descriptions_ are unreliable, and unreliable unevenly.** Coverage ranges from **3/17** (tailwind)
18+
and **8/76** (react) to **99/100** (rust). So a schema cannot depend on the description carrying the
19+
semantics — but it also must not ignore it, because on some repos the description is the _only_ place the
20+
semantics live (rust's `E-easy` description literally reads "…Good first issue.", which name-matching alone
21+
would miss).
22+
3. **`CONTRIBUTING.md` is frequently a signpost, not the rules.** react's is **208 bytes** and kubernetes' is
23+
**525 bytes** — both mostly a link to an external contributor guide. "Has CONTRIBUTING.md" is therefore a
24+
near-worthless presence check; size and content matter, and the real rules often live off-repo where AMS
25+
cannot read them.
26+
27+
**A fourth finding is worth stating because it cuts against our own intuition:** the _linked-issue requirement_
28+
— the rule loopover's own gate enforces hardest — is **not a general convention**. Grepping the real
29+
`CONTRIBUTING.md` of each repo for linked-issue language: loopover **8** mentions, react **0**, rust **0**,
30+
kubernetes **0**. Building the profile around "does a PR need a closing-keyword issue reference" would encode
31+
loopover's local norm as if it were an ecosystem norm.
32+
33+
## The sample
34+
35+
Ten repos: two of JSONbored's own gate-enabled repos, six well-known OSS projects with deliberately different
36+
contribution norms, one repo with AI-agent docs but no human contribution docs, and one with no contribution
37+
docs at all (the negative case #6794 asks for).
38+
39+
| Repo | Labels | Described | `CONTRIBUTING` | PR template | AI-agent docs |
40+
| -------------------------- | -----: | --------: | ------------------ | ----------- | ------------------------ |
41+
| `JSONbored/loopover` | 27 | 25 | ✅ 19,926 B || `AGENTS.md`, `CLAUDE.md` |
42+
| `JSONbored/metagraphed` | 27 | 21 ||| `AGENTS.md`, `CLAUDE.md` |
43+
| `facebook/react` | 76 | 8 | ⚠️ 208 B (pointer) || `CLAUDE.md` |
44+
| `rust-lang/rust` | 100+ | 99 | ✅ 2,712 B |||
45+
| `sveltejs/svelte` | 36 | 12 | ✅ 10,278 B || `AGENTS.md` |
46+
| `denoland/deno` | 100+ | 67 |`.github/` || `CLAUDE.md` |
47+
| `tailwindlabs/tailwindcss` | 17 | 3 |`.github/` |||
48+
| `kubernetes/kubernetes` | 100+ | 36 | ⚠️ 525 B (pointer) || `AGENTS.md` |
49+
| `JSONbored/sure-aio` | 16 | 13 ||| `AGENTS.md` |
50+
| `sindresorhus/slugify` | 8 | 8 ||||
51+
52+
`CONTRIBUTING` lives at the repo root in 6/10 and under `.github/` in 2/10, so extraction must probe both.
53+
54+
## Signal type 1 — eligibility labels
55+
56+
| Repo | Conventional eligibility label present? |
57+
| -------------------------- | --------------------------------------- |
58+
| loopover, metagraphed | `help wanted` only |
59+
| react, tailwindcss | `good first issue` only |
60+
| svelte, slugify, sure-aio | both |
61+
| **rust, deno, kubernetes** | **neither** |
62+
63+
rust's actual taxonomy, with the semantics in the description rather than the name:
64+
65+
```
66+
E-easy :: Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.
67+
E-help-wanted :: Call for participation: Help is requested to fix this issue.
68+
E-medium :: Call for participation: Medium difficulty. Experience needed to fix: Intermediate.
69+
E-hard :: Call for participation: Hard difficulty. Experience needed to fix: A lot.
70+
```
71+
72+
**Implication for the schema:** eligibility-label rules need to be a _list of matchers over name **and**
73+
description_, not a fixed name list — and the profile must be able to say "this repo exposes no eligibility
74+
label at all", which is the honest answer for 3/10 of the sample and is different from "we failed to look".
75+
76+
loopover's own `gittensor:*` labels are the far end of the spectrum: every one carries an explicit,
77+
machine-readable eligibility statement in its description. That is a **local convention**, not a shape to
78+
generalize from — it exists because loopover authored its own labels for its own gate.
79+
80+
## Signal type 2 — exclusion / maintainer-only labels
81+
82+
Nothing in the sample marks issues maintainer-only via a label whose _name_ says so. The closest generic
83+
signals are status labels (`blocked`, `needs-triage`, `on-hold`) whose exclusion meaning is conventional, not
84+
stated. react's `good first issue (taken)` is the one explicit "no longer available" marker found — and it
85+
encodes availability in the **name suffix**, which no other repo in the sample does.
86+
87+
**Implication:** exclusion rules will be weaker and more inferential than eligibility rules; the confidence
88+
indicator #6795 calls for matters most here.
89+
90+
## Signal type 3 — assignee rules
91+
92+
Only loopover's `CONTRIBUTING.md` mentions assignment at all (1 mention); rust and kubernetes: 0. kubernetes
93+
does use `/assign` heavily, but that lives in its **external** community guide, not in-repo.
94+
95+
**Implication:** "not assigned to the repo owner" cannot be sourced from docs for most repos. It is derivable
96+
from the issue's own `assignees` field at query time, which is a _runtime_ check, not a profile rule — worth
97+
separating in the schema.
98+
99+
## Signal type 4 — PR templates and linked-issue requirements
100+
101+
PR templates exist in 6/10, but presence says little: they are largely checklists of prose ("I have read the
102+
contributing guide"), not machine-checkable eligibility gates. As noted in the Summary, the closing-keyword
103+
linked-issue requirement is loopover-specific (8 vs 0 vs 0 vs 0).
104+
105+
## Signal type 5 — AI-agent-facing docs (the surprise)
106+
107+
**6 of 10 carry `AGENTS.md` and/or `CLAUDE.md`** — including react, deno, kubernetes and svelte. This is the
108+
most _consistently present_ signal in the sample after labels themselves, and it did not exist as a convention
109+
two years ago.
110+
111+
`JSONbored/sure-aio` is the sharpest data point: **`AGENTS.md` and no `CONTRIBUTING.md`** — a repo that
112+
documents its rules for an AI contributor and not for a human one. Any extraction that treats `CONTRIBUTING.md`
113+
as the primary source and agent docs as a fallback has the priority backwards for that repo.
114+
115+
## The negative case
116+
117+
`sindresorhus/slugify`: no `CONTRIBUTING`, no PR template, no agent docs, 8 labels — all described, but only
118+
`good first issue` / `help wanted` carry eligibility meaning. `sindresorhus/p-map` (7 labels) and `chalk/chalk`
119+
(10 labels) match. This is a real and common shape: **labels are the only signal**, and the profile must
120+
degrade to "eligibility from labels alone, low completeness" rather than fail.
121+
122+
## What this means for #6795
123+
124+
- **Per-signal provenance and confidence are load-bearing, not nice-to-have.** The sample's signal quality
125+
varies so widely between repos that a single repo-level confidence score would be useless — `rust` has
126+
excellent label descriptions and no PR template; `react` has a PR template and almost no label descriptions.
127+
- **Every rule must be independently absent.** 3/10 have no eligibility label; 2/10 have no docs at all; 4/10
128+
have no agent docs. "Absent" needs to be a first-class value, distinct from "not yet extracted".
129+
- **Do not model the linked-issue rule as a core field.** It is loopover's norm, absent from the rest of the
130+
sample.
131+
- **Probe both `CONTRIBUTING.md` and `.github/CONTRIBUTING.md`,** and treat a very small file as a pointer
132+
rather than as rules.
133+
- **Rank agent docs at least as highly as `CONTRIBUTING.md`** as a rule source.
134+
135+
## Method / reproducibility
136+
137+
Every figure came from the live API; nothing was inferred from memory. Label counts are `length` over
138+
`GET /repos/{owner}/{repo}/labels?per_page=100` (paginated for the 100+ repos); "Described" counts labels whose
139+
`description` is non-null and non-empty; doc presence is a `GET .../contents/{path}` probe (404 ⇒ absent); file
140+
sizes are the contents API's own `size`; the linked-issue and assignment figures are case-insensitive greps of
141+
each decoded `CONTRIBUTING.md`. Counts are a point-in-time snapshot and will drift as these repos evolve — the
142+
_shape_ of the finding (wide, uneven variance) is the durable part, not the exact integers.

0 commit comments

Comments
 (0)