Skip to main content
About 5 minutes. You start a private Linear workspace holding four issues, your own coding agent picks up the queued one and says why, and you read the twin’s own record of every call it made — a tape where the request line is identical on every row, which is what makes a GraphQL agent hard to audit anywhere else.
A digital twin is not a mock. It is a stateful service that answers the same GraphQL and MCP calls as api.linear.app, boots from a declared starting state, records every request, and never touches your workspace. It holds you to a real schema too: a mutation with the wrong field name is rejected here exactly as Linear rejects it, instead of cheerfully answering 200.

Before you start

  • A Pome account. pome login opens the browser sign-in and creates one if you do not have it. No credit card.
  • Your own coding agent — Claude Code, Cursor, anything that can run a shell command and read JSON back.
  • Node 18+ for npx, plus curl and jq. Here jq does double duty: it also builds the request bodies, so you never hand-escape a GraphQL document.
Nothing else. No Linear workspace, no ANTHROPIC_API_KEY, no model inference paid for by Pome: your agent is both the operator and the actor. It drives Pome, and it is the thing that acts on the twin.
Nothing on this page is graded. No task file, no criteria, no score — and no agent eval is charged, because an eval is only ever burned when a run is graded. A sandbox you start, drive and stop costs you nothing. Grading appears exactly once in this curriculum, at the support-triage capstone, where the agent under test is sealed off from the criteria that judge it.

Paste this

Hand this to your coding agent as-is. It names the twin’s own GraphQL surface and the boundary it must not cross.
One token opens a sandbox: POME_AUTH_TOKEN. It is the bearer on every call to the twin, REST and MCP alike. On most twins the secrets file also carries a provider-shaped token — POME_GITHUB_TOKEN=github_pat_…, POME_STRIPE_API_KEY=sk_test_… — and that one is not the bearer; it is what the twin serves inside the sandbox. The linear twin ships none at all. Send that provider-shaped token as the bearer, or send no bearer at all, and the proxy answers an opaque 404 readingNo twin pod for this session. So a 404 on a sandbox you just created is almost always the wrong bearer rather than a dead sandbox.

The world

sandbox create boots a Linear twin from its declared starting state and hands back the URLs that reach it. It also writes the connection secrets to .pome-sandbox.env at mode 0600, and says so on stderr.
One endpoint takes every call. These two helpers post a GraphQL document to it and read the tape back:
The world is one team, ENG, with four issues — one in each workflow state:
ENG-2 is the one thing queued and unstarted, and it is the same every time you create this sandbox — which is what makes anything you observe next reproducible. Before moving it, resolve what “In Progress” actually is on this team: a row with an id, not a string.
Now the two writes — the transition, then the note that says why:
Read it back through the twin’s own surface — not out of the response your agent already holds:
This is the thing worth noticing. ENG-2 comes back in the state you resolved by id, and the new comment sits after the one the workspace already had rather than replacing it. Two comments, not one — the seeded history survives the write, which is why “a comment exists” and “the right comment exists” are different assertions. Stop the sandbox and ENG-2 is queued and quiet again.

Read the tape

Every call above was recorded by the twin as it happened. This is the part neither a mock nor a staging workspace gives you: an account of the run written by the service, not by the agent.
Six identical request lines. Method, path and status carry no information here — and that is not a gap in the recording, it is what a GraphQL API looks like from the outside. It is also why a proxy log tells you nothing about what a Linear agent did. What the twin recorded instead is the document each call sent:
You should see:
  • Six rows, in the order they happened. Read top to bottom and the standup is legible without asking the agent what it did: it identified itself, it read the queue, it resolved the state, it moved one issue, it explained itself, it checked.
  • state_mutation: true on exactly the two mutations. It means the call landed — a write the twin refuses reads false, not true. On this twin that column does the work the method does elsewhere.
  • The recorded document, not just an operation name. The row keeps the whole query, so issue_todo being the id that moved is in the evidence rather than in the agent’s account of itself.
  • tool: null on every row. Unlike the GitHub twin, this one stamps no action vocabulary of its own.
Now the schema. Ask for the transition the way an agent that skipped the lookup would ask for it:
A 400 carrying Linear’s own suggestion, with fidelity: semantic beside it: the twin refused this because the schema refuses it, not because the route is missing. When you are done, stop the sandbox to free the slot — it also expires on its own 30 minutes after it was created:

What you could assert here

Nothing on this page was graded, but a tape and a final state are exactly what a graded check reads. The Linear twin already declares these — no authoring required, and list_checks on the Pome MCP prints the full set with what each one actually compares: Pointers, not a task. Turning them into a graded exam is the capstone below, and Write a task is where the grammar lives.

How real is this twin?

Every surface you called above is compared against a captured response from the real Linear API, re-run daily and published. The Linear row on status.pome.sh carries the current count of twin responses that match, how many of Linear’s own MCP tools are covered, and how old the captured baseline is. Nobody has to take our word for the fidelity, including us — a twin that drifts turns that row red. Which surfaces are covered, and which are shape-only, is on the Linear twin reference.

Next: the one graded lesson

The support-triage capstone

The same twins, now an exam: a sealed agent under test, a deliberate failing score, and one line of prompt that turns it green.

Linear twin reference

Every query, mutation and MCP tool the twin serves, with its fidelity tier.

Write a task

Turn the checks above into a graded exam for your own agent.

pome sandbox

Create, list and stop sandboxes — including multi-twin ones.