Kickgeist

Our World Cup prediction game. 32,252 players submitted 863,000 predictions across iOS, Android and web, in 29 languages, through one tournament.

Who
Social prediction game, three platforms
When
December 2025 to July 2026
What we did
  • Built three clients
  • shipped 92 releases
  • operated it live through the tournament
Result

~10weeks

first commit to global launch

What we got wrong
the first database

PROBLEM The date does not move.

Tippster Media was a social betting platform for the Bundesliga, the European Championship and the NFL. Our founder was its CTO between 2014 and 2017. Kickgeist is our social prediction game for the 2026 World Cup: the same problem, twelve years later.

Kickgeist takes no money from players and pays nothing out. That is what separates the two products, and it is a legal position rather than a preference. It also decides the revenue model: advertising, or nothing.

A tournament product has one deadline and it belongs to somebody else. The tournament ran from 11 June to 19 July 2026, scheduled years before the first commit. A feature that lands after the final is a feature that was never built.

The load follows the fixtures. It concentrates at kickoff and again at the final whistle, when every prediction settles at once. Between matches the system is close to idle.

Scoring is public arithmetic. A player who is scored wrong can verify it and say so publicly.

APPROACH One codebase, no API tier.

Kickgeist runs on iOS, Android and web from one Flutter codebase. It is localised, and it exposes an MCP server for agents. The first release, v25.12.3, shipped on 30 December 2025; the stores went global on 1 March 2026. First commit to global launch took about ten weeks.

An agent pipeline built it.

We built Kickgeist through an agent pipeline rather than by hand. The pipeline drafts, reviews and tests. Intent and the final ruling stay human, and the gates decide what ships. This is the factory we sell, and this product is where we proved the pipeline. It ran in our repositories, under our review process; nothing here shows it in yours.

The shape is recent. Reasoning models became reliable enough to hold a codebase in view and argue about a change before making it. The result below reports what shipped and what missed; it does not isolate the pipeline's contribution.

The cost is not speed and it is worth naming. Review moves from writing code to deciding what is true. A pipeline that is wrong is wrong quickly and at volume, so the gates matter more.

The database is the boundary.

There is no API tier at all. The app talks to Postgres directly, and row-level security is the authorisation boundary. The schema carries 25 tables, 51 database functions and 48 policies. Scoring, referrals and challenge settlement run inside the database, so a tampered client cannot bypass them. The cost is a failure mode that says nothing. A new table needs an explicit grant beside its policy; a policy alone returns a silent 403.

Store review is the risk.

Two of the three clients ship through a store review queue that nobody on the project controls. That queue sits between a finished build and a player, and it does not care about the fixture list. Each client adds a release path and its own platform behaviour to test. The web client is the only one of the three with no queue in front of it. Fastlane covers both store paths: 15 lanes for iOS and 17 for Android, from certificates to phased rollout.

Localisation does not retrofit.

Retrofitting a string catalogue into a shipped view layer means editing every screen. Doing it first costs something smaller: no string can be written directly into a view, ever. Doing it later costs the same edit, with the tournament already running. Kickgeist ships 29 languages across 35 locale files; the store listings and screenshots move with them.

The standard is local, not translated. Every language that distinguishes register uses the informal one. Each carries its own terminology table, and Brazilian Portuguese is rewritten rather than inherited from European Portuguese. Which locale shipped when was a business decision. Indonesian and Brazilian Portuguese went first, scored on smartphone reach, football engagement and regional ad rates.

The agent runs elsewhere.

An MCP server is an integration point: an agent client connects to it and calls the tools it exposes. The game owns its data and its rules; the client does the reasoning. Nothing about that path depends on a model shipped inside the app. The server exposes seven tools behind OAuth 2.1 and static API keys. Agent calls mint a per-user token and run under the same row-level policies as a human player. The cost is a second public interface to specify, version and secure.

No API tier, one backend.Three clients — iOS, Android and web — call Postgres directly. There is no API tier between a client and the database: row-level security is the authorisation boundary, with 25 tables, 51 database functions and 48 policies deciding what a request may do. An MCP server exposes seven tools behind OAuth 2.1; an agent call runs under the same row-level policies a human player does.iOSAndroidWebAgentAPIMCP serverPostgresrow-level securityNo API tier, one backend.Three clients — iOS, Android and web — call Postgres directly. There is no API tier between a client and the database: row-level security is the authorisation boundary, with 25 tables, 51 database functions and 48 policies deciding what a request may do. An MCP server exposes seven tools behind OAuth 2.1; an agent call runs under the same row-level policies a human player does.iOSAndroidWebAPIPostgresrow-level securityMCP serverAgent
Three clients, one backend, no API tier. Row-level security is the boundary for a human and for an agent alike.

RESULT 863,000 predictions, three platforms.

Kickgeist on a phone: a live match card at 65 minutes with the score and the pick already made, a row of upcoming matches to predict, and an experience total with a bar to the next tier.
Live scores arrive from a sync that runs every two minutes.
Kickgeist on a phone: five prediction groups in a list, each a monogram tile with a name and a member count, over a button that creates another.
Signup is creating a group or joining one, so the invite loop is structural.
Kickgeist on a phone, one group opened: tabs for overview, predictions, rankings and challenges, then the group's total points, average accuracy and predictions made, and its members beneath.
Scoring settles inside the database, so a tampered client cannot move a rank.
Kickgeist in English. The same three screens ship in 29 languages.

Install it from the App Store or Google Play, open it on the web, and switch the locale. Those three are settled without asking us anything.

Built once, shipped globally.

~10weeksfirst commit to global launch
measured 5 August 2026
32,252registered users
measured 29 June 2026
863,000predictions submitted
measured 29 June 2026

All three clients shipped, localised, with the MCP server in place. 32,252 players submitted 863,000 predictions across 14,880 groups, and 45% of that base arrived in the week of 8 June 2026.

Three things missed. The group-first architecture produced groups; it did not produce growth. Multi-member groups held 52.9% of players, 278 groups reached eleven or more members, and one reached 152.

The invitation loop did not close. The viral coefficient measured 0.013 against a 0.4 benchmark. Monthly actives fell 60% to 80% within thirty days of the final. The oldest pre-tournament cohorts retain about 4.2%.

The third miss is the revenue model. The assumed rate per thousand ad impressions was roughly eight times the rate it actually earned. Advertising is the only revenue line, so that assumption was the business model. The absolute rates are not published here; the ratio is the part that transfers.

We had planned to build once, operate one tournament and wind down. The three misses replaced that plan. One ranked, localised listing goes quiet between tournaments and wakes for the next one. A new listing would need a new audience.

92 releases shipped, 18 of them in January 2026 alone. Those releases carry 417 merged pull requests, gated by 2,032 automated tests and 231 golden images. The crash pipeline auto-filed 1,450 reports and deduplicated them by stack frame; one fix closed 185 at once.

The figures in this debrief were measured on 5 August 2026.

We got the database wrong.

Signups and predictions failed with a connection error, and only sometimes. Nothing correlated: not the hour, not the platform, not the load. The first reports read as flaky phones.

They were not. The first releases ran on Cloudflare D1, and D1 is SQLite underneath. SQLite takes one writer. The two-minute fixture sync batched over a hundred statements behind a single lock and held it for two to five seconds. Every request arriving inside that window waited, and a signup that waits long enough fails.

We migrated to Postgres in one move, in January 2026, before the stores went global. A queue in front of the writer would have hidden the symptom and kept the cause. The storage engine did not fit the write pattern, and that is not something a workaround repairs. The migration deleted more code than it added, and the custom auth layer and the API tier went with it.

We should have caught it before players did. An outside review had recommended leaving D1 before the build started, and we published our reasoning for staying. That reasoning was sound about the numbers it used. The review reached the right conclusion anyway.

The axis was the mistake. We measured how many people arrive at once, when what mattered was how long one writer holds the lock. What went right was the second decision rather than the first. The failure surfaced in January, on a base small enough to absorb it, and the stores went global on 1 March 2026.

The full account is a note of its own: the review, the reasoning we published against it, and the rule we took away.

Tell us what is in the way.

Say what you're building and where it stopped.

hello@beevelop.com

Copy it into your mail client if that is easier.

The draft opens with

  1. What you are building
  2. What is in the way
  3. The stack
  4. When you want to start

The contact page says who answers, and what the reply contains.

Read the The planning matrix debrief next.