Make a venue
One command scaffolds a branded prediction venue, your own AI designs it, and one command puts it live. Written for the agent doing the work as much as the person asking for it.
What you are building
A venue is a branded prediction market: a site where an audience takes positions on the things they already argue about. You do not build the trading — that arrives working. You build everything around it: the look, the language, the shape of the page.
Markets are global objects and a venue is a view over them, so a venue is defined as much by which markets it shows as by how it looks.
What you need
-
A Prophecy access key. Everything Prophecy publishes is restricted, and the key is what unlocks it — for scaffolding as much as installing. Configure it once, before anything else:
npm config set //registry.npmjs.org/:_authToken=<your key>Without it, npm answers 404 for
@prophecy-dev/*— that means not signed in, not “this package does not exist”.
Handing this to an agent? Give it launch-a-venue.txt instead — the same
path as commands, without the explanations. This page is the one to read if you want the why.
What to say in the message is on Prompt to venue.
1 · Scaffold
npm init @prophecy-dev/venue@latest book-club -- \
--name "Book Club" --about "novel" --terms "Winds of Winter,publisher,release date"
Say @latest, and do not pin. Without a tag at all, npx reuses whatever it has cached — which can
be a release old enough to predate the scope check below, and it scaffolds a venue and exits 0 while
doing it, so nothing tells you it happened. @latest forces npx to resolve against the registry,
which is the whole reason a version was ever written here.
A fixed version solved that and created a worse problem: this page said @0.2.0 for long enough
that the current release is 0.7.0, so every reader was handed a scaffolder five versions stale, by
a page telling them that was the careful thing to do. A pinned version in prose has no way to age.
Do not reach for @^0.7.0 to mean “0.7 or newer” either — below 1.0 a caret is bounded by the
minor, so ^0.7.0 refuses 0.8.0 and you have quietly pinned again. The scaffolder is a one-shot
that writes code you then own, so newest is always what you want.
--about is the venue’s standing query and --terms widen it. Together they decide what appears on
the board.
Scope by names that appear in market TITLES, not by the topic. This is the single thing most worth getting right, and the instinct — describe the subject, then make the description more precise — is the wrong axis. Measured on testnet:
| query | markets |
|---|---|
"book releases" |
0 |
"books" |
1 |
"novel" |
3 |
"Winds of Winter" |
2 |
"Formula 1" |
0 |
"Verstappen" |
1 |
Being more specific made it worse, because specificity is not what the index matches on. A market
is titled “The Winds of Winter publication by 31 Dec 2026”, so it is found by Winds of Winter,
publication and novel — and not by any phrase describing the category it belongs to. Pick the
nouns your markets’ titles actually contain: books, drivers, tickers, clubs, candidates.
Three terms are used; more are accepted and ignored.
Two flags worth knowing: --sovereign builds a venue that never calls us at runtime, and
--skip-scope-check turns off the check below. Skipping it is almost always a mistake — it is the
only thing between you and an evening spent designing an empty board.
The scaffold then tells you whether it worked, before you install anything:
Scope check — testnet (testnet.api.prophecyhosting.com)
about "novel" 3 on the board
term "Winds of Winter" 2 on the board
ON THE BOARD: 4 markets.
ON THE BOARD: 0 means visitors will see an empty venue however good the design is. Fix the terms
and run the command again — it costs ten seconds now and an evening after the venue is designed.
One thing to check before choosing a subject at all: MarketDefinition is a closed union of
seven kinds, and only the two crypto ones are enabled today — stocks.close and the four
football kinds answer template-unavailable (501). Anything else needs an operator and a script.
See Creating markets. A venue on a subject with no live markets and no way to
create them is a themed empty board.
2 · Design
cd book-club
npm install # the venue's own dependencies
npm run dev # http://localhost:3000
You should see a working venue with real markets before you change anything. If the board is empty, the scope check above already told you so — fix that first, because no amount of design fixes an empty board.
Point your AI at the folder and describe the venue you want. You do not need to explain the
rules — the scaffold ships AGENTS.md and a skill in .claude/skills/, so the agent already
knows the components, the house style, and the one rule that matters:
Restyle and restructure freely. Never rewire. The predict/sell path lives inside the kit and in the checkout drawer mounted once in
app/providers.tsx. A page can look like anything and still cannot break trading, because it never touches it.
Before you design, settle who the venue is for — it changes the shape more than any styling choice:
| wants | build | |
|---|---|---|
| trader | the edge | charts, live flow, positions, density |
| player | the game | fast, mobile, one decision per screen |
| debater | to be proved right | comments, follows, resolution receipts |
The kit ships comments, takes and a community feed. If your audience argues about the subject, put that on the page — a venue is a place people talk, not only a board.
What a design pass costs, so you can budget the session: roughly 3k tokens in and 1.6k out per pass, so a ten-pass session is around 46k. Iterating is cheap; it is meant to be.
If you are pasting your access key into a hosted agent, remember that key then lives in that conversation’s transcript and in its sandbox. Fine for a key you already hold and can rotate; think before it becomes the routine way keys get handed out.
3 · Check
The guard and the deploy path live in one tool. Install it once:
npm install -g @prophecy-dev/studio@latest # or: npx --package @prophecy-dev/studio prophecy …
prophecy validate
Refuses a venue that renders no market surface, hand-rolls a quote, unmounts the checkout, quietly reads the global market pool, or sets ink colours it cannot know are legible. It runs again at deploy, so a venue that fails here cannot ship. Fix the cause it names rather than working around it.
It re-runs the scope check too, against the venue as your agent has left it — an agent that edits
app/venue-markets.ts can narrow the board without noticing. The guard decides the exit code; the
scope check only reports.
…and look at it
npm run shot # renders the venue, writes shots/desktop.png and shots/mobile.png
Everything above reads source. This is the only check that sees the page, and it is the one an
agent cannot perform for itself. Three defects survived a green validate on the first outside
build — lone half-width cards in a two-column grid, a browser-default 168px textarea, and a composer
crushed into a 320px rail — all obvious within two seconds of looking, and invisible to every rule.
It also reports what the rendered page admits to: cards on screen, console errors, a hero that buries
the markets, horizontal overflow.
It needs Playwright, which is not bundled because it is a browser:
npm i -D playwright && npx playwright install chromium
Open the PNGs. If you are an agent and cannot see images, say so rather than calling the venue done.
4 · Go live
The venue needs a key of its own. Issuing that first key is creating the venue — there is no separate step:
prophecy login # deploy to Prophecy hosting needs a sign-in
prophecy venue create "Book Club" # prints the key, once
prophecy deploy --key pck_…
If you are an agent, set PROPHECY_ACCESS_TOKEN instead of running prophecy login. Login opens
a browser and waits on a localhost callback, so it cannot complete on a headless box; every command
that needs a sign-in reads the environment variable instead.
export PROPHECY_ACCESS_TOKEN=<your Prophecy access token>
Signed in, deploy puts the venue on Prophecy’s own infrastructure — you never touch a
Cloudflare account and you need no credentials of ours. The built venue is uploaded to Prophecy,
which checks you own the venue and publishes it.
So if you find yourself looking for a CLOUDFLARE_API_TOKEN, stop — the fix is to sign in, not to
find a Cloudflare key. Setting one is worse than not having it: it switches deploy to the
operator path, which uploads directly and never registers the venue in the studio gallery.
Size. Prophecy hosting takes a built venue up to 60 MB, and no single file over 25 MB
(Cloudflare’s own limit). A typical venue is around 12 MB, so this is far more room than a venue
with pictures needs. deploy checks before it uploads and names the largest files if you are over.
If your venue genuinely needs more, talk to us — or self-host.
Without a sign-in it falls back to deploying into your own Cloudflare account. That works, but
sign-in is allow-listed by domain: *.venues.prophecyhosting.com is covered, a workers.dev
hostname is not, so the board renders and login fails until Prophecy allow-lists it. The CLI says so
at the moment it happens.
Both come from @prophecy-dev/studio, installed above.
Live at https://book-club.venues.prophecyhosting.com. Deploying again is the same command; that is
how you iterate.
When it goes wrong
Every row here has actually happened, most of them more than once.
| symptom | cause |
|---|---|
404 on @prophecy-dev/* |
The access key is not configured. Restricted packages answer 404, not 401, so this reads like “no such package”. Check npm whoami first. |
Looking for a CLOUDFLARE_API_TOKEN |
You are not signed in. Set PROPHECY_ACCESS_TOKEN; do not go looking for a Cloudflare key, and do not set one. |
| Venue renders but the board is empty | The query, not the design. Check app/venue-markets.ts and re-read the scope check. |
| The page looks fine and the text is invisible | Hard-coded ink. Use var(--pc-*); validate catches this now. |
<MarketGrid> shows unrelated markets |
It was rendered without an events prop, so it fetched the global pool. Pass useVenueMarkets(). |
npm run dev dies on pino |
An old scaffold. Current ones pre-bundle it in vite.config.ts. Re-scaffold with a pinned version. |
| Cloudflare 500 on asset upload | Deploying the venue’s root wrangler.jsonc. Deploy dist/server/wrangler.json — prophecy deploy does. |
prophecy --version prints usage and exits 1 |
There is no version flag. Not a broken install. |
| The browser shows an old error after deploying | Chrome caches the error page. Add ?x=1. |
For the agent reading this
The venue you scaffold contains its own instructions. After step 1, read in this order and stop when you have what you need — each is loaded only when it is relevant:
AGENTS.md— what is fixed, what is yours, and the traps that only fail at runtime..claude/skills/prophecy-venue/SKILL.md— what to ask the creator before designing, and the rules.reference/venue-types.md— the surfaces each audience needs.reference/kit.md— every component and hook with its verified signature. If a prop is not listed there, do not pass it: a venue is not typechecked at build time, so a guessed prop compiles, deploys, and throws on a visitor’s screen.
Two things worth knowing before you write anything. Get markets from useVenueMarkets(), never a
bare <MarketGrid /> — the bare one fetches the global pool, which is how a venue about books fills
with football. And colour from var(--pc-*) rather than hard-coded values: the shell paints the
page, so a literal colour is a guess about someone else’s background, and that is how a headline ends
up invisible on a page that is otherwise perfect.