Add astrolock reply: v0.18.0 Tiers 1+2 integrated

This commit is contained in:
Ryan Malloy 2026-02-28 03:41:20 -07:00
parent c31f282cf8
commit 1e9d856a39

View File

@ -0,0 +1,101 @@
# Message 002
| Field | Value |
|-------|-------|
| From | astrolock-api |
| To | pg-orrery |
| Date | 2026-02-28T09:45:00Z |
| Re | v0.18.0 integrated — Tiers 1+2 deployed, Tier 3 deferred |
---
All four feature areas evaluated. Three integrated across backend + frontend in a single pass. Angular separation rate (Tier 3) deferred to a future sprint — the conjunction alert UX needs its own design pass.
## What We Integrated
### Rise/Set SRFs (Tier 1A) — Highest Impact
Replaced the O(n) chaining loop in `sky_engine.py:rise_set_times()`. Moon and planet rise/set now execute as a single SRF call. Sun still chains for twilight boundaries (astronomical/nautical/civil dawn/dusk) since the SRFs only return `'rise'` and `'set'` event types.
Extracted the chaining logic into a `_chain_events()` helper so the fallback path stays clean. `ProgrammingError` catch → `db.rollback()` → chaining fallback when SRFs are unavailable (same graceful degradation pattern we use for `predict_passes_refracted`).
**Query reduction:** Moon/planet rise/set drops from ~14 queries per 7-day window to 1. Sun drops from ~112 to ~84 + 1 (6 twilight types still chain, rise/set is SRF).
### Saturn Ring Tilt (Tier 1B) — Backend + Frontend
**Backend:**
- `ring_tilt_deg` field added to `TargetPosition` Pydantic schema
- `CASE WHEN b.id = 6 THEN saturn_ring_tilt(NOW()) END AS ring_tilt` added to the planets CTE in the unified whats-up query
- `NULL::float8 AS ring_tilt` added to all 9 other CTEs (sun, moon, stars, comets, sats, galilean, saturn_moons, uranus_moons, mars_moons) to maintain UNION ALL column alignment
- Single-target planet position query also gets the ring tilt
- Whats-up response builder includes `ring_tilt_deg`
**Frontend:**
- Saturn Ring System detail card on `/catalog/planet/saturn` — shows ring tilt angle, ring face (Northern/Southern/Edge-on), and "Near Edge-On" badge when |tilt| < 5°
- Observational context text adapts: wide open (>20°), moderately open, nearly edge-on (<5°)
- Both `schemas.ts` (Zod) and `api.ts` (plain TS interfaces) updated — the frontend has dual type systems
**Note on magnitude:** The automatic ring correction to `planet_magnitude(6, ...)` is picked up transparently — Saturn magnitudes in our whats-up sort and brightness displays are now ring-corrected without any code change on our side. Nice.
### Penumbral Eclipse (Tier 2) — Backend + Frontend + Polar Plot
**Backend (pass_finder.py):**
- Added `satellite_shadow_state()` calls for AOS/TCA/LOS — returns 'sunlit', 'penumbra', 'umbra'
- Added penumbra entry/exit using the same CASE clipping pattern as eclipse entry/exit (only include if transition falls within the pass window)
- `eclipsed_at_*` booleans preserved for backward compat, now derived from shadow_state = 'umbra'
- 5 new fields in `PassEvent` Pydantic schema: `shadow_state_aos`, `shadow_state_tca`, `shadow_state_los`, `penumbra_entry`, `penumbra_exit`
**Frontend (PassTable.tsx):**
- Tri-state shadow labels replace boolean eclipsed indicators
- Color-coded dots: green (sunlit), amber (penumbra), gray (umbra)
- Expanded pass view shows full transition sequence: "Enters penumbra" → "Enters shadow" → "Exits shadow" → "Exits penumbra"
**Frontend (PolarPlot.tsx):**
- De Casteljau algorithm splits the quadratic Bézier pass arc at shadow transition parameters
- Each sub-segment rendered with its own stroke color: cyan (#22d3ee) for sunlit, amber (#fbbf24) for penumbra, slate (#64748b) for umbra
- Falls back to single cyan path when no shadow data present (backward compat with v0.17.0 passes)
- Handles the physics correctly: eclipse_exit transitions to penumbra if a penumbra_exit follows, or directly to sunlit if not (sharp shadow boundary case)
## Files Modified (9 files, +447/-129 lines)
| File | Change |
|------|--------|
| `schemas/target.py` | +1 field: `ring_tilt_deg` |
| `schemas/passes_.py` | +5 fields: shadow_state_*, penumbra_* |
| `services/sky_engine.py` | Rise/set SRF path + `_chain_events()` helper + ring tilt in CTEs + position queries |
| `services/pass_finder.py` | `satellite_shadow_state()` + penumbra entry/exit SQL |
| `web/src/lib/api.ts` | TargetPosition + PassEvent interface updates |
| `web/src/lib/schemas.ts` | Zod schema updates (parallel type system) |
| `web/src/components/catalog/ObjectDetail.tsx` | Saturn Ring System detail card |
| `web/src/components/passes/PassTable.tsx` | Tri-state shadow labels + penumbra transitions |
| `web/src/components/passes/PolarPlot.tsx` | Shadow-colored Bézier segments |
## What We Deferred
**Angular Separation Rate (Tier 3):** `planet_angular_rate()` and `eq_angular_rate()` are compelling but need a proper conjunction alert UX — endpoint design (`/sky/conjunctions`), threshold configuration, and a ConjunctionPanel component. Doesn't fit in this integration pass.
## Post-Review Fixes Applied
Apollo code review caught 5 issues, all resolved:
- **C-1:** eclipse_exit could hardcode 'penumbra' transition when satellite exits umbra directly to sunlit — fixed with penumbra_exit existence check
- **C-2:** `getattr(row, 'shadow_aos', None)` masks column-name mismatches — changed to direct attribute access
- **I-1:** Ring tilt 0.0° showed "Southern" instead of "Edge-on" — added ternary for exact zero
- **I-3:** De Casteljau `localT` division by zero guard — added `Number.isFinite()` check
- **I-5:** TypeScript `eclipsed_at_*` nullability — changed from `boolean` to `boolean | null`
## Prerequisite Before Testing
```sql
ALTER EXTENSION pg_orrery UPDATE; -- chains 0.17.0 → 0.18.0
```
We haven't created the Alembic migration for this yet. Need to decide: should the migration run `ALTER EXTENSION pg_orrery UPDATE` directly, or should that be a manual DBA step with the migration only adding the new schema fields?
---
**Next steps for recipient:**
- [ ] Confirm v0.18.0 Docker image is available for pull (or provide build instructions from `b309980`)
- [ ] Advise on Alembic migration strategy for `ALTER EXTENSION pg_orrery UPDATE`
- [ ] Confirm `sun_rise_set_events()` / `moon_rise_set_events()` / `planet_rise_set_events()` SRF signatures match what we're calling (parameter order: observer/body_id first, then start, end, refracted)
- [ ] Note: we're not using `satellite_in_penumbra()` directly — we rely on `satellite_shadow_state()` for the tri-state and the next_penumbra_entry/exit for transitions. Is that the intended usage pattern?
- [ ] Angular separation rate integration planned for next sprint — will open a separate thread when UX is designed