/** * Scrape score card — numeric confidence across 4 dimensions. * * 1. Completeness — did we get ALL the stores? * 2. Data quality — is each record richly populated? * 3. Source authenticity — is this a real brand-controlled source? * 4. Freshness — how recent is the data? * * Each dimension is 0-100. The overall score is a weighted composite. * Every score is backed by EVIDENCE — each check shows what it found, * its weight, and how it contributed. No black-box numbers. * * Designed to be auditable: a low score must point to specific recommendations. */ import type { RetailStore } from "../retail.js"; export interface Evidence { /** Short name of the check. */ readonly check: string; /** Human-readable result, with numbers when relevant. */ readonly result: string; /** Weight in this dimension's score (sum = 1.0 per dimension). */ readonly weight: number; /** 0-100 contribution. */ readonly score: number; /** "info" | "warn" | "fail" — for flagging. */ readonly severity: "info" | "warn" | "fail"; } export interface DimensionScore { /** 0-100. */ readonly score: number; readonly evidence: readonly Evidence[]; } export interface ScrapeScoreCard { readonly brand: string; readonly extracted: number; /** Composite 0-100. */ readonly overall: number; /** Confidence band derived from overall score. */ readonly band: "high" | "medium" | "low"; readonly completeness: DimensionScore; readonly dataQuality: DimensionScore; readonly sourceAuthenticity: DimensionScore; readonly freshness: DimensionScore; /** Anything the score card thinks the human should look at. */ readonly flags: readonly string[]; /** Concrete next steps. */ readonly recommendations: readonly string[]; } export interface BrandContext { readonly brand: string; /** Source's own claim (e.g. API returns store_count). */ readonly sourceClaimedCount?: number; /** Manually-curated count from public source (Wikipedia / annual report). */ readonly authoritativeCount?: number; /** Cite the public source for the authoritative count. */ readonly authoritativeCitation?: string; /** Acceptable drift from authoritative. Default 15%. */ readonly tolerancePct?: number; /** Cities the brand is known to have stores in (public press). */ readonly expectedCities?: readonly string[]; /** * Major metros for the metro-coverage completeness check. Defaults to * India's six metros — set this when scoring a brand in another region. */ readonly expectedMetros?: readonly string[]; /** * Bounding box for the coordinate-plausibility quality check. Defaults to * India — set this when scoring a brand in another region. */ readonly geoBounds?: { readonly minLat: number; readonly maxLat: number; readonly minLng: number; readonly maxLng: number; }; /** Source URL / API host. */ readonly sourceDomain: string; /** Brand's primary domain (for the source-authenticity check). */ readonly officialDomain: string; /** * Sibling-brand probes the recon discovered. If any returned non-empty * BUT we didn't extract them, that's an exhaustiveness gap. */ readonly siblingBrandsProbed?: readonly { name: string; expectedCount: number; extracted: boolean; }[]; /** * Manually-asserted authenticity signals. Used because we can't reliably * detect "is this a real company" programmatically — but we CAN detect * "the domain matches a public brand" etc. */ readonly authenticitySignals?: { readonly hasWikipediaArticle?: boolean; readonly publicCompany?: boolean; readonly knownBrand?: boolean; readonly domainMatchesBrand?: boolean; }; } export declare function scoreCard(stores: readonly RetailStore[], ctx: BrandContext): ScrapeScoreCard; export declare function formatScoreCard(card: ScrapeScoreCard): string;