From d3fc801d60d5362d730736f9d9223ac0e425e1f7 Mon Sep 17 00:00:00 2001 From: benweru <138494503+benweru@users.noreply.github.com> Date: Tue, 20 Oct 2026 01:08:35 +0300 Subject: [PATCH] docs(tasks): add web v1 sprint briefs Co-authored-by: Cursor --- docs/plan.md | 11 +++++++ docs/tasks/sprint12-web-scaffold.md | 32 +++++++++++++++++++ docs/tasks/sprint13-web-tle-sgp4.md | 35 ++++++++++++++++++++ docs/tasks/sprint14-web-nairobi-passes.md | 39 +++++++++++++++++++++++ docs/tasks/sprint15-web-cesium-globe.md | 35 ++++++++++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 docs/tasks/sprint12-web-scaffold.md create mode 100644 docs/tasks/sprint13-web-tle-sgp4.md create mode 100644 docs/tasks/sprint14-web-nairobi-passes.md create mode 100644 docs/tasks/sprint15-web-cesium-globe.md diff --git a/docs/plan.md b/docs/plan.md index 35cf3a0..e1418c0 100644 --- a/docs/plan.md +++ b/docs/plan.md @@ -85,6 +85,17 @@ Work is organized in titled sprints. Ben approves each title in chat, and the ag The old weeks 3–4 ("All 15 classes, noise, split; second-reviewer check") are split into Sprints 3 and 4. Later phases are renumbered one per sprint. +### Web v1 (parallel track) + +Interactive globe in `web/`. It is independent of the Python benchmark: `make test` and `make reproduce` must not require Node. **v2** (onboard diagnosis story) starts only after committed files exist in `results/`. Sprint titles below are *(title pending Ben's approval)*. + +| Sprint | Work | Deliverable | +|---|---|---| +| 12: Isolated Vite globe shell *(title pending Ben's approval)* | `web/` Vite + React + TypeScript + Vitest; CI job; no Python coupling | `cd web && npm install && npm run dev` and `npm test` | +| 13: TLE cache and SGP4 *(title pending Ben's approval)* | Fetch CelesTrak GP TLEs, cache with fetch time, propagate with `satellite.js` | Unit-tested TLE parse + ECI/ECF position | +| 14: Nairobi contact windows *(title pending Ben's approval)* | Look angles, elevation mask, next-pass countdown | Rise / max elevation / set for Nairobi, tested | +| 15: Live globe, picker and credits *(title pending Ben's approval)* | CesiumJS globe, moving sat, highlighted next pass, picker, credits; Heavens-Above/N2YO check | v1 acceptance: moving satellite, pass times within ~1 min of an independent source | + ## 9. Hardware and costs (approximate; check local prices) - Laptop he already owns (development and the main grid): KES 0. - Raspberry Pi 5 (8 GB, so the cap is enforced in software) + NVMe/SD + PSU: about USD 120–150 **[verify the Nairobi price]**. diff --git a/docs/tasks/sprint12-web-scaffold.md b/docs/tasks/sprint12-web-scaffold.md new file mode 100644 index 0000000..009299c --- /dev/null +++ b/docs/tasks/sprint12-web-scaffold.md @@ -0,0 +1,32 @@ +# Sprint 12: Isolated Vite globe shell + +- **Sprint:** 12: Isolated Vite globe shell *(title pending Ben's approval)* +- **Owner:** Ben's agent +- **Branch:** `feat/web-vite-scaffold` +- **PR title:** `feat(web): add isolated Vite React TypeScript app` + +## Goal +A `web/` app with its own `package.json` that installs and runs without touching the Python benchmark. `make test` and `make reproduce` still work with no Node. + +## Context +The visualization brief (`web-agent-brief-visualisation`) is v1 first: a Cesium globe later; this sprint only proves the Node island. Stack: Vite + React + TypeScript + Vitest. Pin exact npm versions. Inspired by [Skyhitch](https://skyhitch.app/) / [God's Eye View](https://github.com/bilawalsidhu/gods-eye-view) (MIT), but this PR does not copy their code. + +Cesium stays out of this sprint so CI stays light and the globe work is one PR (`feat/web-orbit-contact-windows` in sprint 15). + +## Steps +1. Scaffold `web/` (Vite React-TS). Pin versions. Add Vitest with one smoke test. +2. Root `.gitignore`: `web/node_modules/`, `web/dist/`. +3. `web/README.md`: `npm install`, `npm run dev`, `npm test`. State that this app never computes benchmark numbers. +4. Placeholder page: what OrbitLog is, that v1 will show real orbits, that diagnosis UI is v2 and **pending** until `results/` exists. No fake figures. +5. Optional `make test-web` that runs `npm test` in `web/`. Do **not** fold it into `make test`. +6. GitHub Actions: a `web` job on PRs that touch `web/**`, using Node 22 (matches the local toolchain used to pin packages). + +## Acceptance criteria +- [ ] `cd web && npm install && npm run dev` serves the placeholder. +- [ ] `cd web && npm test` passes. +- [ ] `make test` still runs pytest only (no Node). +- [ ] `make test` passes locally and in CI. + +## Out of scope +- Cesium, TLEs, SGP4, pass prediction (sprints 13–15). +- Anything under `src/orbitlog/` or `results/`. diff --git a/docs/tasks/sprint13-web-tle-sgp4.md b/docs/tasks/sprint13-web-tle-sgp4.md new file mode 100644 index 0000000..ca1e0dd --- /dev/null +++ b/docs/tasks/sprint13-web-tle-sgp4.md @@ -0,0 +1,35 @@ +# Sprint 13: TLE cache and SGP4 + +- **Sprint:** 13: TLE cache and SGP4 *(title pending Ben's approval)* +- **Owner:** Ben's agent +- **Branch:** `feat/web-tle-sgp4` +- **PR title:** `feat(web): fetch CelesTrak TLEs and propagate with SGP4` + +## Goal +The web app can load a Two-Line Element set (TLE) from CelesTrak, cache it with the fetch time, and turn it into a position at a given UTC instant using SGP4 (`satellite.js`). No globe yet. + +## Context +Operators plan **contact windows** by propagating published TLEs with **SGP4**. CelesTrak GP queries are `https://celestrak.org/NORAD/elements/gp.php?{QUERY}=VALUE[&FORMAT=VALUE]` ([GP data formats](https://celestrak.org/NORAD/documentation/gp-data-formats.php)). `{QUERY}` must be uppercase. **Always pass `FORMAT=TLE`**: as of 2026-05-09 the default format is CSV, not TLE. + +v1 catalog (fixed CATNRs, not a live group dump): +- ISS (ZARYA), CATNR **25544** +- LANDSAT 9 (Earth observation), CATNR **49260** ([SATCAT](https://en.wikipedia.org/wiki/Landsat_9)) +- One CubeSat whose TLE still exists on CelesTrak `GROUP=cubesat` at implementation time (record the CATNR and a live fetch in the PR) + +Comment in plain words: a TLE is a snapshot of an orbit; SGP4 is the standard propagator; it is good for days, not months. + +## Steps +1. Depend on pinned `satellite.js`. +2. Fetch `gp.php?CATNR=&FORMAT=TLE`, parse name + two lines, store in `localStorage` (or equivalent) with `fetchedAt` ISO UTC. Show fetch time in a small status line. +3. `propagateEci(tle, date)` using `satellite.twoline2satrec` + `satellite.propagate`. +4. Vitest: parse a **fixture** TLE (committed, not live-fetched in CI); reject truncated lines; cache round-trip including `fetchedAt`. Live CelesTrak is manual / documented in the PR, not required for CI. + +## Acceptance criteria +- [ ] Unit tests pass with no network in CI. +- [ ] A live fetch of ISS 25544 is pasted into the PR (raw TLE + timestamp). +- [ ] UI (even the placeholder) shows TLE fetch time or “not fetched”. +- [ ] `make test` (Python) still passes without Node. + +## Out of scope +- Look angles, Nairobi passes, Cesium (sprints 14–15). +- Diagnosis / `results/` (v2). diff --git a/docs/tasks/sprint14-web-nairobi-passes.md b/docs/tasks/sprint14-web-nairobi-passes.md new file mode 100644 index 0000000..ba02018 --- /dev/null +++ b/docs/tasks/sprint14-web-nairobi-passes.md @@ -0,0 +1,39 @@ +# Sprint 14: Nairobi contact windows + +- **Sprint:** 14: Nairobi contact windows *(title pending Ben's approval)* +- **Owner:** Ben's agent +- **Branch:** `feat/web-nairobi-passes` +- **PR title:** `feat(web): predict Nairobi contact windows from look angles` + +## Goal +Given a TLE and “now”, compute the next pass over Nairobi: rise time, max elevation, set time, and a live countdown. Pure functions, fully unit-tested. The globe still comes in sprint 15. + +## Context +A **contact window** is the stretch of time the satellite is above a minimum elevation at the ground station. **Look angles** are azimuth (compass direction) and elevation (angle above the horizon) from that station to the satellite. + +**Nairobi station** (city, not a specific dish), WGS-84: +- latitude **−1.286389°**, longitude **36.817223°** (1°17′11″S, 36°49′02″E, [Wikipedia: Nairobi](https://en.wikipedia.org/wiki/Nairobi)) +- altitude **1795 m** (same article, elevation of the city) + +**Elevation mask:** default **10°** (a typical “usable pass” cut, and the usual Heavens-Above default). Also compute **0°** internally so the Heavens-Above / N2YO comparison in sprint 15 can match their horizon setting. Document the mask in the UI. + +`satellite.gstime` + `eciToEcf` + `ecfToLookAngles` in `satellite.js` give azimuth/elevation. A pass is a contiguous run of samples with elevation ≥ mask. + +OrbitLog’s story: windows are about 5–10 minutes, a few times a day. Do not invent byte counts here. + +## Steps +1. `lookAngles(tle, station, date)` → az, el, range. +2. `nextPass(tle, station, from, maskDeg)` → `{ rise, maxEl, maxElAt, set }` or none in the search window (search ≥ 24 h, 1 s or better near crossings). +3. `countdown(to, now)` → remaining seconds (clamp at 0). +4. Vitest with the sprint 13 ISS fixture: a known-in-test pass (freeze `now`); countdown arithmetic; mask 10° vs 0° changes rise/set. +5. Placeholder UI: next ISS pass over Nairobi (rise, max el, set, countdown) once a TLE is cached. + +## Acceptance criteria +- [ ] Unit tests pass with no network. +- [ ] UI shows rise, max elevation, set, countdown, elevation mask, and TLE epoch/fetch time. +- [ ] `make test` (Python) still passes without Node. + +## Out of scope +- Independent Heavens-Above / N2YO check (sprint 15 PR, needs the live globe and a dated screenshot). +- Cesium rendering, satellite picker. +- v2 diagnosis panels (show **pending** if anything is sketched). diff --git a/docs/tasks/sprint15-web-cesium-globe.md b/docs/tasks/sprint15-web-cesium-globe.md new file mode 100644 index 0000000..831810b --- /dev/null +++ b/docs/tasks/sprint15-web-cesium-globe.md @@ -0,0 +1,35 @@ +# Sprint 15: Live globe, picker and credits + +- **Sprint:** 15: Live globe, picker and credits *(title pending Ben's approval)* +- **Owner:** Ben's agent +- **Branch:** `feat/web-orbit-contact-windows` +- **PR title:** `feat(web): show a live satellite and Nairobi contact windows` + +## Goal +v1 of the visualization: a CesiumJS globe with a real satellite moving on its SGP4 track, Nairobi marked, the next contact highlighted, a few satellites to pick, and credits. Pass times agree with an independent source to about a minute. + +## Context +Closes the v1 acceptance list in the visualization brief. Use CesiumJS (or Resium) plus the sprint 13–14 libraries. Official Vite asset wiring: copy Workers/ThirdParty/Assets/Widgets and set `CESIUM_BASE_URL` ([Cesium + Vite](https://cesium.com/blog/2024/02/13/configuring-vite-or-webpack-for-cesiumjs/)). Prefer OSM / Ion-optional imagery so the app runs without a paid token; if `VITE_CESIUM_ION_TOKEN` is set, use it. + +Satellites: ISS 25544, Landsat 9 49260, plus the CubeSat CATNR chosen in sprint 13. + +Honesty: no diagnosis numbers. Any v2 panel is **pending**. Faults are not shown. + +## Steps +1. Pin `cesium` (and Resium only if it earns its keep). Wire static assets. +2. Viewer: Earth, Nairobi point, satellite entity updated from SGP4, orbit polyline, next-pass arc highlighted. +3. HUD: countdown, rise / max el / set, TLE fetch time, elevation mask, satellite picker. +4. In-app credits: CelesTrak, Cesium, imagery provider, satellite.js; mention Skyhitch / God's Eye View as inspiration (MIT) without copying code. +5. Vitest stays on orbital math (already in 13–14). Do not try to unit-test WebGL. +6. **Verification (required in the PR):** ISS over Nairobi vs [Heavens-Above](https://heavens-above.com/) or [N2YO](https://www.n2yo.com/). Use the same mask those sites use (state it). Rise/set within **~1 minute**. Paste times, URLs, and a screenshot. + +## Acceptance criteria +- [ ] `cd web && npm install && npm run dev` shows a real satellite moving. +- [ ] Next Nairobi pass (rise, max elevation, set) shown; independent check within ~1 min, recorded in the PR. +- [ ] Unit tests pass; load feels under ~5 s on a normal laptop (note machine in the PR). +- [ ] Credits visible in the app. +- [ ] `make test` (Python) still passes without Node. + +## Out of scope +- v2: fault injection playback, byte comparison, downlink queue (needs committed `results/`). +- Any fabricated accuracy or byte figures.