v19: Accuracy Audit — Fixing What the Site Was Telling You

A full review of the codebase found that the published methodology had drifted badly from the code that actually runs — the How It Works page was describing v14 while the engine ran v17. Three signal tabs were silently hiding six feeds. Two sources had been dead for months. v19 fixes the scoring, replaces the dead feeds, and rebuilds the methodology page so it reads its numbers directly from the scoring engine and can never drift again.

This release is not a new feature. It is the result of auditing every line of the codebase against what the site claims to do, and finding that a number of those claims had stopped being true. Some were cosmetic. Several were not. This post documents all of them, because a risk meter that misdescribes its own method is not worth much.

The methodology page was describing an older engine

The How It Works page restated every scoring constant by hand. When v17 changed those constants, the page was not updated. The result was a published methodology that described v14 while the meter ran v17 — and a reader who checked the arithmetic against the live score would have concluded the meter was broken.

ClaimPage saidEngine actually ran
Geo blend weight27%40% relative (33% effective)
Geo article pooltop 100top 30
Geo recency half-life6 hours24 hours
Urgency threshold8 keyword matches6 matches
Sentiment threshold6 negative words4 words
Articles sent to AI per cycle5020
Claude modelClaude 3.5 Haikuclaude-haiku-4-5
Market data sourceAlpha VantageFRED + Yahoo Finance
Market score formulamean of four signalsweighted 35/30/20/15
News poll intervalevery 2 minutesevery 5 minutes
Market poll intervalevery 6 hoursevery 2 hours
Space weather pollevery 5 minutesevery 30 minutes

The fix is structural rather than editorial. Every scoring constant now lives in one file that the engine imports, and the How It Works page imports the same file. The blend table, the formulas, the pool sizes, the half-lives, the source list and the severity bands are all generated at render time from the values the scorer is running. Changing a weight now changes the published methodology in the same commit, because there is no second copy to forget.

The blend weights do not sum to 1 — and never did

Writing tests for the regenerated page surfaced something that had been true since v17: the eight blend weights add up to 1.21, not 1.00. When geo was raised from 0.27 to 0.40, the other seven were not reduced to compensate.

The meter itself has always been correct. The scorer divides each weight by the total of whichever signals actually reported, so the normalisation happens at runtime and the arithmetic works out. But the percentages the site published were the raw weights, which meant every one of them was overstated by about a fifth. Geo was never contributing 40% of the meter; its real share is 33%.

published before:  geo 40%  health 16%  market 16%  cyber 14%  energy 10%  climate 9%  space 8%  AI 8%
published now:     geo 33%  health 13%  market 13%  cyber 12%  energy  8%  climate 7%  space 7%  AI 7%

The constants are deliberately unchanged — they express relative importance, which is how they are tuned. What changed is that the site now publishes the effective share, so the numbers on the page are the numbers a reader can verify against the meter.

Three signal tabs were hiding six feeds

Each signal tab kept its own hardcoded list of which sources belong to it, separate from the registry the ingest pipeline polls. Three of those lists had fallen behind. The Health tab queried a source ID that does not exist, so the PAHO feed never appeared, and it omitted two more. The Cyber tab omitted Bleeping Computer and SecurityWeek — the two highest-volume cyber feeds, both added back in v15. The Energy tab omitted the EIA feed.

The score shown on each tab came from the backend and included every source, so the number never matched the articles listed beneath it. There is now a single source registry, shared by the pipeline and the interface, and every tab derives its query from it.

Two sources had been dead for months

Signal confidence had been sitting at 95% for a long time. That figure is the fraction of feeds fetching without errors, and it was telling the truth: two of the 37 were failing every single day.

Outbreak News Today, added in v15, had never completed a single successful fetch. It serves normally to an ordinary browser but its CDN returns 403 to Google Cloud address ranges, so the scheduled function could never reach it — a failure invisible to any test run from a laptop. Inside Climate News worked until 23 June 2026, then began refusing all automated clients across every feed path.

Both are deliberate decisions by those publishers to refuse bots, and they are entitled to make them. Rather than work around the block, both have been replaced with feeds that serve automated clients willingly.

RemovedReplacementGroupVeracityWhy
Outbreak News TodayCIDRAP Infectious Disease NewsHealth0.95The University of Minnesota Center for Infectious Disease Research and Policy. Institutional reporting rather than aggregation, with daily coverage of outbreaks, avian influenza, antimicrobial resistance and vaccine policy. Higher editorial authority than the feed it replaces.
Inside Climate NewsGuardian Climate CrisisClimate0.90The Guardian’s dedicated climate desk. The Guardian world feed already fetches cleanly from our infrastructure, so the domain is known to serve us. Strong coverage of climate policy, extreme weather attribution and energy transition.

Signal confidence returned to 100% on deployment. Category counts are unchanged: health keeps 8 sources, climate keeps 4.

Recency now decays from the publication date

This is the one change in v19 that moves scores. Article recency was decaying from the time we fetched an article rather than the time it was published, and those are not the same thing when a feed publishes slowly.

Articles are deleted after 72 hours. A weekly publication — a WHO bulletin, a CISA advisory, Import AI — is often still sitting in its RSS feed when its record is purged, so the next poll writes it back as though it were brand new and its recency score resets to 10 out of 10. Those categories were getting a phantom freshness bump every three days from stories that were already a week old.

Recency is now measured from the publisher’s own date. Feed dates are not trusted blindly: a timestamp in the future, at epoch zero, or more than thirty days before the fetch falls back to the fetch time, because feeds routinely emit all three. The practical effect is that slow institutional sources now score on their real age. The overall meter moved by less than a tenth of a point, which is the expected size — it only suppresses articles that were being artificially refreshed.

What the scored window really is

The site described a 72-hour scoring window. In practice each scoring run reads the 500 most recent articles, and at current publishing volume those 500 span roughly 32 hours. The cap binds long before the time limit does.

This has a consequence worth stating plainly: because the cap sorts by recency across all categories, high-volume feeds occupy more of the pool than slow institutional ones. A weekly outbreak bulletin can fall outside the scored set entirely. The methodology page and the limitations section now say this outright rather than claiming a full 72-hour window. Addressing it properly means querying each category against its own limit, which is planned but not in this release.

Smaller corrections

Why publish this

WakeUpNeo is an experimental signal, not a factual prediction, and the only thing that makes an experimental signal useful is that its method is legible. A methodology page that describes a version of the engine retired months ago is worse than no page at all, because it invites a reader to trust arithmetic that will not reproduce. Everything above is now either generated from the running code or stated as a known limitation.