The alert said 83 domains had migrated to Cloudflare. We checked the dashboard query first, then checked reality, and the two didn’t agree.
The board that lied by omission
The migration console is an internal admin page with its own tracking ticket. It renders a per-domain lifecycle as five dots: set up, live, secured, apex valid, old SSL removed. The www column pulls from Cloudflare’s custom-hostname API, so it shows active the moment CF issues a cert and accepts the hostname. That part is real. The bug was in what the board implied about the other half of the domain.
Every one of those 83 rows showed ✓ Cloudflare in the www column and a green dot string, which reads as “done.” But www on Cloudflare says nothing about the apex. The apex is deliberately kept off Cloudflare and served through an off-origin relay instead, on its own state (c.apexState), tracked separately in the ledger. The board had two independent trust domains: CF’s own state for www, and our lifecycle ledger for apex. It let the first one carry visual weight for both. A customer whose www went green looked migrated on the dashboard face, even when their apex CNAME had never been touched and was still resolving to us directly at origin.
We didn’t catch this by reading the legacy application’s code. We drove the live page and diffed what it rendered against DNS truth:
[...document.querySelectorAll('#cfListWrapper table tbody tr')].slice(0,3).map(r => { const tds = [...r.children]; return { domain: tds[2].innerText.trim(), state: tds[4].innerText.trim(), www: tds[7].innerText.trim() };})customer-a.example.com ns03.domaincontrol.com CNAME customer-a.example.com NOT-DONEcustomer-b.example.com ns22.domaincontrol.com CNAME customer-b.example.com NOT-DONEcustomer-c.example.com reseller-ns... A <origin> NOT-DONEAll 83 that the board marked as fully migrated were, at the apex, still pointed at us. Zero of the 83 were actually complete. We filed a ticket for the finding.
The wrong turn: auditing the write path
Our first pass at fixing the false positives didn’t touch DNS resolution at all. We assumed the ledger itself had a stale write, some race where the apex-valid dot got set true without the CNAME check actually running. So we audited the lifecycle write path and the cron that updates apexState. That cost most of an hour and turned up nothing wrong, because the code was fine. The lifecycle state machine correctly required a live apex check before flipping the dot.
The real problem was that nobody had run a fresh check against authoritative nameservers in weeks. The cached rows in our DB were just old, sitting there as green dots for domains whose DNS had since drifted or never moved. We were debugging the write logic for a table that was accurately recording stale reads.
Once we gave up on the write path and re-resolved live, the picture showed up in minutes.
Measuring the other 134
With the false positives sorted out, we ran the same live-DNS check against the full remaining list of 134 domains. Friday’s migration mailer had gone out to all of them. Live resolution showed 3 had actually acted on it. Three out of 134, four days after the email.
A third of that list can’t execute the standard instructions at all. The runbook assumes the registrar exposes a normal DNS panel the customer can edit. For a chunk of these domains, the nameserver belongs to a reseller layer we don’t have a support path into, in one case a defunct one. customer-c.example.com sits under a white-label reseller storefront, and both of the reseller’s own support domains fail to resolve. The wholesale registrar one layer up still exists, but our mailer told the customer to go to a login page that no longer has anyone behind it.
Another customer, on customer-d.example.com, was chasing the wrong domain entirely. Her live account runs on customer-d-alt.example.com, a different name on the same GoDaddy nameservers. She’d have gotten a login prompt and no way forward, and the mailer gave her no reason to suspect she had the wrong domain.
We filed three tickets. One for the reseller-blocked cohort, since it needs its own runbook branch rather than a retry of the same instructions. One for the wrong-domain case. And one for the underlying process gap: the wall detector that generates migration alerts had no mechanism to stop alerting on a domain once its deadline had already resolved one way or the other. Stale rows kept surfacing as active work days after they’d been handled or become unreachable through normal means.
Making the board say which state it means
The fix was making the board honest about which of its two states it was reporting. www green means Cloudflare accepted the hostname. It has never meant the apex moved. Once the UI stopped letting one column’s color imply the other column’s state, the count dropped from 83 false completions to zero.
The figure that mattered for deciding what to do next was 3 actions out of 134 emails sent. The next step was to route the reseller-blocked third down a different path and fix the wall detector so it stops reporting solved problems as open ones, instead of sending another reminder.
What I’d check on any migration board
A green status on one part of a migration says nothing about the other parts. Check whether each state, here www and the apex, is shown separately and verified live against authoritative DNS rather than cached records. Counting emails sent is not counting customers who acted, so measure the second before planning the next step.