{"generated_at":"2026-10-31T23:30:49Z","consistency":{"latest":null,"runs":[]},"archive":{"latest":null,"runs":[]},"honesty":{"latest":null,"checks":[{"id":"no_reading","name":"A place with no reading returns no number","result":"not_run","surface":null,"request":null,"expected":"now.value is null, now.usable is false and now.reason says why","observed":null,"checked_at":null},{"id":"stale_feed","name":"A stale feed is not usable","result":"not_run","surface":null,"request":null,"expected":"now.usable is false and now.value is null","observed":null,"checked_at":null},{"id":"er_partial","name":"ER lists say coverage is partial","result":"not_run","surface":null,"request":null,"expected":"coverage is \"partial\" and a warning says Waity tracks only some ERs","observed":null,"checked_at":null},{"id":"non_us","name":"Outside the US gets US only","result":"not_run","surface":null,"request":null,"expected":"coverage is \"none\", no places, and a warning says Waity covers US places only","observed":null,"checked_at":null},{"id":"mixed_sources","name":"Mixed sources carry the warning","result":"not_run","surface":null,"request":null,"expected":"a list whose usable waits come from sources that measure differently warns to compare minutes only within one source","observed":null,"checked_at":null},{"id":"not_covered","name":"A category not covered yet says so","result":"not_run","surface":null,"request":null,"expected":"coverage is \"none\", no places, and a warning says Waity doesn't track it (yet)","observed":null,"checked_at":null}],"runs":[]},"reliability":{"computed_at":"2026-10-31T23:00:00Z","windows":["24h","7d","30d"],"components":[{"id":"api-rest","name":"REST API","windows":{"24h":{"checks":302,"errors":0,"error_rate":0,"p50":745,"p95":1714,"p99":2092},"7d":{"checks":302,"errors":0,"error_rate":0,"p50":745,"p95":1714,"p99":2092},"30d":{"checks":302,"errors":0,"error_rate":0,"p50":745,"p95":1714,"p99":2092}}},{"id":"api-mcp","name":"MCP server","windows":{"24h":{"checks":151,"errors":0,"error_rate":0,"p50":658,"p95":1505,"p99":1688},"7d":{"checks":151,"errors":0,"error_rate":0,"p50":658,"p95":1505,"p99":1688},"30d":{"checks":151,"errors":0,"error_rate":0,"p50":658,"p95":1505,"p99":1688}}},{"id":"website","name":"Website","windows":{"24h":{"checks":302,"errors":0,"error_rate":0,"p50":454,"p95":1675,"p99":2092},"7d":{"checks":302,"errors":0,"error_rate":0,"p50":454,"p95":1675,"p99":2092},"30d":{"checks":302,"errors":0,"error_rate":0,"p50":454,"p95":1675,"p99":2092}}},{"id":"accounts","name":"Accounts","windows":{"24h":{"checks":151,"errors":0,"error_rate":0,"p50":272,"p95":590,"p99":725},"7d":{"checks":151,"errors":0,"error_rate":0,"p50":272,"p95":590,"p99":725},"30d":{"checks":151,"errors":0,"error_rate":0,"p50":272,"p95":590,"p99":725}}}]},"forecasts":{"status":"collecting","scored":0,"issued":0,"min_n":100,"leads":[60,180],"computed_at":null,"models":[]},"methodology":[{"id":"consistency","title":"Raw readings against hourly and daily aggregates","text":["Every night at 11:04 the local day two days back is checked: settled, and its raw readings still in the hot store. Every place with readings or hourly aggregates around that day is ranked by the SHA-256 of \"<date>:<place id>\", and the lowest 8 per category, 40 in all, are checked.","For each, a separate implementation that shares no code with the production rollup or the history API recomputes every hour and the whole day from the raw readings. A reading holds until the next reading, until its source's freshness limit runs out, or until the end of the hour, whichever comes first. A posted range counts at its midpoint, and an empty queue posts no value. avg is the time-weighted mean while open. median and p90 are the smallest values held for at least 50% and 90% of the open time; for a day, they weigh the hour averages by open time. n counts the posted updates.","Each hour is compared with the stored hourly aggregate on avg, min, max, n, open seconds, upper-bound seconds and the local date and hour. Minutes must agree to within 0.000001, and counts exactly. Each hour and day is also compared with GET /v1/places/{id}/history on avg, median, p90, min, max, n and open share. The API rounds minutes to 0.1, so a difference over 0.05 is a mismatch. A bucket on one side only is a mismatch too. Max difference is the largest absolute difference, after rounding the way the API does.","The archive is checked at 11:06. The 10 newest archived files and 10 older ones, ranked by the SHA-256 of \"<date>:<file>\", are read back by a separate reader. Each must match its manifest and the archive index: row count, size, both SHA-256 checksums, gzip members that tile the file exactly, and the line count of every member."]},{"id":"reliability","title":"Reliability","text":["Every minute, the public URLs are probed from Cloudflare's network, as an agent reaches them. REST API: /health and a /v1/places query. MCP server: tools/list. Website: the homepage and /_health. Accounts: the account app.","Every hour, these numbers are computed for the trailing 24 hours, 7 days and 30 days. Checks: the probes in the window. Error rate: the share of probes that got no HTTP answer (a timeout or network error) or a status outside 2xx. A 429 counts as a success: the keyless daily limit answering shows the route works. p50, p95 and p99: nearest-rank percentiles of every probe's response time in milliseconds, meaning the smallest time that at least that share of probes took no longer than. A timeout counts at its 5-second limit.","The probes run inside Cloudflare, so an outage of Cloudflare itself can't be recorded here."]},{"id":"forecasts","title":"Forecast accuracy","text":["At 5 past every hour, the forecasts for one hour ahead (lead 60) and three hours ahead (lead 180) are logged as they are made. Each is scored once its hour is over. The actual is the hour's time-weighted posted mean, and an hour with under 30 open minutes isn't scored. The served model and the early model (baseline_hod_v0, logged but never served) are shown separately.","MAE is the mean absolute difference between actual and forecast, in minutes. Within ±5 min is the share of forecasts off by 5 minutes or less. Two baselines are scored on the same rows. Persistence assumes the wait stays what it was when the forecast was made; rows with no live wait then are left out of that comparison. Usual for the hour is the same model with no live adjustment. Skill is 1 − MAE of the model ÷ MAE of the baseline: above 0 beats the baseline, below 0 loses to it.","Weeks are ISO weeks (Monday to Sunday) of the forecast hour. A week, category and lead shows numbers only once 100 forecasts are scored. Until then it shows how many are scored, never an estimate. The JSON carries the sums behind every number, so each one can be recomputed."]},{"id":"honesty","title":"Data honesty","text":["Every night at 11:08, live production answers are fetched through the public API: over REST with a monitoring key when one is set, otherwise over keyless MCP with its own session. They are checked against six rules. A place with no reading returns no number, with a reason. A place whose last reading is past its freshness limit is not usable. ER lists say coverage is partial. A point outside the US gets no places and a US-only warning. A list mixing sources that measure differently says to compare minutes only within one source. A category Waity doesn't track gets no places and says so.","The places asked about are picked from the data that night, by the same SHA-256 ranking. A check with nothing to test, such as a night with no stale feed, or an answer cut off by a rate limit, is marked skipped, never passed."]},{"id":"keeping","title":"Kept for good","text":["Every run, check and snapshot is stored for good and served as JSON at /status/accuracy.json. This page reads from the site's own database, so it stays up when the API is down."]}],"links":{"page":"https://waity.ai/status/accuracy","status":"https://waity.ai/status","self":"https://waity.ai/status/accuracy.json"}}