Astrology7 MIN READ22 / 31

Dasha Engine — High-Level Design

Status: Draft for review · Part of: Astro Composer · Updated: 2026-10-24

The dasha engine turns the Moon’s position at birth into a Vimshottari timeline: mahadasha, antardasha and pratyantardasha (first-, second- and third-level planetary periods) as continuous UTC instants, plus the active chain at any asOf. It is pure time arithmetic over a rulebook sequence — no clock, no calendar, no binder.

1. Purpose

The birth balance is a fraction of one nakshatra (lunar mansion). One arcsecond of lunar longitude moves a period boundary by roughly two seconds. Year length is also a convention — 365.25, 360 and 365.2425 days all appear — and over 120 years those choices differ by months. Two implementations of one chart therefore produce two calendars for “when my Saturn period starts”.

The dasha engine exists so that:

  1. one function produces every dasha boundary a product shows or notifies on;
  2. periods tile the timeline exactly, at every requested level;
  3. dates are absolute instants, independent of server timezone or locale;
  4. the response names the sequence, year length, profile and rulebook behind it.

2. Where it sits

chart core · Moon longitude + nakshatra id ──┐
request · birth instant + depth ─────────────┼──▶ dasha engine ──▶ periods + lookup
profile · dasha year length ─────────────────┤
rulebook · nine lords, years, 120 total ─────┘
  • Upstream: chart core supplies the binder’s full-precision Moon longitude and nakshatra id. The facade supplies the absolute birth instant, requested depth and one rulebook handle.
  • Downstream: the facade assembles the dasha layer into POST /v1/charts; transit and notification features ask the same engine which period is active at an instant.
  • Never: no binder call, no nakshatra re-derivation, no clock read, no calendar or timezone database.

Input contract

Input Source Used for
Birth instant Caller, canonicalised by the facade Timeline origin
Moon longitude Binder via chart core, full precision Elapsed fraction of the birth nakshatra
Moon nakshatra id Binder via chart core First lord, and the authority on the starting segment
Depth Request 2, 3 or 4 levels; default 3
Year length Profile Duration of one dasha year in days
Sequence table Rulebook Nine lords and their years, summing to 120
asOf Request, optional Active-chain lookup at one instant

Output contract

Output Shape Read by
system vimshottari Response meta
yearLengthDays Profile value, echoed Audit and fixtures
timelineStart Nominal start of the first mahadasha Clients that render the full cycle
periods[] Lord, level, start, end, parent, partial/balance Response, transit windows
active[] One period per level at asOf Transit and notification features
cycle 120-year cycle index for dates after the first cycle Long-range lookups

3. Responsibilities

Area Owns Deterministic rule
Balance Fraction of the birth nakshatra already spent elapsed = (moon mod 13°20′) / 13°20′
First lord Lord of the birth nakshatra Binder nakshatra id, never a recomputed index
Sequence Nine lords and their years Rulebook table; sum of 120 asserted at load
Division Sub-periods at every level Cumulative year fractions, never accumulated durations
Timeline One continuous nested set of periods Children inside the parent; no gap or overlap
Instant math Days to UTC instants days × 86 400 s; no months, timezones or DST
Lookup Active chain at asOf Half-open intervals; deepest requested level wins
Output Canonical ordering and identifiers Depth-first, parent before children

4. Architecture

Dasha Engine — High-Level Design Reference design. Chart core supplies the full-precision Moon longitude and the binder's nakshatra id; the request supplies the birth instant and depth; the profile and rulebook supply the year length and the nine-lord sequence. The engine resolves the balance, divides each period by cumulative year fractions, builds nested UTC periods and answers active-period lookups. It reads no clock and uses no calendar. Dasha Engine — High-Level Design A continuous Vimshottari timeline from one Moon position, with the year length pinned by profile. Chart core · Moon facts full-precision longitude binder nakshatra id Request · birth + depth absolute instant depth 2–4, default 3 Profile + rulebook year length in days nine lords, 120 years Dasha engine · pure time arithmetic, no clock Balance resolver elapsed fraction of nakshatra lord from binder nakshatra id Period divider cumulative year fractions children end at parent end Timeline builder three levels, cap of four UTC instants, no calendar Period lookup active chain at asOf half-open intervals Sequence integrity nine lords sum to 120 checked at rulebook load Timeline output depth-first, parent first one rounding point No clock, no locale, no timezone database. The Moon is the only body Vimshottari reads. Chart response · dasha layer periods with UTC boundaries profile and rulebook named Transit windows active chain at asOf same engine, same boundaries Notifications period boundaries no re-derived dates moon read one instant conventions periods + lookup dependency pure module engine output boundary Reference design · not deployed behaviour.

Diagram: reference design, not deployed behaviour.

The engine reads one Moon position and one sequence table and returns a value object. It holds no calendar, clock or state between requests, so the same birth data reproduces the same boundaries on any JVM in any timezone.

5. Rules

  1. The Moon alone decides. No other body, house, aspect or transit enters Vimshottari. The engine takes one longitude and one nakshatra id from the chart, nothing else.
  2. The binder owns the nakshatra. The engine takes the binder’s nakshatra id for the first lord and computes only the fraction inside it. A computed index that disagrees is a boundary contract failure, not a rounding difference.
  3. Full precision in, one rounding point out. Balance uses the binder’s unrounded longitude. Durations stay at full precision and are serialised to the second once, at assembly.
  4. The year length is a profile field. Default 365.25 days; 360 and 365.2425 are supported by the rulebook. It is echoed in every response, and changing it is a profile version, never a per-request override.
  5. Boundaries come from cumulative fractions. A child starts at parentStart + parentDuration × cumulativeYears / 120 and ends at the next cumulative boundary. Summed durations drift; the last child must end exactly where its parent ends.
  6. Time is absolute. Dates are UTC instants. No timezone database, calendar month, local midnight or leap-second arithmetic; display formatting belongs to the client.
  7. Nesting is an invariant. Every child lies inside its parent, siblings tile without gaps or overlaps, and the 120-year cycle repeats without drift. This is tested, not assumed.
  8. The first period is partial. Its nominal start precedes birth. The response carries that start and partial: true with the balance at birth, so a client can show the full period or “balance at birth” without guessing.
  9. Depth is explicit and capped. Default 3, maximum 4. Depth 4 never silently returns 3; depth 5 is rejected at validation.
  10. Rulebook integrity is a load-time gate. Nine lords, each named once, years summing to 120. A failing table is not served.
  11. Deterministic order and ids. Periods are emitted depth-first in mahadasha order with stable parent references, so two responses diff byte for byte.
  12. There is no “now”. The engine never reads a clock. A caller wanting the current period supplies asOf; without it, the response is the timeline only.
  13. One division primitive. KP sub-lord division, when it lands, reuses the same proportional splitter. A second “divide a segment by the Vimshottari years” implementation is a defect.

6. Edge cases

Case Behaviour
Moon exactly on a nakshatra start Elapsed 0; full first period, no partial balance
Moon exactly on a nakshatra end Half-open rule yields the next nakshatra and elapsed 0
Moon at 359.9999° Wraps to Ashwini; elapsed is never negative
Birth instant exactly on a period boundary The period starting at that instant is the active one
Balance shorter than a single sub-period Children scale inside the short parent; no minimum duration
Year length 360 versus 365.25 Dates differ by months over a cycle; the response names the year length
Depth 4 requested Allowed; each level nests inside its parent
asOf before the nominal timeline start Typed empty result, not the first period and not an error
asOf after 120 years Cycle index reported; the next cycle is generated on demand
Moon missing from the chart Request fails; dasha never defaults to a placeholder
Leap day in a displayed date Not a special case; instants are the source of truth
Retrograde or mean/true node choice Irrelevant to Vimshottari; the Moon is never retrograde
Rulebook years sum to 119 Startup fails; no chart is served under a broken sequence

7. Out of scope

  • Dasha interpretation, antardasha prose, remedies and predictions (product content).
  • KP sub-lord and sub-sub-lord output (later KP profile; the division primitive is shared).
  • Ashtakavarga, transits, Sade Sati and muhurta rules (their own designs).
  • Chart frame, house placement, dignity and combustion (chart core).
  • Divisional charts and vargottama (varga engine).
  • Binder access, caching and provenance (facade and binder).
  • Timezone display, locale formatting and calendar conversion (client).

8. Planned code

Piece Responsibility
dasha/VimshottariSequence Nine lords, year table and load-time integrity checks
dasha/DashaBalanceResolver Elapsed fraction, first lord and balance at birth
dasha/ProportionalDivider Cumulative-fraction subdivision shared by every level
dasha/TimelineBuilder Depth-first periods with parent references and partial flags
dasha/PeriodLookup Active chain at asOf with half-open intervals and cycle index
dasha/DashaInstantMath Day-to-instant arithmetic without calendar or timezone