Packages7 MIN READ74 / 116

Reading the Chart Composer

Chart Composer turns completed astronomical reads into neutral chart facts. It serves both the Vedic and Western engines; those engines add their own judgments afterward.

If you want to understand one request, start with the facade rather than reading every contract first. You can return to the types as each value enters the flow.

Suggested reading order

Read What to look for
composer/src/composer.ts The entry point: validate context, acquire facts, assemble a snapshot.
contracts/src/context.ts The explicit choices that define a calculation.
context/src/normalize-context.ts Which inputs are rejected and which representations are normalized.
composer/src/operations.ts When positions and houses are actually read.
binder-client/src/positions.ts and houses.ts How a request maps to Binder and how returned facts/evidence survive.
chart-core/src/assemble.ts How already acquired facts become one snapshot.
chart-core/src/frame.ts, placement.ts and rosters.ts Reference selection, body-to-house placement and grouping by house.
evidence/src/identity.ts What makes two calculations have the same identity.

Then read geometry/src/aspects.ts for aspect evaluation, or probes/src/session.ts for bounded reads at several instants.

Each internal module has a focused guide: contracts, context, evidence, Binder bridge, chart core, geometry, probes and facade.

Follow one composition

Suppose the host requests Sun and Moon positions at an explicit instant, in the sidereal Lahiri zodiac, with geocentric positions and local whole-sign houses.

  1. The host initializes Binder with its tested TT resolver, wraps it with createBinderReader, and injects that reader into createChartComposer. TT is Terrestrial Time (TT), the uniform astronomical time scale Binder needs; resolving it is outside Composer.
  2. compose(input) validates the requested conventions. It turns an offset-bearing ISO timestamp into epoch milliseconds and resolves omitted observer elevation to zero metres. It does not resolve a civil timezone or shift any astronomical longitude.
  3. Acquisition checks coarse capabilities, then reads positions once. Although the chart has a local house observer, the geocentric position request has no observer field.
  4. Because a house system was requested, acquisition then reads local houses using the house observer. Binder still owns provider admission, coverage checks, normalization and shared facts.
  5. Pure assembly checks that the completed reads match their expected requests and declarations. Lagna uses the supplied ascendant to select the reference sign.
  6. Whole-sign placement counts forward from that sign. For example, with Pisces as house one, Aries is house two. Cusp systems instead delegate placement to the existing resolveHouse helper.
  7. Bodies retain their requested order; rosters list houses 1–12 and group those body IDs. The snapshot retains position and house evidence separately, records degradation, and hashes the conventions and contributing identities.

See the host example and the Binder integration test. The test uses the real Binder coordination code with controlled providers. The additional native test runs the actual Swiss worker through Composer against standalone official-C fixtures.

Terms that are easy to confuse

Term Meaning here
Position origin Where positions are observed from: Earth’s centre (geocentric) or an explicit local observer (topocentric). See geocentric and topocentric.
House observer The local latitude/longitude used for house geometry. It does not change position origin.
Zodiac / ayanamsa The longitude reference (zodiac). Sidereal requires a named ayanamsa, the offset between tropical and sidereal zero points; Binder has already applied it to returned positions.
Chart frame The reference sign used to count houses: lagna, Moon, Sun, or none.
Coordinate frame The astronomical coordinate system, currently apparent-ecliptic-of-date (see apparent positions and the ecliptic). It is not changed by choosing a Moon chart frame.
Lagna The rising sign used as house one (lagna), resolved from the supplied ascendant.
Cusp A boundary between houses (house cusp). A point exactly on a cusp starts the entering house.
Roster Body IDs grouped into one numbered house (a Composer term, not astrological vocabulary).
Nakshatra / pada A sidereal lunar-mansion sector of 13°20′ (nakshatra) and its quarter (pada). Tropical facts use null.
ARMC Equatorial right ascension of the meridian, in degrees. It is not a zodiacal longitude and must not receive an ayanamsa subtraction.
Evidence The operation’s provider declaration, normalization provenance and routing decision.
Identity A deterministic hash of calculation choices and contributing versions/data, not a protected cache key.

Positions use ecliptic longitude/latitude in degrees, distance in astronomical units, and angular speed in degrees/day. Observer elevation is in metres. Absolute instants and probe steps use milliseconds.

Read a snapshot without guessing

The central result is ChartSnapshot. Its groups answer different questions:

Field What it tells you
context Which instant, observers, bodies and conventions were requested.
frame Which reference sign was chosen for house counting.
bodies Unchanged normalized positions, shared semantics, and one-based house placement.
houses The ordered house rosters.
angles Original supplied cusp/angle geometry; ARMC keeps its equatorial meaning.
binderAspects Original aspects from Binder’s construction-time policy.
evidence Separate contributing position/house sources and their precision/rounding information.
identity Reproducibility hash and contributing implementation/rule versions.
degraded Whether any contributing source declares reduced precision.

Use houseSystem: null and frame: 'none' for the simplest position-only request. That acquires no houses, returns null angles and body houses, and returns an empty roster list.

Null speed means the derivative is unavailable, not that the body is stationary. Zero is a real returned derivative. An absent optional field means that part of the contract was not supplied. Readonly types do not freeze the returned object graph at runtime: consumers must not mutate the retained position, semantic or evidence references.

Understand aspect motion

The geometry package always evaluates the explicit rules passed with a policy ID and version. It does not silently reuse Binder’s construction-time orb defaults. An orb is the allowed deviation from an aspect’s exact angular separation.

Motion policy Derivatives used Typical use
returned-speeds Both supplied speeds Same-chart natal aspects.
fixed-target Source speed; target treated as zero A moving transit against a fixed natal reference.
unknown Unknown derivatives Timeless synastry, unless a technique defines a shared time parameter.

For a source at 0° moving +1°/day and a target at 95° moving +2°/day, the square’s orb is 5°. Using both speeds makes separation increase, so the square is separating. Holding the target fixed makes separation decrease, so it is applying. The target’s stored speed remains +2 in both cases. This distinction is exercised in geometry.test.ts.

Technique-derived points carry their method, version and source IDs. Their speed stays null unless the technique defines a derivative; they need neither a fabricated Binder snapshot nor invented houses.

Understand probe state

A probe session pins one context and varies only the instant. It owns a finite evaluation budget, a serial queue, successful-read reuse and a record of failed instants.

evaluate(instant) supports individual refinement probes. series({ instants }) first admits the complete ordered grid, its minimum step and remaining attempt budget, then acquires the reads. The minimum step applies to series grids; individual refinement probes may be closer together.

Successful reads are reused only inside that session. A failed position attempt still consumes budget and is not retried. A reused read retains its evidence, while its returned cumulative consumed count reflects the session’s current usage.

Cancellation and host deadline signals stop subsequent scheduling. They cannot interrupt native work already executing. Invalid instant/grid inputs can throw synchronously; asynchronous capability or acquisition failures reject the returned promise.

Boundaries and current limits

The host owns civil-time resolution, Binder initialization, TT resolution, persistence and cache protection. Binder owns astronomy, provider routing, normalization and base semantics. Composer owns acquisition coordination and neutral chart assembly. The engines own tradition-specific rules.

Returned Binder numbers are already normalized. Composer preserves those digits and their rounding provenance; it cannot recover raw provider precision.

Native Hindu rise/set is implemented through Binder and the optional reader/session port. It uses the explicit house observer and a window of at most 72 hours. Sunset is opt-in. Missing events remain null, including polar cases and a next sunrise outside the window. A session reserves two evaluations for sunrise/next sunrise, or three with sunset. Other horizon definitions remain unsupported. See probe events for the policy.

Verify while reading

From the repository root:

pnpm check:chart-composer
pnpm test:chart-composer

The focused tests cover boundary placement, coordinate admission, motion policies, stable identity, budget enforcement, failure propagation and native-free imports. See the development plan for completed criteria and the remaining event prerequisite.