How every CiteAgentic score is computed.
Open methodology - every formula, every gate, every threshold. Scoring is data; data should be inspectable.
Brand visibility
For each scan run, brand visibility is mentioned_calls / succeeded_calls × 100, expressed as a percent.
succeeded_calls= engine calls that returned an answer (excludes failed engines)mentioned_calls= subset where the deterministic mention detector found at least one of the brand's name variants in the answer
Per-engine visibility is the same formula scoped to one engine. Overall is across all engines.
Why deterministic detection? Whole-word case-insensitive match across every variant the LLM extracted at onboarding (name, lowercase, common misspellings, the bare domain). Whole-word boundaries dodge the "Apple" matching "snapple" trap. An LLM-as-judge per mention would balloon cost and add noise on cases that are unambiguous.
Sentiment
After every scan, every confirmed mention is classified once via a single batched LLM call. Output: positive | neutral | negative. Stored on brand_mentions.sentiment for use in the regression rec and per-engine sentiment tabs.
Why batched? Per-mention LLM calls scale to N×M cost (mentions × engines). One batched call per scan, with up to 60 mentions per request, scales to one call total per run.
Share of voice
Per scan, share of voice is entity_mentions / total_succeeded_calls × 100 per entity (the brand and each tracked competitor). Sums to ≤ 100% per scan; entities not mentioned are 0%.
Time series: the dashboard plots SoV per day, per entity. Top 3 competitors by total volume in the window are surfaced as comparison lines.
Citation diversity
Shannon entropy of channel distribution, normalised to 0–100.
H = −Σ p_i × ln(p_i) for each channel i with p_i = count_i / total
H_max = ln(N) where N = number of observed channels
diversity = round(H / H_max × 100)
- All citations in one channel → 0
- Perfectly distributed across all channels → 100
Used as the headline "citation diversity" stat on the citations tab.
Citation velocity
Week-over-week delta:
thisWeek = sum(citation_count for buckets in [now-7d, now])
lastWeek = sum(citation_count for buckets in [now-14d, now-7d])
delta = thisWeek − lastWeek
deltaPct = round(delta / lastWeek × 100) if lastWeek > 0
dir = up | down | flat
Category benchmark
Each tracked brand contributes one data point: its most-recent finished run's brand-visibility percentage.
benchmark.category = brand.category (e.g. 'b2b-saas-marketing')
benchmark.brandCount = number of active brands in the category with ≥1 finished run
benchmark.avgVisibility = round(mean of all brands' visibility)
benchmark.p50/p75/p90 = percentile of the same series
Min-N gate: brandCount ≥ 5. Below the threshold the cohort is "gated" - we return the count but no aggregates so individual brands aren't reverse-engineered from the average.
Recommendations dedup
Every recommendation has a dedup_key (kind + content-hash). On re-mining, INSERT … ON CONFLICT(brand_id, dedup_key) DO NOTHING preserves the prior status. So a dismissed rec stays dismissed even if the underlying condition still applies. Sentiment regression recs bucket the dedup key weekly so a recurring regression re-emits.
Audit scoring
Each platform's scoring lives in server/scoring.js. Common pattern:
score = 100 − (15 × critical_count + 5 × warning_count + 1 × info_count)
× category_multiplier
× compound_multiplier
Capped at 0. Category multipliers are tuned so foundational/gating signals (Bot Access, Server-render, Retrieval, Chunkability) outweigh ones that are over-measured industry-wide (e.g. another schema warning).
Compound multipliers fire when paired failures co-occur: JS-only render + no JSON-LD = 1.6×, no FAQ schema + no question-form headings = 1.4×.
See the methodology applied to your own brand.
Free scan, no credit card. Walk through the actual numbers and how they were computed.