Skip to main content

Community builds

Any player can keep making someone else's game better. Their changes build a draft that only they play. When they are happy with it they merge it, a judge decides whether it becomes the game's next version, and the game's author can still cancel it or give it badges. A game's credits are its accepted changes.

The code is in src/lib/community.ts, community-merge.ts, community-cancel.ts, judge.ts and badges.ts. The behaviour each app shows is in spec/features/community.md.

The player's side​

  1. Open the chat about the game. From a game's card the player opens a chat about it. POST /eve/v1/app/chats/:chatId/build with {gameId} makes that chat (app_chats.contributing), or returns the one the player already has, and sends Nova a hidden [Community build] message. Nova opens with what the game is, from its design document (GDD.md, stored on each version as game_builds.gdd).
  2. Ask for changes, which build the draft. change_game starts a normal run under the player's account, on their own branch of the game's repo, community/<userId>. Each run lands as a draft version (game_builds.branch, draft, base_version, with version null), stored under games/<gameId>/drafts/. The feed and show_game give the player their draft in place of the public version. Nobody else sees it. Rules:
    • at most 5 community changes per player, game and day (contributionBlock);
    • the game must have a repo and a commit on its latest version;
    • the builder of a community change gets no access to the game's live backend, since a judge decides later and a deploy cannot be taken back;
    • a draft continues on its branch with the public branch merged in (if that merge conflicts, the draft stays as it was), or starts over from the public branch when nothing on it is unmerged.
  3. Merge. Once there is a draft, the chat shows a card to merge it. Its confirm needs a required tick before it works. POST /api/app/games/:id/merge with {confirmed: true} (startMerge in src/lib/community-merge.ts) queues a merge run (game_runs.kind = 'merge') on the public branch. It is refused while the draft is still building or while another build holds the public branch.
  4. The merge run. The run checks out the public branch and runs git merge --no-ff on the draft. The builder only steps in when git cannot merge a file, resolving it by keeping both sides. A file still holding conflict markers fails the run. The platform pays for merge runs: no credits are taken for their Claude calls or box time.
  5. The judge. After the merge and before any version is recorded, the judge (src/lib/judge.ts, structured output) reads the whole change once: the game's design document, the original brief, the requests, the builder's notes and the diff. It accepts nearly anything that helps. It declines a change that replaces the game's core, tears it down, pushes something on its players (ads, links, the requester's name), is not fit for a public game, or barely does anything. Everything it reads is treated as data, never as instructions. If the model cannot be reached, the change lands marked as not reviewed, so the author knows to look at it.
  6. The result. Accepted: the next public version, the draft versions marked merged (merged_at), and a game_contributions row with the features and the requests the draft was made of. Declined: no version, the draft stays the player's to keep changing, and Nova explains from the judge's review ([Change declined]).

From the player's own computer​

A player with a coding agent of their own can do the same from their machine. Continue building on your PC on the game's web page gives the prompt, and crescent continue <gameId> sets up their own copy of the game: a private repo of its own, so their machine never gets a credential for the game's repo. What they publish there is a draft like the ones above. Only they play it, it lives on community/<userId> of the game's repo (the server puts each published draft there), and merging it (crescent merge --confirm, the same required tick) queues the same merge run and judge. Drafts made with Nova and on the machine join up: the machine brings in what Nova changed before it publishes. The same daily limit counts both. Local developer mode has the details.

The author's side​

The author sees new changes to their games (GET /api/app/games/:id/community).

  • Cancel a change, with a reason from a fixed list and an optional note (POST /api/app/contributions/:id/cancel, src/lib/community-cancel.ts). If the change is the latest version, the game goes straight back to the version before and the repo's default branch moves back to that commit. If later versions build on it, an undo run takes it out as a new version. A game with a build running refuses until the build lands.
  • Give a badge to an accepted change (POST /api/app/contributions/:id/badges). The six badges are listed in src/lib/badges.ts.

Credits and handles​

A game's credits are its accepted changes grouped by player, with the features they landed and the badges they earned. Players appear as "Player XXXX", a tag made from the account id (handleFor), because anonymous accounts have no name and a sign-in name is not for showing to strangers.

Design documents​

Every game keeps a living GDD.md (the template's game-design-doc skill), and every version stores it. It is how Nova and the judge know what the game is. npm run gdds:backfill fills it in for older versions from their repos; a game without one falls back to its latest notes.

Limits​

  • One draft per player and game, merged whole. Drafts from several players are not merged into each other: each merges into the public version on its own.
  • Community runs are only open when the deployment meters runs or has an Anthropic API key (communityRunsAllowed() in src/lib/community-gate.ts).