f6bb2b95847e5b07bd709026716116b1e684a32b
100
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f6bb2b9584 |
Pin the 1972 hand-over to the leap seconds from the later side too
The test named for the hand-over caught the switch moved earlier and passed with it moved up to six months later: both samples round midnight then fall on the polynomial (a step of about 0), the last day of 1971 still reads 42.2485 s, and the 1972-06-30 = 42.184 check only sees a move past June. Such a switch leaves TT - UT up to 0.59 s high through the first half of 1972. The test now also requires 1 January 1972 itself to read the table's 42.184 s, the 10 s TAI - UTC began with plus 32.184. Control: the switch moved to JD_1972 + 120 now fails "hands over from the polynomial to the leap seconds at the start of 1972" only (1 failed, 843 passed of 844); before, the suite passed 841 of 841. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
83dc46416b |
Test an exoplanet's aphelion in the framing radius and a derived surface's white, and give Earth's old TDB error as 8.6 degrees
Two lines the review found unguarded, each now held by a test that fails without it:
- outermostRadiusAu takes an exoplanet's eccentricity as well as a solar-system body's.
|
||
|
|
acc4b928f2 |
Say that Makemake's day is known only to a factor of two, citing both readings
|
||
|
|
4cd08ee324 |
Credit the IAU W on the three moons whose orbits take terms from it, guard Tethys's, and say what Mimas's lock ceiling is
Since
|
||
|
|
48fd6fd0e5 |
Stop telling the reader that the hundred directly imaged exoplanets were never imaged
Every exoplanet card without a map ended "Not an observation — no image of this world exists.",
and
|
||
|
|
c45d9160e3 |
Frame the Sun's system on Eris's aphelion, the furthest it draws, not on the grid ring inside it
The scene framed the grid's outer ring, sized from the largest semi-major axis, and two comments said the ring was "always the wider of the two, by construction". That held until Eris came in: its a = 67.93 AU gives an 80 AU ring, but with e = 0.438 its orbit reaches 97.7 AU, and Eris is 95.5 AU out now. The 12 per cent margin protected the ring, not Eris. The review measured Eris's orbit at 0.982 of the half-width on a 390x844 phone (3.5 px from the edge), 0.987 on 1000x1400, and on a 1000x1000 window Eris's marker at NDC 1.002, off screen on arrival. SystemOrbitsRenderer.gridOuterRadiusAu becomes outermostRadiusAu: the ring, or the largest top-level aphelion a(1 + e) where that runs past it. The scene frames that. Some orbit runs past its ring in 303 of the 1 190 exoplanet systems too (counted on exoplanets.json), and they are framed the same way. The 500 AU ceiling rises to 600: the aphelion needs 508 AU on a 390x844 phone, and 600 holds it with its whole margin down to an aspect of 0.39. The comments are corrected. Measured in the app on :4301 after entering the Sun, Eris's drawn orbit, largest |NDC x| over its 129 vertices (review's figures before): 390x844 camera 507.9 AU 0.804 (0.982) 1000x1400 camera 328.5 AU 0.805 (0.987) 1000x1000 camera 234.7 AU 0.812 (1.004, marker off screen) 950x1000 camera 247.0 AU 0.810 1600x1000 camera 234.7 AU 0.508 (0.627) No orbit vertex of Neptune, Pluto, Eris, Haumea or Makemake is off screen at any of them. The cost: inner bodies arrive smaller, the landscape camera 235 AU out instead of 192. Tests: renderer 'reaches as far as an eccentric orbit goes past the grid: Eris's aphelion, 97.7 AU, not the 80 AU ring' (and the ring where every orbit stays inside it), and framing 'leaves Eris's aphelion its whole margin in every window shape'. Controls, each failing its named test only (1 failed, 839 passed): framing on the ring alone; the ceiling back at 500 AU. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
3453cd5d9f |
Say which cards give how far their orbit strays, since Pluto's does not
The date field's description read "Each moon's and dwarf planet's card says how far its orbit strays from 1950 to 2100." Pluto is a dwarf planet on its card, but it moves on Standish's planet elements, and the ETL measures only moons and the SBDB bodies against Horizons: its card ends "Orbit: JPL approximate mean elements (Standish), fit for 3000 BC to AD 3000." and gives no stray figure. In bodies.json, Ceres (7.2), Eris (0.1), Haumea (0.4), Makemake (0.3) and every moon carry one; Pluto does not. The description now reads "AD 1 to AD 3000, where the planets' and Pluto's elements hold. Each moon's card, and Ceres's, Eris's, Haumea's and Makemake's, says how far its orbit strays from 1950 to 2100." The CLOCK_WINDOW comment and the scene's note comment make the same distinction. Test: hud-dock 'opens the date field on the clock's date...' asserts the new sentence. Control: putting the old sentence back fails that test only (1 failed, 836 passed). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
85cb66e63c |
Test that a photograph shows in its own colours once it reaches its body
Since
|
||
|
|
20c34f9662 |
Test the 1972 hand-over to the leap seconds where it happens, and give the ΔT comment its measured joins
The continuity test sampled 1972 at 2451544.5 + (1972 - 2000) x 365.2425 +/- 0.01 d, which is JD
2441317.70 and .72; the switch is at the calendar's 1 January 1972, JD 2441317.5, so both samples
read the leap-second table (42.184 and 42.184) and the join was never compared. Moving the switch
two years early (a 1.99 s step in 1970) passed all 836 tests. The hand-over now has its own test:
the step across midnight must be under 0.1 s (it is 0.067: 42.2514 to 42.184), and the last day of
1971 must still read the polynomial's 42.2485 s. Control: the switch at JD_1972 - 730 fails that
test only (1 failed, 836 passed).
The polynomials' own joins, measured 1e-6 d either side: 0.251 s at 1600, 0.162 at 1700, 0.087 at
500, 0.088 at 1900, and under 0.06 elsewhere.
|
||
|
|
cbe4908219 |
Turn Earth by the Earth Rotation Angle, so its lit face stays Horizons' at AD 1 as it is today
Earth was turned by its IAU W, taken at the clock's UT plus today's 69.184 s. That W is a straight
line fitted to the present: 360.9856235 degrees a day, which, once its pole's -0.641 degrees a
century in right ascension is counted, runs 6.3e-6 degrees a day slow of Earth's real turning. The
followsUt comment said the clock's date "already says how far it has turned"; it did not. Against
Horizons (observer quantity 14 from the Sun, TIME_TYPE=UT, Earth one light-time back) the drawn
sub-solar point was 2.3 degrees off at AD 1000 and 4.5 at AD 1.
Earth is now turned by the IERS Earth Rotation Angle (IERS Conventions 2010, eq. 5.15) at the
clock's date, counted from the node the IAU's W starts at, 90 degrees past the pole's right
ascension. The pole is unchanged. Drawn minus Horizons, in degrees:
date before after
2025-06-01 12:00 (unit) +0.06 -0.003
AD 1000, JD 2086455 (unit) -2.3 -0.001
AD 1, JD 1721600 (unit) -4.5 +0.051
live app, :4301, same probe as the review's
JD 2460900.25 +0.089 +0.005
JD 2086300.5 -2.281 +0.010
JD 1800000 -4.049 +0.056
JD 1721450.75 -4.530 +0.072
At noon UTC on 1 June 2025 the Sun now stands over 0.52 W on the drawn sphere, where the equation
of time puts it at 0.53 W (0.43 W before).
TT_MINUS_UTC_DAYS had no other use and is removed; its comment also counted 37 leap seconds where
UTC has taken 27 on top of the 10 s it started from in 1972.
Tests: body-orientation.spec 'lights Earth's face where Horizons does at the far end of the clock
too: AD 1000 and AD 1' (within 0.15 degrees), and the renderer's AD 1000 Earth test now checks the
drawn face against Horizons instead of against the IAU W the old code used. Control: turning Earth
by its IAU W at UT + 69.184 s again fails both named tests (2 failed, 834 passed). The README says
which model turns Earth.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
||
|
|
dc7277f740 |
Say in the textures README that the mission mosaics' brightness is not albedo, as Iapetus shows
Iapetus's leading hemisphere has an albedo of 0.03-0.05 and its trailing one 0.5-0.6, about a tenth. On iapetus.jpg, between 30 S and 30 N, the leading side (30-150 W) averages 83.4 of 255 and the trailing (30-150 E) 108.2, a ratio of 0.77, measured here again; the USGS source gives the same (83.3 and 108.1), so it is the mosaics' frame-by-frame contrast stretch, not the processing. The README's Iapetus row checked only where Cassini Regio lies, and nothing said the brightness is not albedo; it now does, with those figures. The map is not rescaled: no photometric model was applied to any body, and one hemisphere's worth of scaling would be invented for this one. Documentation only; no behaviour changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
d93bb1ee2c |
Say that Pluto's tilt is checked against its own IAU pole, not against Horizons
The ETL's obliquity check, its failure message and the renderer spec's test name all compared Pluto's drawn tilt with "the obliquity Horizons gives", as |
||
|
|
98c4eb901f |
Credit the sources the solar system's orbits now come from, in the README and a search comment
Since |
||
|
|
a281f22f34 |
Measure every moon and dwarf planet against Horizons from 1950 to 2100, and say on its card how far it strays
The clock reaches AD 1 to AD 3000, but only the planets' cards named a span their elements hold
over; the 25 moons and the four SBDB dwarf planets gave a source and an epoch, though
BodyRecord.orbitSource is documented as "the span they hold over". And the worst offsets the ETL
stated came from twelve New Year's Days: Nereid's year is 360 days, so all twelve fell far from
its periapsis, where a mean ellipse is furthest out. The one date the ETL checked, 2025-01-01, saw
Nereid at 2.6 degrees; it reaches 11.19.
For each moon and each SBDB dwarf planet the ETL now fetches Horizons' ICRF vectors from 1950 to
2100, every other day (daily for Nereid, at an eccentricity of 0.75, and Hyperion, whose row's
eccentricity is a quarter of its real one: every other day gave it 22.14, daily 22.23), and
measures how far the mean elements stray, at the same TDB dates. The card appends it: "JPL SBDB
osculating elements, epoch 2026 Jun 9, within 7.2 degrees of Horizons from 1950 to 2100". Worst
offsets on the real catalogue: the Moon 2.62 (2010 March 27), Phoebe 2.58 (1969, where a comment
claimed "within 2.0"), Phobos 1.26, Mimas 7.43, Iapetus 10.34, Nereid 11.19 (2039 Nov 1),
Hyperion 22.23 (2055 Feb 26), Ceres 7.12 (1953); Io 0.07, Titan 0.06, Eris 0.06.
build.ts recomputes each from the same Horizons positions and fails if an orbit other than
Standish's names no span, if a card states less than it strays, or if a body passes its ceiling:
3 degrees, and Hyperion 23, Nereid 12, Iapetus 11, Mimas 8 and Ceres 8, each explained. The
2025-01-01 check stays for reading errors, its comment no longer passing one date's offsets off as
worst ones. The Sun's note says the moons' and those four's elements were checked from 1950 to
2100, and the date field's description that each card says how far its orbit strays over that span.
In the running app Ceres's, Phobos's and Nereid's cards end "within 7.2", "1.3" and "11.2 degrees
of Horizons from 1950 to 2100".
The CLOCK_WINDOW comment also had the calendars the wrong way at AD 1: proleptic Gregorian dates
are two days behind the Julian calendar there, level from AD 200 to 300, and ten days ahead by
1582. It now says so, and names Ceres's drift where it named Phobos's, which its orbit now carries.
Controls: the ETL measuring nothing fails ("Ceres's orbit ... names no span it holds over"),
rounding the stated figure down fails on Ceres (7.1 against 7.12), and Nereid held to the general
ceiling fails at 11.19; the note and the date field without the span fail their named tests.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
||
|
|
2d4b8a98d5 |
Put each body's photograph on it one frame at a time, so entering the Sun's system no longer stalls
buildMarker gave every photographed body its map at once. A texture is copied to the GPU in the first frame that draws it, and the 28 maps arrive within about 40 ms of each other, so that frame copied some 20 megapixels of JPEG (seven maps at 2048x1024) through copyExternalImageToTexture: a second long task of 135-162 ms about 1.25 s after entering, measured here four times on the committed renderer (reviewers measured 160-210 against 85-100 without the 18 new maps). de34fff's "adds no long task" was measured before those maps landed. A photographed body now starts in its kind's flat colour, as a derived one does, and its texture waits in a queue; each update() puts the first one that has loaded on its body. The copies are spread one a frame, and all 38 bodies have their maps within half a second of the first. In the running app, five fresh entries into the Sun's system at 1600x1000 left one long task of 52-66 ms or none at all ([66], [52], [62], [], [56] ms, where the committed renderer gave [62, 149], [56, 135], [74, 162], [78, 162]). Control: putting every loaded photograph on in one frame fails "puts them on their bodies once loaded, one a frame". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
6c0626ca38 |
Frame the Sun's system out to Eris on a portrait window, where Eris and Makemake arrived off screen
The arrival framing fits the grid's outer ring, which Eris (a = 67.93 AU) took from 40 AU to 80, but its 200 AU ceiling was sized for Pluto's ring. At 390 by 844 the ring needs 416 AU and at 1000 by 1400 269, so both were clamped to 200: Eris arrived at NDC (2.08, 0.48) on the phone, with Makemake at (-1.14, -0.25), and at (1.34, 0.48) on the tall window. The spec never saw it, its solar system ending at Neptune. The ceiling is now 500 AU, which frames the 80 AU ring at any aspect down to 0.385; the landscape fit is unchanged. The window-shape test now includes the solar system out to Eris and a 390 by 844 phone. In the running app every top-level body is on screen on arrival: the camera at 415.8 AU on 390x844, 269.0 on 1000x1400 and 192.1 on 1600x1000. Control: the ceiling back at 200 fails "leaves the outermost ring clear of the frame edge at every scale and window shape". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
8c4f1c11c9 |
Build a body still waiting for its surface with a null map, so three stops warning on each one
Since
|
||
|
|
036af5f02d |
Test what the drawn solar system claims at far dates and on Saturn's ring
Three claims had no test that fails without them: - Standish's rates for a, e and i. The frozen Horizons vectors run from 1950 to 2100, where dropping them moves a planet at most 0.036 degrees (Saturn in 2100), inside every ceiling; the clock runs to AD 3000, and the long span is what those rates are for. Two vectors from Horizons (DE441) for 3000-01-01 now join the table: the Earth-Moon barycentre, 0.005 degrees out (0.129 without the rates), and Saturn's, 0.065 (0.412). - A planet's orbit line turned each tick with its node and periapsis: only the Moon's and Pluto's were tested. Mars must stay on its own line 730 000 days before J2000; on a line left at J2000 it is 3.3 million km from it at AD 1. - Saturn's ring lit and drawn from both faces, which dc20acc's title claims and the tests, reading only its geometry and picking through its front face, never checked. Controls: the three rates dropped fails "puts earth within 0.02 degrees of Horizons on JD 2816787.5" (and Saturn's); the top-level line left unturned fails "turns a planet's drawn orbit with its node"; an unlit front-face-only material fails "is lit, and seen from either face". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
56af5e3553 |
Test the clock where CI could not see it: the date field's time zone, a backwards date, a reopened panel
Three behaviours of the clock had no test that would fail without them: - "jumps the clock to the date submitted, read as UTC" only told UTC from local time on a machine outside UTC. CI runs on ubuntu-latest, in UTC, where both readings are the same instant, so a field read as local time passed all 805 tests there. The test now sets TZ to Asia/Kolkata (UTC +5:30) itself, and afterEach unstubs it. - Nothing checked that a negative rate moves the date backwards; the dock's test read only the rate's sign. The store now checks that at -86 400 s/s a second of wall clock is a day earlier. - Nothing checked that reopening the Display panel fills the date field with the clock's date, rather than the one it held when the dock was built. Controls, the suite run under TZ=UTC: the field read as local time fails "jumps the clock to the date submitted, read as UTC"; the rate's size taken without its sign fails "runs the date backwards at a negative rate"; toggleTab not refilling the field fails "fills the date field again with the clock's date when the panel is opened again". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
34084c0eec |
Test the body page itself: its ring in Saturn's equator, and each body turned for the map's date
No spec mounted BodyDetailSceneComponent, so the two things the page was changed for could be undone with all 805 tests passing: putting audit #47's 17-degree lean back on the page's ring, and sending every body back to the slow turn for show instead of bodyPageView. saturnRing's geometry and bodyPageView were each tested alone; how the page wires them was not. body-detail-scene.component.spec.ts mounts the page on a stand-in engine and data loader, the pattern galaxy-system-scene's spec uses, with the page's template cut to its canvas. It opens Saturn and checks that the ring's face normal, read off its geometry through its world matrix, lies on the planet's pole within 1e-6 rad; and it opens Earth with the clock pinned to 2025-06-01 12:00 UTC and checks that the sphere and the light are what bodyPageView gives for that date. Controls: the ring leant 17 degrees fails "lays Saturn's rings in its equator on the page"; the page falling back to its show spin fails "turns Earth on its page as it stands at the map's date". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
ff7525d3b5 |
Stop a running clock at the ends of its window, where setDate already refused to go
Only setDate held the clock to AD 1 - AD 3000; julianDate did not, so a month a second carried it past either end with nothing to stop it. Past AD 3000 it drew the planets on elements Standish never fitted there, under a note naming "AD 3000"; before AD 1, toISOString writes the six-digit years ECMA-262 uses outside 0000-9999, and the note, the date strip and the date field, which cut it at fixed places, read "... to -000001-10-05 20 UTC.", "-000001-10" and an empty field. julianDate now stops the clock at the end it ran into: re-anchored there, at real time turned back into the window (forwards at AD 1, backwards at AD 3000), as if the reader had set that date. In the running app, run backwards from 0001-01-10 at a month a second for 20 s, the note reads "to 0001-01-01 00:00 UTC." and the strip "0001-01-01", the clock 19.7 s into AD 1 at real time; run on from 2999-12-01 for 8 s, "to 2999-12-31 23:59 UTC." at real time backwards. Control: julianDate unheld fails "stops a running clock at either end of the window". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
97b8dd9dc8 |
Turn the Sun about its IAU pole, once in 25.38 days, as every planet already is
Every body with IAU elements was turned by its pole and W, but the Sun, which is the system's star marker and no BodyRecord, was built with an identity rotation and never touched: its pole pointed at RA 90, Dec 0, 115.03 degrees from the WGCCRE 2015 solar pole (RA 286.13, Dec 63.87), and it stood still where its W turns 14.1844 degrees a day. SUN_ROTATIONAL_ELEMENTS carries NAIF body 10 from pck00011.tpc, and the ETL fails if they are not the kernel's. The scene turns the star marker by bodyOrientation each tick when the star is the Sun, as the renderer turns the planets. In the running app the Sun's drawn pole lies on the IAU's (0.00 degrees) and its map turns 14.1844 degrees between 2026-01-01 and 01-02. The map's longitudes are Solar System Scope's, not Carrington's, so the phase of W is not the Sun's own; the pole and the rate are. Controls: leaving the marker unturned fails "turns the Sun about its IAU pole, once in 25.38 days"; the app's elements off the kernel's (W rate 14.18) fails the ETL. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
8e3a494fe9 |
Print Hyperion's eccentricity as JPL measures it now, 0.105, not the archived row's 0.023
The card listed Hyperion's eccentricity under "Measured" as 0.023: the archived JPL satellite row
the orbit is drawn from gives 0.0232. JPL's current table (SAT441) gives 0.105, and Horizons'
osculating orbit ranges 0.074 to 0.132 from 1980 to 2100 (0.1099 on 2025-01-01). The row stays
the orbit: with 0.105 put into it, Hyperion is further from Horizons, not nearer (median 9.6
degrees against 7.8 over 1980-2100, as a reviewer measured), so only the card changes.
BodyRecord.measuredEccentricity carries the figure the card prints where it is not the orbit's
own; the ETL sets it for Hyperion, and buildBodyViewModel prints it. build.ts now checks every
card's eccentricity against Horizons' osculating one on 2025-01-01, within 0.03: measured at most
0.0151 (Phoebe, and the Moon, whose eccentricity swings) once Hyperion prints 0.105, where the
row put it 0.0867 out. The live Hyperion page reads "ECCENTRICITY 0.105".
Controls: the ETL with Hyperion on its row's figure fails ("Hyperion's card gives an eccentricity of
0.0232, where Horizons' osculating orbit has 0.1099"); the view model ignoring the field fails
"prints the eccentricity measured for a moon whose orbit keeps an older one".
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
||
|
|
f655a4c0f7 |
Turn Eris once in 15.77 days, locked to Dysnomia, not in the 25.9 hours the SBDB flags as unreliable
Eris took its day from the SBDB's rot_per, 25.9 hours, whose own note reads "Result based on less than full coverage, so that the period may be wrong by 30 percent or so" (Roe et al. 2008). Eris is locked to Dysnomia: its light curve repeats every 15.771 +/- 0.008 days (Bernstein et al. 2023, PSJ 4, 115), Dysnomia's 15.78590-day orbit (Holler et al. 2021; Szakáts et al. 2023, A&A 669, L3). It was drawn turning 14.6 times too fast. Eris's BodySpec now carries that day, 378.504 hours, cited as its radius already cites Sicardy et al., and a spec's measured day comes before its source's. build.ts checks that Eris's day is Dysnomia's orbit within 0.2 per cent. In the running app Eris turns 5.707 degrees in six hours, as 15.771 days gives; on 25.9 hours it turned 83.4. Makemake's SBDB period, 22.83 hours, carries the same flag; it is Hromakina et al. 2019's own result and nothing later overturns it, so it is kept. Control: Eris on the SBDB's period fails the ETL: "Eris turns once in 1.079 days". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
86d97b903e |
Print Earth's inclination as 0.00 degrees, not -0.00
Standish's Table 2a fits the Earth-Moon barycentre's inclination as -0.00054346 degrees, and the card printed toFixed(2) of it: "Inclination -0.00°", where the branch's base read 0.00. A negative inclination is the same orbit as its size with the node turned half round, so the card prints the size. The elements the map propagates are left as Standish gives them. The live Earth page now reads "INCLINATION 0.00°". Control: printing the fitted sign again fails "prints the size of an inclination fitted below zero". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
395b613f82 |
Stop telling the reader that the moons drawn without a map were never imaged
provenanceFor ended every derived surface with "Not an observation — no image of this world exists", a sentence written for exoplanets. The branch added eleven solar-system bodies with no map in the catalogue, and eight of them are moons spacecraft photographed: Voyager 2 imaged Miranda, Ariel, Umbriel, Titania, Oberon, Proteus and Nereid, Cassini Hyperion (26 Sep 2005, from about 500 km). The textures README says so itself. Hubble sees Eris, Haumea and Makemake too, as points. Only an exoplanet now gets that sentence. A moon or dwarf planet drawn from its measurements says "no global map of this world is used here", which is true of all eleven. Read off the live pages: Titania, Hyperion and Eris end with it, and an exoplanet keeps the old wording. Controls: giving every derived surface the exoplanets' sentence fails "says a moon without a map is illustrated, without saying it was never imaged"; giving it to none fails "says an exoplanet has never been imaged". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
87e9ec274b |
Turn Venus's map north up, so Maxwell Montes is drawn in the north where the IAU puts it
venus.jpg, from the Solar System Scope pack, is the Magellan radar map turned half round: south up and east to the left. Its brightest feature north or south of 50 degrees, Maxwell Montes, sat at 63.4 S, 8.9 W (blurred at sigma 3), with Lakshmi Planum east of it; the IAU Gazetteer puts Maxwell at 65.2 N, 3.3 E, at Lakshmi's eastern end. The IAU pole and W are right (Venus's sub-Earth longitude matched Horizons to the thousandth), and so is MAP_TO_BODY, which Earth, Mars, the Moon and Mercury were checked against; the file was not, and its surface was drawn turned 180 degrees about the prime meridian's axis. Turned back with PIL's ROTATE_180 and re-saved on the file's own quantisation tables (0.03 grey levels from the exact turn, 240 079 bytes), its brightest point is 63.7 N, 8.3 E with Lakshmi to the west, and the dev server serves that file. The textures README records the check, the MAP_TO_BODY comment adds Venus to the maps it names, and texture-catalog.spec.ts pins the checked file's SHA-256, since no image decoder runs in the unit suite (a triple-slash reference gives that one spec Node's types). Control: the pack's file put back fails "wraps Venus in the map turned north up". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
34ae803c06 |
Take TT - UT from the historical record before 1972, so the far dates the clock reaches turn every body by the right amount
The clock reaches AD 1, but TT - UT was held at today's 69.184 s. At AD 1000 it was 1 574 s and at AD 1 about 10 570 (Espenak and Meeus, NASA's Five Millennium Canon; Horizons' TDB - UT gives 1 658 and 10 466 on JD 2086455 and 1721600). So every spin but Earth's was (ΔT - 69 s) times its rate out, Jupiter 15.2 degrees at AD 1000 and 106 at AD 1, Mars 6 and 43, and every orbit that much behind: the Moon about 0.2 and 1.4 degrees. ttMinusUtSeconds gives TT - UT for a date on the clock: the Espenak-Meeus polynomials before 1972, 32.184 s plus UTC's leap seconds from 1972 to the last one, at the start of 2017, and 69.184 s held after it, as Horizons holds it. Its pieces join within 0.1 s. tdbFromUtc, which positions and spins already share, now adds it. Within 0.2 s of Horizons in 1950, 105 s at AD 1 and 86 s at AD 1000, where the historical record itself is that uncertain. Earth is the exception: its turning is what UT counts, so the clock's date already says how far it has turned, and ΔT would turn it again, 44 degrees at AD 1. Its W, fitted to today, keeps today's 69.184 s (bodyOrientation's followsUt, set for Earth in the system view and on its page). In the running app at 1000-01-01 00:00 UT, Jupiter's drawn prime meridian sits 0.000 degrees from its IAU W at TT and 15.164 from where the held offset put it; Earth's sits on its W at UT + 69.184 s, 6.288 degrees short of what TT would have turned it to. The renderer spec now hands its frozen Horizons vectors over as the UT dates that name them through the same TT - UT, and checks Jupiter's and Earth's prime meridians at AD 1000. Controls: the leap-second rule used before 1972 fails "follows the historical record before 1972"; TT - UT held at 69 s fails "turns Jupiter at AD 1000 by its W"; Earth turned at TDB, or the renderer or the page not keeping it on UT, fails the Earth tests. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
d4808788ec |
Take the orbits at TDB as the spins already were, so a locked moon faces the planet it is drawn round
Every element set here runs on TDB: Standish's T_eph, the SSD satellite and SBDB epochs, the IAU's d and T. bodyOrientation already took the clock's UTC to TDB, but SystemOrbitsRenderer.update and the body page's heliocentricPosition fed the UTC date straight to meanElementsAt, so in one frame each body's place was 69.184 s behind its spin. That is n x 69 s of orbit: Phobos 0.90 degrees, Mimas 0.31, Deimos 0.23, Enceladus 0.21, Miranda 0.20, Io 0.16, Tethys 0.15, Europa 0.08, the Moon 0.011. 48319c3's table measured the app at a UTC date against Horizons at the same number read as TDB, which hid it, and its "nothing for anything else" was wrong: Io's 0.16 is four to five times Io's worst model error there (0.035). tdbFromUtc, in constants.ts, is now the one conversion, and positions and spins both go through it. In the running app, clock pinned to 2025-06-01 12:00 UTC, Io's face towards Jupiter is at 0.024 E, latitude -0.009, where Horizons (observer quantity 14 from Jupiter's centre) has 0.036 E and -0.003: 0.012 degrees apart, where it was 0.175. The renderer spec checks that point, and now hands its frozen Horizons vectors, which are TDB, to update() as the UTC dates that name them, 69.184 s earlier; the same frozen rows fed at the UTC date fail for Io and Europa. A body-page test checks that the Sun lights the point it stands over at the same TDB instant the body is turned for. Controls: taking the renderer's orbits at the clock's UTC fails "faces jupiter and the Sun with the points Horizons gives on io"; taking the page's Sun there fails "takes the Sun where it stands at the same TDB instant". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
f8ee3ac3ab |
Move Mimas, Tethys and Phobos along their orbits by the terms their IAU W already carried
The IAU W of a locked moon follows its mean longitude, so a term of W that is the moon running
ahead of and behind its mean motion is its orbit's too. Two were in bodies.json's W and in no
orbit: the 71-year libration of the Mimas-Tethys 4:2 resonance, -44.85 degrees on Mimas and
+2.23 on Tethys on the angle S5 = 316.45 + 506.2 T of pck00011.tpc, and Phobos's tidal
quadratic, 9.536e-9 degrees a day squared about J2000. JPL's satellite table has a column for
neither. orbitalTermsOfPrimeMeridian now turns each into the row's meanAnomalyTerms about the
row's own epoch (Phobos's 1950 row gets the quadratic re-centred, which adds to its mean motion
and mean anomaly at the epoch), and the ETL takes them for the three moons named in their specs.
Against Horizons: Mimas on 2026 May 27, near the libration's extreme, 2.24 degrees instead of
43.3; Tethys the same day 0.18 instead of 2.05; Phobos in 2100 1.25 instead of 11.1. On the
ETL's 2025-01-01 check Mimas is 1.56 degrees, so its named 46-degree ceiling is gone. The renderer
spec freezes the Mimas and Phobos vectors.
The day-equals-orbit check checked a number that turns no locked moon: since
|
||
|
|
db3af1a820 |
Wrap Deimos in Stooke's Viking map, once its longitudes were settled on the body
Audit #40. deimos.jpg was a 592x592 disc photograph, 32.6% black sky, left in the folder
unlisted by
|
||
|
|
ac6a3bb1ea |
Let the reader set the clock to a date, and run it backwards
The clock from #33 could only run forwards from now. The Display panel's clock now has a Date (UTC) field, a native datetime-local in a form, so Enter submits it and the browser holds it to its min and max. It jumps the clock to that date, and the clock carries on from there at the rate it was running at. A Backwards toggle (aria-pressed) runs the same four rates the other way. The radios still pick the rate's size and keep the direction when it changes. The window is AD 1 to AD 3000. The end is where Standish's Table 2 stops being fitted (3000 BC to AD 3000; every planet within 0.29 degrees of Horizons at each date measured out to 3000). The start is the date input's own floor. TimeStore.setDate refuses anything outside it, and NaN, and leaves the clock where it was. The field is read as UTC. Its dates are proleptic Gregorian, as a Date is, so before 1582 they run up to ten days ahead of the Julian-calendar dates history gives. The window is written beside CLOCK_WINDOW, with the moons' shorter reach (Phobos 11 degrees out by 2100). The system note now names the date it is drawn for, to the minute: "... to 2020-12-21 18:00 UTC.", or "to now, <date> UTC." at the present. Measured in the app (port 4311, keyboard only: fill, Enter): - Set to 2020-12-21 18:00 UTC, Jupiter and Saturn seen from Earth's drawn position are 0.113 degrees apart. Horizons gives 0.102 geocentric (geometric 0.1017, astrometric 0.1018). Distances: 5.9267 and 10.8296 AU against Horizons' 5.9258 and 10.8270. - 3001-06-01 is refused by the form (validity false) and the clock does not move. - Backwards at 1 d/s: -2.011 days in about 2 s. - The field's accessible name is "Date (UTC)" and its description is the window. Its colour-scheme is dark, so the picker icon shows on the HUD. - Back to now puts the field back on the present too. Unit suite 799 -> 805: two tests for the store, three for the dock, one for the scene note. Eleven guarded mutants; each changed its file and made its named test fail. Among them: the window check dropped, the wall clock not re-anchored on a jump, the radio dropping the direction, the field read as local time, and the note not naming the date. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
dc20accfdf |
Draw Saturn's rings in the system view, lit, at the radii their texture draws
Audit #47. The body page's rings already lie in Saturn's equator (
|
||
|
|
302fa963ad |
Wrap seventeen moons and dwarf planets in the missions' own maps, grey where no probe looked
Audit #40. Io, Titan and Pluto had square disc photographs (40.5, 42.6 and 42.9% black sky) that PR #33 unlisted, and every other moon or dwarf planet fell through to the derived surface. Seventeen of them are now wrapped in public-domain global mosaics from USGS Astrogeology and the NASA PDS: Phobos (Viking), Io, Europa, Ganymede, Callisto (Galileo and Voyager), Mimas, Enceladus, Tethys, Dione, Rhea, Titan, Iapetus, Phoebe (Cassini), Triton (Voyager 2), Ceres (Dawn), Pluto and Charon (New Horizons). io.jpg, titan.jpg and pluto.jpg are replaced by maps under the same names. Every file is simple cylindrical over 360 by 180 degrees, with longitude 0 in the middle and east to the right, the frame MAP_TO_BODY puts on the IAU body frame. Processing: the source's no-data pixels (0 in every band) become one flat grey, the mean of the mapped surface, never invented terrain; area downsampling to 2048x1024 for bodies over 1 000 km in radius and 1024x512 for the rest; half a turn where the source is centred on 180; JPEG q85 (Europa q82). Largest file 386 KB (Europa); 3.5 MB for all seventeen. The centre was read from each GeoTIFF's central meridian and left-edge tie point, not from its label: Rhea's and Enceladus's labels say CENTER_LONGITUDE = 180 over images centred on 0. Taken from the label, Rhea came out half a turn round, which the seam it left down the middle of the map gave away. Each map was then checked by eye against the IAU Gazetteer: Pele and Loki on Io, Pwyll on Europa, Osiris and Tros on Ganymede, Valhalla and Asgard on Callisto, Herschel on Mimas, Ali Baba and Aladdin on Enceladus, Odysseus on Tethys, Creusa on Dione, Inktomi on Rhea, Xanadu, Shangri-La and Belet on Titan, Cassini Regio on Iapetus, Jason on Phoebe, Occator and Haulani on Ceres, Stickney on Phobos, Sputnik Planitia and Cthulhu on Pluto, Mordor Macula on Charon, Leviathan Patera on Triton. Unmapped share, now grey: Triton 38.6%, Charon 34.0%, Pluto 31.9%, Phoebe 20.4%, the Galilean polar gaps 3.6-4.3%, Ceres's south pole 3.6%, the rest under 0.2%. Pixels darker than 8 of 255: at most 0.55% (Charon's Mordor Macula, Pluto's Cthulhu), against the 20-43% black sky of the photographs PR #33 dropped. Left out, and said so in src/assets/textures/README.md: Deimos, whose only cylindrical map (Stooke, Viking) has no label for its longitude direction and on which neither Voltaire nor Swift could be found to settle it; the five Uranian moons, whose only maps (Schenk 2020, USRA) carry no licence; Hyperion, Nereid, Proteus, Eris, Haumea and Makemake, which have no photographic simple-cylindrical map. Source URLs, credits, licences, processing and each measurement are in the new src/assets/textures/README.md; the root README now points there and counts twenty-seven bodies in real photography. texture-catalog.spec.ts checks that the seventeen are registered and that Deimos, the Uranian moons, Hyperion and Eris are not. Unit suite 790 -> 792 tests. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
76fb38665f |
Show each body on its page turned as it is at the map's date, under its real Sun
The body page used to spin every body about its pole at 0.08 radians a second, under a light at (4, 3, 5) whatever the date. A body with IAU elements is now drawn as it is at the map's clock, using the same pole, prime meridian and map convention as the system view. It is shown pole up under a Sun held at the light's old azimuth, so the camera still opens on the day side. The Sun's height above the equator is the real one, and so is the face it lights. The clock's rate now turns the page too: at 1 h/s Earth's sub-solar point moved 15.17 degrees in the 1.012 h of sky one wall second carried. What the page gives up is the stars, which do not turn with the body. bodyPageView in src/app/shared/rendering/body-orientation.ts takes the Sun's direction from where the body is: a planet's own mean elements, a moon's planet's place plus its own offset. It sets the sphere's rotation and the light's direction. Exoplanets, Eris, Haumea and Makemake keep the old slow turn and light. Saturn's rings now lie flat in its equator, the page's horizontal. They used to lean 17 degrees, which put them out of the plane they orbit in. Measured: - Live app, clock pinned to 2025-06-01 12:00 UTC: the Sun stands over 0.433 W, 22.125 N on Earth's page, the same point as on its sphere in the system view. - Unit test, raycast on the page's own sphere: Earth one light-time earlier is 0.09 degrees from Horizons' sub-solar longitude. Its latitude, put on the flattened Earth, is within 0.03. - The Moon's sub-solar point is within 0.004 of Horizons'. Three mutants each fail their named test: a moon lit as if it had no planet; the Sun not held at the page's azimuth; the body left in the ICRF instead of the page's frame. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
cdf474bcd5 |
Turn every body in the system view by its IAU pole and prime meridian, so the lit face is the real one
Until now each body's axis was its orbit normal, tipped by the obliquity about the orbit's node, an azimuth the data never gave. Its phase started at an arbitrary point at the elements' epoch. The rate and the sense were real; the face towards the Sun was not. Now each of the 33 bodies with IAU elements is set, every tick, from its pole and its W at the clock's date. Eris, Haumea and Makemake keep the old fallback: their published period, about their orbit normal. None of them has an obliquity, so the tilt code that only served bodies now turned by the IAU is gone. Exoplanets have no rotation published and stay still, as before. The texture convention is settled once, in src/app/shared/rendering/body-orientation.ts (MAP_TO_BODY): - SphereGeometry runs u eastward about +Y from a seam on -X, so u = 0.5 faces +X. - Every photograph in the catalogue is centred on longitude 0 with east to the right. Checked on the maps: Greenwich; Olympus Mons 134 degrees left of centre; Mare Crisium right and Mare Orientale left; Kuiper just left. - A map labelled in west longitude is still drawn east-right, so where longitude 0 sits is the only question, and for all of them it is the centre. - So a quarter turn about X puts the map on the IAU body frame: pole +Z, prime meridian +X. The scene is already ICRF equatorial (the ecliptic is turned into it by the J2000 obliquity), so the pole goes in as it is. The equator frame is built through laplacePlaneToEquatorial, the same conversion the moons' Laplace planes use; moonFrame now calls it too. The clock is UTC and the elements TDB, so TT - UTC (69.184 s) is added: Earth turns 0.29 degrees in that time, Jupiter 0.70 and Phobos 0.90. Measured on the live app (port 4311), clock pinned to 2025-06-01 12:00 UTC: - The Sun stands over 0.433 W, 22.125 N on Earth's drawn sphere. The equation of time puts it at 0.53 W. - Each body was drawn one light-time earlier and compared with Horizons' observer quantities 14 and 15: - Earth (from the Sun): longitude 0.095 off. - Mars: sub-Earth 0.001, sub-solar 0.004. - Jupiter: sub-Earth 0.005, sub-solar 0.002. - The Moon: sub-solar 0.004; sub-Earth 0.699, which is the error of its mean orbit. - Horizons' latitudes are planetodetic. Raw, they differ by the flattening: Earth 0.14, Mars 0.23-0.27, Jupiter 0.33, the Moon (a sphere) 0.000. The unit tests put the same comparison through real raycasts on the drawn spheres' texture coordinates, with the latitudes put on each body's flattened figure. Every residual is within 0.09 degrees, but for the Moon's sub-Earth point (0.70 and 0.09). The retrograde tests of #33 are rewritten for the IAU's convention: a planet's named pole is the one on the north side, so W runs backwards for Venus and Uranus, while Pluto follows the right-hand rule. The spin read off the drawn sphere, against the drawn orbit's normal, is 177.36 for Venus, 97.77 for Uranus and 119.61 for Pluto, all past 90, and 23.44 for Earth. Each is within 0.5 of Horizons. Six mutants, each failing its named test: the map upside down; UTC taken for TDB (Jupiter's test); W turned the wrong way (the retrograde test, and again Earth's noon test); moons, or planets, not turned by the IAU; and the fallback ignoring a negative period. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
1c86584642 |
Carry every body's IAU rotational elements, read from NAIF's kernel of the 2015 report
bodies.json now holds, for 33 of the 38 bodies, the pole right ascension and declination and the prime meridian W of the IAU WGCCRE 2015 report (Archinal et al. 2018), with their rates and the periodic terms. They are read from NAIF's pck00011.tpc, which carries the report in a form a program can read, periodic terms and their angles included. Hyperion (chaotic), Nereid, Eris, Haumea and Makemake have no model in the report. The parser, src/app/shared/astro/rotational-elements.ts, sits beside the other source readers so the unit suite covers it. It reads data blocks only where \begindata stands alone on a line, as the kernel's own prose mentions the token mid-sentence. It reads the Fortran exponent (the Moon's -1.4D-12 d² term) and the degree-2 angles of the Mars system, where Phobos's tidal acceleration lives. NAIF numbers a small body 2 000 000 past its catalogue number, so Ceres is 2000001. Periodic terms under 0.01 degrees are left out. 0.01 degrees moves a point by 0.11 px on the largest body ever drawn (Jupiter at 641 px of radius). That drops 32 terms: - Mercury: 4 (0.0011 degrees and less) - the Moon: 8 of 13 (0.0072 and less) - Mars: 13 (0.00024 and less); its three 0.42-1.59 degree long-period terms stay - Phobos: 1 (0.0063) - Jupiter: 5 (0.0022 and less) - Europa: 1 (0.009) Kept, among others: Mimas's 44.85-degree libration, Triton's 32-degree precession, Miranda's 4.4 and Phobos's 1.14-degree libration. build.ts now checks the elements against Horizons on the real catalogue: - Every body but those five carries elements, and they do not. - The IAU day, 360 over W's rate, is within 1e-4 of Horizons' period. Measured: at most 1.8e-5 (Jupiter). Neptune gets a 0.01 ceiling: 0.89 per cent, because the report takes Karkoschka's 15.9663 h where Horizons keeps Voyager's 16.11. - The spin axis, the pole turned end for end where W runs backwards, is within 0.1 degrees of Horizons' obliquity. Measured: at most 0.058 (Venus, 177.358 against 177.3); Uranus 97.771, Pluto 119.610, Earth 23.435. Full npm run etl passes. Three mutants each fail it on the named check: - W's sign dropped: "Venus's IAU spin axis is 2.642 degrees". - Ceres looked up by catalogue number: "Body ceres has no IAU rotational elements". - W's rate read per century: "Mercury's IAU day ... 3.65e+4". Nothing is drawn from these yet. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
ab7d454db1 |
Read Mercury's obliquity in the arcminutes its Horizons page gives it in
Mercury's page states "Obliquity to orbit[1] = 2.11' +/- 0.1'", in arcminutes, where every other page writes degrees. The pattern took the number alone, so bodies.json had Mercury tilted 2.11 degrees, sixty times too far, and the system view drew it that way. It now reads the arcminute mark and divides by 60: 0.0352 degrees, against the 0.034 the IAU's pole for Mercury makes with its orbit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
1d42be2ad5 |
Add Charon, the moons of Uranus, Saturn's other large moons and the four dwarf planets past Pluto's table
The solar system stopped at 18 bodies: Pluto without Charon, Uranus without a moon, Saturn with Titan alone, no dwarf planet but Pluto (audit #22). bodies.json now holds 38: the eight planets, the five IAU dwarf planets, and every moon in JPL's mean-element table more than 100 km in mean radius. New: Ceres, Eris, Haumea, Makemake; Mimas, Enceladus, Tethys, Dione, Rhea, Hyperion, Iapetus, Phoebe; Miranda, Ariel, Umbriel, Titania, Oberon; Nereid, Proteus; Charon. Search finds each by name (it indexes bodies.json), each has a body page, and the Sun's system draws them. Where they come from - Moons: the same archived JPL satellite table as the others. Uranus's and Pluto's are given against the planet's equator, with the IAU WGCCRE 2015 poles: Pluto's as the IAU gives it (132.993, -6.163), Uranus's at the end the table measures inclinations from (77.311, 15.175) with its nodes counted 180 degrees on, from the IAU pole's crossing; read without that offset every Uranian moon was 180 degrees from Horizons at every date from 1980 to 2100. - Two rows are corrected where they disagree with JPL's own ephemeris and the reason is known. Pluto's section prints epoch 2000 Jan 1.0; JPL's current table gives Charon's as 2000-01-01.5, and at 1.0 Charon was 27.8-28.2 degrees from Horizons at every date, half a day of its motion. Phoebe's mean motion gives 548.02 days where its Horizons page and the current table give 550.30 (the table's own note says its source misstated retrograde moons' mean motions); on the row's figure Phoebe was 24.6 degrees out by 2025 and 100 by 2075. - Dwarf planets: JPL SBDB osculating heliocentric elements with their epoch (2026 Jun 9), carried at their own n. Against Horizons (heliocentric, 1950-2300; the clock only runs forward from now): Ceres 0.02 degrees in 2025, 1.9 in 2050, 4.0 in 2075, 5.3 in 2100, 11.6 in 2200 (Jupiter pulls on it and nothing here carries that); Eris within 0.06 to 2100 and 0.5 to 2300; Haumea within 0.35 to 2100; Makemake within 0.25 to 2100 and 1.7 by 2200. - Size and spin: Horizons pages for the moons (Charon 606 km, Miranda 235.7 as the mean of its three axes). The SBDB for Ceres (469.7 km, 9.074 h) and for the other three's spins (Eris 25.9 h, Haumea 3.915 h, Makemake 22.83 h). Neither source nor the WGCCRE 2015 report has a radius for Eris, Haumea or Makemake, so each carries its stellar-occultation measurement: Eris 1163 km (Sicardy et al. 2011), Makemake 715 (Brown 2013, the mean of 1434 x 1434 x 1422 km), and Haumea 797.6, the radius of a sphere of its volume: it is triaxial, 1161 x 852 x 513 km (Ortiz et al. 2017), and is drawn as that sphere. - Rotation uses the branch's model. Every moon is locked except three: Hyperion's page says "Chaotic" and Nereid's gives no spin, so both are left still; Phoebe turns in 9.274 h. - Charon carries massRatio 0.12205, the GM ratio of the two Horizons pages (106.10 / 869.326), so it and Pluto are drawn round their barycentre 2 131 km from Pluto's centre. Validators (tools/etl/build.ts, on the real catalogue; full npm run etl passes) - Offsets from Horizons on 2025-01-01, new bodies: dwarf planets at most 0.016 degrees (Ceres), under the 0.25 ceiling; moons Dione 0.009, Ariel 0.058, Rhea 0.070, Charon 0.111, Oberon 0.142, Titania 0.185, Umbriel 0.219, Proteus 0.245, Enceladus 0.309, Phoebe 0.984, Miranda 1.162, Tethys 2.042, under the 2.5 ceiling, which is unchanged. - Four moons get their own ceiling, each just above its worst offset at twelve dates from 1980 to 2100 and each named with its reason: Mimas 46 (measured up to 44.7: its resonance with Tethys swings its longitude 44 degrees either way over 70.8 years, which the table has no column for), Hyperion 21 (20.2; held in resonance by Titan, and the row's eccentricity 0.0232 is under a quarter of the current table's 0.105), Iapetus 11 (10.1; the row sits 9.4 degrees behind Horizons at its own epoch and keeps that, with its plane within 0.07 degrees and its period within 0.001 per cent), Nereid 3 (2.6 in 2025; eccentricity 0.75). - New checks: every body has a radius over 0 (Charon's would have been 0 before the page parser learnt its form); a freely spinning moon is not locked; a moon with a mass ratio puts the barycentre outside its planet; there are 5 dwarf planets. - Negative controls, each a full npm run etl on the real catalogue refused with the named message: Uranus's node offset removed (Miranda 172.50 degrees), Charon at the printed epoch (28.08), Phoebe on the row's mean motion (24.61), Charon's radius unread (no radius), free spinners locked (Hyperion), mass ratio inverted (barycentre 17 460 km out). Measured in the running app (port 4311): the Sun's system has 38 members ("13 + 25 moons"); Charon comes back to within 0.0004 degrees of where it started after 6.38723 days and is 179.98 degrees round after half that; Pluto is 2 130.6 km from the barycentre and Charon 17 456.8, exactly opposite; Saturn's moons in order of distance now: Mimas 185 617 km, Enceladus 238 042, Tethys 294 648, Dione 376 805, Rhea 526 964, Titan 1 231 389, Hyperion 1 470 453, Iapetus 3 637 059, Phoebe 11 740 900. At the arrival framing the dwarf planets are held at the 3 px floor and the moons at 1.5 px, half their planet's drawn radius, the scene's existing rule. Searching Charon, Enceladus, Ceres, Titania, Makemake and Phoebe each finds the body; the body pages show Charon 6.39 d and 606 km, Titania 8.71 d, Ceres 4.6 yr and 470 km, Haumea 283 yr and 798 km, Hyperion 21.3 d, each with its orbit source. Long tasks on entering: see the previous commit. The Sun's note now says the four dwarf planets are on the SBDB's osculating elements. Holding Eris's orbit, the arrival framing widens: 192 AU of range on a 1600 x 1000 window, under the 200 AU ceiling. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
de34ffff43 |
Paint a system's derived surfaces after entering it, not while building it
Every body with no photograph gets a surface derived from its measurements, a 128 by 64 texture painted on the main thread as its marker was built, inside the task that enters the system. At about 4.4 ms each (measured in node for the twenty the next commit adds, 88 ms together), that is the cost that grows with the number of bodies: with the solar system at 38 bodies, the long tasks after selectStar(0) were [219, 72], [228, 79] and [177, 72] ms over three runs, against [85, 72], [94, 75] and [78, 67] at 18. buildMarker now gives such a body its kind's flat colour and hands the painting to the renderer, which paints one surface per task (setTimeout 0) once the constructor has returned, and drops the rest if the system is left first. Measured in the running app (port 4311, three runs each, long tasks over 50 ms in the 9 s after entering the Sun's system): - 18 bodies: [79], [73], [77] ms. The task that entered the system is under 50 ms. - 38 bodies: [55, 72], [69, 78], [60, 83], and [52, 78] on a fourth run. The entering task is 52-69 ms, down from 78-94 before this change with 18 bodies, so the twenty new bodies add no long task over what the branch had. What they still add to it is not measured apart. The flat colour shows for a moment: the Sun's 29 derived surfaces were all painted 436, 689 and 399 ms after its renderer was built (three runs), and a surface once painted is cached, so a return visit paints them at once. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
7b65ab4812 |
Draw a planet and a heavy moon going round their barycentre, as Pluto and Charon do
Standish's "Pluto" is the Pluto-Charon barycentre, and Charon is an eighth of Pluto's mass, so that point lies 2 131 km from Pluto's centre, 943 km above its surface. Drawn the usual way, with Pluto at its row's position and Charon going round it, Pluto sits where nothing is and Charon's orbit is 2 131 km too wide on one side. A moon record can now carry massRatio, its mass over its planet's. For such a moon the renderer keeps the pivot at the planet's elements, which is the barycentre, and each tick puts the planet massRatio / (1 + massRatio) of the relative separation back from it and the moon the rest out. Both orbits are the relative ellipse scaled, the moon's by 1 / (1 + q) and the planet's by -q / (1 + q), turned with the moon's node every tick: Charon's spans 17 460 km of radius and Pluto's 2 131, round the same point, and neither passes through Pluto. Only Charon will carry it; every other moon's barycentre is inside its planet. Checked against Horizons in the unit suite, on JPL's records for the two: Pluto (999) from the Pluto-system barycentre (9) in 2100 is 2 131.24 km out, and the renderer puts it within 5 km of that length and 0.5 degrees of that direction, exactly opposite Charon at the inverse of their mass ratio; Charon from Pluto is within 0.5 degrees of Horizons in 2100 (measured 0.37). The same table adds Titania, against Uranus's equator 120 years from its 1980 epoch, within 0.75 (measured 0.62). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
b0d7989c6f |
Read moons given against their planet's equator, and dwarf planets from the Small-Body Database
Two sources the missing bodies need, read and tested before any body uses them.
JPL's satellite table gives Uranus's and Pluto's moons against the planet's equator ("Mean
equatorial orbital elements") rather than a Laplace plane, and does not print that equator's
pole. parseSatelliteMeanElements now takes the pole from its caller for such a section and reads
the row against it exactly as against a Laplace plane's; it throws if a section is equatorial and
no pole was given, or a pole was given for a section that is not. Read as ecliptic elements, which
is what the old code would have done, Titania is 88 degrees from Horizons on 2025-01-01. The
section's plane is now the nearest heading above the row, with the ecliptic as before where there
is none.
Ceres, Eris, Haumea and Makemake are in none of Standish's tables. parseSmallBodyElements reads a
JPL SBDB answer (sbdb.api?sstr=...&phys-par=1&full-prec=1): the osculating heliocentric elements
against the J2000 ecliptic, carried round at their own n with nothing turning, and half the
published diameter and the rotation period where the answer has them. full-prec matters: without
it SBDB rounds to three figures, Ceres's n to 0.214 degrees a day for 0.2143045, 1.1 degrees out
within a decade. The fetcher, tools/etl/lib/mean-elements.ts, caches the answer like the others.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
||
|
|
cdc0cf4678 |
Read the Horizons pages the missing moons are written in, and give triaxial bodies their mean radius
The pages of the moons this branch is about to add state their size and spin in forms the ETL
did not read. Charon's gives "Radius (km, IAU2015) = 606", which none of the radius patterns
matched, so it would have come out at radius 0. Phoebe's gives "Rotational period = 9h 16.438 m",
which the hours-or-days pattern read as 9 hours flat instead of 9.274. Pluto's and the moons' GMs
are now read too ("GM (planet) km^3/s^2 = 869.326" on Pluto's page, "GM (km^3/s^2) = 106.10" on
Charon's), for placing a pair's barycentre.
Miranda and Ariel, like Phobos and Deimos already, give three semi-axes, "240x234.2x232.9". The
first figure was taken as the radius, which is the longest axis. A triaxial body now gets the
radius of the sphere of its volume, the cube root of the product, which is how the IAU states a
mean radius. That changes two bodies already shipped: Phobos 13.1 -> 11.06 km (IAU 11.08) and
Deimos 7.8 -> 6.20 km (IAU 6.2). Nothing else in bodies.json moves.
The page parsers move from tools/etl/lib/horizons.ts to src/app/shared/astro/horizons-page.ts,
as the mean-element parsers did, so the unit suite runs their tests; the ETL imports them. A
small body's command ("1;" for Ceres) is now URL-encoded: sent raw, the semicolon made Horizons
refuse the request ("one or more query parameter was not recognized").
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
||
|
|
2e5daa0f97 |
Give every body the period it is drawn going round in, and say where its orbit comes from
Audit #38: Europa's card listed its axis, eccentricity and inclination but no period, while the scene turned it round Jupiter all the same: heliocentricPeriodDays refused every moon, since the catalogue carried no planet masses. Every solar-system body's period is now 360 over the JPL mean motion that carries it round the scene, filed under Measured since that is JPL's published figure: Europa 3.55 d, the Moon 27.3 d, Saturn 29.5 yr on the live cards, Earth 365.2564 d. heliocentricPeriodDays is gone. Exoplanets keep the archive's period, or none. The card's provenance line now ends with where the orbit comes from, "Orbit: JPL SSD satellite mean elements, epoch 1997 Jan 16." for Europa, "Orbit: JPL approximate mean elements (Standish), fit for 3000 BC to AD 3000." for a planet, and the Sun's system note says so too: "Orbits propagated from JPL mean elements, the planets' fit for 3000 BC to AD 3000, to the current date." Other systems keep "published elements". All three read in the running app. Each has a test that fails without it (moons refused a period, provenance without the orbit, a note without the source). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
48319c3fe2 |
Move the solar system on JPL's mean elements, so it stays right as the clock runs
Every body carried one set of osculating elements from Horizons at 2025-01-01, run forward by
Kepler with a GM from a table of mass ratios. That set is exact at its instant and drifts from
then on, and the clock now runs a month a second: the Moon, with Earth's mass ratio lacking its
own and the osculating axis, went round in 27.70 days instead of 27.32, 66 degrees out after a
year, and its locked face was spun at the same wrong rate.
Planets and Pluto now take Standish's Table 2a/2b ("Keplerian Elements for Approximate
Positions of the Major Planets"): elements against the J2000 ecliptic, their rates per century,
and the b, c, s, f terms of Jupiter to Pluto, fit for 3000 BC to AD 3000. Table 1 is closer near
the present (Saturn 0.23 degrees at worst 1950-2100, against 0.32 here) but is only fit for
1800-2050, and by AD 3000 has Saturn 4.3 degrees out where Table 2 holds every planet within 0.3.
The moons take JPL SSD's satellite mean elements: sidereal mean motion n to ten figures, the
periods of their node and periapsis, and each one's local Laplace plane by its pole. They
propagate with n itself, never a GM: gmForParent and its mass table are gone. Horizons still
gives size, spin and obliquity.
Both tables are read from the Internet Archive's copy of JPL's pages, pinned to one capture: the
live approx_pos page has dropped Pluto, and the live sats/elem page has dropped n and rounds the
period to four or five figures (Phobos 0.3187 d, a revolution out within a decade).
What the tables leave implicit, measured against Horizons before it was accepted:
- The precession periods are magnitudes. A node regresses on a prograde orbit and advances on a
retrograde one; a periapsis advances except where a resonance forces the eccentricity. Io's
and Europa's follow their conjunction line backwards at 2 n(Europa) - n(Io) = 0.74 degrees a
day, which is exactly the 1.625- and 1.394-year periods in the table. Read as advancing, Io
was 0.9 degrees out and Europa 2.1.
- On a retrograde orbit the node's turning is added back to the mean anomaly. Taken off, Triton
drifted a degree a year, 105 degrees by 2100.
- The Laplace frame's x axis is where the plane rises through the ICRF equator, RA of the pole
plus 90. Read against the ecliptic, Io was 2.8 degrees out, Phobos 54 and Titan 127.
Orbit lines are now drawn in their own plane and turned by a quaternion each tick, so a turning
node carries the line with the body: fixed at one date, the Moon's line would be up to 69 000 km
off it nine years on. The Earth row is the Earth-Moon barycentre, 4 700 km from Earth, 0.002
degrees from the Sun. A tidally locked moon's day is now 360 / n, its sidereal period (the Moon
27.321662 d), so it stays locked to the orbit it is drawn on.
Angular error against Horizons VECTORS (ICRF, TDB; heliocentric for planets, planet-centred for
moons), degrees, read from the live renderer's markers in the running app:
body 1950-01-01 1975-01-01 1987-07-23 2000-01-01 2025-01-01 2037-03-06 2050-01-01 2075-01-01 2100-01-01 max
mercury 0.004 0.002 0.003 0.002 0.002 0.001 0.000 0.002 0.000 0.004
venus 0.003 0.007 0.003 0.004 0.004 0.004 0.003 0.004 0.004 0.007
earth 0.003 0.008 0.002 0.005 0.004 0.009 0.003 0.002 0.003 0.009
mars 0.009 0.010 0.008 0.024 0.009 0.012 0.009 0.011 0.028 0.028
jupiter 0.063 0.030 0.171 0.135 0.013 0.020 0.056 0.041 0.075 0.171
saturn 0.080 0.064 0.018 0.320 0.066 0.114 0.044 0.164 0.177 0.320
uranus 0.018 0.169 0.068 0.050 0.101 0.015 0.141 0.017 0.114 0.169
neptune 0.070 0.028 0.004 0.021 0.036 0.037 0.013 0.029 0.072 0.072
pluto 0.045 0.054 0.041 0.033 0.019 0.020 0.023 0.027 0.026 0.054
moon 0.486 1.928 0.127 0.631 1.407 1.086 0.720 0.339 1.180 1.928
phobos 2.068 0.294 0.881 1.113 0.313 0.636 2.089 5.862 11.099 11.099
deimos 0.077 0.043 0.310 0.066 0.164 0.068 0.034 0.468 0.044 0.468
io 0.021 0.015 0.010 0.019 0.009 0.035 0.006 0.011 0.022 0.035
europa 0.036 0.039 0.053 0.064 0.078 0.032 0.006 0.034 0.044 0.078
ganymede 0.132 0.103 0.018 0.007 0.023 0.054 0.091 0.118 0.044 0.132
callisto 0.040 0.019 0.023 0.019 0.038 0.008 0.060 0.119 0.056 0.119
titan 0.003 0.019 0.023 0.023 0.027 0.028 0.048 0.008 0.014 0.048
triton 0.051 0.029 0.009 0.021 0.052 0.048 0.063 0.089 0.137 0.137
Three miss what was hoped for, and why:
- Jupiter 0.17, Saturn 0.32, Uranus 0.17 against the 0.1 hoped for: short-period perturbations
of the giants by one another, which no Keplerian fit carries. Standish states his own Table 2
errors as 600, 1 000 and 2 000 arcseconds (0.17, 0.28, 0.56 degrees). Out to AD 3000, measured
at 1800, 2200, 2400, 2600 and 3000, every planet stays within 0.3.
- The Moon, 1.9: evection (1.27) and variation (0.66), which a mean ellipse leaves out.
- Phobos, 2.1 until 2050, then 5.9 in 2075 and 11.1 in 2100, growing as the square of the time:
its tidal acceleration, which the table has no column for. Its elements are MAR080's, epoch
1950. The map's dates are also UTC where the elements are TDB, 69 s today,
which is 0.9 degrees of Phobos and nothing for anything else.
Held in place by:
- build.ts: each body's mean elements against Horizons' own osculating elements on the ETL's
2025-01-01, at most 0.25 degrees for a planet and 2.5 for a moon (measured: Uranus 0.101, the
Moon 1.407; a regressing Triton node reads 10.24 and fails), and every moon's day equal to its
sidereal period (a 1% error fails).
- Unit tests freezing nine Horizons vectors (Earth 2100, Jupiter 1950, Saturn 2075, Pluto 1975,
the Moon 2050, Io and Europa 1950, Titan and Triton 2100) through SystemOrbitsRenderer, the
Moon kept on its own turning line, the retrograde rule, the Standish terms, the Laplace frame,
and both table parsers. Nine mutants each fail the test named for them, and the two
validators each refuse a mutated build of the real catalogue.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
||
|
|
c38a42cbcb |
Fix what the review of this branch found, starting with the pick rule it only claimed
The off-screen rule for clicks was described in
|
||
|
|
468c98b14a |
Surface the system view with the photographs it already had, lit by its star
Every body in the system view was an unlit sphere wearing a 32 by 16 pixel procedural texture — the size chosen when a marker was a few pixels across and what survived was its average colour. The thirteen real photographs in `src/assets/textures/bodies/` were used only by the detail page. So Mars was a pale grey ball with invented polar caps while its own NASA mosaic sat unread in the repository, and nothing had a day side or a night side. Each marker now takes its own photograph where one exists, at the size the detail page uses, and the derived texture only where none does — the five moons no probe mapped, and every exoplanet, none of which has ever been imaged. The material is lit, and the light is a point at the star, so each world shows the terminator where it really falls. The light does not fall off with distance. Under the inverse square that real light obeys, Neptune receives a thousandth of what Mercury does and reads as black; the map is a set of worlds to look at rather than a light meter, so each is lit as a photograph of it would be. That is the same concession the pixel floor makes for size, and it is only about brightness: the *direction* is real. Spheres are 32 by 24 rather than 16 by 12, since at true scale a body is drawn anywhere from a pixel to the whole frame and the old silhouette was visibly faceted at the near end. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
4b276e44a5 |
Give the map a clock, so the sky it computes can be watched
The orbits and the rotations are both functions of a date, and the only date the map ever asked for was this instant. So a view built on propagated ephemerides showed a still picture: Earth turns 15 degrees an hour and takes a year to go round, and a reader watching for a minute saw nothing move at all. `TimeStore` is that date, at a rate the reader sets: real time, an hour a second, a day a second, a month a second. It is read once a frame rather than held in a signal — it changes continuously, and a signal changing sixty times a second would ask the whole HUD to re-render for a number nothing is watching. Changing the rate re-anchors rather than rewinding, so speeding up and slowing down never jumps the sky, and "Back to now" returns to the world's own time. Measured in the app, three seconds of watching in the Sun's system: | rate | sky elapsed | Earth turned | Jupiter moved | |---|---|---|---| | real time | 0 | 0 | 0 | | 1 h/s | 3.0 h | 45.12 deg | 0.0009 AU | | 1 d/s | 3.0 d | (three full turns) | 0.0222 AU | 45.12 degrees in three hours is 15.04 an hour, which is Earth's own sidereal rate, and Jupiter's 0.0222 AU in three days is its own orbital speed. The rates are radio buttons, not toggles: they are one of four, and the native control carries that to a screen reader and to the arrow keys with no script. The date joins the strip only while the clock is running faster than the world, since at real time it is today's, which the reader's machine already says. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
225d676ab0 |
Turn each body at its own rate, from Horizons' own figures
The view had one rotation in it — the planet on the detail page, at 0.08 rad/s, a number with no source. Nothing in the system view turned at all. The data was already on disk: every cached Horizons page carries how its body spins, in one of five forms. The rate in radians per second is preferred where it appears, because it is signed — that is how Venus and Uranus are known to turn backwards — then a period in hours or days, then the `9h 55m 29.711 s` the giant planets use, and finally the word every major moon here carries instead of a number: Synchronous. A tidally locked moon's day is its orbit, so Kepler supplies it from the elements already parsed and the parent it goes round. Seventeen of the eighteen bodies come out within 1% of their published period — Earth 23.934 h, Jupiter 9.925 h, Venus -5832.5 h, Io 42.5 h, Callisto 400.5 h. Titan is the exception: its page states no period at all, so it is left still rather than turned at an invented rate. The axis is the orbit normal tilted by the obliquity about the orbit's ascending node, which is where an obliquity is measured from and the only line in the orbit the elements name. The phase at the epoch is published for none of these bodies, so the face turned toward the camera is not a claim; the rate and the direction are. At true rates nothing is visible moving — Earth turns 15 degrees an hour. A clock the reader can run faster is the next piece, and the audit asks for it anyway. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
3f0abf8717 |
Draw the system at true scale, drop the halo, and refuse to enter what is off screen
Three changes to what the system view claims, all of them the same claim: that the sizes on screen mean something. **The halo is gone.** It was a sprite sized against the arrival frame — 1.12 AU for the Sun — so it stayed that wide as the camera closed in and ended up a flat gradient filling the screen, over the photograph it was meant to dress. It existed to keep the star visible at a framing that holds the whole system, which is now handled in pixels instead. **Bodies are drawn at their own radius.** The old marker size was exaggerated and scaled to the system span, and clamped: Jupiter and Ganymede both ran past the ceiling and were drawn at one radius, so every moon orbited inside its planet, and Phobos and Triton sat entirely within Mars and Neptune. True scale needs no rule against that — physics already puts a moon outside the planet it orbits. What it costs is visibility at the arrival framing, where every body is sub-pixel, so the scene floors each marker at 3 px on screen and holds a moon to half its planet's drawn size. Measured in the Sun's system: at arrival, planets 3 px and moons 1.5 px, against 3 px for everything before; at Jupiter, the planet 10.8 px at scale 1 with the Galilean moons on their orbits outside it. The Sun is drawn at its own radius too. Every other star keeps a size derived from its innermost orbit, because no stellar radius reaches the app — Gaia's `radius_gspphot` is the obvious next fetch. **A click cannot enter a system that is not on screen.** The picker tested depth but not the frame, and a star's hit area is its drawn size plus a slop, so a click in the last pixels of the view could fly into a system outside it, with nothing on screen to explain where it had gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
e45c3b6287 |
Merge pull request #32 from avalon-vanguard/fix/routes-panel-honesty
Let the Routes panel be clicked as soon as it is back, and stop it departing from elsewhere |
||
|
|
2d06408c3f |
Merge main into fix/routes-panel-honesty
Both sides added a test beside the other in the dock's spec: the panel's own departure guard here, the give-up wording on main. Both kept, and the offer test carries the `least` the route answer now has. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
56870fd9fe |
Merge pull request #31 from avalon-vanguard/fix/grid-rings
Size the distance rings by what the frame reaches, and keep their labels off the star names |
||
|
|
cea39c7186 |
Merge pull request #30 from avalon-vanguard/fix/route-search-budget
Tell a search that gave up from a route that is not there |
||
|
|
03b3d3b2d6 |
Merge pull request #29 from avalon-vanguard/fix/etl-gaia-floor
Refuse a Gaia answer that came back short, and read the body inside the retry |
||
|
|
0c5efec505 |
Measure the ring span along the plane the rings lie in
The span went to `distanceRings` as the target's straight-line distance from the Sun, but a ring of radius r passes within |r - p| of the view's centre, where p is how far out that centre is *along* the galactic plane. For a target above the plane the two differ by its height, so the band was centred on a radius no ring has — and `ringLabels` picks its bearing by comparing its own in-plane distance against the innermost ring, a comparison the new first ring quietly broke. Two comments and a constant, from the same review. A frame short of the survey edge gets its callout only when its last ring overshoots it: 245 pc does, 235 pc does not, which is now a test rather than a sentence. The ring count can reach 16, not 14, now that the span need not start at the Sun. And a ring label was measured as 135 px of star name when "50 pc" is a third of that, which rejected rungs a hand's breadth clear of the name: RING_LABEL_REACH_NDC, 0.23, is the widest of them — "1.5 kpc" with "Survey edge" under it. Three mutants, three caught. Measured again in the app: unchanged for a star in the plane (4 labels at 20 pc above it, 7 at 2 pc), and the rings now follow the plane for one 195 pc above it rather than ringing a place the grid does not reach. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
337602f606 |
Make the budget test spend the budget, and bound the probes that earn nothing
The fixture built for "exactly MAX_VISITED stars reachable" was 338 short: its random cloud leaves clumps the departure never reaches (the LCG gives 12 212 distinct positions for 39 999 stars), so the search settled 39 662 and the pre-fix code answered `gaveUp: false` too. The test could not fail on the code it was written to pin — and the mutant that seemed to prove otherwise was failing to compile, not failing the test. It is now a line of 40 000 a parsec apart with the island off the line: settled 40 000 exactly, 115 ms, and the pre-fix code does report a give-up. Both mutants now compile and are caught. The give-up cap also has to hold while the bisection has earned nothing: the exception added for that case had no bound at all, so a search could spend the resolution's own eight full-budget probes — about 17 s of "Plotting…" — where two used to cost 4 s. Bounded at five. On the repo's crowded-knot fixture: 1.9 s for the unearned ceiling figure with the old cap, 7.2 s for a range the bisection earned, and five probes is where that lands. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
1a5785fb63 |
Keep the row cap live for the queries that can reach it
Gating it on `jobsQuery` switched it off for every override that *widens* the query — which is the only way to fill `select top N` at all. `ETL_GAIA_MAGNITUDE_LIMIT=14` asks for 500 000 rows, the sky holds more, and the answer is the limit rather than the filters: exactly what the tripwire is for, and it no longer fired. It now reads the row limit itself, so only a deliberately smaller slice is silent. Measured with a synthetic answer of exactly 500 000 rows in the cache, under the key the widened query hashes to: refused. With the `jobsQuery` gate back, the same run keeps 500 000 Gaia stars and goes on to publish them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
dd56eafba4 |
Answer the review: hold the offer to the same test as the button
`canPlot()` guarded the Plot button and not `raiseTo`, which is the other way into `plot()`. So with a departure typed but never chosen, clicking "1.8 pc would reach." moved the range control and plotted nothing: the panel then read "No route at this range. 1.8 pc would reach." beside a control already set to 1.8. The offer carries the same `disabled` as the button, since it is the same request by another route. And the departure guard is trimmed, as the scene trims the same text before offering matches for it: one space in the field left it looking empty, with no suggestions to pick from, and Plot dead for no reason on screen. Measured in the app, from inside Barnard's Star with Sirius as the destination: offer enabled with the field empty, disabled once "Sol" is typed and never chosen — a forced click then moves nothing — and enabled again when the field is cleared, where it raises the range to 2.40 pc and plots 7 jumps. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
7fba48c808 |
Answer the review: size the rings to the band the frame covers, and clear the text
The rings are centred on the Sun and the frame need not be. Sizing their step from how far the frame reaches — 210 pc for a star at 190 with the camera 20 pc back — gives 20 pc rings at 180 and 200, both outside a frame 19 pc deep, so a view away from the Sun still had no ring on it and no ladder of labels either. `distanceRings` now takes the span the frame covers rather than its far edge, and the step is a fifth of that: 5 pc rings from 165 to 210 for the same view. Measured in the app, centred on a star 187 pc out in the galactic plane: 4 ring labels drawn 20 pc above the plane and 7 from 2 pc, against 1 and none before. The clearance was a radius around the anchor, and a label is a line of text hanging 135 px to one side of its anchor: at 0.065 NDC apart, past the radius, "50 pc" printed inside "Alpha Centauri". It is now tested against the span the name occupies, on the side it hangs, with the radius kept for the pair whose text runs the other way. Also from the review: the ladder in the clearance test was built at exactly the constant it tests, so 1057 of 2000 camera poses would have decided it by float round-trip error — the rungs now sit 0.02 either side of the rule. And two comments that were wrong: a frame one step short of the survey edge does get its callout, and CSS2DRenderer hides a label behind the camera rather than drawing it at the page edge. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
1a53f26474 |
Answer the review: say which search gave up, and let the bisection earn its offer
The panel printed "No route at this range." beside the range it was offering — which is the sentence this branch exists to stop it printing. It was gated on there being no offer, and a search that gives up usually has one: Sol to HD 120147 at 4.5 pc spends the budget, offers 4.83 pc, and says there is no route where a 71-jump route exists. The wording now follows the search at the range that was asked for, and nothing else. That needs the two give-ups kept apart, so `least` travels beside `gaveUp` to the panel: one says the asked range was not searched out, the other that the search for a range that would work was. HIP 69445 at 3 pc — asked-range search exhaustive in 44 ms, ceiling probe out of budget — used to read "Too many stars to search at this range." and now reads "No route at this range.", with nothing claimed after it. Two more from the same review. The budget flag was read off the settled count, so a search that proved a dead end with the last star it was allowed reported a give-up; it now records why the loop stopped. And the bisection's cap could fire before a single probe had narrowed anything, leaving the ceiling route's own longest hop as the answer: star 1000115173 at 3 pc was told to go to 8.00 pc, the control's maximum, for a crossing that works at 6. It now offers 6.93. Measured in the app, all three: "Too many stars to search at this range. 4.90 pc would reach.", "No route at this range." alone, and 7.00 pc in place of 8.00. The duplicated dead-end test now asks the question it was named for — exactly the budget's worth of stars reaching each other and none of them the destination — and each fix kills its own mutant. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
b7f277ea04 |
Answer the review: a short answer makes more survivors, and must not be skipped
Three things this got wrong. The direction: truncating Gaia leaves the HYG rows whose counterpart it dropped without one, so survivors rise — 10 886 today, 12 711 at half the rows, 16 258 at a third — which the comment claimed was the other way, and which decides whether the 15 000 ceiling can be leaned on at all (it catches a truncation past about two thirds, and nothing shallower). The throw: `fetchStars` catches everything a source throws and skips it, so a truncated CSV was reported as "the archive was unreachable" one step after `writeStarAssets` had already overwritten the published catalogue. Marked with `GaiaAnswerError` and rethrown there, so an answer that cannot be worked with fails the run where it happened. Measured end to end in a throwaway working directory, 300 000 rows in the cache: fails, names the cache file to delete, assets untouched. With the rethrow taken back out again: assets written, then "the archive was unreachable". The row limit: `rows.length >= ROW_LIMIT` is true for every reduced ETL_GAIA_ROW_LIMIT, so the tripwire fired on exactly the deliberate slice the override exists for — and told the operator to raise it. Gated on the same flag as its neighbour. `ETL_GAIA_ROW_LIMIT=20000` now runs through; without the gate it dies on the limit it was given. Also: the row floor names the one cache file it is about rather than a glob that takes the Hipparcos cross-match with it, and says an edited query is a third reason it can fire — DEFAULT_QUERY_ROWS now sits under the query it counts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
7ab92e61a1 |
Give the budget tests room and cells to run in
Both make a search spend its whole 40 000-star budget, twice over in the bisection, and the CI runner timed out at the default five seconds. The crowds are now indexed in cells sized for the ranges asked of them, as the real catalogue is, and the two tests carry their own 30 s timeout. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
365252f534 |
Let the Routes panel be clicked as soon as it is back, and stop it departing from elsewhere
From the review of #24. Two defects, both in a real browser. The panel keeps its entries across a trip to another tab, but still replayed the acquire wipe on the way back, and for the 380 ms that runs, its clip path swallows clicks: type "Siri", leave for Readout, come back and click the Sirius suggestion, and the click lands on the star field behind it — measured, the element under the pointer is the canvas, and the field stays "Siri". The wipe is gone from this one panel: it is not acquiring anything it did not already have. The departure field fell back to the star the view is in whenever nothing had been chosen, text in the field or not. So a field reading "Sol" that was never resolved plotted from Barnard's Star: "1 Barnard's Star, 2 Sol, 3 Sirius", the panel naming one departure and the route leaving from another. Text nobody chose is no longer a departure, and the button waits until it is one or the field is empty again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
5dec528cee |
Size the distance rings by what the frame reaches, and keep their labels off the star names
From the review of #19. The rings are distances from the Sun, but their step was taken from `effectiveDistance`, which under the plan view means the extent of the frame rather than how far the camera is from the Sun. Centred on a star 200 pc out and flipped to 2D, the grid became rings of 2 to 20 pc: not one of them on screen. The step now comes from where the view is centred plus how far the camera is orbiting it, which is the same distance under either projection. The set was also rebuilt while the grid was hidden, and every rebuild disposes the rings and builds every vertex again; it now happens only while the grid is drawn. The ring labels went straight to the overlay: never culled to the frame, and free to land on a star's name. They now have to be on screen and clear of the names already placed, by half the separation two names keep — they are a ladder up one ray a twentieth of the screen apart, and holding them apart from each other would take "Survey edge" off the map. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
7971ec4007 |
Simplify: without a give-up the bisection can only have closed on the resolution
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
862fb65ea4 |
Tell a search that gave up from a route that is not there
The route search stops after MAX_VISITED stars and returned null, which everything downstream read as "the catalogue holds no chain". On the real catalogue that was wrong for real questions: Sol to HD 120147 (136 pc) at 5 pc is 50 jumps, and the panel said there was no route. The budget also sat under what the shipped catalogue needs, so it is now 40 000 rather than 20 000: both that route and a star at 170 pc are found, and Sol to HD 2626 at 6 pc, which used to be refused after 4.7 s, plots 56 jumps in about 2 s. A search now reports whether it gave up. The range search no longer counts a give-up as proof that nothing routes below it — that is what reported ranges up to 29% too wide — and it stops after two of them, since those are the probes that cost the most and settle the least: for HD 2626 at 3 pc it offers 5.92 pc in about 4 s, against 6.13 pc in 4.7 s. At the panel's widest range the refused route and the range search are the same question, so it is asked once. Where nothing can be said, the panel says "Too many stars to search at this range." rather than claiming there is no route. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
44f6a8d086 |
Refuse a Gaia answer that came back short, and read the body inside the retry
The merge gate asks whether Gaia contributed any stars, never how many. The TAP service truncates on its own timeout and still serves a well-formed CSV with a 200, ordered by magnitude — so a half answer is the bright half, which is the half HYG overlaps. Every gate passes: Gaia stars are present, HYG survivors go down rather than up, unmerged twins can only fall. The weekly job would publish a catalogue missing two hundred thousand stars and the runner would cache it for the weeks after. `fetchGaiaStars` now refuses fewer than 95% of the 412 765 rows its query holds, as its sibling query already did, and refuses an answer that fills the row limit. `fetchText` retried the request but not the body: a connection reset part-way through the 57 MB CSV rejected out of the loop, with no wait and no second attempt. The read now happens inside it. Also corrected: the merge gate's account of the HYG survivors (two thirds of them are stars Gaia measures but the main query never downloads, since Gaia puts them past the 250 pc cutoff), and the refresh workflow's comment on what happens when the archive is unreachable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
539a0f0a3e |
Answer the review: budget in drawn pixels, re-ask when the budget moves, sort only the band it runs out in
- The budget counted CSS pixels; lines are drawn in device pixels, so a screen scaled to 150% or 200% drew 1.5-2x the calibrated line. It now counts the canvas's drawn pixels. - A graph was re-asked only when the drawn stars changed, so with a star budget covering the whole catalogue, or a resize, its budget and centre stayed wherever the layer was turned on. A view that chose its stars again now asks, and a graph is rebuilt when the stars, the range or the budget changed (the budget by more than half the margin, or its centre by more than 5 pc). - From inside a system the budget was worked out in astronomical units about the system's origin. Graphs are now asked for in parsec space only; the flight back out asks. - Comparing budgets let a request re-asked with a slightly different one supersede its twin, and the twin's rejection cleared the state of the request that replaced it. A rejection now clears it only for the latest request. - The worker sorted every link to keep a few thousand, 2.2x an unbudgeted build. It now bands links by distance, keeps every band before the one the budget runs out in, and sorts only that one: 142-168 ms on the real catalogue against 233-388 ms, 103 ms unbudgeted, returning early when all fit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
bd5e9d4b0d |
Budget the jump-link layer in pixels of line, nearest the view's centre first
With the drawn set following the view, the layer at 8 pc cost the integrated Radeon 503 ms a frame at 30 pc from the Sun. Measured, the cost follows the length of line on screen (about 10 ms per million pixels near the Sun), not the number of links: 100 000 links were 12 ms at the opening view, 25 000 were 61 ms at 30 pc. So the budget is a length: a million pixels, turned into parsecs at the depth the view is centred on, spent on the links nearest that centre by their nearer end. Orbiting with links at 8 pc on the iGPU: 12-18 ms p50 at every pose measured, no long tasks. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
9631ddf0a4 |
Answer the review: keep up with flights frame by frame, respect portrait frames, and let links follow an orbit
- Flights: the drawn stars were checked once a label pass, and a flight outruns that. Leaving a system jumps the camera to face another way, then zooms out forty-fold in a second: 74-83% of the stars that belong on screen were missing on the first frames back in parsec space, 33-50% before each re-choice on the way out. While the rig animates, the check now runs every frame. Probe on real flights (Gl 806, Barnard's Star, out): 0.90-1.00 of a fresh choice drawn on screen in flight, 1.00 on the frame of the jump; frame p95 12.2 ms, no long tasks. Choosing for the whole sky during flights was tried first and measured worse (0.15-0.18). - Portrait frames: the turn and pan limits use the narrower half-extent, not the height. - Links: a new drawn set arms the rebuild timer only when none is pending, so a continuous orbit gets a graph at most every 250 ms instead of never; an unchanged set asks for nothing. - The centre-move test lets the first pass happen before moving the centre. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
9ad0815acd | Merge branch 'perf/drawn-set-one-walk' into feat/drawn-set-in-view | ||
|
|
a769d70015 | Merge branch 'perf/link-drawn-stars' into perf/drawn-set-one-walk | ||
|
|
52c5d3b144 |
Answer the review: share a graph request asked again, by its range and its drawn list
Graph requests were never shared, on the grounds that the scene never asks for the same graph twice. It does: turning the layer off and on while the worker is busy asks again for the graph already waiting. The new request superseded the old one, and the old one's rejection handler, which finds its request by range and drawn list, wiped the state of the new one: the layer stayed on with no graph. An identical request now shares the outstanding promise, a graph being the same when its range matches and its drawn list is the very same array. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
6cd0666067 |
Draw what the camera shows: the budget goes to the stars in view
The drawn set was two spheres, around the view's centre and around the Sun, then the brightest stars anywhere, so most of the budget sat behind or beside the camera: at 30 pc from the Sun 15.8% of the drawn stars were on screen, at 5 pc 9.1%, in a plan view zoomed to 10 pc 3.8%. The same tiers are now taken only from the camera's frame, widened by a quarter (VIEW_MARGIN), with the planet hosts in view drawn first after the pinned stars, so every ring circles a star that can be clicked. The set is chosen again at the label cadence once the view has turned, zoomed or moved half the margin, switched projection or been resized, and once for the whole sky on the way out to the Galaxy. Drawn stars on screen: 74-76% at 30 pc, 73-75% at 5 pc, 66% in the zoomed plan view, 91.5% at the opening view. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
7064e34d02 |
Choose the drawn stars in one walk of the brightness order
Same stars in the same order, for less: one walk of the brightness index, reading positions laid out in that order, sorts the view's neighbourhood, the Sun's and the rest as it goes, instead of gathering both neighbourhoods in catalogue order and sorting them. A refocus in the page drops from 11.3 ms to 4.6 ms (median; worst 19.1 to 7.1). This comes before the drawn set follows the camera's turns, which makes refocusing far more frequent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
c6206a8311 |
Link only the stars that are drawn
The jump-link graph linked the whole catalogue: 3.7 million links at 8 pc, 7.4-7.8 s in the worker and a 443-515 ms frame on the main thread when they landed, and most of them between stars that were neither drawn nor clickable. A graph request now carries the star field's drawn stars, and the worker links only those, over an index of its own with cells as wide as the range. The scene asks again once a new drawn set has held still for 250 ms. The renderer is handed the graph's bounding sphere instead of computing it: three.js walked every vertex on the main thread in the first frame that drew a new graph, 48-55 ms at 8 pc. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
c77d3ed1d9 |
Keep the Routes panel's entries across a trip to another tab
The panel was unmounted with its tab, so leaving it reset departure, destination and range. The range reset was also a lie: the slider came back at 3 pc while the scene kept drawing the graph at the range last chosen. The panel now stays mounted and is hidden while another tab is open, which still replays the acquire wipe when it is shown again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
f156e03822 |
Answer the review: send the worker one request at a time, keep only the latest, and never wait for a dead one
The adversarial review confirmed three defects in this PR, all reproduced in the browser. 1. Superseded graphs queued up in front of routes. The worker answers messages one at a time and cannot drop one it has started. With the jump-link layer on, every pause on the range slider posted a full graph build, seconds of work at 6-8 pc. Answers no longer wanted were thrown away only once built. A route asked for afterwards waited behind every one of them: a one-jump route took 44 s. RoutingClient now holds requests and sends them one at a time. While one is out, only the latest of each kind waits: a newer graph replaces an older one before it is ever built, and the older promise is rejected with SupersededRequest. Routes go ahead of graphs. The same question asked again while outstanding shares the answer rather than being worked twice, as when the layer is turned off and on during a build. The same scenario in the browser (layer on, range stepped 5 -> 8 pc with 400 ms pauses, then Sol to Proxima): the route came back in 110 ms. The worker was sent "links 3, links 5, route, links 8"; 6 and 7 were never built. 2. A worker that failed left the panel stuck. With no error handling, a worker that failed to load (a 404 on its chunk after a redeploy) or threw left "Plotting…" and a disabled button for good, and a graph at a range could not be asked for again. The worker now answers an exception with a 'failed' message, which rejects that request. A worker that fails to load or dies is abandoned, and what it left outstanding, and everything asked afterwards, is answered in place. The scene releases the panel when a route fails, and forgets a graph range that was never drawn so it can be asked for again. 3. Nothing type-checked the worker. The application builder never reads webWorkerTsConfig, and bundles the worker with esbuild, which strips types without checking them. tsconfig.app.json leaves the file out. A type error in the worker shipped. `npm run worker:typecheck` (tsc -p tsconfig.worker.json) now runs in CI beside the other project checks. webWorkerTsConfig is removed from angular.json, since it only suggested that something checked the worker. Tests with a fake worker cover one request at a time, a waiting graph replaced and a route sent ahead of it, a question shared, a failure rejected and the next request sent, and a failed worker's requests answered in place. A scene test covers the panel released after a failed route. Negative controls, each caught: several requests sent at once, a waiting graph kept, graphs ahead of routes, a question asked twice, a failure answered as a success, a failed worker waited on, the panel left pending, and a type error in the worker (caught by worker:typecheck). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
965739e99e |
Merge branch 'feat/drawn-set-follows-view' into perf/routing-worker
The label and star-field review fixes arrive under the routing client: the scene keeps constructing RoutingClient beside the neighbourhood, and builds the brightness index where it built the order. Both sides' new scene tests are kept. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
86e143131e |
Answer the review: pin by the index the neighbourhood holds, and choose again only when it can matter
The adversarial review confirmed three costs this PR added, all reproduced in the browser. - The first pinned refocus stalled the first flight of a session. The renderer built its own id-to-index Map of 423 651 entries the first time a star was pinned, which is at the first selection, inside the approach flight. The worst frame was 47-103 ms, and the Map stayed as a second copy of a lookup the scene already had. The scene now pins by catalogue index, through the StarNeighbourhood it builds at load (new `indexOf`), and the renderer takes indices. First selection, measured in the browser: worst frame 18 ms. - At galactic scale every label pass rewrote the drawn set. The view centre sweeps hundreds of parsecs a pass there, far past any star, so each pass chose the same 70 000 stars again and uploaded 2 MB to the GPU: 11 times on the flight out to the Galaxy. The scene no longer refocuses at galactic scale, where the whole catalogue is a few pixels, and the renderer leaves its buffers alone when the drawn set is unchanged. Flight to the Galaxy: 2 refocuses, no frame over 50 ms. - At load the same set was chosen twice: once by the renderer's constructor around the Sun, and again by the first label pass, centred on the Sun. The scene now records the constructor's choice as the current focus. Tests: the buffers keep their version for an unchanged set, no refocus at load, none at galactic scale, and pins arrive as indices. Proxima's id in the scene spec now differs from its index, so a lookup by id cannot pass for one by index. Negative controls, each caught: an unchanged set rewritten anyway, a refocus at galactic scale, the boot choice not recorded, and pins passed as ids. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
c3fcb2e481 |
Merge branch 'perf/label-scan' into feat/drawn-set-follows-view
The label fix turns the brightness order into an index with positions and ids laid out beside it. The star field only needs the order, so it is handed `.order`. Both sides added scene tests in the same place; both are kept. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
b071d87d8a |
Answer the review: walk the brightness order in memory order, and stop at the fifteenth label
The adversarial review confirmed a regression in this PR. Near the Sun, the label pass became three to four times slower than the scan and sort it replaced. Within about 11 pc of the Sun, and in any plan view zoomed tighter than that, the label radius clamps to 4 pc. That sphere holds a few dozen faint dwarfs deep in the brightness order, so the walk rarely finds fifteen stars to name and reads nearly the whole catalogue. Reading the star objects in brightness order jumps all over memory, so a full walk took 19-25 ms against the old 5-6 ms. The review also found that spreadLabels checked the label count at the top of its loop. After placing the fifteenth label it asked for a sixteenth candidate, which near the Sun can lie at the far end of the order. brightnessIndex now lays each star's position and id out beside the brightness order, in that order. The walk tests stars from those arrays in sequence and reads a star object only when it yields one. spreadLabels breaks straight after placing the fifteenth label. Measured on the real catalogue with the label logic reduced to what decides placement, camera at the given distance from the Sun (old sort / this PR as first pushed / now): 2 pc 4.9 / 24.7 / 2.5 ms 5 pc 5.7 / 23.0 / 3.1 ms 10 pc 6.3 / 18.2 / 0.95 ms 307 pc 22 / 0.01 / 0.00 ms (the opening view) The labels are identical in every case. Now faster than the old sort at every distance. A new scene test counts the candidates spreadLabels takes: exactly fifteen for fifteen labels. Negative controls, each caught: positions one axis off, ids in catalogue order, the selected star dropped, the radius edge excluded, and the count checked before taking a candidate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
7f8fb59f5d |
Merge main: another scheduled refresh ran with the pre-fix pipeline, keep this branch's data
The 2026-09-14 refresh (
|
||
|
|
8c69a7a8b2 |
Plot routes and build the jump-link graph in a Web Worker
Route plotting ran on the main thread, and so did the jump-link graph: - the range search for a far target, HD 2626 at 236 pc, takes 4-5 s; - the graph at 8 pc is 3.7 million links, 6-10 s to build, then as many link objects again to turn into vertices. The map stopped for as long as either ran. A Web Worker now does both. RoutingClient sends it the catalogue's ids and positions once, and it keeps its own spatial index. A route question comes back with the route, or with the range that would open one. A graph comes back as one Float32Array of segment vertices, transferred rather than copied. On the scene side, only the latest route request is shown: an earlier answer arriving later is dropped. Only the graph for the range last asked for is drawn. The Routes panel says "Plotting…" and holds its button while a request is out. collectJumpLinks gave way to jumpLinkSegments, which writes the vertex pairs straight into floats rather than building link objects first; the scene was its only caller. The routing module (routing.ts) is the message protocol and the one function answering it, so the worker is a dozen lines, and the same answers are worked out in place where there is no Worker, as in the unit tests' DOM. The worker is built with its own tsconfig, as the Angular builder expects. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
2d997e41db |
Draw the stars around wherever the view is, not only around the Sun
The star field draws a budget of the catalogue: everything within 25 pc of the Sun, then the brightest of the rest. That choice was made once, at load, around the Sun, and never again. On the Gaia catalogue it left most of the map empty wherever the view went: - a region 150 pc out drew 49 of the 442 stars within 25 pc of it; - a plotted route ran through stars no one could see or click. Sol to Almach at 8 pc passes 19 stars and drew 6, Sol to Mirfak 11 of 26; - a search for a faint star flew the camera to an empty point. The drawn set now follows the view. The scene chooses it again at the label cadence, once the orbit target has moved more than 5 pc or the pinned stars have changed. The budget goes, in order, to the selected star and the stars of a plotted route, then everything within 25 pc of where the view is centred, then the same around the Sun, then the brightest of the rest. The instance buffers hold the budget and are rewritten in place. Checked in Chromium on WebGPU, framing Mirfak from 12 pc: with the set chosen around the Sun, 122 of the 649 stars within 25 pc were drawn; following the view, all 649. At the opening view the drawn set is the same as before. A refocus takes 9 ms in the browser (5 ms of it choosing). The first version took 16-36 ms in the browser, a visible hitch during a flight. Most of that time went on walking the 423 651-star brightness order once per neighbourhood, out of catalogue order, and on recomputing 70 000 colours. Now both neighbourhoods are gathered in one pass in catalogue order and sorted on their own, and colours and sizes are computed once for the whole catalogue. The brightness order itself sorts a typed copy of the magnitudes, taking 83 ms at load instead of 104-139 ms. STAR_RENDER_BUDGET is now 70 000, and its comment gives the measurements behind it rather than "currently set to the whole catalogue", which stopped being true when Gaia landed. At 1920 x 1080 on a Ryzen 7700X: - on the RTX 4080, the whole catalogue costs the same 6.1 ms a frame as the budget; - on the processor's two-core Radeon, standing in for an entry-level laptop, every 100 000 stars costs about 4 ms: 112 fps at the budget, 44 at the whole catalogue, and the same under WebGL2; - drawn whole, the opening view turns into a grey wash that buries the labels and the host rings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
0a0b301807 |
Name the brightest stars by walking one order, instead of sorting 60 000 five times a second
The star labels are refreshed every 0.2 s. Each pass filtered the whole catalogue to the stars within the label radius, sorted them by magnitude, and turned every one into a label object, all to place at most fifteen. At the opening view the radius holds about 60 000 stars, so each pass was a 55-70 ms task on the main thread. A CPU profile of the opening view, on a Ryzen 7700X with an RTX 4080, counted 29 tasks over 50 ms in 6.7 s, one every 230 ms; updateLabels took 23% of the main thread. That is the stutter the frame-time bench measured on every GPU and every render budget. The catalogue is now sorted by brightness once, when it loads. brightestWithin walks that order and hands stars over lazily, and spreadLabels already stopped once it had placed fifteen labels, so a pass reads only the stars it looks at. The output is the same as before: the same stars, in the same order, with ties in catalogue order, the selected star named wherever it is, and a star exactly on the radius included. The spec checks it against the filter-then-sort it replaces. Stars are no longer scanned at all when the view is at galactic scale, where the result was thrown away. Profiled again on the same view: 0 tasks over 50 ms, and the scene's per-frame work over the window dropped from 2 028 ms to 342 ms. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
efe6667b00 |
Route with A* over numeric cell keys, so a route can reach past the Sun's crowd
The route search widened evenly from the departure, Dijkstra-style, with a budget of 20 000 stars. On the Gaia catalogue those are all within about 40 pc of the Sun, so it found no route to anything farther at any range: Sol to Mirfak (155 pc) failed at 3, 8, 15 and 30 pc alike. Every failure then asked minimumRangeBetween what range would work. That search widened the same way with a 30 pc ceiling, and it ran for up to a minute on the main thread before giving up with nothing. routeBetween is now an A* search. Each star is queued by the distance travelled to it plus the straight line on to the destination, on a binary heap rather than a linear scan of the frontier. It heads for the destination instead of flooding the core around the departure. minimumRangeBetween bisects the range, one routeBetween per step, because whether a chain exists can only become truer as the range grows. Its answer is always the longest hop of a route actually found, so a range it names always opens one. Its ceiling is now the Routes panel's own maximum, MAX_JUMP_RANGE_PC: a range the control cannot be set to is no answer, and raiseTo already clamped any figure above it. The spatial index keys its cells by one number packed from their three indices instead of an "ix,iy,iz" string. A search visits up to 125 cells for every star it expands, and building those strings was half of what a route cost. forEachWithin hands neighbours over unsorted and uncollected, which was most of the other half; within is now that, gathered and sorted. The no-route line said nothing in the catalogue bridged the gap; it now says no chain of jumps up to the panel's maximum reaches the star, which is what was searched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
43b9b1f081 |
Give the map a scale bar, and rings that say how far from the Sun
The map had one way to read a distance: the Range readout, a number for how far back the camera is. The local grid's five rings sat at 50 to 250 pc, fixed and unlabelled. They said nothing from inside a 2 pc hop, and nothing past 250 pc now that the Hipparcos stars Gaia places there are drawn. A scale bar now sits under the scale rail. It shows the longest round length (1, 2 or 5 x 10^n) that fits in 120 px, in AU inside a system and in parsecs or kiloparsecs outside. It is measured at the depth the view is centred on, since under perspective every depth has its own scale; under the plan view it is exact everywhere. The local grid's rings are now distances from the Sun, at a round step of about a fifth of the camera's distance and out past the camera: 50 to 350 pc from the opening view, 2 to 20 pc from twenty parsecs out. Each ring is labelled with its distance, on the side facing what the view is centred on, or across the far side of the grid when that is the Sun (the near side is under the camera and out of frame). The survey edge at 250 pc stays called out, as "Survey edge", whatever the step. The rounding lives in one place, scale-bar.ts, shared by the bar and the rings and tested there. Its formatter keeps three significant digits: one digit, enough for the bar's round lengths, printed the 250 pc ring as "300 pc". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
4eb61ff58e |
Draw each HYG star at Gaia's distance, and keep the ones Hipparcos misplaced
HYG and Gaia were both cut at 250 pc, each on its own distance. A star Hipparcos put at 200 pc and Gaia at 300 was kept by the first, never downloaded from the second, and drawn at 200. That is where 83% of the 9 691 mid-magnitude HYG stars without a Gaia counterpart came from, and at the median Hipparcos had them a third too close. The mirror case, Hipparcos outside and Gaia inside, dropped the HYG row and left its Gaia entry anonymous. Gaia's own Hipparcos cross-match (hipparcos2_best_neighbour, a fixed DR3 table of 99 525 rows) gives a usable Gaia distance for 97 751 of them. placementDistancePc keeps a star either survey puts inside the cutoff, and draws every kept star at the better measurement, inside the cutoff or not. 57 121 HYG stars now sit at Gaia's distance. 6 833 of them are past 250 pc: Zet Per 230 -> 259 pc, 35 Ori 137 -> 330, 44 Cnc 223 -> 613, and the farthest, HIP 69445, at 8.7 kpc. 3 666 stars that Hipparcos put outside are now kept, and 3 656 of them give a Gaia entry its name. The cross-match is required rather than skipped when unreachable. Without it, every one of those stars would move back to its Hipparcos distance, and the published map would flip with the archive's availability. The ESA TAP answered it with a 500 at first and in 102 s on the next try. So fetches now retry 5xx and network failures twice, after 30 s and 120 s, in the fetch every source goes through. The refresh job also carries the Gaia DR3 responses from run to run in the Actions cache: the release is frozen, and a live re-fetch has already reproduced stars.bin byte for byte. 423 651 stars (+10), 61 168 HYG rows folded into Gaia entries (+3 656), 351 597 unnamed designations (-3 656). 10 886 HYG survivors and 23 unmerged pairs under an arcsecond, both inside the merge gate's ceilings. The same 1 972 exoplanets have a host; KELT-4 A b and MWC 758 c now sit on their named star. The HUD's "Radius" becomes "Survey radius": 250 pc is where Gaia is surveyed to, and no longer the edge of the map. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
29d3ddb6ef |
Say so when Gaia is missing, rather than as 68 000 unmatched stars
With the merge gate in place, a Gaia DR3 outage no longer ships a HYG-only catalogue: the ETL skips the unreachable source, and validateMerge then fails on the survivor count. That is the right outcome and the wrong message: "68 000 HYG stars found no Gaia counterpart" sends the reader looking at the merge. Gaia contributing nothing is now checked first, by name. Two comments said Gaia was best-effort, in data-refresh.yml and on the merge in fetchStars. They now say what happens instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
f935bae3b3 |
Fail the ETL on a merge that keeps the same star twice
The catalogues are regenerated by a scheduled job that pushes straight to main once the unit suite and a production build pass in the same run. Both passed, every Monday, on a catalogue that carried 23 000 stars twice: the suite tests code against fixtures, and no fixture is 400 000 real stars. Nothing between the ETL and the map ever looked at what came out. Two numbers now have to hold, and each is the signature of a way the merge has actually failed here. Different catalogues placing a star within an arcsecond of each other is never two stars at this depth, and one catalogue does not list a star twice, so every cross-source pair that close is a miss. Nineteen survive today — each a second HYG row wanting a Gaia entry that already absorbed one, which is how Gliese lists some doubles — against 1 112 in the catalogue on main, where a Hipparcos parallax off by half outvoted a direction that agreed to a hundredth of an arcsecond. The ceiling is 100. The epoch failure leaves no close pair at all, because sixteen years of proper motion had already carried the two entries tens of arcseconds apart. What it leaves instead is HYG rows that found no counterpart: 36 056 on main against the 10 876 Gaia genuinely lacks — the stars it saturates on and the red dwarfs past its magnitude cut. The ceiling is 15 000. The pair sweep sorts by declination and walks a one-arcsecond window, so it costs about 300 ms on 423 641 stars — cheap enough to run on every ETL, which is the point: the gate has to sit where the bot already is, before the push, because a GITHUB_TOKEN push fires no CI of its own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
db511f7aa4 | Merge branch 'fix/merge-epochs' into fix/host-sky-match | ||
|
|
037545d036 |
Answer the review: a name two stars answer to names neither, and NaN is not a proper motion
Three guards the matcher was missing, none of which changes a byte of the regenerated data — the ETL re-run after them is identical — and all three now have a test that fails without them. A proper motion that is not a number poisoned every comparison rather than one: NaN loses every `<` it appears in, so `cosine < minCosine` was false for every star, each one reached the distance guard, and the last one in catalogue order won — a confident wrong answer, order-dependent, where the honest answer is "no match". The archive's own parser never produces one (parseOptionalNumber maps a blank cell to undefined), but the matcher is exported for offline re-cross-referencing and a caller reaching for bare Number() is exactly the coercion the CSV helper documents as having caused two prior bugs. An unusable motion now reads as no motion. Normalizing a name strips the dot, so `Gl 55.2` and `Gl 552` — two stars 135 degrees apart — share one key, and the index kept whichever came last; 64 such groups exist in the catalogue, among them `Gl 84.1A`/`Gl 841A` and `HD 96600` twice. A name that names two stars names neither, so ambiguous keys are dropped and the query goes to the sky, where direction settles it. No archive hostname lands on one today, which is why the data is unchanged. And the cache is keyed by the whole request rather than the query alone, here and in gaia.ts: fetchTextCached records only that some response arrived, so an endpoint edit would have kept serving the old host's bytes — the same silent staleness the query hash was added to close. The tests now discriminate what the comments claim. Eight mutants, each caught: judging only the published position, only the carried-back one, judging each star on its worse epoch rather than its better, letting a distance-rejected star claim best-so-far and shadow the true host behind it, an unguarded proper motion, a last-wins name index, a fixed angular tolerance instead of a transverse one, and no distance guard at all. The GJ 887 test grew a decoy standing halfway along the star's own track: it is nearer than Lacaille 9352 at the published position and nearer at the worse of the two epochs, so it wins unless both epochs are tried and the better one decides — the property the test's comment had been claiming untested. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
3df5396349 |
Merge main: the scheduled refresh ran with the pre-fix pipeline, keep this branch's data
The 2026-09-07 "Refresh the astronomical catalogues" commit regenerated
exoplanets.json and stars-index.json on main with the merge this branch
fixes, so both sides touched both files. Resolved by keeping this branch's:
they are the output of the reviewed pipeline, and the one substantive thing
main's side carried — the HYG designation-prefix casing from
|
||
|
|
080bbe16dc |
Match exoplanet hosts on the sky, at both epochs the archive might mean
The host cross-reference matched in 3D, nearest star within half a parsec. That is the wrong space for the same reason the star merge learned it: a direction is measured, a distance is inferred. At 170 pc half a parsec is a ten-arcminute cone, wide enough to hand the planets of stars our catalogue does not carry to whatever bright star floats nearest — HATS-6 b sat on HD 39500, seventy arcseconds away. At 60 pc it is tighter than the routine disagreement between the archive's Hipparcos distances and our Gaia ones, which is how four bright giants (7 CMa, HD 81688, omi UMa, xi Aql) lost their planets and GJ 15 A's landed on a neighbouring entry. Hosts are now resolved like stars are merged: by name first, then the nearest star on the sky within a transverse budget — angle times the archive's distance, 0.01 pc — whose distance does not flatly contradict the archive's (the merge's own 50 % ratio). The budget is transverse because the dominant error is proper motion over an epoch difference, a physical displacement that is the same in parsecs at every distance: as an angle it is 60" for Proxima and 2" for a host at 100 pc. Measured on the 504 hosts whose archive name matches a catalogue name outright, true pairs reach 3.4e-3 pc; shifting every host a quarter of a degree finds nothing else within 0.01 but Proxima's own entry, whose budget at 1.3 pc is wider than the shift. The archive never says which epoch a position is for, and they are mixed: alf Tau and GJ 273 publish J2000, HD 133131 and TOI-2459 publish Gaia's J2016. So the query asks for sy_pmra/sy_pmdec too, tries each position at both ends of those sixteen years, and judges a star on whichever is closer. Guess one epoch and a fast star's planets land on a companion: J2016 puts Aldebaran's on Gl 171.1B, J2000 puts GJ 15 A's on a Gaia entry 15.9" out. 1 972 of 6 354 planets now sit on a host, 1 548 before: 432 gained, 26 on a better star (GJ 15 A to Groombridge 34, GJ 676 A off its companion, HD 19994 to 94 Cet), 8 lost — six false 3D matches to stars the catalogue never contained, and GJ 273 b/c, whose archive row says 5.92 pc for Luyten's Star at 3.79: a distance in flat contradiction is exactly what the ratio guard exists to refuse, and the number to fix is upstream. The 2 pc "rematch" apparatus is gone. build.ts recomputed every match after fetchExoplanets had already written the file — at a different tolerance, so the log reported a match count the data did not contain — and the offline entry point that persisted it had no caller. One matcher, one set of constants, used once. The archive cache is now keyed by a hash of the TAP query, so a response cached before the proper-motion columns cannot serve rows without them, where a missing cell would quietly read as "does not move"; the row's astrometry is stored with each planet, which is what made these tolerances measurable offline in the first place. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
dc2ce08694 |
Answer the review: direction settles distance, brightness is one-sided, and a lost id stops the scene
Three findings from the adversarial review of the merge, all reproduced. The distance test was hiding 1 489 stars that sit under an arcsecond from their Gaia entry with a Hipparcos parallax off by half — thirty of them at a false few parsecs from the Sun (HIP 82724 at 3.7 pc, where Gaia has it at 62.8) — and the first audit did not see them because it counted residual doubles through the same 50 % filter. Under three arcseconds the distances are now not consulted: a coincidence of direction that close is never chance at this depth (the quarter-degree shift finds none), and the parallax is the thing to fix. Brightness keeps its say at any separation, and is now one-sided: a folded entry may be five magnitudes fainter (a red dwarf in V against G) but not one brighter, because an entry a magnitude brighter than what is already at that spot is a primary Gaia does not carry — Almach, Alfirk and Ashlesha had all been folded into their companions' entries, 93 in all. The sky grid wraps at 0h. The Gaia query orders by source_id after G, so the row order — and the ids assigned from it — is a function of the archive's content rather than of the server's plan for 20 064 ties; the cache key is a hash of the query. And a bookmark to a star id the catalogue no longer holds — 56 000 Gaia ids change with this — sent the scene through reconcileSelection, enterSystem, its decline, finishTransition and reconcileSelection again until the stack overflowed. The selection is cleared instead, at the one place every path goes through. Regenerated: 423 641 stars, 57 512 HYG identities on Gaia positions, no HYG id or name lost, no star within 20 pc left with an unclaimed Gaia entry under an arcsecond. 403 HYG survivors still have an unclaimed Gaia entry within 60": 13 under an arcsecond, where the brightness guard does not trust HYG's magnitude, and the rest components 3" to 60" from their counterpart. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QL6F9Bgfh8SgAiAAcPB9Hw |
||
|
|
08534279fb |
Bring Gaia to HYG's epoch before merging, and keep a star's name when it matches
Gaia DR3 gives positions for J2016.0, HYG for 2000.0, and the merge matched them on the sky to one arcsecond without propagating any proper motion. Sixteen years of motion is 62" for Proxima and 166" for Barnard's Star, so every star faster than ~62 mas/yr — most of the nearest ones — was kept twice, some 23 000 in all. The slow ones were matched, and lost: the merge kept Gaia's row whole, so 102 proper names, 1 336 Bayer/Flamsteed names and 32 000 spectral types became "Gaia DR3 <id>" and "Unknown", and 92 named exoplanet hosts handed their planets to their anonymous twin. Gaia is now asked for its proper motions and carried back to J2000 before it leaves the fetcher. HYG is placed from its own x/y/z columns, which are right where its `ra` is not: that column was carried from the Hipparcos epoch without the cos δ its motion needs, 17.9" off for Proxima. A match combines the two entries — Gaia's position, HYG's name, type, magnitude, colour and id — instead of choosing one. The tolerance is 15" with a five-magnitude guard, both set by measurement: 55 457 pairs sit under 1" once the epochs agree, the Gliese-only entries up to 12" (Ross 248), shifting every entry a quarter of a degree finds 16 chance neighbours at 15", and the guard keeps Sirius out of Sirius B's entry. Entries of one source are never merged with each other: the 1 411 Gaia doubles resolved under 1" are two stars, not one. Regenerated: 425 071 stars (was 447 410), 56 082 of them Gaia positions carrying HYG identities; no HYG id or name lost; the sixteen stars nearest the Sun carry no survey designation; 196 residual doubles, all components 17" or more from their counterpart. Five planets of four bright giants (7 CMa, HD 81688, omi UMa, xi Aql) lose their host link: their Gaia distance sits 0.7–1.1 pc from the archive's Hipparcos-based one, past the 0.5 pc the host match allows. Matching hosts on the sky rather than in space, as the merge does, is the follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QL6F9Bgfh8SgAiAAcPB9Hw |
||
|
|
7a112e4bb3 |
Answer the review: HYG's own last-resort name is a designation too
Junie: a HYG star that fell all the way through the ETL's naming chain -- no proper name, Bayer, Flamsteed, HD, Gliese or HIP -- is called "HYG <id>", and with `source: 'hyg'` the predicate was looking for a lower-case "hyg " prefix and calling it named. None in the current catalogue, but the path is in `tools/etl/fetchStars.ts` and a refresh could walk it. Fixed in the table rather than in the predicate: `hyg: 'HYG'` next to `gaia: 'Gaia DR3'`, so the encoder, the decoder and the predicate all read the one rule. The sourceless case reads the same entry instead of repeating it. npm test 609/609, build and ETL typecheck clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014fcUfL82nvyh9VebX1Fz6w |
||
|
|
f14e252b19 |
Name the neighbours that have a name, before the ones that only have a number
The neighbour ring exists to say where you are. Since the catalogue refresh it has been spending one of its four places in Sol on "Gaia DR3 5853498713190525696" -- a nineteen-digit survey id for the star printed beside it as Proxima Centauri, the same star twice -- and that duplicate row pushed Barnard's Star off the ring altogether. 91.9% of the refreshed catalogue is named that way. Named stars now come first, and survey designations fill in only where fewer than four named ones are in reach. The line between the two is the one the catalogue format already draws: a name is a designation when it is what the star's source would generate for it. Judged by the prefix rather than by rebuilding "prefix id" from the row, because the number after "Gaia DR3" is the survey's own id, which the 32-bit row id cannot hold -- a round trip through the id would have called every one of those stars named. The preference lives on the index as `nearestPreferring`: the preferred pass exhausts the search before the fill runs, so a named star is never outranked by a nearer unnamed one. That is the whole point of asking. The end-to-end spec names Barnard's Star again, on purpose. The four nearest named stars to the Sun are a fact about space, not about which catalogue was refreshed last, and without this change that is exactly the label that vanished -- checked by running the spec with the preference stashed: it fails on that line, and passes with it back. npm test 609/609, npx playwright test 16/16 under CI=true --workers=2. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014fcUfL82nvyh9VebX1Fz6w |