Ephemeris4 MIN READ09 / 31

Canonical Normalizer — High-Level Design

Reading guide: The current package architecture defines the binder boundary. Sections below describe reference designs unless an implementation note says otherwise. Standalone facade, cache and provenance services are dropped; their integration responsibilities belong to the consumer.

Status: Draft for review · Part of: Astro Binder · Updated: 2026-10-24

The normalizer converts one provider answer into the canonical contract: canonical identifiers, units and frame, exactly one zodiac conversion, one rounding point and a provenance block. It is pure code, with no I/O, no provider choice and no astrology.

The workspace implementation now normalizes the shared adapter contract and provides an injected adapter wrapper. It validates declarations, wraps angles, rounds output once and records provenance. Raw-unit/frame transforms and normalizer-side ayanamsa calculation below remain planned; current adapters already return canonical units/frame and apply sidereal offsets themselves.

1. Purpose

Swiss Ephemeris returns typed arrays with flags; Horizons returns a text table inside JSON. Neither shape may leak past the provider edge, and neither means the same thing by default. The normalizer resolves them into one shape and one meaning for every downstream calculation.

2. Where it sits

provider adapter ──▶ canonical normalizer ──▶ astrology semantics
  • Upstream: the Swiss or NASA adapter returns raw values plus a declaration of frame, units and time scales.
  • Downstream: semantics derives signs and nakshatras (lunar mansions); the facade assembles the response.
  • Never: selects a provider, caches, retries or reads the clock.

3. Responsibilities

Stage Owns Refuses to do
Admit Provider status and error mapping, required fields, NaN/range checks Coerce a missing value into zero
Map identifiers Provider body ids → canonical ids; km→AU, km/s→AU/day, angles in degrees Guess an unknown body
Resolve frame Provider-declared frame → canonical apparent ecliptic-of-date, airless Mix two frames in one result
Apply zodiac once Provider-native sidereal or binder-side ayanamsa subtraction Subtract an ayanamsa twice
Validate, round, stamp Range checks, single rounding point, precision and provenance Claim precision the engine cannot provide
Shared tables Transform library and precision rules Contain provider-specific branches

4. Architecture

Canonical normalizer — high-level design Reference design. The normalizer admits a provider result, maps identifiers and units, resolves the frame, applies or verifies the zodiac conversion once, validates and rounds, then stamps provenance. It is a pure module with no I/O, provider choice or astrology semantics. Canonical Normalizer — High-Level Design One provider answer in, one canonical result with provenance out. Pure, deterministic, no I/O. Raw provider result Swiss arrays or parsed Horizons declared frame and units Request intent zodiac · ayanamsa · observer requested precision context Canonical normalizer · pure module Admit provider errors → typed errors range · NaN · missing values Map identifiers body ids → canonical ids km→AU · km/s→AU/day Resolve frame declared frame → canonical apparent · ecliptic of date Apply zodiac once native or binder-side records who applied it Validate, round, stamp one rounding point at serialisation engine · files · JD UT/TT · ΔT Transform library obliquity · precession · wrap shared, unit-tested Precision rules engine precision · fallback never over-claim arcsec Canonical result values in canonical units provenance + warnings one ayanamsa application · one rounding point · one provider per result dependency module result

Diagram: reference design, not deployed behaviour.

The two shared cards are deliberate: more than one stage uses frame math and precision rules, so neither may live inside an adapter. Stage order is the contract’s order of operations, not a request sequence.

5. Rules

  1. One conversion per property. Convert provider units to canonical units once; later stages may validate but never re-scale.
  2. One ayanamsa application. Swiss can return sidereal longitudes natively; Horizons cannot. Whoever applies the ayanamsa (the tropical-to-sidereal offset) is recorded in ayanamsaAppliedBy (provider or normalizer), with the ayanamsa value and mode.
  3. One rounding point. Full precision is carried internally; rounding happens at serialisation and is stated in provenance.
  4. Unknown values fail. An unknown body, house system or node type is a typed error, never a silent default.
  5. No silent clamping. A longitude outside 0–360 is wrapped explicitly and recorded; a latitude or distance outside its range is rejected.
  6. One provider per result. The shape carries a single provenance block by construction.
  7. Determinism. Same provider output and engine version give byte-identical canonical values.

6. Edge cases

Case Behaviour
Moshier fallback used Result returned with reduced precisionArcsec and degraded: true
Horizons table column mismatch Provider-response error with the API version; never zero-filled columns
Longitude exactly 360° Normalised to 0° and recorded as a wrap
Speed absent from provider output speed: null; the module does not differentiate numerically
Sidereal request on Horizons Normalizer applies the ayanamsa; houses remain unsupported
Provider returns an unknown body id Typed error naming the adapter, not a dropped body
Cache hit Stored canonical values are returned unchanged; no re-normalisation

7. Out of scope

  • Choosing or falling back between providers (router).
  • Deriving signs, nakshatras, aspects or house placement (semantics).
  • Caching and provenance storage (cache and provenance store).
  • Protocol handling such as HTTP retries, timeouts and response parsing (adapters).

8. Planned code

Piece Responsibility
normalizer/CanonicalNormalizer Stage orchestration and result assembly
normalizer/BodyMapper Provider body ids ↔ canonical ids
normalizer/UnitMapper km, km/s, angles → AU, AU/day, degrees
normalizer/FrameResolver Declared frame → canonical frame
normalizer/ZodiacResolver Native or binder-side ayanamsa, applied-once guard
normalizer/ProvenanceStamper Engine, files, flags, time scales, precision