diff --git a/packages/scraper/src/robots/spec-matcher.ts b/packages/scraper/src/robots/spec-matcher.ts index 4f75ee0..ec3aa40 100644 --- a/packages/scraper/src/robots/spec-matcher.ts +++ b/packages/scraper/src/robots/spec-matcher.ts @@ -14,9 +14,18 @@ * - Max 30 competitor matches per FX product (safety cap) * * Match quality: - * confidence = 0.85 + * confidence = 0.85 (starting prior only) * match_basis = '{spec}' - * status = 'auto_approved' + * status = 'pending' — unlike OPN matches (manufacturer-confirmed ground + * truth), spec matches are a coarse JOIN with no standard_name / fiber_type / + * recent-price discrimination, so they route into the same + * maintenance:re-research-equivalences pipeline as catalog-reconcile.ts's + * pending rows (evaluateEquivalenceResearch(), scheduler.ts) for real + * confidence scoring before ever being served as an approved equivalence. + * Found live 2026-07-17: of the equivalences this matcher had previously put + * straight into 'auto_approved', ~19% failed a recent-competitor-price check + * and ~8% had a conflicting standard_name — i.e. real false-positive risk + * was going live unreviewed. */ import { pool } from "../utils/db"; @@ -39,13 +48,14 @@ const INSERT_SPEC_MATCHES = ` match_basis, match_notes, created_at, - updated_at + updated_at, + re_research_due_at ) SELECT DISTINCT fx.id AS flexoptix_id, comp.id AS competitor_id, 0.85 AS confidence, - 'auto_approved' AS status, + 'pending' AS status, ARRAY['spec'] AS match_basis, 'Spec match: ' || fx.form_factor || ' ' || fx.speed_gbps || 'G ' || CASE WHEN fx.reach_meters <= 300 THEN 'SR' @@ -57,7 +67,8 @@ const INSERT_SPEC_MATCHES = ` THEN ' @' || tip_extract_wavelength_nm(fx.wavelengths) || 'nm' ELSE '' END AS match_notes, NOW() AS created_at, - NOW() AS updated_at + NOW() AS updated_at, + NOW() AS re_research_due_at FROM transceivers fx JOIN vendors vfx ON vfx.id = fx.vendor_id AND UPPER(vfx.name) LIKE '%FLEXOPTIX%' JOIN transceivers comp