Merge the solar-system branch, so the star catalogue lands on the sky it now shares

Both branches changed the system view's star, the body card's provenance line, the exoplanet
fetch and the ETL's validators. Resolved by keeping both sides:

- The system view's star is the catalogue's (its own radius and temperature, a limb-darkened
  surface in its colour) and turns like a planet when it is the Sun (the solar branch's IAU pole
  and 25.38-day turn), keyed on SUN_STAR_ID, since the catalogue branch dropped the scene's own
  SOL_STAR_ID. Framing takes the outermost thing drawn (an eccentric orbit's aphelion, from the
  solar branch) and the star's radius for a giant (from the catalogue). The solar branch's comment
  about a halo is dropped: there has been none since #33.
- The card's no-temperature sentence is the catalogue's (the host's luminosity or the orbit's size,
  not "not in the catalogue", which holds for 27 planets) and ends with the solar branch's reason
  why no image is used (a point of light for the 101 imaged planets, none for the rest).
- fetchExoplanets reads the composite table and the distance errors (catalogue) and the imaged
  list (solar); build.ts runs both branches' validators.

The data were regenerated by the full ETL on the merged code, from cache (nothing refetched):
stars.bin, stars-meta.bin, stars-index.json and deepsky.json come out byte for byte the catalogue
branch's, bodies.json the solar branch's, and exoplanets.json the catalogue branch's but for the
imaged flag on 101 planets, WASP-108 b not among them. Unit suite 977 passed, the two branches'
870 and 837 over their shared 730, so no test was lost.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-30 22:07:34 +02:00
co-authored by Claude Opus 5.5
67 changed files with 6791 additions and 593 deletions
+12
View File
@@ -44,6 +44,16 @@ const TAP_URL = `${TAP_BASE_URL}?query=${TAP_QUERY}`;
// silently wrong rather than visibly broken.
const CACHE_FILE = `exoplanet-archive-ps-${createHash('sha1').update(TAP_URL).digest('hex').slice(0, 8)}.csv`;
// The planets the archive flags as detected by imaging (`ima_flag`): 101 of them in September 2026,
// HR 8799's four and 51 Eri b among them, and bet Pic c and eps Ind A b, found by radial velocity
// and imaged since. Asked for on its own, so adding it did not refetch the table above and move
// every other planet to a newer snapshot. A transiting planet is left out: the one flagged,
// WASP-108 b, takes its flag from Bohn et al. 2020, a VLT/SPHERE survey of transiting planets' host
// stars that imaged a 0.35 solar-mass companion 0.124" from its star. The planet goes round in 2.68
// days, 0.04 AU out, 0.15 mas at its 259 pc, and no imager has resolved it.
const IMAGED_URL = `${TAP_BASE_URL}?query=select+pl_name+from+ps+where+default_flag=1+and+ima_flag=1+and+tran_flag=0+order+by+pl_name&format=csv`;
const IMAGED_CACHE_FILE = `exoplanet-archive-imaged-${createHash('sha1').update(IMAGED_URL).digest('hex').slice(0, 8)}.csv`;
/**
* The host's columns from the Planetary Systems Composite table, for the cells a planet's
* default row leaves blank and for the columns that query does not ask for.
@@ -112,6 +122,7 @@ export async function fetchExoplanets(stars?: StarRecord[]): Promise<{ exoplanet
const rows = parseCsvObjects(csv);
const composite = new Map(parseCsvObjects(await fetchTextCached(COMPOSITE_URL, COMPOSITE_CACHE_FILE)).map((row) => [row['pl_name'], row]));
const distanceErrors = new Map(parseCsvObjects(await fetchTextCached(DISTANCE_ERRORS_URL, DISTANCE_ERRORS_CACHE_FILE)).map((row) => [row['pl_name'], row]));
const imaged = new Set(parseCsvObjects(await fetchTextCached(IMAGED_URL, IMAGED_CACHE_FILE)).map((row) => row['pl_name']));
let matched = 0;
// Catalogue stars known only by their Gaia designation, which take the archive's host name —
@@ -188,6 +199,7 @@ export async function fetchExoplanets(stars?: StarRecord[]): Promise<{ exoplanet
radiusEarth: parseOptionalNumber(row['pl_rade']),
massEarth: parseOptionalNumber(row['pl_bmasse']),
discoveryYear: parseOptionalNumber(row['disc_year']),
imaged: imaged.has(row['pl_name']) || undefined,
// The period was already being downloaded and thrown away. With the semi-major axis it
// determines the host's gravitational parameter, so keeping it is the difference between
// propagating a planet at its real rate and pretending every host is the Sun.