Files
BillyOutlastandJohn Smith 86e93031fb feat: one-click /apply-ai-fix for AI review suggested entries (#590)
- 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]>
2026-10-19 19:41:45 -04:00

94 lines
7.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 (`path` for 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`](../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`](../.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 |