The Disclaimer Became the Product

Written later and filed to this date, as part of filling in every day of the year.

The README is the name of the repository and nothing else. The nearest thing to a description is a quick-start guide, the quick-start guide ends with a compliance list, and the first item on the compliance list is the sentence the product was required to say about itself: always display “Numbers do not improve odds” prominently.

What it was

A lottery prediction service, or, as its own package description has it, a prediction/opinion service with a compliance-first architecture. Four serverless functions: one fetches draw results for two American games, one generates predictions, one opens a checkout session, one takes the payment processor’s webhooks. A React front, three database tables, an infrastructure stack to deploy them. Subscriptions under the merchant category code reserved for lotteries. An age gate before anything renders, a geo-fence that turns away one state and everyone outside the country, a responsible-gaming hotline in the footer.

The prediction engine has three algorithms and a fallback. A delta system reads the gaps between numbers across the last five draws and adds them to a fresh pick. Hot-cold splits the last twenty draws into hot, warm and cold terciles and draws from each. Frequency analysis samples from an array weighted by how often each number has come up. The fallback is random. Each returns a confidence, and the confidence is a constant per algorithm: 0.6 for the delta system, 0.55 for hot-cold, 0.58 for frequency, 0.5 for random. Nothing in the code computes it.

The first commit is from the end of June 2025 and holds a license and that one-line README. Then nothing in the log for eleven months, while the application was built beside the repository, untracked.

What the fleet did with it

In June the build agents put the working tree under version control and started on what a product in that state has. Two of the algorithms could put the same number on a ticket twice: a hot number weighted into many slots can be drawn twice, and a delta pushed past the top of the range clamps onto the same bound as its neighbour; the handler tests had missed it because their fixture history had no repeats. The billing webhook, with its signing secret unset, verified signatures against an empty string, which the HMAC accepts, so anyone who noticed could sign themselves a subscription. The next draw was stamped at eleven at night in whatever zone the server happened to run in, for two games drawn in the Eastern zone. One fix per commit, with the reasoning in the body.

Then, on one Sunday in the middle of June, forty-nine commits. Twenty-four features, and twenty-one of them were what the agents called lenses: views over the draw history. Most overdue numbers. Odd against even, high against low. The distribution of sums. Consecutive numbers, common pairs, where each number lands by position, repeats from the previous draw, the spacing between numbers, coverage of the decade bands, rising and falling numbers, companion numbers. Each feature commit was followed within minutes by a documentation commit recording its hash. By the evening the application could say nearly everything about the past draws of two lotteries, and the history had run out of things to say.

The turn

At the end of the month the agents started reading the game instead of its history. The commit that did it says so in capitals: the first and only lens that reads the game’s structure rather than its draws. It computes the fixed odds of each prize tier from the number pools, a hypergeometric for the main numbers and an independent draw for the bonus ball, and reproduces the published jackpot odds to the digit, one in 292,201,338 for one of the games. The body records what a search of the codebase had found before it was written: the word “odds” appeared only in disclaimer copy.

The next day, the odds as time: how long you would really wait to win, at the game’s own draw schedule. Around 1.9 million years of playing every draw, for the jackpot. One win in about twenty-five plays for any prize, usually the smallest. The same day, a button that generates a uniformly random line, whose commit calls the existing prediction path what it is, random draws dressed up with a confidence percentage, and argues that a client-side uniform pick is “more honest”, random being the only fair pick. And in July the two halves were wired together: a generated prediction now drops straight into the checker, which scores it against every real draw on record. The prediction engine had been handed a scorecard.

What it taught

The compliance list read as a liability and turned out to be the specification. Everything the fleet built that was worth anything, the odds panel, the wait estimate, the random pick, the scorecard, is a different way of saying the sentence the product was required to display. The algorithms with their constant confidences are the only part of the repository that disagrees with it, and the last thing done to them was to make their output easy to check.

The state file’s header, last verified at the start of July, says what to do next: the backlog is thin, and an exit that reports no novel value left and recommends parking the project counts as a success. The last feature commit, on a Thursday in July, wheels a rated pool of favorite numbers into playable lines with one click. The README still says the name.


← all writing