§ Track record

What we caught, how fast, and what we kept out

Our own record, counted every hour from what we published. Tap any number to see exactly how it is counted.

Counted

What moved

833 retail moves in the last 7 days, including 161 on stores and expansion, 60 on funding and deals and 39 leadership moves.

Three worth knowing

Retail moves by kind, last 7 days
  • Launches, pricing and campaigns221
  • Results, demand and outlook176
  • Stores and expansion161
  • Regulation, legal and governance79
  • Funding, deals and listings60
  • Leadership moves39
  • Strategy, partners and operations30
  • Everything else67

5,638 retail moves in the last 30 days.

How fast

When one outlet breaks a story and another follows, you would have read it here before the second outlet published on 90% of them in the last 30 days, a median 3 h 51 min earlier.

Behind the outlet that breaks it
median 4.5 min; 9 in 10 within 16.9 min (1,047 breaking stories)
Ahead of the next outlet
first on 549 of the 604 breaking stories a second outlet also ran

For example, 12 Oct 2026

ASICS reaches 150 India stores with 1,508-square-foot Jaipur outlet at Pacific Mall
  1. IMAGES Business of Fashion breaks it
  2. On RetailIntel, 3 min after it broke
  3. Fashion Value Chain, 3 h 34 min after us

Times IST.

What we kept out

Old news stays out of your inbox. In the last 30 days we held back 609 stories first published 14 or more days before we found them, and 742 more went out marked as old news, never as an alert.

Is it up

RetailIntel answered 99.4% of our 347 five-minute checks since 15 Oct, 22:40 IST, and the MCP server for AI assistants 99.4%.

Site
  1. 18 Sept 2026: not checked yet
  2. 19 Sept 2026: not checked yet
  3. 20 Sept 2026: not checked yet
  4. 21 Sept 2026: not checked yet
  5. 22 Sept 2026: not checked yet
  6. 23 Sept 2026: not checked yet
  7. 24 Sept 2026: not checked yet
  8. 25 Sept 2026: not checked yet
  9. 26 Sept 2026: not checked yet
  10. 27 Sept 2026: not checked yet
  11. 28 Sept 2026: not checked yet
  12. 29 Sept 2026: not checked yet
  13. 30 Sept 2026: not checked yet
  14. 1 Oct 2026: not checked yet
  15. 2 Oct 2026: not checked yet
  16. 3 Oct 2026: not checked yet
  17. 4 Oct 2026: not checked yet
  18. 5 Oct 2026: not checked yet
  19. 6 Oct 2026: not checked yet
  20. 7 Oct 2026: not checked yet
  21. 8 Oct 2026: not checked yet
  22. 9 Oct 2026: not checked yet
  23. 10 Oct 2026: not checked yet
  24. 11 Oct 2026: not checked yet
  25. 12 Oct 2026: not checked yet
  26. 13 Oct 2026: not checked yet
  27. 14 Oct 2026: not checked yet
  28. 15 Oct 2026: up 16 of 16 checks
  29. 16 Oct 2026: up 286 of 288 checks
  30. 17 Oct 2026: up 43 of 43 checks
MCP
  1. 18 Sept 2026: not checked yet
  2. 19 Sept 2026: not checked yet
  3. 20 Sept 2026: not checked yet
  4. 21 Sept 2026: not checked yet
  5. 22 Sept 2026: not checked yet
  6. 23 Sept 2026: not checked yet
  7. 24 Sept 2026: not checked yet
  8. 25 Sept 2026: not checked yet
  9. 26 Sept 2026: not checked yet
  10. 27 Sept 2026: not checked yet
  11. 28 Sept 2026: not checked yet
  12. 29 Sept 2026: not checked yet
  13. 30 Sept 2026: not checked yet
  14. 1 Oct 2026: not checked yet
  15. 2 Oct 2026: not checked yet
  16. 3 Oct 2026: not checked yet
  17. 4 Oct 2026: not checked yet
  18. 5 Oct 2026: not checked yet
  19. 6 Oct 2026: not checked yet
  20. 7 Oct 2026: not checked yet
  21. 8 Oct 2026: not checked yet
  22. 9 Oct 2026: not checked yet
  23. 10 Oct 2026: not checked yet
  24. 11 Oct 2026: not checked yet
  25. 12 Oct 2026: not checked yet
  26. 13 Oct 2026: not checked yet
  27. 14 Oct 2026: not checked yet
  28. 15 Oct 2026: up 16 of 16 checks
  29. 16 Oct 2026: up 286 of 288 checks
  30. 17 Oct 2026: up 43 of 43 checks

all up 99%+ under 99% not checked

346 of 347 checks recorded; a check that did not run counts as down.

How we count

Retail moves

Stories we published in the window, by the time we published them, that are not old news and pass the same retail check as the home page: a phone launch or a hotel signing is not a retail move. The kind of move is the story's stored kind, grouped the way Competitor Watch groups it.

The query
SELECT s.id, s.slug, s.headline, s.kind, s.primary_theme, s.score, s.source_name,
       s.created_at, s.recency_class,
       CASE WHEN ri.published_at_confidence = 'time' AND s.event_time_confidence = 'trusted'
            THEN ri.published_at END AS outlet_at,
       COALESCE(o.hosts, 0)::int AS other_outlets, o.first_other_at, o.first_other_name
  FROM clarity.signals s
  INNER JOIN clarity.sources src ON src.id = s.source_id
  LEFT JOIN clarity.raw_items ri ON ri.id = s.raw_item_id
  LEFT JOIN LATERAL (
    SELECT count(DISTINCT pub.alias_host)::int AS hosts,
           min(ari.published_at) FILTER (WHERE ari.published_at_confidence = 'time') AS first_other_at,
           (array_agg(alias_src.name ORDER BY ari.published_at)
              FILTER (WHERE ari.published_at_confidence = 'time'))[1] AS first_other_name
      FROM clarity.signal_url_aliases a
      JOIN clarity.sources alias_src ON alias_src.id = a.source_id
      LEFT JOIN clarity.raw_items ari ON ari.id = a.raw_item_id
      CROSS JOIN LATERAL (
             SELECT regexp_replace(lower(regexp_replace(substring(a.alias_url FROM '^[A-Za-z][A-Za-z0-9+.-]*://([^/?#:@]+)'), '^www[.]', '')), '^.+[.]economictimes[.]indiatimes[.]com$', 'economictimes.indiatimes.com') AS alias_host,
                    regexp_replace(lower(regexp_replace(substring(alias_src.url FROM '^[A-Za-z][A-Za-z0-9+.-]*://([^/?#:@]+)'), '^www[.]', '')), '^.+[.]economictimes[.]indiatimes[.]com$', 'economictimes.indiatimes.com') AS alias_feed_host,
                    regexp_replace(lower(regexp_replace(substring(src.url FROM '^[A-Za-z][A-Za-z0-9+.-]*://([^/?#:@]+)'), '^www[.]', '')), '^.+[.]economictimes[.]indiatimes[.]com$', 'economictimes.indiatimes.com') AS own_host
           ) pub
     WHERE a.signal_id = s.id
       AND alias_src.vertical = 'retail'
AND alias_src.enabled IS TRUE
AND alias_src.is_private IS FALSE
AND alias_src.owner_user_id IS NULL
       AND (a.source_id <> s.source_id
            AND pub.alias_host IS NOT NULL
            AND pub.alias_feed_host IS NOT NULL
            AND (pub.own_host IS NULL OR (
                pub.alias_host <> pub.own_host
                AND right(pub.alias_host, length(pub.own_host) + 1) <> '.' || pub.own_host
                AND right(pub.own_host, length(pub.alias_host) + 1) <> '.' || pub.alias_host
                AND pub.alias_feed_host <> pub.own_host
                AND right(pub.alias_feed_host, length(pub.own_host) + 1) <> '.' || pub.own_host
                AND right(pub.own_host, length(pub.alias_feed_host) + 1) <> '.' || pub.alias_feed_host)))
  ) o ON true
 WHERE src.vertical = 'retail'
AND src.enabled IS TRUE
AND src.is_private IS FALSE
AND src.owner_user_id IS NULL
AND s.vertical = 'retail'
AND s.owner_user_id IS NULL
AND isfinite(s.published_at)
AND s.published_at >= (
  TIMESTAMP '0001-01-01 00:00:00 BC' AT TIME ZONE 'UTC'
)
AND s.published_at < TIMESTAMPTZ '10000-01-01 00:00:00+00'
AND (s.event_time_utc IS NULL OR isfinite(s.event_time_utc))
AND (s.event_time_utc IS NULL OR (
  s.event_time_utc >= (
    TIMESTAMP '0001-01-01 00:00:00 BC' AT TIME ZONE 'UTC'
  )
  AND s.event_time_utc < TIMESTAMPTZ '10000-01-01 00:00:00+00'
))
AND (s.first_seen_at_utc IS NULL OR isfinite(s.first_seen_at_utc))
AND (s.first_seen_at_utc IS NULL OR (
  s.first_seen_at_utc >= (
    TIMESTAMP '0001-01-01 00:00:00 BC' AT TIME ZONE 'UTC'
  )
  AND s.first_seen_at_utc < TIMESTAMPTZ '10000-01-01 00:00:00+00'
))
AND s.id NOT IN ('sig_bd766794701d3d9f')
   AND s.superseded_at IS NULL
   AND NULLIF(BTRIM(s.slug), '') IS NOT NULL
   AND s.created_at >= $1 AND s.created_at <= $2

Up before the next outlet

Breaking or fresh stories from the last 30 days that at least one other independent outlet also ran (a different source on a different website), where that outlet stamped its publish time to the minute. "Up first" means our page went live before the earliest of those outlets published. The median lead is over the stories we had first, rounded down to the minute. We can only compare against outlets we read.

The query
SELECT s.id, s.slug, s.headline, s.kind, s.primary_theme, s.score, s.source_name,
       s.created_at, s.recency_class,
       CASE WHEN ri.published_at_confidence = 'time' AND s.event_time_confidence = 'trusted'
            THEN ri.published_at END AS outlet_at,
       COALESCE(o.hosts, 0)::int AS other_outlets, o.first_other_at, o.first_other_name
  FROM clarity.signals s
  INNER JOIN clarity.sources src ON src.id = s.source_id
  LEFT JOIN clarity.raw_items ri ON ri.id = s.raw_item_id
  LEFT JOIN LATERAL (
    SELECT count(DISTINCT pub.alias_host)::int AS hosts,
           min(ari.published_at) FILTER (WHERE ari.published_at_confidence = 'time') AS first_other_at,
           (array_agg(alias_src.name ORDER BY ari.published_at)
              FILTER (WHERE ari.published_at_confidence = 'time'))[1] AS first_other_name
      FROM clarity.signal_url_aliases a
      JOIN clarity.sources alias_src ON alias_src.id = a.source_id
      LEFT JOIN clarity.raw_items ari ON ari.id = a.raw_item_id
      CROSS JOIN LATERAL (
             SELECT regexp_replace(lower(regexp_replace(substring(a.alias_url FROM '^[A-Za-z][A-Za-z0-9+.-]*://([^/?#:@]+)'), '^www[.]', '')), '^.+[.]economictimes[.]indiatimes[.]com$', 'economictimes.indiatimes.com') AS alias_host,
                    regexp_replace(lower(regexp_replace(substring(alias_src.url FROM '^[A-Za-z][A-Za-z0-9+.-]*://([^/?#:@]+)'), '^www[.]', '')), '^.+[.]economictimes[.]indiatimes[.]com$', 'economictimes.indiatimes.com') AS alias_feed_host,
                    regexp_replace(lower(regexp_replace(substring(src.url FROM '^[A-Za-z][A-Za-z0-9+.-]*://([^/?#:@]+)'), '^www[.]', '')), '^.+[.]economictimes[.]indiatimes[.]com$', 'economictimes.indiatimes.com') AS own_host
           ) pub
     WHERE a.signal_id = s.id
       AND alias_src.vertical = 'retail'
AND alias_src.enabled IS TRUE
AND alias_src.is_private IS FALSE
AND alias_src.owner_user_id IS NULL
       AND (a.source_id <> s.source_id
            AND pub.alias_host IS NOT NULL
            AND pub.alias_feed_host IS NOT NULL
            AND (pub.own_host IS NULL OR (
                pub.alias_host <> pub.own_host
                AND right(pub.alias_host, length(pub.own_host) + 1) <> '.' || pub.own_host
                AND right(pub.own_host, length(pub.alias_host) + 1) <> '.' || pub.alias_host
                AND pub.alias_feed_host <> pub.own_host
                AND right(pub.alias_feed_host, length(pub.own_host) + 1) <> '.' || pub.own_host
                AND right(pub.own_host, length(pub.alias_feed_host) + 1) <> '.' || pub.alias_feed_host)))
  ) o ON true
 WHERE src.vertical = 'retail'
AND src.enabled IS TRUE
AND src.is_private IS FALSE
AND src.owner_user_id IS NULL
AND s.vertical = 'retail'
AND s.owner_user_id IS NULL
AND isfinite(s.published_at)
AND s.published_at >= (
  TIMESTAMP '0001-01-01 00:00:00 BC' AT TIME ZONE 'UTC'
)
AND s.published_at < TIMESTAMPTZ '10000-01-01 00:00:00+00'
AND (s.event_time_utc IS NULL OR isfinite(s.event_time_utc))
AND (s.event_time_utc IS NULL OR (
  s.event_time_utc >= (
    TIMESTAMP '0001-01-01 00:00:00 BC' AT TIME ZONE 'UTC'
  )
  AND s.event_time_utc < TIMESTAMPTZ '10000-01-01 00:00:00+00'
))
AND (s.first_seen_at_utc IS NULL OR isfinite(s.first_seen_at_utc))
AND (s.first_seen_at_utc IS NULL OR (
  s.first_seen_at_utc >= (
    TIMESTAMP '0001-01-01 00:00:00 BC' AT TIME ZONE 'UTC'
  )
  AND s.first_seen_at_utc < TIMESTAMPTZ '10000-01-01 00:00:00+00'
))
AND s.id NOT IN ('sig_bd766794701d3d9f')
   AND s.superseded_at IS NULL
   AND NULLIF(BTRIM(s.slug), '') IS NOT NULL
   AND s.created_at >= $1 AND s.created_at <= $2

Outlet to our page

Breaking or fresh stories whose outlet stamped the publish time to the minute, and whose event time we verified as that time. Minutes from it to our page going live, rounded up to the next tenth of a minute.

The query
SELECT s.id, s.slug, s.headline, s.kind, s.primary_theme, s.score, s.source_name,
       s.created_at, s.recency_class,
       CASE WHEN ri.published_at_confidence = 'time' AND s.event_time_confidence = 'trusted'
            THEN ri.published_at END AS outlet_at,
       COALESCE(o.hosts, 0)::int AS other_outlets, o.first_other_at, o.first_other_name
  FROM clarity.signals s
  INNER JOIN clarity.sources src ON src.id = s.source_id
  LEFT JOIN clarity.raw_items ri ON ri.id = s.raw_item_id
  LEFT JOIN LATERAL (
    SELECT count(DISTINCT pub.alias_host)::int AS hosts,
           min(ari.published_at) FILTER (WHERE ari.published_at_confidence = 'time') AS first_other_at,
           (array_agg(alias_src.name ORDER BY ari.published_at)
              FILTER (WHERE ari.published_at_confidence = 'time'))[1] AS first_other_name
      FROM clarity.signal_url_aliases a
      JOIN clarity.sources alias_src ON alias_src.id = a.source_id
      LEFT JOIN clarity.raw_items ari ON ari.id = a.raw_item_id
      CROSS JOIN LATERAL (
             SELECT regexp_replace(lower(regexp_replace(substring(a.alias_url FROM '^[A-Za-z][A-Za-z0-9+.-]*://([^/?#:@]+)'), '^www[.]', '')), '^.+[.]economictimes[.]indiatimes[.]com$', 'economictimes.indiatimes.com') AS alias_host,
                    regexp_replace(lower(regexp_replace(substring(alias_src.url FROM '^[A-Za-z][A-Za-z0-9+.-]*://([^/?#:@]+)'), '^www[.]', '')), '^.+[.]economictimes[.]indiatimes[.]com$', 'economictimes.indiatimes.com') AS alias_feed_host,
                    regexp_replace(lower(regexp_replace(substring(src.url FROM '^[A-Za-z][A-Za-z0-9+.-]*://([^/?#:@]+)'), '^www[.]', '')), '^.+[.]economictimes[.]indiatimes[.]com$', 'economictimes.indiatimes.com') AS own_host
           ) pub
     WHERE a.signal_id = s.id
       AND alias_src.vertical = 'retail'
AND alias_src.enabled IS TRUE
AND alias_src.is_private IS FALSE
AND alias_src.owner_user_id IS NULL
       AND (a.source_id <> s.source_id
            AND pub.alias_host IS NOT NULL
            AND pub.alias_feed_host IS NOT NULL
            AND (pub.own_host IS NULL OR (
                pub.alias_host <> pub.own_host
                AND right(pub.alias_host, length(pub.own_host) + 1) <> '.' || pub.own_host
                AND right(pub.own_host, length(pub.alias_host) + 1) <> '.' || pub.alias_host
                AND pub.alias_feed_host <> pub.own_host
                AND right(pub.alias_feed_host, length(pub.own_host) + 1) <> '.' || pub.own_host
                AND right(pub.own_host, length(pub.alias_feed_host) + 1) <> '.' || pub.alias_feed_host)))
  ) o ON true
 WHERE src.vertical = 'retail'
AND src.enabled IS TRUE
AND src.is_private IS FALSE
AND src.owner_user_id IS NULL
AND s.vertical = 'retail'
AND s.owner_user_id IS NULL
AND isfinite(s.published_at)
AND s.published_at >= (
  TIMESTAMP '0001-01-01 00:00:00 BC' AT TIME ZONE 'UTC'
)
AND s.published_at < TIMESTAMPTZ '10000-01-01 00:00:00+00'
AND (s.event_time_utc IS NULL OR isfinite(s.event_time_utc))
AND (s.event_time_utc IS NULL OR (
  s.event_time_utc >= (
    TIMESTAMP '0001-01-01 00:00:00 BC' AT TIME ZONE 'UTC'
  )
  AND s.event_time_utc < TIMESTAMPTZ '10000-01-01 00:00:00+00'
))
AND (s.first_seen_at_utc IS NULL OR isfinite(s.first_seen_at_utc))
AND (s.first_seen_at_utc IS NULL OR (
  s.first_seen_at_utc >= (
    TIMESTAMP '0001-01-01 00:00:00 BC' AT TIME ZONE 'UTC'
  )
  AND s.first_seen_at_utc < TIMESTAMPTZ '10000-01-01 00:00:00+00'
))
AND s.id NOT IN ('sig_bd766794701d3d9f')
   AND s.superseded_at IS NULL
   AND NULLIF(BTRIM(s.slug), '') IS NOT NULL
   AND s.created_at >= $1 AND s.created_at <= $2

Old news held back

Articles we read in the last 30 days and did not publish, because the page itself was first published 14 or more days before we found it.

The query
SELECT count(*)::int AS n
  FROM clarity.raw_items ri
  JOIN clarity.sources src ON src.id = ri.source_id
 WHERE src.vertical = 'retail'
AND src.enabled IS TRUE
AND src.is_private IS FALSE
AND src.owner_user_id IS NULL
   AND ri.council_decision IN ('held_page_old', 'held_spot_old')
   AND ri.council_decided_at >= $1 AND ri.council_decided_at <= $2

Marked as old news

Stories published in the last 30 days that we marked as old news (first reported well before we found them). They stay on their page with the label and never go out as email or webhook alerts.

The query
SELECT s.id, s.slug, s.headline, s.kind, s.primary_theme, s.score, s.source_name,
       s.created_at, s.recency_class,
       CASE WHEN ri.published_at_confidence = 'time' AND s.event_time_confidence = 'trusted'
            THEN ri.published_at END AS outlet_at,
       COALESCE(o.hosts, 0)::int AS other_outlets, o.first_other_at, o.first_other_name
  FROM clarity.signals s
  INNER JOIN clarity.sources src ON src.id = s.source_id
  LEFT JOIN clarity.raw_items ri ON ri.id = s.raw_item_id
  LEFT JOIN LATERAL (
    SELECT count(DISTINCT pub.alias_host)::int AS hosts,
           min(ari.published_at) FILTER (WHERE ari.published_at_confidence = 'time') AS first_other_at,
           (array_agg(alias_src.name ORDER BY ari.published_at)
              FILTER (WHERE ari.published_at_confidence = 'time'))[1] AS first_other_name
      FROM clarity.signal_url_aliases a
      JOIN clarity.sources alias_src ON alias_src.id = a.source_id
      LEFT JOIN clarity.raw_items ari ON ari.id = a.raw_item_id
      CROSS JOIN LATERAL (
             SELECT regexp_replace(lower(regexp_replace(substring(a.alias_url FROM '^[A-Za-z][A-Za-z0-9+.-]*://([^/?#:@]+)'), '^www[.]', '')), '^.+[.]economictimes[.]indiatimes[.]com$', 'economictimes.indiatimes.com') AS alias_host,
                    regexp_replace(lower(regexp_replace(substring(alias_src.url FROM '^[A-Za-z][A-Za-z0-9+.-]*://([^/?#:@]+)'), '^www[.]', '')), '^.+[.]economictimes[.]indiatimes[.]com$', 'economictimes.indiatimes.com') AS alias_feed_host,
                    regexp_replace(lower(regexp_replace(substring(src.url FROM '^[A-Za-z][A-Za-z0-9+.-]*://([^/?#:@]+)'), '^www[.]', '')), '^.+[.]economictimes[.]indiatimes[.]com$', 'economictimes.indiatimes.com') AS own_host
           ) pub
     WHERE a.signal_id = s.id
       AND alias_src.vertical = 'retail'
AND alias_src.enabled IS TRUE
AND alias_src.is_private IS FALSE
AND alias_src.owner_user_id IS NULL
       AND (a.source_id <> s.source_id
            AND pub.alias_host IS NOT NULL
            AND pub.alias_feed_host IS NOT NULL
            AND (pub.own_host IS NULL OR (
                pub.alias_host <> pub.own_host
                AND right(pub.alias_host, length(pub.own_host) + 1) <> '.' || pub.own_host
                AND right(pub.own_host, length(pub.alias_host) + 1) <> '.' || pub.alias_host
                AND pub.alias_feed_host <> pub.own_host
                AND right(pub.alias_feed_host, length(pub.own_host) + 1) <> '.' || pub.own_host
                AND right(pub.own_host, length(pub.alias_feed_host) + 1) <> '.' || pub.alias_feed_host)))
  ) o ON true
 WHERE src.vertical = 'retail'
AND src.enabled IS TRUE
AND src.is_private IS FALSE
AND src.owner_user_id IS NULL
AND s.vertical = 'retail'
AND s.owner_user_id IS NULL
AND isfinite(s.published_at)
AND s.published_at >= (
  TIMESTAMP '0001-01-01 00:00:00 BC' AT TIME ZONE 'UTC'
)
AND s.published_at < TIMESTAMPTZ '10000-01-01 00:00:00+00'
AND (s.event_time_utc IS NULL OR isfinite(s.event_time_utc))
AND (s.event_time_utc IS NULL OR (
  s.event_time_utc >= (
    TIMESTAMP '0001-01-01 00:00:00 BC' AT TIME ZONE 'UTC'
  )
  AND s.event_time_utc < TIMESTAMPTZ '10000-01-01 00:00:00+00'
))
AND (s.first_seen_at_utc IS NULL OR isfinite(s.first_seen_at_utc))
AND (s.first_seen_at_utc IS NULL OR (
  s.first_seen_at_utc >= (
    TIMESTAMP '0001-01-01 00:00:00 BC' AT TIME ZONE 'UTC'
  )
  AND s.first_seen_at_utc < TIMESTAMPTZ '10000-01-01 00:00:00+00'
))
AND s.id NOT IN ('sig_bd766794701d3d9f')
   AND s.superseded_at IS NULL
   AND NULLIF(BTRIM(s.slug), '') IS NOT NULL
   AND s.created_at >= $1 AND s.created_at <= $2

Two or more independent outlets

New stories (not old news) from the last 30 days that at least one other outlet on a different website also ran: the same count a signal page shows. Shares are rounded down.

The query
SELECT s.id, s.slug, s.headline, s.kind, s.primary_theme, s.score, s.source_name,
       s.created_at, s.recency_class,
       CASE WHEN ri.published_at_confidence = 'time' AND s.event_time_confidence = 'trusted'
            THEN ri.published_at END AS outlet_at,
       COALESCE(o.hosts, 0)::int AS other_outlets, o.first_other_at, o.first_other_name
  FROM clarity.signals s
  INNER JOIN clarity.sources src ON src.id = s.source_id
  LEFT JOIN clarity.raw_items ri ON ri.id = s.raw_item_id
  LEFT JOIN LATERAL (
    SELECT count(DISTINCT pub.alias_host)::int AS hosts,
           min(ari.published_at) FILTER (WHERE ari.published_at_confidence = 'time') AS first_other_at,
           (array_agg(alias_src.name ORDER BY ari.published_at)
              FILTER (WHERE ari.published_at_confidence = 'time'))[1] AS first_other_name
      FROM clarity.signal_url_aliases a
      JOIN clarity.sources alias_src ON alias_src.id = a.source_id
      LEFT JOIN clarity.raw_items ari ON ari.id = a.raw_item_id
      CROSS JOIN LATERAL (
             SELECT regexp_replace(lower(regexp_replace(substring(a.alias_url FROM '^[A-Za-z][A-Za-z0-9+.-]*://([^/?#:@]+)'), '^www[.]', '')), '^.+[.]economictimes[.]indiatimes[.]com$', 'economictimes.indiatimes.com') AS alias_host,
                    regexp_replace(lower(regexp_replace(substring(alias_src.url FROM '^[A-Za-z][A-Za-z0-9+.-]*://([^/?#:@]+)'), '^www[.]', '')), '^.+[.]economictimes[.]indiatimes[.]com$', 'economictimes.indiatimes.com') AS alias_feed_host,
                    regexp_replace(lower(regexp_replace(substring(src.url FROM '^[A-Za-z][A-Za-z0-9+.-]*://([^/?#:@]+)'), '^www[.]', '')), '^.+[.]economictimes[.]indiatimes[.]com$', 'economictimes.indiatimes.com') AS own_host
           ) pub
     WHERE a.signal_id = s.id
       AND alias_src.vertical = 'retail'
AND alias_src.enabled IS TRUE
AND alias_src.is_private IS FALSE
AND alias_src.owner_user_id IS NULL
       AND (a.source_id <> s.source_id
            AND pub.alias_host IS NOT NULL
            AND pub.alias_feed_host IS NOT NULL
            AND (pub.own_host IS NULL OR (
                pub.alias_host <> pub.own_host
                AND right(pub.alias_host, length(pub.own_host) + 1) <> '.' || pub.own_host
                AND right(pub.own_host, length(pub.alias_host) + 1) <> '.' || pub.alias_host
                AND pub.alias_feed_host <> pub.own_host
                AND right(pub.alias_feed_host, length(pub.own_host) + 1) <> '.' || pub.own_host
                AND right(pub.own_host, length(pub.alias_feed_host) + 1) <> '.' || pub.alias_feed_host)))
  ) o ON true
 WHERE src.vertical = 'retail'
AND src.enabled IS TRUE
AND src.is_private IS FALSE
AND src.owner_user_id IS NULL
AND s.vertical = 'retail'
AND s.owner_user_id IS NULL
AND isfinite(s.published_at)
AND s.published_at >= (
  TIMESTAMP '0001-01-01 00:00:00 BC' AT TIME ZONE 'UTC'
)
AND s.published_at < TIMESTAMPTZ '10000-01-01 00:00:00+00'
AND (s.event_time_utc IS NULL OR isfinite(s.event_time_utc))
AND (s.event_time_utc IS NULL OR (
  s.event_time_utc >= (
    TIMESTAMP '0001-01-01 00:00:00 BC' AT TIME ZONE 'UTC'
  )
  AND s.event_time_utc < TIMESTAMPTZ '10000-01-01 00:00:00+00'
))
AND (s.first_seen_at_utc IS NULL OR isfinite(s.first_seen_at_utc))
AND (s.first_seen_at_utc IS NULL OR (
  s.first_seen_at_utc >= (
    TIMESTAMP '0001-01-01 00:00:00 BC' AT TIME ZONE 'UTC'
  )
  AND s.first_seen_at_utc < TIMESTAMPTZ '10000-01-01 00:00:00+00'
))
AND s.id NOT IN ('sig_bd766794701d3d9f')
   AND s.superseded_at IS NULL
   AND NULLIF(BTRIM(s.slug), '') IS NOT NULL
   AND s.created_at >= $1 AND s.created_at <= $2

Corrections

A correction is a published story whose figure, date, name or attribution we changed because it was wrong, or a story we withdrew. Each one is listed here with its date and what changed, from 15 Oct 2026, when this log opened. Before then corrections were not logged, so we make no claim about them.

Uptime

Every 5 minutes one of our servers loads / from the outside, through the same network readers use, and sends our MCP server a ping. Up means the page came back complete within 15 seconds, or the MCP server answered. A check that did not run counts as down, because a full outage stops the checks too. Shares are rounded down to a tenth of a percent.