Accuracy

What we measured, and what we still get wrong

Every figure on this page comes from a regression report generated by the test suite. Where we have not measured something, we say nothing rather than estimate.

Verification

Three tiers, three different failure modes

Each tier catches something the others cannot. Reference comparison catches a wrong rule. The determinism lock catches a rule change that moved an unrelated date. Audit replay catches a deployment that diverged from what was audited.

Tier A

42 / 42

Reference comparison

vs DrikPanchang

7 festivals × 6 years, spanning 1950–2100

Diwali, Janmashtami, Maha Shivaratri, Ganesh Chaturthi, Vijayadashami, Holi and Makar Sankranti are compared against DrikPanchang across a 150-year span. This catches rules that happen to be right for the current decade but wrong historically.

DiwaliJanmashtamiMaha ShivaratriGanesh ChaturthiVijayadashamiHoliMakar Sankranti

Tier B

Locked

Determinism lock

vs Itself

Every-day festival hash across 8 years

A hash of every festival on every day for eight years is stored in tier_b_lock.json. Re-running the engine must reproduce it exactly. This is the guard against silent drift: change a rule for one festival and accidentally move another, and the lock breaks immediately.

Tier C

26 / 26

Audit replay

vs Production data

22 days from the 2026–27 accuracy audit

Assertions taken from a manual accuracy audit are replayed against production. Where Tier A proves agreement with an external reference, Tier C proves the deployed system still behaves as audited.

Regression testing

152 tests, 91% coverage

The suite runs on every build. The coverage gate is 90%; below that, the build does not ship.

SuiteTestsScope
festival_engine_v2/tests/115Evaluators, kala, intervals, tie-break, series, validator, engine, db adapter, materialisation
tests/test_endpoint_tiers.py16Plan gating matrix, including Enterprise backward compatibility
tests/test_webhooks.py21HMAC signing, backoff, delivery outcomes, idempotency, producer filtering
Total152Coverage 91% on festival_engine_v2 (gate 90%)

Why our coverage number went down

Mid-build the reported figure was 99%, but that predated db_adapter.py and materialize.py, which had no unit coverage at all. Re-measuring put the package at 77%. Twenty-three mocked-database tests were added to reach a genuine 91%. We are recording this because a coverage number without that history would have been misleading.

Performance

Measured on production, not a laptop

Ten samples against the live API. Engine-level figures are measured inside the festival engine, excluding network and serialisation.

End-to-end, production

EndpointMedianp95Target
/v1/panchang138 ms154 ms< 2000 ms
/v1/festivals148 ms169 ms< 2000 ms
/v1/festivals/explain160 ms183 ms< 2000 ms

Engine-level

MetricMeasuredTarget
Festival lookup p952.4 ms< 50 ms
Full year sweep0.8 s< 5 s

On the numbers you may have seen elsewhere

An earlier version of this website claimed sub-50 ms responses. That figure described engine-side lookup, not an API round trip, and presenting it as a response time was misleading. The real end-to-end p95 is 154–183 ms. We have corrected it.

Coverage

What is verified, and how far

Festival derivation

location 1

Fully verified for New Delhi across Tier A, B and C. Other locations compute correctly through the same engine but have thinner cached data behind them.

Historical span

1950–2100

Tier A compares across a 150-year window specifically to catch rules that are correct for the present decade and wrong historically.

Determinism

8 years, every day

Tier B hashes every festival on every day across eight years. Any unintended change breaks the lock on the next run.

Ayanamsa

Lahiri

The Indian standard for sidereal calculation. Kala definitions follow Dharmasindhu and Nirnayasindhu conventions, which is why comparison against DrikPanchang is meaningful.

Corrections shipped

Defects we found and fixed

These were real errors in production before Festival Engine V2. We list them because a platform that claims never to have been wrong is not one you should trust.

FestivalWasNow
Diwali / Lakshmi PujaMissing entirelyAmavasya prevailing at Pradosha
Krishna JanmashtamiMissing entirelyNishita-vyapini Ashtami (Smarta default)
Ganesh ChaturthiOne day lateMadhyahna-vyapini
VijayadashamiOne day lateAparahna-vyapini
Maha ShivaratriOne day lateNishita-vyapini
HoliWrong when Purnima ended before sunsetHolika Dahan + 1 day, as a series
Abhijit MuhuratSuppressed by Rahu/Yamaganda overlapAlways returned; overlaps listed separately

Known limitations

What is still wrong, in our own words

This list is the actual contents of our internal limitations document. We would rather you find a limitation here than discover it in production.

Open

amanta_month_key is mislabeled in source data

The column is off by one month for Krishna-paksha days. V2 rules therefore use the verified-correct purnimanta_month_key exclusively.

ImpactNo effect on current output. Any future rule needing amanta reckoning must not trust that column until the astronomy data is regenerated.OwnerPanchang Platform v2.1
Open

Panchang data coverage is uneven across locations

Locations 2–9 have no supplementary tables and fall back to live Swiss Ephemeris computation. Locations 10–15 are roughly 65% imported. Hyderabad has 51 of 701 years cached.

ImpactFestival derivation is fully verified for location 1 (New Delhi). Other locations work, but with thinner cached data.OwnerPanchang Platform v2.1
Open

Rule packs exist but only north_india is populated

The pack mechanism is implemented and tested. Regional variants — Gujarati, Tamil, ISKCON, Nepal — are not yet authored.

ImpactRequests resolve to the North Indian pack regardless of the pack requested.OwnerPanchang Platform v2.1
Open

Materialisation covers location 1 only

The nightly job materialises a 430-day window for location 1. Other locations compute on demand.

ImpactOn-demand computation remains fast — engine-side p95 is 2.4 ms — so this affects cost, not correctness.OwnerMulti-location materialisation, v2.2.0
Open

Webhooks are one-way and at-least-once

There are no inbound webhooks. Delivery is at-least-once, so subscribers must deduplicate on X-TathaAstu-Event-Id. Retries stop after 5 attempts; endpoints auto-disable after 20 consecutive failures.

ImpactIntegrations must be idempotent. This is a design choice, not a defect.OwnerBy design

Philosophy

How we handle being wrong

Rules are never edited in place

A correction inserts a new rule version pointing at the row it supersedes. The old row is marked DEPRECATED, not deleted. Any response we served in the past can still be reproduced from its recorded rule id and version.

Every correction is traceable to a reason

Traces record the rule, its version, the evaluator, the overlap in minutes, and the shastric authority. When a date changes, you can see precisely which rule changed and why.

Limitations are published, not buried

The list above is the actual contents of our internal KNOWN-LIMITATIONS document. We would rather you find a limitation here than discover it in production.

A number we cannot measure is not a claim we make

Every figure on this page comes from a regression report generated by the test suite. Where we have not measured something, we say nothing rather than estimate.

Report a date you believe is wrong

Send the request you made and the value you expected. Accuracy reports are triaged ahead of everything else. If you are right, the fix ships as a new rule version and appears in the changelog.