📋 perihelion: wave-2 brief — four parallel lanes (NEARGOD·SELENE·SYZYGY·VESSEL) (fable)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6391273ac5
commit
e7bcf424d7
257
PERIHELION_BRIEF.md
Normal file
257
PERIHELION_BRIEF.md
Normal file
@ -0,0 +1,257 @@
|
||||
# PERIHELION — SOLARGOD wave 2 · NEARGOD · SELENE · SYZYGY · VESSEL
|
||||
|
||||
> **You are an execution agent (Opus).** You have been assigned ONE lane. Read
|
||||
> Part 0 fully, execute only your lane, end with the §0.6 report. The planning
|
||||
> model (Fable) reviews every lane before merge. Wave 1 (the full build) shipped
|
||||
> at `main@6391273` — 13/13 verify gates; you are polishing a working instrument,
|
||||
> not rescuing a broken one. **Do not break a passing gate.**
|
||||
|
||||
---
|
||||
|
||||
## Part 0 — shared context (all lanes read this)
|
||||
|
||||
### 0.1 The four lanes
|
||||
|
||||
| lane | name | what | why |
|
||||
|---|---|---|---|
|
||||
| **N** | 🌑 NEARGOD | real orbits for the NEO close-approach layer | the one layer whose *positions* are schematic — make them true |
|
||||
| **M** | 🌖 SELENE | real textures + tidal lock for the major moons | worlds become places, twice over |
|
||||
| **E** | 🌓 SYZYGY | oppositions & conjunctions — computed sky events on the timeline | the almanac learns to predict |
|
||||
| **V** | 🛳 VESSEL | vendor Three.js (offline) + deep-time mode (3000 BC–3000 AD) | no CDN dependency; the full Table-2a range the engine already ships |
|
||||
|
||||
### 0.2 Ground truth & hard gate
|
||||
|
||||
- **GATE:** branch from `main` ≥ `6391273`. Confirm: `git log --oneline -3`
|
||||
includes the 📡 NEO-status commit, and `verify.html` passes 13/13 in a browser
|
||||
**before you write a line** (`python3 serve.py <your port>` → `/verify.html`).
|
||||
If either fails, STOP and report.
|
||||
- Read `SOLARGOD_BRIEF.md` (wave 1) for architecture: truth→view in `scale.js`
|
||||
only; floating origin; layer factory contract (`js/main.js` header comment);
|
||||
fail-soft network layers; §12 traps (all still apply, especially: don't hammer
|
||||
JPL — the serve.py disk cache is the rate limiter; Horizons/SBDB burst-throttle).
|
||||
- Multi-file repo (unlike godstrument's monolith) — territory is per-FILE here.
|
||||
Match house style: 2-space indent, `const`, terse precise comments, no deps.
|
||||
|
||||
### 0.3 Territory & merge order — what makes parallel work safe
|
||||
|
||||
| lane | OWNS (may edit) | MUST NOT touch |
|
||||
|---|---|---|
|
||||
| N | `js/layers/neos.js` · `serve.py` (ONE `UPSTREAMS` line) · append-only `verify.html` gate | everything else |
|
||||
| M | `js/bodies.js` (texture/rotation fields only) · `js/layers/moons.js` · `assets/textures/` · `assets/textures/CREDITS.md` | ephem/scale/main; planet entries in bodies.js |
|
||||
| E | `js/layers/events.js` (new) · `js/layers/almanac.js` · append-only `css/style.css` block · append-only `verify.html` gate · ONE line in `js/main.js` `LAYER_MODULES` | everything else in main.js |
|
||||
| V | `index.html` · `verify.html` (importmap lines) · `vendor/` (new) · `js/config.js` (time block) · `js/main.js` | layers/, ephem.js math, scale.js |
|
||||
|
||||
- **Merge order: N → M → E → V.** E and V both touch `main.js`; V merges last and
|
||||
rebases first (`git rebase main`) — V's owner expects this. `verify.html` and
|
||||
`style.css` additions are **append-only** (new rows/blocks at the end, marked
|
||||
`/* PERIHELION-<lane> */`) so they merge clean.
|
||||
- If you believe you must cross your boundary: STOP, log it as an open question
|
||||
in your report, work around it.
|
||||
|
||||
### 0.4 Git & run
|
||||
|
||||
```sh
|
||||
cd /Users/m3ultra/Documents/SOLARGOD
|
||||
git worktree add ../SOLARGOD-N -b perihelion/n-neargod # lane N
|
||||
git worktree add ../SOLARGOD-M -b perihelion/m-selene # lane M
|
||||
git worktree add ../SOLARGOD-E -b perihelion/e-syzygy # lane E
|
||||
git worktree add ../SOLARGOD-V -b perihelion/v-vessel # lane V
|
||||
```
|
||||
|
||||
Work only in your worktree. **Dev ports** (serve.py takes the port as argv):
|
||||
N=8148 · M=8149 · E=8150 · V=8151. Port 8147 is the user's. Commit style:
|
||||
emoji + lowercase + `(opus)`, e.g. `🌑 neargod: the ring becomes real (opus)`.
|
||||
Never push to `main` — push your branch; Fable merges after review.
|
||||
|
||||
### 0.5 Hard rules (inherited from wave 1)
|
||||
|
||||
- No npm, no build step, no frameworks. Stdlib-only serve.py.
|
||||
- Never commit `cache/` or `assets/art/candidates/`.
|
||||
- Verification gates are mandatory; measurement beats reasoning. Every lane adds
|
||||
at least one `verify.html` gate for its own work and re-runs the full suite
|
||||
(must stay green) before reporting.
|
||||
- UI input paths get a REAL-input test (the wave-1 lesson: OrbitControls was dead
|
||||
because everything was tested via JS — if you add anything clickable, click it
|
||||
via the browser tools, don't just call its handler).
|
||||
|
||||
### 0.6 The report (end your run with this)
|
||||
|
||||
1. What shipped (per feature, one line each). 2. Gate results with numbers
|
||||
(verify.html count + your lane's new gates + the full-suite re-run). 3. Any
|
||||
deviation from this brief and why. 4. Open questions for Fable. 5. Honest list
|
||||
of what you could not verify and why.
|
||||
|
||||
---
|
||||
|
||||
# Lane N — 🌑 NEARGOD ("the ring becomes real")
|
||||
|
||||
The close-approach layer currently draws REAL data (designation, miss distance,
|
||||
date, speed from JPL CAD) at SCHEMATIC positions — a ring around Earth, honestly
|
||||
labeled. Replace the schematic with truth.
|
||||
|
||||
**N.1 The lookup.** CAD gives designations; orbital elements come from the SBDB
|
||||
*lookup* API (a different endpoint from the `sbdb_query` the belt uses). Add ONE
|
||||
line to serve.py's `UPSTREAMS`:
|
||||
`"sbdb_lookup": "https://ssd-api.jpl.nasa.gov/sbdb.api"`. Client-side:
|
||||
`proxy/sbdb_lookup?sstr=<des>` → JSON with `orbit.epoch` (JD, TDB) and
|
||||
`orbit.elements` — an array of `{name, value}` STRINGS. Parse by NAME
|
||||
(`a, e, i, om, w, ma`), `+Number` each, epoch from `orbit.epoch`. Reject if any
|
||||
is non-finite or `e >= 0.98` (the wave-1 elliptical-only rule; note skips in the
|
||||
status line). Existing 24 h cache TTL covers this endpoint automatically —
|
||||
verify with a repeated fetch (`X-Solargod-Cache: hit`).
|
||||
|
||||
**N.2 The layer.** Rewrite `neos.js`: for each CAD row (keep `dist-max=0.05`,
|
||||
`limit=40`), fetch elements **sequentially** through the proxy (never parallel —
|
||||
burst throttling), then propagate with the existing shared core
|
||||
(`ephem.smallBodyEcl`) exactly like the belt. Each NEO gets: marker sprite at its
|
||||
TRUE position each tick (reuse the reticle glyph; keep the miss-distance color
|
||||
ramp: <1 LD red, <5 amber, else green), its real orbit drawn on `worldGroup` via
|
||||
`ephem.orbitPathFromElements` (thin, 25% opacity, the marker's color), and the
|
||||
existing label/`handlePick` facts (des · LD · date · speed — all unchanged).
|
||||
Fail-soft PER OBJECT: a failed lookup falls back to the wave-1 schematic ring
|
||||
placement for that object alone, and the status line reports the split honestly:
|
||||
`38 real orbits · 2 schematic · CAD+SBDB`. First load is ≤40 sequential lookups
|
||||
(~20–40 s cold): status must count up `fetching 12/40…` like the spacecraft
|
||||
layer does. Cached reloads are instant.
|
||||
|
||||
**N.3 Gate (append to verify.html).** Pick one NEO from the current CAD window
|
||||
programmatically; at its close-approach epoch (`cd`/`jd` from CAD), compute
|
||||
`|helio(NEO) − helio(EMB)|` from your propagated elements — it must be within
|
||||
**25%** of CAD's `dist` (generous: Earth≈EMB, two-body propagation, CAD dist is
|
||||
geocentric; the point is catching wrong-by-an-AU bugs, not chasing arcseconds).
|
||||
Full suite stays green. Real-input test: toggle the layer on in the browser and
|
||||
click one reticle.
|
||||
|
||||
---
|
||||
|
||||
# Lane M — 🌖 SELENE ("the moons get faces")
|
||||
|
||||
21 moons render as flat-color spheres. Give the eight that matter real surfaces,
|
||||
and lock every moon's face to its parent.
|
||||
|
||||
**M.1 Textures.** Targets: **Moon (has one already — leave it), Io, Europa,
|
||||
Ganymede, Callisto, Titan, Triton, Charon, Phobos** (skip any you can't source
|
||||
cleanly). Sources, in order of preference: USGS Astrogeology equirectangular
|
||||
mosaics, NASA/JPL photojournal maps, NASA 3D-resources — public-domain NASA/USGS
|
||||
imagery only (no fan maps, no unknown-license repacks). Downscale to **1024×512
|
||||
JPG** (`sips -Z 1024 --setProperty format jpeg …`), target ≤150 KB each,
|
||||
**≤2 MB added total** (wave-1 committed size is 8.2 MB against a 25 MB budget).
|
||||
Files: `assets/textures/1k_<moon>.jpg`. Every file gets a line in
|
||||
`assets/textures/CREDITS.md`: source URL, mission/instrument, license. If a
|
||||
download 404s: skip, flat color stays (the wave-1 fallback is already in
|
||||
`moons.js` — verify it still works by feeding one bogus filename in dev, then
|
||||
remove the test).
|
||||
|
||||
**M.2 Wiring.** `bodies.js`: add `texture:` to each sourced moon — touch NOTHING
|
||||
else in the file. `moons.js`: call the ctx-provided `loadTextureInto` (same as
|
||||
planets.js does) — the material/fallback machinery already exists.
|
||||
|
||||
**M.3 Tidal lock.** Every major moon keeps one face to its parent. In
|
||||
`moons.js`'s tick: `mesh.rotation.y = θ + π` where θ is the SAME orbital angle
|
||||
main.js uses for the moon's position (`2π·(jd − J2000)/periodDays` — negative
|
||||
period gives retrograde rotation for free; copy the formula, don't import
|
||||
main.js internals). Eyeball gate: focus Jupiter at 1 hr/s and confirm Io's
|
||||
textured face toward Jupiter stays fixed across a full orbit.
|
||||
|
||||
**M.4 Gate.** No verify.html math gate (this is a content lane) — instead:
|
||||
screenshot Jupiter-focus showing ≥2 textured Galileans, zero console errors,
|
||||
full suite still 13/13, texture bytes added reported exactly, CREDITS.md
|
||||
complete. Real-input test: click a textured moon → focus flight works.
|
||||
|
||||
---
|
||||
|
||||
# Lane E — 🌓 SYZYGY ("the almanac learns to predict")
|
||||
|
||||
Compute real sky events from the ephemeris — no network, pure math on
|
||||
`ephem.helioEcl`.
|
||||
|
||||
**E.1 The finder** (`js/layers/events.js`, new — also export plain functions).
|
||||
Event types, all from heliocentric longitudes λ(body) = atan2(y, x) of the
|
||||
J2000-ecliptic position:
|
||||
|
||||
- **Opposition** (outer planets, Mars→Neptune + Pluto): `normDegPM180(λp − λe)`
|
||||
crosses **0** — Sun–Earth–planet in a line, planet at its brightest.
|
||||
- **Solar conjunction** (outer): the same function crosses **180**.
|
||||
- **Inferior / superior conjunction** (Mercury, Venus): crossing **0 / 180**.
|
||||
- **Greatest elongation** (Mercury, Venus): local maxima of the GEOCENTRIC
|
||||
elongation angle `acos( (−E·(P−E)) / (|E||P−E|) )` where E, P are heliocentric
|
||||
vectors — scan for the derivative sign flip.
|
||||
|
||||
Scanner: coarse 5-day steps across a window, detect sign changes / extrema,
|
||||
refine by bisection to <1 hour. API:
|
||||
`findEvents(jdStart, jdEnd) → [{jd, type, bodies, label}]` and
|
||||
`nextEvent(bodyId, jd)`. Compute lazily ±2 years around the current sim date,
|
||||
recompute when the clock leaves the computed window. Keep it off the hot path —
|
||||
run the scan in chunks (a few planets per frame or via `setTimeout(0)`), never
|
||||
a 4-year scan in one tick.
|
||||
|
||||
**E.2 The timeline.** Events render as small diamond marks on `#timeline-outer`
|
||||
(DOM divs positioned by date fraction, your own append-only CSS block, brass for
|
||||
oppositions, dim for conjunctions). Hover title = label + exact date; click =
|
||||
jump the sim clock there (set `clock.simMs` via the ctx — the layer contract
|
||||
already hands you `clock`). HUD row: `Sky events` (default ON), status like
|
||||
`14 events ±2 yr · computed`.
|
||||
|
||||
**E.3 The almanac hook.** In `almanac.js` (yours this wave): planets' cards gain
|
||||
one live fact — `Next opposition` (outer) / `Next elongation` (inner) with date
|
||||
and countdown, via `nextEvent()` imported from events.js. Keep the existing
|
||||
≤5 Hz update discipline.
|
||||
|
||||
**E.4 The one main.js line.** Add `'./layers/events.js'` to `LAYER_MODULES`.
|
||||
Nothing else in main.js.
|
||||
|
||||
**E.5 Gates (append to verify.html).** (a) Mars opposition: known truth —
|
||||
**2027-02-19/20** (next Mars opposition). Your finder, scanned over 2026–2028,
|
||||
must produce a Mars opposition within **±2 days** of 2027-02-19. (b) Venus
|
||||
greatest elongations come ~72 days apart in alternating east/west pairs; assert
|
||||
two consecutive found elongations are 60–80 days apart. Full suite green.
|
||||
Real-input test: click a timeline diamond in the browser → clock jumps.
|
||||
|
||||
---
|
||||
|
||||
# Lane V — 🛳 VESSEL ("cut the tether, open deep time")
|
||||
|
||||
**V.1 Vendor Three.js.** Fetch the exact pinned files the importmap uses
|
||||
(`three@0.170.0`: `build/three.module.js`, `examples/jsm/controls/OrbitControls.js`,
|
||||
`examples/jsm/renderers/CSS2DRenderer.js` + anything they import — chase the
|
||||
import graph, it's shallow) into `vendor/three/`. Rewrite BOTH importmaps
|
||||
(`index.html`, `verify.html`) to relative vendor paths. Add a top-of-file
|
||||
comment in vendor/: version, source URL, MIT license note. Gate: **disconnect
|
||||
network** (or block jsdelivr via a hosts trick — simpler: rename the CDN domain
|
||||
in devtools offline mode) → full app boots, planets move, 13/13 gates minus the
|
||||
live-Horizons ones (which legitimately need JPL — note them as expected-fail
|
||||
offline, confirm they pass online).
|
||||
|
||||
**V.2 Deep time.** The engine already ships Table 2a + 2b
|
||||
(`ephem.setExtendedRange`) — wave 1 built the plumbing, you build the door.
|
||||
HUD toggle `EPOCH: 1800–2050 / 3000 BC–3000 AD` next to MEGA/TRUE. Deep-time
|
||||
mode: `ephem.setExtendedRange(true)`, `CONFIG.time` swaps to
|
||||
`Date.UTC(-2999, 0, 1) … Date.UTC(2999, 11, 31)` (JS Date handles astronomical
|
||||
years fine), timeline end labels read from CONFIG (they're hardcoded `1800`/
|
||||
`2050` in index.html today — fix that), and the hash gains `&e=1` so deep-time
|
||||
views share. **Toggle = reload**: orbit paths and element resolution are baked
|
||||
per-session by design, and you don't own the layers — so the toggle serializes
|
||||
state and reloads the page with `&e=1` in the hash (hash restore is already
|
||||
proven; add the `e` token to `applyHash`/`serializeHash`, which you own). This
|
||||
is deterministic, one line of behavior, and honest about what changes
|
||||
underneath. Spacecraft/NEO/
|
||||
belt layers already fail-soft outside their data windows — verify they degrade
|
||||
gracefully at 1000 BC (spacecraft hidden, belt propagates fine, no errors).
|
||||
- **Time-rate presets**: deep time needs faster travel — append `1 decade/s` and
|
||||
`1 century/s` to `CONFIG.rates` (they're config-driven; the menu builds
|
||||
itself).
|
||||
|
||||
**V.3 Gates (append to verify.html).** Deep-time accuracy: with
|
||||
`setExtendedRange(true)`, Jupiter heliocentric position at **JD 1355818.5
|
||||
(1000 BC-01-01)** vs live Horizons through the proxy (`TLIST`, same pattern as
|
||||
gates 10–11), tol **0.15 AU** (Table 2a is an approximation over millennia —
|
||||
the gate catches wrong-table/wrong-term bugs, not arcseconds). Mercury at
|
||||
2500-01-01, tol 0.02 AU. Confirm Table 2b's M-correction actually engages: with
|
||||
the correction terms zeroed in a scratch copy of the math (in-page, not in
|
||||
ephem.js), Jupiter@1000 BC must get WORSE — that proves the terms are live.
|
||||
Full suite green, online and offline-minus-live-gates.
|
||||
|
||||
---
|
||||
|
||||
*Merge order N → M → E → V; Fable reviews each. HERALD (deploy to the VPS) is
|
||||
deliberately NOT in this wave — it needs the user's DNS/tunnel decision first.*
|
||||
Loading…
Reference in New Issue
Block a user