Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions data/raw/horizons/planets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# JPL Horizons planet vectors

Input to `npm run build-planet-ephemeris`, which fits each planet's `Horizons − Kepler`
residual into `src/data/bodies/planetEphemerisCorrections.generated.ts`. The CSVs are
gitignored; regenerate with `npm run fetch-horizons-planets` (8 files, ~3 MB each).

- **Upstream:** JPL Horizons API, `https://ssd.jpl.nasa.gov/api/horizons.api` (JPL DE ephemeris; public domain, NASA/JPL).
- **Files:** `<naif>.csv`, columns `jd,x_km,y_km,z_km`, one row per day from JD 2415020.5 (1900-01-01) to 2488069.5 (2100-01-01).
- **Targets:** Mercury `199`, Venus `299`, Earth–Moon barycentre `3`, then the system barycentres `4`–`8` (Mars through Neptune).

## Query

Every target is fetched in four 50-year chunks (1900–1950, 1950–2000, 2000–2050, 2050–2100),
each retried up to five times. Each chunk includes both endpoints, so the shared boundary row is
dropped once when the chunks are merged.

```
format=json
COMMAND='<naif>'
OBJ_DATA='NO'
MAKE_EPHEM='YES'
EPHEM_TYPE='VECTORS'
CENTER='500@10' # Sun body centre
REF_PLANE='FRAME' # equatorial ICRF, the scene's world frame
TIME_TYPE='UT' # UT1 before 1962, UTC after: the app's simDays is UTC
OUT_UNITS='KM-S'
CSV_FORMAT='YES'
VEC_TABLE='1' # position only
START_TIME='<chunk start>'
STOP_TIME='<chunk stop>'
STEP_SIZE='1 d'
```

Fetched 2026-10-04.
2 changes: 1 addition & 1 deletion docs/BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ Items with a **→ details** link have a full write-up in [`backlog/`](backlog/)
- [ ] **Filaments + flow field lack scale fade bands** `ready` — both layers gate on user intent alone, with no zoom-based fade like the survey point clouds. → [details](backlog/2026-07-24-filaments-flow-scale-bands.md)
- [ ] **Flow field does not reseed on a particle-count change** `needs-verification` — confirmed on the deployed build; the latch arms and the seed pass encodes, so the suspect is a stale trail ring or index-hashed seed positions. → [details](backlog/2026-09-15-flow-field-no-reseed-on-count-change.md)
- [ ] **Moon orbit rate double-counts apsidal precession** `needs-design` — some satellite `P` columns are longitude periods but read as anomalistic, so Europa/Enceladus sit ~120° off orbital phase per decade. → [details](backlog/2026-10-03-moon-orbit-rate-double-counts-apsidal-precession.md)
- [ ] **Barycentric orbit pairs** `needs-design` — one-hop `focusId` pins Pluto (and Earth) instead of wobbling around the pair barycentre; needs an invisible focus-graph node. → [details](backlog/2026-08-16-barycentric-orbit-pairs.md)
- [ ] **Barycentric orbit pairs** `needs-design` — Pluto's wobble is one `BARYCENTRIC_PAIRS` row (Earth–Moon already uses it); the minor moons still need an invisible focus-graph node. → [details](backlog/2026-08-16-barycentric-orbit-pairs.md)
- [ ] **Pluto's minor moons (Styx/Nix/Kerberos/Hydra)** `blocked` — they orbit the barycentre, so honest placement waits on barycentric pairs. → [details](backlog/2026-08-16-barycentric-orbit-pairs.md)
- [ ] **ZoA label em-height not radius-invariant** `needs-design` — `LABEL_EM_MPC` is baked at construction while `LABEL_RADIUS_MPC` is per-frame; a tunable label radius would change apparent glyph size with nothing re-deriving to match. → [details](backlog/2026-08-17-zone-of-avoidance-shape-constants.md)
- [ ] **Consolidate the renderer hygiene-basket duplications** `ready` — grow-on-demand instance buffer (×7+), 16-byte fade-scratch (×4), fullscreen-triangle (×5 + a second `.wesl` copy), sub-pixel cull (×3). → [details](backlog/2026-08-20-renderer-hygiene-basket.md)
Expand Down
5 changes: 5 additions & 0 deletions docs/DATA.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ Planet/moon/ring textures and Earth's imagery are gitignored raw pulls with comm

Earth's whole-globe base texture and its surface tile pyramid are two publications of one Blue Marble month (a 21600×10800 equirect and eight 21600×21600 quadrants, ~421 MB). The month is chosen once in [`bmngVintage.ts`](../tools/utils/io/bmngVintage.ts) and every registry path, upstream URL, and attribution string reads it from there. The tile layer falls back to the base outside its baked window, so a vintage split between the two would draw a visible seasonal seam along the tile frontier.

### Planet positions (JPL Horizons)

Each planet's position is its Keplerian row plus a fitted correction series that matches JPL DE to ≤ 1,000 km over 1900–2100. `npm run fetch-horizons-planets` pulls 1-day heliocentric ICRF vectors (8 × ~3 MB, gitignored) into `data/raw/horizons/planets/`, and `npm run build-planet-ephemeris` fits them into the committed `src/data/bodies/planetEphemerisCorrections.generated.ts`. Regenerate after any planet element-row or frame change. The exact query is in [`data/raw/horizons/planets/README.md`](../data/raw/horizons/planets/README.md).

### Earth surface tile pyramid

The bake emits `public/data/images/earth-tiles/v10/albedo/<z>/<x>/<y>.webp`: 512 px lossy WebP tiles whose alpha channel doubles as the land mask, plus two sidecars, `index.txt` (one path per line) and `manifest.json` (the flat band list every product's planner filters by `builtFrom`). `npm run build-surface-tiles` is idempotent per tile (a tile whose output already exists is skipped, byte-identical, on a re-run) and refuses to write `index.txt`/`manifest.json` at all if any tile the prior index promised is missing on disk, leaving both untouched — an interrupted or partially-deleted bake never advances the pointer to half-baked data ([`buildSurfaceTiles.ts`](../tools/textures/buildSurfaceTiles.ts)).
Expand All @@ -216,6 +220,7 @@ Every refresh shares one shape: fetch, build, then `npm run sync-r2-secure` from
| Clusters/superclusters | `fetch-structures` | `build-structures` (after `build-tiers`) |
| DESI | `fetch-desi` | `build-tiers` (`desi-{deep,wedge,sgw}.bin`) |
| Planet textures | `fetch-textures` (`--dev` for a subset) | `build-textures` |
| Planet positions | `fetch-horizons-planets` | `build-planet-ephemeris` (commits a generated `.ts`, no R2 sync) |
| Earth surface tiles | `fetch-textures` + `fetch-eox` + `fetch-height` | `build-surface-tiles` (`--dev` for a quick z5 pass, albedo only) |

Raw files and built artefacts are gitignored; only provenance READMEs and `.sha256` sidecars are committed. Two small deterministic bakes are the exceptions and live in git under `public/`: the MSDF font atlases in `public/fonts/` (`npm run build-fonts`) and the split-sum environment-BRDF LUT in `public/lut/` (`npm run build-env-brdf-lut`). Full-resolution texture and tile builds run post-merge from the main worktree.
Expand Down
10 changes: 7 additions & 3 deletions docs/backlog/2026-08-16-barycentric-orbit-pairs.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ From the [add-Pluto/Charon grill session](../grill-sessions/add-pluto-charon-202

The focus graph is strictly one-hop: `OrbitalElements.focusId` makes a body orbit its parent's *center* (`src/services/engine/frame/deriveBodyStates.ts` resolves positions along `focusResolveOrder`). Real binary-ish systems orbit a shared barycentre:

- **Pluto–Charon**: Charon is ~12% of Pluto's mass; the barycentre sits ~2,130 km from Pluto's centre — 1.8 Pluto radii, i.e. ~940 km *above* the surface. The shipped model (Charon as a Moon-style satellite of Pluto) pins Pluto at its heliocentric position — an error of ~10% of the pair separation, visible only when parked near the barycentre with time running. The same approximation already applies to Earth–Moon (Earth doesn't wobble around the EMB), just less visibly.
- **Pluto–Charon**: Charon is ~12% of Pluto's mass; the barycentre sits ~2,130 km from Pluto's centre — 1.8 Pluto radii, i.e. ~940 km *above* the surface. The shipped model (Charon as a Moon-style satellite of Pluto) pins Pluto at its heliocentric position — an error of ~10% of the pair separation, visible only when parked near the barycentre with time running. Earth–Moon no longer has this error: see the pair table below.
- **Pluto's minor moons** — Styx, Nix, Kerberos, Hydra — orbit the *barycentre*, not Pluto, at wider radii where the approximation grows proportionally worse. They are deliberately absent until this lands (grill Q5): four sub-pixel irregular rocks drawn dishonestly around Pluto's center add clutter without honesty.

## Shape of a fix
## What exists now

`BARYCENTRIC_PAIRS` (`src/data/bodies/barycentricPairs.ts`) treats a primary's element row as the pair barycentre, and `deriveBodyStates` subtracts the secondary's mass-weighted reflex from the primary. Earth–Moon is its one row. Pluto's wobble is therefore one more row (`pluto`/`charon`, Charon's mass fraction ≈ 0.1086), once Pluto's row is re-fitted to the Pluto-system barycentre. The minor moons still need the invisible node below, because they orbit the barycentre rather than either body.

## Shape of a fix (minor moons)

An invisible barycentre node in the focus graph that both partners orbit. Touchpoints that currently assume focus-graph nodes are visible bodies: pick tables (`resolvePickTable.ts` maps source → seed table by index), region extents (`bodyRegions.ts` maxes over members), labels/captions (every `SCENE_PLANETS` row gets one), glint partition. Either a `visible: false` body kind or a separate node table that only `deriveBodyStates` sees.

## Follow-on once landed

- Let Pluto (and optionally Earth) wobble: re-parent Pluto → `pluto-barycentre` with a ~2,130 km semi-major element row; delete the "looks wrong, don't fix" comment on Charon's row.
- Let Pluto wobble: add the `pluto`/`charon` row to `BARYCENTRIC_PAIRS`; delete the "looks wrong, don't fix" comment on Charon's row.
- Add Styx/Nix/Kerberos/Hydra as ordinary satellite rows parented on the barycentre node (no textures — no usable maps exist; flat albedo + glint only).
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# SDD ledger — plan: docs/superpowers/plans/2026-10-04-accurate-planet-positions.md

Asks (2026-10-04): parallelism = stack (feature starts right away on `accurate-planets` atop `accurate-planets-prep`); perf gate = SKIP. Lean protocol: one final review per PR; T5/T6 tagged review: yes.
Base: prep branch off origin/main d435719c0 (main moved from a600d661f; #827 touches no planned file).

## Pre-flight scan
| Rows | Produces → consumes | Finding |
|---|---|---|
| T1 ↔ T6 | T1 test reads eye-relative km basis (floats 34..45); T6 on-trail test reuses it | consistent |
| T2 ↔ T6 | both regenerate bodyStatesJ2000 + golden traces + EARTH_TARGET | T6 re-does after T2; consistent (sequential) |
| T2 ↔ T5 | T5 fit must run AFTER T2's obliquity (residual in app frame) | stacked branch guarantees order |
| T3 ↔ T5 | `EphemerisCorrection`, `ephemerisCorrectionMpc` → T5 verifies with it | consistent |
| T4 ↔ T5 | `data/raw/horizons/planets/<naif>.csv` via rawDataPath('horizons.planets') | consistent |
| T5 ↔ T6 | `PLANET_EPHEMERIS_CORRECTIONS` keyed by planet id | consistent |
| T1 self | test fails today, passes after | ok |
| T2 self | no new test (constant) | ok per testing.md |
| T3–T6 self | tests match code | ok |
Dispatch A (Tasks 1–2, Sonnet) launched on accurate-planets-prep; draft PR #834; base c798ac56a
Task 1: complete (commits c798ac56a..2183ed6b7, final review pending)
Task 2: complete (commits 2183ed6b7..7edf35cb5, final review pending)
Ruling: Task 1 on-trail tolerance 0.1 km, not 1 m — the eye-relative basis is packed f32 (~30 m step at the Moon's 4e5 km); injected offset 1e6 km is 4 orders above — costs: a sub-100 m anchoring regression would pass. Same tolerance for Task 6's on-trail test.
Ruling: 18–20-digit keplerianEllipse comparisons left unloosened — they still pass, loosening would be churn — costs nothing.
Golden traces checked: same structure (26/26, 47 entries), only J2000 Earth coordinates rotated by the obliquity fix.
Prep pushed 7edf35cb5; CI watch on #834; final review (Opus) dispatched. Feature branch accurate-planets created at 7edf35cb5; Dispatch B (Tasks 3–5, Opus) launched.
Prep final review: 3 Minor (propagateElements.test.ts:93-94 garbled comment; orbitalElements.ts:4 header not re-wrapped; settleGoldenTrace.json minified by re-record). No Critical/Important.
Ruling: controller fixes the 3 Minors inline on accurate-planets-prep (<50 lines, lean protocol) AFTER Dispatch B finishes (worktree is on accurate-planets), then merges prep into accurate-planets — avoids switching branches under a live agent — costs: prep PR waits ~1 dispatch.
Prep CI RED (1/1426): cameraFraming.test.ts:76 GALACTIC_DISC_FORWARD decodes to yaw −1.4208018 vs −1.4208 @1e-6 — the exact-obliquity ecliptic frame shifts the decode ~2e-6 rad.
Ruling: relax yaw/pitch to toBeCloseTo(…, 4) with a one-line why (still catches a mistyped 4th decimal, 1e-4 > 5e-5); do NOT re-derive GALACTIC_DISC_FORWARD (a 1e-5 rad view change nobody sees) — costs: the pin is 50× looser. Folded into the controller's prep fix commit after Dispatch B.
Prep fix commit 309d53be8 (3 review Minors + cameraFraming 4-place ruling; settle recorder now writes 2-space JSON and trace re-recorded) pushed; CI re-watch running.
Task 3: complete (commits 7edf35cb5..428f92fdc, final review pending)
Task 4: complete (commits 428f92fdc..e79ff43f2, final review pending)
Task 5: complete (commits e79ff43f2..2f0c8f0d3, final review pending) — 525 terms, max verified 900 km (Earth), 36.2 kB text
Ruling (Dispatch B deviations accepted): golden-section frequency refine instead of parabolic (2 vs 21 terms on two-tone test); fit grid always includes the final row (Uranus failed 1,012 km at 2100 otherwise); generated file 36 kB text not ~17 kB (plan assumed f32 binary; text with 12-digit ω is the honest size) — costs: bundle +36 kB raw (~less gzipped).
Prep merged into accurate-planets (948758534); feature draft PR #835 (base accurate-planets-prep). Dispatch C (Task 6, Opus) launched.
Prep PR #834 CI GREEN at 309d53be8; review clean after fixes → ready for user smoke/merge ruling.
Task 6: complete (commits 948758534..b707c020b, final review pending) — vs Horizons fixture: Me 593, V 616, E 621 (vs 399), Ma 868, J 307, S 333, U 279, N 300 km; k=0 mutation → Earth 4,830 km, reflex test fails.
Ruling (accepted): BARYCENTRIC_REFLEX_BY_PRIMARY as 2nd export of data/bodies/barycentricPairs.ts (frameFilePurity bans the module-level map in deriveBodyStates) — costs: data file with two exports. Saturn on-trail check at 1 m on the f64 conic (packed f32 step 128 km at Saturn); Moon/Hubble 0.1 km on packed basis. HorizonsSeries type moved to tools/bodies/@types (noInlineTypes).
Feature final review: clean of Critical/Important; 6 Minors (unused maxErrKm; BARYCENTRIC_PAIRS export unused + test restates k; inline dot helper; golden-section double eval; lo=0 NaN probe; parser throw-case test). Ruling: fix all 6 in ONE fix dispatch after local CI finishes (stacked PR gets no GitHub CI — base ≠ main; local typecheck+lint+test run as the gate).
Local CI on b707c020b: typecheck+lint+test GREEN (1432 files / 14950 tests). Fix dispatch (6 Minors, Sonnet) launched.
Fix dispatch 8eea5fe69: 6 Minors fixed; fixer reverted the regenerated file (1e-10 rounding noise broke exact fixtures).
Ruling: controller regenerated corrections from the final tool + re-blessed bodyStatesJ2000 and both golden traces (60a10eec9) so the committed generated file equals tool output — costs: fixture churn of rounding noise only (same term counts, same max errors).
Local CI on 60a10eec9: 1429/1429 files, 14943 tests pass; 3 worker-start timeouts (load) re-run green (3 files, 7 tests). Both PRs review-clean. NEXT: user smoke (plan DoD) → /feature-done → merge on user's word (prep #834 first, then retarget #835 to main).

## /feature-done IN PROGRESS (2026-10-04) — user smoke PASSED ("looks good")
DoD findings so far: tests+typecheck GREEN (local full run on 60a10eec9, no change since); checkboxes 0/31 ticked (tick all on READY); DoD section PRESENT; spec Ground preparation PRESENT; unexpected files (all explained): earthUniverseLoop.test.ts, settleGoldenTrace.test.ts (recorder), mergeHorizonsChunks(.test).ts, tools/@types/math/FitResult.d.ts, tools/bodies/@types/HorizonsSeries.d.ts, tools/parsers/@types/HorizonsVectorRow.d.ts, tools/utils/math/dot.ts; untouched planned: none; new TODOs: 0; comment density OK; test parity grew; smoke FOUND; deferred = plan "Out of scope" list (moons, Pluto pair row → backlog detail updated, Voyager → mission-trails spec next, runtime TDB, orbitReachByRegion margin).
IN-FLIGHT: deletion-audit agent (opus, GREENFIELD/--justify, read-only) → report at scratchpad `deletion-audit-accurate-planets.md`. ON RESULT: apply SAFE-NOW bin as its own deletion commit on `accurate-planets` (dispatch Sonnet if >2 files), run targeted tests, push; NEEDS-RULING items → ask user. Then READY → tick plan checkboxes; git mv plan → plans/completed/, spec → specs/completed/; copy this ledger → plans/completed/2026-10-04-accurate-planet-positions.ledger.md; backlog sweep (barycentric-pairs item STAYS, only its clause was updated); commit "docs(plan): mark accurate-planet-positions complete" (NO Co-Authored-By per user memory); push accurate-planets.
THEN: merge ONLY on user's "merge this PR": #834 first (squash), then retarget #835 base → main, rebase/merge main, GitHub CI, merge. After both land: Voyager mission-trails spec (memory project_mission_trails.md).
Dev server for this wt: localhost:5176 (bg, started this session) — leave running.
Deletion audit done (report: scratchpad deletion-audit-accurate-planets.md): safe-now ~27 LOC (FitResult.d.ts, import guard, keplerKm reuse, edge-continuity test, HorizonsSeries.d.ts) → Sonnet dispatch applying. NEEDS-RULING asked as dash ask 4UUQ (site test keep(rec) · real-data on-trail test delete(rec) · pair table keep(rec) · mergeHorizonsChunks keep(rec)).
Safe-now deletion commit 788c5f627 pushed (typecheck both tsconfigs + tests/tools tests/utils/orbit green). Waiting on ask 4UUQ.
DoD audit READY 2026-10-05: deletion audit ~27 LOC safe-now (788c5f627) + ruled on-trail test drop (3ff4005a5); 4UUQ: keep site test, keep pair table, keep mergeHorizonsChunks. Plan/spec moved to completed/.
Loading
Loading