Contributing to Metagraphed#
Metagraphed is the Bittensor subnet integration registry — every subnet, metagraphed. This is the backend: a Cloudflare Worker API plus Node build scripts. JSON Schema is the canonical contract → OpenAPI → typed clients. Generated artifacts under public/metagraph/ are projections of reviewed source, never hand-authored truth.
Live: metagraph.sh · API api.metagraph.sh · License AGPL-3.0 (Apache-2.0 client SDKs)
Two kinds of contribution, two paths:
- Code / schema changes → normal feature PR, run the gates below.
- Community data → add a surface to one subnet file, see Community submissions.
Setup & gates#
Fastest path: open this repo in a devcontainer-aware tool (VS Code, GitHub Codespaces, the devcontainer CLI) — Node 22 and Playwright's Chromium come preinstalled, no manual setup. Otherwise, use Node 22 (.nvmrc pins it — nvm use picks it up).
npm install
npm test
npm run validate
npm run build
npm run validate runs schema, API, and OpenAPI checks. For a full local data pipeline run, use npm run pipeline:check. Match focused checks to what you touch (npm run validate:schemas, validate:api, validate:openapi, worker:test) rather than running everything.
npm run build here regenerates the root schema/OpenAPI contract and is what worker:test depends on — it's a separate step from apps/ui's own build. If you're touching apps/ui/, see apps/ui/CONTRIBUTING.md for its own npm run build --workspace=apps/ui cycle; a PR touching both may need both.
New files are TypeScript. All new code, script, and test files must be .ts — never .mjs/.js. The repo-wide TypeScript migration (#7510) is complete, and the validate:no-hand-written-mjs CI gate fails any PR that adds a .mjs/.js file under src/, workers/, scripts/, tests/, or deploy/wss-lb/.
Schema-first rule#
The contract is generated, so you never edit it by hand:
- Edit the source under
schemas/orschemas/components/. - Run
npm run buildto regenerateopenapi.jsonand the types/clients. - Commit the regenerated artifacts in the same PR.
Skipping the rebuild trips validate:contract-drift in CI. Schemas are the source of truth; everything downstream follows.
Don't hand-bump version fields#
These are bumped automatically after your PR merges — leave them alone:
packages/client/package.json's"version".src/mcp-server.ts'sMCP_SERVER_VERSIONand the matching"version"inserver.json.
CI never requires you to bump these yourself — a PR that changes the contract or adds an MCP tool is valid without touching either. Bumping one of them in your own PR doesn't help and risks a version conflict with the automation's own follow-up PR; expect it to be closed with a request to resubmit without that edit.
Where to start#
- Enrich a subnet (the best first PR) — we track one scoped task per subnet under the surface-enrichment epic #427. Browse
good first issue+help wanted: pick a subnet, find its real public API / OpenAPI / data artifact, and add it as a surface on the subnet's file (Community submissions below). Each issue links thesurface:addcommand and flagssubnet:newwhen the subnet file does not exist yet. - Data gaps — generate the current curation queue:
npm run curation:brief(add-- --limit 20for more,-- --jsonfor machine-readable). Start with profile-light subnets: directory-only entries, missing websites or source repos, public APIs with no OpenAPI metadata yet. Seedocs/curation-playbook.md.
Community submissions#
Surfaces live in one file per subnet: registry/subnets/<slug>.json → its surfaces[] array. A community contribution adds a surface to that one file — npm run surface:add writes it with authority: "community" and review.state: "community-submitted". If the subnet has no manifest yet, scaffold that one subnet file first with npm run subnet:new, then add the surface to the same file. There is no per-surface candidate file anymore (recreating registry/candidates/community/*.json is rejected by CI), so you can't farm one surface per PR: one subnet = one file = one PR.
Change only the one
registry/subnets/<slug>.json— no generated artifacts. For an existing subnet manifest, that means appending your community surface without changing top-level subnet metadata. For a missing subnet manifest, the requiredsubnet:newscaffold fields are expected in the new file. First-time provider? Pass--provider-name+--provider-urlandsurface:addscaffolds theregistry/providers/<slug>.jsonstub for you in the same PR; provider identity still gets reviewed before it's trusted.
Plagiarism is not tolerated. Copying another contributor's PR, surface, or work and submitting it as your own — including duplicated or lightly reworded copies filed under a different account — is a hard violation. Don't copy others to farm Gittensor rewards: anyone attempting to cheat or copy for gain is permanently blocked from contributing across all of our repositories. See
CODE_OF_CONDUCT.md.
A linked, currently-open issue is required on every PR — issue creation is maintainer-only, so pick an existing open issue to link rather than filing your own (browse
good first issue/help wanted); a PR with no linked issue, or one linked to an issue that's already closed, is automatically closed before its content is even scored. What is against policy is using more than one account you control (alt / sock-puppet accounts) to inflate contribution credit, along with manufacturing low-value/slop PRs against off-scope issues and bulk point-chasing surface PRs. Farmed work earns no linked-issue bonus, and repeat or any confirmed multi-account farming is closed on sight and blocked. Enforcement is proportional; the full ladder is inCODE_OF_CONDUCT.md.
Add a surface locally — three steps:
# 1. Find the provider slug for the team/operator behind this surface.
npm run providers:list
# 2. Append the surface to the subnet's file with a REAL --provider slug.
# Debut provider? Add --provider-name + --provider-url and surface:add also
# scaffolds registry/providers/<slug>.json so the PR validates in one shot.
npm run surface:add -- \
--netuid 7 --kind docs \
--url https://docs.example.com \
--source-url https://github.com/example/project \
--provider <provider-slug> --submitted-by <github-login> --write
# debut provider: --provider-name "Example Team" --provider-url https://example.com
# 3. Check it before pushing — a fast local pre-check (schema + provider slug +
# review-state + real subnet name) without the full build (CI runs full validate).
npm run validate:surface -- registry/subnets/<slug>.json
New subnet with no file yet?
npm run subnet:new -- --netuid <n> --name "<Real Name>" --writefirst — a real--nameis required (placeholder on-chain identities like "Team TBC" are rejected) — then add your surface to it.
A good surface PR is small: one public url, one source_url proving the claim, the right kind, all on the subnet's single file. Auto-review kinds: docs, website, source-repo, dashboard, openapi, subnet-api, sse, data-artifact, sdk, example.
Higher-trust application surfaces (authenticated or paid APIs, unknown providers, identity disputes) are welcome too — the autonomous reviewer scrutinizes identity/evidence harder and, when in doubt, closes or escalates rather than merging. Make the proof airtight (an independent source_url proving ownership). Base-layer subtensor-rpc/subtensor-wss/archive endpoints are maintainer-curated network infrastructure served through the endpoint lane, not contributor subnet surfaces.
Hard boundaries:
- Health, uptime, latency, incidents, and pool eligibility are probe-derived only — never hand-set them (or a surface's
verification). The build's prober owns them. - No secrets, PATs, wallet paths, private URLs, or validator-local data.
- Don't invent API/status surfaces a subnet doesn't publish.
- Schema-valid ≠ accepted. The review gate makes the final call.
Accepted vs rejected at a glance — the visible checklist (the final merge decision is the review gate's):
| ✅ Tends to get accepted | ❌ Gets closed / routed to manual |
|---|---|
Exactly one subnet manifest changed: append to an existing file, or create the missing subnet:new scaffold plus the surface (+ an optional providers/*.json for a debut) | Touches generated artifacts, scripts, or workflows |
A surface with a public url plus a source_url that proves the claim | source_url 404s or doesn't back the claim |
authority: community + review.state: community-submitted, an auto-review kind, an active netuid, a provider | A surface the subnet already exposes — duplicate |
auth_required: false, public_safe: true | Secrets/PATs/wallet paths, private/localhost URLs, unproven ownership, or a recreated candidate file |
Callable surface with documented limits? Add an optional structured rate_limit — { requests, window, burst?, scope?, cost_notes? } (requests + window required) — so agents and SDKs can pace calls. It's integration-only: metagraphed never enforces it and it doesn't feed completeness.
Pull requests#
- Short and focused, Conventional Commit-style titles.
- Include the validation commands you ran in the PR body.
- No local paths, machine-specific setup, env dumps, or private notes.
- Frontend/UI work lives at
apps/ui/in this same repo (folded in from the former metagraphed-ui repo via monorepo consolidation). Any PR touching visual output (routes, components, styles) requires a before/after screenshot table in the PR body and is always held for manual review, regardless of AI-review confidence — see themetagraphedskill's Path C for the full contract. A PR confined toapps/ui's data/hooks layer or tests, with no visual change, follows the normal one-shot gate.
How reviews work#
Timing is typical, not an SLA. When the gittensory maintainer agent is operating, most PRs are reviewed and auto-merged or auto-closed within ~1 hour of CI finishing; when it is paused or under maintenance, manual review typically takes 24–48 hours, depending on volume. These are observations, not commitments — reviews happen when they happen.
One-shot, merge-ready as-is. We do not request changes on contributor PRs — a PR is merged exactly as it stands or it is closed; there is no "changes requested" back-and-forth. Before CI we rebase your branch onto main with a merge commit, then review after CI completes — so a rebase conflict, or any failing gate (schema, API, OpenAPI, contract-drift, or surface validation), closes the PR. Keep to the one-subnet-one-file rule, regenerate artifacts, and make it green before pushing; recover from a close by opening a fresh, corrected PR. PRs touching guarded paths (build scripts, the Worker API, CI config) are held for manual review.
You can have at most 2 open PRs against this repo at a time — a 3rd is closed on sight. Every automatic close, including this one, comes with a comment from the gate explaining exactly what was wrong so you can fix it in your next PR.
If we close your PR by mistake, that's on us — we may reopen or re-review at our discretion as time permits. There is no fixed window, and a fresh PR is usually fastest.
Don't ask for or chase reviews. The queue is automated and best-effort, and the gate posts its own status on your PR. Do not DM, @-mention, or comment asking for a review or status — it will not speed anything up and will deprioritize your PR (expect at least 5 days added to its place in the manual queue). Persistent pestering (here, Discord, or elsewhere) is a conduct violation and may get the PR closed and the account blocked.
Scoring and rewards are not ours to grant. Contribution scoring and any Gittensor rewards are set by the subnet's on-chain hyperparameters and validators, not by this repo. Merging a PR is not a promise of score, ranking, or compensation, and all review decisions are at maintainer discretion and final.
This repository is itself Gittensor-listed. PRs merged here by registered miners may earn TAO under the subnet's own rules — see the badge in README.md for a live, aggregate merged-PR/line-count summary. That eligibility and scoring is Gittensor's, not ours, and doesn't change the review bar above.
Deeper docs#
docs/curation-playbook.md— what to curate and in what order.docs/api-stability.md— API/contract stability guarantees.docs/adr/— architecture decision records (why the system is built this way);RELEASING.md— the release runbook.
By contributing you agree your work is released under the repository's AGPL-3.0 License — or Apache-2.0 for contributions to the client SDKs under packages/client/ and python/.