Chart Core — High-Level Design
Status: Draft for review · Part of: Astro Composer · Updated: 2026-10-24
Chart core turns one binder snapshot plus one profile into the D1 chart: frame, houses, bodies and planet judgements. It is the only engine that picks the chart frame, and every other engine reads its output.
1. Purpose
The binder reports positions, signs, nakshatras (lunar mansions) and padas (quarter divisions). It does not:
- choose the chart reference (the lagna, or rising sign, by default);
- place bodies in houses;
- judge dignity, combustion (being burnt by the Sun) or planetary war (two planets close together).
Vargas (divisional charts), transits, matching and muhurta all depend on those decisions being made once. Chart core exists so that:
- one lagna and one house convention produce one frame per response;
- a body’s house comes from binder facts, never a second longitude-to-sign function;
- dignity, combustion and planetary war come from rulebook tables, not scattered thresholds;
- later engines read one stable, ordered chart object instead of the binder.
2. Where it sits
binder positions + houses ──┐
├──▶ chart core ──▶ chart facts ──▶ varga · dasha · transit · matching
profile + rulebook handle ──┘
- Upstream: the composition facade supplies one binder read set, one profile and one rulebook handle. Chart core runs once per composition.
- Downstream: the varga engine takes the D1 longitudes, sign ids and lagna; the dasha engine takes the Moon facts; transit, matching and muhurta read the assembled chart.
- Never: no binder call, no HTTP parsing, no varga division, no dasha time arithmetic.
Input contract
| Input | Source | Used for |
|---|---|---|
positions[] |
Binder POST /v1/positions |
Longitude, sign, degree in sign, nakshatra, pada, speed, motion |
houses |
Binder POST /v1/houses |
Cusps, ascendant, midheaven and other angles |
Per-body house |
Binder positions block, when available |
Cusp placement without local cusp walking |
meta |
Binder response | Provenance passthrough, not reinterpretation |
| Profile | Request | Frame reference, house system, required body set, node type |
| Rulebook handle | Registry | Dignity, combustion and planetary-war tables |
Output contract
| Output | Shape | Read by |
|---|---|---|
frame |
Lagna sign and degree, reference body, house system | Varga frame, transits, muhurta |
bodies[] |
Binder facts plus house, dignity, combust, war |
Response, matching, strength |
houses[] |
House number, sign or cusp span, member body ids | Response, KP work later |
aspects |
Passed through from the binder by reference | Later layers, never recomputed |
diagnostics |
Unsupported placement, missing body, refusal reason | Facade degradation and errors |
3. Responsibilities
| Area | Owns | Deterministic rule |
|---|---|---|
| Frame selection | Which body anchors the chart | Lagna by default; profile may name the Moon or Sun |
| House placement | Which house holds each body | Whole sign: ((bodySign − refSign) mod 12) + 1 |
| Cusp placement | Binder placement, passed through | Only when the profile declares a cusp system |
| House rosters | Bodies per house in canonical order | Built from resolved placement, never inferred |
| Dignity | Own, exalted, debilitated, moolatrikona, friend, neutral, enemy | Rulebook lookup over sign, sign lord and degree |
| Combustion | Solar orb state per planet | Shortest arc from the Sun, against a per-planet, motion-aware orb |
| Planetary war | Two non-luminaries inside the war orb | Rulebook outcome; lower longitude wins only where the table says so |
| Assembly | One stable chart object | Canonical body and house order, one rounding point |
4. Architecture
Diagram: reference design, not deployed behaviour.
Chart core is a set of pure functions, not a service. The facade owns every read and every field chart core does not produce. One shared chart context stops a body from being placed under a different lagna.
5. Rules
- Longitudes are read, not derived. Only integer sign distance is computed, for whole-sign houses. A local
floor(longitude / 30)is a defect. - Cusp placement stays binder-owned. Without per-body binder placement, a cusp profile reports the layer unsupported instead of walking cusps.
- Whole sign means whole sign. Cusps never enter a whole-sign chart, even when the binder returned them for another layer.
- One frame per chart. The reference body is resolved once, recorded in
frame, and never re-chosen downstream. - Judgements are table lookups. Exaltation degrees, moolatrikona ranges, combustion orbs and war outcomes live in the rulebook; code holds no planet-specific threshold.
- Half-open boundaries are inherited. A body exactly on a sign or cusp boundary belongs to the binder’s interval; chart core never re-opens it.
- The profile’s body set is required. A missing body fails the request unless
allowPartialis set, which marks affected judgements unavailable. - Aspects are consumed, not recomputed. Binder aspects are attached by reference; separation, orb and applying state are not recalculated.
- Deterministic ordering. Bodies follow the profile’s canonical graha order, houses run 1–12, and no hash or map order reaches the response.
- No clock, locale or storage. Chart core takes a canonicalised context and returns a value object; it reads no “now” and persists nothing.
6. Edge cases
| Case | Behaviour |
|---|---|
| Body exactly at 0°00′ of a sign | Binder sign and degree are used; no local recomputation |
| Whole-sign chart with cusps present | Cusps ignored; house comes from sign distance |
| Cusp profile without per-body binder placement | LAYER_UNSUPPORTED; no guessed house number |
| Body exactly on a cusp | Binder placement decides; the interval is not reopened |
| Placidus beyond the polar circle | Binder refusal propagates as HOUSE_SYSTEM_UNDEFINED |
| Sun absent from a partial request | Combustion marked unavailable, not computed against nothing |
| Retrograde planet inside the combustion orb | Orb from the rulebook; motion state from the binder |
| Two planets at the same longitude | War table resolves, or returns undetermined; never a silent tie-break |
| Rahu or Ketu | Table-defined dignity only; no exaltation, combustion or war |
| Single-body request | Body read allowed; rosters and war marked unavailable, not returned empty |
| Moon or Sun requested as the frame | Same house arithmetic, different reference sign, recorded in frame |
| Tropical profile | Signs and degrees still come from the binder; nakshatra fields stay absent |
7. Out of scope
- Divisional charts and vargottama (varga engine).
- Vimshottari periods and period lookup (dasha engine).
- Panchang, transits, matching and muhurta rules (their own designs).
- Aspect geometry, nakshatra and pada derivation (binder semantics).
- Provider calls, retries, caching and provenance (facade and binder).
- Chart drawing, glyph placement and interpretation copy (client and product content).
8. Planned code
| Piece | Responsibility |
|---|---|
chartcore/FrameResolver |
Reference body, lagna sign and degree, angles |
chartcore/HousePlacementResolver |
Whole-sign distance and cusp passthrough |
chartcore/HouseRosterBuilder |
Bodies per house in canonical order |
chartcore/DignityResolver |
Rulebook dignity lookup over sign, lord and degree |
chartcore/CombustionResolver |
Angular distance from the Sun against orb tables |
chartcore/PlanetaryWarResolver |
War pairs, orb check and rulebook outcome |
chartcore/ChartAssembler |
One chart value object with stable order and rounding |
chartcore/ChartTables |
Read-only rulebook views used by the resolvers |