On June 29, 2026, a mobile app reported that "the first wave of parity with the PC version is complete, 162 tests green." When we actually touched it on a real device (via TestFlight, Apple's beta-app distribution), every major user flow was broken. It ignored the SafeArea (the screen region free of the notch and system bars) and ran under the notch; chat failed the instant you hit send; the settings screen returned 404; knowledge upload failed. 162 tests were green, and the number of major flows that actually worked on the device was 0. This article dissects, with real cases from several products, the false "it's done" reports you run into when you have an AI write code — something you hit before you ever reach the limits of its ability — and records how we distilled that into machine-enforced quality gates.
The root cause of that 162-to-0 came down to a single thing: we reported the green of a fully mocked unit-test suite as evidence of "a working app." In the retrospective we laid out seven root causes with code evidence, but the gist is this. The API baseURL was not wired up. The authentication hookup was deferred behind a comment reading "future step." And there was zero operation on a real device. In other words, the tests only confirmed that "the mocks I wrote behave the way I expect" — they were never connected to the outside world. From here we adopted "passing tests ≠ a working feature" as a watchword and created a new completion-gate skill.
False completion is not caused only by AI-specific hallucination. "Implemented, but not present in production" — which humans hit too — wears the same face. On a news-curation platform, we implemented a multi-stage fallback to salvage broken JSON, yet the errors would not stop in production. On investigation, the json-repair
library used by the fallback was not in the worker's requirements file (Python's dependency manifest), so it did not exist in the Docker image. It was the pitfall of a setup with two requirements files. The code was written, it passed review, and yet in production it was dead code (code that never actually runs). Meanwhile, monitoring had piled up over ten thousand error events. After this, we made the packaging contract itself something a test asserts.
def test_fallback_dependency_is_packaged():
reqs = read_requirements("requirements-worker.txt")
assert "json-repair" in reqs, "fallback dependency missing from the production image"
import importlib
assert importlib.util.find_spec("json_repair") is not None
We also saw the most AI-like false completion. In dogfooding (using the product to develop the product itself) — where we throw the local LLM orchestrator's own changes at it — an execution agent that had cloned the repository read that repo's config file (a rule saying "delegate implementation to a sub-agent") and tried to delegate. But in a headless environment (no interactive UI) it could not spawn a grandchild sub-agent, so it hallucinated "I added one line" without having changed anything and pushed an empty branch. Naturally, GitHub rejected the "empty push" with a 422. There are two lessons here. One is that "only when you develop on yourself does your own repo's config become poison for the execution agent." The other is that you must not trust the report; you have to look at the artifact (the actual diff).
Looking at it calmly, the false completions shared a few mechanisms.
First, an AI tends to judge completion by "did it reach a plausible terminal state," not "did it satisfy the intent of the task." Intermediate outputs like green tests, a created commit, or an opened PR do not, on their own, mean the intent was achieved. Second, "deploy" and "real device" are not in the denominator of completion. When you equate "I did 100% of it inside the repository" with "it works in production," you get the json-repair case above, or the plugin freeze described below. Third, verifying the wrong target. One gate looked only at the working tree's diff and carried a bug where "once you commit, the tree becomes clean, and it is treated as forever incomplete." In other words, the very definition of "done" was cut off from the outside world.
The remedy was consistently to "move the definition of completion from the AI's self-report to machine-verifiable evidence." We layered gates in stages.
The first is a gate that blocks completion claims that lack evidence. It forbids calling a boundary-crossing change (external API, payments, DB writes, SaaS-to-SaaS integration) "complete" on weak proxy evidence alone — "the build passed," "units are green" — and demands E2E (end-to-end) evidence equivalent to a real device. On an uptime-monitoring SaaS, failing this real-device gate once (a principle we call MUST 25 internally) surfaced, in one shot, five bugs hiding behind an all-green unit suite (a DB create that fails, a scheduler that omits required data from its payload, an install-order slip in the Dockerfile, and so on).
The second is to give boundary-heavy areas like mobile a dedicated completion checklist as a skill. The idea looks like this.
- [ ] The API baseURL is wired to the real environment, with evidence of one round trip without mocks
- [ ] The auth flow was passed on a real device (no "future step" comment left behind)
- [ ] Each major flow (create, send, upload) was manually run once on a real device
- [ ] SafeArea / navigation confirmed by a real-device screenshot
The third is to run gates in a clean environment. It passes on your local machine but fails in an environment checked out clean — this flushes out non-hermetic tests (tests that depend on the actual working directory) that rely on your real directory. In fact, one gate ran the tests in a clean worktree (a fresh checkout of the repository) and detected "a defect you would never notice with a local npm run check
." That was exactly the value the gate was designed for.
Finally, there is a clear example of what happens when "deploy" drops out of the denominator of completion. Across a fleet of production accounts running on autopilot, the plugin being distributed was frozen at an old version for a full 12 days, yet the quality gates were all green and no one noticed. Of five structural holes, the core ones were that we equated "100% inside the repository" with "reflected in production," and that the detection layer had only push-based hooks. A hook that runs at git-commit time is structurally unable to observe state outside of git (which version is actually running at the distribution target). The fix is a pull-based parity check, where a cron job at the distribution target reconciles "the installed version" against "the version that should be distributed." Here we promoted the principle "distribution drift can only be detected by pull-based periodic reconciliation" into our documented standards.