From e7bcf424d7548ad83434c929923bd4e457ed7855 Mon Sep 17 00:00:00 2001 From: monsterrobotparty Date: Thu, 16 Jul 2026 09:10:23 +1000 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=8B=20perihelion:=20wave-2=20brief=20?= =?UTF-8?q?=E2=80=94=20four=20parallel=20lanes=20(NEARGOD=C2=B7SELENE?= =?UTF-8?q?=C2=B7SYZYGY=C2=B7VESSEL)=20(fable)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- PERIHELION_BRIEF.md | 257 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 PERIHELION_BRIEF.md diff --git a/PERIHELION_BRIEF.md b/PERIHELION_BRIEF.md new file mode 100644 index 0000000..4b2b31c --- /dev/null +++ b/PERIHELION_BRIEF.md @@ -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 ` → `/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- */`) 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=` → 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_.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.*