# Build with an agent (/docs/agents) These docs are written to be read by a coding agent. An agent can do two separate things with Ascendant: | The agent should | Use | Where | | --- | --- | --- | | **Write code** that calculates charts, dashas, and transits | The docs and a prompt | This page | | **Answer astrology questions** from a saved birth chart | The Ascendant skill | [Skills and plugins](https://astro-ascendant.vercel.app/docs/skills) | ## Start with the prompt [#start-with-the-prompt] [Getting started](https://astro-ascendant.vercel.app/docs#start-with-a-prompt) has a prompt for any coding agent. Paste it, replace the last line with your task, and send it. It carries the docs location, runtime pattern, time-zone handling, and school rules. ## Give the agent the docs [#give-the-agent-the-docs] Pick the form that fits the job: | Form | Address | Use it for | | --- | --- | --- | | Index | [`/llms.txt`](https://astro-ascendant.vercel.app/llms.txt) | Every page with a one-line description. Start here. | | Everything | [`/llms-full.txt`](https://astro-ascendant.vercel.app/llms-full.txt) | All pages in one file, about 200 KB. Send it when the agent has the room. | | One page | `/docs/.md` | A single page as Markdown, such as `/docs/quickstart.md` or `/docs/guides/transits.md`. Getting started is `/docs/index.md`. | | Copy button | **Copy Markdown** on each page | One page pasted into a chat. | | Open menu | **Open** on each page | A page sent to ChatGPT, Claude, Cursor, or Scira AI, or its source on GitHub. | | Types | `node_modules/astro-ascendant` | Exact signatures from the shipped TypeScript declarations, once installed. | The [API reference](https://astro-ascendant.vercel.app/docs/api-reference/chart) is generated from the published package, so its names and types are exact. The guides explain how the pieces fit. ### Reading order [#reading-order] Done means the agent can name the guide and API page its task uses. 1. Read `/llms.txt` to see what exists. 2. Read the [Quickstart](https://astro-ascendant.vercel.app/docs/quickstart.md) and [Core concepts](https://astro-ascendant.vercel.app/docs/concepts.md) for the vocabulary and the runtime pattern. 3. Read the one guide for the task, such as [settings](https://astro-ascendant.vercel.app/docs/guides/settings.md), [dashas](https://astro-ascendant.vercel.app/docs/guides/dashas.md), or [transits](https://astro-ascendant.vercel.app/docs/guides/transits.md). 4. Look up each name in its API reference page before using it. ### Effect [#effect] Ascendant is built on Effect 4, and names changed between releases. Add Effect's own guide for current code: [`LLMS.md`](https://raw.githubusercontent.com/Effect-TS/effect/refs/heads/main/LLMS.md). The Quickstart lists more Effect resources. ## Keep the rules in your project [#keep-the-rules-in-your-project] Put the rules in the file your agent reads every session, such as `AGENTS.md` or `CLAUDE.md`: ```markdown ## Astrology (astro-ascendant) - Docs index: https://astro-ascendant.vercel.app/llms.txt. Read the relevant page before writing code, and take names from the API reference. - Effect docs: https://raw.githubusercontent.com/Effect-TS/effect/refs/heads/main/LLMS.md - Build one `ManagedRuntime` from `Runtime.MainLayer`, reuse it, and dispose it at shutdown. - Keep all moments in UTC. Convert local time with its IANA zone and the birth date, using `DateTime.makeZoned` with `disambiguation: "reject"`. Ask for missing birth data. - Calculate Placements once with `Chart.generate` and reuse them. - Default is KrishnamurtiVP291 + Placidus (KP). Use Lahiri + WholeSign for Parashari. Judge each question in one school, and report the settings used. - Handle typed errors with `Effect.catchTag`, and let defects propagate. - Describe results as mechanics: positions and periods, never predictions or medical, legal, or financial advice. ``` ## Review what the agent wrote [#review-what-the-agent-wrote] Each row is a mistake this library invites. Done means every row holds. | Check | Why it matters | | --- | --- | | One `ManagedRuntime`, created once | A runtime per request starts with an empty position cache. See [Ephemeris adapters and caching](https://astro-ascendant.vercel.app/docs/guides/ephemeris-and-caching). | | Local time converted with the zone for that **date** | Offsets change over time, and a daylight-saving gap or overlap can shift a birth by an hour. See [From local time to a Located Moment](https://astro-ascendant.vercel.app/docs/guides/located-moments). | | One school per judgment, settings stated | KP and Parashari use different ayanamsas and house systems. See [Choose a school and settings](https://astro-ascendant.vercel.app/docs/guides/settings). | | Every function and import exists | Agents invent names or reach for removed ones. `Chart.generate` replaces `Chart.project`, removed in 6.0. | | `Dasha.at` and `Dasha.atRashi` results checked for `null` | They return `null` outside the timeline. See [Dashas](https://astro-ascendant.vercel.app/docs/guides/dashas). | | Errors handled by tag | `Chart.generate` fails typed on bad input. See [Errors and typed failures](https://astro-ascendant.vercel.app/docs/guides/errors). | | Arudha Pada described as the plain projection | The library skips the classical adjustment. See [Jaimini calculations](https://astro-ascendant.vercel.app/docs/guides/jaimini). | | Results framed as positions and periods | A calculation is not a promise. | ### A known-good result [#a-known-good-result] Have the agent calculate this birth and compare. A match means its runtime, time conversion, and chart code are almost certainly right. - **Birth:** `2000-01-01 17:30` local time, `Asia/Kolkata`, latitude `12.9716`, longitude `77.5946` - **Settings:** defaults, KrishnamurtiVP291 with Placidus | Point | Expected | | --- | --- | | D1 Lagna | 69.68°, Gemini, Ardra pada 1 | | D9 Lagna sign | Sagittarius | | Moon | 199.55°, Swati pada 4, not retrograde | | Saturn | 16.62°, Bharani pada 1, retrograde | The Quickstart builds this program and shows its full output. The settings recorded on each result give the agent, and you, something to cite. See [Provenance and verification](https://astro-ascendant.vercel.app/docs/guides/provenance). ## Agents that answer questions [#agents-that-answer-questions] The Ascendant skill saves birth data once and answers from that record with cited evidence: ```bash npx ascendant-skills install ``` It works with Claude Code, Codex, OpenCode, Cursor, Gemini CLI, Copilot, and other skill-compatible agents. See [Skills and plugins](https://astro-ascendant.vercel.app/docs/skills) for per-tool steps. # AstroParams (/docs/api-reference/astro-params) {/* Generated from astro-ascendant@6.1.0. Do not edit by hand. Run `make sync-api` to regenerate. */} Import from `astro-ascendant/astro-params`: ```ts import * as AstroParams from "astro-ascendant/astro-params"; ``` ## Functions [#functions] ### `layer` [#layer] ```ts function layer(options: Options): Layer.Layer; ``` ### `layerConfig` [#layerconfig] ```ts function layerConfig( config: Config.Wrap, ): Layer.Layer; ``` ## Classes [#classes] ### `AstroParams` [#astroparams] ```ts declare class AstroParams extends Service_base {} ``` ## Constants [#constants] ### `Ayanamsa` [#ayanamsa] ```ts const Ayanamsa: Schema.Literals< readonly [ "FaganBradley", "Lahiri", "DeLuce", "Raman", "Ushashashi", "Krishnamurti", "DjwhalKhul", "Yukteshwar", "JNBhasin", "BabylKugler1", "BabylKugler2", "BabylKugler3", "BabylHuber", "BabylEtPSC", "Aldebaran15Tau", "Hipparchos", "Sassanian", "GalacticCenter0Sag", "J2000", "J1900", "B1950", "SuryaSiddhanta", "SuryaSiddhantaMeanSun", "Aryabhata", "AryabhataMeanSun", "SSRevati", "SSCitra", "TrueCitra", "TrueRevati", "TruePushya", "GalacticCenterGilBrand", "GalacticEquatorIAU1958", "GalacticEquator", "GalacticEquatorMidMula", "Skydram", "TrueMula", "DhruvaGalCenterMulaWilhelm", "Aryabhata522", "BabylBritton", "TrueSheoran", "GalacticCenterCochrane", "GalacticEquatorFiorenza", "ValensMoon", "Lahiri1940", "LahiriVP285", "KrishnamurtiVP291", "LahiriICRC", ] >; ``` ### `DefaultAstroParams` [#defaultastroparams] ```ts declare const DefaultAstroParams: Layer.Layer; ``` ### `HouseSystem` [#housesystem] ```ts const HouseSystem: Schema.Literals< readonly [ "Placidus", "Koch", "Porphyrius", "Regiomontanus", "Campanus", "Equal", "VehlowEqual", "WholeSign", "Meridian", "Azimuthal", "PolichPage", "Alcabitus", "Morinus", ] >; ``` ### `Options` [#options] ```ts const Options: Schema.Struct<{ readonly ayanamsa: Schema.Literals< readonly [ "FaganBradley", "Lahiri", "DeLuce", "Raman", "Ushashashi", "Krishnamurti", "DjwhalKhul", "Yukteshwar", "JNBhasin", "BabylKugler1", "BabylKugler2", "BabylKugler3", "BabylHuber", "BabylEtPSC", "Aldebaran15Tau", "Hipparchos", "Sassanian", "GalacticCenter0Sag", "J2000", "J1900", "B1950", "SuryaSiddhanta", "SuryaSiddhantaMeanSun", "Aryabhata", "AryabhataMeanSun", "SSRevati", "SSCitra", "TrueCitra", "TrueRevati", "TruePushya", "GalacticCenterGilBrand", "GalacticEquatorIAU1958", "GalacticEquator", "GalacticEquatorMidMula", "Skydram", "TrueMula", "DhruvaGalCenterMulaWilhelm", "Aryabhata522", "BabylBritton", "TrueSheoran", "GalacticCenterCochrane", "GalacticEquatorFiorenza", "ValensMoon", "Lahiri1940", "LahiriVP285", "KrishnamurtiVP291", "LahiriICRC", ] >; readonly houseSystem: Schema.Literals< readonly [ "Placidus", "Koch", "Porphyrius", "Regiomontanus", "Campanus", "Equal", "VehlowEqual", "WholeSign", "Meridian", "Azimuthal", "PolichPage", "Alcabitus", "Morinus", ] >; }>; ``` # Chart (/docs/api-reference/chart) {/* Generated from astro-ascendant@6.1.0. Do not edit by hand. Run `make sync-api` to regenerate. */} Import from `astro-ascendant/chart`: ```ts import * as Chart from "astro-ascendant/chart"; ``` ## Functions [#functions] ### `generate` [#generate] ```ts const generate: (input: ChartParams, divisions?: readonly (1 | 2 | 3 | 4 | 7 | 9 | 10 | 12 | 16 | 20 | 24 | 27 | 30 | 40 | 45 | 60)[] | undefined) => Effect.Effect<{ readonly placements: { readonly lagna: { readonly name: "Lagna"; readonly longitude: number & import("effect/Brand").Brand<"Longitude">; readonly star: { readonly name: "Anuradha" | "Ardra" | "Ashlesha" | "Ashwini" | "Bharani" | "Chitra" | "Dhanishta" | "Hasta" | "Jyeshtha" | "Krittika" | "Magha" | "Mrigashira" | "Mula" | "Punarvasu" | "Purva Ashadha" | "Purva Bhadrapada" | "Purva Phalguni" | "Pushya" | "Revati" | "Rohini" | "Shatabhisha" | "Shravana" | "Swati" | "Uttara Ashadha" | "Uttara Bhadrapada" | "Uttara Phalguni" | "Vishakha"; readonly lord: "Jupiter" | "Ketu" | "Mars" | "Mercury" | "Moon" | "Rahu" | "Saturn" | "Sun" | "Venus"; readonly pada: 1 | 2 | 3 | 4; }; }; readonly planets: readonly { readonly name: "Jupiter" | "Ketu" | "Mars" | "Mercury" | "Moon" | "Rahu" | "Saturn" | "Sun" | "Venus"; readonly longitude: number & import("effect/Brand").Brand<"Longitude">; readonly is_retrograde: boolean; readonly star: { readonly name: "Anuradha" | "Ardra" | "Ashlesha" | "Ashwini" | "Bharani" | "Chitra" | "Dhanishta" | "Hasta" | "Jyeshtha" | "Krittika" | "Magha" | "Mrigashira" | "Mula" | "Punarvasu" | "Purva Ashadha" | "Purva Bha // … truncated ``` ### `getDivisionalTarget` [#getdivisionaltarget] ```ts const getDivisionalTarget: ( longitude: number, division: 1 | 2 | 3 | 4 | 7 | 9 | 10 | 12 | 16 | 20 | 24 | 27 | 30 | 40 | 45 | 60, ) => Effect.Effect< import("./model.js").SourcePosition, DivisionalMappingError, never >; ``` ## Classes [#classes] ### `ChartCalculationError` [#chartcalculationerror] ```ts class ChartCalculationError extends ChartCalculationError_base {} ``` ### `DivisionalMappingError` [#divisionalmappingerror] ```ts class DivisionalMappingError extends DivisionalMappingError_base {} ``` ### `LocatedMomentValidationError` [#locatedmomentvalidationerror] ```ts class LocatedMomentValidationError extends LocatedMomentValidationError_base {} ``` ## Interfaces [#interfaces] ### `ChartParams` [#chartparams] ```ts interface ChartParams extends Schema.Schema.Type {} ``` ### `D1Chart` [#d1chart] ```ts interface D1Chart extends Schema.Schema.Type {} ``` ### `D1House` [#d1house] ```ts interface D1House extends Schema.Schema.Type {} ``` ### `DivisionalChart` [#divisionalchart] ```ts interface DivisionalChart extends Schema.Schema.Type {} ``` ### `DivisionalHouse` [#divisionalhouse] ```ts interface DivisionalHouse extends Schema.Schema.Type {} ``` ### `DivisionalTarget` [#divisionaltarget] ```ts interface DivisionalTarget { readonly signIndex: number; readonly degree: Degree; readonly longitude: Longitude; } ``` ### `LocatedMoment` [#locatedmoment] ```ts interface LocatedMoment extends Schema.Schema.Type {} ``` ### `Placements` [#placements] ```ts interface Placements extends Schema.Schema.Type {} ``` ### `SourcePosition` [#sourceposition] ```ts interface SourcePosition { readonly longitude: Longitude; readonly signIndex: number; readonly degree: Degree; } ``` ### `Subdivision` [#subdivision] ```ts interface Subdivision { readonly partIndex: number; readonly degree: number; } ``` ## Types [#types] ### `CircleAngle` [#circleangle] ```ts type CircleAngle = typeof CircleAngleSchema.Type; ``` ### `Degree` [#degree] ```ts type Degree = typeof DegreeSchema.Type; ``` ### `DivisionalDivision` [#divisionaldivision] ```ts type DivisionalDivision = typeof DivisionalDivision.Type; ``` ### `Longitude` [#longitude] ```ts type Longitude = typeof LongitudeSchema.Type; ``` ## Constants [#constants] ### `CalculationCharts` [#calculationcharts] ```ts const CalculationCharts: Schema.TupleWithRest>; readonly provenance: Schema.Struct<{ readonly school: Schema.Literal<"Universal">; readonly method: Schema.Literal<"house-system-cusps">; readonly version: Schema.Literal<"2026-11-01">; }>; readonly division: Schema.Literal<1>; readonly houses: Schema.$Record, Schema.Struct<{ readonly sign: Schema.Literals; readonly cusp: Schema.brand & { /** Wraps any finite angle onto the circle; use `Position.normalizeLongitude` for unchecked input. */ normalize(degrees: number): Longitude; /** The Longitude at a Degree within the Sign at a 0-based wheel position. */ inSign(signIndex: number, degree: number): Longitude; }; readonly signLord: Schema.Union, Schema.Literal<"Lagna">]>; readonly significations: Schema.$Array; readonly planets: Schema.$Array>; readonly provenance: Schema.Struct<{ readonly school: Schema.Literal<"Universal">; readonly method: Schema.Literal<"house-system-cusps">; readonly version: Schema.Literal<"2026-11-01">; }>; readonly division: Schema.Literal<1>; readonly houses: Schema.$Record, Schema.Struct<{ readonly sign: Schema.Literals; readonly cusp: Schema.brand & { /** Wraps any finite angle onto the circle; use `Position.normalizeLongitude` for unchecked input. */ normalize(degrees: number): Longitude; /** The Longitude at a Degree within the Sign at a 0-based wheel position. */ inSign(signIndex: number, degree: number): Longitude; }; readonly signLord: Schema.Union, Schema.Literal<"Lagna">]>; readonly significations: Schema.$Array; readonly planets: Schema.$Array & { /** Wraps any finite angle onto the circle. */ normalize(degrees: number): CircleAngle; }; readonly mc: Schema.brand & { /** Wraps any finite angle onto the circle. */ normalize(degrees: number): CircleAngle; }; readonly armc: Schema.brand & { /** Wraps any finite angle onto the circle. */ normalize(degrees: number): CircleAngle; }; readonly vertex: Schema.brand & { /** Wraps any finite angle onto the circle. */ normalize(degrees: number): CircleAngle; }; readonly equatorialAscendant: Schema.brand & { /** Wraps any finite angle onto the circle. */ normalize(degrees: number): CircleAngle; }; readonly coAscendant1: Schema.brand & { /** Wraps any finite angle onto the circle. */ normalize(degrees: number): CircleAngle; }; readonly coAscendant2: Schema.brand & { /** Wraps any finite angle onto the circle. */ normalize(degrees: number): CircleAngle; }; readonly polarAscendant: Schema.brand & { /** Wraps any finite angle onto the circle. */ normalize(degrees: number): CircleA // … truncated ``` ### `ChartCalculation` [#chartcalculation] ```ts const ChartCalculation: Schema.Struct<{ readonly placements: Schema.Struct<{ readonly lagna: Schema.Struct<{ readonly name: Schema.Literal<"Lagna">; readonly longitude: Schema.brand & { /** Wraps any finite angle onto the circle; use `Position.normalizeLongitude` for unchecked input. */ normalize(degrees: number): Longitude; /** The Longitude at a Degree within the Sign at a 0-based wheel position. */ inSign(signIndex: number, degree: number): Longitude; }; readonly star: Schema.Struct<{ readonly name: Schema.Literals; readonly lord: Schema.Literals; readonly pada: Schema.Literals; }>; }>; readonly planets: Schema.$Array; reado // … truncated ``` ### `D1ChartProvenance` [#d1chartprovenance] ```ts const D1ChartProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Universal">; readonly method: Schema.Literal<"house-system-cusps">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `D1Houses` [#d1houses] ```ts const D1Houses: Schema.$Record, Schema.Struct<{ readonly sign: Schema.Literals; readonly cusp: Schema.brand & { /** Wraps any finite angle onto the circle; use `Position.normalizeLongitude` for unchecked input. */ normalize(degrees: number): Longitude; /** The Longitude at a Degree within the Sign at a 0-based wheel position. */ inSign(signIndex: number, degree: number): Longitude; }; readonly signLord: Schema.Union, Schema.Literal<"Lagna">]>; readonly significations: Schema.$Array; readonly planets: Schema.$Array; readonly longitude: Schema.brand & { /** Wraps any finite angle onto the circle; use `Position.normalizeLongitude` for unchecked input. */ normalize(degrees: number): Longitude; /** The Longitude at a Degree within the Sign at a 0-based wheel position. */ inSign(signIndex: number, degree: number): Longitude; }; // … truncated ``` ### `Division` [#division] ```ts const Division: Schema.Literals< readonly [1, 2, 3, 4, 7, 9, 10, 12, 16, 20, 24, 27, 30, 40, 45, 60] >; ``` ### `DivisionalChartProvenance` [#divisionalchartprovenance] ```ts const DivisionalChartProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Parashari">; readonly method: Schema.Literal<"whole-sign-from-mapped-lagna">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `DivisionalHouses` [#divisionalhouses] ```ts const DivisionalHouses: Schema.$Record, Schema.Struct<{ readonly sign: Schema.Literals; readonly cusp: Schema.brand & { /** Wraps any finite angle onto the circle; use `Position.normalizeLongitude` for unchecked input. */ normalize(degrees: number): Longitude; /** The Longitude at a Degree within the Sign at a 0-based wheel position. */ inSign(signIndex: number, degree: number): Longitude; }; readonly signLord: Schema.Union, Schema.Literal<"Lagna">]>; readonly significations: Schema.$Array; readonly planets: Schema.$Array; readonly longitude: Schema.brand & { /** Wraps any finite angle onto the circle; use `Position.normalizeLongitude` for unchecked input. */ normalize(degrees: number): Longitude; /** The Longitude at a Degree within the Sign at a 0-based wheel position. */ inSign(signIndex: number, degree: number): Longitude; // … truncated ``` ### `HOUSE_SIGNIFICATIONS` [#house_significations] ```ts const HOUSE_SIGNIFICATIONS: { readonly 1: readonly [ "birth", "head", "physical body", "limbs", "physical features", "livelihood", ]; readonly 2: readonly [ "wealth", "food", "right eye", "face", "speech", "family", "property", ]; readonly 3: readonly [ "courage", "firmness", "right ear", "younger siblings", "heroism", "mental strength", ]; readonly 4: readonly [ "house", "home", "land", "mother", "vehicle", "happiness", "learning", ]; readonly 5: readonly [ "intellect", "children", "son", "belly", "traditional law", "virtuous acts", ]; readonly 6: readonly [ "debt", "wounds", "disease", "enemy", "sin", "fear", "humiliation", ]; readonly 7: readonly [ "desire", "love", "passion", "cohabitation", "partner", "public", "marriage", ]; readonly 8: readonly [ "life", "longevity", "mental pain", "defeat", "sorrow", "scandal", "death", "obstacle", ]; readonly 9: readonly [ "guru", "father", "worship", "virtue", "spiritual initiation", "fortune", ]; readonly 10: readonly [ "livelihood", "work", "commerce", "business", "rank", "honor", "occupation", ]; readonly 11: readonly [ "profit", "gain", "income", "receipt", "fulfillment", "elder siblings", "friends", ]; readonly 12: readonly [ "disappearance", "bondage", "loss", "bed", "poverty", "decline", "left eye", "leg", ]; }; ``` ### `Houses` [#houses] ```ts const Houses: Schema.Literals; ``` ### `HouseSignificators` [#housesignificators] ```ts const HouseSignificators: Schema.Struct<{ readonly house: Schema.Literals< readonly [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] >; readonly level1: Schema.$Array< Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] > >; readonly level2: Schema.$Array< Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] > >; readonly level3: Schema.$Array< Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] > >; readonly level4: Schema.$Array< Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] > >; }>; ``` ### `Lagna` [#lagna] ```ts const Lagna: Schema.Struct<{ readonly name: Schema.Literal<"Lagna">; readonly longitude: Schema.brand & { /** Wraps any finite angle onto the circle; use `Position.normalizeLongitude` for unchecked input. */ normalize(degrees: number): Longitude; /** The Longitude at a Degree within the Sign at a 0-based wheel position. */ inSign(signIndex: number, degree: number): Longitude; }; readonly degree: Schema.brand & { /** The Degree of a Longitude within the Sign that contains it. */ of(longitude: Longitude): Degree; }; readonly sign: Schema.Struct<{ readonly name: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly lord: Schema.Literals< readonly ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"] >; }>; }>; ``` ### `LagnaName` [#lagnaname] ```ts const LagnaName: Schema.Literal<"Lagna">; ``` ### `Moment` [#moment] ```ts const Moment: Schema.Struct<{ readonly date: Schema.DateTimeUtc; }>; ``` ### `Pada` [#pada] ```ts const Pada: Schema.Literals; ``` ### `PlacementsProvenance` [#placementsprovenance] ```ts const PlacementsProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Parashari">; readonly method: Schema.Literal<"sidereal-ephemeris-placements">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `Planet` [#planet] ```ts const Planet: Schema.Struct<{ readonly name: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly longitude: Schema.brand & { /** Wraps any finite angle onto the circle; use `Position.normalizeLongitude` for unchecked input. */ normalize(degrees: number): Longitude; /** The Longitude at a Degree within the Sign at a 0-based wheel position. */ inSign(signIndex: number, degree: number): Longitude; }; readonly degree: Schema.brand & { /** The Degree of a Longitude within the Sign that contains it. */ of(longitude: Longitude): Degree; }; readonly is_retrograde: Schema.Boolean; readonly in_sign: Schema.$Array< Schema.Literals< readonly [ "EXALTED", "MOOLA_TRIKONA", "OWN", "FRIEND", "NEUTRAL", "ENEMY", "DEBILITATED", ] > >; readonly sign: Schema.Struct<{ readonly name: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly lord: Schema.Literals< readonly ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"] >; }>; }>; ``` ### `PlanetDignity` [#planetdignity] ```ts const PlanetDignity: Schema.Literals< readonly [ "EXALTED", "MOOLA_TRIKONA", "OWN", "FRIEND", "NEUTRAL", "ENEMY", "DEBILITATED", ] >; ``` ### `Planets` [#planets] ```ts const Planets: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; ``` ### `PlanetSignification` [#planetsignification] ```ts const PlanetSignification: Schema.Struct<{ readonly planet: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly agent: Schema.optionalKey< Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] > >; readonly level1: Schema.$Array< Schema.Literals >; readonly level2: Schema.$Array< Schema.Literals >; readonly level3: Schema.$Array< Schema.Literals >; readonly level4: Schema.$Array< Schema.Literals >; }>; ``` ### `PlanetsLagna` [#planetslagna] ```ts const PlanetsLagna: Schema.Union< readonly [ Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >, Schema.Literal<"Lagna">, ] >; ``` ### `RashiLords` [#rashilords] ```ts const RashiLords: Schema.Literals< readonly ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"] >; ``` ### `Rashis` [#rashis] ```ts const Rashis: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; ``` ### `Sex` [#sex] ```ts const Sex: Schema.Literals; ``` ### `Sign` [#sign] ```ts const Sign: Schema.Struct<{ readonly name: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly lord: Schema.Literals< readonly ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"] >; }>; ``` ### `SourceLagna` [#sourcelagna] ```ts const SourceLagna: Schema.Struct<{ readonly name: Schema.Literal<"Lagna">; readonly longitude: Schema.brand & { /** Wraps any finite angle onto the circle; use `Position.normalizeLongitude` for unchecked input. */ normalize(degrees: number): Longitude; /** The Longitude at a Degree within the Sign at a 0-based wheel position. */ inSign(signIndex: number, degree: number): Longitude; }; readonly star: Schema.Struct<{ readonly name: Schema.Literals< readonly [ "Ashwini", "Bharani", "Krittika", "Rohini", "Mrigashira", "Ardra", "Punarvasu", "Pushya", "Ashlesha", "Magha", "Purva Phalguni", "Uttara Phalguni", "Hasta", "Chitra", "Swati", "Vishakha", "Anuradha", "Jyeshtha", "Mula", "Purva Ashadha", "Uttara Ashadha", "Shravana", "Dhanishta", "Shatabhisha", "Purva Bhadrapada", "Uttara Bhadrapada", "Revati", ] >; readonly lord: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly pada: Schema.Literals; }>; }>; ``` ### `SourcePlanet` [#sourceplanet] ```ts const SourcePlanet: Schema.Struct<{ readonly name: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly longitude: Schema.brand & { /** Wraps any finite angle onto the circle; use `Position.normalizeLongitude` for unchecked input. */ normalize(degrees: number): Longitude; /** The Longitude at a Degree within the Sign at a 0-based wheel position. */ inSign(signIndex: number, degree: number): Longitude; }; readonly is_retrograde: Schema.Boolean; readonly star: Schema.Struct<{ readonly name: Schema.Literals< readonly [ "Ashwini", "Bharani", "Krittika", "Rohini", "Mrigashira", "Ardra", "Punarvasu", "Pushya", "Ashlesha", "Magha", "Purva Phalguni", "Uttara Phalguni", "Hasta", "Chitra", "Swati", "Vishakha", "Anuradha", "Jyeshtha", "Mula", "Purva Ashadha", "Uttara Ashadha", "Shravana", "Dhanishta", "Shatabhisha", "Purva Bhadrapada", "Uttara Bhadrapada", "Revati", ] >; readonly lord: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly pada: Schema.Literals; }>; }>; ``` ### `Star` [#star] ```ts const Star: Schema.Struct<{ readonly name: Schema.Literals< readonly [ "Ashwini", "Bharani", "Krittika", "Rohini", "Mrigashira", "Ardra", "Punarvasu", "Pushya", "Ashlesha", "Magha", "Purva Phalguni", "Uttara Phalguni", "Hasta", "Chitra", "Swati", "Vishakha", "Anuradha", "Jyeshtha", "Mula", "Purva Ashadha", "Uttara Ashadha", "Shravana", "Dhanishta", "Shatabhisha", "Purva Bhadrapada", "Uttara Bhadrapada", "Revati", ] >; readonly lord: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly pada: Schema.Literals; }>; ``` ### `Stars` [#stars] ```ts const Stars: Schema.Literals< readonly [ "Ashwini", "Bharani", "Krittika", "Rohini", "Mrigashira", "Ardra", "Punarvasu", "Pushya", "Ashlesha", "Magha", "Purva Phalguni", "Uttara Phalguni", "Hasta", "Chitra", "Swati", "Vishakha", "Anuradha", "Jyeshtha", "Mula", "Purva Ashadha", "Uttara Ashadha", "Shravana", "Dhanishta", "Shatabhisha", "Purva Bhadrapada", "Uttara Bhadrapada", "Revati", ] >; ``` # Dasha (/docs/api-reference/dasha) {/* Generated from astro-ascendant@6.1.0. Do not edit by hand. Run `make sync-api` to regenerate. */} Import from `astro-ascendant/dasha`: ```ts import * as Dasha from "astro-ascendant/dasha"; ``` ## Classes [#classes] ### `DashaCalculationError` [#dashacalculationerror] ```ts class DashaCalculationError extends DashaCalculationError_base {} ``` ### `DashaEvidenceError` [#dashaevidenceerror] ```ts class DashaEvidenceError extends DashaEvidenceError_base {} ``` ## Types [#types] ### `EligibleBrahmaPlanet` [#eligiblebrahmaplanet] ```ts type EligibleBrahmaPlanet = typeof EligibleBrahmaPlanets.Type; ``` ## Constants [#constants] ### `AntarDasha` [#antardasha] ```ts const AntarDasha: Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly antardasha: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; }>; ``` ### `Brahma` [#brahma] ```ts const Brahma: Schema.Struct<{ readonly planet: Schema.Literals; readonly sign: Schema.Literals; readonly source: Schema.Literal<"strength">; readonly selection: Schema.Struct<{ readonly rashiBalas: Schema.Tuple; readonly charaBala: Schema.Finite; readonly sthiraBala: Schema.Finite; readonly drishtiBala: Schema.Finite; readonly planetCount: Schema.Int; readonly aspectingPlanets: Schema.$Array>; readonly total: Schema.Finite; }>, Schema.Struct<{ readonly sign: Schema.Literals; readonly charaBala: Schema.Finite; readonly sthiraBala: Schema.Finite; readonly drishtiBala: Schema.Finite; readonly planetCount: Schema.Int; readonly aspectingPlanets: Schema.$Array; readonly sign: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly dignity: Schema.Literals< readonly [ "EXALTED", "MOOLA_TRIKONA", "OWN", "FRIEND", "NEUTRAL", "ENEMY", "DEBILITATED", ] >; readonly dignityBala: Schema.Finite; readonly charaKarakaRoles: Schema.$Array< Schema.Literals< readonly [ "Atmakaraka", "Amatyakaraka", "Bhratrikaraka", "Matrikaraka", "Putrakaraka", "Gnatikaraka", "Darakaraka", ] > >; readonly charaKarakaBala: Schema.Finite; readonly kendradiHouseFromAtmakaraka: Schema.Int; readonly kendradiBala: Schema.Finite; readonly exactDegreeWithinSign: Schema.brand & { of(longitude: import("../chart/model.js").Longitude): Degree; }; readonly naturalStrength: Schema.Int; readonly total: Schema.Finite; }>; ``` ### `BrahmaSelection` [#brahmaselection] ```ts const BrahmaSelection: Schema.Struct<{ readonly rashiBalas: Schema.Tuple; readonly charaBala: Schema.Finite; readonly sthiraBala: Schema.Finite; readonly drishtiBala: Schema.Finite; readonly planetCount: Schema.Int; readonly aspectingPlanets: Schema.$Array>; readonly total: Schema.Finite; }>, Schema.Struct<{ readonly sign: Schema.Literals; readonly charaBala: Schema.Finite; readonly sthiraBala: Schema.Finite; readonly drishtiBala: Schema.Finite; readonly planetCount: Schema.Int; readonly aspectingPlanets: Schema.$Array>; readonly total: Schema.Finite; }>]>; readonly referenceSign: Schema.Literals; readonly referenceTieBreak: Schema.Literal<"lagna-on-equal-rashi-bala">; readonl // … truncated ``` ### `CharaDasha` [#charadasha] ```ts const CharaDasha: Schema.Struct<{ readonly system: Schema.Literal<"Chara">; readonly provenance: Schema.Struct<{ readonly school: Schema.Literal<"Jaimini">; readonly method: Schema.Literal<"kn-rao-co-lord-strength">; readonly version: Schema.Literal<"2026-11-01">; }>; readonly mahadashas: Schema.$Array< Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; readonly antardashas: Schema.$Array< Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly antardasha: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; }> >; }> >; }>; ``` ### `CharaDashaProvenance` [#charadashaprovenance] ```ts const CharaDashaProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Jaimini">; readonly method: Schema.Literal<"kn-rao-co-lord-strength">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `CurrentDasha` [#currentdasha] ```ts const CurrentDasha: Schema.Struct<{ readonly mahadasha: Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly provenance: Schema.Struct<{ readonly school: Schema.Literal<"Parashari">; readonly method: Schema.Literal<"moon-nakshatra-balance">; readonly version: Schema.Literal<"2026-11-01">; }>; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; readonly antardashas: Schema.$Array< Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly antardasha: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; }> >; }>; readonly antardasha: Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly antardasha: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; }>; }>; ``` ### `CurrentRashiDasha` [#currentrashidasha] ```ts const CurrentRashiDasha: Schema.Struct<{ readonly system: Schema.Literals; readonly mahadasha: Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; readonly antardashas: Schema.$Array< Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly antardasha: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; }> >; }>; readonly antardasha: Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly antardasha: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; }>; }>; ``` ### `EligibleBrahmaPlanets` [#eligiblebrahmaplanets] ```ts const EligibleBrahmaPlanets: Schema.Literals< readonly ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus"] >; ``` ### `MahaDasha` [#mahadasha] ```ts const MahaDasha: Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly provenance: Schema.Struct<{ readonly school: Schema.Literal<"Parashari">; readonly method: Schema.Literal<"moon-nakshatra-balance">; readonly version: Schema.Literal<"2026-11-01">; }>; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; readonly antardashas: Schema.$Array< Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly antardasha: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; }> >; }>; ``` ### `RashiAntarDasha` [#rashiantardasha] ```ts const RashiAntarDasha: Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly antardasha: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; }>; ``` ### `RashiBala` [#rashibala] ```ts const RashiBala: Schema.Struct<{ readonly sign: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly charaBala: Schema.Finite; readonly sthiraBala: Schema.Finite; readonly drishtiBala: Schema.Finite; readonly planetCount: Schema.Int; readonly aspectingPlanets: Schema.$Array< Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] > >; readonly total: Schema.Finite; }>; ``` ### `RashiDasha` [#rashidasha] ```ts const RashiDasha: Schema.toTaggedUnion<"system", readonly [Schema.Struct<{ readonly system: Schema.Literal<"Chara">; readonly provenance: Schema.Struct<{ readonly school: Schema.Literal<"Jaimini">; readonly method: Schema.Literal<"kn-rao-co-lord-strength">; readonly version: Schema.Literal<"2026-11-01">; }>; readonly mahadashas: Schema.$Array; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; readonly antardashas: Schema.$Array; readonly antardasha: Schema.Literals; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; }>>; }>>; }>, Schema.Struct<{ readonly system: Schema.Literal<"Sthira">; readonly provenance: Schema.Struct<{ readonly school: Schema.Literal<"Jaimini">; readonly method: Schema.Literal<"bv-raman-koch-brahma-strength">; readonly version: Schema.Literal<"2026-11-0 // … truncated ``` ### `RashiMahaDasha` [#rashimahadasha] ```ts const RashiMahaDasha: Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; readonly antardashas: Schema.$Array< Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly antardasha: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; }> >; }>; ``` ### `SthiraDasha` [#sthiradasha] ```ts const SthiraDasha: Schema.Struct<{ readonly system: Schema.Literal<"Sthira">; readonly provenance: Schema.Struct<{ readonly school: Schema.Literal<"Jaimini">; readonly method: Schema.Literal<"bv-raman-koch-brahma-strength">; readonly version: Schema.Literal<"2026-11-01">; }>; readonly brahma: Schema.Struct<{ readonly planet: Schema.Literals; readonly sign: Schema.Literals; readonly source: Schema.Literal<"strength">; readonly selection: Schema.Struct<{ readonly rashiBalas: Schema.Tuple; readonly charaBala: Schema.Finite; readonly sthiraBala: Schema.Finite; readonly drishtiBala: Schema.Finite; readonly planetCount: Schema.Int; readonly aspectingPlanets: Schema.$Array>; readonly total: Schema.Finite; }>, Schema.Struct<{ readonly sign: Schema.Literals; readonly method: Schema.Literal<"bv-raman-koch-brahma-strength">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `VimshottariDasha` [#vimshottaridasha] ```ts const VimshottariDasha: Schema.$Array< Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly provenance: Schema.Struct<{ readonly school: Schema.Literal<"Parashari">; readonly method: Schema.Literal<"moon-nakshatra-balance">; readonly version: Schema.Literal<"2026-11-01">; }>; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; readonly antardashas: Schema.$Array< Schema.Struct<{ readonly mahadasha: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly antardasha: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly start: Schema.DateTimeUtc; readonly end: Schema.DateTimeUtc; }> >; }> >; ``` ### `VimshottariDashaProvenance` [#vimshottaridashaprovenance] ```ts const VimshottariDashaProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Parashari">; readonly method: Schema.Literal<"moon-nakshatra-balance">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` # Ephemeris (/docs/api-reference/ephemeris) {/* Generated from astro-ascendant@6.1.0. Do not edit by hand. Run `make sync-api` to regenerate. */} Import from `astro-ascendant/ephemeris`: ```ts import * as Ephemeris from "astro-ascendant/ephemeris"; ``` ## Classes [#classes] ### `Ephemeris` [#ephemeris] ```ts declare class Ephemeris extends Service_base {} ``` ### `EphemerisError` [#ephemeriserror] ```ts class EphemerisError extends EphemerisError_base {} ``` ## Interfaces [#interfaces] ### `HouseData` [#housedata] ```ts interface HouseData extends Schema.Schema.Type {} ``` ### `PlanetaryPosition` [#planetaryposition] ```ts interface PlanetaryPosition extends Schema.Schema.Type< typeof PlanetaryPosition > {} ``` ### `PositionCacheOptions` [#positioncacheoptions] ```ts interface PositionCacheOptions { readonly capacity?: number | undefined; readonly timeToLive?: Duration.Input | undefined; } ``` ## Constants [#constants] ### `JulianDay` [#julianday] ```ts const JulianDay: Schema.brand; ``` # Jaimini (/docs/api-reference/jaimini) {/* Generated from astro-ascendant@6.1.0. Do not edit by hand. Run `make sync-api` to regenerate. */} Import from `astro-ascendant/argala`: ```ts import * as Jaimini from "astro-ascendant/argala"; ``` ## Functions [#functions] ### `calculate` [#calculate] ```ts const calculate: (placements: Placements) => Effect.Effect< { provenance: { readonly school: "Jaimini"; readonly method: "exact-degree-shared-roles"; readonly version: "2026-11-01"; }; assignments: { Atmakaraka: readonly [Holder, ...Holder[]]; Amatyakaraka: readonly [Holder, ...Holder[]]; Bhratrikaraka: readonly [Holder, ...Holder[]]; Matrikaraka: readonly [Holder, ...Holder[]]; Putrakaraka: readonly [Holder, ...Holder[]]; Gnatikaraka: readonly [Holder, ...Holder[]]; Darakaraka: readonly [Holder, ...Holder[]]; }; }, CharaKarakasEvidenceError | CharaKarakasParseError, never >; ``` ## Classes [#classes] ### `ArgalaCalculationError` [#argalacalculationerror] ```ts class ArgalaCalculationError extends ArgalaCalculationError_base {} ``` ### `ArgalaEvidenceError` [#argalaevidenceerror] ```ts class ArgalaEvidenceError extends ArgalaEvidenceError_base {} ``` ### `ArudhaPadaEvidenceError` [#arudhapadaevidenceerror] ```ts class ArudhaPadaEvidenceError extends ArudhaPadaEvidenceError_base {} ``` ### `CharaKarakasEvidenceError` [#charakarakasevidenceerror] ```ts class CharaKarakasEvidenceError extends CharaKarakasEvidenceError_base {} ``` ### `CharaKarakasParseError` [#charakarakasparseerror] ```ts class CharaKarakasParseError extends CharaKarakasParseError_base {} ``` ### `KarakamshaCalculationError` [#karakamshacalculationerror] ```ts class KarakamshaCalculationError extends KarakamshaCalculationError_base {} ``` ### `KarakamshaEvidenceError` [#karakamshaevidenceerror] ```ts class KarakamshaEvidenceError extends KarakamshaEvidenceError_base {} ``` ### `UpapadaEvidenceError` [#upapadaevidenceerror] ```ts class UpapadaEvidenceError extends UpapadaEvidenceError_base {} ``` ## Interfaces [#interfaces] ### `RankedHolder` [#rankedholder] ```ts interface RankedHolder extends Holder { readonly exactDegree: ExactDegreeValue; } ``` ## Types [#types] ### `CalculationError` [#calculationerror] ```ts type CalculationError = ArgalaCalculationError; ``` ### `EvidenceError` [#evidenceerror] ```ts type EvidenceError = ArgalaEvidenceError; ``` ### `ParseError` [#parseerror] ```ts type ParseError = CharaKarakasParseError; ``` ### `Role` [#role] ```ts type Role = typeof Roles.Type; ``` ## Constants [#constants] ### `Assignments` [#assignments] ```ts const Assignments: Schema.$Record< Schema.Literals< readonly [ "Atmakaraka", "Amatyakaraka", "Bhratrikaraka", "Matrikaraka", "Putrakaraka", "Gnatikaraka", "Darakaraka", ] >, Schema.NonEmptyArray< Schema.Struct<{ readonly planet: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", ] >; readonly degree: Schema.brand & { of(longitude: import("../../chart/model.js").Longitude): Degree; }; }> > >; ``` ### `ClassicalPlanets` [#classicalplanets] ```ts const ClassicalPlanets: Schema.Literals< readonly ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"] >; ``` ### `Holder` [#holder] ```ts const Holder: Schema.Struct<{ readonly planet: Schema.Literals< readonly ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"] >; readonly degree: Schema.brand & { of(longitude: import("../../chart/model.js").Longitude): Degree; }; }>; ``` ### `KetuReference` [#ketureference] ```ts const KetuReference: Schema.Struct<{ readonly kind: Schema.Literal<"Ketu">; }>; ``` ### `Placement` [#placement] ```ts const Placement: Schema.Struct<{ readonly planet: Schema.Literals< readonly ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"] >; readonly sign: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; }>; ``` ### `Positions` [#positions] ```ts const Positions: Schema.Literals; ``` ### `Provenance` [#provenance] ```ts const Provenance: Schema.Struct<{ readonly school: Schema.Literal<"Jaimini">; readonly method: Schema.Literal<"structural-positions">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `Reference` [#reference] ```ts const Reference: Schema.Union< readonly [ Schema.Struct<{ readonly kind: Schema.Literal<"Sign">; readonly sign: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; }>, Schema.Struct<{ readonly kind: Schema.Literal<"Ketu">; }>, ] >; ``` ### `Relation` [#relation] ```ts const Relation: Schema.Struct<{ readonly position: Schema.Literals; readonly sign: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; readonly planets: Schema.$Array< Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] > >; }>; ``` ### `Result` [#result] ```ts const Result: Schema.Struct<{ readonly provenance: Schema.Struct<{ readonly school: Schema.Literal<"Jaimini">; readonly method: Schema.Literal<"structural-positions">; readonly version: Schema.Literal<"2026-11-01">; }>; readonly reference: Schema.Union; readonly sign: Schema.Literals; }>, Schema.Struct<{ readonly kind: Schema.Literal<"Ketu">; }>]>; readonly referenceSign: Schema.Literals; readonly direction: Schema.Literals; readonly supporting: Schema.Tuple; readonly sign: Schema.Literals; readonly planets: Schema.$Array>; }>, Schema.Struct<{ readonly position: Schema.Literals; readonly sign: Schema.Literals; ``` ### `SignReference` [#signreference] ```ts const SignReference: Schema.Struct<{ readonly kind: Schema.Literal<"Sign">; readonly sign: Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >; }>; ``` # Provenance (/docs/api-reference/provenance) {/* Generated from astro-ascendant@6.1.0. Do not edit by hand. Run `make sync-api` to regenerate. */} Import from `astro-ascendant/provenance`: ```ts import * as Provenance from "astro-ascendant/provenance"; ``` ## Types [#types] ### `MethodId` [#methodid] ```ts type MethodId = keyof typeof methods; ``` ## Constants [#constants] ### `ashtakavarga` [#ashtakavarga] ```ts const ashtakavarga: { readonly provenance: { readonly school: "Parashari"; readonly method: "bphs-bav-trikona-ekadhipatya"; readonly version: "2026-11-01"; }; readonly steps: readonly [ { readonly id: "score-bhinna"; readonly description: "Give each planet and the Lagna a bindu per contributor whose classical houses include the sign."; }, { readonly id: "sum-sarva"; readonly description: "Add the seven planetary tables into the Sarvashtakavarga."; }, { readonly id: "reduce"; readonly description: "Apply Trikona then Ekadhipatya reduction, the latter by planet occupancy of each pair of signs."; }, { readonly id: "weigh-pinda"; readonly description: "Weigh the reduced scores by the Rashi and Graha multipliers into Shodhya Pinda."; }, ]; readonly verification: readonly [ "Each planetary table totals its classical bindu count and the Sarvashtakavarga totals 337.", "Reduction never leaves a negative score.", ]; }; ``` ### `AshtakavargaProvenance` [#ashtakavargaprovenance] ```ts const AshtakavargaProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Parashari">; readonly method: Schema.Literal<"bphs-bav-trikona-ekadhipatya">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `CalculationProvenance` [#calculationprovenance] ```ts const CalculationProvenance: Schema.Struct<{ readonly school: Schema.String; readonly method: Schema.String; readonly version: Schema.Union; }>; ``` ### `charaDasha` [#charadasha] ```ts const charaDasha: { readonly provenance: { readonly school: "Jaimini"; readonly method: "kn-rao-co-lord-strength"; readonly version: "2026-11-01"; }; readonly steps: readonly [ { readonly id: "derive-direction"; readonly description: "Set direction from the ninth sign's pada group."; }, { readonly id: "resolve-co-lords"; readonly description: "Resolve Scorpio and Aquarius co-lords by occupation, association count, and exact degree."; }, { readonly id: "build-periods"; readonly description: "Build sign periods and equal-twelfth antardashas."; }, ]; readonly verification: readonly [ "All twelve sign periods are contiguous.", "Scorpio and Aquarius co-lord resolution is deterministic.", ]; }; ``` ### `CharaDashaProvenance` [#charadashaprovenance] ```ts const CharaDashaProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Jaimini">; readonly method: Schema.Literal<"kn-rao-co-lord-strength">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `d1ChartProjection` [#d1chartprojection] ```ts const d1ChartProjection: { readonly provenance: { readonly school: "Universal"; readonly method: "house-system-cusps"; readonly version: "2026-11-01"; }; readonly steps: readonly [ { readonly id: "place-points"; readonly description: "Use the Lagna and planets at their source longitudes."; }, { readonly id: "build-cusp-houses"; readonly description: "Begin each house at the selected House system's cusp."; }, { readonly id: "derive-kp"; readonly description: "Derive KP lords, significations and ruling planets from the cusps."; }, ]; readonly verification: readonly [ "D1 is an identity projection.", "Planets fall in the house whose cusp span contains them.", "Degenerate cusps fail rather than fall back to equal houses.", ]; }; ``` ### `D1ChartProvenance` [#d1chartprovenance] ```ts const D1ChartProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Universal">; readonly method: Schema.Literal<"house-system-cusps">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `divisionalChartProjection` [#divisionalchartprojection] ```ts const divisionalChartProjection: { readonly provenance: { readonly school: "Parashari"; readonly method: "whole-sign-from-mapped-lagna"; readonly version: "2026-11-01"; }; readonly steps: readonly [ { readonly id: "map-longitudes"; readonly description: "Map the Lagna and planets into the Division."; }, { readonly id: "build-whole-sign-houses"; readonly description: "Count twelve whole-sign houses from the mapped Lagna's Sign."; }, ]; readonly verification: readonly [ "Requested divisions are unique and ordered after D1.", "Each house holds exactly the planets mapped into its Sign.", ]; }; ``` ### `DivisionalChartProvenance` [#divisionalchartprovenance] ```ts const DivisionalChartProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Parashari">; readonly method: Schema.Literal<"whole-sign-from-mapped-lagna">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `jaiminiArgala` [#jaiminiargala] ```ts const jaiminiArgala: { readonly provenance: { readonly school: "Jaimini"; readonly method: "structural-positions"; readonly version: "2026-11-01"; }; readonly steps: readonly [ { readonly id: "validate-placements"; readonly description: "Require one placement for every planet."; }, { readonly id: "derive-relations"; readonly description: "Calculate supporting and obstructing sign positions."; }, ]; readonly verification: readonly [ "Ketu reference reverses direction.", "Supporting and obstructing positions are fixed.", ]; }; ``` ### `JaiminiArgalaProvenance` [#jaiminiargalaprovenance] ```ts const JaiminiArgalaProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Jaimini">; readonly method: Schema.Literal<"structural-positions">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `jaiminiArudhaPada` [#jaiminiarudhapada] ```ts const jaiminiArudhaPada: { readonly provenance: { readonly school: "Jaimini"; readonly method: "plain-projection"; readonly version: "2026-11-01"; }; readonly steps: readonly [ { readonly id: "derive-source"; readonly description: "Find the source sign and its lord for the requested house."; }, { readonly id: "project"; readonly description: "Project the lord's distance from the source sign."; }, ]; readonly verification: readonly [ "Exactly one placement is required for the house lord.", ]; }; ``` ### `JaiminiArudhaPadaProvenance` [#jaiminiarudhapadaprovenance] ```ts const JaiminiArudhaPadaProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Jaimini">; readonly method: Schema.Literal<"plain-projection">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `jaiminiCharaKarakas` [#jaiminicharakarakas] ```ts const jaiminiCharaKarakas: { readonly provenance: { readonly school: "Jaimini"; readonly method: "exact-degree-shared-roles"; readonly version: "2026-11-01"; }; readonly steps: readonly [ { readonly id: "rank"; readonly description: "Rank classical planets by exact degree within their signs."; }, { readonly id: "assign"; readonly description: "Assign Karaka roles, sharing every role at an exact tie."; }, ]; readonly verification: readonly [ "All seven classical planet placements are required.", "Exact ties share roles.", ]; }; ``` ### `JaiminiCharaKarakasProvenance` [#jaiminicharakarakasprovenance] ```ts const JaiminiCharaKarakasProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Jaimini">; readonly method: Schema.Literal<"exact-degree-shared-roles">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `jaiminiKarakamsha` [#jaiminikarakamsha] ```ts const jaiminiKarakamsha: { readonly provenance: { readonly school: "Jaimini"; readonly method: "atmakaraka-d9-sign"; readonly version: "2026-11-01"; }; readonly steps: readonly [ { readonly id: "select-atmakaraka"; readonly description: "Use the Chara Karaka Atmakaraka assignment."; }, { readonly id: "map-d9"; readonly description: "Map each Atmakaraka to its D9 sign."; }, ]; readonly verification: readonly ["Every tied Atmakaraka is reported."]; }; ``` ### `JaiminiKarakamshaProvenance` [#jaiminikarakamshaprovenance] ```ts const JaiminiKarakamshaProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Jaimini">; readonly method: Schema.Literal<"atmakaraka-d9-sign">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `jaiminiRashiDrishti` [#jaiminirashidrishti] ```ts const jaiminiRashiDrishti: { readonly provenance: { readonly school: "Jaimini"; readonly method: "movable-fixed-dual"; readonly version: "2026-11-01"; }; readonly steps: readonly [ { readonly id: "classify-reference"; readonly description: "Classify the reference sign as movable, fixed, or dual."; }, { readonly id: "derive-targets"; readonly description: "Return the three signs receiving its Rashi Drishti."; }, ]; readonly verification: readonly [ "Every reference sign has exactly three targets.", ]; }; ``` ### `JaiminiRashiDrishtiProvenance` [#jaiminirashidrishtiprovenance] ```ts const JaiminiRashiDrishtiProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Jaimini">; readonly method: Schema.Literal<"movable-fixed-dual">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `jaiminiUpapada` [#jaiminiupapada] ```ts const jaiminiUpapada: { readonly provenance: { readonly school: "Jaimini"; readonly method: "twelfth-house-plain-projection"; readonly version: "2026-11-01"; }; readonly steps: readonly [ { readonly id: "fix-house"; readonly description: "Use the twelfth house as the Arudha source."; }, { readonly id: "project"; readonly description: "Apply the plain Arudha projection to that source."; }, ]; readonly verification: readonly ["The result always reports house twelve."]; }; ``` ### `JaiminiUpapadaProvenance` [#jaiminiupapadaprovenance] ```ts const JaiminiUpapadaProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Jaimini">; readonly method: Schema.Literal<"twelfth-house-plain-projection">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `methods` [#methods] ```ts const methods: { readonly d1ChartProjection: { readonly provenance: { readonly school: "Universal"; readonly method: "house-system-cusps"; readonly version: "2026-11-01"; }; readonly steps: readonly [{ readonly id: "place-points"; readonly description: "Use the Lagna and planets at their source longitudes."; }, { readonly id: "build-cusp-houses"; readonly description: "Begin each house at the selected House system's cusp."; }, { readonly id: "derive-kp"; readonly description: "Derive KP lords, significations and ruling planets from the cusps."; }]; readonly verification: readonly ["D1 is an identity projection.", "Planets fall in the house whose cusp span contains them.", "Degenerate cusps fail rather than fall back to equal houses."]; }; readonly divisionalChartProjection: { readonly provenance: { readonly school: "Parashari"; readonly method: "whole-sign-from-mapped-lagna"; readonly version: "2026-11-01"; }; readonly steps: readonly [{ readonly id: "map-longitudes"; readonly description: "Map the Lagna and planets into the Division."; }, { readonly id: "build-whole-sign-houses"; readonly description: "Count twelve whole-sign houses from the mapped Lagna's Sign."; }]; read // … truncated ``` ### `MethodSpecification` [#methodspecification] ```ts const MethodSpecification: Schema.Struct<{ readonly provenance: Schema.Struct<{ readonly school: Schema.String; readonly method: Schema.String; readonly version: Schema.Union; }>; readonly steps: Schema.NonEmptyArray< Schema.Struct<{ readonly id: Schema.String; readonly description: Schema.String; }> >; readonly verification: Schema.NonEmptyArray; }>; ``` ### `MethodStep` [#methodstep] ```ts const MethodStep: Schema.Struct<{ readonly id: Schema.String; readonly description: Schema.String; }>; ``` ### `placements` [#placements] ```ts const placements: { readonly provenance: { readonly school: "Parashari"; readonly method: "sidereal-ephemeris-placements"; readonly version: "2026-11-01"; }; readonly steps: readonly [ { readonly id: "sample-positions"; readonly description: "Read each graha's sidereal longitude at the moment for the selected ayanamsa; Ketu is opposite Rahu, the mean node."; }, { readonly id: "place-lagna"; readonly description: "Take the Lagna from the ascendant of the selected house system."; }, { readonly id: "assign-stars"; readonly description: "Assign each point its nakshatra, pada and star lord."; }, ]; readonly verification: readonly [ "Every longitude lies in [0, 360).", "Rahu and Ketu are opposite and never flagged retrograde.", ]; }; ``` ### `PlacementsProvenance` [#placementsprovenance] ```ts const PlacementsProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Parashari">; readonly method: Schema.Literal<"sidereal-ephemeris-placements">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `sthiraDasha` [#sthiradasha] ```ts const sthiraDasha: { readonly provenance: { readonly school: "Jaimini"; readonly method: "bv-raman-koch-brahma-strength"; readonly version: "2026-11-01"; }; readonly steps: readonly [ { readonly id: "select-reference"; readonly description: "Compare Lagna and seventh-sign Rashi Bala."; }, { readonly id: "select-brahma"; readonly description: "Score eligible sixth, eighth, and twelfth lords by Graha Bala."; }, { readonly id: "build-periods"; readonly description: "Start at Brahma's sign and assign 7, 8, or 9 years by modality."; }, ]; readonly verification: readonly [ "Brahma scorecards expose all candidate scores.", "Exact-degree then natural-strength breaks score ties.", ]; }; ``` ### `SthiraDashaProvenance` [#sthiradashaprovenance] ```ts const SthiraDashaProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Jaimini">; readonly method: Schema.Literal<"bv-raman-koch-brahma-strength">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `transitSearch` [#transitsearch] ```ts const transitSearch: { readonly provenance: { readonly school: "Parashari"; readonly method: "transit-sample-bisect"; readonly version: "2026-11-01"; }; readonly steps: readonly [ { readonly id: "sample-coarse"; readonly description: "Leap from the located moment by the time the graha needs to reach the nearest boundary, target or station, from per-planet speed and acceleration limits."; }, { readonly id: "bisect-brackets"; readonly description: "Refine each bracketed crossing to the requested precision with Newton and secant steps kept inside the bracket."; }, ]; readonly verification: readonly [ "Every zero-crossing counts in strict time order.", "Exhausted searches fail typed instead of returning short lists.", "Searches find the events an hourly scan of the ephemeris finds.", ]; }; ``` ### `TransitSearchProvenance` [#transitsearchprovenance] ```ts const TransitSearchProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Parashari">; readonly method: Schema.Literal<"transit-sample-bisect">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` ### `vimshottariDasha` [#vimshottaridasha] ```ts const vimshottariDasha: { readonly provenance: { readonly school: "Parashari"; readonly method: "moon-nakshatra-balance"; readonly version: "2026-11-01"; }; readonly steps: readonly [ { readonly id: "derive-balance"; readonly description: "Find the Moon's nakshatra lord and the exact arc travelled to get the period elapsed at birth."; }, { readonly id: "build-mahadashas"; readonly description: "Order the nine lords from the star lord and start the first period before birth by the elapsed part."; }, { readonly id: "build-antardashas"; readonly description: "Split each Mahadasha by the lords' shares of the 120-year cycle."; }, ]; readonly verification: readonly [ "The nine Mahadashas are contiguous and span 120 years.", "Antardashas tile their Mahadasha and end on its end.", ]; }; ``` ### `VimshottariDashaProvenance` [#vimshottaridashaprovenance] ```ts const VimshottariDashaProvenance: Schema.Struct<{ readonly school: Schema.Literal<"Parashari">; readonly method: Schema.Literal<"moon-nakshatra-balance">; readonly version: Schema.Literal<"2026-11-01">; }>; ``` # SAV (/docs/api-reference/sav) {/* Generated from astro-ascendant@6.1.0. Do not edit by hand. Run `make sync-api` to regenerate. */} Import from `astro-ascendant/sav`: ```ts import * as SAV from "astro-ascendant/sav"; ``` ## Functions [#functions] ### `calculate` [#calculate] ```ts const calculate: (placements: Placements) => Effect.Effect<{ provenance: { readonly school: "Parashari"; readonly method: "bphs-bav-trikona-ekadhipatya"; readonly version: "2026-11-01"; }; bhinna: { readonly Jupiter: { readonly Aquarius: number; readonly Aries: number; readonly Cancer: number; readonly Capricorn: number; readonly Gemini: number; readonly Leo: number; readonly Libra: number; readonly Pisces: number; readonly Sagittarius: number; readonly Scorpio: number; readonly Taurus: number; readonly Virgo: number; }; readonly Lagna: { readonly Aquarius: number; readonly Aries: number; readonly Cancer: number; readonly Capricorn: number; readonly Gemini: number; readonly Leo: number; readonly Libra: number; readonly Pisces: number; readonly Sagittarius: number; readonly Scorpio: number; readonly Taurus: number; readonly Virgo: number; }; readonly Mars: { readonly Aquarius: number; readonly Aries: number; readonly Cancer: number; readonly Capricorn: number; readonly Gemini: number; readonly Leo: number; readonly Libra: number; // … truncated ``` ## Classes [#classes] ### `SAVCalculationError` [#savcalculationerror] ```ts class SAVCalculationError extends SAVCalculationError_base {} ``` ## Constants [#constants] ### `AshtakavargaEntities` [#ashtakavargaentities] ```ts const AshtakavargaEntities: Schema.Union< readonly [ Schema.Literals< readonly ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"] >, Schema.Literal<"Lagna">, ] >; ``` ### `AshtakavargaPlanets` [#ashtakavargaplanets] ```ts const AshtakavargaPlanets: Schema.Literals< readonly ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"] >; ``` ### `AshtakavargaResult` [#ashtakavargaresult] ```ts const AshtakavargaResult: Schema.Struct<{ readonly provenance: Schema.Struct<{ readonly school: Schema.Literal<"Parashari">; readonly method: Schema.Literal<"bphs-bav-trikona-ekadhipatya">; readonly version: Schema.Literal<"2026-11-01">; }>; readonly bhinna: Schema.$Record, Schema.Literal<"Lagna">]>, Schema.$Record, Schema.Int>>; readonly sarva: Schema.$Record, Schema.Int>; readonly reduced: Schema.$Record, Schema.$Record, Schema.Int>>; readonly shodhya_pinda: Schema.$Record, Schema.Struct<{ readonly rashi_pinda: Schema.Int; readonly graha_pinda: Schema.Int; readonly shodhya_pinda: Schema.Int; }>>; readonly totals: Schema.Struct<{ readonly Sun: Schema. // … truncated ``` ### `AshtakavargaTotals` [#ashtakavargatotals] ```ts const AshtakavargaTotals: Schema.Struct<{ readonly Sun: Schema.Int; readonly Moon: Schema.Int; readonly Mars: Schema.Int; readonly Mercury: Schema.Int; readonly Jupiter: Schema.Int; readonly Venus: Schema.Int; readonly Saturn: Schema.Int; readonly Lagna: Schema.Int; readonly sarva: Schema.Int; }>; ``` ### `BhinnaAshtakavarga` [#bhinnaashtakavarga] ```ts const BhinnaAshtakavarga: Schema.$Record< Schema.Union< readonly [ Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", ] >, Schema.Literal<"Lagna">, ] >, Schema.$Record< Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >, Schema.Int > >; ``` ### `Pinda` [#pinda] ```ts const Pinda: Schema.Struct<{ readonly rashi_pinda: Schema.Int; readonly graha_pinda: Schema.Int; readonly shodhya_pinda: Schema.Int; }>; ``` ### `ReducedAshtakavarga` [#reducedashtakavarga] ```ts const ReducedAshtakavarga: Schema.$Record< Schema.Literals< readonly ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"] >, Schema.$Record< Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >, Schema.Int > >; ``` ### `ShodhyaPinda` [#shodhyapinda] ```ts const ShodhyaPinda: Schema.$Record< Schema.Literals< readonly ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"] >, Schema.Struct<{ readonly rashi_pinda: Schema.Int; readonly graha_pinda: Schema.Int; readonly shodhya_pinda: Schema.Int; }> >; ``` ### `SignScores` [#signscores] ```ts const SignScores: Schema.$Record< Schema.Literals< readonly [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] >, Schema.Int >; ``` # Swisseph (/docs/api-reference/swisseph) {/* Generated from astro-ascendant@6.1.0. Do not edit by hand. Run `make sync-api` to regenerate. */} Import from `astro-ascendant/swisseph`: ```ts import * as Swisseph from "astro-ascendant/swisseph"; ``` ## Constants [#constants] ### `SwissephLayer` [#swissephlayer] ```ts declare const SwissephLayer: Layer.Layer; ``` # Transit (/docs/api-reference/transit) {/* Generated from astro-ascendant@6.1.0. Do not edit by hand. Run `make sync-api` to regenerate. */} Import from `astro-ascendant/transit`: ```ts import * as Transit from "astro-ascendant/transit"; ``` ## Functions [#functions] ### `findTransits` [#findtransits] ```ts const findTransits: ( request: TransitRequest, ) => Effect.Effect< readonly TransitEvent[], | import("../chart/error.js").ChartCalculationError | import("../ephemeris/error.js").EphemerisError | import("../chart/error.js").LocatedMomentValidationError | import("../utils/position.js").LongitudeError | TransitSearchExhausted | TransitValidationError, AstroParams | Ephemeris >; ``` ## Classes [#classes] ### `TransitSearchExhausted` [#transitsearchexhausted] ```ts class TransitSearchExhausted extends TransitSearchExhausted_base {} ``` ### `TransitValidationError` [#transitvalidationerror] ```ts class TransitValidationError extends TransitValidationError_base {} ``` ## Constants [#constants] ### `TransitDirection` [#transitdirection] ```ts const TransitDirection: Schema.Literals; ``` ### `TransitEvent` [#transitevent] ```ts const TransitEvent: Schema.Struct<{ readonly planet: Schema.Literals; readonly moment: Schema.DateTimeUtc; readonly longitude: Schema.brand & { normalize(degrees: number): Longitude; inSign(signIndex: number, degree: number): Longitude; }; readonly kind: Schema.Literals; /** * For `sign-ingress`, the sign entered at that instant in the direction of the search * (a backward search reports the earlier sign); otherwise the sign at the event. */ readonly sign: Schema.optionalKey>; /** Whether the graha moves backwards at the event; just after it, at a `station`. */ readonly is_retrograde: Schema.Boolean; readonly direction: Schema.Literals; readonly provenance: Schema.Struct<{ readonly school: Schema.Literal<"Parashari">; readonly method: Schema.Literal<"transit-sample-bisect">; readonly version: Schema.Literal<"2026-11-01">; }>; readonly calculation: Schema.optionalKey; ``` ### `TransitRequest` [#transitrequest] ```ts const TransitRequest: Schema.Struct<{ readonly planet: Schema.Literals< readonly [ "Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu", ] >; readonly from: Schema.Struct<{ readonly moment: Schema.Struct<{ readonly date: Schema.DateTimeUtc; }>; readonly latitude: Schema.Finite; readonly longitude: Schema.Finite; }>; readonly count: Schema.Int; readonly direction: Schema.Literals; readonly kinds: Schema.NonEmptyArray< Schema.Literals< readonly ["sign-ingress", "cusp-crossing", "longitude-hit", "station"] > >; readonly targetLongitude: Schema.optionalKey< Schema.brand & { normalize(degrees: number): Longitude; inSign(signIndex: number, degree: number): Longitude; } >; readonly house: Schema.optionalKey< Schema.Literals >; readonly maxYears: Schema.optionalKey; readonly precisionMinutes: Schema.optionalKey; readonly includeCharts: Schema.optionalKey< Schema.$Array< Schema.Literals< readonly [1, 2, 3, 4, 7, 9, 10, 12, 16, 20, 24, 27, 30, 40, 45, 60] > > >; }>; ``` # Core concepts (/docs/concepts) Ascendant calculates positions once, then derives everything else from them. The same few terms appear in every function name, type, and guide, so they are worth learning first. moment + latitude + longitude"] --> B["Chart.generate"] S["AstroParams
ayanamsa + house system"] --> B B --> P["Placements"] B --> D1["D1 chart
cusp houses + KP"] B --> DV["Divisional charts
whole-sign houses"] P --> DA["Dasha"] P --> SAV["Ashtakavarga"] P --> J["Jaimini"] P --> T["Transit search"]`} /> ## Input [#input] | Term | Meaning | | --- | --- | | **Moment** | The instant at which positions are calculated. It can be a birth, a transit, or any other event. It is always UTC and is separate from settings. | | **Located Moment** | A Moment paired with a latitude and longitude. It is the input to every chart calculation. | | **AstroParams** | The two settings of a calculation: the **ayanamsa** and the **house system**. They live outside the Moment, and every result records them. | The **ayanamsa** aligns tropical longitudes to the fixed zodiac, and Ascendant ships 47 predefined ones. The **house system** decides the cusps of D1, and Ascendant supports 13. The default is KrishnamurtiVP291 with Placidus. See [Choose a school and settings](https://astro-ascendant.vercel.app/docs/guides/settings). ## Positions and charts [#positions-and-charts] | Term | Meaning | | --- | --- | | **Placements** | The exact sidereal longitudes and motion states of the Lagna and the grahas at a Located Moment, with each star and pada. Every chart and every other calculation is derived from them. | | **Chart calculation** | The Placements plus the requested charts, derived together under one set of AstroParams. It always contains D1. | | **Division** | A chart's identity in the divisional system: D1, D2, D3, and so on up to D60. | | **D1 chart** | The chart whose houses start at the house system's cusps. Only D1 carries the chart angles, the KP star, sub and sub-sub lords, significations, and ruling planets. | | **Divisional chart** | A chart made by mapping the Lagna and grahas through a Division. Its houses are whole signs counted from the mapped Lagna, whatever the house system. It has no angles or KP data. | | **Planet** | A graha as placed in one chart: its longitude, degree, sign, dignity, and retrograde state. Its star and pada stay on the Placements. | | **Sign** | One of the twelve zodiac signs, with its lord. | | **Degree** | A position within a sign, from 0 up to but not including 30. In a divisional chart it is the degree the mapping produces. | Rahu and Ketu are grahas that behave like planets in every way except that they do not rule a sign. Relationships run one way. A house lists its planets, but a planet does not carry its house. To find a planet's house, scan the chart's houses for it. Because you may know these ideas by other names: a **kundali**, a **horoscope**, or a **natal chart** is a Chart calculation. "Natal" is a meaning you give a calculation made for a birth moment, not a kind of chart. ## Schools [#schools] Ascendant implements more than one tradition, and a single judgment never mixes them. | School | Typical settings | Notes | | --- | --- | --- | | **KP** | KrishnamurtiVP291 ayanamsa, Placidus houses | The default. It uses D1's cusps, star lords, sub lords, and significators. | | **Parashari** | Lahiri ayanamsa, whole-sign houses | Used for promise, quality, and divisional charts. | Always state the school and settings alongside a result. ## Timing and scoring [#timing-and-scoring] | Term | Meaning | | --- | --- | | **Dasha** | A timeline of periods. Vimshottari gives planetary Mahadashas and Antardashas. Chara and Sthira give Jaimini sign periods. You can ask which period is active at any date. | | **Ashtakavarga** | A Parashari scoring system built from the sign positions of the Sun, Moon, Mars, Mercury, Jupiter, Venus, Saturn, and Lagna. | | **Bhinnashtakavarga (BAV)** | The twelve sign scores for one of those eight entities, counted in **bindus** (benefic points). | | **Sarvashtakavarga (SAV)** | The twelve sign scores formed by adding the seven planetary BAV tables. It totals 337 under the classical table. | | **Shodhya Pinda** | A weighted total for one planet, from its reduced BAV after Trikona and Ekadhipatya reduction. | | **Transit search** | A time-ordered search for the next or previous events of one graha. The events are sign ingresses, longitude hits, house-cusp crossings, and retrograde stations. | ## Jaimini [#jaimini] Jaimini is a school, not a single calculation. You request each result by name. | Term | Meaning | | --- | --- | | **Chara Karakas** | Seven planetary roles assigned in descending order of degree within a sign. Planets at the same degree share the roles they tie for. | | **Rashi Drishti** | The signs a reference sign influences under Jaimini's sign aspects. | | **Karakamsha** | The D9 sign occupied by each planet holding the Atmakaraka role. | | **Arudha Pada** | The sign reached by repeating the distance from a house's sign to its lord. | | **Upapada** | The Arudha Pada of the twelfth house. | | **Argala** | The supporting and obstructing relationships around a reference sign, or around Ketu in the reverse direction. | ## Provenance [#provenance] Every result carries its **provenance**: a `school`, a `method`, and a `version`. Each one points to a **method specification** in the library's registry, which lists the calculation steps and the criteria used to verify it. That is how a result can be checked and repeated. See [Provenance and verification](https://astro-ascendant.vercel.app/docs/guides/provenance). # Ashtakavarga (/docs/guides/ashtakavarga) **Ashtakavarga** is a classical Parashari scoring system. Each of eight contributors, the Sun, Moon, Mars, Mercury, Jupiter, Venus, Saturn, and the Lagna, gives a benefic point, called a **bindu**, to some of the twelve signs. The scores for a sign are then added up in several ways. Ascendant calculates the whole system from the **Placements** you already have. This guide runs the calculation, explains each table in the result, and shows how to read a score by sign and by house. ## What goes in and what comes out [#what-goes-in-and-what-comes-out] `SAV.calculate` takes only the placements. It reads the sign of each of the seven classical planets and of the Lagna. Rahu and Ketu take no part. It needs no moment or location, because everything it uses is already in the placements. | Result field | What it holds | | --- | --- | | `bhinna` | The Bhinnashtakavarga (BAV): twelve sign scores for each of the eight entities. | | `sarva` | The Sarvashtakavarga (SAV): twelve sign scores, the sum of the seven planetary BAVs. The Lagna is not included. | | `reduced` | The seven planetary BAVs after Trikona and then Ekadhipatya reduction. | | `shodhya_pinda` | For each planet, its Rashi Pinda, Graha Pinda, and their sum, the Shodhya Pinda. | | `totals` | The total of each BAV table, plus the SAV total. | | `provenance` | The method that made the result: Parashari `bphs-bav-trikona-ekadhipatya`. | ## Calculate [#calculate] ```ts import { Chart, Runtime, SAV } from "astro-ascendant"; import { DateTime, ManagedRuntime } from "effect"; const runtime = ManagedRuntime.make(Runtime.MainLayer); const located = Chart.LocatedMoment.make({ moment: Chart.Moment.make({ date: DateTime.makeUnsafe("2000-01-01T12:00:00Z") }), latitude: 12.9716, longitude: 77.5946, }); const { placements, charts } = await runtime.runPromise(Chart.generate(located)); const ashtakavarga = await runtime.runPromise(SAV.calculate(placements)); ``` ## Read the tables [#read-the-tables] Every table is keyed by sign name. A BAV table is keyed by entity first: ```ts ashtakavarga.bhinna.Sun.Leo; // 6 ashtakavarga.bhinna.Lagna.Cancer; // 6 ashtakavarga.sarva.Leo; // 34 ``` For the birth above, these are the scores by sign: | Sign | Sun BAV | Lagna BAV | SAV | | --- | --- | --- | --- | | Aries | 4 | 4 | 29 | | Taurus | 5 | 3 | 22 | | Gemini | 1 | 2 | 22 | | Cancer | 3 | 6 | 26 | | Leo | 6 | 3 | 34 | | Virgo | 5 | 5 | 33 | | Libra | 5 | 3 | 30 | | Scorpio | 4 | 4 | 30 | | Sagittarius | 5 | 5 | 28 | | Capricorn | 2 | 4 | 21 | | Aquarius | 4 | 5 | 32 | | Pisces | 4 | 5 | 30 | The other six planets have their own BAV tables under `bhinna`, keyed the same way. ## Checksums [#checksums] Each BAV table totals a fixed number, and the SAV totals 337. The `totals` field returns the sums so you can display or assert them: | Entity | Total | | --- | --- | | Sun | 48 | | Moon | 49 | | Mars | 39 | | Mercury | 54 | | Jupiter | 56 | | Venus | 52 | | Saturn | 39 | | Lagna | 49 | | SAV | 337 | Ascendant checks these against the classical values on every calculation. If a table does not match, the calculation fails with a typed `SAVCalculationError` and names the entity, the total it found, and the total it expected, so you never receive a table that breaks the rules. ## Reduced tables and Shodhya Pinda [#reduced-tables-and-shodhya-pinda] `reduced` applies two reductions to the seven planetary BAVs, Trikona first and then Ekadhipatya. The Ekadhipatya step looks at each pair of signs one planet rules and reduces it by whether the seven planets occupy them. Reduced scores are smaller, and many signs drop to zero: ```ts ashtakavarga.reduced.Sun; // Leo: 2, Virgo: 3, Libra: 4, Scorpio: 1, Sagittarius: 1, Aquarius: 3, // and 0 in every other sign ``` `shodhya_pinda` builds on the reduced tables. For each planet it holds a **Rashi Pinda** (weighted by signs), a **Graha Pinda** (weighted by the signs the seven planets occupy), and their sum, the **Shodhya Pinda**: | Planet | Rashi Pinda | Graha Pinda | Shodhya Pinda | | --- | --- | --- | --- | | Sun | 113 | 61 | 174 | | Moon | 100 | 43 | 143 | | Mars | 123 | 78 | 201 | | Mercury | 96 | 62 | 158 | | Jupiter | 55 | 8 | 63 | | Venus | 130 | 69 | 199 | | Saturn | 59 | 5 | 64 | Use these as they are. A Shodhya Pinda is a weighted total for one planet, not a probability and not a prediction. ## Score a house [#score-a-house] Ashtakavarga scores belong to signs. To score a house, find the sign of that house in D1 and look it up: ```ts const [d1] = charts; const tenth = d1.houses[10].sign; // "Pisces" ashtakavarga.sarva[tenth]; // 30 ashtakavarga.bhinna.Sun[tenth]; // 4 ``` To score all twelve houses at once: ```ts const houseScores = Object.entries(d1.houses).map(([house, { sign }]) => ({ house: Number(house), sign, sav: ashtakavarga.sarva[sign], })); ``` For the birth above, with Gemini rising, the SAV scores of houses 1 to 12 are 22, 26, 34, 33, 30, 30, 28, 21, 32, 30, 29, and 22. The highest is the third house, Leo, at 34. The sign scores do not depend on the house system. They were identical across all 13 house systems for this birth. The ayanamsa can change them, because it moves each planet's longitude and so can move it into a different sign. Which sign a house maps to does depend on the house system. With a cusp-based system such as Placidus, a house starts at a cusp and `houses[n].sign` is the sign of that cusp. For Parashari work, where each house is one whole sign, use the Lahiri and whole-sign settings from [Choose a school and settings](https://astro-ascendant.vercel.app/docs/guides/settings). ## Terms in this guide [#terms-in-this-guide] | Term | Meaning | | --- | --- | | **Bindu** | One benefic point assigned to a sign by one contributor. A sign's score is its number of bindus. | | **BAV** | The twelve sign scores for one entity. There are eight tables. | | **SAV** | The sum of the seven planetary BAV tables. It excludes the Lagna. | | **Reduced BAV** | The seven planetary BAVs after Trikona and Ekadhipatya reduction. | | **Shodhya Pinda** | A planet's weighted total: its Rashi Pinda plus its Graha Pinda. | # Dashas (/docs/guides/dashas) A **dasha** is a timeline of periods. Each period has a lord, a start, and an end, and each Mahadasha is divided into Antardashas. Ascendant calculates three systems from the same **Placements** you already have from `Chart.generate`: | System | Periods are ruled by | Mahadashas | Antardashas in each | School | | --- | --- | --- | --- | --- | | **Vimshottari** | Planets, in a fixed cycle | 9 | 9 | Used with both KP and Parashari | | **Chara** | Signs | 12 | 12 | Jaimini | | **Sthira** | Signs | 12 | 12 | Jaimini | This guide builds each one, reads it, and asks which period is running on a given date. ## Start from placements [#start-from-placements] Every dasha function takes the birth **Moment** and the **Placements**. Calculate the placements once and reuse them: ```ts import { Chart, Dasha, Runtime } from "astro-ascendant"; import { DateTime, ManagedRuntime } from "effect"; const runtime = ManagedRuntime.make(Runtime.MainLayer); const located = Chart.LocatedMoment.make({ moment: Chart.Moment.make({ date: DateTime.makeUnsafe("2000-01-01T12:00:00Z") }), latitude: 12.9716, longitude: 77.5946, }); const { placements } = await runtime.runPromise(Chart.generate(located)); ``` The dasha is anchored to the birth moment, so pass the same `located.moment` you used for the chart. ## Vimshottari [#vimshottari] `Dasha.calculate` builds the Vimshottari timeline. It starts from the Moon's star and the portion of that star's period still left at birth. ```ts const timeline = await runtime.runPromise( Dasha.calculate(located.moment, placements), ); ``` The result is an array of nine Mahadashas covering the full 120-year cycle. Each has a `mahadasha` lord, a `start`, an `end`, and nine `antardashas`: ```ts const day = (date: DateTime.Utc) => DateTime.formatIso(date).slice(0, 10); for (const period of timeline) { console.log(`${period.mahadasha}: ${day(period.start)} to ${day(period.end)}`); } ``` For the birth above this prints: | Mahadasha | Start | End | | --- | --- | --- | | Rahu | `1982-08-12` | `2000-08-12` | | Jupiter | `2000-08-12` | `2016-08-12` | | Saturn | `2016-08-12` | `2035-08-12` | | Mercury | `2035-08-12` | `2052-08-12` | | Ketu | `2052-08-12` | `2059-08-12` | | Venus | `2059-08-12` | `2079-08-12` | | Sun | `2079-08-12` | `2085-08-12` | | Moon | `2085-08-12` | `2095-08-12` | | Mars | `2095-08-12` | `2102-08-12` | The first Mahadasha begins before the birth date, here in 1982, because the person is born part-way through the period of the Moon's star lord. The timeline shows the whole Rahu period, and the birth falls inside it. Use the query below to find what is running on a given date. Each Mahadasha holds its own Antardashas. The first three of the Rahu Mahadasha are Rahu to `1985-04-24`, Jupiter to `1987-09-18`, and Saturn to `1990-07-24`. ```ts const first = timeline[0]; for (const sub of first.antardashas) { console.log(`${sub.mahadasha}-${sub.antardasha}: ${day(sub.start)} to ${day(sub.end)}`); } ``` ## Find the running period [#find-the-running-period] `Dasha.at` returns the Mahadasha and Antardasha active at a moment. It uses the current time when you pass none: ```ts const when = DateTime.makeUnsafe("2026-11-01T00:00:00Z"); const current = await runtime.runPromise(Dasha.at(timeline, when)); if (current) { console.log(current.mahadasha.mahadasha); // "Saturn" console.log(current.antardasha.antardasha); // "Sun" console.log(day(current.antardasha.start), day(current.antardasha.end)); // 2026-08-02 2027-07-15 } ``` It returns `null` when the date falls outside the timeline, for example in 1900 or 2500, so always check before reading the result. ## Chara and Sthira [#chara-and-sthira] The Jaimini systems are sign-based. Call them with the same arguments: ```ts const chara = await runtime.runPromise( Dasha.calculateChara(located.moment, placements), ); const sthira = await runtime.runPromise( Dasha.calculateSthira(located.moment, placements), ); ``` Both return an object with a `system` tag, a `provenance`, and twelve `mahadashas`, each with twelve `antardashas`. Their lords are signs, not planets. For the birth above, both begin at the birth moment: | System | First four Mahadashas | | --- | --- | | Chara | Gemini `2000-01-01` to `2006-01-01`, Taurus to `2012-01-01`, Aries to `2022-01-01`, Pisces to `2033-01-01` | | Sthira | Libra `2000-01-01` to `2007-01-01`, Scorpio to `2015-01-01`, Sagittarius to `2024-01-01`, Capricorn to `2031-01-01` | Because the result is tagged by `system`, you can tell the two apart in your own types: ```ts const describe = (dasha: Dasha.RashiDasha) => dasha.system === "Sthira" ? `Brahma: ${dasha.brahma.planet} in ${dasha.brahma.sign}` : "Chara"; ``` Sthira also returns the data behind its **Brahma** selection: the winning planet and sign, the Atmakaraka used as the reference, the sign strengths for the two candidate signs, and a score for each eligible planet. Here the Brahma is the Moon in Libra. Show or log `sthira.brahma.selection` if you need to explain the choice. Query them with `Dasha.atRashi`, which works for both: ```ts const running = await runtime.runPromise(Dasha.atRashi(chara, when)); if (running) { console.log(running.system); // "Chara" console.log(running.mahadasha.mahadasha, running.antardasha.antardasha); // Pisces Virgo } ``` For the same date, the Chara period is Pisces-Virgo, from `2026-08-01` to `2027-07-01`, and the Sthira period is Capricorn-Taurus. `Dasha.at` takes a Vimshottari timeline and `Dasha.atRashi` takes a Chara or Sthira one. Each result carries the types of its own system, so TypeScript stops you from mixing them. ## How dates are counted [#how-dates-are-counted] The systems measure time differently, and you can see it in the boundaries: - **Vimshottari** periods are decimal years converted into calendar months, days, hours, and minutes. Their boundaries fall at any time of day. In the example, Saturn begins at `12:48 UTC` on `2016-08-12`. - **Chara and Sthira** periods are whole calendar months. Their boundaries fall on the first of a month, such as `2026-08-01`. Treat the exact instants as part of the result. Do not round them to a day if you compare periods across systems. ## Which method was used [#which-method-was-used] Every dasha records its method in `provenance`. Chara reports the Jaimini method `kn-rao-co-lord-strength` and Sthira reports `bv-raman-koch-brahma-strength`. Vimshottari returns a plain array, so its provenance sits on each Mahadasha, as the Parashari method `moon-nakshatra-balance`. The version of each is a date, currently `2026-11-01`. Store these with the timeline if you will show it later. See [Provenance and verification](https://astro-ascendant.vercel.app/docs/guides/provenance). ## When it fails [#when-it-fails] Dasha functions fail with typed errors. `DashaCalculationError` covers a calculation that cannot complete, and `DashaEvidenceError` means the placements did not contain exactly one entry for a required planet or the Lagna. It points to placements that were built or edited by hand instead of coming from `Chart.generate`. See [Errors and typed failures](https://astro-ascendant.vercel.app/docs/guides/errors). # Divisional charts (/docs/guides/divisional-charts) A **divisional chart** reworks the same planetary positions through a division of each sign. The D9, for example, splits every sign into nine parts. Ascendant derives all of them from the shared **Placements**, so asking for more divisions costs very little extra and never changes the D1. This guide shows how to request divisions, read the result, tell divisional charts apart from D1 in your types, and map one longitude on its own. ## Supported divisions [#supported-divisions] Ascendant supports D1 plus fifteen divisional charts: | Division | Commonly known as | Division | Commonly known as | | --- | --- | --- | --- | | D2 | Hora | D20 | Vimsamsa | | D3 | Drekkana | D24 | Chaturvimsamsa | | D4 | Chaturthamsa | D27 | Bhamsa | | D7 | Saptamsa | D30 | Trimsamsa | | D9 | Navamsa | D40 | Khavedamsa | | D10 | Dasamsa | D45 | Akshavedamsa | | D12 | Dwadasamsa | D60 | Shashtiamsa | | D16 | Shodasamsa | | | The type `Chart.Division` lists exactly these numbers, so TypeScript rejects any other value at compile time. ## Request divisions [#request-divisions] Pass the divisions you want as the second argument to `Chart.generate`: ```ts import { Chart, Runtime } from "astro-ascendant"; import { DateTime, ManagedRuntime } from "effect"; const runtime = ManagedRuntime.make(Runtime.MainLayer); const located = Chart.LocatedMoment.make({ moment: Chart.Moment.make({ date: DateTime.makeUnsafe("2000-01-01T12:00:00Z") }), latitude: 12.9716, longitude: 77.5946, }); const calculation = await runtime.runPromise(Chart.generate(located, [9, 10])); ``` The result's `charts` always starts with D1, followed by the divisions you asked for. They come back in numeric order with duplicates removed, so `[10, 9, 9]` gives D1, D9, and D10. To get every divisional chart at once, list them all: ```ts const everything = [2, 3, 4, 7, 9, 10, 12, 16, 20, 24, 27, 30, 40, 45, 60] as const; const calculation = await runtime.runPromise(Chart.generate(located, everything)); ``` ## D1 versus a divisional chart [#d1-versus-a-divisional-chart] `calculation.charts` is typed as a D1 chart followed by any number of divisional charts, so you can split it directly: ```ts const [d1, ...divisional] = calculation.charts; d1.angles; // chart angles, only on D1 d1.rulingPlanets; // only on D1 d1.houses[1].subLord; // KP lords, only on D1 ``` When you have a `Chart.Chart`, a value that could be either, check its `division` to narrow it: ```ts const describe = (chart: Chart.Chart) => chart.division === 1 ? `D1 with ${chart.rulingPlanets.length} ruling planets` : `D${chart.division}`; ``` The two kinds differ like this: | | D1 chart | Divisional chart | | --- | --- | --- | | Houses start at | The house system's cusps | The start of each sign, counted from the mapped Lagna | | Uses the house system | Yes | No | | Angles, KP lords, significations, ruling planets | Yes | No | | Houses per chart | 12 | 12 | Divisional charts have no KP data because those belong to source longitudes, and a mapped longitude is not a source longitude. Their houses are whole signs whatever house system you chose. ## Read a divisional chart [#read-a-divisional-chart] Each chart has twelve `houses`, keyed 1 to 12. A divisional house has these fields: | Field | Meaning | | --- | --- | | `sign` | The sign of the house. | | `cusp` | Where the house begins, which is the start of its sign (for example `240` for Sagittarius). | | `signLord` | The lord of that sign. | | `significations` | The house's classical significations. | | `planets` | The planets in the house, with their mapped degree and sign. | | `lagna` | The mapped Lagna, in the house that holds it. Otherwise `null`. | A chart also records its `division` and its `provenance`, which for every divisional chart is the Parashari `whole-sign-from-mapped-lagna` method. To find where a planet sits, scan the houses. The data is one-directional on purpose: a house lists its planets, but a planet does not carry its house. ```ts const signOf = (chart: Chart.Chart, planet: Chart.Planets) => Object.values(chart.houses).find((house) => house.planets.some((placed) => placed.name === planet), )?.sign; const d9 = calculation.charts.find((chart) => chart.division === 9); const sunInD9 = d9 && signOf(d9, "Sun"); // "Leo" for the birth above ``` ### Example: one birth through every division [#example-one-birth-through-every-division] For the birth above, the Lagna and Sun land in these signs: | Division | Lagna | Sun | | --- | --- | --- | | D1 | Gemini | Sagittarius | | D2 | Leo | Cancer | | D3 | Gemini | Aries | | D4 | Virgo | Gemini | | D7 | Leo | Pisces | | D9 | Sagittarius | Leo | | D10 | Virgo | Taurus | | D12 | Virgo | Gemini | | D16 | Taurus | Leo | | D20 | Aquarius | Cancer | | D24 | Pisces | Virgo | | D27 | Gemini | Gemini | | D30 | Aquarius | Sagittarius | | D40 | Aries | Aquarius | | D45 | Aquarius | Sagittarius | | D60 | Capricorn | Virgo | The D1 uses the default KP settings, Placidus houses, but the sign of each planet is the same under any house system. The house system changes only D1's cusps. ## Map one longitude [#map-one-longitude] If you need only the divisional position of a single longitude, skip the chart and use `getDivisionalTarget`: ```ts import { getDivisionalTarget } from "astro-ascendant/chart"; import { Effect } from "effect"; const target = await runtime.runPromise(getDivisionalTarget(256.5924, 9)); // { // signIndex: 4, // 0 = Aries, so 4 = Leo // degree: 29.33, // degree within the target sign // longitude: 149.33, // the mapped longitude // } ``` For the Sun at 256.59° in D1, the D9 target is Leo at 29.33°, which matches the Sun's D9 sign in the table above. For division `1`, the function returns the source position unchanged. The same function is also exported from `astro-ascendant/chart/divisional-mapping`. ## Where divisions are used [#where-divisions-are-used] - **Karakamsha** reads the D9 sign of each Atmakaraka planet. See [Jaimini calculations](https://astro-ascendant.vercel.app/docs/guides/jaimini). - Parashari readings use divisional charts for the quality of a result, with the Lahiri and whole-sign settings from [Choose a school and settings](https://astro-ascendant.vercel.app/docs/guides/settings). ## When it fails [#when-it-fails] - An unsupported division number is a compile-time error. If a value gets past the types, for example through a cast, `Chart.generate` ends in a defect, not a typed failure, so validate numbers from untrusted input before you pass them in. - `Chart.generate` fails with a `ChartCalculationError` at the `"houses"` stage if the chosen house system's D1 cusps do not divide the circle into twelve houses. - `getDivisionalTarget` fails with a `DivisionalMappingError` when the longitude cannot be used. See [Errors and typed failures](https://astro-ascendant.vercel.app/docs/guides/errors) for how to handle them. # Ephemeris adapters and caching (/docs/guides/ephemeris-and-caching) Every chart, dasha, and transit in Ascendant starts from planetary positions, and those come from one place: the **ephemeris**. This guide explains the ephemeris service, the layers that provide it, how the position cache works and how to tune it, and how to write your own adapter for testing or for another position source. ## The ephemeris service [#the-ephemeris-service] `Ephemeris.Ephemeris` is an Effect service with three operations. Charts consume it, and adapters implement it. | Operation | What it does | | --- | --- | | `dateToJulianDay(date)` | Converts a UTC `DateTime` to a Julian Day. | | `calculatePosition(julianDay, planet, ayanamsa)` | Returns the sidereal position of one graha. It takes any of the nine, Rahu and Ketu included. | | `calculateHouses(julianDay, latitude, longitude, houseSystem, ayanamsa)` | Returns twelve sidereal house cusps keyed 1 to 12, and the chart angles. | All three fail with a typed `EphemerisError`. The interface speaks in domain terms: an adapter hides its native bodies, its node conventions, its cusp layout, and its tropical offset, and hands back sidereal longitudes. ## The layers you can provide [#the-layers-you-can-provide] | Layer | What it gives you | | --- | --- | | `Swisseph.SwissephLayer` | The Swiss Ephemeris adapter. Every position lookup calls the native library. | | `Swisseph.SwissephCachedLayer` | The same adapter with a position cache in front. Use this for transits and anything repeated. | | `Runtime.MainLayer` | The default AstroParams plus the cached Swiss Ephemeris layer. What the Quickstart uses. | | `Ephemeris.withPositionCache(options)` | A decorator that adds the cache to any ephemeris layer, yours included. | If you do nothing else, use `Runtime.MainLayer`, which already gives you the cached layer. ## What the cache does [#what-the-cache-does] The cache wraps `calculatePosition` only. It remembers each result by Julian Day, planet, and ayanamsa. That matters because the same positions are requested over and over: - A single chart makes nine position calls, one for each graha, plus one houses call. - A transit search looks up the same graha again and again as it scans ahead and refines each crossing to the minute. - A dashboard that redraws the same moment asks for the same positions again. Its behavior is simple: - **Hits skip the native call.** A second chart for the same moment makes no new position calls. - **Concurrent identical lookups share one call.** If two requests ask for the same position at once, only one runs. - **Failures are never cached.** A transient native error stays retryable. - **Only positions are cached.** Houses and Julian Day conversion run once per chart and gain nothing from caching. - **Defaults are 10,000 entries and a 10-minute lifetime.** For measured speedups on a warm cache, see the [stress benchmark notes](https://github.com/astro-ascendant/ascendant-ts/blob/main/benchmarks/stress.md). The cache lives inside the layer. A `ManagedRuntime` builds its layers once and keeps them, so one long-lived runtime shares one cache across all your requests. Each additional runtime builds its own layer and gets its own, empty cache. That is why the Quickstart tells you to create the runtime once and reuse it. ## Tune the cache [#tune-the-cache] Wrap the plain layer yourself to change the capacity or the lifetime: ```ts import { AstroParams, Chart, Ephemeris, Swisseph } from "astro-ascendant"; import { Layer, ManagedRuntime } from "effect"; const tuned = Ephemeris.withPositionCache({ capacity: 50_000, timeToLive: "1 hour", }).pipe(Layer.provide(Swisseph.SwissephLayer)); const runtime = ManagedRuntime.make( Layer.mergeAll(AstroParams.DefaultAstroParams, tuned), ); ``` Raise the capacity for services that scan many moments, such as wide transit windows. Shorten the lifetime if memory matters more than speed. Because positions for a given moment never change, the lifetime only bounds memory use. To keep the default settings but change only the ephemeris, reuse the runtime's settings layer: ```ts import { Runtime } from "astro-ascendant"; const layer = Layer.mergeAll(Runtime.AstroParamsSubgraph, tuned); ``` `Runtime.AstroParamsSubgraph` is the AstroParams half of `MainLayer`, and `Runtime.EphemerisSubgraph` is the ephemeris half. ## The Swiss Ephemeris adapter [#the-swiss-ephemeris-adapter] The built-in adapter wraps the native `@swisseph/node` package. It does the conversion work so the rest of the library does not have to: - It decodes every native result into validated schemas, so corrupt output fails with a typed `EphemerisError` and does not spread. - It reads Rahu from the mean lunar node and places Ketu opposite it. - It turns the native thirteen-slot cusp array into twelve sidereal cusps keyed by house. - It applies the `WholeSign` convention itself. ## Write your own adapter [#write-your-own-adapter] You can provide your own `Ephemeris` for unit tests, for another source of positions, or for an environment where the native library is not available. An adapter is a `Layer` that implements the three operations. This one reports fixed longitudes, which makes charts exact and repeatable in tests: ```ts import { AstroParams, Chart, Ephemeris } from "astro-ascendant"; import { DateTime, Effect, Layer, ManagedRuntime } from "effect"; const LONGITUDES: Record = { Sun: 256.6, Moon: 199.5, Mars: 304.2, Mercury: 244.1, Jupiter: 25.7, Venus: 285.3, Saturn: 41.9, Rahu: 69.3, Ketu: 249.3, }; const fixedEphemeris = Layer.succeed( Ephemeris.Ephemeris, Ephemeris.Ephemeris.of({ dateToJulianDay: (date) => Ephemeris.JulianDay.makeEffect(Ephemeris.julianDayOfDateTime(date)).pipe( Effect.mapError((cause) => Ephemeris.EphemerisError.make({ operation: "dateToJulianDay", cause }), ), ), calculatePosition: (_julianDay, planet) => Effect.succeed({ longitude: Chart.Longitude.make(LONGITUDES[planet]), latitude: 0, distance: 1, longitudeSpeed: 1, latitudeSpeed: 0, distanceSpeed: 0, }), calculateHouses: (_julianDay, _latitude, _longitude, houseSystem) => Effect.succeed({ houseSystem, cusps: Object.fromEntries( Chart.Houses.literals.map((house) => [ house, Chart.Longitude.make(((house - 1) * 30 + 69.6) % 360), ]), ) as Record, angles: { ascendant: Chart.CircleAngle.make(69.6), mc: Chart.CircleAngle.make(339.6), armc: Chart.CircleAngle.make(339.6), vertex: Chart.CircleAngle.make(159.6), equatorialAscendant: Chart.CircleAngle.make(0), coAscendant1: Chart.CircleAngle.make(0), coAscendant2: Chart.CircleAngle.make(0), polarAscendant: Chart.CircleAngle.make(0), }, }), }), ); const runtime = ManagedRuntime.make( Layer.mergeAll(AstroParams.DefaultAstroParams, fixedEphemeris), ); const located = Chart.LocatedMoment.make({ moment: Chart.Moment.make({ date: DateTime.makeUnsafe("2000-01-01T12:00:00Z") }), latitude: 12.9716, longitude: 77.5946, }); const calculation = await runtime.runPromise(Chart.generate(located)); calculation.charts[0].houses[1].sign; // "Gemini" ``` Nothing else in the library changes. `Chart.generate`, the dashas, and the other calculations use whichever `Ephemeris` you provide. ### What an adapter must do [#what-an-adapter-must-do] - **Return sidereal longitudes.** Apply the `ayanamsa` argument yourself. The rest of the library treats every longitude as sidereal. - **Handle all nine grahas.** Rahu and Ketu are positions like any other, so your adapter decides how to place them. - **Return twelve cusps keyed 1 to 12 and the full set of chart angles.** Honor the `houseSystem` argument, including `WholeSign`. - **Report real speeds for transit searches.** Stations are found where `longitudeSpeed` changes sign, so a constant speed gives no stations. The fixed adapter above is fine for charts, but not for station searches. - **Fail with `EphemerisError`.** Set `operation` to the name of the call that failed and put the underlying reason in `cause`. See [Errors and typed failures](https://astro-ascendant.vercel.app/docs/guides/errors). ### Add the cache to your adapter [#add-the-cache-to-your-adapter] Wrap your adapter in the same decorator to get the cache for free: ```ts const cachedFixed = Ephemeris.withPositionCache({ capacity: 1_000, timeToLive: "1 hour" }).pipe( Layer.provide(fixedEphemeris), ); ``` With this layer, the first chart for a moment makes nine position calls to your adapter, and a second chart for the same moment makes none. ## Which layer to use [#which-layer-to-use] | You are | Provide | | --- | --- | | Following the Quickstart, or building a typical app | `Runtime.MainLayer` | | Running transit searches or serving repeated moments | `Swisseph.SwissephCachedLayer`, in one long-lived runtime | | Debugging the native adapter, or wanting no state | `Swisseph.SwissephLayer` | | Needing a different capacity or lifetime | `Ephemeris.withPositionCache(...)` over `Swisseph.SwissephLayer` | | Writing tests, or using another source of positions | Your own `Ephemeris` layer, optionally wrapped in `withPositionCache` | # Errors and typed failures (/docs/guides/errors) Ascendant functions return an `Effect`, and an Effect has three outcomes: it succeeds, it fails with a **typed error**, or it dies with a **defect**. This guide covers the first two, which are the failures your program should expect, and how to tell them from the third. The idea is simple. Anything that can go wrong because of the input or the data, such as an invalid latitude, a search window that ends too early, or placements missing a planet, is a typed error. TypeScript knows the exact list for each function, so you handle failures by name and the compiler tells you what you missed. ## What each function can fail with [#what-each-function-can-fail-with] TypeScript infers these from the function's return type. The table lists them so you can plan ahead. | Function | Typed errors | | --- | --- | | `Chart.generate` | `LocatedMomentValidationError`, `ChartCalculationError`, `LongitudeError` | | `getDivisionalTarget` | `DivisionalMappingError` | | `Dasha.calculate`, `Dasha.calculateChara` | `DashaEvidenceError` | | `Dasha.calculateSthira` | `DashaEvidenceError`, `DashaCalculationError` | | `Dasha.at`, `Dasha.atRashi` | None. They return `null` outside the timeline. | | `SAV.calculate` | `SAVCalculationError` | | `CharaKarakas.calculate` | `CharaKarakasEvidenceError`, `CharaKarakasParseError` | | `Karakamsha.calculate` | `KarakamshaEvidenceError`, `KarakamshaCalculationError` | | `ArudhaPada.calculate` | `ArudhaPadaEvidenceError` | | `Upapada.calculate` | `UpapadaEvidenceError` | | `Argala.calculate` | `ArgalaEvidenceError`, `ArgalaCalculationError` | | `RashiDrishti.calculate` | None | | `Transit.findTransits` | `TransitValidationError`, `TransitSearchExhausted`, `ChartCalculationError`, `LocatedMomentValidationError`, `LongitudeError`, `EphemerisError` | The names above are the error **tags**, the strings you pass to `catchTag`. They are also the names of the exported classes. Older code may use the unprefixed Jaimini names `EvidenceError` and `CalculationError`, which remain as deprecated aliases. ## What an error looks like [#what-an-error-looks-like] Every error is an `Error` with a `_tag` and a `message`, plus fields specific to its kind. Most also carry a `cause` with the underlying detail. | Error | Extra fields | Meaning | | --- | --- | --- | | `LocatedMomentValidationError` | `cause` | The moment or coordinates are invalid, for example a latitude outside -90 to 90. | | `ChartCalculationError` | `stage`, `cause` | A chart could not be built. `stage` is `"validation"`, `"placements"`, `"houses"`, or `"mapping"`. | | `LongitudeError` | `cause` | An angle could not be converted to a valid longitude. | | `EphemerisError` | `operation`, `cause` | The ephemeris failed. `operation` names the call that failed. | | `DashaEvidenceError` and the Jaimini `*EvidenceError`s | `placement`, `expected`, `actual` | The placements did not hold exactly one entry for a planet the calculation needs. | | `SAVCalculationError` | `cause` | An Ashtakavarga table broke a classical checksum. `cause` names the entity and the totals. | | `TransitValidationError` | `cause` | The request was incomplete or invalid, or the search window left the supported Julian Day range. | | `TransitSearchExhausted` | `found`, `searchedUntil` | The search window ended before `count` events were found. `found` holds the events it did find. | A `ChartCalculationError` at the `"houses"` stage means the chosen house system's D1 cusps did not divide the circle into twelve houses. See [Choose a school and settings](https://astro-ascendant.vercel.app/docs/guides/settings). ## Handle an error by tag [#handle-an-error-by-tag] Use `Effect.catchTag` to recover from one error: ```ts import { Chart, Runtime } from "astro-ascendant"; import { DateTime, Effect, ManagedRuntime } from "effect"; const runtime = ManagedRuntime.make(Runtime.MainLayer); const input = { moment: Chart.Moment.make({ date: DateTime.makeUnsafe("2000-01-01T12:00:00Z") }), latitude: 120, // out of range longitude: 77, } satisfies Chart.LocatedMoment; const message = await runtime.runPromise( Chart.generate(input).pipe( Effect.catchTag("LocatedMomentValidationError", (error) => Effect.succeed(`Invalid input: ${error.message}`), ), ), ); // "Invalid input: Moment and geographic coordinates must be valid" ``` The message is short and fixed. The detail is in `error.cause`, which for this error says `Expected a value between -90 and 90` at `latitude`. Show the message to users and log the cause. To handle several errors in one place, use `Effect.catchTags`. This maps each one to an HTTP-style response: ```ts const response = await runtime.runPromise( Chart.generate(input).pipe( Effect.map((calculation) => ({ status: 200, body: calculation })), Effect.catchTags({ LocatedMomentValidationError: (error) => Effect.succeed({ status: 400, body: error.message }), ChartCalculationError: (error) => Effect.succeed({ status: 500, body: `${error.stage}: ${error.message}` }), LongitudeError: (error) => Effect.succeed({ status: 500, body: error.message }), }), ), ); ``` Because `catchTags` lists every tag in the function's error type, the result no longer has an error channel, and TypeScript will flag the call if a future version adds a new error. ## Handle errors at the promise boundary [#handle-errors-at-the-promise-boundary] `runtime.runPromise` rejects with the error itself. The rejection is a real `Error` instance with its `_tag`, so you can branch on the tag in an ordinary `catch`: ```ts try { await runtime.runPromise(Chart.generate(input)); } catch (error) { if (error instanceof Error && "_tag" in error) { console.error(error._tag, error.message); } else { throw error; } } ``` To inspect the outcome without throwing, use `runPromiseExit` and the `Cause` helpers: ```ts import { Cause, Exit } from "effect"; const exit = await runtime.runPromiseExit(Chart.generate(input)); if (Exit.isFailure(exit)) { const failure = Cause.findErrorOption(exit.cause); // failure is Some(error), with error._tag === "LocatedMomentValidationError" } ``` Prefer `catchTag` and `catchTags` inside your Effect programs, and keep `runPromiseExit` for the edge of your application. ## Partial results [#partial-results] `TransitSearchExhausted` carries the events it did find, so you can keep a partial answer instead of discarding it: ```ts import { Transit } from "astro-ascendant"; const events = await runtime.runPromise( Transit.findTransits(request).pipe( Effect.catchTag("TransitSearchExhausted", (error) => Effect.succeed(error.found)), ), ); ``` See [Transit searches](https://astro-ascendant.vercel.app/docs/guides/transits). ## Defects are not for catching [#defects-are-not-for-catching] A defect is a bug in the calling code, not an expected failure. An Effect that dies does not appear in the typed error channel. A division number outside the supported list is one example. TypeScript rejects it at compile time, but if a cast gets a value like `5` through, `Chart.generate` dies: ```ts const exit = await runtime.runPromiseExit( Chart.generate({ ...input, latitude: 1 }, [5] as never), ); Exit.isFailure(exit) && Cause.hasDies(exit.cause); // true ``` Do not catch defects to hide them. Validate numbers that come from outside your program, such as a division from a query string, before you pass them in, and fix the code that produced a value the types rule out. ## A checklist [#a-checklist] - Handle `LocatedMomentValidationError` where you accept user input. - Treat `ChartCalculationError` and `EphemerisError` as server-side failures, and log their `cause`. - Expect `null` from `Dasha.at` and `Dasha.atRashi`, not an error. - Use `TransitSearchExhausted.found` when a partial list is acceptable. - Do not catch defects. Fix the code instead. # Jaimini calculations (/docs/guides/jaimini) **Jaimini** is a school of Jyotisha, not a single calculation. Ascendant exposes each Jaimini result as its own function, and you ask for exactly the one you want. There is no "run Jaimini" call. | Function | Result | | --- | --- | | `CharaKarakas.calculate` | Seven planetary roles, ranked by degree within a sign. | | `RashiDrishti.calculate` | The signs a reference sign aspects. | | `Karakamsha.calculate` | The D9 sign of the planet holding the Atmakaraka role. | | `ArudhaPada.calculate` | The Arudha Pada of one house. | | `Upapada.calculate` | The Arudha Pada of the twelfth house. | | `Argala.calculate` | Supporting and obstructing relationships around a reference. | Apart from `RashiDrishti`, which takes a sign, every function takes the **Placements** from `Chart.generate`. None of them needs a moment or location. This guide runs each one for the same birth and shows how to read the result. ## Set up [#set-up] ```ts import { Argala, ArudhaPada, CharaKarakas, Chart, Karakamsha, RashiDrishti, Runtime, Upapada, } from "astro-ascendant"; import { DateTime, ManagedRuntime } from "effect"; const runtime = ManagedRuntime.make(Runtime.MainLayer); const located = Chart.LocatedMoment.make({ moment: Chart.Moment.make({ date: DateTime.makeUnsafe("2000-01-01T12:00:00Z") }), latitude: 12.9716, longitude: 77.5946, }); const { placements, charts } = await runtime.runPromise(Chart.generate(located, [9])); const lagnaSign = charts[0].houses[1].sign; // "Gemini" ``` The Jaimini functions read sign positions and degrees from the placements, so the ayanamsa you choose can change their results. See [Choose a school and settings](https://astro-ascendant.vercel.app/docs/guides/settings). The examples below use the library defaults, and the results are the same for any house system. ## Chara Karakas [#chara-karakas] The Chara Karakas are seven roles given to the seven classical planets in descending order of degree within their signs. The planet with the highest degree is the **Atmakaraka**, and the lowest is the **Darakaraka**. Rahu and Ketu take no role. ```ts const karakas = await runtime.runPromise(CharaKarakas.calculate(placements)); for (const role of CharaKarakas.Roles.literals) { const holders = karakas.assignments[role]; console.log(role, holders.map(({ planet, degree }) => `${planet} ${degree.toFixed(2)}°`)); } ``` For the birth above: | Role | Planet | Degree in sign | | --- | --- | --- | | Atmakaraka | Moon | 19.55° | | Amatyakaraka | Saturn | 16.62° | | Bhratrikaraka | Sun | 16.59° | | Matrikaraka | Mercury | 8.11° | | Putrakaraka | Venus | 7.79° | | Gnatikaraka | Mars | 4.19° | | Darakaraka | Jupiter | 1.48° | Each role maps to a non-empty list of holders. Ties are exact: planets at the same degree share every role their tied ranks cover, so a role can have more than one holder. Always treat `assignments[role]` as a list. ## Rashi Drishti [#rashi-drishti] Rashi Drishti gives the signs that one reference sign influences under Jaimini's sign aspects. It takes a sign, not placements: ```ts const drishti = await runtime.runPromise(RashiDrishti.calculate(lagnaSign)); console.log(drishti.targets); // ["Virgo", "Sagittarius", "Pisces"] ``` Gemini is a dual sign, and its three targets are the other dual signs. The result's provenance method is `movable-fixed-dual`. ## Karakamsha [#karakamsha] The Karakamsha is the D9 sign occupied by the planet holding the Atmakaraka role: ```ts const karakamsha = await runtime.runPromise(Karakamsha.calculate(placements)); console.log(karakamsha.placements); // [{ planet: "Moon", sign: "Pisces" }] ``` The result is a list. If planets tie for the Atmakaraka role, each of them contributes a Karakamsha sign. The Chara Karakas that led to it are not returned, so call `CharaKarakas.calculate` as well if you need them. ## Arudha Pada [#arudha-pada] An Arudha Pada projects a house onto a sign. Count from the house's sign to its lord, then count the same distance again from the lord. Ask for the house you want by number, 1 to 12: ```ts const a1 = await runtime.runPromise(ArudhaPada.calculate(placements, 1)); const a7 = await runtime.runPromise(ArudhaPada.calculate(placements, 7)); ``` Each result carries the working, so you can show it: | House | Source sign | Lord | Lord's sign | Arudha sign | | --- | --- | --- | --- | --- | | 1 | Gemini | Mercury | Sagittarius | Gemini | | 7 | Sagittarius | Jupiter | Aries | Sagittarius | The fields are `house`, `sourceSign`, `lord`, `lordSign`, and `sign`. Ascendant calculates the plain projection, and its provenance method is `plain-projection`. It does not apply the classical adjustments for a projection that lands on the source sign or on the seventh sign from it. In the example, both Arudhas land on their own source signs, which is exactly where some traditions apply an adjustment. Apply that rule yourself if your tradition requires it, and say so alongside the result. ## Upapada [#upapada] The Upapada is the Arudha Pada of the twelfth house, with its own function: ```ts const upapada = await runtime.runPromise(Upapada.calculate(placements)); // { house: 12, sourceSign: "Taurus", lord: "Venus", lordSign: "Scorpio", sign: "Taurus" } ``` Its provenance method is `twelfth-house-plain-projection`, so the same plain-projection note applies. ## Argala [#argala] Argala describes the planets that support or obstruct a reference from the signs around it. The reference is either a sign or Ketu: ```ts const argala = await runtime.runPromise( Argala.calculate(placements, { kind: "Sign", sign: lagnaSign }), ); ``` A sign reference counts in the ordinary direction. A Ketu reference starts at the sign Ketu occupies and counts in the reverse direction: ```ts const fromKetu = await runtime.runPromise(Argala.calculate(placements, { kind: "Ketu" })); // fromKetu.referenceSign === "Capricorn", fromKetu.direction === "reverse" ``` The result separates the relations by position. For Gemini: | Kind | Position from the reference | Sign | Planets | | --- | --- | --- | --- | | Supporting | 2 | Cancer | Rahu | | Supporting | 4 | Virgo | none | | Supporting | 11 | Aries | Jupiter, Saturn | | Obstructing | 12 | Taurus | none | | Obstructing | 10 | Pisces | none | | Obstructing | 3 | Leo | none | | Secondary supporting | 5 | Libra | Moon | | Secondary obstructing | 9 | Aquarius | Mars | Read the fields as `supporting` and `obstructing`, each with three relations, plus `secondarySupporting` and `secondaryObstructing`, each with one. The result also records `direction` (`forward` or `reverse`) and the `referenceSign`. ## Provenance [#provenance] Every Jaimini result carries a `provenance` with `school: "Jaimini"`, a method, and a version. Store it with the result, because it names the exact rule used: | Result | Method | | --- | --- | | Chara Karakas | `exact-degree-shared-roles` | | Rashi Drishti | `movable-fixed-dual` | | Karakamsha | `atmakaraka-d9-sign` | | Arudha Pada | `plain-projection` | | Upapada | `twelfth-house-plain-projection` | | Argala | `structural-positions` | The version is the date the method last changed, currently `2026-11-01` for all six. See [Provenance and verification](https://astro-ascendant.vercel.app/docs/guides/provenance). ## When it fails [#when-it-fails] Each function fails with its own typed errors. An evidence error means the placements did not contain exactly one entry for a planet the calculation needs, which points to placements built or edited by hand. Karakamsha and Argala can also fail with a calculation error. See [Errors and typed failures](https://astro-ascendant.vercel.app/docs/guides/errors). # From local time to a Located Moment (/docs/guides/located-moments) Every calculation in Ascendant starts from a **Located Moment**: one instant in UTC, plus the latitude and longitude where positions are calculated. People, though, give you a local date, a clock time, and a place. This guide covers the step in between: converting local time to UTC safely, handling times that are ambiguous or do not exist, and passing valid coordinates. ## What the library expects [#what-the-library-expects] A Located Moment has three fields: | Field | Type | Rule | | --- | --- | --- | | `moment` | `Chart.Moment` | Wraps one UTC `DateTime`. | | `latitude` | `number` | Decimal degrees, from -90 to 90. North is positive. | | `longitude` | `number` | Decimal degrees, from -180 to 180. East is positive, so west is negative. | Ascendant never reads a time zone. It works only in UTC, and the same Located Moment always gives the same Placements. Getting from local time to UTC is your job, and it is where most mistakes happen. ## Build a Moment [#build-a-moment] There are two common starting points. Both use Effect's `DateTime`. ### From a timestamp that already has an offset [#from-a-timestamp-that-already-has-an-offset] If your source gives an ISO 8601 string with an offset, parse it and you are done: ```ts import { Chart } from "astro-ascendant"; import { DateTime, Option } from "effect"; const parsed = DateTime.make("2000-01-01T17:30:00+05:30"); // Option.some(2000-01-01T12:00:00.000Z) const moment = Option.map(parsed, (date) => Chart.Moment.make({ date })); ``` `DateTime.make` returns an `Option`, because the string might not be valid. The result is already UTC. ### From a local date, time, and time zone [#from-a-local-date-time-and-time-zone] If you have a wall-clock time and an IANA time zone name such as `Asia/Kolkata`, convert it with `DateTime.makeZoned`: ```ts const zoned = DateTime.makeZoned("2000-01-01T17:30:00", { timeZone: "Asia/Kolkata", adjustForTimeZone: true, disambiguation: "reject", }); const utc = Option.map(zoned, DateTime.toUtc); // Option.some(2000-01-01T12:00:00.000Z) ``` Two options matter here: - `adjustForTimeZone: true` reads the string as a local wall-clock time in that zone. Without it, the string is read as UTC and only labeled with the zone. - `disambiguation: "reject"` returns `Option.none()` for times that are ambiguous or do not exist. See the next section. ## Local times that are ambiguous or do not exist [#local-times-that-are-ambiguous-or-do-not-exist] Clock changes create two kinds of bad local times: | Case | Example in `America/New_York` | With `"reject"` | | --- | --- | --- | | A **gap**, when clocks jump forward, so the time never happened | `2021-03-14 02:30` | `Option.none()` | | An **overlap**, when clocks fall back, so the time happened twice | `2021-11-07 01:30` | `Option.none()` | The other disambiguation modes (`"earlier"`, `"later"`, `"compatible"`) silently pick an answer. That can move a birth by an hour and change the Lagna. For astrology, prefer `"reject"` and ask the user which of the two moments they mean, or for the exact UTC offset from their birth record. ## Convert in one function [#convert-in-one-function] Put the conversion in one place so every input takes the same path: ```ts import { Chart } from "astro-ascendant"; import { DateTime, Effect, Option } from "effect"; export const toLocatedMoment = (input: { readonly localDateTime: string; // "2000-01-01T17:30:00" readonly timeZone: string; // IANA name, e.g. "Asia/Kolkata" readonly latitude: number; readonly longitude: number; }): Effect.Effect => Option.match( DateTime.makeZoned(input.localDateTime, { timeZone: input.timeZone, adjustForTimeZone: true, disambiguation: "reject", }), { onNone: () => Effect.fail("That local time does not exist, or happens twice, in this time zone"), onSome: (zoned) => Effect.succeed({ moment: Chart.Moment.make({ date: DateTime.toUtc(zoned) }), latitude: input.latitude, longitude: input.longitude, }), }, ); ``` It fails with a message you can show the user, and succeeds with a value you can pass straight to `Chart.generate`: ```ts const calculation = yield* toLocatedMoment(input).pipe(Effect.flatMap(Chart.generate)); ``` ## Find the time zone for a place [#find-the-time-zone-for-a-place] Ascendant does not map coordinates to a time zone, and the UTC offset depends on both the place and the date. A place can change its offset over time, and daylight saving rules differ by year. Look the zone up with a time zone lookup package, or ask for it, and always combine it with the birth date. `DateTime` uses the time zone data of the runtime it runs on. Rules for dates before the mid-twentieth century, and for places that changed zones, can be wrong or simplified in that data. If your birth record states the exact UTC offset, pass it as an ISO string with the offset instead of converting from a zone name. ## Coordinates [#coordinates] Give latitude and longitude as decimal degrees. If you hold degrees, minutes, and seconds, convert them first: `degrees + minutes / 60 + seconds / 3600`, negated for south and west. `Chart.generate` validates its input. A value outside the range fails with a typed `LocatedMomentValidationError`, so a bad latitude does not produce a chart: ```ts // latitude: 120 fails with: // LocatedMomentValidationError: // Expected a value between -90 and 90 at ["latitude"] ``` Note that `Chart.LocatedMoment.make` throws on the same input, because it validates immediately. Use a plain object, as the function above does, when you want the failure to arrive as an Effect error instead of an exception. ## Optional: the person's sex [#optional-the-persons-sex] `Chart.generate` also accepts `Chart.ChartParams`, which is a Located Moment with an optional `sex` field. Ascendant copies it into each chart and does not use it in any calculation. ## A short checklist [#a-short-checklist] - Convert local time with the **birth date**, not today's date. - Use `disambiguation: "reject"` and handle `Option.none()`. - Remember that longitudes west of Greenwich are negative. - Pass the exact offset when you have it, especially for historical births. - Keep the UTC moment and the original local input together if you will show the result back to the user. # Provenance and verification (/docs/guides/provenance) An astrology result is only useful if you can say how it was produced. Ascendant answers that in two ways. Every result carries a **provenance**, a short label naming the method that made it. And every method has a **method specification** in a registry, which lists its steps and the properties it guarantees. This guide shows how to read both, how to connect them, and what to store alongside a result. ## What provenance is [#what-provenance-is] A provenance has three fields: | Field | Meaning | Example | | --- | --- | --- | | `school` | The tradition the method belongs to: `Parashari`, `Jaimini`, or `Universal`. | `Jaimini` | | `method` | The name of the specific method. | `plain-projection` | | `version` | The date the method last changed, as `YYYY-MM-DD`. | `2026-11-01` | Together they identify exactly one specification. Results that came from different versions came from different specifications. Today every method has the version `2026-11-01`. A D1 chart is `Universal`, because its houses follow whichever house system and ayanamsa you chose. Divisional charts, Placements, Vimshottari, Ashtakavarga, and transit events are `Parashari`. The Jaimini results and the Chara and Sthira dashas are `Jaimini`. ## Where to find it [#where-to-find-it] Provenance sits on the result itself. This table shows where to find it and which method each result reports: | Result | Where | School / method | | --- | --- | --- | | Placements | `placements.provenance` | Parashari / `sidereal-ephemeris-placements` | | D1 chart | `charts[0].provenance` | Universal / `house-system-cusps` | | Divisional chart | `charts[n].provenance` | Parashari / `whole-sign-from-mapped-lagna` | | Vimshottari dasha | `timeline[n].provenance`, on each Mahadasha | Parashari / `moon-nakshatra-balance` | | Ashtakavarga | `result.provenance` | Parashari / `bphs-bav-trikona-ekadhipatya` | | Chara Karakas | `result.provenance` | Jaimini / `exact-degree-shared-roles` | | Rashi Drishti | `result.provenance` | Jaimini / `movable-fixed-dual` | | Karakamsha | `result.provenance` | Jaimini / `atmakaraka-d9-sign` | | Arudha Pada | `result.provenance` | Jaimini / `plain-projection` | | Upapada | `result.provenance` | Jaimini / `twelfth-house-plain-projection` | | Argala | `result.provenance` | Jaimini / `structural-positions` | | Chara dasha | `timeline.provenance` | Jaimini / `kn-rao-co-lord-strength` | | Sthira dasha | `timeline.provenance` | Jaimini / `bv-raman-koch-brahma-strength` | | Transit event | `event.provenance` | Parashari / `transit-sample-bisect` | Two details: Vimshottari returns a plain array, so its provenance is on each Mahadasha and not on the array. And `placements.provenance` is optional in the type, because you can build Placements yourself. `Chart.generate` always fills it. Reading one is ordinary property access: ```ts import { CharaKarakas, Chart, Runtime } from "astro-ascendant"; import { DateTime, ManagedRuntime } from "effect"; const runtime = ManagedRuntime.make(Runtime.MainLayer); const located = Chart.LocatedMoment.make({ moment: Chart.Moment.make({ date: DateTime.makeUnsafe("2000-01-01T12:00:00Z") }), latitude: 12.9716, longitude: 77.5946, }); const { placements, charts } = await runtime.runPromise(Chart.generate(located, [9])); const karakas = await runtime.runPromise(CharaKarakas.calculate(placements)); charts[0].provenance; // { school: "Universal", method: "house-system-cusps", version: "2026-11-01" } karakas.provenance; // { school: "Jaimini", method: "exact-degree-shared-roles", version: "2026-11-01" } ``` ## The method registry [#the-method-registry] `Provenance.methods` is the registry. It holds one specification for each method in the table above: | Key | Method | | --- | --- | | `placements` | Placements from the ephemeris | | `d1ChartProjection` | D1 chart from house-system cusps | | `divisionalChartProjection` | Divisional chart from the mapped Lagna | | `vimshottariDasha` | Vimshottari dasha from the Moon's nakshatra balance | | `ashtakavarga` | Ashtakavarga with Trikona and Ekadhipatya reduction | | `jaiminiCharaKarakas` | Chara Karakas | | `jaiminiRashiDrishti` | Rashi Drishti | | `jaiminiKarakamsha` | Karakamsha | | `jaiminiArudhaPada` | Arudha Pada | | `jaiminiUpapada` | Upapada | | `jaiminiArgala` | Argala | | `charaDasha` | Chara dasha | | `sthiraDasha` | Sthira dasha | | `transitSearch` | Transit search | Each specification has three parts: its `provenance`, an ordered list of `steps`, and a `verification` list of the properties it guarantees. Here is the Arudha Pada: ```ts import { Provenance } from "astro-ascendant"; Provenance.methods.jaiminiArudhaPada; // { // provenance: { school: "Jaimini", method: "plain-projection", version: "2026-11-01" }, // steps: [ // { id: "derive-source", description: "Find the source sign and its lord for the requested house." }, // { id: "project", description: "Project the lord's distance from the source sign." }, // ], // verification: ["Exactly one placement is required for the house lord."], // } ``` The Chara dasha runs three steps, `derive-direction`, `resolve-co-lords`, and `build-periods`. The transit search guarantees that every zero-crossing counts in strict time order, that an exhausted search fails with a typed error instead of returning a short list, and that it finds the events an hourly scan of the ephemeris finds. The Ashtakavarga checks that each planetary table totals its classical bindu count and that the Sarvashtakavarga totals 337. `Provenance.MethodId` is the union of the registry keys, so you can write a lookup that TypeScript checks: ```ts const id: Provenance.MethodId = "charaDasha"; Provenance.methods[id].steps.map((step) => step.id); // ["derive-direction", "resolve-co-lords", "build-periods"] ``` ## From a result to its specification [#from-a-result-to-its-specification] To explain a result, find the specification whose provenance matches it: ```ts const specificationFor = (provenance: Provenance.CalculationProvenance) => Object.values(Provenance.methods).find( (spec) => spec.provenance.school === provenance.school && spec.provenance.method === provenance.method && spec.provenance.version === provenance.version, ); const spec = specificationFor(karakas.provenance); spec?.steps; // rank, assign spec?.verification; // ["All seven classical planet placements are required.", "Exact ties share roles."] ``` Every provenance in the table above resolves to a specification this way. Use it to build a "how was this calculated?" view in your own product, showing the steps in plain words next to the result. ## Store what you need to repeat a result [#store-what-you-need-to-repeat-a-result] A result is repeatable when you keep its inputs, its settings, and the methods that produced it. This record holds all three: ```ts const { placements, charts, astroParams } = await runtime.runPromise( Chart.generate(located, [9]), ); const record = { moment: DateTime.formatIso(located.moment.date), latitude: located.latitude, longitude: located.longitude, astroParams, // { ayanamsa: "KrishnamurtiVP291", houseSystem: "Placidus" } methods: [charts[0].provenance, charts[1].provenance, karakas.provenance], }; const stored = JSON.stringify(record); ``` When you read it back, validate the provenance with the library's schema before you trust it: ```ts import { Schema } from "effect"; const restored = JSON.parse(stored) as typeof record; const methods = restored.methods.map((method) => Schema.decodeUnknownSync(Provenance.CalculationProvenance)(method), ); ``` A value with a missing field fails with a schema error, so a damaged record does not slip through. Also store the version of `astro-ascendant` you ran. It is the simplest way to reproduce a result exactly. ## What to show readers [#what-to-show-readers] For any reading you present, state: - The **settings**: ayanamsa and house system, from `calculation.astroParams`. - The **school** of each judgment, and that the two schools were not mixed. See [Choose a school and settings](https://astro-ascendant.vercel.app/docs/guides/settings). - The **method** and **version** of each result. # Choose a school and settings (/docs/guides/settings) Every calculation in Ascendant depends on two settings, the **ayanamsa** and the **house system**. Together they are called **AstroParams**. This guide shows how to choose them for your use case, set them in code or from the environment, and confirm which ones a result used. ## Pick settings quickly [#pick-settings-quickly] | You want | Ayanamsa | House system | School | | --- | --- | --- | --- | | KP readings, using cusps, star lords, sub lords, and significators | `KrishnamurtiVP291` | `Placidus` | KP | | Parashari readings, using signs and divisional charts | `Lahiri` | `WholeSign` | Parashari | | No preference | Leave the defaults | | KP | The library defaults are `KrishnamurtiVP291` with `Placidus`, so if you set nothing, you are working in the KP school. A single judgment should never mix the two schools. If your product offers both, run each one with its own settings, as shown in [Use both schools in one app](#use-both-schools-in-one-app). ## What the two settings do [#what-the-two-settings-do] The **ayanamsa** is the offset that aligns tropical longitudes with the fixed sidereal zodiac. It changes every longitude Ascendant reports, for the Lagna and for each graha. The **house system** decides where the twelve house cusps of the **D1** chart fall. It affects D1 only. Divisional charts always use whole-sign houses, counted from their mapped Lagna, whichever house system you choose. AstroParams are not part of the birth moment. They are settings of the calculation, and every chart calculation records the ones it used. ## Set them in code [#set-them-in-code] Provide the settings with `AstroParams.layer` when you build your runtime: ```ts import { AstroParams, Swisseph } from "astro-ascendant"; import { Layer, ManagedRuntime } from "effect"; const runtime = ManagedRuntime.make( Layer.mergeAll( AstroParams.layer({ ayanamsa: "Lahiri", houseSystem: "WholeSign" }), Swisseph.SwissephCachedLayer, ), ); ``` `AstroParams.layer` takes an `Options` value, so TypeScript rejects any ayanamsa or house-system name the library does not know. Use `AstroParams.DefaultAstroParams` for the defaults, which is what `Runtime.MainLayer` uses. ## See what changes [#see-what-changes] The same birth, calculated under both presets, shows how much the settings matter. The birth is `2000-01-01 12:00 UTC` at `12.9716° N`, `77.5946° E`. | Result | `KrishnamurtiVP291` + `Placidus` | `Lahiri` + `WholeSign` | | --- | --- | --- | | Lagna longitude | 69.684° | 69.607° | | Lagna sign | Gemini | Gemini | | House 2 cusp in D1 | 95.392° | 90.000° | The ayanamsa moves the Lagna by a fraction of a degree. That is small here, but it is enough to change a star, a sub lord, or a sign at a boundary. The house system moves the cusps. Whole-sign houses always begin at a multiple of 30°, and Placidus cusps do not. ## Choose an ayanamsa [#choose-an-ayanamsa] Ascendant supports 47 ayanamsas, taken from the Swiss Ephemeris catalog. These are the ones most products use: | Name | Notes | | --- | --- | | `KrishnamurtiVP291` | A variant of the Krishnamurti ayanamsa. The library default, and the KP choice. | | `Krishnamurti` | The original Krishnamurti ayanamsa. | | `Lahiri` | The Chitrapaksha ayanamsa, widely used for Parashari work. | | `Raman` | Named for B. V. Raman. | | `FaganBradley` | The standard for Western sidereal astrology. | | `Yukteshwar` | Named for Sri Yukteshwar. | | `TrueCitra` | Keeps the star Chitra fixed at 180°. | ### All 47 ayanamsas [#all-47-ayanamsas] Pass any of these names exactly as written. They are listed in Swiss Ephemeris catalog order, not by how often they are used.
FaganBradley Lahiri DeLuce
Raman Ushashashi Krishnamurti
DjwhalKhul Yukteshwar JNBhasin
BabylKugler1 BabylKugler2 BabylKugler3
BabylHuber BabylEtPSC Aldebaran15Tau
Hipparchos Sassanian GalacticCenter0Sag
J2000 J1900 B1950
SuryaSiddhanta SuryaSiddhantaMeanSun Aryabhata
AryabhataMeanSun SSRevati SSCitra
TrueCitra TrueRevati TruePushya
GalacticCenterGilBrand GalacticEquatorIAU1958 GalacticEquator
GalacticEquatorMidMula Skydram TrueMula
DhruvaGalCenterMulaWilhelm Aryabhata522 BabylBritton
TrueSheoran GalacticCenterCochrane GalacticEquatorFiorenza
ValensMoon Lahiri1940 LahiriVP285
KrishnamurtiVP291 LahiriICRC
The [AstroParams reference](https://astro-ascendant.vercel.app/docs/api-reference/astro-params) shows the same list as a type. ## Choose a house system [#choose-a-house-system] Ascendant supports 13 house systems. `Placidus` is the default. | Name | How it divides the houses | | --- | --- | | `Placidus` | By time. Unequal houses. The default. | | `WholeSign` | One sign per house. The Lagna's sign is house 1, and every house is 30°. | | `Koch` | By time, from the birthplace meridian. Unequal houses. | | `Equal` | Twelve 30° houses counted from the Lagna's exact degree. | | `VehlowEqual` | Equal 30° houses with the Lagna in the middle of house 1. | | `Porphyrius` | Divides each quadrant between the angles into three equal parts. | | `Regiomontanus` | A medieval system based on divisions of the celestial equator. | | `Campanus` | Based on divisions of the prime vertical. | | `Meridian` | Axial rotation: equal in time, measured from the meridian. | | `Morinus` | Based on the celestial equator, with equal 30° houses. | | `Alcabitus` | A medieval Arabic system. | | `PolichPage` | Topocentric, a modern time-based system close to Placidus. | | `Azimuthal` | Based on the horizon. For specialized use. | Placidus and Koch are undefined inside the polar circles, where some degrees never rise or set. There, `Chart.generate` fails with a `ChartCalculationError` instead of returning bad cusps. In a test, 66° N worked and 68° N failed. Other systems, such as `Equal`, `WholeSign`, and `Campanus`, still return cusps, but they are not meaningful near the poles. If your users may be born far north or south, choose the house system deliberately. ## Use both schools in one app [#use-both-schools-in-one-app] Build one runtime per school and send each judgment to the right one. This keeps KP and Parashari results from mixing: ```ts import { AstroParams, Swisseph } from "astro-ascendant"; import { Layer, ManagedRuntime } from "effect"; export const kp = ManagedRuntime.make( Layer.mergeAll( AstroParams.layer({ ayanamsa: "KrishnamurtiVP291", houseSystem: "Placidus" }), Swisseph.SwissephCachedLayer, ), ); export const parashari = ManagedRuntime.make( Layer.mergeAll( AstroParams.layer({ ayanamsa: "Lahiri", houseSystem: "WholeSign" }), Swisseph.SwissephCachedLayer, ), ); ``` Then run a calculation with the runtime that matches the question, for example `kp.runPromise(Chart.generate(located))`. Dispose both at shutdown. ## Read the settings from the environment [#read-the-settings-from-the-environment] `AstroParams.layerConfig` reads the settings from Effect `Config`, so you can choose them per deployment without code changes: ```ts import { AstroParams, Swisseph } from "astro-ascendant"; import { Config, Layer, ManagedRuntime } from "effect"; const settings = AstroParams.layerConfig({ ayanamsa: Config.schema(AstroParams.Ayanamsa, "AYANAMSA"), houseSystem: Config.schema(AstroParams.HouseSystem, "HOUSE_SYSTEM"), }); const runtime = ManagedRuntime.make( Layer.mergeAll(settings, Swisseph.SwissephCachedLayer), ); ``` Run it with the variables set: ```bash AYANAMSA=Raman HOUSE_SYSTEM=Equal bun run app.ts ``` A name the library does not know, such as `AYANAMSA=Nope`, fails the program with a `ConfigError` whose message lists the accepted values. Your app stops at startup instead of producing a chart with the wrong settings. ## Confirm which settings a result used [#confirm-which-settings-a-result-used] Each chart calculation carries the settings it was made with: ```ts const calculation = yield* Chart.generate(located); console.log(calculation.astroParams); // { ayanamsa: "Lahiri", houseSystem: "WholeSign" } ``` Store or display this next to every result. A reading is only repeatable if you can say which ayanamsa and house system produced it. # Transit searches (/docs/guides/transits) A **transit search** finds when one graha does something specific: enters a sign, reaches a longitude, crosses a house cusp, or turns around. You give a starting **Located Moment**, and the search returns the next or previous events in time order. It reports exact moments and nothing else. There is no interpretation in the result. `Transit.findTransits` is the module's only function. This guide runs a first search, explains every request field and event kind, and shows how to handle a search that cannot finish. ## Run a first search [#run-a-first-search] Find the next three sign ingresses of Jupiter after 1 November 2026: ```ts import { Chart, Runtime, Transit } from "astro-ascendant"; import { DateTime, ManagedRuntime } from "effect"; const runtime = ManagedRuntime.make(Runtime.MainLayer); const from = Chart.LocatedMoment.make({ moment: Chart.Moment.make({ date: DateTime.makeUnsafe("2026-11-01T00:00:00Z") }), latitude: 12.9716, longitude: 77.5946, }); const events = await runtime.runPromise( Transit.findTransits({ planet: "Jupiter", from, count: 3, direction: "forward", kinds: ["sign-ingress"], maxYears: 12, }), ); ``` Each event reports where and when: | Moment (UTC) | Longitude | Sign | Retrograde | | --- | --- | --- | --- | | `2027-01-25 11:30` | 120° | Cancer | yes | | `2027-06-25 13:09` | 120° | Leo | no | | `2027-11-25 23:14` | 150° | Virgo | no | The first event is Jupiter moving backward across the Cancer and Leo boundary while retrograde. The search counts **every** crossing in strict time order, so a retrograde sweep produces extra events and a boundary can appear three times. An event's `sign` is the sign the graha moves into at that instant, in the direction of the search. Here Jupiter moves back into Cancer, then forward into Leo, then Virgo. ## The request [#the-request] | Field | Meaning | | --- | --- | | `planet` | The graha to follow. Any of the nine, Rahu and Ketu included. | | `from` | The Located Moment to search from. Its location is used for house cusps and attached charts. | | `count` | How many events to return, from 1 to 100. | | `direction` | `"forward"` for the next events, `"backward"` for the previous ones. | | `kinds` | One or more event kinds, listed below. At least one is required. | | `targetLongitude` | Required with `longitude-hit`. | | `house` | Required with `cusp-crossing`, from 1 to 12. | | `maxYears` | How far to search, up to 200. The default is 30. | | `precisionMinutes` | How precisely each moment is refined. The default is 1 minute. | | `includeCharts` | Divisions to attach to every event. See [Attach charts](#attach-charts). | The search includes `from` itself: a graha that sits exactly on a boundary, target, or cusp at that moment reports it there. To search into the past, set `direction: "backward"`. The events then come back newest first. From 1 November 2026, Jupiter's previous two ingresses were into Cancer at `2026-10-30 15:40 UTC` and into Gemini at `2026-06-01 10:36 UTC`. ## Event kinds [#event-kinds] | Kind | The event | Needs | | --- | --- | --- | | `sign-ingress` | The graha crosses a sidereal sign boundary, at a multiple of 30°. | Nothing | | `longitude-hit` | The graha reaches an exact longitude. | `targetLongitude` | | `cusp-crossing` | The graha crosses the cusp of a house, as that cusp stands at `from`. | `house` | | `station` | The graha stops and reverses direction. | Nothing | You can list several kinds in one search. A missing field fails with a typed `TransitValidationError`, for example `targetLongitude is required for longitude-hit searches`. ### Sign ingress [#sign-ingress] This is the search from the first example. It needs no extra fields and returns the sign entered. ### Longitude hit [#longitude-hit] Pass the longitude you want in `targetLongitude`: ```ts const events = await runtime.runPromise( Transit.findTransits({ planet: "Saturn", from, count: 2, direction: "forward", kinds: ["longitude-hit"], targetLongitude: Chart.Longitude.make(350), }), ); ``` Saturn crosses 350° at `2027-03-10 09:27` and again at `2056-04-17 20:34`, about a lap later. Searching the Sun for 100° returns one hit a year: `2027-07-27`, `2028-07-26`, `2029-07-26`, and `2030-07-27`. ### Cusp crossing [#cusp-crossing] Name the house, and the search follows the cusp of that house as it stands in the D1 chart at `from`: ```ts const events = await runtime.runPromise( Transit.findTransits({ planet: "Sun", from, count: 2, direction: "forward", kinds: ["cusp-crossing"], house: 10, }), ); ``` The cusp comes from the house system in your settings, so a different house system gives a different cusp and different crossing times. For the birth location above, the tenth cusp is at 91.747°, so the Sun crosses it on `2027-07-18 18:20`, in Cancer, and again on `2028-07-18 00:37`. ### Station [#station] A station is the moment a graha's apparent motion reverses: ```ts const events = await runtime.runPromise( Transit.findTransits({ planet: "Mercury", from, count: 4, direction: "forward", kinds: ["station"], }), ); ``` For Mercury from 1 November 2026: | Moment (UTC) | Longitude | Sign | What happens | `is_retrograde` | | --- | --- | --- | --- | --- | | `2026-11-13 15:54` | 190.876° | Libra | Turns direct | `false` | | `2027-02-09 17:36` | 311.819° | Aquarius | Turns retrograde | `true` | | `2027-03-03 12:32` | 296.760° | Capricorn | Turns direct | `false` | | `2027-06-10 18:14` | 72.194° | Gemini | Turns retrograde | `true` | At a station the speed is zero, so `is_retrograde` reports the motion just after it: `true` at a station that starts a retrograde, and `false` at one that ends it. ## Attach charts [#attach-charts] Add `includeCharts` to receive a full chart calculation for each event's moment, at the location in `from`: ```ts const events = await runtime.runPromise( Transit.findTransits({ planet: "Jupiter", from, count: 1, direction: "forward", kinds: ["sign-ingress"], maxYears: 12, includeCharts: [9], }), ); const calculation = events[0].calculation; // placements, D1 and D9, and the settings used ``` The search resolves the moments first and attaches charts afterward, so it costs little when you leave `includeCharts` out. D1 is always included. ## When a search cannot finish [#when-a-search-cannot-finish] The search fails with a typed error rather than returning a short list quietly. If the window ends before `count` events are found, the error is `TransitSearchExhausted`. It carries the events it did find and the time it searched until, so you can keep the partial result: ```ts import { Effect } from "effect"; const events = await runtime.runPromise( Transit.findTransits({ planet: "Saturn", from, count: 100, direction: "forward", kinds: ["sign-ingress"], maxYears: 1, }).pipe(Effect.catchTag("TransitSearchExhausted", (error) => Effect.succeed(error.found))), ); // 2 events: Aries and Pisces ``` An invalid or incomplete request fails with `TransitValidationError`, and so does a search window that leaves the supported Julian Day range. See [Errors and typed failures](https://astro-ascendant.vercel.app/docs/guides/errors). ## Speed [#speed] A search leaps from the starting moment to the earliest time a graha could reach the next boundary, target, or station, and refines each crossing with a few more lookups. Per the 6.1.0 release notes, typical searches use 5 to 25 times fewer ephemeris calls than before. `Runtime.MainLayer` also uses the cached Swiss Ephemeris layer, so repeated and overlapping searches reuse earlier positions. If you build your own layers, use `Swisseph.SwissephCachedLayer` and not `Swisseph.SwissephLayer`. See [Ephemeris adapters and caching](https://astro-ascendant.vercel.app/docs/guides/ephemeris-and-caching). ## Provenance [#provenance] Each event records its method: school `Parashari`, method `transit-sample-bisect`, version `2026-11-01`. The search leaps from the located moment by the time the graha needs to reach the nearest boundary, target, or station, then refines each crossing to `precisionMinutes`. # Getting started (/docs) Ascendant calculates sidereal (Vedic) astrology. It works out planetary **Placements** once for a moment and place, then derives everything else from them: charts, dasha timelines, Ashtakavarga, Jaimini results, and transit searches. There are three ways to use it, depending on what you want to do. ## Choose your path [#choose-your-path] ## Start with a prompt [#start-with-a-prompt] Copy this prompt into your coding agent (Claude Code, Codex, Cursor, Copilot, Gemini CLI, or any other) and replace the last line with what you want to build. It points the agent at these docs and sets the rules that keep Ascendant code correct. ```text I want to build an astrology feature in TypeScript with the astro-ascendant library (https://www.npmjs.com/package/astro-ascendant). Before writing code: 1. Read https://astro-ascendant.vercel.app/llms.txt and open the pages relevant to my task. Use https://astro-ascendant.vercel.app/llms-full.txt if you need everything. Add .md to any docs page URL to get it as Markdown. 2. Take names and signatures from the API reference. Do not invent functions, options, or ayanamsa and house system names. Rules: - Install with `astro-ascendant` and `effect`. The library is Effect-based and every calculation returns an Effect. - Create one ManagedRuntime from `Runtime.MainLayer`, reuse it for every call, and dispose it only at shutdown. - All moments are UTC. Convert the birth's local date and time with its IANA time zone, which depends on the place and the date. If I haven't given a birth time, place, or time zone, ask me. Never guess them. - Calculate Placements once with `Chart.generate` and reuse them for dashas, Ashtakavarga, Jaimini results, and transits. - Defaults are the KrishnamurtiVP291 ayanamsa with Placidus houses (KP). Use Lahiri with WholeSign houses only for Parashari work, and never mix the two schools in one judgment. State the settings used. - Only D1 has cusp-based houses and KP data. Divisional charts use whole-sign houses from their mapped Lagna. - Handle failures through Effect's typed errors. Do not swallow them. - The library calculates mechanics, not predictions. Do not present interpretations as certainty or as medical, legal, or financial advice. Start with a short plan, then implement it. Verify the code compiles and runs. My task: [describe what you want to build] ``` ## Install the library [#install-the-library] Ascendant is published on npm as `astro-ascendant`. It needs `effect` alongside it. ```bash bun add astro-ascendant effect ``` ```bash npm install astro-ascendant effect ``` Then follow the [Quickstart](https://astro-ascendant.vercel.app/docs/quickstart) to calculate your first chart and the Vimshottari dasha running today. ## What the library calculates [#what-the-library-calculates] | Calculation | What you get | Guide | | --- | --- | --- | | **Charts** | A cusp-aware D1 with KP evidence, and 15 whole-sign divisional charts. | [Divisional charts](https://astro-ascendant.vercel.app/docs/guides/divisional-charts) | | **Dashas** | Vimshottari planetary periods, and Jaimini Chara and Sthira sign periods, with date queries. | [Dashas](https://astro-ascendant.vercel.app/docs/guides/dashas) | | **Ashtakavarga** | Bhinnashtakavarga, Sarvashtakavarga, reduced tables, and Shodhya Pinda. | [Ashtakavarga](https://astro-ascendant.vercel.app/docs/guides/ashtakavarga) | | **Jaimini** | Chara Karakas, Rashi Drishti, Karakamsha, Arudha Pada, Upapada, and Argala. | [Jaimini calculations](https://astro-ascendant.vercel.app/docs/guides/jaimini) | | **Transits** | The next or previous sign ingresses, longitude hits, cusp crossings, and stations of a graha. | [Transit searches](https://astro-ascendant.vercel.app/docs/guides/transits) | | **Settings** | 47 ayanamsas and 13 house systems, recorded on every result. | [Choose a school and settings](https://astro-ascendant.vercel.app/docs/guides/settings) | By default Ascendant uses the sidereal zodiac, the KrishnamurtiVP291 ayanamsa, and Placidus houses. Methods are named so you can check and repeat a result. A reading is guidance, not certainty, and not a substitute for medical, legal, or financial advice. ## How these docs are organized [#how-these-docs-are-organized] | Section | Who it is for | Start with | | --- | --- | --- | | **For horoscope users** | Anyone who wants a horoscope and an assistant to help read it. | [Create a horoscope](https://astro-ascendant.vercel.app/chart), then [Skills and plugins](https://astro-ascendant.vercel.app/docs/skills) | | **Start here** | Developers new to the library. | [Quickstart](https://astro-ascendant.vercel.app/docs/quickstart), then [Core concepts](https://astro-ascendant.vercel.app/docs/concepts) | | **Developer guides** | Developers building a feature. | [Choose a school and settings](https://astro-ascendant.vercel.app/docs/guides/settings) | | **API reference** | Exact names, types, and errors. | [Chart](https://astro-ascendant.vercel.app/docs/api-reference/chart) | | **Agents** | Anyone building with a coding agent. | [Build with an agent](https://astro-ascendant.vercel.app/docs/agents) | # Quickstart (/docs/quickstart) In this guide you will calculate a **D1** chart, a **D9** chart, and the **Vimshottari dasha** running today, for one birth. You will finish with a single TypeScript file you can run on Bun or Node.js and then grow into a real application. The Python SDK ([`astro-ascendant`](https://pypi.org/project/astro-ascendant/) on PyPI) shares its name with the TypeScript SDK but is **not at feature parity** and is **not actively developed**. Use the TypeScript SDK instead. If you need it maintained, [fork it on GitHub](https://github.com/astro-ascendant/ascendant). ## Install [#install] You need an ES module project with TypeScript and Node.js 20 or later, or Bun. ```bash bun add astro-ascendant effect ``` ```bash npm install astro-ascendant effect npm install --save-dev tsx ``` Set `"type": "module"` in your `package.json` so the file below can use top-level `await`. Ascendant is built on [Effect](https://effect.website). Every calculation returns an `Effect`: a description of work that does nothing until you run it. You write programs with `Effect.gen` and `yield*`, and you run them once, at the edge of your app. This page explains each piece as it appears. To learn Effect itself, start with [effect.website](https://effect.website), [effect.institute](https://effect.institute), or [effect.kitlangton.com](https://effect.kitlangton.com). ## Build the program [#build-the-program] ### Create a runtime [#create-a-runtime] ```ts import { Chart, Dasha, Runtime } from "astro-ascendant"; import { Console, DateTime, Effect, ManagedRuntime } from "effect"; const runtime = ManagedRuntime.make(Runtime.MainLayer); const degrees = (value: number) => Number(value.toFixed(2)); ``` `degrees` only rounds longitudes so the tables stay readable. A `ManagedRuntime` is how you run Effect programs from ordinary code, and it is the recommended way to do so. You build it once and reuse it for every calculation. `Runtime.MainLayer` supplies what Ascendant needs: the Swiss Ephemeris, with a cache for repeated position lookups, and the default calculation settings (see [Defaults and settings](#defaults-and-settings)). ### Describe the birth moment [#describe-the-birth-moment] ```ts const program = Effect.gen(function* () { const birth = DateTime.makeZonedUnsafe("2000-01-01T17:30:00", { timeZone: "Asia/Kolkata", adjustForTimeZone: true, disambiguation: "reject", }); const located = Chart.LocatedMoment.make({ moment: Chart.Moment.make({ date: DateTime.toUtc(birth) }), latitude: 12.9716, longitude: 77.5946, }); ``` Ascendant always works in UTC. Here the local wall-clock time and its IANA time zone are converted to a UTC `Moment`. A `LocatedMoment` pairs that moment with the latitude and longitude where the positions are calculated. This example names the time zone directly. In a real application, you must find the time zone for the birth place and date yourself, because the UTC offset depends on both. See [From local time to a Located Moment](https://astro-ascendant.vercel.app/docs/guides/located-moments). ### Generate the charts [#generate-the-charts] ```ts const { placements, charts, astroParams } = yield* Chart.generate(located, [9]); ``` `Chart.generate` calculates the **Placements** once, then derives the charts from them. D1 is always first, and `[9]` adds the D9. `astroParams` records the ayanamsa and house system the calculation used. `yield*` runs the effect and gives you its result inside `Effect.gen`. ### Calculate the dasha [#calculate-the-dasha] ```ts const timeline = yield* Dasha.calculate(located.moment, placements); const current = yield* Dasha.at(timeline); ``` The dasha reuses the same placements instead of recalculating them. `Dasha.calculate` builds the full Vimshottari timeline. `Dasha.at` finds the Mahadasha and Antardasha active at a given moment, and now when you pass none. ### Print the results [#print-the-results] ```ts yield* Console.log(`Placements (${astroParams.ayanamsa}, ${astroParams.houseSystem})`); yield* Console.table([ { Point: placements.lagna.name, Longitude: degrees(placements.lagna.longitude), Star: placements.lagna.star.name, Pada: placements.lagna.star.pada, Retrograde: "—", }, ...placements.planets.map((planet) => ({ Point: planet.name, Longitude: degrees(planet.longitude), Star: planet.star.name, Pada: planet.star.pada, Retrograde: planet.is_retrograde, })), ]); yield* Console.log("Charts"); yield* Console.table( charts.map((chart) => ({ Chart: `D${chart.division}`, "Lagna Sign": chart.houses[1].sign, })), ); if (current) { yield* Console.log("Running dasha"); yield* Console.table([ { Mahadasha: current.mahadasha.mahadasha, Antardasha: current.antardasha.antardasha, Start: DateTime.formatIso(current.antardasha.start), End: DateTime.formatIso(current.antardasha.end), }, ]); } }); await runtime.runPromise(program); await runtime.dispose(); ``` `Console.log` and `Console.table` are Effect's versions of the console methods. They are Effects too, so you `yield*` them inside the program like any other step. `Console.table` takes an array of objects and prints one row per object, with the keys as column headings. Nothing has run yet. `runtime.runPromise` runs the program and returns a promise. `runtime.dispose()` releases the runtime's resources when you are done. ## The full program [#the-full-program] Save this as `quickstart.ts`: ```ts title="quickstart.ts" import { Chart, Dasha, Runtime } from "astro-ascendant"; import { Console, DateTime, Effect, ManagedRuntime } from "effect"; const runtime = ManagedRuntime.make(Runtime.MainLayer); const degrees = (value: number) => Number(value.toFixed(2)); const program = Effect.gen(function* () { const birth = DateTime.makeZonedUnsafe("2000-01-01T17:30:00", { timeZone: "Asia/Kolkata", adjustForTimeZone: true, disambiguation: "reject", }); const located = Chart.LocatedMoment.make({ moment: Chart.Moment.make({ date: DateTime.toUtc(birth) }), latitude: 12.9716, longitude: 77.5946, }); const { placements, charts, astroParams } = yield* Chart.generate(located, [9]); const timeline = yield* Dasha.calculate(located.moment, placements); const current = yield* Dasha.at(timeline); yield* Console.log(`Placements (${astroParams.ayanamsa}, ${astroParams.houseSystem})`); yield* Console.table([ { Point: placements.lagna.name, Longitude: degrees(placements.lagna.longitude), Star: placements.lagna.star.name, Pada: placements.lagna.star.pada, Retrograde: "—", }, ...placements.planets.map((planet) => ({ Point: planet.name, Longitude: degrees(planet.longitude), Star: planet.star.name, Pada: planet.star.pada, Retrograde: planet.is_retrograde, })), ]); yield* Console.log("Charts"); yield* Console.table( charts.map((chart) => ({ Chart: `D${chart.division}`, "Lagna Sign": chart.houses[1].sign, })), ); if (current) { yield* Console.log("Running dasha"); yield* Console.table([ { Mahadasha: current.mahadasha.mahadasha, Antardasha: current.antardasha.antardasha, Start: DateTime.formatIso(current.antardasha.start), End: DateTime.formatIso(current.antardasha.end), }, ]); } }); await runtime.runPromise(program); await runtime.dispose(); ``` ## Run it [#run-it] ```bash bun run quickstart.ts ``` ```bash npx tsx quickstart.ts ``` You should see output like this: ```text Placements (KrishnamurtiVP291, Placidus) ┌───┬─────────┬───────────┬───────────────┬──────┬────────────┐ │ │ Point │ Longitude │ Star │ Pada │ Retrograde │ ├───┼─────────┼───────────┼───────────────┼──────┼────────────┤ │ 0 │ Lagna │ 69.68 │ Ardra │ 1 │ — │ │ 1 │ Sun │ 256.59 │ Purva Ashadha │ 1 │ false │ │ 2 │ Moon │ 199.55 │ Swati │ 4 │ false │ │ 3 │ Mars │ 304.19 │ Dhanishta │ 4 │ false │ │ 4 │ Mercury │ 248.11 │ Mula │ 3 │ false │ │ 5 │ Jupiter │ 1.48 │ Ashwini │ 1 │ false │ │ 6 │ Venus │ 217.79 │ Anuradha │ 2 │ false │ │ 7 │ Saturn │ 16.62 │ Bharani │ 1 │ true │ │ 8 │ Rahu │ 101.26 │ Pushya │ 3 │ false │ │ 9 │ Ketu │ 281.26 │ Shravana │ 1 │ false │ └───┴─────────┴───────────┴───────────────┴──────┴────────────┘ Charts ┌───┬───────┬─────────────┐ │ │ Chart │ Lagna Sign │ ├───┼───────┼─────────────┤ │ 0 │ D1 │ Gemini │ │ 1 │ D9 │ Sagittarius │ └───┴───────┴─────────────┘ Running dasha ┌───┬───────────┬────────────┬──────────────────────────┬──────────────────────────┐ │ │ Mahadasha │ Antardasha │ Start │ End │ ├───┼───────────┼────────────┼──────────────────────────┼──────────────────────────┤ │ 0 │ Saturn │ Sun │ 2026-08-02T12:48:00.000Z │ 2027-07-15T12:48:00.000Z │ └───┴───────────┴────────────┴──────────────────────────┴──────────────────────────┘ ``` The positions are fixed by the birth moment, so yours will match. The **Running dasha** table depends on the day you run it, and this output is from 1 November 2026. The unnamed first column is the row number that `Console.table` adds. ## What you got back [#what-you-got-back] - `placements` holds the shared sidereal positions of the Lagna and the nine grahas, each with its star and pada. Reuse it with every other calculation. - `charts[0]` is the **D1**. Its houses start at the house system's cusps, and only D1 carries the chart angles and the KP star, sub, and sub-sub lords. - `charts[1]` is the **D9**. Its houses are whole signs counted from its mapped Lagna. - `timeline` is the full Vimshottari timeline, and `current` is the period active now. The [Core concepts](https://astro-ascendant.vercel.app/docs/concepts) page explains each of these terms. ## Defaults and settings [#defaults-and-settings] `Runtime.MainLayer` uses the library defaults: the KrishnamurtiVP291 ayanamsa and Placidus houses. Every calculation records the settings it used, so results stay reproducible. To use Lahiri with whole-sign houses, as Parashari readings do, build the runtime from your own settings: ```ts import { AstroParams, Swisseph } from "astro-ascendant"; import { Layer, ManagedRuntime } from "effect"; const runtime = ManagedRuntime.make( Layer.mergeAll( AstroParams.layer({ ayanamsa: "Lahiri", houseSystem: "WholeSign" }), Swisseph.SwissephCachedLayer, ), ); ``` See [Choose a school and settings](https://astro-ascendant.vercel.app/docs/guides/settings) for every ayanamsa and house system, and how to pick between them. ## Using it in a long-running app [#using-it-in-a-long-running-app] Create the runtime once, when your server starts, and call `runtime.runPromise` in each request handler. The ephemeris and its cache are then shared across requests. Call `runtime.dispose()` only when the process shuts down. In a short script, as above, dispose at the end. Every calculation can fail with a typed error, such as an invalid division or an exhausted transit search. See [Errors and typed failures](https://astro-ascendant.vercel.app/docs/guides/errors) to handle them. # Skills and plugins (/docs/skills) ## Overview [#overview] **Ascendant** saves a birth chart once, then answers astrology questions from that stored record instead of recalled positions. It runs as an agent skill in OpenCode, Claude Code, Codex, and any skill-compatible agent. The workflow has two moves: 1. **Save a chart.** Give a person's birth data; the skill writes a record to `persons//` and generates the Vedic and KP charts, dashas, and Ashtakavarga its answers draw on. The birth header stays immutable, and confirmed past events accumulate in the record's `MEMORY.md`. 2. **Ask a question.** Ask in plain words. The reply leads with the judgment, then shows the method and citations underneath. Each answer commits to one school. **KP** (KrishnamurtiVP291, Placidus, Vimshottari) is the default for every question. **Parashari** (Lahiri, whole-sign, Vimshottari) applies only on an explicit request, where it covers promise, quality, yogas, and divisional charts. The two are never mixed, and the answer states its school, ayanamsa, house system, and dasha system up front. Three properties keep readings consistent: - **Cited evidence**: every claim points to a chart file (`path:field`) and a guidebook section, so judgments trace to the record, not memory. - **One method per judgment**: KP placements are never read with Parashari rules. - **No hedging**: when evidence is missing or conflicts block judgment, the skill returns `Insufficient evidence: run init or attach X`. The skill declines to time or assert deaths, serious accidents, or severe illnesses. ## Installation [#installation] ### Universal Install [#universal-install] One command for any skill-compatible agent. From the working directory where you want to use Ascendant, run: ```bash npx ascendant-skills install ``` ### OpenCode Plugin [#opencode-plugin] Run: ```bash opencode plugin add opencode-ascendant ``` ### Claude Code Plugin [#claude-code-plugin] Run these commands inside Claude Code: ```text /plugin marketplace add astro-ascendant/skills /plugin install ascendant@ascendant /reload-plugins ``` ### Codex Plugin [#codex-plugin] Run: ```bash codex plugin marketplace add astro-ascendant/skills --ref main codex plugin add ascendant@ascendant ```