Godstrument/GODSIGH_CROSSOVER_BRIEF.md
2026-07-13 13:34:18 +10:00

79 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# GODSIGH ✕ GODSTRUMENT — Crossover Build Brief
**Audience:** Claude Opus 4.8, executing autonomously in this repo (`godstrument`).
**Author:** Claude Fable 5, 2026-07-13, after mapping both codebases.
**Companion:** `~/Documents/GODSIGH/SPEC2.md` (the sibling wave in the GODSIGH repo). Task 3 below shares a **cache contract** with it — the contract text must stay in sync.
**Before anything:** skim `README.md`, `run.py` (worker supervision + profiles), one worker (`workers/world_iss.py` is the cleanest contract example), `config.json` (signals/routes/groups shape), and the Spirits shelf region of `viz/index.html` (~line 5111 on, "one Source, many Spirits, one Matter"). Match house style: workers are argparse + pythonosc standalone processes emitting `/gs/<group>/<name>`; commits carry an emoji prefix (`🛰`, `🥁`, `📖`…); shipped features get a grimoire/manual entry (see the `📖` commits and `GODSTRUMENT_MANUAL_SOURCE.md`); `*_BRIEF.md` files are deploy-excluded. Verify in the browser (`python3 run.py --profile minimal` + your new worker) before each commit.
Execute the four tasks in order; commit + push per task.
---
## Task 1 — 🎹 Land the local "keyroll" work (groove editor) safely
**Situation.** Branch `local/groove-editor` (commit `c0c3a98`, single commit on top of pre-godrum `3ff105d`) holds ~637 lines of real local work on the sequencer: a piano-roll editor with **draggable note lengths** (`.seqnote` overlay blocks with an `ew-resize` handle), **velocity + chance lanes** (`.explane` draggable bar-per-step lanes), a `seqedit` wrapper with lane toggles/selectors, **Euclidean tools** (`.euc`, `.tnum`), groove **knobs** (`.aknob`), bar-line markers, and a scroll container. Meanwhile `main` merged the whole **godrum** line, which reworked the same sequencer UI area and independently built per-step velocity+chance, Euclidean fills, swing/humanize, and Follow Actions.
**Do NOT `git merge local/groove-editor` blind** — the branches rewrote the same regions and an auto-merge will produce soup. Instead:
1. **Feature-gap analysis first.** Read the branch diff (`git diff 3ff105d..local/groove-editor -- viz/index.html`) and the current main sequencer ("Groove" region, ~`viz/index.html:202`/`:238`, plus godrum's beat panel). Build an explicit table: for each keyroll feature — note lengths / velocity lane / chance lane / lane toggles / euclid tools / groove knobs / bar lines / scroll container / big-grid variant — does current main already have an equivalent (however differently spelled)?
2. **Port only the gaps, natively.** Reimplement each missing feature on top of *current* main's structures and naming (the branch is the reference spec, not a patch to apply). Likeliest genuine gaps, from marker analysis: the **note-length piano roll** (`seqnote` spanning blocks with drag-resize — main has none) and possibly the **explane-style aligned drag-lanes UI**; likeliest already-covered: euclid, velocity/chance *data* (godrum has the data model — the gap may be only the lane *editor UI*).
3. Keep godrum's behavior intact — its choke anchors, euclid guard, kick-duck etc. were carefully review-hardened (`fa5aa43`, `b0fad08`); regressions there are worse than missing a keyroll nicety.
4. Verify by playing: place notes with lengths, drag velocities/chances, euclid-fill, confirm godrum voices still behave, MIDI export still valid.
5. When everything valuable is landed on main, delete the branch (`git branch -D local/groove-editor`) and say so in the commit body — that branch is the only copy of the reference work, so delete it **only after** the port is committed and pushed.
6. Grimoire/manual entry for the new editor capabilities, per house convention.
## Task 2 — 🛰 `workers/world_sats.py`: real satellites as a source (SGP4, no polling APIs)
GODSIGH proved the pattern: TLEs from Celestrak + SGP4 gives *any* satellite's position locally at any rate — no per-fix HTTP like `world_iss.py` needs. Bring that superpower here as a worker:
- **Dependency:** `sgp4` (pure-python, pip). Add to `requirements.txt`.
- **TLEs:** fetch at startup from `https://celestrak.org/NORAD/elements/gp.php?GROUP=stations&FORMAT=tle` plus `NAME=GAOFEN&FORMAT=tle` and `NAME=COSMOS%202486` / `NAME=COSMOS%202506` (the recon birds — thematic gold). Cache the TLE text to `~/.cache/godverse/tles.txt` and refresh only if the cache is >24 h old (Celestrak politeness; TLEs age slowly). Tolerate fetch failure by using a stale cache; tolerate no-cache-no-net by exiting politely like other workers.
- **Compute** (default `--interval 2`): propagate each satrec to now (`sgp4` gives ECI; convert to lat/lon/alt via the standard GMST rotation — port the math from GODSIGH `js/layers/satellites.js` / satellite.js's `eciToGeodetic`, it's ~15 lines), then derive **elevation angle** above the venue city's horizon (city lat/lon from `cities.json` via the same `--lat/--lon` retargeting `run.py` applies to other geo workers — accept those flags).
- **Emit** (the contract: raw floats, hub normalizes):
- `/gs/sats/best_elev` — highest elevation (degrees, can be negative) among tracked sats: the "something is overhead" continuous signal.
- `/gs/sats/overhead_count` — count with elevation > 10°.
- `/gs/sats/iss_lat`, `/gs/sats/iss_lon` — keep the ISS sweep available even when `world_iss.py` isn't running.
- `/gs/sats/pass.event` — impulse when any sat **crosses rising 30°** (a recon bird crests the sky and a bell rings). Include which sat in the printed log line.
- `/gs/sats/recon_elev` — best elevation among only the GAOFEN/COSMOS set (the watchers' own channel).
- **config.json:** declare the new signals with sensible `norm` ranges (elev `lo:-30, hi:90`; count `lo:0, hi:8`) + labels; add 23 starter routes (suggest: `sats.best_elev → pad.brightness` gentle; `sats.pass → delay.feedback` impulse; `sats.recon_elev → lfo.rate` — the sky watching back). Add the worker to the `WORLD` profile in `run.py`.
- Grimoire/manual entry: "the sky's schedule joins the orchestra."
## Task 3 — 🤝 Shared OpenSky cache (reader side)
**The contract (mirrored verbatim in GODSIGH SPEC2.md §4 — keep in sync):**
> **OpenSky shared cache v1.** Path: `~/.cache/godverse/opensky-states.json`, overridable via env `OPENSKY_CACHE_FILE`. Content: the raw, unmodified OpenSky `/states/all` JSON response body (global, no bbox). Writer: whoever successfully fetches from upstream writes the body **atomically** (write to `<path>.tmp` in the same directory, then `os.replace`). Freshness = file mtime. Readers treat the cache as fresh if `mtime` is within **120 s**; on upstream 429/failure, readers may serve/use a stale cache rather than nothing.
GODSIGH's dev proxy implements the writer side. Here, make `workers/world_planes.py` cache-aware:
1. Each poll cycle: if the cache file exists and is **fresh**, read it and skip the HTTP call entirely (log `[world_planes] cache hit`). GODSIGH fetches globally; if this worker uses a bbox, filter the cached global states client-side by lat/lon — same fields, indices 5/6.
2. On a real HTTP poll that succeeds **with a global scope**, write the cache per the contract (atomic tmp+rename, `mkdir -p` the directory). If this worker polls a bbox, do NOT write (never write partial data into a global-contract file).
3. On 429 or network failure: fall back to a stale cache if present (log it), else current behavior.
4. Keep the existing ≥450 s cadence — the cache makes the *effective* shared cost whatever the most aggressive consumer pays, once, per window.
Result: GODSIGH (90 s cadence) + godstrument running together spend ~one poller's quota instead of two.
## Task 4 — 👁 "GOD'S EYE" Spirit (a skin for the shelf)
A new Spirit in the existing shelf system (register exactly like the others — bagua `~:5791`, Kandinsky `~:5689` are good templates): the world's sensor picture as the cosmology. 2D canvas, house style — no new libraries.
- **Stage:** a minimal equirectangular world (a sparse hand-drawn continent outline is enough — store a small `[[lon,lat],...]` polyline set inline; do NOT ship a heavy geo dataset into the one-file viz), graticule every 30°, dark ground, the venue city marked with a ring.
- **Seat the feeds geographically, not symbolically** (this Spirit's whole cosmology is *literal coordinates*): quakes flash at their true lat/lon (the quake worker's events carry location — check what reaches the WS feed and use what's available; if only intensity arrives, flash at the venue antipode and note it), ISS/satellites sweep as moving points with short trails (from Task 2's `sats.*` lat/lon signals), planes as a shimmer band whose brightness = `planes.count`-ish signal, market as a ticker pulse at the financial capitals (NY/London/Tokyo dots), solar wind as an aurora wash at the poles, wiki edits as brief sparks at random civilizational coordinates.
- **Tuning:** like every Spirit, choose a scale + palette identity — suggest a cold surveillance palette (near-black, cyan `#35e0ff`, warning amber) and a minor-pentatonic retune, so entering the Spirit *sounds* like a watch floor.
- Register it on the shelf, respect performance mode (`P`) and zen (`Z`), grimoire/manual entry ("the eye that hears").
## Verification & workflow
- Per task: run `python3 run.py --profile minimal` plus the touched worker(s) with explicit flags; watch the hub log for your signals arriving; open the viz and confirm in the browser (Spirit renders, sequencer plays, no console errors). `python3 -m py_compile` every touched .py; keep workers import-safe when optional deps are missing (house rule).
- Commit per task with the emoji house style; push after each. End commit messages with `Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>`.
- Do not deploy anything from this brief; deployment of godstrument follows its own conventions and is not part of this work.
## Pitfalls
- Task 1: the keyroll branch predates godrum — never trust an auto-merge; port by intent. Delete the reference branch only after the port is pushed.
- `sgp4`'s `Satrec.sgp4()` returns position in **km, TEME frame** — the GMST rotation to geodetic is mandatory, and `satrec.no_kozai` is rad/min (mirror GODSIGH's math, it's verified).
- Celestrak NAME queries can return `No GP data found` plain text — detect (first data line must start with `1`) and treat as empty, not fatal (verified behavior, same as GODSIGH).
- Atomic cache writes: keep the `.tmp` beside the target file (same filesystem), not in `/tmp`.
- The viz is one 9,256-line file — make surgical, well-commented insertions in the established regions; don't reorganize it.