DEV Community

Cover image for An AI's “Done” Can't Be Trusted — Quality Gates That Stop False Completion With Evidence
uehara
uehara

Posted on Originally published at zenn.dev

An AI's “Done” Can't Be Trusted — Quality Gates That Stop False Completion With Evidence

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 classic false completion: passing off "the tests pass" as "it works"

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.

CI (continuous integration) run for the promptflow mobile app. All 162 unit tests are green (the screen is reconstructed and anonymized; the numbers are actual measurements). Behind this green, not one major flow worked on a real device.

Another face: "implemented" and "running in production" are also different things

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.

# Back up "we implemented it" with "it is in the production image"
def test_fallback_dependency_is_packaged():
    # json-repair must be declared in the worker's requirements
    reqs = read_requirements("requirements-worker.txt")
    assert "json-repair" in reqs, "fallback dependency missing from the production image"
    # and it must actually be importable (declared is not the same as resolvable)
    import importlib
    assert importlib.util.find_spec("json_repair") is not None
Enter fullscreen mode Exit fullscreen mode

 raw `ModuleNotFoundError: No module named 'json_repair'` endraw  piling up on the monitoring dashboard. The fallback code had been written, but the dependency was not in the production image, so it stayed dead code while error events accumulated past ten thousand (the screen is reconstructed and anonymized; the numbers are actual measurements).

Hallucinated reports: "I added one line" on an empty branch

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).

The mechanism: why does an AI say "it's done"?

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 fix: define completion by "evidence," not by "claim"

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.

# feature-done gate (do not call it "done" until it is satisfied on a real device)
- [ ] 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
Enter fullscreen mode Exit fullscreen mode

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.

Distribution drift (deploy drift): a freeze invisible to push-based hooks

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.

A pull-based parity check run at the distribution target. It reconciles the installed version against the version that should be distributed and detects 12 days of drift. Throughout that time, the quality gates stayed entirely green (the screen is reconstructed and anonymized; the period is an actual measurement).

Summary — transferable lessons

  • "Passing tests ≠ a working feature." Green from an all-mock unit suite only confirms "my expectations match my expectations." Judge boundaries as complete with real-device E2E.
  • "Implemented ≠ running in production." Back it up with contract tests that include packaging and deploy. Dead code stays green and goes silent in production.
  • Define completion by evidence, not by claim. Do not let weak proxy evidence alone — build OK, units green, deploy SUCCEED — be called "done."
  • See deploy drift with a pull-based approach. Push-based hooks cannot observe outside of git. "100% inside the repository" is not "reflected in production."

Top comments (0)