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:
- chart rules live once, in a versioned, pure engine;
- a chart is a deterministic function of input, profile and rulebook version;
- astronomy stays with the binder: no Swiss Ephemeris, Horizons or other provider call here;
- 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
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
- One binder read set per composition; the facade never re-reads the same instant twice.
- The binder’s
metapasses through by reference, not paraphrase: the chart’s provenance is the binder’s. - A binder
degraded: truemakes the responsedegraded: true, cause preserved. - Binder error codes are mapped, not replaced:
OUT_OF_EPHEMERIS_RANGEsurfaces unchanged, with layering context added. - 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
profileby id; the response echoesprofileandprofileVersion. - The profile pins
rulebookVersion. “Latest” is allowed only as an explicitrulebook: "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
- Validate the request; resolve the profile and one rulebook handle.
- 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.
- Compute the cache key; return a stored response on a hit.
- Call the binder for the instants the layers need, in parallel where independent.
- Run layers in dependency order: chart core, varga, strength and ashtakavarga, dasha, panchang, matching, muhurta.
- Assemble the response: profile, rulebook, layer results, binder
meta,derivedFrom. - 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
- No astronomy. No provider call, ephemeris interpolation, ΔT or sidereal arithmetic.
- 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. - Vargas come from D1, never from a varga. Chaining divisions compounds rounding and disagrees with published tables.
- Whole-sign means whole-sign. House is sign distance from lagna; cusp arithmetic only under a declared cusp system.
- One rounding point. Engines return full precision; rounding happens at serialisation and is
stated in
meta. - Half-open boundaries. A longitude exactly on a sign, nakshatra or tithi boundary belongs to the next interval, as in the binder.
- Unknown input fails. No silent fallback to Lahiri, mean node or the previous rulebook.
- 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"). - Deterministic ordering. Bodies, periods and windows follow a documented order, so responses diff byte for byte.
- Partial only on request.
allowPartial: truereturnsdegraded: truewithmissingLayers; otherwise a missing layer fails. - 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. - 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
- Which implementation is authoritative when two references disagree — Jagannatha Hora, PyJHora behaviour, a commercial API, or a named edition of BPHS?
- Mean or true node per profile? The choice moves nakshatra boundaries and dasha balances.
- Dasha year length: 365.25 days, 360, or 365.2425? Configurable per profile, but the product default needs a decision.
- Is western astrology in scope at all, or is the third profile a placeholder?
- Topocentric or geocentric positions for panchang and muhurta?
- Who owns the rise/set endpoint — binder roadmap or a joint milestone?
- Should
allowPartialexist in v1, or is it a foot-gun better added when a real caller needs it?
19. References
- Astro Binder — high-level design
- Astrology semantics — the boundary the composer must not cross
- Canonical normalizer — rounding and provenance rules the composer inherits
- Domain research, convention register and validation plan: research.md
- Engine designs: chart core, varga engine, dasha engine, panchang engine, transit engine, matching engine
- Swiss Ephemeris programming interface, rise/set and houses: https://www.astro.com/swisseph/swephprg.htm