ADR-028 — Outer planets + dwarf planets in /plan: scope and scaling
Status · Accepted Date · 2026-05-02 Closes · RFC-008 (Outer planets + dwarf planets) TA anchor · §components/lambert-worker · §components/plan-screen Related ADRs · ADR-008 (Lambert worker), ADR-022 (Lambert protocol), ADR-023 (mobile magnifier), ADR-026 (multi-destination v0.1.6 / 5 destinations)
Context
ADR-026 shipped 5 destinations in v0.1.6: Mercury, Venus, Mars, Jupiter, Saturn. Three outer planets (Uranus, Neptune) and Pluto plus all dwarf planets were explicitly deferred to v0.3.0. RFC-008 raised three open questions: Y-axis scaling for multi-decade transfers, gravity-assist modelling, and dwarf-planet inclusion. This ADR locks every variable so the v0.3.0 implementation has a single contract.
Decision
Destination scope (RFC §dwarf-planets — Option DW-B refined)
Four new destinations ship in v0.3.0: Uranus, Neptune, Pluto, Ceres. Total destination count rises from 5 → 9.
| id | Hohmann transit | tof_range_days | mission_types | Notes |
|---|---|---|---|---|
| uranus | ~16.0 yr | [3000, 6500] (8.2–17.8 yr) | FLYBY | Voyager 2 only flown |
| neptune | ~30.6 yr | [10000, 20000] (27–55 yr) | FLYBY | Voyager 2 only flown |
| pluto | ~45.5 yr | [12000, 22000] (33–60 yr) | FLYBY | New Horizons only flown |
| ceres | ~1.3 yr (Hohmann-class) | [120, 480] d (short-way Lambert window) | LANDING, FLYBY | Dawn (orbit insertion); Y-axis days (tof_range_days[1] ≤ 730). Draft [800, 1800] d produced zero converged cells with the v0.1.6 short-way solver — see scripts/precompute-porkchops.ts. |
LANDING availability for Ceres mirrors Mars/Venus/Mercury (per ADR-026's possibility-driven rule). All other v0.3.0 entries are FLYBY-only — surface landings on gas giants / ice giants / Pluto are not defined manoeuvres at this fidelity level.
Eris, Makemake, Haumea explicitly deferred. Their multi-century orbits don't read on the 112×100 grid and they have no flown missions — adding them would be educational noise.
Y-axis scaling (RFC §y-axis — Option YA-A)
Per-destination tof range expressed in years on the axis label when tof_range_days[1] > 730 (the v0.1.6 threshold). Year ticks are integer-valued (5y, 10y, 15y ...). Inner planets, Mars, and Ceres stay in days; gas giants, Uranus, Neptune, and Pluto flip to years.
The educational moment the v0.1.6 work landed for Saturn ("12-year cycle") extends naturally — Neptune now visibly takes "a generation" while a Mars launch window arrives every 26 months.
Logarithmic axis (Option YA-B) rejected — the porkchop's "cheapest cell" insight depends on linear time; log scaling would compress the Hohmann minimum into illegibility.
Gravity-assist scope (RFC §gravity-assist — Option GA-A)
Direct-Hohmann trajectories only. The pre-computed grids show what a direct launch costs; real outer-planet missions use planetary gravity assists which are out of scope.
Each outer-planet panel surfaces a one-line caveat banner (mirroring the v0.1.7 sparse-data caveat pattern):
Direct trajectory shown. Real outer-planet missions use planetary gravity assists — see Voyager 2, Galileo, Cassini, New Horizons in /missions for actual flight paths.
Option GA-C (Voyager/Galileo reference overlays) deferred to a possible v0.4. Its educational power is real but engineering scope (multi-leg Lambert with patched-conic approximations) belongs in its own RFC.
Schema additions
DestinationConstants in src/lib/lambert-grid.constants.ts gains an optional e field (eccentricity). Pluto sets e from small-bodies.json so arrival radius varies with true anomaly. Ceres omits e (circular at a) so the short-way Lambert porkchop grid converges; mission-arc destinationPos matches. All planets use circular a in this map (consistent with marsPos() and historical goldens).
export interface DestinationConstants {
id: DestinationId;
a: number; // semi-major axis, AU
a0: number; // mean longitude at epoch, rad
meanMotionRadPerDay: number;
e?: number; // eccentricity (default 0 — circular approx)
}DestinationId enum extends to include the 4 new ids.
Lambert convergence policy (RFC OQ #2)
For multi-decade transfers, Lambert's iterative solution can fail to converge or produce negative ∆v values. The solver already returns DV_FAILED for such cells; outer-planet grids may have higher failure rates than inner-planet grids.
scripts/precompute-porkchops.ts records the per-destination convergence statistic in the JSON's credit field:
Computed at build time via Lambert solver. Uranus: 84% convergence. Failed cells render as DV_FAILED.
UI behaviour: failed cells render as the existing dark-grey background; magnifier shows "—" for non-convergent cells.
/fly arc geometry for multi-year trajectories
The v0.1.6-introduced single-ellipse outboundArc() already handles outer destinations (positive eccentricity for outer, negative for inner). For Pluto (e=0.249), pass the eccentricity through destinationPos / porkchop arrival so the visible arc reads as the truly-elongated trajectory rather than a misleading circular curve. Ceres uses circular geometry in these paths (see §Schema additions).
Camera framing per cameraDistanceFor() from v0.1.10: Neptune at ~30 AU = ~2400 scene units; Pluto at ~40 AU = ~3200 units. Adjust the camR clamp upper bound from 900 → 4000.
Mission catalogue additions (in scope)
v0.3.0 also adds the 4 missions that justify these destinations:
- Voyager 2 (1977) — Jupiter / Saturn / Uranus / Neptune flybys. Implemented:
dest: NEPTUNE(final giant-planet encounter); overlay copy explains the full tour. Gravity assists are not modelled in porkchops or/flyheliocentric arcs. - Galileo (1989) — Jupiter orbiter.
dest: JUPITER. - New Horizons (2006) — Pluto flyby (+ later Kuiper belt targets).
dest: PLUTO;flight_data_qualitymay staysparseuntil a dedicated ADR-027 population pass. - Dawn (2007) — Vesta + Ceres orbiter.
dest: CERES(final orbital target); overlay mentions Vesta. A future optionalitinerary[](ADR-020 amendment) may split multi-body missions without changing v1destsemantics.
Total mission catalogue: 32 → 36 (16 Mars + 16 Moon + 4 outer-system entries).
Future expansion: multi-leg missions may add itinerary[] or secondary_dest in a follow-on ADR — not required for the four shipped records.
URL contract (extends ADR-026)
/plan?dest={mercury|venus|mars|jupiter|saturn|uranus|neptune|pluto|ceres}&type={LANDING|FLYBY}- Default still
dest=mars(no regression). - Invalid
?dest=eris(or any deferred dwarf) coerces tomarswith a one-time console warning, not a UI error.
Rationale
The decision cluster around three principles already locked elsewhere:
- PRD-002: "intuitive before explanation" — drives the linear (not log) Y-axis and the direct-trajectory-only choice. A user reading the Neptune porkchop should think "this takes a generation," not "I need to learn what a log axis means."
- PA §promises — fail honestly — drives the gravity-assist caveat banner. We don't pretend the porkchop shows what real missions do; we tell the user.
- ADR-016 (build-time assets) — drives the pre-computed grid pattern; same as v0.1.6.
The "drop dwarfs" rule (DW-B refined) is selective: Pluto + Ceres earn inclusion through New Horizons + Dawn. The remaining dwarfs (Eris/Makemake/Haumea) lack flown missions and have orbits that don't read at our format.
Alternatives considered
- Option DW-C — all four dwarfs rejected because Eris (a=67 AU, e=0.43) doesn't compute a sensible Hohmann porkchop — its perihelion is at 38 AU and aphelion at 97 AU, so transfer parameters depend heavily on which orbital phase you target. Adding it would be visual noise.
- Option YA-B — log-scale Y-axis rejected because porkchops teach "this is the cheapest date," and log-scale obscures the date axis for the user.
- Option GA-B — two grids per outer destination (direct + Jupiter-assisted) rejected as scope-doubling for marginal teaching gain. The caveat banner + the v0.4 reference-trajectory option captures the teaching moment cleanly.
- Skip Pluto entirely rejected — New Horizons is the most-Googled outer-system mission of the last decade; its absence would be glaring.
Consequences
Positive:
/plancovers 9 of the major solar-system destinations (8 planets + Ceres). Pluto's inclusion as a dwarf opens the door for an educational thread on the IAU 2006 redefinition.- Lambert worker's parameterisation extends naturally — same per-destination JSON contract as v0.1.6.
- Y-axis scaling continues the pattern from v0.1.6 (Saturn already in years).
- 4 new missions deepen the catalogue: Voyager 2, Galileo, New Horizons, Dawn — landmark missions that v0.1.x oddly omitted.
Negative:
- ~880 KB of new pre-computed grid data (4 × ~220 KB; outer-planet grids are slightly larger because Lambert convergence stats compress less). Total now ~1.4 MB for the 9-destination grid set. Within budget but worth noting.
- Multi-year transfer windows + Lambert convergence at extreme distances need careful testing. The
DV_FAILEDrate may be 15–20% for Neptune/Pluto vs <5% for inner planets. Document this honestly. /flycamera-distance upper bound lifts from 900 → 4000. Some legacy mission entries may need camR re-tuning.- The gravity-assist caveat banner may frustrate STEM students looking for "the real trajectory." The follow-up RFC for reference overlays (Option GA-C) is already framed.
Implementation notes
Implementation lives in v0.3.0 milestone (issue #27 tracker). Slice plan:
- 3.0a-1 — Schema + types. Extend
DestinationConstantswith optionale. NewDestinationIdenum entries. Updatelambert-grid.constants.tsto include all 9 destinations. - 3.0a-2 — Pre-computed grids.
scripts/precompute-porkchops.tsruns over the 4 new destinations. Verify Lambert convergence stats; record per-destination in JSONcreditfield. - 3.0a-3 —
/planUI. Destination dropdown extends to 9. LANDING/FLYBY toggle correctly disabled for outer planets + Pluto. Y-axis year ticks at 5y intervals. - 3.0a-4 —
/flyarc geometry. Pass eccentricity throughoutboundArc()for Pluto. AdjustcameraDistanceFor()upper bound. Caveat banner under the FLY button. - 3.0a-5 — Mission catalogue. Add Voyager 2, Galileo, New Horizons, Dawn per ADR-020 + ADR-027 shapes; extended
destenum; 36 missions shipped. Full measuredflightblocks may follow in a data-only pass. - 3.0a-6 — Tests + docs roll-up. Extend e2e to cover Neptune porkchop convergence, Pluto camera framing, Voyager 2 mission load. Tag v0.3.0.
The v0.4 deferred work (gravity-assist reference overlays) is tracked separately; this ADR doesn't open that thread.