Astrology21 MIN READ16 / 31

Astro Composer — High-Level Design

Status: Draft for review · Scope: composition layer over Astro Binder · Updated: 2026-10-24

Astro Composer turns canonical astronomy into astrology charts. It reads positions and houses from Astro Binder and composes natal charts, vargas (divisional charts), Vimshottari dashas (planetary periods), panchang, transits, matching and muhurta.

The binder binds two ephemeris providers into one contract; the composer composes chart meaning on top. It owns no provider, ephemeris file or astronomy.

1. Purpose

Astro Binder stops at positions, houses and base semantics. Every feature above it — kundali, dasha timeline, panchang, match report, muhurta picker — needs the same second layer: one ayanamsa (zodiac offset), one varga table set, one dasha year length, one house convention. Built per feature, two screens disagree about the same birth data, as they would about positions without the binder.

Astro Composer exists so that:

  1. chart rules live once, in a versioned, pure engine;
  2. a chart is a deterministic function of input, profile and rulebook version;
  3. astronomy stays with the binder: no Swiss Ephemeris, Horizons or other provider call here;
  4. every response states which conventions produced it, so a chart can be audited and reproduced years later.

2. Scope

In scope

  • Natal chart assembly: lagna (rising sign), house frames, bodies in signs, houses, nakshatras, dignity.
  • Divisional charts: the sixteen Parashara vargas from D1.
  • Vimshottari dasha: mahadasha, antardasha and pratyantardasha periods with dates.
  • Panchang: tithi, vara, nakshatra, yoga and karana for a local day, with boundary times.
  • Transits (gochara), Sade Sati windows and ashtakavarga.
  • Compatibility: ashtakoota for a chart pair, plus implied doshas.
  • Muhurta: versioned, purpose-specific auspicious-window packs.
  • Platform pieces: chart profiles, rulebook registry, derived-result cache, per-response provenance.

Out of scope

  • Astronomy. Ephemeris access, provider routing, ayanamsa arithmetic and rounding stay in the binder.
  • Base semantics. Signs, nakshatras, padas, retrograde state, house placement and aspect geometry are binder facts. Exception: whole-sign occupancy — sign distance over two binder sign ids, not longitude arithmetic.
  • User data. People, birth records and saved charts belong to the chart/account domain, which stores a composer response as an immutable snapshot.
  • Interpretation. Text, remedies, gemstones, predictions. The composer returns facts; wording belongs to the product.
  • Other branches. Rectification, prashna, mundane astrology, eclipses, vaastu.
  • Civil time. Timezone databases. Callers send an absolute instant or explicit UTC offset, as for the binder.

Assumptions

  • The binder is reachable over REST and stays the single source of astronomy.
  • Binder phase 0 is live: POST /v1/positions, POST /v1/houses, GET /v1/capabilities.
  • The composer is a JVM (Spring Boot) service like the rest of the platform.
  • Rule tables ship as versioned deployment resources, not runtime-edited database rows.

3. Where it sits

birth data ──▶ chart/account domain ──▶ Astro Composer ──▶ Astro Binder ──▶ Swiss Ephemeris
                      │                      │                 └──────────▶ JPL Horizons (validation)
                      └── stores the response snapshot

Ownership is a rule, not a preference:

Concern Owner Reason
Instant, time scales, ΔT, frame Binder One interpretation of “when”
Provider choice, precision, provenance Binder One interpretation of “where”
Zodiac conversion, ayanamsa value Binder Applied once, recorded once
Sign, degree, nakshatra, pada Binder Base meaning shared by every feature
Retrograde and motion state Binder Derived from provider speed
House cusps, chart angles Binder Swiss-only by construction
Aspects (separation, orb, applying) Binder Geometry, not interpretation
Which house a body occupies Binder Cusp interval arithmetic; whole sign is sign distance
Lagna selection and chart frame Composer A chart choice, not an astronomical fact
Dignity, combustion, planetary war Composer Chart-level judgement with rule tables
Vargas, dashas, panchang, transits Composer Composition over binder output
Ashtakavarga, ashtakoota, muhurta Composer Rule tables plus composition
Saved charts and their history Chart domain Neither service owns user data

Stateless. The composer holds a derived-result cache and nothing else. The durable record of “the chart we showed this person in 2026” is a snapshot owned elsewhere.

No new licence surface. No astronomy code, Swiss call or ephemeris file enters it, so the AGPL/professional-licence decision stays in the binder (section 12).

4. Design decisions

# Decision Why
D1 Compose astronomy, never compute it. Binder provenance on every position; no provider call, no interpolation. One answer to “where was the Moon” across charts, panchang and transits.
D2 Charts are pure functions of input, profile and rulebook. chart = f(positions, houses, profile, rulebook); no clock, locale, database row or mutable config. “Now” is caller-supplied.
D3 Conventions are explicit and versioned. No implicit “Vedic” mode: a profile pins a rulebook, and the response echoes both plus a content hash. Rule changes are new versions.
D4 The binder owns base semantics. Signs, padas and cusp houses come from the binder; a local floor(longitude / 30) would make lagna and planet signs disagree.
D5 One rulebook version per response. Every layer resolves from one handle, so v1.2 varga tables cannot mix with v1.3 dasha tables.
D6 Fail closed, degrade explicitly. A missing binder answer fails the request; partial charts only on request, labelled degraded with missing layers listed.
D7 Cache derived results; never author charts. Key hashes input, profile, rulebook and binder version. Losing the cache changes latency, never output.
D8 The API stays smaller than the feature list. One chart endpoint with a layer selector serves natal, varga and dasha; panchang, transit, matching and muhurta endpoints exist because inputs differ, not per screen.

5. Architecture

Astro Composer high-level architecture Reference design. Product callers use one REST API. The composition facade resolves a versioned chart profile and rulebook, reads binder positions and houses through a binder client, and runs pure chart, varga, dasha, panchang, transit, matching and muhurta engines. Astro Binder remains the only owner of ephemeris providers and base astrology semantics. The composer keeps no primary datastore; its cache holds derived results keyed by input and versions. Astro Composer — High-Level Design One composition layer over Astro Binder: deterministic chart facts for natal, varga, dasha, panchang and transit features. Product BFF · apps · scheduled jobs birth data resolved upstream · internal callers Astro Composer service · one deployable Chart profiles vedic · KP · western explicit conventions, no defaults REST API · /v1 charts · panchang · transits validation · problem+json Rulebook registry immutable tables · semver + hash varga · dasha · koota · orbs Derived-chart cache hash of input + versions encrypted · TTL · no payload logs Composition facade select layers · call binder assemble · stamp versions Binder client positions · houses · rise-set version handshake · provenance Chart core D1 · lagna · houses dignity · combustion Varga engine 16 Parashara vargas from D1 · vargottama Dasha engine Vimshottari 120 y 3 levels · 365.25 d Panchang engine tithi · yoga · karana vara · sunrise mode Transit engine gochara · Sade Sati snapshot now · windows later Matching engine ashtakoota 36 doshas · cancellations Muhurta packs phase 3 · versioned panchang + lagna Engines are pure: no provider, network, clock or storage access. Composer is stateless: derived cache only; the chart/account domain persists snapshots. One rulebook version per response; convention changes are versioned upgrades, never hot edits. Binder contract extensions rise-set · batch positions · cusp placement Astro Binder · existing deployable Swiss Ephemeris worker swe_* · DE441 · ΔT local compute · AGPL boundary Binder REST API · /v1 positions · houses · capabilities canonical normalizer · semantics JPL Horizons API public · no auth · no SLA validation only Existing service: provider routing · result cache · provenance store · drift validation HTTPS · JSON canonical request profile + defaults versioned tables cache key binder calls layer requests HTTPS · JSON swe_* calls HTTPS synchronous dependency composer module entry / backing service service boundary Reference design · not deployed behaviour.

Diagram: reference design, not current repository behaviour.

Component Owns Notes
REST API /v1 Request shape, validation, problem+json errors No chart rules
Chart profiles Named convention bundles: zodiac, ayanamsa, houses, node, orbs, vargas Immutable and versioned
Rulebook registry Varga, dasha, dignity, koota, ashtakavarga and muhurta tables Content-addressed; read-only
Composition facade Layer resolution, binder calls, engine sequencing, assembly Single entry point
Binder client Positions, houses and (proposed) rise-set and timeseries calls Only component that knows the binder API
Chart core Lagna frame, house occupancy, dignity, combustion, planetary war Pure functions
Varga engine Sixteen divisional charts from the D1 longitude Pure functions
Dasha engine Vimshottari periods at three levels Pure time arithmetic
Panchang engine Tithi, vara, nakshatra, yoga, karana and their boundaries Needs rise/set; pure boundary probes
Transit engine Gochara, Sade Sati, ashtakavarga scoring Needs a window and a pinned reference
Matching engine Ashtakoota and its doshas for two charts Two chart references, no binder call
Muhurta packs Purpose-specific auspicious windows Phase 3; versioned per purpose
Derived-chart cache Complete responses keyed by input and versions Encrypted, TTL, no PII in keys
Astro Binder Astronomy, base semantics and provenance Existing deployable

Purity — no provider, network, clock or storage — keeps charts reproducible and tests meaningful.

Component designs

  • Chart core — lagna frame, whole-sign or binder cusp placement, house rosters, dignity, combustion, planetary war.
  • Varga engine — sixteen Shodasavarga charts as one total mapping from D1, including the varga frame and vargottama.
  • Dasha engine — Vimshottari at three levels as continuous UTC periods; profile-pinned year length; no clock or calendar.
  • Panchang engine — five limbs as sunrise-anchored intervals with solved boundaries, kshaya and adhika tithis, behind a boundary-probe port.
  • Transit engine — gochara from the natal Moon, Sade Sati phases, ashtakavarga scores over a version-matched reference.
  • Matching engine — eight kootas, thirty-six points, doshas and named cancellations from two Moon-fact references.

6. Binder integration

6.1 What the composer consumes

Binder surface Used for Status
POST /v1/positions Every body longitude, speed, sign, nakshatra, pada Available
POST /v1/houses Cusps and angles for the chart frame and cusp-based houses Available
GET /v1/capabilities Profile support check, engine version, precision at startup Available
POST /v1/rise-set Sunrise/sunset for panchang, vara, muhurta and upagraha Proposed
POST /v1/timeseries Batched positions for transit scans and muhurta windows Proposed

6.2 Contract gaps

The first slice covers natal charts, vargas and Vimshottari, not panchang, muhurta or transit scans. Three gaps must close in the binder, not here.

Gap 1 — No rise/set endpoint. Panchang is defined at sunrise: the day’s tithi runs then, and vara changes then. Without a binder answer the composer would implement solar altitude and horizon geometry — astronomy, which D1 forbids. Sunrise from positions also needs refraction and disc semantics the binder standardises.

Proposed shape, Swiss SE_BIT_HINDU_RISING semantics (disc centre, no refraction, geocentric, solar latitude ignored):

{
  "instant": "2026-10-24T00:00:00+05:30",
  "observer": { "latitude": 19.0760, "longitude": 72.8777 },
  "bodies": ["sun", "moon"],
  "events": ["rise", "set", "upperCulmination"],
  "horizon": "hindu"
}

Events return with the positions provenance block, so the composer can cite its definition; horizon: "astronomical" stays for western profiles.

Gap 2 — No batch or timeseries call. A transit window or muhurta scan is a sequence of instants. One call per instant makes a 30-day scan 30 calls and a muhurta scan hundreds; section 14 assumes batching.

Gap 3 — Cusp-based house placement is not on the wire. Binder semantics owns which house holds a body, but positions has no house field and houses returns cusps only. Whole-sign profiles derive the house from two binder sign ids — body and lagna — arithmetic on semantic output, not a duplicate rule. Cusp profiles cannot: walking cusps locally would reimplement a binder rule and drift at boundaries. Cheapest fix: an optional houses block on positions:

{ "houses": { "system": "whole_sign" }, "bodies": ["sun", "moon"] }

Each position gains house and houseCuspDistance. Until then, whole-sign profiles use sign ids; cusp profiles report the layer unsupported.

Aspect ownership. Binder semantics owns aspect geometry too, but the first slice exposes no aspects block. Yogas, matching and western profiles need aspects; expose binder aspects with a named orb profile rather than reimplement them. The composer owns only conclusions drawn from them (a yoga, a koota score).

Declination and obliquity. Neither is in the contract. Western parallel aspects and some KP work need declination, hence obliquity. Optional extension, not a phase 1 dependency.

6.3 Integration rules

  1. One binder read set per composition; the facade never re-reads the same instant twice.
  2. The binder’s meta passes through by reference, not paraphrase: the chart’s provenance is the binder’s.
  3. A binder degraded: true makes the response degraded: true, cause preserved.
  4. Binder error codes are mapped, not replaced: OUT_OF_EPHEMERIS_RANGE surfaces unchanged, with layering context added.
  5. The composer pins a minimum binder capability version at startup and refuses traffic from an older engine.

7. Profiles and rulebooks

A profile answers “which astrology are we doing”: a named, immutable convention bundle.

Field vedic-lahiri-whole-sign vedic-kp western-tropical-placidus
Zodiac sidereal sidereal tropical
Ayanamsa lahiri krishnamurti not applicable
House system whole_sign placidus placidus
Node type mean mean (to confirm) true
Aspects Vedic drishti set KP set Western orbs
Vargas sixteen D1 and D9 initially none
Dasha Vimshottari Vimshottari none
Nakshatra output yes yes, with sub-lords omitted

Rules:

  • The request names profile by id; the response echoes profile and profileVersion.
  • The profile pins rulebookVersion. “Latest” is allowed only as an explicit rulebook: "latest" and recorded as such, so an audit knows the chart was not pinned.
  • Any profile change that alters a number is a new version, not a toggle.
  • Unknown profile fields are rejected. No per-request override of ayanamsa, house system or node type: that is how two screens start disagreeing.

A rulebook is a content-addressed bundle: varga mappings, dasha sequence and year length, dignity and combustion thresholds, ashtakavarga contributions, koota tables and exceptions, panchang names, muhurta packs, yoga definitions. Semver is coarse — major for result-changing edits, minor for additive tables, patch for names and metadata. The hash is the identity; the version is for humans.

8. Composition pipeline

  1. Validate the request; resolve the profile and one rulebook handle.
  2. Canonicalise the instant. For a panchang day the caller supplies date and UTC offset; the composer derives the search window and never reads a timezone database.
  3. Compute the cache key; return a stored response on a hit.
  4. Call the binder for the instants the layers need, in parallel where independent.
  5. Run layers in dependency order: chart core, varga, strength and ashtakavarga, dasha, panchang, matching, muhurta.
  6. Assemble the response: profile, rulebook, layer results, binder meta, derivedFrom.
  7. Stamp versions, cache, emit metrics, return.

Layer dependencies:

  • varga needs chart core.
  • ashtakavarga needs chart core and the binder’s sign assignments.
  • dasha needs only the Moon.
  • panchang needs Sun and Moon at sunrise and at every boundary the solver asks for.
  • transit needs a pinned reference chart plus positions over the window.
  • matching needs two version-matched Moon-fact references.
  • muhurta needs panchang plus lagna.

9. Chart contract

The API mirrors the binder’s conventions — absolute instants, canonical lowercase ids, degrees only, one meta block — so one style serves both services.

Method Path Purpose
GET /v1/capabilities Profiles, rulebook versions, supported layers, limits
POST /v1/charts Natal chart with selected layers: vargas, dasha, strength
POST /v1/panchang Day panchang for a date, offset and observer
POST /v1/transits Gochara and Sade Sati for a window against a reference chart
POST /v1/matching Ashtakoota and doshas for two charts
POST /v1/muhurta Ranked auspicious windows for a purpose and window

POST /v1/charts

{
  "instant": "1990-04-14T09:30:00+05:30",
  "observer": { "latitude": 19.0760, "longitude": 72.8777, "elevationMeters": 14 },
  "profile": "vedic-lahiri-whole-sign",
  "rulebook": "2026.1",
  "layers": ["chart", "vargas", "dasha"],
  "vargas": ["D9", "D10"],
  "dashaLevels": 3
}
{
  "data": {
    "frame": { "lagna": "leo", "lagnaDegree": 17.42, "houseSystem": "whole_sign" },
    "bodies": [
      {
        "body": "sun", "longitude": 0.42, "speedLongitude": 0.9856,
        "sign": "aries", "house": 9, "nakshatra": "ashwini", "pada": 1,
        "retrograde": false, "dignity": "exalted", "combust": false
      }
    ],
    "houses": [{ "house": 1, "sign": "leo", "bodies": ["jupiter"] }],
    "vargas": { "D9": [{ "body": "sun", "sign": "sagittarius" }] },
    "dasha": {
      "system": "vimshottari", "yearLengthDays": 365.25,
      "periods": [
        { "lord": "ketu", "level": 1, "start": "1990-04-14T04:00:00Z", "end": "1997-04-13T22:00:00Z" }
      ]
    }
  },
  "meta": {
    "profile": "vedic-lahiri-whole-sign", "profileVersion": "1.0.0",
    "rulebook": { "id": "parashara-core", "version": "2026.1", "hash": "sha256:…" },
    "composerVersion": "1.0.0",
    "binder": { "engineVersion": "2.x", "ephemeris": "DE441", "ayanamsa": "lahiri", "degraded": false },
    "degraded": false, "missingLayers": [], "cacheHit": false
  }
}

Errors use application/problem+json with a stable code, in the binder’s style:

Code HTTP Meaning
UNKNOWN_PROFILE 400 Profile id not served
UNKNOWN_RULEBOOK 400 Rulebook version not available
PROFILE_RULEBOOK_MISMATCH 400 Version does not satisfy the profile constraint
LAYER_UNSUPPORTED 422 Layer unavailable for this profile (for example panchang before rise/set)
INSUFFICIENT_INPUT 422 Matching or transit reference lacks required data
REFERENCE_VERSION_MISMATCH 422 Reference chart differs from the request in profile, rulebook or binder engine
LIMIT_EXCEEDED 422 Window, step or probe count outside the configured cap; never silently truncated
HOUSE_SYSTEM_UNDEFINED 422 Binder refused Placidus at this latitude; no silent Porphyry
BINDER_UNAVAILABLE 503 Binder could not serve the request
BINDER_TIMEOUT 504 Binder exceeded the deadline
INTERNAL 500 Composition failure with a correlation id

10. Layer rules

  1. No astronomy. No provider call, ephemeris interpolation, ΔT or sidereal arithmetic.
  2. No duplicated base semantics. Signs, padas, motion, aspects and cusp houses come from the binder; whole-sign houses from two binder sign ids. A local floor(longitude / 30) is a defect.
  3. Vargas come from D1, never from a varga. Chaining divisions compounds rounding and disagrees with published tables.
  4. Whole-sign means whole-sign. House is sign distance from lagna; cusp arithmetic only under a declared cusp system.
  5. One rounding point. Engines return full precision; rounding happens at serialisation and is stated in meta.
  6. Half-open boundaries. A longitude exactly on a sign, nakshatra or tithi boundary belongs to the next interval, as in the binder.
  7. Unknown input fails. No silent fallback to Lahiri, mean node or the previous rulebook.
  8. No clock, no locale. Response dates are UTC instants; display timezones are the client’s problem. Month and weekday names are ids (tithi: "shukla_panchami").
  9. Deterministic ordering. Bodies, periods and windows follow a documented order, so responses diff byte for byte.
  10. Partial only on request. allowPartial: true returns degraded: true with missingLayers; otherwise a missing layer fails.
  11. References are version-matched, never reconciled. Transit and matching need an exact match on profile, rulebook hash and binder engine; anything else is REFERENCE_VERSION_MISMATCH.
  12. Engines never reach the binder. Only the facade reads it; panchang uses a boundary-probe port; an exceeded window or probe cap is LIMIT_EXCEEDED, never a truncated result.

11. Precision and reproducibility

Precision is inherited: double arithmetic, one rounding step at the end. One arcsecond of lunar longitude is about two seconds of dasha time, so dasha dates expose rounding regressions first and fixtures compare them to the second.

Four identifiers make output reproducible: profile version, rulebook hash, composer version, binder engine version. Given those four, the same request is byte-identical. Rules:

  • A number-changing rulebook edit invalidates that layer’s fixtures in the same commit.
  • An output-changing composer release bumps its version and records why; a fixture diff is a review gate, never routine.
  • A binder upgrade can change longitudes; recompute is a new chart revision, and stored snapshots keep the old numbers.
  • Golden fixtures cover fixed birth data, cities, epochs and profiles, including one high-latitude case, one polar-circle refusal and one DST-ambiguous birth time resolved by the caller.

12. Cache and data ownership

Derived-chart cache. Key: HMAC over the canonical request (instant, observer, profile, rulebook hash, layers, options) plus composer and binder engine versions. Birth data never appears in a key in plaintext, so a cache dump exposes no birth times. Values are complete responses, encrypted at rest, with a TTL by layer: natal and panchang 30 days, transit snapshots by explicit asOf, never a server-supplied “now”. Rulebook or engine changes evict by key construction, not a purge job.

No primary datastore. The composer owns no user, chart or account data. The chart/account domain stores a snapshot: the full response plus computedAt, the caller and the four identifiers above. It may re-request a chart, but the stored snapshot is what the user was shown.

Snapshot versus recompute. A stored chart is immutable. “The same chart under the current rulebook” is an explicit recompute producing a new revision and hash; both revisions stay readable. Silent recompute on read would break the audit trail.

13. Failure and degradation

Condition Behaviour Caller sees
Binder healthy Normal path degraded: false
Binder unavailable Fail closed; no local astronomy fallback 503 BINDER_UNAVAILABLE
Binder timeout Fail closed with the correlation id 504 BINDER_TIMEOUT
Binder degraded (Moshier) Propagate, do not hide degraded: true, binder precision
Placidus beyond the polar circle Binder refuses; composer refuses 422 HOUSE_SYSTEM_UNDEFINED
Rise/set unavailable Panchang and muhurta rejected before any computation 422 LAYER_UNSUPPORTED
Requested layer unsupported by profile Rejected at validation, not mid-composition 422 LAYER_UNSUPPORTED
Optional layer fails with allowPartial Return the layers that succeeded 200, degraded: true, missingLayers
Rulebook hash mismatch at load Fail startup; do not serve charts Deployment blocked
Cache unavailable Compute and continue 200, cacheHit: false
Cache value fails integrity check Discard, recompute, count a metric 200

Two rules outrank the table: never invent astronomy, never answer under a convention other than the requested one.

14. Non-functional targets

Concern Target
Natal chart (D1, D9, Vimshottari 3 levels) p95 ≤ 250 ms
Full sixteen vargas on an existing chart p95 ≤ 350 ms
Panchang day, including sunrise and boundary times p95 ≤ 800 ms
Matching (two charts) p95 ≤ 400 ms
Muhurta scan, 30-day window p95 ≤ 2 s
Composer availability 99.9 %, excluding binder outages
Determinism Byte-identical output for pinned versions
Typical chart response ≤ 64 KB uncompressed
Binder calls per natal chart ≤ 2
Binder calls per panchang day ≤ 50 with per-instant calls, ≤ 3 with a timeseries endpoint

That budget explains section 6.2: bisection over 24 hours needs about seventeen exact evaluations; with speeds and a Newton step, three to five. Four boundaries times two bodies turns a sub-second calculation into a hundred binder calls.

15. Security, licensing and privacy

Licence surface. No Swiss Ephemeris code, ephemeris data or provider client lives here, so the composer does not extend the AGPL/professional-licence decision. That gate sits in the binder.

Prior art. Reference implementations here are usually copyleft — PyJHora is AGPL-3.0 — so the composer must be written from traditional rules and published formulas. Record table comparisons as fixtures, not copied code, and route tables through legal review before release. A table transcribed from a copyleft project stays a licensing risk even when the numbers are traditional.

Privacy. Birth date, time and coordinates are personal data, and a chart identifies more than a position because it is stable across requests. Hence: no request-body logging, no coordinates in metric labels, HMAC cache keys, encrypted cache at rest, per-caller authorisation on chart reads, no third-party call at all.

Abuse. Matching and muhurta are the expensive endpoints. Per-caller quotas and a muhurta window cap stop a scan becoming a denial-of-service against the binder.

16. Observability

  • Per-layer latency and failure rate, so a slow varga table is distinguishable from a slow binder.
  • Binder dependency latency and error codes, split by endpoint.
  • Cache hit ratio per layer, and eviction reasons.
  • Active profile and rulebook hash as gauges: a mixed-version fleet is visible immediately.
  • Fixture canary at startup and on deploy: compute the golden charts and compare hashes.
  • Panchang boundary convergence iterations and residual seconds, the early warning for a precision regression.
  • Response size and layer count, to catch accidental payload growth.
  • One trace per request, with a span per binder call and per engine.

17. Delivery phases

Phase Content Exit criterion
0 Profiles, rulebook v1, chart core, Vimshottari, D9, /v1/charts, capabilities Golden natal fixture passes for the Vedic profile; cusp profiles return signs, degrees and cusps, house placement unsupported until gap 3 closes
1 Sixteen vargas, dignity and combustion, strength inputs, cached responses Varga fixtures match reference software for all sixteen charts
2 Binder rise/set, panchang day with boundaries, vara, ashtakavarga Panchang matches a published almanac for 30 consecutive days in two cities
3 Transits, Sade Sati, matching, muhurta packs (needs binder timeseries) Scan latency inside section 14; matching fixtures reviewed
4 KP profile (sub-lords, significators), yogas, western synastry, cusp placement from the binder Profile-specific fixtures and a documented convention register

Phase 2 is gated on the binder, not the composer: panchang cannot start until POST /v1/rise-set exists. Phase 3 is gated on batch or timeseries reads. Both are proposed in section 6.2 so the dependency is visible before either team plans a release.

18. Risks and open questions

Risk Impact Mitigation
Rule tables diverge from the reference software users compare against Trust loss on day one Fixture set agreed before phase 0; convention register in research
Variant schemes (D10, D30, koota exceptions) chosen implicitly Two charts, one input Every variant is a named rulebook entry, never a code path
Binder semantic duplication creeps in Sign and house disagreement Review rule: no floor(longitude / 30) in the composer
Rise/set slips Panchang and muhurta blocked Shared roadmap item with an explicit owner
Scan traffic overwhelms the binder Latency collapse Quotas, window caps, batching before phase 3
Copyleft contamination from prior art Legal exposure Clean-room tables, legal review, fixtures not code
Rulebook edits without a version bump Irreproducible charts Content hash is the identity; fixture diff blocks the merge
Snapshot recompute on read Audit trail lost Immutable revisions; recompute is explicit

Open questions

  1. Which implementation is authoritative when two references disagree — Jagannatha Hora, PyJHora behaviour, a commercial API, or a named edition of BPHS?
  2. Mean or true node per profile? The choice moves nakshatra boundaries and dasha balances.
  3. Dasha year length: 365.25 days, 360, or 365.2425? Configurable per profile, but the product default needs a decision.
  4. Is western astrology in scope at all, or is the third profile a placeholder?
  5. Topocentric or geocentric positions for panchang and muhurta?
  6. Who owns the rise/set endpoint — binder roadmap or a joint milestone?
  7. Should allowPartial exist in v1, or is it a foot-gun better added when a real caller needs it?

19. References