GOOGLE plus CLOUD matched SoundCloud. Why the fuzzy categoriser had to go.

The keyword list for a Google category read “GOOGLE” and “CLOUD”. The match builder propagated. Two “Google One” rows appeared, as expected. Then SoundCloud appeared, via the “cloud” fragment.

That’s a false positive the user has no way to reason about after the fact. The categoriser made a plausible inference. It was wrong. Phase R v2 exists because of it.

What Phase Q was doing

Phase Q introduced a “Similar — review” propagation list. Confirm a category for a payee; the system surfaces similar-looking payees via fuzzy matching. The idea was that a confirmed categorisation could propagate outward by similarity.

The mechanics were opaque. Similarity score: not shown. Reason two payees were considered “similar”: not surfaced. The user got a list and a binary choice (accept or reject) with no way to inspect why any particular row had appeared.

The Halifax case broke it. Every Halifax card payee ends in “CD <last4>”: “CD 1234”, “CD 5678”. The fuzzy matcher treated the shared suffix as a meaningful similarity signal. Confirm a category on one Halifax payee and the system would propagate to every other Halifax payee via the “CD” fragment, regardless of what the transactions were actually for. The matcher couldn’t distinguish a structural constant from a semantic indicator. The user couldn’t tell what the matcher was doing either. That asymmetry was the problem.

Phase R v1: visible parts

Phase R replaced the propagation list with a Match Builder. The design is part-based: the system tokenises each payee name and lets the user select which parts to match on, rather than accepting an opaque similarity score.

This solved the Halifax problem directly. “CD 1234” decomposes into “CD” and “1234”. The parts are visible. The user doesn’t select “CD” as a match token because “CD” is structural noise, not a category signal. The matcher can’t treat it as meaningful because the user has already said it isn’t.

Device QA on v1 surfaced the next failure immediately.

The SoundCloud problem

The Match Builder was part-based, but the matching was still ANY-match. Select “GOOGLE” and “CLOUD”, and any payee containing either fragment qualifies. SoundCloud contains “cloud”. It matched.

This is a subtler failure than the Halifax one, because it arises from correct use of the tool rather than a quirk in the data. “CLOUD” is a real part of a real Google payee name. Selecting it wasn’t unreasonable. The problem is that “cloud” also appears in “SoundCloud”, a different service entirely.

v1 offered no recourse. Deselect “CLOUD” to fix the false positive and you lose Google Cloud rows. The user is forced to blunt a useful token to suppress a single bad match.

Phase R v2: three tools

v2 adds two mechanisms to avoid that tradeoff.

The first is per-part exclusion. Keep “CLOUD” as a match token; add “SOUNDCLOUD” to an exclude list. The builder applies exclusions before the match. SoundCloud is filtered out even though it contains “cloud”. Google Cloud rows still qualify.

The second is per-row select. Rather than relying on part tokens extracted from payee names, you can select specific rows directly and mark them as explicit category members. Any row that doesn’t fit the part-based logic cleanly gets pulled in by hand.

The two tools compose. Part-based matching handles the common case at scale; manually selecting every “GOOGLE *” row is not viable. Exclusions handle edge cases where a token’s blast radius is too wide. Per-row select handles true one-offs that don’t fit any token pattern. The workflow: define parts, review the results, add exclusions for false positives, pull in stragglers per-row.

Why transparency was the actual requirement

The SoundCloud failure looked like a logic bug. Switch from ANY-match to AND-match and SoundCloud no longer qualifies on “GOOGLE” plus “CLOUD” alone. That fixes this case, but it breaks Google rows that only contain “GOOGLE” without “CLOUD”. Tightening the logic produces false negatives. Loosening it produces false positives. No single matching strategy eliminates both.

Phase Q’s response was to route everything through the user: here are the candidates, accept or reject. The problem was that the user couldn’t reason about why a candidate appeared. Without that, accept/reject is guessing with extra clicks.

Phase R v2 answers differently. The user doesn’t just decide the outcome; the user configures the inputs. The parts list, the exclusions, and the manual selects are all visible and editable. Every match in the result set has a traceable cause: it matched this token, it was excluded by this word, it was added manually. The user can inspect that chain and change it.

Trust follows inspectability. A categoriser the user can reason about will get corrected when it’s wrong. An opaque one gets worked around or abandoned.

What this cost

Three-tool builders are more complex than a single propagation pass. v2 has state for parts, exclusions, and per-row overrides, and those three states have to be composed correctly at match time. v1 had one state: selected parts.

That complexity is warranted here because a smarter matching algorithm doesn’t solve the transparency problem. Tune the similarity function indefinitely; the SoundCloud case yields to a different edge case, the Halifax suffix yields to a different structural constant. The underlying problem isn’t match quality. The user has no way to understand or override what the matcher is doing.

Out of scope: a recommendations layer that suggests which parts to select based on existing categorisations. That would reduce initial configuration burden, but it’s an optimisation on top of transparency, not a substitute for it. Phase Q was already providing recommendations. The lesson is to get the explainability right first.

The principle

Categorisation UI has two failure modes. The first is poor match quality: the system guesses wrong. The second is poor legibility: the system is right or wrong in ways the user can’t follow. Phase Q hit both. Phase R v1 fixed the first. Phase R v2 addresses the second.

The SoundCloud false positive was never really about “cloud” being a bad token. It was about having no recourse other than removing it entirely. Give the user a named escape hatch, an exclusion list they can inspect and edit, and the problem resolves without sacrificing coverage. The system is still making inferences. It’s making them where the user can see.

All writing