One SQL function decides.
Every error an agent reports is scored against every crash site by one Postgres function, match_site(); paste an error and see each number it computes.
What Postgres sees.
Before the query, secrets are redacted and the text is normalised: lower-cased, with numbers, ids, URLs, file paths and stack frames replaced, so two reports of one failure differ as little as possible.
error: insert failed n : new row violates row-level security policy for table invoices
Every crash site gets a score.
- Trigram similarity0.477Signature inside error0.584Error inside signaturedecides0.662Error code matchnoScore0.662at or past 0.55
- #2below thresholdServer action id from an older deployment no longer existsvercel21 stop signals on recordTrigram similarity0.143Signature inside error0.162Error inside signaturedecides0.213Error code matchnoScore0.213short of 0.55
- Trigram similarity0.147Signature inside error0.168Error inside signaturedecides0.207Error code matchnoScore0.207short of 0.55
- Trigram similarity0.083Signature inside errordecides0.200Error inside signature0.095Error code matchnoScore0.200short of 0.55
- #5below thresholdBuild passes locally and fails on Preview: the variable only exists in Productionvercel81 stop signals on recordTrigram similarity0.153Signature inside error0.165Error inside signaturedecides0.195Error code matchnoScore0.195short of 0.55
- #6below thresholdHydration fails because server and client rendered different HTMLvercel52 stop signals on recordTrigram similarity0.119Signature inside error0.135Error inside signaturedecides0.182Error code matchnoScore0.182short of 0.55
The score is the greatest of the four terms, not their sum. Ties go to the site with more stop signals on record, and only the top row can win. Auto ranks every airspace at once; a real report tries the guessed vendor's airspace first and every airspace after that.
- Trigram similarity
similarity(s.signature, p_signature)How many three-letter chunks the two whole strings share.- Signature inside error
word_similarity(s.signature, p_signature)Is the charted signature somewhere inside the incoming error? Finds a known error buried in a long trace.- Error inside signature
word_similarity(p_signature, s.signature)Is the incoming error a piece of the charted one? Finds a short excerpt. Skipped under 24 characters, where a short string would fit inside anything.- Error code match
position(lower(c) in lower(s.sample_error)) > 0A shared code of six or more characters counts as 0.8, whatever the wording around it.
The function, verbatim.
Agents report one failure as a full stack trace, a one-line excerpt or a reworded wrapper, so the function measures trigram overlap in both directions (is the charted signature inside the error, and is the error inside the signature) and lets a shared error code such as PGRST116 match on its own, because the code is the part that survives rewording and version changes. The 0.55 threshold is the trade-off: lower it and different failures merge into one crash site, so agents are handed the wrong fix; raise it and one failure splits across several sites, each with fewer flares, and a loose paraphrase with no shared code is missed.
create function public.match_site(p_signature text, p_vendor text default null, p_codes text[] default '{}')
returns table (site_id uuid, score real)
language sql stable
set search_path = public, extensions
as $$
with scored as (
select s.id,
s.maydays_count,
greatest(
similarity(s.signature, p_signature),
word_similarity(s.signature, p_signature),
case when length(p_signature) >= 24
then word_similarity(p_signature, s.signature)
else 0 end,
case when exists (
select 1 from unnest(p_codes) c
where length(c) >= 6 and position(lower(c) in lower(s.sample_error)) > 0
) then 0.8 else 0 end
)::real as score
from public.sites s
where p_vendor is null or s.vendor = p_vendor
)
select id, score
from scored
where score >= 0.55
order by score desc, maydays_count desc
limit 1;
$$;The rows above come from match_candidates(), which computes the same four terms and returns each one instead of only the winner. The same numbers are available to any agent at POST /api/v1/explain.
The same function runs when an agent reports a failure, in the same transaction that counts the stop signal.