- renderComment() adds maintainer hint for fix+valid YAML - new apply-ai-fix.yml workflow (issue_comment trigger, permission-gated) - new scripts/apply-ai-fix.js (extract, validate, commit to PR branch) - docs/policy.md documents command - tests: 16 new, 46 total pass Co-authored-by: John Smith <[email protected]>
7.7 KiB
Catalog Policy
How entries get into the catalog, how they are kept honest, and when they leave. Automation enforces most of this; this page is the reference it links to.
Admission
An entry must:
- work with OpenCode;
- point at a public, non-archived repository root (
pathfor a monorepo folder); - be the only entry for that repository and path;
- not be a cryptocurrency, token or trading project (zero tolerance);
- pass
npm run validate(schema, field limits, kind/category fit).
OpenCode V2 requirement for new plugins
New entries in data/plugins/ must use the V2 plugin API: depend on an @opencode/* package (normally @opencode/plugin) and export Plugin.define({ id, setup(ctx) }). Pre-release V2 builds published under the old name (@opencode-ai/[email protected]*, @next, @beta, or @opencode-ai/client) also count. Packages that ship both V1 and V2 entry points count as V2.
The validate check detects this from, in order: the entry's npm package, package.json files in the repository (at path, the root and packages/* / plugins/*), then the source code. Results:
| Detected | New plugin PR | Meaning |
|---|---|---|
v2 |
passes | @opencode/* dependency or Plugin.define found |
v1 |
blocked, labeled needs-v2 |
Only @opencode-ai/plugin/sdk 1.x, or a legacy V1 plugin function without Plugin.define |
unknown |
passes with needs-human |
Nothing conclusive found; a maintainer verifies by hand. Adding npm or path usually resolves it |
PRs labeled needs-v2 are re-checked every night. When the target migrates, the bot relabels the PR, fills in any missing fields on same-repo branches, re-runs validate and tells the author. After 60 days still on V1, the PR is closed; reopen it or resubmit through the issue form after migrating.
Themes, agents, projects and resources are not blocked on V2. Their compat is informational (v2/v1 when they use the SDK, otherwise n/a).
Existing V1 plugins: sunset on 2027-01-01
Plugins already in the catalog that are V1-only carry a V1 only badge in the README and on the site. The nightly health job re-detects every plugin not marked v2; when one is detected as V2 it opens a PR (bot/health-compat) marking the entry compat: v2. After 2027-01-01 V1-only plugins are removed by the health job's removal PR, but only when that night's re-check still detects V1: a V2 or inconclusive result keeps the entry, and inconclusive ones are listed in the health report for a maintainer. Plugins with unknown compatibility are listed there too for manual verification.
The bot keeps the list in the V1 sunset tracking issue (label v1-sunset) current, posts one reminder there 31 days before the sunset, and closes the issue once no V1-only plugins remain.
Health checks and removal
A nightly job checks every entry and maintains the Catalog health report issue.
| Finding | Action |
|---|---|
| Repository renamed or transferred | Bot PR updates the repo URL |
| Repository archived, deleted (404), or npm package deprecated | Listed in the report; removal PR after 30 days if it doesn't recover |
Plugin not marked v2 now detected as V2 |
Bot PR sets compat: v2 |
| V1-only plugin after 2027-01-01, still detected as V1 | Removal PR |
| No push for 6–12 months / 12+ months | quiet / stale badge on the site. Never removed for inactivity alone: small, finished tools are fine |
Removal PRs are reviewed like any other PR; closing one keeps the entries until the next run proposes them again. Git history is the archive; there is no separate retired list.
Discovery
A weekly scout searches npm for packages published since V2 shipped that depend on @opencode/*, keeps public, licensed, non-archived, non-fork repositories that are not already listed or in an open PR, and ranks them by weekly downloads, then stars. It posts a Scout issue; a maintainer ticking a candidate's box makes the bot open a PR for it. Add a package or repo to discovery/declined.txt to stop suggesting it.
AI review
An OpenCode V2 agent reviews the judgment calls the deterministic checks leave open. It is advisory: it never merges or closes anything.
| What | When | Result |
|---|---|---|
Open PRs labeled needs-human or scout |
daily; a PR is reviewed again only after it gets a new commit | One "AI review" comment (verdict, reasons, suggested placement, and for fix a corrected entry checked against the schema) and one label: ai-approve, ai-fix, ai-close or ai-unsure |
| Unticked candidates on the open Scout issue | weekly, after the scout run | Confident, policy-clean accepts get a PR opened by the bot, with the AI's kind, topics and tagline; everything else gets an "AI: …" note on its line |
A maintainer still merges every PR, including the ones the AI opened, and the validate check still applies. Delete the comment or remove the label to discard a verdict; dispatching the workflow with force re-reviews.
Maintainers can apply a fix verdict in one click by commenting /apply-ai-fix on the PR. The bot validates the sender, checks the suggestion is fresh and schema-valid, commits it to the PR branch (same-repo PRs only), and validate re-runs.
How it is contained:
- It runs only on a schedule or manual dispatch, with base-branch code; PR code is never checked out.
- The model runs in a job whose GitHub token has no permissions and is never in its environment. Its agent (
.opencode/agents/catalog-reviewer.md) may only read, glob and grep inside the repository; a self-test before every run proves shell, edit and web access are unavailable. - It sees a pre-built evidence bundle (the PR's entries, check results, the target's README and
package.json, similar entries), treats it as untrusted data, and must reply with one JSON verdict. - A separate job validates that verdict against a strict schema (known verdicts, categories, kinds and topics; matching PR number and commit) and renders all comment text itself, escaping HTML and breaking @mentions. Anything else is dropped.
The model comes from the OPENCODE_MODEL repository variable; credentials from the OPENCODE_GO_API_KEY (or OPENCODE_API_KEY) secret.
Branch protection
main has a ruleset: changes land only through pull requests with a passing validate check (GitHub rulesets do not accept direct pushes while a status check is required), and force-pushes and deletion are blocked. Bots open PRs too; GitHub holds workflow runs on bot-opened PRs for approval, so each bot approves the validate run on its own PR. Repository admins can merge a PR whose check is failing or pending (for a broken CI).
Automation map
| Workflow | When | What |
|---|---|---|
validate-pr.yml |
every PR | Required validate check |
triage.yml |
PR opened/updated | Labels and a results comment (base-branch code only). needs-human is only ever removed by a maintainer |
generate-readme.yml |
push to main | README.md and dist/registry.json; fails without writing on any invalid entry. The regenerated commit goes through a bot PR (bot/readme-*) that merges itself once validate passes |
site.yml |
push to main, daily | Search site on GitHub Pages |
health.yml |
daily | Health report and V1 sunset issues; rename, V2-migration and removal PRs |
reprobe.yml |
daily | Re-checks needs-v2 PRs |
scout.yml |
weekly | Scout issue with V2 plugin candidates |
issues.yml |
issue opened/edited | Issue-form submissions and scout acceptances become PRs |
ai-review.yml |
daily (PRs), weekly (scout) | Advisory AI review of needs-human and scout PRs; opens PRs for scout candidates it accepts |