Commit Graph
214 Commits
Author SHA1 Message Date
SenrokaiandClaude Opus 5.5 a9e910c2ea Keep a body page's camera on its Sun's side when the clock takes the Sun across the equator, aimed at the body in that frame
The page put its camera on the side of the equator its Sun lights once, when a body was shown.
The Clock tab the same page gained in 74535ac changes the date without showing the body again, so
on Saturn's page, opened today with the Sun and the camera south of the rings, Go to 2045 moved
the Sun 25.7 degrees north (sunLight y +3.063) and left the camera south (-0.6): the unlit face
of the rings, the view the camera side was there to avoid. A clock left running through the May
2025 equinox did the same.

tick now remembers which side the Sun stood on at the last frame, and moves the camera across
only when that side changes: when a body is shown (the side is reset to 0 then) and when the Sun
crosses the equator, as the clock runs or is set. Between crossings the camera is the reader's to
orbit, below the rings if they like. The move came after the controls had aimed the camera for
the frame, which was drawn straight after with the body 22.6 degrees (2 atan(0.6/3)) off the
middle of the view; the camera now looks at the controls' target again before that frame.

Live on :4301 (this worktree's ng serve), Saturn's page opened today: camera -0.6, Sun -0.935.
Clock tab, 2045-06-01T00:00, Go: camera +0.6, Sun +3.063, the rings' lit face; 265 frames drawn
across the move, every one aimed within 0.00 degrees of Saturn. The camera then put south by
hand stayed there (-0.6 a second later). On Earth's page the Sun crosses twice a year, so at a
month a second the camera changes side about every six seconds.

Tests: the page followed into 2045 with the camera north and aimed at Saturn in that frame; and a
camera the reader takes over the rings while the Sun stays south is left there, which the old
single-flip rule only held through the flag it cleared and no test covered. Guarded mutants (full
suite), each failing only its named test: the side chosen only when a body is shown ('follows the
Sun across Saturn's equator'); the side never remembered, so the camera is mirrored back every
frame ('leaves the camera where the reader orbits it'); no lookAt after the move ('follows the
Sun across', on the aim).

Unit suite 860/860, tsc -p tsconfig.app.json and etl:typecheck clean.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 17:27:27 +02:00
SenrokaiandClaude Opus 5.5 2f49002027 Check that a redrawn orbit line reaches the GPU, and hold Saturn to the chords' own sag
The redraw test read the reshaped line's points on the CPU, which reshapeOrbitLine rewrites
whether or not it then sets needsUpdate. three uploads a buffer again only when its version
rises, which only that setter does, so a renderer that dropped the line left J2000's ellipse on
screen and passed the whole suite (857/857). The test now takes Saturn's position version after
drawing J2000 and expects it higher once the clock is at AD 1.

Its Saturn bounds, 0.002 AU at AD 1 and 0.0015 at J2000, sat inside the 128 chords' own sag, which
reaches 0.0032 AU near aphelion, where points spaced evenly in true anomaly lie furthest apart:
the test passed only because Saturn falls near a vertex on those two dates, and a line correct by
construction failed it (Saturn without its a, e and i rates: 0.00229 AU). Both are now 0.0035,
which still catches the line left unreshaped (0.054 AU at AD 1). The renderer's comment put the
sag at 0.003 for Saturn and 0.0005 for Mars; it now says 0.0032 and 0.00055, and where.

Guarded mutants (full suite):
- position.needsUpdate dropped: only the redraw test fails ("expected 0 to be greater than 0");
- the reshapeOrbitLine call dropped: only the redraw test fails;
- Saturn's a, e and i rates removed from bodies.json, line and marker on one ellipse: the
  distance bound now passes (0.00229 < 0.0035); the redraw test still fails, on the version, as
  it should with nothing to redraw, and so does Saturn's AD 3000 Horizons test (0.41 degrees).

Unit suite 858/858, tsc -p tsconfig.app.json and etl:typecheck clean.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 17:22:28 +02:00
SenrokaiandClaude Opus 5.5 74ea1d655e Turn a locked moon's pole round with its drawn node, so its axis stays on its orbit at every date the clock reaches
The IAU carries a locked moon's pole round its orbit normal on a term of the node's angle, as a
moon in a Cassini state keeps it, but at the node rate the IAU's source had: Miranda's U11 at
-2024.22 degrees a century where the JPL table its orbit is drawn from has -2030.80, Mimas's S3 at
-36505.5 against -36511.16. Over AD 1-3000 that parted Miranda's drawn axis from its drawn orbit
normal by up to 7.89 degrees (AD 9), so Uranus swung 7.6 degrees north and south on its sky every
1.41 days, and Mimas's by 2.63. Only Iapetus's pole had been put on its orbit.

lockedToOrbit now sets every periodic term whose angle turns within 5 per cent of k times the
drawn node rate (k from 1 to 9, the most the report takes, Triton's) to exactly that multiple, and
moves its constant so the angle, and the pole and W with it, are unchanged on 2025-01-01 (the
2025 pole and W of every changed moon are identical to the 1e-14 degree). Measured: the largest
offset taken is Ganymede's J5, 3.4e-2, then Rhea's 1.2e-2; the nearest term that is not a node is
6.0e-2 out (a W-only term of Miranda's), and Umbriel's W has one 1.0e-2 from ten times its node,
which the k limit leaves. Ten moons change: Deimos, Io, Europa, Ganymede, Mimas, Tethys, Rhea,
Miranda, Triton and Proteus. The Moon and Phobos, whose W carries a quadratic, are left as before,
and so is Callisto's J6, 40 per cent from its node rate.

Worst angle between the spin axis and the drawn orbit normal over AD 1-3000, every 135 days,
before and after: Miranda 7.89 -> 0.42, Mimas 2.63 -> 0.47, Rhea 0.77 -> 0.17, Triton 0.51 ->
0.15, Europa 0.33 -> 0.13, Ganymede 0.50 -> 0.16. Faces: Miranda 2.75 -> 2.39, Mimas 8.94 ->
8.89, Deimos 2.14 -> 2.08, Triton 2.88 -> 2.81; none got worse.

build.ts now checks that angle for every locked moon over the same 8114 dates as the face check,
at most 1 degree (Tethys 0.97, whose IAU pole sits 0.69 from its orbit today; Titan 0.94, whose
IAU pole is still while its node turns in 705 years), with four named ceilings: the Moon 7.1 (its
real 6.7-degree tilt, 6.98 at worst), Phobos 2 and Deimos 2 (1.81 and 1.74) and Proteus 1.2
(1.09), whose IAU poles nod with Mars's and Neptune's precessing poles while the Laplace poles
their orbits are drawn round are fixed. The obliquity check at Horizons' epoch shares the new
axisFromOrbitDeg with it. Nothing checked the axis against the orbit before: an Iapetus pole left
on its Laplace pole, 8.30 degrees off at every date, passed the ETL and the suite.

Guarded mutants, each through the solar ETL and then the suite on the data it wrote:
- node terms left at the IAU rates: the ETL fails with "Moon mimas's spin axis leans up to 2.63
  degrees ... (at most 1 expected)", and the suite with only the new test failing;
- Iapetus's pole put on its Laplace pole, W re-phased so its face today is unchanged (worst face
  16.41, under its 16.5 ceiling): the ETL fails with "Moon iapetus's spin axis leans up to 8.30
  degrees", and the suite with only the new test failing.

Also: lockedToOrbit called the Iapetus normal's circle "8.3 degrees across"; 8.3 is its radius (the
row's i = 8.298 to the Laplace plane) and it is 16.6 across. The README credited every rotation to
pck00011 without saying that a locked moon's W and node terms are re-rated to its JPL mean elements
and Iapetus's pole carried round its orbit normal; both of its lines now say so, as the body
model's doc does.

Unit suite 858/858, etl:typecheck and tsc -p tsconfig.app.json clean, solar ETL passes on the
real data.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 17:17:54 +02:00
SenrokaiandClaude Opus 5.5 a15a46c617 Correct what the ETL, the code and the textures README said of their own sources and figures
- The IAU day check said it compared W with the period Horizons states. That holds for the eight
  planets and Phoebe only. Pluto's and Ceres's periods are the IAU's own rate restated (8.5e-12 and
  3.3e-10: Horizons' Pluto period is 360 over its W, and the SBDB notes it derived Ceres's from the
  report's 952.1532 degrees a day), and the 22 locked moons' is their orbit's, from JPL's table, not
  from their Horizons pages ("Synchronous" on eighteen, nothing on Titan's or Proteus's), and now
  their W's own rate. The comment, the log line and the error message say which is which; the log
  shows 0.0e+0 for the twenty locked moons turned at their orbit's rate, 1.1e-8 and 3.1e-7 for the
  Moon and Phobos. BodyRecord.rotationPeriodHours says that where a source states no period, or
  one a later measurement overturns, it is the one the ETL spec carries (Nereid's, Eris's), where
  the previous commit had Eris among the bodies whose source states none.
- Phoebe's spec justified its period by a note in the satellite table, which is about another
  source (Jacobson 2000, Jupiter's outer moons) and says the table carries corrected values. The
  row's n is right as the table defines it, the rate of the mean longitude: n less twice the node's
  rate is 0.6541855 degrees a day, against 360 / 550.30391 = 0.6541840. What made Phoebe drift is
  that the propagator reads a retrograde moon's n as its sidereal rate, as Triton's row gives it.
  The comment now says so; the period, 550.30391 days, is kept.
- body-orientation.ts and its spec said the IAU's W for Earth, "taken at UT", left its face 2.3 and
  4.5 degrees off Horizons at AD 1000 and AD 1. The old code took it at UT + 69.184 s; those are
  that figure's, and at UT itself they are 2.0 and 4.2, as three reviewers measured through the
  code (4.25, 4.25 and 4.28 at AD 1).
- The renderer said the 28 maps entering the Sun's system are about 20 megapixels of JPEG. Read
  from their frame headers: 37.75, nine at 2048 by 1024 with the Sun's, Jupiter's at 3840 by 1920
  and eighteen smaller.
- The textures README gave Titan 0.02 per cent unmapped, counting only the source's zeros. Its
  largest gap is a flat grey of the source's own (147 and 148, its two commonest values), which
  NASA's caption for PIA19658 names as the gap in coverage: measured on titan.jpg, one region of
  1.09 per cent of the pixels, 0.87 of the sphere, at 48-68 N and 37 W to 25 E. The row says 1.1 per
  cent and where, and step 1 says how it is counted; commit 302fa96's "the rest under 0.2%" is
  therefore not true of Titan.

No behaviour changes but the ETL's log and error wording; the solar ETL passes with the new text.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 16:16:53 +02:00
SenrokaiandClaude Opus 5.5 dadae7b8ca Give Haumea's card its three semi-axes beside its mean radius
Haumea is triaxial, 1161 x 852 x 513 km (Ortiz et al. 2017, Nature 550, 219), and is drawn as the
sphere of its volume, 797.6 km. Its card listed "Radius 798 km" under Measured and said nothing of
its shape, which only a code comment and a commit message gave: its long semi-axis is 1.46 times
that radius and its short one 0.64.

BodyRecord takes semiAxesKm, set by the ETL from Haumea's spec, and the card then reads "Mean
radius 798 km" and "Semi-axes 1,161 x 852 x 513 km". Every other body keeps its one Radius row. The
ETL checks that a body's radius is the mean of its semi-axes, the radius of the sphere of the same
volume, to 0.1 per cent (797.6 against 797.62).

Measured on :4301, Haumea's page: "Mean radius 798 km, Semi-axes 1,161 x 852 x 513 km". Test: the
card of Haumea as shipped in bodies.json. Guarded mutants: the semi-axes dropped from the spec (run
through the solar ETL), the row, or the view model, or the label left as Radius, each fail it and
only it; a radius that is not their mean fails the ETL ("Haumea's radius, 1161 km, is not the mean
of its semi-axes").

Horizons gives triaxial radii for Phobos, Deimos, Miranda and Ariel too, which the page parser
already reads into their mean; they are not carried here.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 16:12:30 +02:00
SenrokaiandClaude Opus 5.5 fc7677e715 Turn Nereid in the 11.594 hours Kepler measured, where it was drawn still
Nereid's Horizons page states no spin, and the ETL, finding none, left it still; the validator's
comment read that as "Nereid has no spin". Its rotation is measured: Kepler's K2 light curve gives
11.594 +/- 0.017 hours, confirming earlier ground-based periods (Kiss et al. 2016, MNRAS 457, 2908;
arXiv:1601.02395). Its spec now carries that day, as Eris's carries Bernstein et al.'s, and with no
known pole it turns about its orbit normal, as Eris, Haumea and Makemake do. The free-spinner check
accepts it (11.594 hours against a 360-day orbit).

A new validator: a moon without a lock must have a day unless it tumbles, and only Hyperion
("Rotational period = Chaotic") does. Nereid, left without one, fails it: "Moon nereid is drawn not
turning, and is not known to tumble". The renderer spec's example of a body left still was Titan,
said to have no period on Horizons, though it carries its orbit's; it is Hyperion now, and
BodyRecord.rotationPeriodHours says where each kind of period comes from.

Measured: the solar ETL passes; in bodies.json only Hyperion has no rotationPeriodHours; live on
:4301 Nereid's marker turns 60.000 degrees in a sixth of its day. Test: Nereid, as shipped, turns 60
degrees in 1.93 hours. Guarded mutant, the day removed from its spec and run through the solar
ETL: the validator fails, and the suite on the data it wrote fails that test and only it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 16:02:30 +02:00
SenrokaiandClaude Opus 5.5 e8e857ec25 Hold Io and Europa to their own track ceilings, and Hyperion's one-date ceiling to just above its offset
Io's and Europa's periapses turn backwards, held by the Laplace resonance (apsidesRegress), which
brings them to 0.07 and 0.23 degrees of Horizons from 1950 to 2100. Nothing guarded the flag: with it
dropped the ETL still passed, Io at 0.96 and Europa at 2.24 under the general 3-degree track
ceiling, their cards quietly rewriting themselves to "within 1.0" and "within 2.3". They now have
ceilings of 0.2 and 0.5, as Tethys has for the term it takes from its W. The comment on the
one-date check, which said it catches the periapsis run the wrong way, now says it does not
(0.904 against 2.5) and which check does.

Hyperion's one-date ceiling was 21 degrees, described as just above its offset on that date, where
it is 9.413: the 21 was its worst over twelve dates in an earlier check. It is now 10.

Measured on the real catalogue with the solar ETL: Io 0.07, Europa 0.23 at worst, Hyperion 9.413,
all passing. Guarded mutants, run through the solar ETL: apsidesRegress removed from both specs
fails with "Io's mean elements put it up to 0.96 degrees ... (at most 0.2 expected)"; Hyperion's row
misread by 6 degrees of node fails the one-date check at 15.41, which 21 let through.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 15:59:09 +02:00
SenrokaiandClaude Opus 5.5 3b4fd1af6c Turn each locked moon at its orbit's rate and Iapetus's pole round its orbit, so they face their planets at every date the clock reaches
The IAU gives a locked moon's W the mean motion of whichever orbit its authors had, and JPL's table
has another. Near the present the difference is nothing; over the clock's AD 1 to 3000 it turned
Proteus's far side to Neptune at AD 1 (146 degrees), Mimas 52 degrees from Saturn and Miranda 23.
Iapetus was worse for another reason: its IAU pole is a straight line, 3.9 degrees a century in
right ascension, through its orbit normal's 3 439-year circle round the Laplace pole, which by AD 1
has run past the celestial pole (Dec 97.9), 11 degrees off the orbit, with the face 87 degrees
from Saturn. Mimas and Iapetus carry mission maps, so a wrong hemisphere was drawn facing Saturn.
The ETL's lock check sampled only 1950-2100, so none of it failed.

tools/etl/lib/locked-spin.ts, lockedToOrbit, called for every locked moon:
- W's rate becomes the orbit's own mean motion, its constant moved so W is unchanged on
  2025-01-01; the pole and every periodic term stay the IAU's. A W with a quadratic is left
  (Phobos's orbit already takes it; the Moon's is its tidal slowing, 0.75 degrees at AD 1). The
  kernel's rate must be within 1e-5 of the orbit's first (at most 3.4e-6, Iapetus).
- Iapetus (poleFollowsOrbit): the pole follows its orbit normal, as a moon in a Cassini state does,
  in the IAU's own form: sines of the node's angle and four harmonics on right ascension, cosines
  on declination, fitted to the normal's circle and pinned to the IAU pole at the present; W takes
  sines of the same angles, fitted to hold the face where it is today.

build.ts samples the lock over AD 1 to 3000 (8 114 dates, every 135 days) instead of 1950-2100, and
subPlanetLongitudeDeg moved to the lib, shared by both. Measured on the real catalogue: at most
5.36 degrees (Titan) but the Moon 7.62 (its eccentricity, and W's quadratic at AD 1: a new named
ceiling of 8), Mimas 8.94 (ceiling 11 -> 9.5) and Iapetus 15.95 (19 -> 16.5, 9.4 of it its row's
lag); Proteus's own ceiling of 9 is gone, at 2.66. Iapetus's axis stays within 0.74 degrees of its
orbit normal (11.06 before) and its pole is the IAU's at the present to 1e-4 degrees.

Live on :4301, the longitude facing the planet at AD 1 / 1000 / 2025 / 2999: Proteus 2.6 / 2.6 /
2.6 / 2.6 (was -146.5 / -72.9 / 2.6 / 74.5), Iapetus -15.9 / -15.4 / -15.3 / -9.3 (-87.0 / -50.9 /
-15.3 / 22.8), Mimas 4.1 / 6.8 / 5.7 / 8.4 (48.6 / 29.3 / 5.7 / -13.0), Miranda -0.1 / 2.2 / 0.0 /
1.4 (-23.4 / -9.6 / 0.0 / 12.6); the present is unchanged.

The renderer spec now takes the rotational elements from bodies.json too, so no hand copy is left,
and a new test turns Proteus, Miranda, Mimas and Iapetus to their planets at AD 1 and AD 3000.
Guarded mutants, each run through the solar ETL and then the full suite on what it wrote:
lockedToOrbit bypassed (validator: Mimas 52.30, ceiling 9.5; the new test fails), Iapetus on the
IAU's straight pole (98.48), and its pole round the orbit without W's terms (73.13); each fails the
new test and only it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 15:58:04 +02:00
SenrokaiandClaude Opus 5.5 09eaf9532f Keep the Display panel shorter on a phone, and fold it away once a date is set
The date form made the Display panel 400 px tall at 360x640 (from 257) and 363 at 390x844 (from
220), and the Sun's system sat behind it: every orbit at 360x640, 81 per cent of their points at
390x844. A reader who set a date could not see what it did without closing the panel.

The window's description is one line, "AD 1 to AD 3000, where the planets' elements hold." (the
sentence on how far each moon's orbit strays is on each card and in the system note), and below
sm the field shrinks so Go stays on its line. Measured on :4301 with the panel open: 318 px at
360x640, Go at y 501 beside the field at 500; 318 px at 390x844, where 9 per cent of the orbits'
points are behind it and 37 of the 39 lines show.

At 360x640 the system is framed behind even the old 257 px sheet, so a date submitted with Go on
a narrow viewport now folds the sheet, as choosing a search result already does. Measured: after
Go at both sizes the panel is gone and the strip reads "Date 2020-12-21". Wide screens keep it
open.

Tests: the sheet folds after Go on a narrow viewport, and stays open on a wide one; jsdom has no
matchMedia, so the spec gives the dock one it can turn narrow. Guarded mutants: no fold fails the
first, a fold on every screen the second (and "jumps the clock to the date submitted", which then
cannot find Back to now).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 15:44:02 +02:00
SenrokaiandClaude Opus 5.5 74535accc5 Show the date and the clock on a body's page, and open it on the side of the equator its Sun lights
Since the page turns a body as it stands at the map's date, it has been drawn for a date it never
showed, at a rate it gave no way to change: set to 2032 at a day a second in the system view,
Saturn's page ran on at that rate with only Search and Bookmarks on its dock, and at a month a
second Earth turned about 183 degrees a frame at 60 Hz. The dock now takes `clock`, which offers
the clock without the layers, as a tab named Clock, and the page binds it with the date strip the
system view already has. Measured on :4301 at 2032-06-01: the strip reads "Date 2032-06-01", the
tabs are Search, Bookmarks and Clock, and the Clock panel has the four rates and the date field;
Back to now clears the strip.

The camera opened 11.3 degrees north of the equator whatever the season, which since 2025 put
Saturn's page on the unlit face of its rings, and will until 2039. When a body is shown, the next
frame puts the camera on the side of the equator the Sun is on: measured on Saturn's page, the
Sun at -26.71 degrees on 2032-06-01 and the camera at -11.31; Earth's in June stays north.

Tests, each proved by a guarded mutant that fails it:
- the page follows the clock after its first frame (frozen at the first frame: 90 degrees out);
- a body with no IAU model shown after Earth gets the page's own light back (left at Earth's Sun);
- the page's dock shows the date and a Clock tab, and no date at the present (date never set, or
  the dock bound without the clock);
- the dock offers the clock alone as a Clock tab (no tab, or a tab still named Display);
- Saturn's page in 2032 opens with the camera and the Sun both south (camera held north).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 15:42:58 +02:00
SenrokaiandClaude Opus 5.5 28fa79e1e0 Check the Horizons vectors against the bodies.json the app ships, and every rate Standish's rows give
The frozen Horizons tests ran on a hand copy of seventeen records, so an ETL that lost Standish's
a, e and i rates, or Io's and Europa's backward periapses, wrote a bodies.json that passed both
its own validators and the whole unit suite: the data-refresh job's "Unit tests against the new
data" read none of it. record() now takes kind, orbit, rates, laplacePole, parentBodyId and
massRatio from src/assets/data/bodies.json, read with node:fs as texture-catalog.spec.ts reads its
JPEG, and the copy is gone. Today's data passes as the copy did (all seventeen were identical).

The parser test checked only the mean motion and the periapsis rate on Earth's row. It now checks
the node, a, e and i rates too, against Standish's Table 2a (-0.24123856, -0.00000003, -0.00003661,
-0.01337178 a century). Dropped, those rates move Saturn 0.66 degrees at AD 1 (node) and 0.36 at
AD 3000 (a, e, i), where no date from 1950 to 2100 shows more than 0.036.

Guarded mutants, each run on the full suite:
- bodies.json without the a, e and i rates, as that ETL writes it: 'puts saturn within 0.1 degrees
  of Horizons on JD 2816787.5' and Earth's fail (and the new orbit-line test, on the same data).
- bodies.json with Io's and Europa's periapsis rates turned positive: Io's and Europa's 1950
  tests fail.
- the parser without its a, e and i rates, and with a node rate of 0: 'gives the rates per day'
  fails, and only it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 15:25:41 +02:00
SenrokaiandClaude Opus 5.5 3e1bd33b6c Draw every body in a system on one shared sphere, so returning to the Sun's system is no long task
Each marker built its own 64 by 32 SphereGeometry, and the Sun's system now has 38 of them: the
renderer's constructor took 15 ms, 12 of them building spheres, and with their first upload a
return to the system made a long task of 52 to 70 ms that the base's 18 bodies never did.

Every marker is now the one unit sphere, scaled to its radius, which it keeps in
userData.radiusAu. The shared sphere is never disposed; Saturn's ring is built in the sphere's
own units, since it is the marker's child; keepMarkersLegible reads the stored radius and scales
against the sphere's.

Measured on :4301, eight returns to the Sun's system each (select null, then 0, at 1600x1000):
before, a long task on 3 of 8 (52-57 ms), swapToSystemSpace 13-17 ms and the first render 30-40;
after, no long task on 8 of 8, the swap 2.7-4.4 ms and the first render 20-38. Earth is drawn at
the same 0.656 AU at arrival, and every member shares one geometry.

Tests: one sphere for every marker, each at bodyMarkerRadiusAu of its radius, and not disposed
with its system; Earth held to its 3-pixel floor at the arrival framing, which no test covered.
Guarded mutants, each failing only its named test: a sphere per marker, the shared sphere
disposed, the ring built in AU inside the scaled marker ('picks Saturn through its rings'), and
the legibility scale divided by the body's radius.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 15:14:02 +02:00
SenrokaiandClaude Opus 5.5 c2683da37b Redraw a planet's orbit line as its axis and eccentricity drift, so Saturn stays on it at AD 1
The orbit lines kept the shape of the J2000 elements and only turned with the node, while the
markers moved on Standish's drifting a and e. Saturn's eccentricity falls 0.00032 a century, so
at AD 1 its line passed 0.056 AU (8.4 million km) from Saturn, Jupiter's 0.016 AU from Jupiter,
and Pluto's 0.021 AU from Pluto at AD 3000. The comment that said no drawn line shows the drift
weighed one century of Pluto's axis, not twenty of Saturn's eccentricity.

update() now writes the line's 129 points again once |da| + a |de| since they were drawn passes
1e-4 AU, well under the 128 chords' own sag. Measured in the app on :4301, marker to its own
polyline: Saturn 0.0016 AU at AD 1 and 0.0028 at AD 2999, Jupiter 0.0014 at AD 1, Pluto 0.0078
at AD 2999 and 0.0082 today, all the chord sag.

The existing Mars test checked only that Mars stays in its line's plane. A new test measures the
distance to the drawn chords: Saturn 0.0017 AU and Mars 0.0005 at AD 1 (0.054 and 0.0022 without
the redraw). Guarded mutants: the call removed, and the threshold raised to 1 AU, each fail it
and only it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 15:01:08 +02:00
SenrokaiandClaude Opus 5.5 d449214309 Stop calling WASP-108 b imaged, and stop saying no map exists of an imaged exoplanet
48fd6fd took the archive's ima_flag as it stood, so WASP-108 b's card said it had been imaged as a
point of light beside its star. It is a transiting hot Jupiter (period 2.676 d, 0.04 AU out, 0.15 mas
at 258.8 pc). Its flag comes from Bohn et al. 2020 (A&A 635, A73), a VLT/SPHERE survey of transiting
planets' host stars, which imaged a 0.35 solar-mass companion 0.124" from WASP-108, not the planet.
The imaged query now also asks for tran_flag=0. The archive flags exactly one transiting planet as
imaged (queried today: WASP-108 b); with it left out the list has 101 names, the old 102 less that
one, and still above the validator's floor of 95. The new URL is cached under its own name, and
exoplanets.json, regenerated by the full ETL, changes by that one record's field and nothing else
(checked record by record against HEAD).

build.ts now fails if WASP-108 b is marked imaged. The count cannot see a false positive, and its
record carries neither a period nor an axis, so no separation check could. Control: the full ETL on
the old query (its cache still present) fails with "WASP-108 b is marked as imaged; it transits, and
only a companion star beside it was imaged (Bohn et al. 2020)."

The imaged sentence ended "and no map of it exists". Luhman 16 b, one of the 101 and a brown dwarf,
was mapped by Doppler imaging (Crossfield et al. 2014, Nature 505, 654). It now reads "and no map of
it is used here", which holds for all of them; the doc comment names Luhman 16 b. Control: the old
sentence fails "says a directly imaged exoplanet was seen as a point of light" only (1 failed, 843
passed of 844). Live on :4301: WASP-108 b and Kepler-22 b read "no image of this world exists";
Luhman 16 b and HR 8799 b read the new sentence.

48fd6fd missed a third copy of the claim it corrected: the body page's comment still said every
exoplanet gets a derived surface "since none has ever been imaged". It now says none has had its
surface imaged, as planet-appearance.ts and the README do.

A correction to 48fd6fd's message: HR 8799 b, c and d are Marois et al. 2008 (Science 322, 1348),
and e is Marois et al. 2010 (Nature 468, 1080), not "b to e (Marois et al. 2008)".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 23:23:11 +02:00
SenrokaiandClaude Opus 5.5 936d1c01f8 Leave a system outwards from wherever the camera stands, and test that the scene frames the furthest it draws
Leaving a system flew the camera to a fixed 400 AU. Since c45d916 frames the Sun's system on Eris's
97.7 AU aphelion, a phone held upright arrives 508 AU out, so leaving drew the system 21 per cent
nearer during the 0.9 s exit (review, 390x844: 507.9 -> 400.0 AU). The exit now flies to 400 AU or
half as far again as the camera already stands, whichever is further. Measured live on :4301, the
distance from the Sun through the exit, first and last frame:

  390x844    507.9 -> 761.8 AU   (was 507.9 -> 400.0)
  360x780    508.5 -> 762.7
  768x1024   312.9 -> 469.3      (was 312.9 -> 400)
  1600x1000  234.7 -> 400.0      (unchanged)

and never nearer in between. The swap puts the camera at GALAXY_APPROACH_DISTANCE_PC whatever the
exit distance, so only the animation changes.

No test checked that the scene hands outermostRadiusAu to the framing: framing on the 67.9 AU
semi-major axis instead, which on a square window puts Eris off screen on arrival, passed all 841
tests. The scene spec's fixture held Earth alone. A describe now adds Eris (a = 67.934, e = 0.4382):
- "frames the furthest the system draws" requires the settled camera to stand at
  systemFramingDistanceAu(outermostRadiusAu) from its target, 234.7 AU at aspect 1.
- "leaves the system outwards even from a phone's framing" enters at aspect 390/844, then samples the
  camera each frame of the exit until the swap: never nearer, and further at the end.

Controls, each run on the full suite:
- outermostRadiusAu -> maxTopLevelSemiMajorAxisAu in the scene fails both (2 failed, 842 passed);
  the second fails too because the arrival is then under 500 AU.
- Math.max -> Math.min in the exit (back to 400 AU) fails the exit test only (1 failed, 843 passed).

The framing spec's fit() said it was "what the scene actually composes"; it frames the ring only, so
its comment now says it is the grid's half and where the aphelion is tested.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 23:17:00 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 23:16:48 +02:00
SenrokaiandClaude Opus 5.5 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. c45d916 said
  303 of the 1 190 exoplanet systems have an aphelion past their grid ring, but both tests built the
  system from a BodyRecord. The new case is HD 20782 b (a = 1.3649 AU, e = 0.95), the most eccentric
  of them, whose aphelion is 1.66 times its 1.6 AU ring. Control: feeding eccentricity 0 for
  exoplanets fails "reaches an exoplanet's aphelion too" only (1 failed, 843 passed of 844).
- A derived surface resets its marker to white once painted, as a photograph does. Without it every
  exoplanet, the five Uranian moons, Proteus, Nereid, Hyperion, Eris, Haumea and Makemake would show
  their texture multiplied by the kind's flat colour. "paints them after the system is built, one a
  task" now asserts the exoplanet magenta before and 0xffffff after. Control: deleting
  material.color.set(0xffffff) on that path fails that test only (1 failed, 843 passed).

The AD 1000 Earth test's comment said the IAU W taken at TDB left the drawn face 6.6 degrees off.
6.6 is only the turn ΔT adds (1 572 s at 360.9856 deg/day); it adds to the W's own lead, so the
face is 8.57 degrees off. Measured here by putting Earth back on the IAU W at TDB in the renderer:
"expected -8.565166921766826". The 2.3 degrees the comment gives for the old code is the W at
UT + 69.184 s; at UT itself it is 2.0, and the comment now says both.

cbe4908's table also gave the 2025-06-01 12:00 row as "+0.06 before, -0.003 after". Two reviewers
measured the same Horizons point (1.579501 E) at +0.087 before and +0.0034 after: the row should read
+0.09 and +0.003. The message cannot be changed without rewriting history, so it is restated here.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 23:16:40 +02:00
SenrokaiandClaude Opus 5.5 acc4b928f2 Say that Makemake's day is known only to a factor of two, citing both readings
f655a4c kept Makemake's SBDB period, 22.83 hours, saying "nothing later overturns it", a reading
its author had not checked. The SBDB flags that period as Eris's was ("may be wrong by 30 percent
or so"). Hromakina et al. 2019 give 22.8266 h as the double-peaked period a "possible lightcurve
asymmetry suggests", of a light curve that repeats every 11.4 h. Kiss et al. 2024 (arXiv:2410.22544),
with TESS and Gaia, find the 11.401 +/- 0.076 h single peak again, "cannot confirm that the
double-peaked 22.8 h is Makemake's true rotation period", and take 11.4 h as their default.

Nothing is changed in what is drawn: the literature does not settle which it is, and 22.83 stays as
the SBDB's. The Makemake spec now cites both papers and says the drawn day may be twice the real
one, and the renderer's comment no longer calls the three dwarf planets' periods known.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 21:50:29 +02:00
SenrokaiandClaude Opus 5.5 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 f8ee3ac, Mimas, Tethys and Phobos move by terms taken from their IAU W in NAIF's pck00011:
Mimas's -44.85 degree S5 term, Tethys's +2.23 on the same angle, and Phobos's tidal quadratic,
which also re-centred its M0 (91.059 to 94.239) and n. Their cards still credited only "JPL SSD
satellite mean elements". They now read, for Mimas, "JPL SSD satellite mean elements, epoch 2000
Jan 1, with the orbital terms of its IAU W (NAIF pck00011), within 7.5 degrees of Horizons from
1950 to 2100"; Tethys and Phobos likewise (0.3 and 1.3). build.ts requires any moon carrying such
terms to name the kernel. Control: the full ETL with the old card fails with "Phobos's orbit
carries terms taken from its IAU W, and its card ... credits only the table."

Tethys's half of the libration had no guard. Without its term it strays 2.09 degrees from Horizons
over 1950-2100 against 0.28 with it, under the general 3-degree ceiling, and its card rewrote
itself as "within 2.1"; no unit test loads bodies.json. Tethys now has its own track ceiling of
0.5 degrees. Control: the full ETL without Tethys's orbitFromW fails with "Tethys's mean elements
put it up to 2.09 degrees from Horizons ... (at most 0.5 expected)".

The lock ceiling's comment said Mimas's 10.15 degrees was "physical libration, which W carries and
Horizons shows as 5 to 9 degrees". Neither holds: Mimas's measured physical libration is 0.84
degrees (Tajeddine et al. 2014), W carries no such term, and Horizons, on the same W against its
integrated orbit, runs from -2.7 to +12.7 degrees over 1950-2100 (the review's scan every 20
days). The drawn face's 2.5 to 10.15 is the IAU's W and JPL's mean longitude disagreeing by about
6.3 degrees, W turning 6.0e-5 degrees a day faster than the row's n (381.9945550 against
381.9944948, 3.3 degrees over the span), and 2e = 2.3 degrees of eccentricity. The ceiling itself
is unchanged.

The baseline ETL from the cache passes and changes bodies.json on those three cards only.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 21:50:21 +02:00
SenrokaiandClaude Opus 5.5 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 395b613 wrote that rule into a comment ("Only an exoplanet has never been imaged") and a test.
The NASA Exoplanet Archive flags 102 planets as detected by imaging (ima_flag), all 102 of them in
exoplanets.json: HR 8799 b to e (Marois et al. 2008), bet Pic b, 51 Eri b, AF Lep b, and bet Pic c
and eps Ind A b, found by radial velocity and imaged since. The review read the sentence on the
live pages of HR 8799 b, 51 Eri b and bet Pic b.

The ETL now asks the archive for those names in a query of its own, cached apart from the main
table so the other 6 252 planets stay on the snapshot they were built from, and carries
`imaged: true` on the matching records. exoplanets.json changes by that field on 102 records and
nothing else (compared record by record). Their cards now end "Not an observation — it has been
imaged only as a point of light beside its star, and no map of it exists."; the rest keep "no
image of this world exists". The provenance comment and the texture catalogue's comment say which
is which.

Checked live on :4301: HR 8799 b and eps Ind A b carry the new sentence, Kepler-22 b the old one.

Tests: body-view-model.spec 'says a directly imaged exoplanet was seen as a point of light, not
that no image of it exists'; the old test is renamed 'says an exoplanet the archive does not flag
as imaged has no image'. Unit controls, each failing that test only (1 failed, 840 passed): the
provenance ignoring the flag; the view model dropping it. build.ts now requires at least 95
imaged planets (measured 102); control, the full ETL with the join made on the host's name
instead of the planet's, fails with "Only 0 exoplanets are marked as imaged".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 21:50:04 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 21:24:23 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 21:18:13 +02:00
SenrokaiandClaude Opus 5.5 85cb66e63c Test that a photograph shows in its own colours once it reaches its body
Since 2d4b8a9 a photographed marker is built in its kind's flat colour (a planet's 0.55, 0.75, 1.0,
a moon's 0.75 grey, a dwarf planet's 0.8, 0.7, 0.55) and only showNextPhotograph sets it back to
white when the map goes on. The material multiplies its map by its colour, so without that one line
every photograph would be tinted: each planet's turned blue, Mars's red cut by 45 per cent. The
review's mutant that drops it passed all 835 tests; the photographs test only looked at the map.

The test now also reads each material's colour: the kind's colour before the image loads, and
0xffffff for both bodies once their maps are on. Controls, each failing that test only (1 failed,
836 passed): showNextPhotograph without the white reset; and the marker built white from the start.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 21:16:04 +02:00
SenrokaiandClaude Opus 5.5 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. 34ae803 said its pieces join within 0.1 s; they join
within 0.26 s, a step in NASA's published Espenak-Meeus coefficients, which the code copies as
they are. The test's bound is tightened from 1 s to 0.3 s to say so. Control: starting the
1600-1700 piece 0.5 s high (a 0.75 s jump, which the 1 s bound let through) fails it only.

Comments in constants.ts: TT - UTC from 1972 is 32.184 s plus TAI - UTC, the 10 s UTC started from
and the 27 leap seconds since (it read "the 10 to 37 of them"). The Moon's error when ΔT was held at
69 s is 0.21 to 0.26 degrees at AD 1000 and 1.44 to 1.79 at AD 1 depending on where it is on its
eccentric orbit, not a single 0.22 and 1.43.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 21:13:09 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 21:09:54 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 19:58:52 +02:00
SenrokaiandClaude Opus 5.5 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 1c86584 and cdf474b said too. Horizons'
Pluto page states none (grep -i obliq finds only the page's IAU76 frame note); the 119.6 degrees is
hand-entered in fetchSolarSystem.ts from the IAU WGCCRE 2015 pole (RA 132.993, Dec -6.163), which
with Pluto's orbit gives 119.609. So for Pluto the check confirms that the kernel's pole and the
sense of W were read as written, which it would still catch misread, but not the pole against a
second source.

The build.ts comment says so, its message names Pluto's reference as its IAU pole, and the spec's
test name and comment no longer attribute Pluto's tilt to Horizons. Labels only; no behaviour
changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 19:58:33 +02:00
SenrokaiandClaude Opus 5.5 98c4eb901f Credit the sources the solar system's orbits now come from, in the README and a search comment
Since 48319c3 and 1d42be2 no position comes from Horizons: the planets move on Standish's mean
elements (Table 2a/2b), the moons on JPL SSD's satellite table, Ceres, Eris, Haumea and Makemake
on the Small-Body Database's osculating elements, and every body turns by the IAU's rotational
elements from NAIF's pck00011. bodies.json's orbitSource values say so (9 Standish, 25 satellite
table, 4 SBDB, none Horizons). Horizons gives sizes, spins and the positions the ETL checks
against. The README still credited "Solar-system ephemerides: NASA/JPL Horizons", said the Sun's
bodies came "from JPL Horizons", listed fetchSolarSystem's source as "JPL Horizons / SSD", and had
Horizons reporting every element against the ecliptic, where the moons' are against a Laplace
plane or their planet's equator. The branch had edited the credits paragraph and left that line.
search-ranking.ts called the solar-system bodies "eighteen famous objects"; there are 38.

Documentation only; no behaviour changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 19:57:50 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 19:57:16 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 19:48:31 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 19:42:23 +02:00
SenrokaiandClaude Opus 5.5 8c4f1c11c9 Build a body still waiting for its surface with a null map, so three stops warning on each one
Since de34fff paints derived surfaces after the system is built, every body without a photograph
was given map: undefined, and three's Material.setValues warns "parameter 'map' has value of
undefined" for each: eleven warnings every time the Sun's system was entered, one per exoplanet in
any other. The marker now starts with map: null, which three takes without a word and which
the deferred paint replaces as before. In the running app, entering the Sun (38 members) now logs
no console message at all.

Control: undefined again fails "builds a body still waiting for its surface without three warning
of an undefined map".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 19:35:27 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 19:33:38 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 19:30:38 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 19:27:06 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 19:24:29 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 19:21:15 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 19:17:06 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 19:14:20 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 19:11:56 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 19:10:35 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 19:07:03 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 19:03:26 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-29 18:45:26 +02:00
SenrokaiandClaude Opus 5.5 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 cdf474b every one is
turned by its IAU W. build.ts now checks what is drawn instead: the east longitude of the planet
on the moon's IAU body-fixed frame, from where the mean elements put it, every 135 days from 1950
to 2100. Measured: at most 6.70 degrees (the Moon's own eccentricity swing), with Mimas 10.15
(its physical libration, which Horizons shows too), Iapetus 18.33 (its row sits 9.4 degrees
behind Horizons) and Proteus 8.18 under their own ceilings. The lock holds only over that span:
Proteus's W turns 6.3e-7 of its rate slower than its orbit, which drifts its face 74 degrees by
AD 3000, and Mimas's W runs 6e-5 degrees a day ahead of the table's mean motion. Without the new
terms the check fails: Phobos's face turned 13.78 degrees from Mars by 2100, and Mimas's 54.5.

The comments that promised the lock "however long the clock runs" now say what the check covers.
The ceilings comment in build.ts gives Hyperion's and Nereid's worst offsets sampled daily (22.2
and 11.2 degrees, where twelve New Year's Days gave 20.2 and 2.6).

Controls: dropping the libration's cosine term, the quadratic, the quadratic's re-centring or
swapping sine and cosine each fails its named test; the ETL without Mimas's term fails at 44.68
degrees and without Phobos's at 13.78.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 18:37:58 +02:00
SenrokaiandClaude Opus 5.5 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 302fa96 because the one cylindrical map found then (USGS
wms_basemaps/Deimos/deimoscyl4.jpg) gave no way to tell which way its longitudes ran. It is now
Stooke's later map of the same set, from the NASA PDS Small Bodies Node
(MULTI-SA-MULTI-6-STOOKEMAPS-V3.0, deimos_cyl_viking_mro.jpg): Viking Orbiter images with MRO
HiRISE detail, 7200x3600 simple cylindrical, "0 longitude at the center".

The guide does not say which way longitude runs, and USGS georeferences its copy of the older
version with 0 at the left edge. Settled on the body: read with 0 in the middle and east to the
right, and drawn as a globe from outside, the hemisphere at 90 E matches unmirrored the sheet
Stooke titles "trailing side" (270 W), and the one at 90 W his "leading side". A synchronous
prograde moon trails at 90 E, so that reading holds; read the USGS way, the sheets would land on
the wrong hemispheres. A 1 km depression sits at Swift's Gazetteer position (12.5 N, 1.8 E).

Processed like the other maps (build_maps.py): no no-data pixels to grey (13 source pixels of 26
million at 0), area-downsampled to 1024x512, JPEG q85, 55 KB. Black pixels (under 8 of 255): 0%.
The map wraps seamlessly (mean 1.8 grey levels across the seam).

Measured in the running app (port 4311) through the IAU rotation and MAP_TO_BODY: Mars stands
over 0.18 W, 0.41 W and 0.20 W of the drawn map (latitudes within 0.04 deg) on 2000-01-01.5,
2025-01-01 and 2050-01-01, and Deimos heads toward 90.08 W, 90.30 W and 90.11 W: the hemisphere
that matched Stooke's leading sheet leads.

texture-catalog.spec.ts now lists Deimos among the mapped bodies. Mutant: the deimos line removed
from BODY_TEXTURE_PATHS; only 'wraps the moons and dwarf planets that have a mission mosaic in it'
failed (1 of 805). The textures README gives the source, credit, licence (the PDS archive states no
use restriction) and the longitude check; the root README counts twenty-eight photographed bodies.
The five Uranian moons stay derived: USGS has only Voyager control networks for them, no mosaic.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 13:29:22 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-25 00:05:50 +02:00
SenrokaiandClaude Opus 5.5 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 (76fb386),
but the system view drew Saturn as a bare sphere, and the page sized the rings
to 1.4-2.6 planet radii regardless of what saturn_ring.png draws where.

The strip runs straight out from its left edge to its right. Read off its
alpha, the C ring's inner edge (74 490 km) is at px 91 of 1 280, the B ring's
inner and outer edges (92 000 and 117 580 km) at 404.5 and 860, the A ring's
outer edge (136 775 km) at 1 204 and the F ring (140 180 km) at 1 267.5: one
scale of 55.9 km a pixel fits all five within 1.8 px, so the strip spans
69 400 to 141 000 km. Only the Cassini Division's outer edge misses, drawn
30 px (1 700 km) too far in. Sized to the brief's 74 500 and 140 220 km (the C
ring's inner edge and the F ring) instead, the B ring's inner edge would sit
3 300 km out.

saturnRing (texture-catalog.ts) now builds the rings for both views: flat in
the XZ plane of a sphere built round +Y, sized against the planet as drawn,
MeshStandardMaterial lit from both faces, the strip's own alpha as opacity
(the page used the texture as its own alphaMap too, multiplying its alpha by
its green channel, at 0.85 opacity). In the system view the ring is a child of
Saturn's marker, so the IAU pole turns it into the equator and the pixel floor
scales it with the planet; a ray through it picks Saturn (memberForObject
accepts a marker's child). On the page it now reaches 2.42 radii, not 2.6.
Jupiter's, Uranus's and Neptune's rings are left out: dark, narrow or dusty,
too faint to see at any scale drawn here.

Measured in the running app, the ring's opening to Earth against Horizons'
sub-Earth latitude on Saturn (planetodetic, taken back to planetocentric with
f = 0.09796): 26.963 against 26.966 degrees on 2017-10-16, 0.075 against
0.042 on 2025-03-23, the plane crossing, and -7.764 against -7.813 on
2026-09-24. The Sun stood 26.64, 0.70 and -7.50 degrees above the ring plane
on those dates. At the closest the system view allows, 0.05 AU, Saturn is
about 5 px in radius and its rings reach about 12 px; on the plane-crossing
date they vanish edge-on.

Tests: the three openings against frozen Horizons values and a pick through
the B ring (system-orbits-renderer.spec.ts); the rings' extent, their lying
in the sphere's equator, and the strip sampled outwards so its B ring starts
at 92 000 km and its A ring ends at 136 775 (texture-catalog.spec.ts). Unit
suite 792 -> 799 tests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 23:42:48 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-24 23:41:28 +02:00
SenrokaiandClaude Opus 5.5 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>
2026-09-24 22:50:26 +02:00