After Phase 5's dry-run gate was reviewed and approved (gate resolved: player → player_start, rerun shows 0 flips on the blesk fixture), exact matching becomes the default validation path and the old substring behavior is removed. Mechanical single-swap.
Scope so tight that no smart-discuss table is needed. Replace three lines in validateEvents; remove the old substring path.
src/utils/eventUtils.ts:111-113const hasDataLayer = eventSources.DataLayer.events.some(e => e.includes(eventSources.DataLayer.expected)); const hasGA4 = eventSources.GA4.events.some(e => e.includes(eventSources.GA4.expected)); const hasGemius = eventSources.Gemius.events.some(e => e.includes(eventSources.Gemius.expected));
const hasDataLayer = matchExpectedEvent(eventSources.DataLayer.events, eventSources.DataLayer.expected, aliasMap[functionName].dataLayer); const hasGA4 = matchExpectedEvent(eventSources.GA4.events, eventSources.GA4.expected, aliasMap[functionName].ga4); const hasGemius = matchExpectedEvent(eventSources.Gemius.events, eventSources.Gemius.expected, aliasMap[functionName].gemius);
Add imports: matchExpectedEvent from ./eventMatching, aliasMap from ../lib/Types.
validateEvents uses exact + alias matching only; no substring fallback in any code path.