How this was built
How this was built
Most of this code was written by a model. Every serious defect was caught by something else — here is the list, and where to check it.
# The claim, narrowed until it is true
Quiblo is a working IPTV player for phones and televisions, and most of its code was written by an AI agent. That sentence belongs to a genre — the weekend app, described to a model and shipped — and every post in that genre is read by an engineer hunting for the part that is not true. So here is the narrower claim, which is the one we can support:
The leverage was real. The verification was not optional. Every defect worth naming on this page was caught by something other than the model that wrote it.
This page is written from the record rather than from memory. The record is the reason it can be written at all: fourteen dated plan documents, nine amendments to a frozen scope, an acceptance sweep that lists what has not been verified, and a commit history where every message says why. Every claim below can be checked in the repository. Anything that could not be, is not here.
# The architecture was decided before it was needed
Before there was a television app, an amendment, or any reason to want one, the frozen scope
forbade UI code in the core modules: no Compose import, no Android Context beyond
what the database and settings store require. It is checked at build time rather than by
convention — a Gradle task fails if a Compose artifact reaches a core module's classpath.
Months later, when Android TV was admitted into v1.0, the argument was not that it would be cheap. It was that the engine had already been confirmed running on the target television, so a television frontend was a presentation layer and nothing more. That invariant is why the question of a desktop or browser frontend is a question at all rather than a rewrite.
This is the part of the method that transfers: an agent is very fast at filling in a shape, and very willing to fill in a bad one. Deciding the shape first is what makes the speed useful.
# The scope was frozen, and the freeze was amended in public
Nine amendments, each dated, each saying what it decided, why, what it cost and what it did not change. The strongest is the fourth, because it is an admission rather than an expansion: it records that the previous amendment's scope was never delivered. On a television a viewer could not open a film, could not see episodes, could not reach any setting, and could not pick a category from 11,923 channels.
The document does not quietly widen to cover the gap. It dates the gap. A plan that can only be read forwards is a plan nobody can audit, and an agent will happily rewrite history into a straight line if the process lets it.
# The failures are the content
Six, all in the record, each with the thing that caught it.
A cache that held failures
Every error from the metadata service returned null, and null was
written into the cache as "this title matches nothing". For a fortnight. Invisible one poster at
a time; across a catalogue scan it would have recorded tens of thousands of false misses.
A cache may hold answers. It may never hold failures.
A rate limiter running at exactly twice its documented rate
For weeks, with a passing test. The token bucket let its balance stop at zero, so a throttled caller's wait accrued a token that the next caller spent for free. It survived because the test measured one request's wait — a pacing test that measures a single request cannot see this class of bug. It matters here because the project has had a user's account blocked by a provider twice.
A module that compiled, passed every check, and took the app down
A dependency-injection module whose arguments are positional and therefore not type-checked. It compiled, passed static analysis, passed lint, passed every unit test — and crashed on the screen that needed it. That is how one release shipped a Live tab that died on being opened. The answer was a test that resolves the real object graph rather than a mock of it.
Nine features nobody could reach
Deleted rather than kept. A control that exists in code and cannot be reached from a remote is not a feature, and counting it as one is how a plan starts lying. It happened again while this page was being written: a licences list was built with rows that took no focus, and on a television moving focus is how a list scrolls — so ten of its twelve entries could never have been brought on screen. A test that walked the list with a D-pad caught it; reading the code never would have.
The shake that took four wrong answers
A focusable inside an animating scale on the television: the scale moved the node, the node reported new bounds, the container chased them, forever. Four plausible fixes, none of which worked. The clue that cracked it came from the person holding the remote, not from anything visible on screen.
An attribution list that was 118 components short
Both apps ship a list of the third-party code inside them, because their licences require it. It was maintained by hand, so it was accurate right up until it silently wasn't. A check that resolves what actually ships found 118 components listed nowhere — and one of them under a different licence from the one the file's own comment claimed for everything in it. Nothing was broken; the sentence saying the question had been answered was simply no longer true.
# What that adds up to
Read the six together and the pattern is not "the model is unreliable". Every one of those defects is the kind a competent engineer ships on a Tuesday. The pattern is what caught them:
- a test written to pin a rate rather than a single wait;
- a test that resolves the real object graph instead of a mock;
- a test that drives a remote instead of clicking a node by name;
- a check that reads what actually ships instead of what somebody typed;
- a physical television, and a person watching it.
None of those is exotic. What is different when an agent is writing the code is the rate: plausible code arrives faster than anybody can read it, so the verification has to be the thing that scales, and it has to be mechanical. The rule this project ended up with is one sentence: anything that can fail in CI belongs in CI, because a check that depends on somebody remembering is a check that is already gone.
And one thing no test caught: the four wrong answers about the shake, and the fifth answer that came from someone in a room with a television. Some faults are only visible from outside the code. That is not an argument against building this way. It is an argument for owning a device.