BLITZ

Strategy · 8 Sep 2026

Discovery first.

The feed for what people make with AI — pictures, videos, graphics, web pages and games — built for the AI renaissance. How the first minute gets designed, how cards 18 to 5,000 arrive without a content team, when the ranker is allowed to promote, and what has to be true before a single launch post goes out.

Written by a thirteen-agent panel: one researcher who played the live feed end to end, five planners with distinct lenses (feed mechanics, discovery, AI-generated content, mobile craft, growth), three judges, two red-teamers, one synthesis. The discovery plan won two of three judges; the growth plan came second and its instrumentation and seed-cohort bets were grafted in.

≥ 50%
Position-0 hold
First-visit sessions on card 0 that last 5 s server-side without a crash.
≥ 8cards
Median depth
Cards per visit, from the host-stamped visit ordinal.
< 10%
Idle-timeout exits
Share of hosted-card sessions ended by the 20 s sweep — today it is every session past 20 s.
≥ 10%
D1, day 21
Browser arrivals from the seed cohort back the next day, interval written beside it.
01

Where we are

Blitz today is seventeen cards by one author, playable in about two minutes, opening on the five least attractive cards in the catalogue and ending on a dimmed button over a third-party page that does not fit a phone. The first words a visitor reads are SESSION, APM, DECISION, FPS and INPUT·PM, and on twelve of the seventeen cards those cells show dashes for the entire visit. On those same twelve cards the host cannot see a single touch, so a white "Paused" modal interrupts people mid-play at twenty seconds, ends their telemetry session, and caps every web-card dwell we have ever recorded at the timer. Two of the three rail actions do nothing, there is no way back on a phone, browser Back lands on about:blank, and a second visit is the same seventeen cards in the same order with no memory of the first. Underneath, the infrastructure is better than the product: server-trusted telemetry lands (61 of 61 batches accepted in a two-minute tour), the ranker is fabrication-proof by construction, share links open playing in under six seconds, and none of it has anything to rank because there are tens of sessions a day and one deck.

Founder's amendment, 8 September. The linked site Deepstate opens the feed and no editorial position is immune to the vote any more (migration 0008: with twenty real votes and a hold-rate deficit, card 0 moves like any other). The founder rates the static catalogue — the five wasm games and the eleven generated toys — well below linked sites, and wants what people link, not what a generator makes, as the primary supply. Bet 6 (harvest the embeddable web, concierge the makers) therefore outranks bet 5 (the factory), which runs only as the sixty-card probe until its numbers argue otherwise. Sites that refuse framing are accepted through mirror mode (migration 0010, docs/EMBEDDING-A-SITE.md): the page is served from the deploy's second origin without its framing headers; chess-program-tutor.netlify.app is the second editorial card that way. Mirroring sets aside a site's own clickjacking protection, so it is for sites whose owners want them in Blitz.

Observed on the live site on 7 September, before the chrome fix (viewport between the bars, meta strip below the card) shipped that evening. The other findings stand.

First paint: the chrome, a spinner, and the words SESSION · APM · DECISION · FPS · INPUT before any content.
Card 0 as first seen: Flick Aim, a test fixture, with the meta strip and rail painted over its lower third (the chrome fix shipped the same evening).
The one linked site: a desktop layout at 375 px, its dialog half under our Tip button, BLITZ dimmed — the end of the feed with no message.
02

The thesis

Cards 18 onward come from a generator the founder runs and a web the founder harvests with the maker’s yes; none goes live faster than the feed can judge it; and until a hundred networks a day are voting, the feed’s job is composition, not ranking.

The thesis carries its own test, in section 8: if seventeen cards under the new chrome and composition do not move position-0 hold and depth, no number of generated cards will.

Founder's positioning, 25 September — this is the headline, and everything else is read under it. Blitz is a feed of content, much of it made with AI: pictures, videos, graphics, web pages and games. AI-generated content is getting better every month and is about to explode — we are entering an AI renaissance — and Blitz is the feed built for it. Blitz is not a gaming platform, and "things you play" is not the headline anywhere: a game is one kind of card among many, beside the image, the clip, the graphic and the page.

What sets the feed apart is what it trusts. TikTok's ranking is built on a watch-time signal it can see end to end; Blitz cannot see inside a card that runs its own code and refuses to trust what a card says about itself, so its only currency is what a phone gives away honestly: whether you stayed, and whether you pressed the button. The next card is a different kind of thing, and one button is the whole interface. StumbleUpon was the existing web filtered by people, and it died when the web stopped fitting the frame; Blitz's supply is the part of the web that fits a thumb, plus a generator that makes more of it on demand, plus the people who made those things, who can be shown host-measured numbers nobody else will give them. Roblox is a place you install, join and learn; Blitz is one URL, no install, no sign-up, playing before the network answers. For the next twelve weeks the thesis is one sentence: cards 18 onward come from a generator the founder runs and a web the founder harvests with the maker's yes, not from creators who have not arrived, and none of them goes live faster than the feed can give it a verdict; and until a hundred networks a day are voting, the feed's job is composition, not ranking — a different kind of card every BLITZ, shuffled per visit from what this phone has already held, looping, never ending on a dead button, and remembering you when you come back. The thesis carries its own test, written in section 8: if seventeen cards under the new chrome and composition do not move position-0 hold and depth, no number of generated cards will, and that is a verdict on the feed, not on the catalogue.

03

The first minute

This is the designed first session on a phone, cold, no account. Timings assume the measured numbers (85 KB bundle, hosted toys painting in 100–260 ms) with the boot no longer gated on Supabase.

  1. 0.0 s — the shell

    Dark ground; the feed is dark-only (the light shell around dark cards goes; the landing keeps both themes). Top bar: the BLITZ mark left, the person icon right, and a thin progress line between them — the seventeen dots become a line because decks will soon be thirty cards long. Centre: the slide-thumb of card 0 (its thumbnail and title) drawn from the bundled catalogue, not a spinner. Bottom bar: a 36 px back chevron on the left, dim because there is nothing behind it yet; the violet BLITZ button, centred, with a 48 px hit area; nothing else. Under BLITZ, the word "next" in 10 px caps — shown until the first press ever on this device, then never again. That word plus the glowing button is the entire tutorial. No stats cells, no Developer-toolbar button, no "+", no coach marks.

  2. ~0.5 s — card 0, Ripple Tap

    The iframe paints before anonymous sign-in and feed() return; the transport queues batches until the session exists. Ripple Tap's own ghost pointer is already rippling the grid. Any touch anywhere produces a ripple; nothing can be failed. The meta strip sits below the surface, never over it: "Ripple Tap · @blitzlabs · toy" on the left, Like and Share as two 40 px icons on the right, and a small "…" beside them that holds Report (and, later, Remix). Ripple Tap is the opener for every first visit. The Gravity Well comparison waits until after the retention bar: 100 first visits per arm is the whole seed cohort's card-0 signal, spent on a ±10-point answer, and the position-0 bar cannot be read across a split.

  3. ~6 s — first BLITZ

    320 ms slide; card 1 is already running because N+1 is preloaded. Stack Drop: a game with a score, ten-second rounds, instant restart. The word in the strip is "game". The first tap drops a block; the result is visible. The back chevron is now live. Pressing it returns to Ripple Tap, still running. The chevron is the way back and the only one: browser history stays one entry (replaceState, for the reason share.ts documents), because hardware Back and the iOS edge-swipe arrive as the same popstate and cannot be told apart, so history-as-deck would let an OS gesture move the feed and end sessions mid-drag on cards that own the left edge (Bloom's wind, Colour Split, Echo Lines). Whether one sentinel history entry so that Back steps one card is wanted, knowing it must also answer the edge-swipe, is a one-sentence ruling the founder gives before bet 1 ships; the default is no.

  4. ~15 s — second BLITZ, the daily slot

    Slot 2 is "New today": the day's audition pick — a factory toy, a remix, or a harvested sketch under 30 lifetime votes — with the word "new" in the strip, drawn by a lottery that rotates by provenance from day to day, which the ranker as written cannot do (section 6). When nothing qualifies it is Gravity Well. This is the one slot every visitor shares, and the reason tomorrow is different from today.

  5. 15–60 s — slots 3 to 8

    A shuffle seeded by this device and today's date, under one hard rule: no two consecutive cards share a verb family (timing, ambient, tap, drag, plan — a normalised map, not the raw verbs), with same source and same template as penalties the sequencer avoids when it can and drops when it cannot, because eleven of seventeen cards are hosted and a rule that forbids hosted adjacency cannot be satisfied on the catalogue it runs on. Tide Pool (ambient) then Orbit Cut (timing) then a sketch harvested from its platform's embed page (site, by its platform, with a permanent "Open" at the right of the strip) then Colour Split (this-or-that) then Echo Lines then Pulse Lock. The five wasm test games appear no earlier than slot 8, as variety rather than first impression, unchanged except that their idle pause is a dim of the surface plus one line, "Still there? Tap to continue", and a tap within 60 s continues the same session — a pause at 20 s and an exit at 80 s under one session id, which is a change to the host, the worker and the agent, not a flag. Hosted toys never pause: each carries the snippet and the host sees every touch. A linked site has no timer at all and no alarm either: from outside the frame the host cannot tell a working page from a blank one, so blankness is decided once, at admission, on a phone-sized render, re-checked nightly, and the strip's "Open" is there all the time.

  6. ~60 s

    The visitor has seen eight to ten different kinds of thing. A Like on Gravity Well was recorded. A Share from Tide Pool said "Play this." (not "See if you can beat it", which is now reserved for cards that have reported a score); when the OS sheet closes, one line offers "Want credit when they play it? Claim a handle" — the only account ask in the first minute, and only after intent.

  7. The end of the deck

    After the last card, a hosted end card, not a dimmed button: "That was all 24. You've seen 24. New card tomorrow." with Again (a fresh shuffle minus what you held), Share Blitz, and Link something for accounts or Claim a handle for guests. BLITZ on it fetches a new deck minus what you held; the button never dims and aria-label "End of feed" goes away. If the visit arrived inside an in-app webview — Reddit's, Discord's — the end card's first line is "Open in your browser", with the address to copy, because a webview's storage is not Safari's or Chrome's and a return from the home screen would be a stranger to us.

  8. The second visit

    Nothing in the frame can remember you: hosted cards run in an opaque origin under a CSP with no connect-src, so localStorage throws and there is no server. The host remembers instead. blitz-card.js carries a save channel, and Stack Drop's strip on a return reads "your best: 14" from state the host kept for that card on this device; a drawing tool keeps the drawing, a puzzle keeps its progress. A returning device opens on "New today", then the card it held longest, then the unseen interleaved one-to-two with what it held, weighted towards the verb families it stayed for — all from this phone's own record, which the server never reads. After the second held session ever on this device, one card in the deck is an add-to-home-screen card (beforeinstallprompt where it exists, the two-step iOS instruction where it does not), shown once. Accounts get one web push a day, "New card: <title>", from week 3.

  9. Removed from the screen

    , named: the five bottom-bar stat cells and their shrink rules; the Developer-toolbar button (the toolbar stays behind ?dev=1); the MIG verb chips (verbs stay in the data; one plain word — toy, game, puzzle, site, new — renders); the Tip button and its "Active Mechanical Seconds ledger" toast; the "Ready — compiled and paused at tick 0" badge on the incoming slide; the "Compiling…" spinner card; the <code> crash string (visitor copy: "This one did not load. BLITZ for the next."); the light feed theme; the "+" for anonymous visitors; the white Paused modal; the dimmed BLITZ at the tail; the floating rail over the card surface; on the landing page, "Create account" as the primary CTA, the "Dev tools" footer link, the "<1s to first play" fact, and the Cannae, data-explorer and quiz promises; in Supabase, email confirmation — after the concierge makers' handles are reserved, because an unverified account is free and a handle is the only thing in it worth squatting.

04

Bets, prioritized

Ordered by expected retention impact per week of work. Every bet is startable today by one engineer with an agent; none needs a second engineer to begin. Costs: days means under one working week; weeks means two to four; months means a pipeline built in days that runs for the quarter.

Bet 1

The first-minute floor: consumer chrome, a way back, boot before the network, an end that is a sentence

days

What

First, confirm the live build is 6e5c69d, which carries b98c77b (viewport between the bars, meta strip below the surface); if a fresh tour still shows the strip over Colour Split's palette, it is a deploy problem and gets fixed before anything else. Then, in App.tsx, TelemetryDisplay.tsx and GameViewer.tsx: the stat cells, the toolbar button, the "+" for anonymous visitors, the tick-0 badge, the spinner card, the crash <code>, and the light feed theme move behind ?dev=1; the verb chips become one plain word; Like, Share and a "…" holding Report move into the right end of the 56 px strip; Tip goes; share text follows the card's class. A 36 px back chevron left of BLITZ that calls the Carousel's existing goTo(i-1); replaceState stays exactly as share.ts reasons it should (one entry, Back leaves the site), the chevron is logged as a ui_event of kind back and its exit recorded as SWIPE_DOWN, and the sentinel-entry question goes to the founder in one sentence before this ships. The first minute exists nowhere in data today, so one commit rewrites card_editorial (Ripple Tap 0, Stack Drop 1, the wasm games at 8 or later), MOCK_GAMES, feed.json, tools/seed-cards.mjs and the e2e fixture together, so the bundled boot and the server show the same card 0. Card 0 mounts from the bundled MOCK_GAMES the moment the bundle executes; anonymous sign-in runs, then feed() — not before it, as App.tsx does today, or the seed and the actor arrive null — and the server order is spliced in behind the active index only, with a second reconcile when sign-in lands late (30 an hour per IP; carrier NAT will 429, and the transport keeps queueing). A share link /c/<slug> whose card is not in the returned page resolves the card from the bundle or a card_public select by id and splices it at index 0 ahead of the deck — today feed(n: 60) plus Math.max(0, findIndex) in Carousel.tsx opens Ripple Tap for any card past the page and stamps entry_surface='share' on the wrong card, which is every concierge and remix link there will be; n rises and an e2e case covers a slug outside the page. feed.json joins the service worker's shell URLs; a host-stamped boot_ms (navigationStart to first running) rides the first batch of visit_ordinal 1. The end card ships as a hosted card of kind end, with the webview line and the add-to-home-screen card beside it. On the landing page the primary CTA becomes "Open the feed" linking /c/ripple-tap?via=landing, "Create account" moves to the creators section, the three real card names replace the invented ones, the "<1s" fact becomes the measured p50 or nothing, and Supabase Confirm-email is switched off once the concierge makers' handles sit in reserved_handle. The e2e helpers' slots-0-to-4 contract moves to a fixture.

Why

Every visitor today reads telemetry labels before content, cannot go back, and ends on an error state with no error message. The repo's own comment states the rule the deployed build violated. These are the cheapest points of position-0 hold and depth there are, and every later bet ships new cards under whatever chrome exists.

Metric

Position-0 hold (server span ≥ 5 s, no crash) for first-visit actors ≥ 50%; SWIPE_UP within 3 s at position 0 < 25%; median cards per visit (max visit_ordinal per visit_id) ≥ 5 before composition; SWIPE_DOWN share of exits > 0 on touch devices (structurally 0 today); boot_ms p50 < 1,000 ms on visit_ordinal 1 (headless proxy < 900 ms across three fresh contexts); a share link to a card outside the page opens on that card (e2e) and entry_surface='share' lands on the card that was shared; visits reaching the end card ≥ 25%, and the loop taken (a position-0 session with visit_ordinal past the deck length) more often than TAB_HIDDEN on the last card. Every number read against the baseline row written on day 1.

CostDays (six to eight).Depends onThe bet 3 migration, for boot_ms, liked and back to land at all — ingest_batch ignores payload keys it has no column for, it does not reject them — and the founder's ruling on the sentinel. Everything else, nothing.

Bet 2

blitz-card.js, the idle policy and the save channel: make web cards visible to the host, and let the host remember for them

days

What

A ~1 KB dependency-free snippet, inlined — the CSP's script-src 'unsafe-inline' has no host source, so a hosted card cannot <script src> it; the lint looks for the inline marker, and only external sites load it by URL. On the first requestAnimationFrame post {blitz:'ready'}; on every pointerdown post {blitz:'touch'} throttled to 4 Hz (WebCardHost.onWindowMessage already resets lastInputAt and counts it); expose window.blitz.beat(); set touch-action:none; -webkit-touch-callout:none; user-select:none on the root so a long press never pops the iOS callout inside the frame. And a save channel, because a hosted frame is an opaque origin (sandboxTokensFor gives /cards/* allow-scripts only) where localStorage, IndexedDB and cookies throw, and connect-src 'none' leaves it no server: {blitz:'save', data} (4 KB cap, throttled to one a second) that the host writes to the parent's localStorage keyed by card id, and {blitz:'load'} answered at ready with whatever it holds — per-actor Supabase rows later, for accounts. window.blitz.save(obj) and window.blitz.load() wrap it. The host measured what it stores, so the strip may show "your best" from it; nothing in it ever ranks. Add the snippet to all eleven toys under public/cards and to the factory template; Stack Drop and Tile Flip keep a best score through it as the first two users. Idle policy in Carousel.tsx: hosted cards keep the 20 s sweep (a hosted card that sends nothing is a lint failure, not a policy case); external cards drop the timer entirely — the session ends only on BLITZ, back, TAB_HIDDEN or DESTROYED; wasm cards pause at 20 s (pause('idle'), a dim plus one line at the bottom of the surface) and exit at 80 s under the same session id, which means GameHost.resume() stops minting a uuid, GameWorker and TelemetryAgent learn the paused-not-ended state, and tests/e2e/telemetry.spec.ts, which asserts the current contract, is rewritten — a day of the budget, not a flag. External frames get a transparent background so the slide-thumb shows behind them and a permanent "Open" at the right of the strip, target=_blank from the parent DOM (the frame itself never gets allow-popups). The 3 s "Nothing showing?" line is gone: no third-party page sends ready or touch, so it would fire under every harvested card, Deepstate at 100% painted included, and never clear. Blankness is bet 6's job, at admission. reported_touches and reported_beats ride the batch as diagnostic columns that never rank. Publish the snippet with the scoped frame-ancestors header in docs/EMBEDDING-A-SITE.md as "make your page a Blitz card".

Why

Twelve of seventeen cards are iframes the host cannot see into; the sweep fires while people are playing (Bloom, Deepstate), ends the session, caps recorded web-card dwell at 20 s and mints a new id on resume. Every later bet measures hold and dwell on web cards, and the host side of the protocol already exists — only the cards are silent. And no card can remember anyone: an instant, stateless card is Playco's mode, one session and no second, and every family whose loop is "come back and see" — idle, clicker, drawing, puzzle — is dead without the save channel, as is any D1 number set against it.

Metric

IDLE_TIMEOUT share of hosted-card exits under 10% within a week of deploy (today it is every session that outlasts 20 s), exactly 0 for external cards; p50 server-observed span on hosted cards moves past the 20 s ceiling; hosted "held" per card_rank becomes comparable with the wasm cards; a second visit to Stack Drop on the same device shows its best (e2e).

CostDays (four to five).Depends onNothing.

Bet 3

Instrument the loop, then seed it

days

What

One migration: a ui_event table (actor, card_id, kind in like/share/back/report, reason, visit_id, entry_surface, entry_source, ua_class, server_ts) written by a log_ui RPC from GameViewer's handlers and the back chevron; a host-stamped liked boolean on the final batch; boot_ms, reported_touches, reported_beats and the SWIPE_DOWN exit as ingest_batch columns, since it ignores payload keys it does not know; card_event.entry_source from ?via= captured next to ?r= in captureEntry, and ua_class (browser, in-app webview, headless) from the user agent; on card, status gains bench, and the columns provenance (editorial, factory, harvest, remix, link — source stays what it is, the sandbox reads it), template_id, parent_card_id, content_hash, platform, platform_terms, pack jsonb and generation jsonb, with ingest_batch joining creator_id, template_id and parent_content_id from card instead of trusting the payload; a house flag on actor for the founder's uploading account; card_status_log (card, from, to, reason, actor, ts) and an owner-only card_set_status that is the only thing that ever flips a status, scheduled by pg_cron for the automatic hides in section 5 — card_rank is read-only today and nothing flips anything. Before the first seed link, the existing internal-flag function runs for the founder's, the checker's and the smoke's actors, so a test tour never votes. A growth_weekly view, owner-only: sessions and distinct networks per day; new actors by entry_surface × entry_source × ua_class; position-0 hold; median depth; held rate, deep hold (server span ≥ 20 s), IDLE_TIMEOUT share and crash rate by provenance and by family; share taps and reports per 100 impressions; share arrivals and their hook rate (visit_ordinal ≥ 2); D1 and D7 by first-session week, browser arrivals only; cards awaiting 20 votes and median days-to-graduate; live and bench cards by provenance and by handle. Then the traffic, in two steps: a 100-visit personal pilot the day bets 1 and 2 are live, ?via=pilot&r=<founder>, read for two days and fixed; then, only after composition is live, the seed cohort — 200–300 visits through founder-controlled links only, personal network, r/WebGames, one generative-art Discord, each ?via=<community>&r=<founder>. Nothing broader. Numbers are per browser profile, not per person: the cross-device merge is the known limitation in docs/ACCOUNTS.md, and it stays known, not fixed, this quarter; ua_class is what makes the retention rows readable in the meantime.

Why

Like is a local boolean, Share and Tip taps vanish, channels are unattributed, reports have no table, nothing can flip a status, and the only readout is card_rank(28) in SQL. Nothing else in this plan can be judged without these columns, and the cohort is only worth sending once they land and once composition exists, because it is the one denominator this plan gets for months and a cohort sent into a fixed-order feed measures the build we are replacing.

Metric

The Monday query returns every column for the previous week; the pilot read (position-0 hold, depth, IDLE_TIMEOUT share) exists by day 7; ≥ 200 new actors with entry_source and ua_class set by day 14; D1 on browser arrivals reported on day 21 with its interval written next to it (at 200 actors that is about ±5 points before the webview rows are dropped, wider after).

CostDays (four to five of engineering; the pilot and the cohort are founder hours).Depends onBets 1 and 2 live before the pilot; bet 4 live before the cohort.

Bet 4

Composition: a different kind of card every BLITZ, shuffled per visit, looping, and a deck that knows what this phone held

weeks

What

feed() becomes feed(n, seed, exclude). The client mints a device seed once into localStorage and sends it; the server and the bundled fallback compute the same md5(seed || day || card_id), so card 0 never swaps under the visitor's thumb and the server order and the bundle agree. Tiers are editorial data, never engagement: anchor is the card_editorial rows, fresh is a live card under 14 days old or under 20 votes, tail is the rest; a card demoted two or more slots drops a tier, so demotion still bites in a shuffled world where a one-slot move inside a tier would be invisible — this is the v1.5 rule and goes into docs/RANKING.md. No tier boundary reads hold_ucb until Gate 1 opens: the earlier draft's "anchor = top hold_ucb" was promotion on telemetry through one indirection, and with the 40-pseudo-vote prior twenty cheap networks in a day would have lifted a tail card into it. Rules: (a) slots 0–1 editorial, slot 2 "New today" — the audition pick, rewritten so it can reach house cards (section 6) — falling back to the third editorial card; (b) within each tier a seeded shuffle under one hard rule, no two consecutive cards share a normalised verb family (the map from raw verbs to families is checked into src/api and the migration), with same source and same template_id within three slots as penalties in a plpgsql greedy that has an explicit fallback when the penalty cannot be avoided, and a unit test that runs the sequencer against the seventeen on both sides so server and bundle produce the same order for the same seed; generated cards fill one slot in two below the editorial block while the live catalogue is under 100 and one in four after; (c) the five wasm games no earlier than slot 8; (d) the end card at the tail, and BLITZ on it calls feed with the card ids held this visit in exclude. The returning device is the client's business and only the client's: from its own localStorage record of what it held — never sent, never read by the server, so it opens no Sybil path — it puts "New today" first, the card it held longest second, then interleaves unseen and held one-to-two with the shuffle weighted 3:1 towards the verb families it stayed for, until the unseen pool is larger than three decks, at which point held cards go to the back. Demote-only ranking is untouched; composition reads tiers, not rates, so it opens no fabrication path.

Why

Every visitor sees the same seventeen in the same order, worst five first, and feed_position is perfectly confounded with quality, which docs/RANKING.md says v2 cannot untangle after the fact. "The next thing is unknown and still good" is a composition property before it is a ranking property, and it works at seventeen cards today. And a returning visitor must not open on the leftovers: sending what they held to the back is the opposite of what a feed people come back to does, and refusing server-side personalisation on fabrication grounds is right while extending that refusal to one phone reordering its own deck from its own record is a category error.

Metric

Position-0 bounce (SWIPE_UP before a 5 s span) down by a third against the baseline row; median cards per visit up 50%, to ≥ 8; per-card feed_position variance above zero so position-adjusted hold becomes computable; returning devices' position-0 hold ≥ new visitors'; the sequencer test green on both sides; D1 reported from day 21.

CostWeeks (two, including the card_rank change).Depends onBet 1 (end card, bundled boot, the card_editorial rewrite), bet 3 (the readout, house, provenance).

Bet 5

The generated-toy factory: a 12-family probe onto a bench, live only as fast as the feed can judge

weeks

What

Before the first generated file is served: the CSP line tools/verify-cards.mjs already tests under (default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: blob:; connect-src 'none'; form-action 'none'; base-uri 'none') goes into public/_headers and netlify.toml for /cards/* — it is absent on the live site today — and the eleven toys are re-verified under it. Generated files are committed into the repo at public/cards/g/<id>/index.html, one batched commit a day, because Netlify applies custom headers only to files it serves itself and a /cards/g/* rewrite to Supabase storage would ship the machine-written class with no CSP at all — fetch, image beacons and remote scripts all working, "no external request" living only in the lint; every generated file also carries its own <meta http-equiv="Content-Security-Policy"> as belt and braces, the lint requires it, and verify-cards runs in CI on the commit with Playwright installed. Then the pipeline the founder runs, as a probe, not a catalogue: one family spec each for word toys, quizzes and trivia with a score, this-or-that and polls, drawing and creation tools, sound and music toys, physics toys, logic puzzles, one-thumb arcade with 15 s rounds and instant restart, idle and clickers, ambient art, micro-stories and choose-your-path, maps and data explorers — five cards per family, sixty in all, so words, people, scores and jokes are forced into the mix and no family is expanded before it has been read. Idle, clicker, drawing and puzzle families must use the save channel; the lint refuses them without it. A generation prompt that emits one self-contained file with the snippet inlined and its tunables in a single <script type="application/json" id="pack"> block (the shape bet 7 needs). A lint that refuses anything over 40 KB, any external request, a missing snippet or meta CSP, a card that does not move on its own within 1 s or react to a touch within 100 ms, or one that consumes vertical drag. Duplicate refusal at admission: sha256 of the file plus a normalised-script hash with whitespace and identifiers stripped, so "regenerate with a new title" is the same card. A headless smoke: loads under the CSP, paints non-background pixels within 1 s, survives 12 s of synthetic taps and drags with zero console errors; a 375x667 screenshot at 3 s becomes the thumbnail, as a WebP under 30 KB, because five hundred PNGs would outweigh the catalogue. A review page where the founder plays it on a phone and answers yes/no plus two verbs and the one plain word. Every admitted card is inserted status='bench', provenance='factory', source='hosted', template_id = family, generation = {prompt_hash, model, spec_version}, creator_id = the founder's house account. The bench is the rule that answers the arithmetic: a vote is one network per card per day, first-ever sessions and pure-bounce days never count, the fresh tier gets a fraction of each visit, and at the plan's own target — 50 visits a day at depth 8 — the whole fresh tier earns on the order of a hundred votes a day; spread over 300 cards no card would ever reach 20, no family criterion could fire, Gate 1 could never open, and the feed would be shuffling cards it could neither rank nor retire. So the live fresh set is capped at what the feed can judge in a 28-day window — (fresh votes per day × 28) / 20, about 15 cards today and about 50 at target, recomputed weekly from growth_weekly — and a bench card goes live only when a fresh card graduates (20 votes: it keeps its tier or is retired) or the cap rises, oldest-reviewed first, rotating by provenance, logged in card_status_log. The daily slot reaches them by the section 6 lottery; otherwise they enter at the tail.

Why

The eleven toys are the only cards a visitor would show a friend; they cost 7–20 KB, paint in 100–260 ms and crashed zero times in three tours. The class is proven and cheap, and its bottleneck is variety, not speed: by the fifth one "the next card is genuinely unknown" has collapsed into "another dark neon particle thing". StumbleUpon's supply was the web; Blitz's cheapest supply is a generator, provided the grid, not taste, decides what gets made — and provided the catalogue never outruns the signal, which is how Facebook Instant Games died: thousands of games nobody could rank, and users learned that instant meant junk. Sixty cards the feed can judge are worth more than five hundred it cannot.

Metric

Sixty probe cards admitted to the bench by week 3, six families by day 10; every live card has a verdict — cards awaiting 20 votes never above the cap, median days-to-graduate ≤ 28, and if it exceeds 28 generation stops; a family is read on its five cards' pooled votes, not on 5-second hold, which every self-animating card satisfies by being stared at: deep hold (server span ≥ 20 s) and share taps per 100 impressions, reported_touches as the tiebreak, first read at 50 pooled votes and expanded or rewritten at 100 (about two weeks a family at target volume; at today's volume the bench waits, which is the point); generation pass rate on first try ≥ 60%; reports and crashes per 1,000 impressions on generated versus editorial cards.

CostWeeks (pipeline in days 5–10; the probe runs through week 3; expansion is gated on graduation, not the calendar).Depends onBet 2 (the snippet and the save channel), bet 3 (bench, provenance, lineage and hash columns).

Bet 6

Harvest the embeddable web through a phone-fit check, ask before framing anyone's work, and give makers a phone link before asking for anything

weeks

What

One RPC and a script. card_stats(slug) returns aggregates only — impressions, held, distinct networks, share arrivals — with card_rank's exposure discipline, shown on the profile view. There is no check_link RPC: 20260908130000_accounts.sql revoked embed_check from clients precisely because it was an unmetered make-the-database-fetch-any-URL endpoint, and every anonymous session is authenticated; the checker is a Playwright script on the founder's machine that reads the final URL, X-Frame-Options, frame-ancestors and content type itself, and submit_link's server-side check stays authoritative at insert. The checker loads the candidate in a 375x667 touch viewport, waits 3 s, screenshots and counts painted pixels — blank or under 60% painted is a refusal, because from the feed's side a blocked frame and a blank page are identical — and the verdict is written on the row. Two kinds of candidate. Platform embed endpoints built for third parties — Shadertoy /embed, Scratch /embed, OpenProcessing's embed, p5.js full-view, CodePen full-page, itch.io HTML5 embeds, Observable embeds — are harvested without a DM, with the platform's embed terms recorded in platform_terms next to platform. Anything with an identifiable author on their own address — Neocities, *.github.io, *.vercel.app, *.netlify.app, *.pages.dev — is asked first and framed second; StumbleUpon's frame was resented, and Blitz does not get to find that out twice. Passers reviewed on a phone, inserted status='bench' with provenance='harvest', developer = domain, and live through the same cap as everything else. A nightly embed_check by pg_cron on every live external card and a weekly pixel check from the founder's machine; a card that fails either is hidden by card_set_status with the reason, because a site that adds X-Frame-Options a month later becomes a blank card that "holds" while the visitor stares at it, and inflates external hold against hosted. The concierge, an hour a day from day 10: three makers found where they post — r/vibecoding, r/ClaudeAI "I built", Bolt and Lovable showcases, p5, itch — by DM only, never a post, never where the community forbids it, and the DM leads with the thing they lack: a phone link that opens their sketch playing, with an OG image and a thumbnail; Blitz as the share-my-sketch-to-a-phone tool, the feed as the bonus. Claiming is a one-tap claim-token URL in the DM — account plus handle, no domain proof, because a maker on OpenProcessing, the p5 editor, Shadertoy or Scratch cannot set a header or a meta tag on a page they do not serve; on claim developer becomes the handle and the profile gains a per-card panel from card_rank aggregates with the snippet offered as "make it Blitz-ready". The "one more thing" ask waits until their card has 20 votes to show, because a dashboard that says n=7 is the Instant Games payoff. Every harvested card carries "yours? Remove it" as well as "yours? Claim it": a takedown address on the card and in docs/EMBEDDING-A-SITE.md, and a removal is done the same day, by hand.

Why

The server-side header check exists, but the one linked card shows the real failure is layout, which can only be judged from a phone-sized render. Platforms built for embedding are the part of the web that says yes to framing, and their sketches are already one-screen, one-touch things. What a p5 or itch maker cannot get anywhere else is a link that opens their thing playing on a phone, and after that honest, host-measured hold from a phone audience; StumbleUpon grew supply by showing site owners the traffic, and lost the goodwill by framing them without asking. This is the upload path the landing page already promises, run from our side of the funnel, with the ask in the right order.

Metric

Live external cards 25 by week 2 from platform embed endpoints, then as many as the bench graduates; per-platform deep hold and share taps within 10 points of the hosted toys at 50 pooled votes (a platform below that is dropped); crash rate on linked cards under 2%; nightly re-check hides counted weekly; ≥ 15 handles with a live card by day 21 and 30 claimed harvested cards by week 12; creator self-share ≥ 50% (a session with referrer_actor_id = the creator); checker pass rate on approached URLs around 60% — below 30% the hunting ground is wrong; DM reply rate ≥ 20%, and zero takedowns not honoured the same day.

CostWeeks (checker, pg_cron job and RPC in days; the harvest and concierge are a standing daily hour).Depends onBet 2 (the "Open" affordance and the snippet to offer), bet 3 (platform, provenance, entry_source, card_set_status).

Bet 7

Remix-by-pack: one change to an existing toy is the first creator generation path

weeks

What

Refactor four toys with obvious tunables — Tile Flip (grid, palette, target, par), Stack Drop (slab colours, swing speed, start width), Tide Pool (creature count, colours, speed), Bloom (palette, branching, wind) — so every tunable reads from the pack block, each with a checked-in JSON Schema including a title and one-line caption; the factory template emits the same shape for every new family. submit_pack(template_id, pack, title): account with handle required (the submit_link gate), schema-validated server-side, every string field run through a profanity and URL filter — the code in a pack is inert, its strings are not, and a title, a caption or a word-toy's word list is the moderation surface — sha256-deduped (same template plus same pack is the same card), creator_id = caller, parent_card_id = the card Remix was pressed on, provenance='remix', 3 per actor per day and the existing 10 per network. There is no server to inject anything on a static site and a hosted frame cannot fetch its pack under connect-src 'none', so the pack travels in the URL fragment: /p/<template>#<base64 pack> is a Netlify rewrite to the template's own file (so _headers applies), the template reads location.hash, nothing is sent to any server, page_url stays unique per hash, and the link is shareable as it is; if a pack ever outgrows a URL the host posts {blitz:'pack', pack} after ready instead. A Remix button in the "…" on packed cards opens a sheet with controls derived from the schema; the one-line prompt box that fills the schema through a Supabase Edge Function as constrained structured output is shown to accounts with a handle only, with its own token bucket on the function, because an ungated generation endpoint is a cost problem before it is anything else; the result previews instantly and Publish calls submit_pack. The card reads "remix of <parent> by @handle", the parent shows a remix count, and the remix enters on the bench with its own place in the section 6 lottery.

Why

The premise is the AI-generated-content era and there is no make-a-card flow at all. Open prompts from anonymous visitors are a moderation and cost problem; a bounded pack on a reviewed mechanic cannot execute, cannot fetch, cannot be a login form — the worst its code can be is ugly, and its strings are filtered and reportable — so its moderation is automatic on day one and its quality floor is the parent's. It gives every card a face other than @blitzlabs and makes lineage real in the schema before it must be backfilled.

Metric

Remix conversion: of held sessions on a packed card where the sheet opened, the share that published ≥ 3%; hold rate of remixes within 10 points of their parents over the same 28-day window; share of live cards not by @blitzlabs at 20% by week 12; distinct handles with a live card (today: one); reports per 1,000 impressions on remixes versus their parents.

CostWeeks (three to four).Depends onBet 5 (CSP, template shape, lineage columns), bet 3 (report, the filter's ui_event row).

Bet 8

Ranker v2 in gates keyed to measured volume

months

What

Write the thresholds into card_rank now and flip each on only when its number is met (section 6). Gate 0 is bet 4's tier rule and tier-drop demotion, already live. Gate 1: position-adjusted promotion within tier. Gate 2: per-actor verb affinity with a permanent no-personalisation holdout. Gate 3: promotion into the editorial block. Verbs stay author-assigned until the wasm telemetry and the snippet's beats can recover them; content-reported numbers never rank.

Why

The ranker is correct and idle at tens of sessions a day. Opening promotion early is a ranking by willingness to fabricate; building personalisation before there is a returning population to personalise for is building for nobody. Writing the gates now means the day the numbers arrive is a flag flip, not a design meeting.

Metric

Gate 1: distinct voting networks per day, and once open, hold rate of promoted cards in their new slot versus the slot's prior occupant. Gate 2: cards per visit and D7 for personalised actors versus the holdout. All from card_event using actor_id, visit_id, feed_position and the network hash only.

CostMonths (the thresholds are days of work now; the flips wait on volume).Depends onBet 4, bet 3.
05

Content

To a catalogue with a verdict, not to a number. Three sources, one review, one bench. In the first quarter: sixty probe cards from the factory (twelve families, five each), the harvested cards that pass the phone-fit check from platforms built for embedding and from makers who said yes, and the cards other people make — claimed harvests, concierge makers' second cards, remixes. Every one of them is opened once on a phone by a person before it leaves the bench, and reviewed against three questions only: does it respond to the first touch, does it fit 375x667, would you show it to someone. Admitted cards wait on the bench and go live only as fast as the feed can judge them — the live fresh set is capped at what 28 days of votes can carry to 20, about fifteen cards at today's traffic and fifty at the target — oldest-reviewed first, rotating by provenance. Live cards enter at the tail; the daily slot is their only distribution until they have votes, and the lottery rotates by provenance from day to day so "New today" alternates among the factory, the harvest and the remixes rather than by anyone's taste. The number of live cards at week 12 is whatever graduated; the target is that every one of them has a verdict.

To 5,000. The same three sources, with the gate on review moved, not removed, and the cap on the live fresh set raised only by traffic. Remix-by-pack scales without review because packs are inert and their strings are filtered. Full-code generation opens to accounts, not anonymous visitors, and only after a per-creator track record: three graduated cards holding within 10 points of the pool. Review becomes sampled — every card from a creator with fewer than three graduated cards, one in five otherwise — once the lint and smoke have 500 graduated cards of precision data behind them. Families expand from five cards to twenty-five only on pooled deep hold and share taps. Harvest scales by platform, driven only by per-platform numbers. The end-of-feed "Make one" entry opens when the factory's first-try pass rate is above 60% and the first 500 have been played, because a generation path that fails four times in ten in front of a stranger is a bounce, not a feature.

What is automated. Generation from the family spec; the lint; the smoke; the screenshot and thumbnail; duplicate refusal at admission; insertion on the bench; bench-to-live within the cap; the audition lottery; the nightly header re-check and the weekly pixel check on external cards; auto-hide (never auto-block), by card_set_status under pg_cron with a card_status_log row for every flip, on host-detected crash rate above 25% over eight or more votes, on a snippet heartbeat that stops while dwell continues, on a re-check failing, or on reports above 3 per 100 impressions over 50 or more impressions; the string filter in submit_pack; the daily and weekly readouts.

What is deliberately not. The grid — which families exist and how many of each — is decided by a person against the per-family deep-hold and share numbers, never by what the generator finds easiest and never by 5-second hold, which a screensaver satisfies. The yes/no on a phone. The flip from hidden to blocked, or back to live. A takedown. Promotion of any card into the top three. The DM to a maker. No model ever decides what executes in front of strangers; the model makes, the lint refuses, a person admits, the bench waits. The Deepstate lesson stands: framing permission is not fit, and only a phone-sized render can tell.

The founder's week. One person ships bets 1 to 8 and runs the content. The standing hours, per week, once the pipeline exists: phone review, about three minutes a card, three hours for the sixty-card probe spread over three weeks; harvest triage, about three hours per hundred URLs; the concierge, five hours; generation spend at a 60% first-try pass, cents a card; Netlify bandwidth trivial with cards committed rather than proxied. Around fifteen hours a week on content against a full engineering week, which is why the probe is sixty and not three hundred. When the hours run out the order of what stops is: the concierge, then harvest triage, then generation beyond the first six families; the measurement, the pilot and the phone review of anything that goes live are never cut. Hiring for content stays the signal the loop failed.

06

Ranking evolution

Today, per docs/RANKING.md and card_rank: editorial order is the ranking; a card needs 20 real votes before it can be demoted, only from positions ≥ 2, one slot per 0.10 of hold-rate deficit against the pool, capped at three, never out of the top two; a 40-pseudo-vote prior, a one-sided 90% upper bound, a 28-day window with a 14-day half-life; one network one vote per card per day, at most 20 votes per card per day; first-ever sessions, pure-bounce actor-days, internal actors and a card's own uploader never vote; one upload a day auditions at slot 2 until it has 30 lifetime votes or is 30 days old. RANKING.md says promotion opens "when there are enough distinct networks voting per day that one-network-one-vote makes fabrication expensive" and leaves the number open. This plan sets it.

Gate 0, now: composition, not ranking. Everything in bet 4 reads tiers that are editorial data — anchor is card_editorial, fresh is age under 14 days or votes under 20, tail is the rest — and interleaves by verb family, with source and template as penalties. It never reads a hold rate, directly or through an indirection, so a forger gains nothing from it. Demote-only stays exactly as written in SQL and, because a one-slot move inside a shuffled tier would be invisible, gains a v1.5 rule that makes it bite in the feed: a card demoted two or more slots drops a tier. And the audition slot is rewritten, because as written it does not reach the cards this plan makes: explore_pool draws one ticket per uploader actor, requires the uploader not to be internal, and distinct on (uploader_actor_id) … order by created_at shows that uploader's oldest card until it clears 30 votes — so sixty factory cards and every harvested card under the founder's account would share one ticket and show the same oldest card for a month, and zero tickets the moment the founder's account is marked internal, which it must be before the first seed link or every test tour votes on every editorial card. The change, shipped with bet 4: a house flag on actor; for house uploads the pick is md5(current_date || card_id) over eligible live cards bucketed by provenance, with the bucket rotating by day; the internal exclusion applies to voters, not uploaders; the three-held-cards test stays for outside uploaders. Before day 3 the three-line SQL that shows today's pick against a seeded house card runs; if it returns Gravity Well, nothing about "New today" is true.

Gate 1, position-adjusted promotion within tier. Opens when distinct voting networks per day (count distinct ip_hash in card_event) has been at or above 100 for 7 consecutive days and at least 20 cards carry 20 or more votes — a condition the bench cap of bet 5 is what makes reachable at all. Why 100: the daily sample per card is capped at 20 votes drawn from the day's networks, so at 100 networks a forger who wants to own a card's sample needs dozens of distinct addresses every day for the 14-day half-life to move it five slots — more than the slots are worth. Mechanics: hold is regressed on the feed_position bucket the client has been stamping since v1, sessions with visit_ordinal 1 are excluded as orientation, one-network-one-vote and the 20-vote cap stay, and a card may rise at most 5 slots a day within its tier. The editorial top three and the audition slot are untouched.

Gate 2, per-actor verb affinity. Opens when at least 30% of actors in the 28-day window have sessions on two or more distinct days, and there are at least 500 such actors. Per actor, held versus bounced by primary verb over 28 days is the whole interest vector — for a playable card, interest is not a topic; it is how you like to touch (tap, drag, time, plan), the tempo (calm or twitch) and the goal shape (score, finish, toy, social object). The next-card picker draws from the actor's top two verb families three times in four and from a family they have not played once in four. Actors with hash mod 10 = 0 are a permanent no-personalisation holdout, and the gate stays open only while personalised actors beat the holdout on cards per visit and D7. The on-device ordering in bet 4 is not this gate: it reorders one phone's deck from that phone's own record, the server never reads it, and it opens no fabrication surface; this gate is the server learning across phones, and it waits.

Gate 3, promotion into the editorial block. "Goes viral, ranks first" opens at 1,000 distinct networks per day sustained for 14 days, still one vote per network, still capped, still only on held and bounced. Until then, moving a card into the top three is an editorial act, as it is today.

Nothing a card or a client reports ever enters any gate: not Like, score, touches, beats, saved state, remix counts, reports or share taps. They are diagnostics, and they stay in their own columns.

07

Growth

Blitz can only grow through sent cards and through makers, because the product is a URL and the makers are the only acquisition channel that also fixes supply. The channels, in order:

  1. Founder-controlled links, in two steps — a 100-visit personal pilot the day bets 1 and 2 are live, ?via=pilot, read for two days; then the seed cohort of bet 3, 200–300 visits from the personal network, r/WebGames and one generative-art Discord, every link ?via= tagged, sent only after composition is live, because it is the one denominator this plan gets for months and it is not spent on a fixed-order feed. Reddit's and Discord's in-app webviews keep their own storage, so a person who comes back from Safari is a new actor to us; ua_class is recorded on arrival, retention is read on browser arrivals only, and the end card tells webview visitors to open Blitz in their browser.
  2. The creator concierge — three makers a day from day 10, each DMed a link that opens their thing playing on a phone with an OG image, the feed as the bonus, the claim as one tap, the numbers only when there are enough of them to mean something. Every claimed creator is a channel with a face and an audience that already likes their thing.
  3. After the gates only: Show HN "the feed for what people make with AI", r/InternetIsBeautiful, and three 15-second vertical recordings (Gravity Well, Tide Pool, Bloom) on TikTok and Shorts with the card's /c/ link in bio, each with its own ?via=. The source with the best D1 × depth gets the next month's founder hours.

The retention bar, read from growth_weekly before any broader post, in three reads. Day 14, on the pilot and the first days of the cohort: position-0 hold ≥ 50%; median cards per visit ≥ 8; IDLE_TIMEOUT share of hosted-card exits < 10%; share-to-arrival ≥ 30% (new actors with entry_surface='share' divided by share taps); arrival hook ≥ 50% (share arrivals with visit_ordinal ≥ 2). Day 21: D1 ≥ 10% on browser arrivals from the cohort, with its interval written next to it. Day 28: D7 on the same. D1 is only worth gating on once there is a way back — the host-held state and the add-to-home-screen card ship in the fortnight, the daily push for accounts (VAPID plus a Supabase cron, about a day) in week 3 — and until day 21 the plan says plainly that it has measured first sessions. If the day-14 bar is not met, weeks 3 and 4 go to the first minute and composition, not to supply and not to posting. Paid channels stay off until D1 ≥ 20% on ≥ 1,000 organic new actors. There is one first launch, and sending a Show HN crowd into a feed that dead-ends in two minutes spends it on the wrong build.

08

Two-week plan

The critical path is chrome and snippet, deploy, pilot, fix, composition, cohort. Everything else runs beside it and is what gets dropped when hours run out, in this order: concierge, harvest, factory beyond six families, landing. The pilot is never dropped and the readout is never moved.

  1. Day 1

    The baseline row first: from the 61-batch tours and one fresh headless tour, today's position-0 hold, median depth, IDLE_TIMEOUT share on hosted cards, SWIPE_UP-within-3-s at position 0 and boot_ms, written next to every target in this document before any code. Confirm the live build is 6e5c69d; screenshots of Tile Flip and Colour Split. The CSP line for /cards/* in public/_headers and netlify.toml; verify-cards green. blitz-card.js written with the save channel and inlined into all eleven toys. The internal-flag function run for the founder's, the checker's and the smoke's actors. Push to main; Netlify builds. Measured next morning: IDLE_TIMEOUT share of hosted-card exits.

  2. Day 2

    The migration: ui_event and log_ui with report; liked, boot_ms, the diagnostic counters and the SWIPE_DOWN exit as ingest_batch columns; entry_source from ?via= and ua_class; bench, provenance, template_id, parent_card_id, content_hash, platform, platform_terms, pack, generation on card; house on actor; card_status_log and card_set_status; ingest_batch joins lineage from card; the explore_pick rewrite and the SQL check that today's pick is a house card; growth_weekly v0. captureEntry reads ?via=. The card_editorial rewrite with MOCK_GAMES, feed.json, tools/seed-cards.mjs and the e2e fixture in one commit.

  3. Days 2–5

    Consumer chrome behind ?dev=1; one plain word per card; Like, Share and the "…" with Report in the strip, Tip gone, share text per class; the back chevron on goTo(i-1), the founder's ruling on the sentinel; idle policy (external no timer and a permanent "Open", wasm pause at 20 s and exit at 80 s under one session id with the e2e contract rewritten); card 0 from the bundle, session before feed(), reconcile behind the active index; /c/<slug> resolved and spliced at 0; boot_ms; the end card with the webview line; the add-to-home-screen card; Stack Drop's "your best". Deploy on day 5.

  4. Days 3–6

    Landing front door: "Open the feed" primary to /c/ripple-tap?via=landing, "Create account" to the creators section, three real card names, measured or no "<1s" fact, no "Dev tools" link; "+" hidden for anonymous visitors; the concierge makers' handles into reserved_handle, then Supabase Confirm-email off.

  5. Days 5–7

    The pilot: 100 visits through the founder's own network, ?via=pilot; position-0 hold, depth and IDLE_TIMEOUT share read against the baseline row on day 7; what it shows is fixed before composition ships.

  6. Days 6–9

    Composition v0: feed(n, seed, exclude), editorial tiers with tier-drop demotion, slot 2 as "New today" from the rewritten lottery, wasm games at 8 or later, seeded shuffle on verb families with the sequencer test green on both sides, fresh slots one in two, the on-device order for returning devices, BLITZ on the end card re-fetches minus held; client seed and bundled mirror. Ripple Tap is the opener for everyone. Deploy on day 9.

  7. Days 5–10

    The factory: template with the pack block and the inlined snippet; lint with the meta CSP and the save-channel rule; dedupe; smoke; WebP thumbnail; the phone review page; the commit-a-day path with verify-cards in CI; the first thirty probe cards across six families — word toys, quizzes with a score, this-or-that, sound, drawing, micro-story — five each, onto the bench; the cap computed from the pilot's numbers and the first bench cards let through it.

  8. Days 8–12

    The checker script (no RPC); 100 candidate URLs from Shadertoy, Scratch, OpenProcessing and p5.js embed pages and itch.io HTML5; the 25 that pass reviewed on a phone and inserted on the bench with developer = domain, platform and platform_terms set; the nightly re-check under pg_cron. Concierge from day 10: three makers a day, DMed a phone link that opens straight to their work, with a claim token.

  9. Days 10–12

    Seed cohort: 200–300 visits through founder-controlled links, ?via= and ?r= on every one, into the composed feed.

  10. Day 14

    The readout from growth_weekly, with cohort sizes and the baseline row beside it: position-0 hold, depth, IDLE_TIMEOUT share, share-to-arrival, arrival hook. Not D1 — nobody who arrived on day 12 has a day-21 yet — and not D7. Result: about 70 cards admitted (17 plus 30 probe cards plus 25 harvested plus the makers'), of which as many live as the cap allows, a feed that differs per visit, remembers the device and never ends, web cards that can be measured, and a cohort large enough to read the first bar. Day 21: D1 on browser arrivals. Day 28: D7, and the go or no-go on the launch post.

Kill criteria

  • If IDLE_TIMEOUT share on hosted cards is not under 10% by day 4, the snippet is not reaching the host; nothing else ships until it does, because it is the measurement.
  • There is no opener A/B in the fortnight; Ripple Tap opens for everyone, and the Gravity Well comparison runs after the bar at 200 first visits per arm or not at all.
  • A factory family whose five cards, at 50 pooled votes, sit more than 10 points below the original eleven on deep hold or share taps gets its spec rewritten before another card is made; a second rewrite drops the family. If median days-to-graduate exceeds 28, generation stops until it does not.
  • A harvest platform more than 10 points below the hosted toys on the same two numbers is dropped; checker pass rate under 30% on approached URLs means the hunting ground is wrong.
  • If median depth over the cohort does not rise against the pilot, the shuffle goes; the editorial top three and the on-device order for returning devices stay.
  • If the day-14 bar (section 7) is not met, no launch post; weeks 3 and 4 return to the first minute.
  • And one for the thesis, not a bet. If, with seventeen cards under the new chrome and composition, position-0 hold and median depth do not move against the baseline row across the pilot and the cohort — hold still under 40% and depth still under 5 on day 14, with no upward trend between the two reads — that is the verdict on the feed, not on the catalogue. Then the factory stops at the probe, no cohort is spent on supply, and the founder's next fortnight goes to the question of whether Blitz is a feed people return to at all.
09

What we refuse to do, and why

  • No wasm or C SDK ecosystem, no porting games for supply.The five wasm cards cost far more than the eleven toys, read as test fixtures, and were the worst first impression the feed had. The ABI stays as it is until a creator asks for it.
  • No promotion on telemetry and no personalisation before the gates in section 6, including through a tier boundary.At tens of sessions a day any promotion path is a ranking by willingness to fabricate, and a tier whose membership reads a rate is a promotion path with one more step. Demote-only is the rule until the numbers say otherwise.
  • Nothing a card or client reports ever ranks.Like, score, touches, beats, saved state, remix counts, share taps — recorded as diagnostics, never as votes.
  • No open-prompt "make anything" for anonymous visitors in the first 500 cards, no ungated generation endpoint, and no model ever flips a card to live.Remix-by-pack is the only in-product generation path until the lint and review have a track record, its prompt box is for accounts with a handle behind its own bucket, and a person admits what executes in front of strangers.
  • No card goes live unreviewed on a phone, no card goes live faster than the feed can judge it, and no harvesting of desktop layouts.Framing permission is not fit; the feed cannot see a blank or squeezed frame from outside, and Deepstate is the proof. A catalogue that outruns its signal is Instant Games, and the bench is the refusal.
  • No framing a person's work before asking.Platform embed endpoints built for third parties are fair; a maker's own page is asked first, every harvested card carries "Remove it" next to "Claim it", and a takedown is honoured the same day.
  • No swipe or gesture feed navigation.The founder's rule stands, and discovery does not need it — composition is what makes the next press worth taking. That includes history-as-deck: the chevron is the way back, and a sentinel entry so that hardware Back steps a card, which cannot be told from the iOS edge-swipe, ships only on the founder's explicit ruling.
  • No spend on tips, the ledger or payouts.The Tip button is gone; the rate stays at zero; the creator payoff for the first year is a phone link that opens straight to their work, host-measured numbers and feed position.
  • No topic taxonomy, no pick-your-interests step, no onboarding screen, no coach marks.The verbs plus held/bounced per verb are the interest model — on the device now, on the server after Gate 2 — and any question asked before the first play costs the first play. The first card is the onboarding.
  • No longer idle timer as the fix for hosted cards.The snippet is the fix. External cards get no timer at all, which is a different thing: their session ends on exits the host can actually see.
  • No RPC that fetches a URL for an anonymous caller.embed_check was revoked from clients for a reason; the checker runs on the founder's machine and submit_link checks on insert.
  • No launch post, press or paid dollar before the retention bar, and no opener A/B on the cohort that has to answer it.There is one first launch and it is not spent on a two-minute feed.
  • No content team.Cards 18 onward come from the generator, the harvest and the makers they surface, inside the founder's fifteen content hours a week; when the hours run out the concierge and the harvest stop before the measurement does, and hiring for content is the signal the loop failed.