The models are good. The map is not.
Global flood platforms are excellent where the data is thick: dense gauge networks, LIDAR terrain, decades of insurance claims. Most of the people who drown live where none of that exists. That is the whole company.
Nobody is building here.
That is the whole company. Tomorrow.io, Jupiter Intelligence, 7Analytics and Vassar Labs all win on assets we do not have and are not trying to acquire. We do not compete with them on their ground - we operate in the quadrant they skip: real-time decisions, made where the inputs are thin.
The quadrant is empty for a reason. It is unglamorous, and you cannot enter it from a desk in another country.
We measured it. It is worse than the brochures suggest.
These are not estimates. Each one is a number we hit while building, on real Odisha feeds, and each one is a reason a platform designed for Rotterdam does not transfer.
0 / 12
CWC river stations that were decision-grade on day one
Not one of them could be trusted to move a score on the day we started ingesting.
0.0%
of readings at Kishan Nagar outside physical range
A platform that trusts the feed wholesale would have alerted on noise.
0
conflicting danger-level tables for the same gauge
DoWR's real danger level for Akhuapada is 18.33 m; INDOFLOODS lists 17.83, which is actually the warning level.
0 mm
what the IMD daily grid can read on a peak event day
While OpenWeather reports localised Odisha rain falling hard. No single feed is safe alone.
Three of these four look like data.
Raw gauge feed · before validationsensor stuck, months of identical values
feed frozen 143 days, last good value repeats
spike to 1133 m, and minus 834 m at Jenapur
Akhuapada 18.45 m against a danger level of 18.33
A flatline, a stall and an impossible spike all arrive as valid rows with valid timestamps. Only the fourth is a river rising.
That is why river level is gated: a station only reaches the risk path when it is marked status == live. Otherwise the briefing says the read is rainfall-only, out loud. The moat is not a model - it is the accumulated knowledge of which feed lies, and when.
A forecast is not a decision.
Forecasts answer what will happen. A district officer at three in the morning needs an answer to what do I do, and needs to defend that answer at nine. Those are different products.
62%
probability of flooding, next 24 hours
- Do I evacuate?
- Which wards?
- How do I justify it tomorrow?
The officer improvises. The number takes no responsibility.
Rain 6 h
78 mm
p95 baseline
41 mm
Antecedent
3 wet d
Forecast 24 h
46 mm
- Why this score
- 78 mm in 6 h against a p95 of 41 mm, on soil already saturated by three wet days. Forecast adds 46 mm in the next 24 h.
- Closest past event
- Nearest match in 653 labelled historical events, and what happened in the 48 hours after it. Officers reason by precedent, not probability.
- Suggested action
- Pre-position at Naraj. Alert wards 4 to 9. One line, not a plan.
- What we could not see
- No live gauge on this reach, so this read is rainfall-only and the score is capped accordingly. The absence is stated, not smoothed over.
Every field is something the engine already computed. None of it needs a bigger model.
The hazard changes. The shape does not.
Flood is the wedge because it is the most expensive and the most tractable. Underneath, the engine is hazard-agnostic: a baseline, an observation, a rule, a decision. Heat already runs on it, in shadow, with no way to alert.
Flood, five cities
Mature the alerting set. North Odisha scored in shadow. Close the recession gap with river gauges on the risk path.
Heat, same engine
Eleven locations scored against ERA5 normals, five surfaced. Stays in shadow until it has an audit a buyer can check.
One industry vertical
Mining first, and discovery before any build. Ten to fifteen conversations decide whether there is pull, before a line of product code.
Not in the next ninety days: cyclone, drought, ports, extra industries, or a sixth alerting city. Deliberately.
That is the argument. Everything below is the evidence for it, including the parts that went against us.
The evidence
Everything we know, including what broke.
A district cannot audit what it cannot see. Every figure here carries a label saying what kind of number it is, and the failures are given the same weight as the wins.
One hundred and forty-three validated positives.
Every one of them counted toward a 99.3% detection rate, and that number led every deck we had.
Each one snapped to the nearest pilot city within 120 km.
In Odisha that radius crosses whole basins. Watch what it swept in.
They belonged to other rivers.
Baitarani 79. Rushikulya 34. Brahmani 29. The engine had been scored on water we were not claiming to watch.
One of 143 was Mahanadi.
We retired the number, published the audit, and put the precision problem it exposed on the public site.
Schematic. Basin courses are indicative; the event counts and distances are from the relabel audit.
14 of 15 river-over-danger spells flagged two days ahead.
The 2026 monsoon on the Baitarani, scored end to end. Fifteen times a river crossed its danger mark on the four reaches we watch. With rainfall forecasts plus the state’s own daily gauge bulletin we flagged 14 of 15 two days ahead, and were HIGH or above on all 27 days a river already stood above danger (a nowcast, not a forecast). n = 15 spells · forecasts as issued, 2 Jun-5 Oct 2026 · unmodified engine.
What production actually ran, rainfall only: 9 of 15.
And LOW on 6 of those 27 above-danger days: Jajpur on 26-27 September with the river at 1.06× danger. That miss is why the river term now exists.
The cost: MEDIUM on about 80% of monsoon days.
Akhuapada sat within 10% of danger for half the season, so Bhadrak and Jajpur read MEDIUM on about 80% of monsoon days. MEDIUM is a river watch; HIGH is the action bar.
13 of 13 floods flagged two days before the river crossed its danger level.
Baitarani, 2024 to 2026, on forecasts as they were issued rather than on hindsight rainfall. Precision 27.7%, false alarm rate 21.8%, against a 7.7% base rate: a 3.6 times lift. n = 13.
Thirteen floods is a small sample and we state it as a count, not a percentage. A 13 of 13 result carries a 95% lower bound near 77% recall, so it is not a claim of perfect accuracy and we will not write it as one.
It is also worth being exact about what was measured. Anandapur and Akhuapada are Baitarani gauges on the shadow surface; neither is one of the five cities that actually alert, which sit on the Mahanadi and the Brahmani. This is the engine scored against a gauge record, not the alerting product describing itself.
Across all thirteen gauges it is weak, and that is the finding.
51% of floods caught at one day’s lead, at 6.6% precision. Roughly fourteen false alarms for every true flood. Rainfall alone is not a statewide alerting product.
The Baitarani is a slow-response basin, so two days of forecast rain actually reaches the gauge. Rajghat on the Subarnarekha scores 36%, and Purusottampur and Gunupur score zero of two. A single statewide average would hide both the basin that works and the ones that do not, which is why no figure here appears without its basin.
Where the labels come from. 497 daily flood bulletins published by Odisha’s Department of Water Resources, 2023 to 2026, archived and parsed into 11,408 gauge readings carrying a danger level. The label is the state’s own operational record: did the river cross its published danger mark. Not our judgement, and not an academic dataset, which is what makes these numbers checkable by the people who published the source.
We audited our own 99.3% and it did not survive.
Our event labeller snapped each INDOFLOODS gauge to the nearest pilot city within 120 km. In Odisha that distance crosses whole basins. The number was mislabelled, not fabricated - but it was wrong, and we retired it from the site.
We could have quietly fixed it. Instead we published the audit, the replacement number, and the precision problem it exposed underneath.
Where our 143 validated positives actually came from
Anandapur, 86.8 km from Cuttack
Purushottampur, 104.6 km, in Ganjam
Jenapur, 49.4 km
Naraj - one event, out of 143
94.4%
north Odisha onsets, 102 of 108
95.1%
across all 143 events
97.5%
Baitarani
86.2%
Brahmani
What survived the relabel. Hindsight rainfall, not forecast-as-issued; superseded by the forecast numbers above. Rain-only, scored against each gauge’s own ERA5 p95, negatives sampled at the same gauge. No leaked river term.
The finding nobody publishes: our precision does not exist.
The old “0% false positive rate” was measured on dry days - the one case a rainfall rule separates by construction. On heavy monsoon days at or above the gauge’s own p95 with no recorded flood, the engine fires on every single one: 647 of 647. Sweeping 0.5 to 8 times p95, precision never exceeds 24.2% against a 20% base rate.
And the reason is interesting. North Odisha flood days have a median of 1.94 times p95. Non-flood heavy days have 2.03 - the days without floods had more rain. Either rainfall genuinely cannot discriminate and we need river level as a feature, or the flood record is not exhaustive and we need river level as a label. Both answers point the same way.
What we have not solved, as of October 2026
- Open
The trust gate is not passed
Trust gate v2 is defined. v25_dowr passes on the 2026 tape; rain-only fails G1. Still OPEN for the five LIVE cities. Fourteen consecutive dry days with zero false HIGH remains the live-city bar, and Sambalpur and Rourkela still block it.
- Open
Precision is measured now, and statewide it is weak
This sat here as unanswerable until we archived the state’s own gauge record and could ask it properly. It is answered, and the answer is not flattering: 6.6% precision across all thirteen gauges, roughly fourteen false alarms for every true flood, against 27.7% on the Baitarani. Rainfall alone is not a statewide alerting product. That is a finding about where this engine works, not a disclaimer.
- Shadow
The ML model has never driven an alert, and its old numbers are retired
XGBoost v2 runs on every cycle and is logged, and it has never altered a user-facing score. The offline figures we used to quote, 91% leave-one-region-out and 96% event holdout, are withdrawn: the training set defined water level as 85% of the event peak, so roughly 77% of the feature importance was encoding the label. It has not been re-measured since. Every flood number on this site is the rule engine, not the model.
- Shadow
Heat has a dataset, not a model
About 82k leakage-audited rows under ml/heatwave/, of which roughly 23.7k are forecast-backed. No model has been trained. HeatBench v0 exists; the IMD grid cross-check sits at roughly 62% within 1.5 degrees of ERA5.
How to read every number on this site
In production, driving user-facing alerts.
Measured on history. Never quoted as live accuracy.
Computed and published, structurally unable to alert.
The engine was wrong, and here is the write-up.
A known gap we have not closed.
If we cannot label it, we do not make the claim.
We deleted the constants and went to the source.
The engine used to carry hardcoded river levels. They are gone. CWC hourly telemetry now arrives from the NWDP open API (46 gauges across Mahanadi, Brahmani and Baitarani), and the daily DoWR flood bulletin is parsed for 28 more.
What the feed actually looked like
- Out-of-range at Kishan Nagar, including a spike to 1133 m and minus 834 m at Jenapur34.6%
- Sensors flatlined: Akhuapada, Champua, Pamposh, Nimapara, Seorinarayan5 stuck
- Brahmani and Baitarani feed, stalled since 3 June143 days
- Real but stale: Sambalpur, Hirakud, Basantpur, Khairmal38–79 h
So the ingest ships with a QC layer it turned out to need badly: physical-range filter, stuck-sensor detection over a 500-reading window, and a datum check that strips a published danger level when it does not share the station’s datum. A level counts toward a score only when the gauge is genuinely live, and since Rule v2.5 a live level near its danger mark can raise the call on its own, without waiting for rainfall to agree.
It worked, so we shipped it
10 of 12 location-days in the 19–21 August recession window that rainfall scored LOW would have been rescued by the river hold. On 19 August, Akhuapada was literally above its danger level, 18.45 m against 18.33, while the rainfall engine said 0.20 LOW. That measurement is what put river stage into the score in Rule v2.5, where a live gauge near its mark can now raise the call without rainfall’s agreement.
Live in production
Sambalpur now reads 181.68 m from Jamadarpali. Cuttack 131.22 from Kishan Nagar, Puri 3.22 from Nimapara, Rourkela 171.4 from Pamposh, all correctly marked unavailable and contributing nothing. Bhubaneswar has no CWC station at all, so it now shows no river level rather than a frozen constant.
Public and academic data, named, and filtered before it counts.
Nothing reaches a score straight from a feed. Every reading passes four gates, and a station that fails any of them contributes nothing rather than contributing noise.
INDOFLOODS
653 labelled historical flood events. Kuntla & Saharia, BAMS 2025.
IFI
India Flood Inventory. Saharia et al.
ERA5
1991–2020 climatology. Each location's own wet-day p95, and the heat baseline.
IMD
Daily gridded rainfall and Odisha district normals.
Odisha AWS
State automatic weather stations, ingested twice daily.
CWC / NWDP
Hourly river telemetry, 46 gauges, open-licensed.
DoWR Odisha
Daily flood bulletin: 28 gauges with danger, warning and historic high, plus reservoir gates and discharge.
NRSC · OpenWeather
Satellite baselines and live forecast. Derived risk product; no raw data resale.
Derived risk product; no raw data resale. Advisory only.
The honest position, October 2026
Every claim above is labelled. If we cannot label it, we do not make it.
The flood engine alerts on five cities. The ML runs in shadow and cannot alter a user-facing score. Heat has no live mode in the code, not a switch we are choosing not to flip. There is no signed MoU and no paying customer.