This is what happens between an HTTP request and a festival date, in the order it happens.
Step 1 — Scope check
The rule declares a month system (amanta, purnimanta or saura), a target month, a paksha, and whether it should be skipped in an adhika maas. If the requested day does not match, the rule fails immediately and emits a trace explaining why. Most rules fail here on most days, which is what keeps the pipeline cheap.
Step 2 — Build the kala window
The KalaCalculator takes sunrise, sunset and next sunrise for the location and produces the required window for each candidate day. It is a pure function — no database access, no festival logic — which is why it can be unit-tested against synthetic intervals.
Step 3 — Measure vyapti
Overlap between the tithi interval and the kala interval, per candidate day, in minutes. This single number is the evidence the decision rests on, and it appears in the trace.
Step 4 — Resolve
The TieBreakEngine applies four branches in order: full overlap on exactly one day; larger overlap in minutes; the rule's declared strategy; kshaya fallback. Each branch sets a different confidence score, so the trace records not just the answer but how it was reached.
Step 5 — Apply the rule pack
Traditions differ. Rather than embedding one tradition in code and treating others as exceptions, variants live in rule packs with defined precedence: an explicit request parameter beats an account preference, which beats a location default, which beats the global default.
Step 6 — Emit the trace
Every evaluation produces a trace, whether it matched or not. This is the part most engines skip, and it is the part that makes the rest defensible.
{
"festival": "FESTIVAL_DIWALI",
"rule_code": "DIWALI_V2",
"rule_version": 2,
"evaluator": "PRADOSHA_VYAPINI",
"candidate_days": ["2026-11-08", "2026-11-09"],
"overlap_minutes": { "2026-11-08": 96, "2026-11-09": 0 },
"selected": "2026-11-08",
"confidence": 100,
"reason": "Amavasya fully covers Pradosha on Nov 8",
"authority": ["Dharmasindhu", "Nirnaya Sindhu"]
}Multi-day observances
Navratri and the Diwali cluster are not independent rules that happen to land adjacently. They are anchored series: one rule fixes day one, and members are placed relative to it, each optionally refined by its own evaluator. A member can never fall outside the anchor's maximum span, which is what keeps an eight-day Navratri coherent in years where a tithi is lost.
Why rules are data
Adding a tradition means adding rule rows and, occasionally, one evaluator class. The pipeline above never changes. That constraint is deliberate: it means a rule correction cannot introduce a regression in unrelated festivals, and the determinism hash lock will catch it immediately if it somehow does.