Ephemeris4 MIN READ11 / 31

Drift Validator — 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 drift validator recomputes sampled cases with Swiss Ephemeris and JPL Horizons, then alerts if the primary engine drifts from it. It never runs on a request path.

The comparison and batch-run boundary is implemented in the separate @astrology/drift-validator package. It accepts injected adapters and persistence/alert/breaker hooks. Aggregates and streaks are per run; daily scheduling, concrete stores/alerters, worker isolation and a live Horizons adapter remain host integrations. TT is checked from provider declarations rather than forced through the current UTC-instant adapter port. The design below describes the broader target.

1. Purpose

Swiss Ephemeris is the source of record, so its mistakes stay invisible without a second opinion. Drift enters through engine upgrades, different file sets, wrong time-scale or ΔT assumptions, or changed Horizons responses. The validator reports it daily and alerts.

It also proves the Horizons fallback works before anyone needs it.

2. Where it sits

scheduler · live samples · backfill
              │
              ▼
     drift validator ──▶ drift store
              │
              ▼
          alerter ──▶ on-call
  • Reads: its scheduler and a sample of live calculation metadata.
  • Calls: the Swiss worker and Horizons adapter via the service ports.
  • Reuses: the canonical normalizer to align both sides.
  • Never: changes a response, switches a provider, retries in a hot loop.

3. Responsibilities

Component Owns Notes
Triggers Periodic sweep, live-sample capture, manual backfill Backfill covers post-upgrade checks
Case selector Deterministic body × instant × epoch bucket sampling Covers file boundaries and historical/future dates
Comparator Recomputing the same case with both providers Same TT, apparent, airless, same ayanamsa
Judge Angular delta, thresholds and breach streak Distance recorded but not alerted
Drift store Raw samples and daily aggregates Keyed by engine version
Alerter Paging on-call with body, epoch, delta and engine tag Links to the failing sample

4. Architecture

Drift validator — high-level design Reference design. An asynchronous worker selects comparison cases, recomputes them with Swiss Ephemeris and JPL Horizons, measures per-body deltas, judges them against thresholds and writes samples, aggregates and alerts. It never runs on the request path. Drift Validator — High-Level Design Asynchronous Swiss-versus-Horizons comparison that protects accuracy without touching a caller's request. Triggers 15-min sweep · live samples manual backfill by range Swiss Ephemeris same instant, recomputed local worker JPL Horizons reference values only no houses or ayanamsa Drift validator · async worker Case selector body × instant × epoch bucket deterministic stratification Comparator Swiss primary, Horizons reference same frame · TT · airless Judge planet 1″ · Moon 5″ breach streak, not one sample Drift store samples + daily aggregates keyed by engine version Alerter pages on-call with engine tag body · epoch · delta case delta Off the request path · never blocks a caller. Daily aggregates set the agreement field in /v1/capabilities. dependency module external provider

Diagram: reference design, not deployed behaviour.

The asynchronous worker shares no thread pool, breaker or quota with user traffic. Its only shared code is the normalizer and the two provider adapters.

5. Rules

  1. Compare like with like. Both sides use the same time scale (TT), frame (apparent, ecliptic of date, airless), observer mode and ayanamsa; otherwise the delta measures configuration.
  2. Compare tropical, derive sidereal. Horizons has no native sidereal mode, so both sides use tropical longitudes with the same ayanamsa.
  3. Recompute, do not read the cache. A cached primary result could hide a regression.
  4. A single outlier is not an alert. A breach must persist across a streak or exceed the daily p95.
  5. No birth coordinates in stored samples. Geocentric or offset sites only, per the parent privacy rules.
  6. Failure pauses, it does not storm. If Horizons is down or throttling, the breaker opens and the run is marked incomplete.
  7. Drift never auto-corrects. It reports; a human or a release gate decides.

6. Thresholds

Body class Warning Critical
Sun, planets, Pluto 0.7 arcsec 1.0 arcsec
Moon 3 arcsec 5 arcsec

The Moon band is wider: apparent motion is about 0.55 arcsec per second, so time-scale and ΔT differences dominate. Samples outside the leap-second era record the ΔT difference and are reported as time-scale sensitive, not an ephemeris regression.

7. Edge cases

Case Behaviour
A body outside a provider’s date range Skipped and counted as unsupported; no alert
Unrecognised Horizons table layout Run fails loudly with the API version; no partial aggregates
Engine or file upgrade Old and new engines compared against the same Horizons reference
Moon at perigee or station Same threshold; raw delta recorded
Topocentric live sample Coordinates stripped; re-run geocentric
Breach during a known outage Suppressed while the breaker is open; noted in the run record
No samples for a bucket Coverage gap, not agreement

8. Out of scope

  • Houses, ayanamsa correctness, sidereal definitions: no independent reference exists.
  • User-facing validation or blocking a request (the facade owns degradation).
  • Automatic provider switching or automatic engine rollback.
  • Astrology semantics and chart-level checks.

9. Planned code

Piece Responsibility
drift/CaseSelector Stratified body and epoch sampling
drift/Comparator Dual recomputation through the provider ports
drift/DeltaJudge Wrapped angular delta, thresholds and streak logic
drift/DriftStore Samples, aggregates and coverage gaps
drift/DriftAlerter Paging payload and capability agreement status