How I Found and Fixed a Critical Bug in My Own Keyword Clustering Tool (13 Test Runs Later

    I build KeywordArchitect, an AI keyword clustering tool for Google Ads. Last week I came across a Reddit post from a PPC manager who said something that stuck with me:

    ·10 min read

    "Every tool still sucks at handling mixed-intent keywords. You'll get a cluster that mixes informational how-to queries with transactional buy queries just because they share entities, even if the SERP is totally split. I still end up manually eye-balling the final sheet."

    I figured this was a good opportunity to stress-test my own tool properly. What followed was 13 test runs, one confirmed critical bug, and a series of fixes that made the output genuinely production-ready. Here's the full breakdown.

    Run 1 and 2: The $0 problem
    My first tests came back with Campaign Analytics showing $0 for impressions, clicks, cost and budget estimates across the board. I assumed it was a bug and started writing Lovable prompts to fix it.
    Turns out it wasn't a bug at all. I was testing with a keywords-only list that had no volume or CPC data attached. The tool had nothing to calculate from. Lesson learned: always test with realistic data including volume, CPC and competition columns.

    Run 3: The navigational keyword problem
    Once I added proper volume data, a new issue appeared. The tool was including navigational keywords like "LegalZoom sign in", "Avvo lawyer search", "PACER login" and "jail inmate lookup" in the campaign output. Nobody running a law firm should be bidding on those. They have zero conversion potential and would burn budget immediately.
    I added a filter to exclude navigational keywords before clustering. But my first prompt was too vague and the tool responded by creating a brand new "Brand and Competitor" campaign instead of removing them entirely. One tighter prompt specifying "do not create a campaign for them, remove them from all output entirely" fixed it.

    Runs 4 through 8: The intent separation problem
    This is where it got interesting. The three-campaign structure I was aiming for was High-Intent with Exact match for hire-ready keywords, Commercial Investigation with Phrase match for comparison and cost queries, and Research and Informational with Broad match for how and what queries.
    The tool kept getting this wrong in different ways across multiple runs. Sometimes it sorted by CPC value instead of intent, putting high-CPC informational queries into the High-Intent campaign. Sometimes it created the wrong number of campaigns. Sometimes the ad group names were generic numbered labels instead of practice area names.
    Each run taught me something and I refined the master prompt accordingly until I landed on a clean three-rule structure based purely on keyword intent signals, never CPC value.

    Runs 9 through 11: The disappearing transactional keywords
    This was the most frustrating phase. I added an Intent column to my test file explicitly labelling 50 keywords as "transactional". Every single one of them disappeared from every output. The High-Intent campaign kept showing up with four or five informational queries in it instead.
    I tried multiple prompt fixes. Nothing worked. So I asked Lovable to show me the actual code rather than just patch it blindly.

    That was the right call.
    The real bug: shouldExclude substring matching
    The code revealed a filter called shouldExclude running before clustering that used plain .includes() substring matching against a blocklist. The blocklist included terms like "find a lawyer", "portal" and "directory" to prevent navigational keywords from entering the output.
    The problem was that .includes() matches anywhere in a string. So "find a divorce lawyer near me" was being killed by the "find a lawyer" pattern. "Client portal lawyer" was killed by "portal". Dozens of completely legitimate transactional keywords were being silently deleted before the clustering engine ever saw them.
    The edge function logs confirmed it: the [CLUSTER] Excluded N non-converting keywords line was showing N of approximately 50. Every single one of my transactional test keywords was hitting the filter as a false positive.
    The fix was switching from plain substring matching to word-boundary regex and tightening the blocklist so terms like "portal" and "directory" only excluded keywords where those terms were the primary intent, not when they appeared alongside a clear service signal like lawyer, attorney, plumber or dentist.

    Runs 12 and 13: Clean output
    Run 12 was the first output where all 50 transactional keywords appeared correctly in the High-Intent campaign with Exact match. Run 13 cleaned up the ad group naming to use practice area labels instead of numbered placeholders.
    The final structure across 123 keywords came out as 29 High-Intent keywords in practice area ad groups like Divorce and Family Law, Bankruptcy, Immigration and Employment Law. 44 Commercial Investigation keywords in Phrase match covering reviews, cost comparisons and settlement calculators. And 50 Research and Informational keywords in Broad match covering all the how and what queries.

    No duplicates across campaigns. No navigational keywords. No intent bleed between campaign types.
    What this means for the tool
    The Reddit poster's frustration was valid. Most tools do produce mixed-intent clusters and most users never find out why because the bug is silent. Keywords disappear before clustering starts and the tool just outputs whatever survives the filter.

    KeywordArchitect now separates intent at the campaign level automatically, transactional keywords in High-Intent, commercial in Commercial Investigation, informational in Research and Informational, with the right match types assigned to each. If you upload a keyword file with an Intent column it reads it directly. If you don't, it infers intent from keyword signals.

    The shouldExclude bug is fixed. The filter now uses word-boundary matching and only removes true navigational keywords like branded login pages and government database lookups, not transactional service keywords that happen to contain a common word.
    Thirteen runs to get here. Worth it.