---
name: autonomous-night
description: >
  Unattended full-night run-book for a Seestar S50: assess, run the ranked plan
  target-by-target, react to conditions/QA, and wind down + park at dawn — on its own,
  behind hard safety guardrails and a mandatory dry-run confirmation. Use whenever the
  user wants to hand over the whole night — e.g. "run the whole night", "image
  unattended", "run an autonomous session", "image all night on your own", "run my
  target list unattended", "let it run itself till dawn". Starts with a NO-MOTION
  `simulate_night` projection that the user must explicitly approve before the first
  motion command. Orchestrates the existing tools in a visible loop — it decides
  *whether to keep going and what's next*; it does not re-implement motion (that is
  `run-session`).
---

# Seestar S50 Autonomous Night

This skill runs a whole night unattended: propose a plan, get one explicit go-ahead,
then loop target-by-target under hard guardrails, and park at dawn or on any hard stop.
Autonomy here is **Claude driving the existing audited tools in a visible loop** — not a
hidden background engine. Every decision is logged and surfaced. When in doubt, **stop
and park (fail safe).**

## Operating assumptions
- The user is usually watching from the Claude phone app via Remote Control on a small
  screen. Every message is one line, lead with state, not prose. Notify on each target
  change and every stop — the phone is how they know what happened overnight.
- This skill owns *sequencing and safety*, not motion. Planning judgment lives in
  **`observing-planner`**; execution (goto → focus → stack → monitor) lives in
  **`run-session`**; faults live in **`anomaly-playbook`**; QA thresholds live in
  **`qa-policy`**. Do not re-implement any of those here.
- Provenance: the tools log their calls; you add a one-line human-readable note per
  guardrail decision and target switch.

## Phase A — Propose (NO MOTION, mandatory confirmation gate)
1. `simulate_night` (optionally pass `types` / `limit` if the user asked). This is a
   **dry run — it issues NO motion.** It returns the conditions verdict, the dark
   window, and the ordered projected schedule.
2. **Reconcile the schedule against the CURRENT time.** `simulate_night` packs from the
   start of the dark window, not from now — so if the night is already underway (the common
   case), its early slots are already in the past and the list is not runnable as returned.
   Drop the slots that have passed and re-pack the remainder from the current time, so you
   present **only what can actually be observed tonight**. Say it in one line, e.g.
   `Dark started 22:57; it's now 00:06 — first two slots already past. Runnable remainder:`
   Never hand the user a schedule whose first target's window has already closed.
3. Present, compactly:
   - the **one-line conditions verdict** (from `simulate_night`'s `conditions`);
   - the **ordered schedule** — per `ScheduledTarget`, one line: name · window (UTC) ·
     minutes · `subs × 10s` · the one-line reason, e.g.
     `1. M27 · 22:40–00:10 UTC · 90 min · 540×10s · long sweet-band pass, suits site.`
   - the **guardrail defaults** that will apply: dawn margin (15 min), battery floor
     (20%), max session (10 h), weather no-go stops.
   - a note that **each target spends ~2–4 min acquiring** (alignment + autofocus) before
     its first frame stacks, so a 45-min slot yields roughly 42 min of integration. Do not
     promise the full slot as integration time.
4. **State plainly that this is a dry run and REQUIRE explicit user confirmation before
   ANY motion command.** Say it in one line, e.g.
   `Dry run only — nothing has moved. Reply "go" to start the run; I'll park at dawn or on any hard stop.`
   This confirmation gate is **mandatory and non-skippable.** Do not slew, focus, stack,
   or otherwise command motion until the user explicitly says to begin.
5. If conditions are **no-go** (`simulate_night` returns `ok:false`, an empty schedule,
   or a no-go verdict), say so in one line and **do not start.** Offer to re-simulate
   later or for a clearing window, but issue no motion.

## Guardrail semantics (read this before overriding a stop)
Hard stops are **predictive, not reactive.** `check_night_guardrails` reads the forecast and
device health, so a weather stop can fire while the current sky is still stacking cleanly
with zero dropped frames. **That is correct behavior, not a false positive** — the lead time
is exactly what lets the mount stop and fold *before* precipitation or heavy dew arrives.
Two signals commonly drive it:
- **precipitation** forecast inside the session window, and
- **dew risk** — a small temperature/dew-point spread (a couple of °C or less) means
  condensation forming on the optics, which ends the night's usefulness even under a clear
  sky.

When a stop fires: corroborate once with `assess_conditions` to get the human-readable
reason, state it in one line, and **wind down (Phase C).** Do not re-run the check hoping
for a different answer, and never resume a stopped run unless the user explicitly asks.

## Phase B — Loop (per target)
Record the run's `session_start_utc` at first go-ahead. Then, for each target:

1. **Guardrail check FIRST — every iteration, no exceptions.** Call
   `check_night_guardrails(session_start_utc=...)`. If it returns
   `action: "park_and_stop"`, go straight to **Phase C** and quote the hard-stop reason
   in one line (from `hard_stops` / `reasons`). **Never skip this check between
   targets.**
2. Otherwise take the **next `ScheduledTarget`** from the schedule and hand it to the
   **`run-session`** skill: goto → plate-solve → focus → stack → monitor (the `qa_tier1`
   cadence plus the Phase 4 live reactivity — conditions watch and sweet-band watch).
   Notify the user of the target change in one line.
   - **The approved plan is an authorization, not a blank cheque.** The user approved *that
     schedule*. Running it as-is needs no further confirmation, but any **material
     deviation** — skipping a target, substituting one that was not in the dry run,
     reordering, or materially extending a slot — must be **surfaced in one line as it
     happens**, with the reason. Skipping an obstructed target and moving on is fine and
     expected; doing it silently is not. Anything that would take the night somewhere the
     user did not see in the dry run (a target off-plan, the dew heater, a mask edit) needs
     a fresh confirmation.
   - **When a target is blocked, check its neighbours before slewing.** A local obstruction
     is a *direction*, not a single target: before taking the next item, scan the remaining
     plan for targets at similar azimuth and **lower** altitude and skip them together.
     Discovering the same roofline one slew at a time can waste most of an hour.
3. **End the target's slot** when any of these happen: its scheduled window ends, it
   leaves its sweet band (nearing the field-rotation ceiling or the altitude floor), or
   QA collapses. Then call `log_session_result(...)` for it (integration, sub counts,
   median FWHM per the wind-down in `run-session`) and **re-enter the loop** at step 1
   for the next target.
4. **Faults → `anomaly-playbook`.** Route any mid-target fault (stall, solve/focus
   failure, tracking loss, connection drop, weather flip) there. If it resolves, resume
   the loop. If it is an **unrecoverable fault or a hard guardrail stop**, go to Phase C
   — end in `park`. Re-check guardrails on any anomaly, not just at slot boundaries.

## Phase C — Wind down + park
Reached on any hard stop, unrecoverable fault, end of schedule, or user stop.
1. `stop_view` to end stacking cleanly.
2. `log_session_result(...)` for the **in-progress** target so its integration is not
   lost.
3. **`park`** the mount (stops tracking, optics to horizontal). Parking is
   non-negotiable on any hard stop.
4. **Summarize the night** in a compact block and **notify the user**: targets imaged,
   integration on each, projects advanced, and the reason the run ended (dawn / battery
   / weather / connection / max duration / schedule complete / user stop).
5. Only `shutdown` if the user **pre-authorized** it (shutdown ends the seestar_alp
   link). Otherwise leave the scope parked and connected.

## Hard rules
- **The dry-run + explicit confirmation before motion is MANDATORY and non-skippable.**
  Nothing moves in Phase A. The first motion command only follows an explicit user
  go-ahead.
- **Five HARD stops, each of which ALWAYS ends in `park`:** astronomical **dawn**
  (within the margin), **low battery** (below floor), **precipitation / hard weather
  no-go**, **lost connection / unverified scope**, **max session duration** exceeded.
  Hard stops are non-negotiable.
- **Never skip `check_night_guardrails` between targets** — call it at the top of every
  loop iteration and on any anomaly.
- **Log every session** (`log_session_result`), including the in-progress target at
  wind-down, so integration accumulates across nights.
- **Keep the user notified** of each target change and every stop (Remote Control
  surfaces these on the phone).
- **Fail safe: when in doubt, stop and park.** If scope health can't be confirmed
  (`check_night_guardrails` can't read device state → treated as disconnected), the run
  stops and parks. Never leave the mount slewed or tracking on a fault.
- **This skill decides *whether to keep going and what's next*; it does not re-implement
  motion** (that is `run-session`). Planning = `observing-planner`, execution =
  `run-session`, faults = `anomaly-playbook`, QA = `qa-policy`.

## Operating notes
Non-obvious behaviors that cost real observing time when ignored.
- **`goto_target` returns ok even when the mount does NOT slew, and a normal alignment sits
  in `Initialise` for minutes.** Use the acquisition discriminator in **`run-session`**
  (Phase 1) to tell a healthy alignment from an unsolvable field; skip obstructed targets
  rather than waiting them out. Prefer high, unobstructed targets when the horizon is
  cluttered.
- **PARK strands the pointing model.** `park` points the optics at the cradle; the firmware
  can't plate-solve from there, so every subsequent goto silently fails to slew. **Park
  ONLY at wind-down (Phase C).** To pause/resume mid-night use `stop_view`, never `park`.
  Recovering a mid-session park needs a full re-alignment — a power-cycle, after which the
  first goto runs a 3-point `Initialise` alignment (takes a few minutes).
- **Confirm framing with a real image, not telemetry.** Once per target (early), check the
  object is in frame, focused, and cloud-free — cheaply via the live plate-solve annotation
  (`Stack.Annotate` centre `pixelx/pixely` + `radius`), or the newest sub JPG from the
  scope's share. Frame counts don't prove the object is in frame. If it is off-centre,
  **classify the offset** with the procedure in **`run-session`** ("Visual framing check")
  before reacting — do not assume it is systematic, and do not burn a slot re-centring one
  that is.
- **High-latitude / short nights:** astronomical dark can be short, and at high latitude in
  summer there may be no true darkness before sunrise. Put **broadband** targets in the real
  dark and **LP/dual-band nebulae** into twilight — the dual-band tolerates the brightening
  sky far better. Expect the drop rate to climb sharply toward dawn; that's the natural end
  of the useful night, not a fault to chase.
- **Coordinates:** pass catalog **J2000 degrees** to `goto_target` — it converts RA to the
  firmware's hours internally. Don't pre-convert to hours (it double-converts).
- **Never run a file offload off the scope's share during the run.** Heavy transfers compete
  with the scope's control link and can starve it — the run may stall, or the bridge may fail
  to authenticate. Do offloads before the run or after wind-down only.
- **A target dropping EVERYTHING ≠ end of night.** Distinguish a *local* block (a low
  bearing over a roofline or tree line, or a drifting cloud bank) from a *global* one (dew /
  widespread cloud) by slewing to a high target in a different direction: if it stacks clean,
  skip the blocked spot and keep going; only wind down if the whole sky is bad. Don't abandon
  the night on one clouded patch.
- **Pacing a long slot.** A 45-minute slot does not need minute-by-minute polling, and tight
  loops cost more than they reveal. Sample the stack counters on a slow cadence (~1 min) and
  watch for four exit conditions: the **slot boundary**, a **drop-spike** (dropped frames
  climbing sharply across consecutive samples — a cloud bank or something drifting into the
  light path), a **link fault** (repeated solve failures or connection errors), or a
  **guardrail stop**. Any of those warrants a decision; between them, stay quiet.
- **Guardrails inside the slot, not just between targets.** Re-run
  `check_night_guardrails` on a slow cadence (~10 min) *during* a slot as well as at each
  boundary. Weather, dew, and battery do not wait for a slot to end, and a 45-minute slot
  checked only at its edges is 45 minutes unguarded — which defeats the lead time the
  predictive stops exist to give you.
