How the accuracy net corrects a wrong Lodge/Post number, why the safe-looking regex silently no-opped, and why over-correcting it is worse than the original bug.
includes/fact-guard.php is the network accuracy net: it overwrites AI drift on facts we hold canonically. Rule 1 is the organization identity number (Elks/Moose "Lodge", VFW/American Legion "Post", Eagles "Aerie", K of C "Council"). Two hooks, zero API cost: a render-layer heal in business-site.php and a DB-boundary heal in saveGeneratedContent(). Repair tool: dz factguard (audit / per-business decontaminate / heal=stored).
Shipped v2.2.318, the corrector required a No. / Number / # separator between the keyword and the digits, deliberately, so ordinary prose ("post 2020 renovation") could never be mangled. It was correct and it was tested. But the hallucination in the wild is uniformly the bare form: "Elks Lodge 339 in Plentywood". The heal matched nothing, changed nothing, threw nothing, and read as working for two months (operator note #18, July 20 2026).
Two things compounded it:
factGuardHeal() call sites passed no $opts, so the number was derived fromthe business NAME. Many lodges are scraped as plain "Elks Lodge" or "Elk's Club" with no number at all, so the heal was a guaranteed no-op for exactly the sites that needed it.
businesses has no domain column and bothgeneration entry points load the row with a bare SELECT *, so the subdomain — the one identity fact we MINT rather than scrape — was out of scope in buildBusinessContext(). Meanwhile the prose brief asks for "a concrete fact: founding/charter year". The prompt rewarded inventing precisely the thing nothing supplied.
Takeaway: a healer that cannot report what it changed is indistinguishable from a healer that does nothing. dz factguard heal=stored now emits a real before/after per row.
The first cut of the bare-form rule rewrote every lodge number in the content to ours. On live pages that produced:
Freemasons of Newark Lodge #83 → #1249 (a Masonic lodge sharing the building)a party of six from Lodge 401 → Lodge 1249close relationship with Dallas Elks Lodge 71 → Dallas Elks Lodge 2485Each trades one wrong fact for a false claim about a different real organization. Enumerating "foreign cues" kept losing, so the rule was inverted: only correct a mention that is positively OURS. A mention is skipped when preceded by a foreign-order or other-lodge cue, by from, or by a place name that is not one of our own city tokens.
Leaving another lodge's number intact is not a false claim about us. Changing it is a
false claim about them. The tie always breaks toward leaving it.
The cost of that conservatism is real and accepted: a handful of pages simply never state their own number in prose. That is a gap, not a lie.
?LodgeNumber=0031 is lodge 31. A stringcompare flagged ~130 correct rows as contaminated and would have rewritten them all.
ai_generated_content differs inslash/unicode escaping even when nothing changed, which reported ~170 phantom rewrites.
\d{2,6} floorsilently skipped them.
Plentywood (business 1658) had the wrong lodge in its own record: website pointing at LodgeNumber=339, email secretary@alleghenyelks.org, a Pittsburgh 412 phone in deep_research. Enrichment scraped the wrong lodge; the generator faithfully wrote what it was fed. Signal in, signal out. Seven elks businesses had a LodgeNumber= URL contradicting their subdomain, two of which still got the right number in content, so the bad-website problem is slightly wider than the bad-content one.
That leaked ownership, which mattered more than the copy. With admin_email NULL, the scraped email IS the implicit owner ([[project_implicit_verification]]), so an officer of the Pittsburgh lodge could have signed in by magic link and managed a Montana lodge's site. Three businesses were in that state. A scrape of the wrong entity is untrustworthy field-by-field, so decontamination drops the derived scrape wholesale (deep_research.contact_email, additional_phones, enrichment provenance) and re-points website at the lodge's own canonical page. Google's geo-verified phone and address are never touched, and an email naming our own lodge or city is KEPT.
Verification method that settled every case: fetch elks.org/lodges/home.cfm?LodgeNumber=N and compare its city/ZIP to the business's formatted_address. All seven subdomains proved correct at ZIP level; the stored websites were the wrong ones.
After the websites were corrected, Flora IL still linked to Carmichael CA's Facebook and rendered Carmichael's photo as its hero, because the first pass cleared four keys and left social_media, images, logo_url. Camillus NY had a correct website but only Chicago lodge 2367's images. Wrong photo and wrong social link are worse than a wrong number, and neither is reachable by a text healer.
Detection took three tries; the failures are the useful part:
~elks(\d+)~ read Facebook page ids and image dimensions(WoburnStonehamElks-150x150.png → "150") as charter numbers, condemning 5 clean rows.
lodge's asset (a state president's photo, a shared bingo graphic).
only when none of the assets are ours.
json_encode writes \/, so a char class stopping at the backslash truncates theURL to www.elks.org and hides the path carrying the number. Un-escape first.
purge=1 forces the purge for a hand-verified case, because repairing the website first removes the signal the detector keys on.
Two businesses (685, 1601) carry a spurious second elks domain whose lodge number differs from the one their content correctly states. That is a domain-mapping problem, not an accuracy one; dz domdupes disable=<domain_id> is the tool. dz factguard refuses any business whose elks domains disagree, so it cannot "fix" one of these by corrupting good data.
See also: [[elks-root-theme]], [[content-generation]], [[source-of-truth-cache]], [[dev-api]].