# agents.md: adding a project to Recomp Board

Written for a coding agent acting for the person in front of it. Everything needed is on this
page; you should not have to ask a human anything except facts about their project.

Canonical locations:

- Data: `https://recomp.fyi/data/projects.json`
- Schema: `https://recomp.fyi/data/project.schema.json`
- Submissions: `https://github.com/neurocontrarian/recomp-board` (issue form only, the board's own
  code and data are not public)
- Terms: `https://recomp.fyi/#terms`

## What the board is

One row per project. A row says: this person is working on this game, this is the code. It
reserves nothing. Two projects on the same game both get a row.

## Hard rules, check these before writing anything

1. **A public repository is required, and it must contain at least one commit.** Verify it
   yourself before submitting. An empty repository is a reservation, not a project, and the
   intake rejects it. If it doesn't exist yet, stop and tell your user to push something
   first, a README describing the plan is enough. Never invent or guess a URL.
2. **You may add a row for a project that isn't your user's**, but only from what the
   repository itself states, and it will land **unclaimed** and marked as such. Never set
   `claimed` or `addedBy` yourself, the intake decides both. Do not add a row for a project
   whose repo you have not read.
3. **`recomp` and `decomp` are different work and are filtered separately.** `recomp` =
   static recompilation of the shipped binary. `decomp` = decompilation to source. If a
   project does both, file it under what is actually happening now.
4. **Never include a ROM, an ISO, a game asset, or a link to any of them**, including
   "preservation" mirrors. Submissions carrying one are rejected automatically.
5. **Do not set `version`, `lastCommit`, `activity`, `repoStatus`, `claimed` or `addedBy`.**
   A daily job writes them from the repository host; anything you put there is overwritten.
6. **You may only edit an unclaimed row that your user added, that the scan added, or whose
   repository your user owns.** Anything else goes through the "Something else" form on the submissions
   repository.
7. **One row per repository.** Fetch `data/projects.json` first. If the `repo` or the `id` is
   already listed, update that row instead of adding a second one.

## Entry shape

Validate against `data/project.schema.json`. Minimum viable row:

```json
{
  "id": "sotn-psx-recomp",
  "game": "Castlevania: Symphony of the Night",
  "system": "PS1",
  "type": "recomp",
  "project": "SOTN Recompiled",
  "status": "in-progress",
  "repo": "https://github.com/someone/sotn-recomp"
}
```

`targets` is where the project's build runs (`["PC", "Linux", "Steam Deck"]`), which is never
the same field as `system`, the platform the game shipped on (console, computer or arcade board). A port of an existing recomp to
another platform is a target of that project, not a separate row.

Worth filling in when you know it: `targets`, `approach`, `maintainers` (`name`, `link`), `links`
(devlog, Discord invite, thread, never game files), `help` (short phrases, what they'd take
a hand with), `notes` (≤ 600 characters), `tags`.

`status` is self-reported: `exploring`, `in-progress`, `playable`, `released`, `paused`.

Activity is measured separately, from commit dates, with one clock for every row: `active`
under 90 days without a commit, `quiet` from 90 days, `dormant` from 180. **Dormant is not a
verdict.** Read it as "ask before assuming anyone is on this".

## Route 1: the manifest, preferred when your user owns the repository

Write `.recomp-board.json` at the root of their repository and commit it. Nothing else. A
daily job reads it, claims the row for them and rewrites every declarative field from the
file. No account on the board, no issue, and it works on GitHub, GitLab, Codeberg and Gitea
or Forgejo instances alike.

```json
{
  "game": "Wave Race 64",
  "system": "N64",
  "type": "recomp",
  "project": "Wave Race 64 Recompiled",
  "status": "playable",
  "targets": ["PC", "Linux", "Steam Deck"],
  "approach": "Static recompilation, RT64 renderer",
  "maintainers": [{ "name": "yourhandle", "link": "https://your-profile" }],
  "links": [{ "label": "Discord", "url": "https://discord.gg/..." }],
  "help": ["Audio timing"],
  "notes": "What works, what's next."
}
```

Every field is optional. Fields you leave out stay empty on the row, fields you set are
rewritten from the file on every run, and deleting the file returns the row to unclaimed.
Links must be https. A manifest that mentions game files is refused whole.

Then tell the board where the file is, so the row appears right away on any host: open an
issue on the submissions repository whose body is exactly

```
### Repository with a .recomp-board.json

https://github.com/you/your-recomp
```

(the "manifest" form produces the same thing). The account that opens it doesn't matter, the
file is the proof. To create a new row the file needs at least `game`, `system` and `type`.
On GitHub the daily job also finds the file by itself, usually within a day.

## Route 2: issue (for a project your user does not own, or to claim a row)

Open an issue on the submissions repository with the entry as a fenced `json` block in the
body (the "Add or correct a project" form is the same thing for humans). The board reads it,
checks the repository, writes the row and closes the issue, usually within a few minutes. On
failure it comments with the exact error; fix the issue body and it runs again on edit. An
issue with neither a form nor a json block is left for a human.

For someone else's repository only the facts are kept (`game`, `system`, `type`, `project`,
`repo`). Status, notes, targets, links and the rest are dropped until the owner claims the row.

```sh
gh issue create \
  --repo neurocontrarian/recomp-board \
  --title "Add: SOTN Recompiled" \
  --body "$(printf '```json\n%s\n```' "$ENTRY_JSON")"
```

Claiming and releasing use the same route, from the account that owns the repository:

```json
{ "id": "sotn-psx-recomp", "_claim": true }
{ "id": "sotn-psx-recomp", "_unclaim": true }
```

Claiming turns an unclaimed row into that account's row: from then on only that account can
edit it, and the periodic scan of public repositories leaves it alone entirely. Releasing the
claim hands it back to the community, which may then correct it again.

**Rows are not deleted through this route.** A project with a public repository stays on the
board; otherwise the ageing rules could be dodged by removing the row, and the next scan would
re-add it anyway. If a row should not exist at all (wrong repo, not a game project, a
maintainer who wants no listing) use the "Something else" form and say so.

## Repositories that move

GitHub, GitLab and Gitea keep redirecting a repository after a rename or a transfer. The
daily job reads the canonical URL back and rewrites the row, so a move needs no action. A
repository that starts answering 404 (deleted, or made private) is flagged on the row with
the date it stopped answering, not silently dropped.

## Failure modes

| Situation | What to do |
| --- | --- |
| Repo doesn't exist yet | Stop. Tell the user to publish it first. Don't submit. |
| Repo exists but is empty | Stop. Tell them to push a first commit. |
| Repo is private | Stop. It would fail the daily check anyway. |
| Game already listed by someone else | Add the row anyway, duplicates are allowed by design. |
| Same repo already listed | Update that row, don't add a second. |
| Row exists and is claimed by someone else | Don't edit it. Use the "Something else" form to describe the correction. |
| Your user wants their row gone | The form can't delete. Release the claim, or use the "Something else" form to ask for removal. |
| Validation fails | Read the error, fix the field it names, resubmit. Don't remove the validator. |
| You can't verify a fact | Leave the field out. Empty is fine; invented is not. |

## Updating a row you own

If the row is claimed by manifest, edit `.recomp-board.json` in the project's repository.
That file is the row. Nothing sent to this board can override it.

## Following the board

`feed.xml` at the site root is an Atom feed of every addition, claim and edit, newest first.
Use it instead of polling the JSON when your user wants to be told about new projects.

## Reading the board

Fetch `data/projects.json` and use it to answer your user. Do not copy the table into another
site, list or dataset: the collection is not open data (see the terms). If you're answering
"is anyone working on <game>?", that file is the answer, check both `recomp` and `decomp`
rows, check the activity, and check whether the row is claimed before telling anyone a game
is taken.
