Hold the published catalogue to what its host radii, distance errors and Gaia-distance flags come to

Three things the branch added could vanish from the data with every check passing, as the review
showed with guarded ETL mutants: dropping st_rad or st_teff from each planet left 0 of 6 354
carrying their host's radius or temperature, storing Gaia's parallax_error in milliarcseconds
rather than over the parallax changed 431 464 distance readouts, and dropping the flag fetchStars
sets relabelled 8 129 HYG stars placed at Gaia's distance "HYG". The round trip compares decoded
with encoded, and the counts of missing bands and errors do not look at what the values are.

validateExoplanets now requires 90 % of planets to carry their host's radius and temperature
(measured 6 030 and 6 054 of 6 354). validateStars requires the median relative error of the
distances at Gaia's to be under 1 % (measured 0.33 %), at most 1 % of stars to have a parallax
distance with an error of a fifth or more (measured 1 109, 0.24 %; the archive's, which are on the
distance and never ranged, are left out), and at least 6 000 HYG stars flagged at Gaia's distance
(measured 8 129). Figures from an ETL run from cache on this branch.

Each proved by a guarded mutant in a scratch copy of the ETL, run from the cache, failing with its
own message: host radius dropped ("Only 0 of 6354 exoplanets carry their host's radius and 6054 its
temperature"), host temperature dropped ("6030 ... and 0"), the Gaia error left in mas ("The median
error of Gaia's distances is 1.92 %"), the same with the median check waived ("17689 parallax
distances have an error of a fifth or more"), and the flag dropped ("Only 0 HYG stars are flagged
at Gaia's distance"); a no-op edit completed. No ceiling is loosened.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-29 21:20:38 +02:00
co-authored by Claude Opus 5.5
parent 6d81c45f45
commit f82b5e1c74
+55
View File
@@ -28,6 +28,21 @@ function assertCondition(condition: boolean, message: string): void {
*/ */
const MAX_STARS_WITHOUT_BAND = 1_000; const MAX_STARS_WITHOUT_BAND = 1_000;
const MAX_STARS_WITHOUT_DISTANCE_ERROR = 1_000; const MAX_STARS_WITHOUT_DISTANCE_ERROR = 1_000;
/**
* What the errors themselves come to, which the two counts above cannot see: Gaia's parallax_error
* stored in milliarcseconds rather than over the parallax still encodes, decodes and passes both.
* Measured: the median relative error of the stars at Gaia's distance is 0.33 %, and 1 109 parallax
* distances (0.24 % of the stars) have one of a fifth or more, which the card prints as a range.
* The mistake above gives 1.92 % and 17 689.
*/
const MAX_MEDIAN_GAIA_DISTANCE_ERROR = 0.01;
const MAX_RANGED_DISTANCE_SHARE = 0.01;
/**
* HYG stars that keep their own row but sit at Gaia's distance, which fetchStars flags for the card
* to say "HYG, Gaia DR3 distance". Measured 8 129; the flag dropped leaves none, and nothing else
* notices — the other 62 002 carry it from their Gaia row.
*/
const MIN_HYG_STARS_AT_GAIA_DISTANCE = 6_000;
function validateStars(stars: StarRecord[]): void { function validateStars(stars: StarRecord[]): void {
assertCondition(stars.length > 0, 'No stars were produced.'); assertCondition(stars.length > 0, 'No stars were produced.');
@@ -81,6 +96,30 @@ function validateStars(stars: StarRecord[]): void {
); );
console.log(` ${withoutBand} stars with a stand-in magnitude; ${withoutError} distances without a published error.`); console.log(` ${withoutBand} stars with a stand-in magnitude; ${withoutError} distances without a published error.`);
const gaiaErrors = stars
.filter((star) => star.distanceFromGaia && star.distanceError !== undefined)
.map((star) => star.distanceError!)
.sort((a, b) => a - b);
const medianGaiaError = gaiaErrors[Math.floor(gaiaErrors.length / 2)] ?? 0;
assertCondition(
medianGaiaError <= MAX_MEDIAN_GAIA_DISTANCE_ERROR,
`The median error of Gaia's distances is ${(medianGaiaError * 100).toFixed(2)} % (at most ${MAX_MEDIAN_GAIA_DISTANCE_ERROR * 100} % expected) — the parallax errors are no longer relative.`
);
// The archive's errors are on the distance and never printed as a range; see formatDistance.
const ranged = stars.filter((star) => star.source !== 'exoplanet-archive' && (star.distanceError ?? 0) >= 0.2).length;
assertCondition(
ranged <= stars.length * MAX_RANGED_DISTANCE_SHARE,
`${ranged} parallax distances have an error of a fifth or more (at most ${MAX_RANGED_DISTANCE_SHARE * 100} % of stars expected).`
);
const hygAtGaiaDistance = stars.filter((star) => star.source === 'hyg' && star.distanceFromGaia).length;
assertCondition(
hygAtGaiaDistance >= MIN_HYG_STARS_AT_GAIA_DISTANCE,
`Only ${hygAtGaiaDistance} HYG stars are flagged at Gaia's distance (at least ${MIN_HYG_STARS_AT_GAIA_DISTANCE} expected) — fetchStars no longer says whose parallax it placed them by.`
);
console.log(
` Gaia distances a median ${(medianGaiaError * 100).toFixed(2)} % uncertain; ${ranged} parallax distances ranged; ${hygAtGaiaDistance} HYG stars at Gaia's distance.`
);
// Hipparcos's mark on a classification it does not print in full reached the card as "Spectral // Hipparcos's mark on a classification it does not print in full reached the card as "Spectral
// type A0m...", for Sirius and 2 126 other stars, which reads as text the app cut short. // type A0m...", for Sirius and 2 126 other stars, which reads as text the app cut short.
const dotted = stars.filter((star) => star.spectralType.endsWith('...')).length; const dotted = stars.filter((star) => star.spectralType.endsWith('...')).length;
@@ -198,6 +237,14 @@ function validateBodies(bodies: BodyRecord[]): void {
*/ */
const MIN_HOSTED_SHARE = 0.995; const MIN_HOSTED_SHARE = 0.995;
/**
* The share of planets whose host's radius and temperature the archive gives, which is what
* starSurfaceOf draws a host with before deriving one. Measured 6 030 and 6 054 of 6 354 (0.95).
* Both come from the composite table only; a column lost on the way leaves every host derived —
* Proxima 0.105 R☉ instead of 0.141 — and nothing else fails.
*/
const MIN_HOST_SURFACE_SHARE = 0.9;
function validateExoplanets(exoplanets: ExoplanetRecord[], stars: StarRecord[]): void { function validateExoplanets(exoplanets: ExoplanetRecord[], stars: StarRecord[]): void {
assertCondition(exoplanets.length > 0, 'No exoplanets were produced.'); assertCondition(exoplanets.length > 0, 'No exoplanets were produced.');
const starsById = new Map(stars.map((star) => [star.id, star])); const starsById = new Map(stars.map((star) => [star.id, star]));
@@ -237,6 +284,14 @@ function validateExoplanets(exoplanets: ExoplanetRecord[], stars: StarRecord[]):
`Only ${crossReferenced} of ${exoplanets.length} exoplanets have a host star (at least ${MIN_HOSTED_SHARE * 100} % expected) — hosts are no longer being matched or added.` `Only ${crossReferenced} of ${exoplanets.length} exoplanets have a host star (at least ${MIN_HOSTED_SHARE * 100} % expected) — hosts are no longer being matched or added.`
); );
const withRadius = exoplanets.filter((exoplanet) => exoplanet.hostStarRadiusSolar !== undefined).length;
const withTemperature = exoplanets.filter((exoplanet) => exoplanet.hostStarTemperatureK !== undefined).length;
assertCondition(
Math.min(withRadius, withTemperature) >= exoplanets.length * MIN_HOST_SURFACE_SHARE,
`Only ${withRadius} of ${exoplanets.length} exoplanets carry their host's radius and ${withTemperature} its temperature (at least ${MIN_HOST_SURFACE_SHARE * 100} % expected) — st_rad or st_teff is being lost.`
);
console.log(` ${withRadius}/${exoplanets.length} carry their host's radius, ${withTemperature} its temperature.`);
// How many can be propagated at their real rate rather than as if the host were the Sun. // How many can be propagated at their real rate rather than as if the host were the Sun.
const withPeriod = exoplanets.filter((exoplanet) => exoplanet.periodDays !== undefined).length; const withPeriod = exoplanets.filter((exoplanet) => exoplanet.periodDays !== undefined).length;
const withHostMass = exoplanets.filter((exoplanet) => exoplanet.hostStarMassSolar !== undefined).length; const withHostMass = exoplanets.filter((exoplanet) => exoplanet.hostStarMassSolar !== undefined).length;