Skip to main content
Every twin boots with a world already in it: acme/api and one open bug on GitHub, a #general channel on Slack, an empty ledger on Stripe. That world is there so the first command works. It is not there because it looks like yours. A seed is how you replace it. Write your repositories, your threads, your customers, and the twin serves them to your agent as though they were real — same routes, same status codes, same recorded tape. This page is the whole job, from generating the file to proving every field of it landed.

A seed replaces the world. It does not merge into it.

This is the single most costly thing to get wrong, so it comes first.
A seed is the twin’s entire starting state, not a patch on top of the default one. Seed a GitHub world and acme/api is gone — anything your agent needs to find has to be in the file you wrote.
Measured on a local twin booted from the file further down this page:
There is no merge mode and no partial seed. A seed that omits labels boots a repository with no labels, not a repository with ours.

Generate the file; do not write it

pome twin new-seed prints the twin’s own starting state, computed from the twin’s declared world and normalised through the same parseSeed the twin runs at boot. Whatever it prints, the twin loads. Start from that and edit; do not compose one from the field names in a reference table. This is not a style preference. On 2026-08-26 three of the five seed examples published on this site could not boot the twin they were written for — repositories.0.owner missing on GitHub, primaryMailbox typed as a string on Gmail, issues.0.assignee null on Linear. Every one of them was correct the day it merged, and each was hand-typed. Copying one produced a 503 Failed to spawn twin pod twelve seconds later. Every seed block on this site is now generated and re-parsed in CI against its own twin. The generated file is a starting point with real bulk to it — with @pome-sh/cli@0.42.1: 82 lines for GitHub, 89 for Slack, 136 for Gmail, 270 for Linear, 14 for Stripe’s deliberately empty ledger. Cutting it down is the normal next step.

Seed file

Here is the GitHub file above, cut to one organisation, one repository and the one issue a triage task needs. It is what pome twin new-seed github printed, with the sample world’s names and rows replaced:
One twin is flat; two or more is the per-twin envelope. The file above is GitHub’s world with no wrapper around it — which also means it says nothing about which twin it is for, and that is why both --seed doors below take the twin’s name as well. Add a second twin and the file grows the envelope, one key each:
Write one file covering both in a single command:
Seeding two twins is not the same job as running them side by side against one agent — that half has its own guide, Two twins, one sandbox. JSON or YAML — both doors parse either. A _meta block, which pome compile-seeds writes into a task’s sidecar, is dropped before the twin sees it. Each twin’s own generated block, and the field names behind it, live on that twin’s page: github, stripe, slack, gmail, linear.

One file, both doors

The same world.seed.json boots a twin on your laptop and a hosted sandbox. No conversion, no second copy.
Name the twin. A one-twin file is flat, so it cannot say which twin it is for, and a file covering several does not say which of them to boot; only a hand-written envelope naming exactly one twin makes the argument optional. The command prints the twin’s URL and a bearer, and the boot line names the file:
The seed is parsed before a hosted sandbox is provisioned, so an unbootable world costs a 422 in well under a second rather than a spawn that fails. POST /v1/seeds/validate runs the same check and provisions nothing at all.

Check that every field landed

A twin that boots is not proof that your whole world arrived — until 2026-08-29 a typo’d field name could be silently dropped on three of the five twins. No longer, on your machine. Measured 2026-08-30 against @pome-sh/cli@0.42.1, a GitHub seed carrying isuses instead of issues refuses to boot, naming the key and its path:
All five twins refuse the same way at twin start --seed since F-1689 landed. A wrong type was always refused everywhere — expected string, received undefined — so a local boot now vouches for every field name and type in your file.
The hosted door has not caught up yet. POST /v1/seeds/validate and sandbox create still run the previously pinned parsers (@pome-sh/sandbox-domains@0.2.8), on which github, slack and stripe accept an unrecognised key and silently drop it — the isuses seed above answers valid: true and boots a hosted world with no issues in it. Until the pin moves, two habits close that gap: boot the seed locally first, where the refusal above catches the typo for free, and read your hosted collections back — an empty array where you wrote rows is a dropped key. F-1775 tracks the pin move.

Seed file, or task sidecar

One file, two destinations. pome twin new-seed <twin> writes it either way, and the shape follows the twin count rather than where it is going. Both are flat for one twin and the per-twin envelope from two up, so a file written for one destination works at the other. What differs is only the last row: a sidecar has the task’s ## Config beside it, while a seed file travels alone and so has to be handed the name. Replace-not-merge applies to both, and both accept JSON or YAML. A task is not required to seed a world. A hosted sandbox created with twins and seed and no task records no task at all, which is what a bring-your-own-eval harness wants: you drive the agent, and you score the tape from the twin’s own /_pome/events.

Next

Write a task

Turn the world you just built into a graded exam — criteria, [code] checks, and why a criterion the seed already satisfies leaves the denominator.

pome twin

Every flag on twin new-seed, twin start and twin status.

Seeds over REST

The wire shape, POST /v1/seeds/validate, and the error vocabulary a client branches on.

Digital twins

Each twin’s generated starting state, its declared checks, and what it serves.