Loopfund MVP: spec and status
Updated 2 September 2026, 7:50 pm CT. Three internal pages: this one is what we're building and where it stands (product, phases, decisions, next steps); /implementation is how the machine runs (architecture, the loop, money plumbing, observability, for engineers); /brand is how we talk and look (voice, vocabulary, pitch, palette, type).
What Loopfund is
Kickstarter, except the money can never reach the creator. Backers buy compute; that compute runs a hosted machine where AI agents build the creator's game against a visual bar the creator set; every dollar is visible as it is spent; whatever isn't spent is refunded. The creator's cash-out is $0 by rule, which is what lets strangers fund a project from someone they've never heard of.
Internally the build method is a Gauntlet Loop: one lead agent splits work between builder agents and fresh-context critic agents; critics compare the build blind against reference frames and can never edit; the loop runs unattended, wave after wave, until it ships, is paused, or the pot is empty. Three invariants hold everywhere:
- Critics never edit. Enforced by a tool hook, not by prompt.
- Nobody interrupts a wave. Notes from the creator land only at wave boundaries.
- Spend is capped by the pot. The run's model key has a hard dollar limit equal to what backers paid, minus a reserve.
Public copy says "project", "build", "round", "judge". "Loop", "wave", "critic", "harness" and "sandbox" are for this page and the implementation notes.
Status board
- Browse, search, category filter
- Campaign page with real pot, backer wall, split
- Stripe Checkout, live mode, 85/15 ledger
- Receipt page
- Creator sign-in (Clerk, dev instance), wizard, drafts, dashboard, direct uploads
- Two seeded campaigns with AI-generated pitch video and target frames
- Docker harness: Claude Code 2.1, Chromium, hooks,
loopdwave supervisor - Claude Code talking to OpenRouter by direct model slug (Fable 5.1, Haiku 4.5) from inside the image
- Critic edit-denial hook, event log, per-agent token attribution from transcripts
- Start run, Booting, Run console, Watch pages are static screens
- No orchestrator: nothing mints a key, boots a machine, or writes a wave yet
- Refunds are manual in Stripe
Phases
Re-sequenced on 2 September to Miguel's priorities: money and posting first, then the machine, then run-state surfaces. Checked items shipped; half-filled boxes are partly done.
Phase A: backers can pay live
- Stripe live account, hosted Checkout, webhook with signature check
- Ledger rows per payment:
pledge_captured,platform_fee,pot_credit, idempotent - Campaign flips FUNDING to FUNDED at threshold
- Receipt page reads the session, falls back to Stripe if the webhook is late
- Homepage, campaign page, checkout wired to real data
- Refund tooling (ops endpoint, pro-rata at ship/cancel, full at never-started)
- Stripe Tax
Phase B: creators can post live
- Clerk sign-in (GitHub, Google, email code), user row created on first sight
- Wizard: project and video, the bar, funding and credentials; drafts saved per step; Save and exit
- Direct-to-storage uploads for pitch video, poster, reference frames (private bucket)
- Publish requires a verified GitHub account; clear Connect GitHub path when missing
- Creator dashboard: drafts, live campaigns, what each still needs
- Production auth: Clerk production instance on
clerk.loopfunder.com, GitHub + Google with our own OAuth apps, live keys on Vercel - Edit a published campaign (currently drafts only)
Phase 0: harness in a box gate passed, first full run pending
- Docker image with Claude Code, Chromium, capture tools, hooks,
loopd - OpenRouter reachable from inside the image with direct model slugs; presets not available on this account, so slugs are pinned in env
- Critic guard hook, event log hook, StopFailure classification (billing vs transient)
- Transcript parser attributes tokens per subagent via
agent_transcript_path - The $25 run: Matt Shumer's prompt against a folder of reference frames, at least 3 unattended waves, stop reason
pot_empty, transcript spend within 5% of the key's usage - OpenRouter Management key so runs get their own capped key (Miguel: keys page, Management API section)
Phase 1: hosted machine and brakes
- E2B template from the same Dockerfile (Daytona as tested fallback)
- Run orchestrator:
POST /api/runs/startmints key, boots machine, injects goal and bar;loopdposts wave results back - Pause and resume with disk and session intact; key disabled while paused
- Chaos tests: 10 minutes of 429s, key limit to $0 mid-wave, machine killed and recovered from GitHub
- Thin pass-through proxy in front of OpenRouter for per-call reserved/settled rows (decision 5)
Phase 2: run-state surfaces
- Start run, Booting, Run console read and drive real run state
- Watch page from workbench snapshots on a CDN, verdict log, spend meter
- Episodes as GitHub Releases per wave; playable build hosting for backers
- Creator nudges queued to
NUDGES.mdat wave boundaries; Pause, Ship, break-glass - Plateau checkpoint: 5 flat waves pauses and notifies; auto-cancel after 14 days of creator silence
Phase 3: later
- Backer votes between rounds
- Stripe Identity above a pot threshold; manual review queue for the first 50 campaigns
- Categories beyond games; Twitch or VOD pipeline from the shot archive
Surfaces and mocks
Every screen was mocked first, then rebuilt as HTML, then wired to the API where the backend exists. Tags say where each one is today.
| Route | What it is | State |
|---|---|---|
| / | Browse: featured project with pitch video, funding grid, search and categories, where the money goes | live |
| /projects/:slug | Campaign (server-rendered head for link previews): pitch, target frames, pot, split, backer wall, creator card, FAQ | live run sections hidden until a run exists |
| /checkout | Amount, email, backer-wall choice, hands off to Stripe | live |
| /thanks | Receipt: what was paid, split, next steps | live |
| /creators | For creators: what you bring, what you get, four steps, FAQ | live |
| /dashboard | Creator's drafts and live campaigns | live |
| /onboard-goal, /onboard-bar, /onboard-fuel | Wizard: project and video, the bar, funding and publish | live |
| /onboard-start, /onboard-boot | Start run confirmation, boot sequence | mock |
| /console | Creator run console: artifact, shot comparison, nudge box, wave log, controls, spend by role | mock |
| /watch | Public live view: workbench, judge grid, verdict log, spend | mock |

















State machine
One run per campaign in the MVP. Three resources are bound to a run and move together: the machine (disk and memory), the OpenRouter key, and the Claude Code session. Campaign states up to FUNDED are live today; everything from PROVISIONING on is Phase 1.
| State | Machine | Key | Session | Backers see |
|---|---|---|---|---|
| FUNDING / FUNDED | none | none | none | pot meter, goal, creator card |
| PROVISIONING | create from template, clone repo, write bar and agent config | minted, limit = pot minus reserve | none | Booting |
| RUNNING | running | enabled | one long session, resumed each wave | workbench, judge grid, verdicts, spend |
| PAUSED (pot) | paused with memory | disabled | frozen | Paused, add funds to resume |
| PAUSED (provider) | paused | enabled | frozen | Provider outage, retrying |
| PAUSED (creator / plateau) | paused at wave boundary | enabled | frozen | Paused by creator |
| SHIPPED | final capture and build to CDN, deleted after 7 d | disabled then deleted | archived | playable, final episode, refund notice |
Money
Charge now, refund if it never starts. A backer pays through Stripe Checkout and is charged immediately. 15% is the all-in fee (card processing, gateway, hosting, platform); 85% is credited to the campaign's pot. Loopfund is merchant of record; nothing is transferred to the creator, ever. Unused pot is refunded pro-rata at ship or cancel; if a run never starts, everything is refunded in full and Loopfund absorbs the card fees.
The key limit is not the pot. When a run starts, an OpenRouter key is minted with a dollar limit equal to the pot minus a reserve for in-flight requests. Spend is reconciled from three sources: the key's usage counter, Claude Code transcripts (per agent), and, from Phase 1, per-call rows from the pass-through proxy.
Ledger rows are the source of truth: pledge_captured, platform_fee, pot_credit today; key_minted, spend_reserved, spend_settled, refund with the run. Every row carries a Stripe or OpenRouter reference so the chain can be audited end to end.
| Rough unit cost of a wave | |
|---|---|
| Typical wave, Fable 5.1 everywhere | about $41 |
| Fable critics, Opus builders | about 35% less |
| $500 goal after fee | about 10 waves |
| Hosted machine, 4 vCPU / 8 GB, no GPU | cents per hour, inside the fee |
Architecture
Static HTML pages fetch JSON from Vercel functions. Supabase holds the data and files. Stripe holds the money. Clerk holds identity. The machine is a Docker image today and an E2B template next; it talks to OpenRouter directly with a per-run key whose limit is the pot. The orchestrator is the one missing service.
The build loop, as it runs
loopd is the wave supervisor and lives outside Claude Code, because Claude Code's own stop hook can only block a fixed number of times and the loop must be unbounded. Each wave: loopd appends any queued creator notes to NUDGES.md, starts Claude Code with --resume on the run's session at max effort, waits for the lead to write WAVE.json (score, best frames, cost), captures the workbench, tags a GitHub Release, posts the wave to the API, and starts the next one. It stops only on pot_empty, a creator pause, or a provider failure it can't route around.
- Lead reads
GOAL.mdand the bar, splits the work, spawns builders and critics, decides when a wave is done. - Builders edit the repo. Model: Fable 5.1 by default; Opus 5 on the cheaper plan.
- Critics start with fresh context, capture the same named angles from the build and the bar, and pick the better frame blind. Always Fable 5.1. A
PreToolUsehook denies them every edit tool. - Hooks log every subagent start and stop, every tool use, and every API failure to an event log the transcript parser reconciles against OpenRouter's usage counter.
Decisions
| Topic | Decision | Why |
|---|---|---|
| Hosted machine | E2B first, Daytona as tested fallback | Most-used Claude Code templates, memory-preserving pause, Node SDK. The 24 h runtime clock is handled by loopd. |
| Pledge model | Charge now; refund in full if never started | Miguel accepts eating card fees on campaigns that never start. |
| Fee | 15% all in | One number backers can read before paying. |
| Critic model | Always Fable 5.1 | Builders may be routed cheaper in experiments; judges never. |
| Per-call ledger | Pass-through proxy in front of OpenRouter, Phase 1 | Exact per-call cost and reserve/settle rows; optional via env so Phase 0 talks to OpenRouter directly. |
| "Escrow" | Never in copy. "Held for this project." | Legal shape and plain language. |
| Session strategy | One long session per run, resumed each wave | Closest to the runs the method was proven on; WAVE.json and the repo are the durable memory if a session must be rebuilt. |
| Backer input | Creator-only notes in MVP; votes later | Nudge text is a prompt-injection surface. Creator is semi-trusted; backers get no free text yet. |
| Plateau | 5 flat waves pauses and notifies; advisory; auto-cancel after 14 days of creator silence | Never binding while the creator is responsive; never auto-kill an improving run. |
| Reference IP | Copyrighted references allowed, stored privately, never published | Homages are fine; copies are not. The judge grid shows our frames only. |
| Auth | Clerk (GitHub, Google, email); GitHub required to publish | GitHub is the verifiable creator credential backers see. |
| Models via OpenRouter | Direct slugs pinned in env, not presets | Presets aren't available on this account; slugs give the same routing with less indirection. |
Open questions
- Legal shape of prepaid balances. "Prepaid compute, earmarked, refundable if unused" avoids the crowdfunding money-transmitter shape, but stored-value and gift-card statutes need counsel. Refund SLA copy: initiated within 5 business days; Stripe settlement time disclosed.
- Sales tax. Digital service; Stripe Tax not yet on.
- Creator KYC threshold. GitHub OAuth only in MVP. At what goal size does Stripe Identity kick in? Proposed: above $2,500, plus manual review for the first 50 campaigns.
- 24-hour machine clock. E2B caps continuous runtime; pause/resume around it vs Fly Sprites which don't cap. Decide on Phase 1 chaos-test results and price.
- Workbench serving. Snapshots on a CDN, never a live tunnel into the machine. Snapshot cadence and cost to be set.
- Playable hosting for backers. Needs a static build target and signed URLs; implied by "early access" but not designed.
- No GPU. Stills and captures are fine; frame-rate claims aren't reproducible. Wizard says so; is that enough?
- Abuse and content policy. Egress allowlist and CPU quotas are planned; what the loop is allowed to build follows Anthropic's usage policy as we are the API customer. Written policy pending.
- Ops at 3 a.m. A run PAUSED(provider) for hours: retry, notify the creator, never refund on a provider outage. Runbook to write.
- Capture shrinkage. Not an issue with charge-now, but declined top-ups mid-run need a UX.
- Cloud-agent PR #1. Close without merging; add branch protection so agents open PRs and can't deploy.
Next steps
- OpenRouter Management key MiguelFrom the keys page, Management API section. Lets each run mint its own capped key instead of sharing the inference key.
- The first real Gauntlet LoopRun the harness locally for $25 against a folder of reference frames. Pass: 3 or more unattended waves, critic denied at least once, stops on
pot_empty, transcript spend within 5% of key usage. This is the Phase 0 gate. - One real backingA $5 charge on Claude of the Wild with a real card, watching the ledger, wall and receipt land.
- E2B template and orchestratorSame Dockerfile as a template;
POST /api/runs/start; loopd posts waves back; pause and resume with the key disabled. - Run-state surfacesStart run, Booting, Console and Watch read real state; watch page from snapshots; releases per wave.
- RefundsOps endpoint for full and pro-rata refunds; auto-cancel timers for 60 days unfunded and 14 days unstarted.
- Deferred on purposeBacker votes, Stripe Identity, categories beyond games, Twitch.
Tests that matter
| Test | Phase | Pass condition |
|---|---|---|
| Stripe payment becomes pot | A | ledger chain pledge_captured, platform_fee, pot_credit unbroken; wall and receipt update passed with a signed synthetic event |
| Critic never edits | 0 | hook denial count above 0 in a run where the lead tried; zero critic-authored diffs |
| Unbounded loop | 0 | 3 or more waves with no human input; stop reason pot_empty |
| Spend attribution | 0 | transcript total within 5% of OpenRouter key usage |
| Empty-pot pause keeps disk | 1 | workspace hash identical before pause and after resume; session resumes |
| 429s don't kill the harness | 1 | zero loopd restarts; at most one wave retry; provider handoff visible in the ledger |
| Key can't spend past the pot | 1 | OpenRouter usage at or under limit; reserve covers observed overrun |
| Nudges land at wave boundaries only | 2 | NUDGES.md modification times match wave starts only |
| Backers never see break-glass | 2 | watch payload contains no break_glass events |