Guía avanzada
Para cuando quieres el porqué: la física, los formatos, los algoritmos y las decisiones que hay detrás, por escrito.
Cómo está construido
- NightScribe — Arquitectura
- NightScribe — Diseño de producto
- NightScribe — Flujos guiados (UX v3)
- NightScribe: desarrollar
- NightScribe — Fuentes de datos
- NightScribe — Botones y exportaciones del proyecto
- NightScribe — Guía de estilo visual (viz/)
Cómo se hace la ciencia
- NightScribe — Motor de sugerencias y scoring
- NightScribe — Explorador orbital («Explora»)
- Campañas de observación en NightScribe
- Estrellas HADS en NightScribe — guía de campo y referencias
- Fotometría en NightScribe: cómo se mide la luz
- SEQUENCES · Fotometría de secuencias
- Astrometría de cuerpos menores: guía de uso
- Fotometría de precisión en NightScribe: cómo se consigue
- Cómo NightScribe sube el SNR del objeto débil
- Editor FITS unificado (UFE)
- Unified FITS Editor (UFE)
- Envío al MPC: cómo NightScribe manda un reporte de astrometría
Decisiones de diseño (ADR)
Por qué el programa es como es, decisión a decisión: qué se eligió, qué se descartó y cuánto costó. Viven en el repositorio, donde se leen tal cual.
- 000 · Usar ADRs / Use ADRs
- 001 · PySide6 (Qt6) para la GUI / for the GUI
- 002 · SQLite para caché y estado / for cache and state
- 003 · NEOfixer fuente primaria de NEOs / as primary NEO source
- 004 · Sin astropy/astroquery / No astropy dependency
- 005 · `.ui` de Designer en runtime / Designer files at runtime
- 006 · Motor de sugerencias por reglas / Rule-based suggestion engine
- 007 · Estilo saas, voz humana / saas style, human voice
- 008 · Visualizaciones propias, copyright / Own visualizations
- 009 · Efemérides Schlyter / Schlyter ephemerides
- 010 · matplotlib motor único / single render engine
- 011 · Cometas: COBS + SBDB / Comets: COBS + SBDB
- 012 · PCCP vía scraping MPC / PCCP via MPC scraping
- 013 · Documentación bilingüe / Bilingual documentation
- 014 · i18n con Qt Linguist / UI i18n with Qt Linguist
- 015 · Exoplanetas: ExoClock + Archive / Exoplanets: ExoClock + Archive
- 016 · Imágenes de SN propias / Own SN images
- 017 · UX v2: menú, «ahora», columnas dinámicas / menu, "now", dynamic columns
- 018 · Blink SN: FITS/WCS propio + cutouts casados / own FITS/WCS + matched cutouts
- 019 · UX v3: flujo centrado en proyectos / project-centric workflow
- 020 · Horizonte local y restricciones / local horizon & observing constraints
- 021 · Exportar secuencias y efemérides / capture & ephemeris exports
- 022 · Reporte MPC: pegar y validar / MPC report: paste & validate
- 023 · Órbitas preliminares NEOCP vía NEOfixer /orbit/ / NEOCP preliminary orbits via NEOfixer
- 024 · Post autocontenido: el markdown referencia gráficos y blink / self-contained post: markdown references charts & blink
- 025 · Magnitud límite híbrida: duro donde se mide, aviso donde se predice / hybrid limiting mag: hard where measured, warned where predicted
- 026 · Tema oscuro global: una hoja de estilos, una paleta / dark global theme: one stylesheet, one palette
- 027 · Ficha con contexto del planner: la historia no depende de SIMBAD/SBDB / panel context fallback: the story does not depend on SIMBAD/SBDB
- 028 · Configuración en cuatro pestañas / settings as four tabs
- 029 · Cartas vectoriales en la GUI (QGraphics, sin matplotlib) / vector chart widgets (QGraphics, no matplotlib)
- 030 · Integración en vivo con CCDciel (JSON-RPC) / CCDciel live integration (JSON-RPC)
- 031 · Ficha de objeto unificada: coords copiables, tabla multilínea y por tipo, «Discovered» en NEOs, filtro de apertura / unified object card: copyable coords, per-type multi-line table, NEO "Discovered", aperture gate
- 032 · Carpeta contenedora de proyectos / project container folder
- 033 · Versionado automático con setuptools-scm / automatic versioning with setuptools-scm
- 034 · Estrellas HADS: catálogo empaquetado, observación continua sin fase / HADS stars: bundled catalogue, phase-free continuous observation
- 035 · Variables de largo periodo y campañas de observación (kind `variable`, campaña 1:N, VSX/SIMBAD, HJD) / long-period variables and observing campaigns (`variable` kind, 1:N campaign, VSX/SIMBAD, HJD)
- 036 · Historial → Diario de observación (menú, vista derivada) y Solar → «Sol y cielo» (divulgación) / History → observing journal (menu, derived view) & Solar → "Sun & sky" (outreach)
- 037 · Campañas = consola de señales; eventos de variables en tres pisos / Campaigns = signals console; variable-star events in three tiers
- 038 · Prominencia de acciones, lenguaje llano y el dashboard «Necesita tu atención» / action prominence, plain language and the attention dashboard
- 039 · *(reservado: acordeón de la web, stream paralelo / reserved: web accordion, parallel stream)*
- 040 · El sistema solar como fuente de eventos: «Calendario del cielo» y lunas de Júpiter / the solar system as an event source: "Sky calendar" & Jupiter's moons
- 041 · La página del proyecto como barra de pestañas perezosas (5 pestañas planas, una visible a la vez, deep-links y tarjeta «Siguiente» intactos) / the project page as a lazy tab bar (5 flat tabs, one visible at a time, deep links and the "Next" card intact)
- 042 · Secuencias fotométricas: VizieR (Gaia/APASS/VSX), transformaciones y propuesta automática de comparaciones / photometric sequences: VizieR (Gaia/APASS/VSX), transformations and automatic comparison proposal
- 043 · La pestaña Observatory se pliega en el paso Captura; pasos renombrados (Captura/Seguimiento/Follow-up, Worklog/Bitácora) y plan autoguardado / the Observatory tab folds into the Capture step; steps renamed (Capture/Track/Follow-up, Worklog/Bitácora) and silent plan auto-save
- 044 · Editor FITS unificado (UFE): una ventana, una pestaña por funcionalidad, escena en píxeles de placa / unified FITS editor (UFE): one window, one tab per feature, scene in plate pixels
- 045 · El flujo del proyecto se reconstruye sobre visitas (Ficha → Captura → Análisis → Publicación) / the project flow rebuilds around visits (Card → Capture → Analysis → Publication)
- 046 · Anotación de cartas: cajas de metadatos en las esquinas y estilo de marcador configurable (anillo/cruz) / chart annotation: metadata corner boxes and configurable marker style (ring/cross)
- 047 · El estado de la placa vive en la placa: mediciones ligadas a su origen y recuperación completa / plate state lives on the plate: points tied to their source plate, full recovery
- 048 · Serie medida frame a frame: ZP por frame, ensemble con veto MAD, puertas que marcan y nunca borran, deshacer por ejecución / series measured frame by frame: per-frame ZP, weighted ensemble with MAD veto, gates that flag and never delete, undo per run
- 049 · ExoClock: export manual HOPS + `ExoClock_info.txt`, sin credenciales ni scraping / ExoClock: manual HOPS export + `ExoClock_info.txt`, no credentials, no scraping
- 050 · En vivo y agrupación de tomas cortas en el dominio de la medida (nunca apilado de píxeles) / live mode and short-exposure grouping in the measurement domain (never pixel stacking)
- 051 · Solver local ASTAP tras un dispatcher, WCS en memoria, `-update` solo explícito / local ASTAP solver behind a dispatcher, in-memory WCS, `-update` opt-in only
- 052 · Orquestación de EXOTIC: ejecución externa headless e importación de su curva y parámetros (la vía numpy queda de previsualización) / EXOTIC orchestration: external headless run and import of its curve and parameters (the numpy path stays as a preview)
- 053 · Rediseño del menú Ayuda: guía curada, fuentes, novedades y Acerca de / Help menu redesign: curated guide, sources, what's new and About
- 054 · Búsqueda de período (Lomb-Scargle + PDM), plegado e informe de fase, con lo que la línea base no puede decir / period search (Lomb-Scargle + PDM), folding and the phase report, with what the baseline cannot say
- 055 · Interfaz 1.0: el arranque es el hub de proyectos, shell con vistas apiladas y pestaña vertical, Bienvenida = asistente en línea, Tonight bajo demanda, UFE embebido / Interfaz 1.0: startup is the project hub, stacked-view shell with a vertical tab, Welcome = inline wizard, Tonight on demand, embedded UFE
- 056 · Navegación con historial, migas y atajos (atrás/adelante, pestaña en la ubicación, Bienvenida permanente) / navigation with history, breadcrumbs and shortcuts (back/forward, tab in the location, permanent Welcome)
- 057 · La ficha como dossier: hero con anillo de score, tira KPI «esta noche», secciones temáticas de parámetros y datos nuevos por tipo / the object card as a dossier: hero with score ring, "tonight" KPI strip, themed parameter sections and new per-kind data
- 058 · Ningún código, clasificación o cifra sin explicación: `core/explain.py` con dos densidades (short/long) / no code, classification or figure without an explanation: `core/explain.py` with two densities (short/long)
- 059 · La captura como consola de misión: tira resumen KPI, tarjetas `PanelCard` y el panel CCDciel como tarjeta / Capture as a mission console: KPI summary strip, `PanelCard` cards and the CCDciel panel as a card
- 060 · Política de dependencias: «numpy primero, biblioteca estándar cuando aporta» (reabre ADR-004; autoriza astropy/scipy/photutils) / dependency policy: "numpy first, standard library when it earns its place" (reopens ADR-004; authorises astropy/scipy/photutils)
- 061 · Calibración de imágenes: biblioteca de masters indexada, receta declarativa, flat por filtro / image calibration: indexed master library, declarative recipe, per-filter flat
- 062 · Astrometría por track & stack: medida doble, multiobservación, chequeo delegado en Find_Orb y reporte ADES/80col (reabre ADR-022) / track & stack astrometry: double measurement, multi-observation, Find_Orb-delegated check and ADES/80col report (reopens ADR-022)
- 063 · Ningún denoiser de IA en el camino de la medida (rompe el modelo de error, puede mover el centroide, y el suelo del MPC existe por esto); si algún día entra, es solo para presentación y lo juzga la inyección-recuperación / no AI denoiser in the measurement path (it breaks the error model, can move the centroid, and the MPC's floor exists for this); if it ever enters, it is for presentation only and injection-recovery judges it
- 064 · Cascada de efemérides para el apilado: Horizons con reintentos, órbita local de SBDB/NEOfixer y MPCORB descartado (reabre ADR-062) / ephemeris cascade for stacking: Horizons with retries, local SBDB/NEOfixer orbit and MPCORB rejected (reopens ADR-062)
- 065 · Modo manual para un objeto por debajo de la puerta: el stack base no se tira, la marca humana sustituye a la detección y el run se reabre con sus pilas (reabre ADR-062) / manual mode for an object below the gate: the base stack is not thrown away, the human mark replaces the detection and the run reopens with its stacks (reopens ADR-062)
- 066 · Nunca una estrella saturada ni por encima de la linealidad de la cámara: los dos techos en un solo sitio, la medida los obedece y, si la linealidad no se conoce, se dice / never a saturated star or one above the camera's linearity: the two ceilings in one home, the measurement obeys them and, when the linearity is unknown, it is said
- 067 · El motor de fotometría se queda el nuestro: medido contra photutils (misma precisión en medida individual, +1,8 % de mediana en serie, 1,43× de velocidad) y por 250 a 450 líneas netas, que son medio punto de la app y cero en la serie (cierra ADR-060 §3 para `photometry` y `series_measure`) / the photometry engine stays ours: measured against photutils (same precision on single measurements, +1.8 % median on series, 1.43× speed) and for 250 to 450 net lines, half a point of the app and zero in the series (closes ADR-060 §3 for `photometry` and `series_measure`)
- 068 · La combinación decide la profundidad y la interpolación el aspecto: la media recortada sigma por defecto (la mediana cuesta 0,26 mag), el stack dice cómo se hizo y la regla de descartar fotogramas no verificados se mantiene (cuesta 0,11 mag apilarlos) / the combination decides depth and the interpolation the look: the sigma-clipped mean by default (the median costs 0.26 mag), the stack says how it was made and the rule of leaving unverified frames out stays (stacking them costs 0.11 mag)
- 069 · El flat sintético se enmascara antes del estadístico y el pedestal se quita: sin estrellas dentro, con el polvo, también en campo estático (máximo 1,1136 contra el 1,1102 del flat real), el criterio pasa a la desviación sobre lo enmascarado (2,05 % contra 33,83 %), y el flat se escribe y se mira / the synthetic flat is masked before the statistic and the pedestal is removed: no star inside, with the dust, on a static field too (maximum 1.1136 against the real flat's 1.1102), the check becomes the deviation over the masked pixels (2.05 % against 33.83 %), and the flat is written and looked at
- 070 · La guía de usuario y la web: una fuente, dos renderizados (el markdown que ya lee la app genera la web, la paleta y los chips salen del tema y del catálogo, y un test la vigila) / the user guide and the website: one source, two renderings (the markdown the app already reads generates the site, the palette and the chips come from the theme and the catalogue, and a test watches it)
- 071 · Ajustes por tarea: raíl de seis categorías, buscador y secciones avanzadas, la cámara unificada y una sola tabla de campos (revisa ADR-028) / settings by task: a six-category rail, a search and advanced sections, the unified camera and one field table (revises ADR-028)
- 072 · La ganancia se mide; la cabecera no manda: Ajustes → la medida en los frames → la recordada → la cabecera, `EGAIN`/`CCDGAIN` antes que `GAIN`, y una nota cuando discrepan; la recordada (por cámara y ajuste) resuelve el caso de una sola imagen, con la acción «Medir ganancia…» (Ajustes → Cámara) / the gain is measured, the header does not rule: Settings → the measurement on the frames → the remembered one → the header, `EGAIN`/`CCDGAIN` before `GAIN`, and a note when they disagree; the remembered gain (per camera and setting) solves the single-image case, with the "Measure gain…" action (Settings → Camera)
- 073 · La resta de huésped iguala la PSF: gaussiano dirigido por los datos + kernel óptimo (Alard-Lupton) regularizado, el que menos residuo deja (medido: 0,465 → 0,353 en un frame real; en sintético el kernel recupera el modelo al nivel del ruido) / host subtraction matches the PSF: a data-driven Gaussian + a regularized optimal kernel (Alard-Lupton), whichever leaves the least residual (measured: 0.465 → 0.353 on a real frame; on synthetic data the kernel recovers the model to the noise level)
- 074 · El catálogo de cámaras y una linealidad que no miente: 21 sensores agrupados por familia, cada preset con bits/sensor/oscuridad+temperatura, la linealidad sugerida sale del menor entre el full well y el techo del ADC, y los datos viven en `assets/cameras.toml` con un fichero de usuario que añade/corrige/oculta cámaras (revisa ADR-071) / the camera catalogue and a linearity that does not lie: 21 sensors grouped by family, each preset with bits/sensor/dark+temperature, the suggested linearity comes from the lesser of the full well and the ADC ceiling, and the data lives in `assets/cameras.toml` with a user file that adds/corrects/hides cameras (revises ADR-071)
- 075 · La IA como asistente no invasivo: un endpoint compatible con OpenAI (nube gratis o local), el brief como única fuente, el redactor solo frasea y la plantilla es el respaldo; apagada por defecto, se invoca y nunca escribe ni toca la medida (el asistente conversacional hereda la carta) / AI as a non-invasive assistant: an OpenAI-compatible endpoint (free cloud or local), the brief as the single source, the writer only phrases and the template is the fallback; off by default, invoked and never writing nor touching the measurement (the conversational assistant inherits the charter)
- 076 · El seguimiento de la fotometría: el run dice por qué se cayeron comparsas (un error grande deja de parecer un fallo), la receta elige si la banda reporta la medida o la efeméride, el brillo se re-mide sobre los stacks guardados sin re-apilar, y una placa de stack ya no acumula dos marcas; el PA se mide (traza y barrido) y su realimentación queda propuesta / the photometry follow-up: the run says why comps were dropped (a large error stops looking like a bug), the recipe chooses whether the band reports the measurement or the ephemeris, the brightness is re-measured on the saved stacks without re-stacking, and a stack plate no longer collects two marks; the PA is measured (trail and sweep) and feeding it back is proposed
- 077 · Identidad NEOCP: la referencia se pide por el nombre confirmado (confirmado → NEOCP → alias de la lista rodante del MPC), los estados del chequeo se dicen sin fingir (sin binario / sin referencia / no ajustó), el listado completo de residuos se pinta con nuestras filas resaltadas, y un objeto confirmado desde la visita se avisa muy visible en el chequeo, la ficha y la visita, con la circular donde están tus datos / NEOCP identity: the reference is fetched under the confirmed name (confirmed → NEOCP → alias from the MPC's rolling list), the check's states are stated without faking (no binary / no reference / could not fit), the whole residual listing is painted with our rows highlighted, and an object confirmed since the visit is announced very visibly in the check, the card and the visit, with the circular where your data is
- 078 · El listón del MPC recomienda, no filtra: el reporte incluye todas las observaciones y avisa de las que están por debajo, y el observador decide con un checkbox que deja una observación fuera del reporte y del chequeo (marcada, nunca borrada, reversible) / the MPC floor advises, it does not filter: the report includes every observation and warns about the ones below it, and the observer decides with a checkbox that leaves an observation out of the report and the check (flagged, never deleted, reversible)
- 079 · Los catálogos de traducción se cargan desde memoria (sin mmap) y se recompilan atómicamente: `QTranslator` mapea el `.qm` y reescribirlo con la app abierta la tumbaba en la siguiente traducción (core dump real, 2026-10-10); el buffer se mantiene vivo y `tools/i18n.py` renombra el `.qm` sobre el destino / the translation catalogues are loaded from memory (no mmap) and rebuilt atomically: `QTranslator` maps the `.qm` and rewriting it with the app open crashed it on the next translation (a real core dump, 2026-10-10); the buffer is kept alive and `tools/i18n.py` renames the `.qm` over the target
- 080 · El reporte MPC es una sumisión completa (cabecera COD/CON/OBS/MEA/TEL/NET/NUM/ACK/AC2 y la designación en su columna; obsContext en ADES) y vive en su propia ventana con tres formatos (XML, PSV, 80 col); el envío nunca es mudo: un diálogo pide email/acuse/objetivo/modo y hace POST al MPC (con modo Test), bloqueado sin email de contacto / the MPC report is a full submission (COD/CON/OBS/MEA/TEL/NET/NUM/ACK/AC2 header and the designation in its column; obsContext for ADES) and lives in its own window with three formats (XML, PSV, 80-col); sending is never silent: a dialog asks for email/ack/target/mode and POSTs to the MPC (with a Test mode), blocked without a contact email
- 081 · Backends de observatorio: CCDciel **o** NINA/PINS (Advanced API v2), elegidos en Ajustes, con superficie común, **Stop goto** de seguridad en su propio worker, filtros del plan tomados de la rueda real y «Send plan» que lee de vuelta lo preparado; NINA no admite contador de frames en su snapshot y no tiene staging persistente / observatory backends: CCDciel **or** NINA/PINS (Advanced API v2), picked in Settings, with a common surface, a safety **Stop goto** on its own worker, the plan's filters taken from the real wheel and "Send plan" reading back what was staged; NINA has no frame count in its snapshot and no persistent staging
- 082 · La última toma en la ventana: el paso Captura vigila la carpeta de captura (ajuste nuevo), muestra una miniatura de la toma más reciente y estable (nunca un fichero que crece, nunca una toma anterior a este run) y la abre en el editor con un clic; solo se ven los campos del backend elegido, y el buscador tampoco revela el otro / the last frame in the window: the Capture step watches the capture folder (a new setting), shows a thumbnail of the newest stable frame (never a growing file, never a frame older than this run) and opens it in the editor with one click; only the chosen backend's fields are shown, and the search does not reveal the other one either
- 083 · La receta de captura: dithering y autofocus decididos por el tipo de objeto y la física (amplitud 5–15 px, cada 1–3 tomas; ΔT ≈ 1 °C, 30–60 min; sin dither en objetos que ya se mueven y sin huecos en cadencia rápida), ganancia/offset/binning por plan, escrito en la secuencia de CCDciel/NINA y aplicado en vivo lo que la API permite; la cadencia de AF y el dither por tomas van en la secuencia / the capture recipe: dithering and autofocus decided by the object type and the physics (amplitude 5–15 px, every 1–3 frames; ΔT ≈ 1 °C, 30–60 min; no dither on objects that already move and no gaps in fast cadence), per-plan gain/offset/binning, written into the CCDciel/NINA sequence and applied live where the API allows; the AF cadence and per-frame dither live in the sequence
- 084 · Start and follow: un run dirigido de N observaciones de un objeto que se mueve, cada una llenada hasta el cruce del campo, re-apuntando sobre la efemérides actualizada (el «recenter» de CCDciel, el «Center After Drift» de NINA, pero sobre un objeto que se mueve), con el coste del re-apuntado y la ventana en el presupuesto y Stop en el propio botón; bucle abierto por tiempo, y en NINA cada observación es 1 toma salvo secuencia nativa / start and follow: a driven run of N observations of a moving object, each filled up to the field crossing, re-pointing onto the updated ephemeris (CCDciel's "recenter", NINA's "Center After Drift", but onto a moving object), with the re-point cost and the window in the budget and Stop on the button itself; time-based open loop, and on NINA each observation is 1 frame unless the native sequence is loaded
- 085 · Re-selección manual del centroide: un menú contextual (tabla e imagen) re-mide SOLO el stack point desde una marca humana que se ajusta al centroide gaussiano, sobre el stack ya guardado (sin re-apilar), firma el punto y repite el chequeo; se avisa de que arregla el centroide, no un registro / manual centroid re-selection: a context menu (table and image) re-measures ONLY the stack point from a human mark that snaps to the gaussian centroid, on the saved stack (no re-stacking), signs the point and re-runs the check; it says it fixes the centroid, not a registration
- 086 · Reporte combinado: a nivel de proyecto, lista las mediciones de todos los runs con un checkbox por observación, corre UN chequeo contra las publicadas y construye UN reporte MPC; la selección y el veredicto se guardan en `astrometry_combined` (no en el contexto) / combined report: at the project level, lists the measurements of every run with a checkbox per observation, runs ONE check against the published ones and builds ONE MPC report; the selection and the verdict are saved in `astrometry_combined` (not in the context)
- 087 · Empaquetado de escritorio: Find_Orb viaja **dentro** (programa separado, agregación legal para una app no comercial), un `HOME` privado pre-poblado hace funcionar la copia relocalizada, y tres paquetes Linux con el **Python del sistema** (`.deb` Debian 13+/Ubuntu 25.10+, `.rpm` Fedora/openSUSE, PKGBUILD Arch); `imageio-ffmpeg` es opcional (solo el MP4) y la efeméride DE430t se descarga desde Ajustes / desktop packaging: Find_Orb travels **inside** (a separate program, legal aggregation for a non-commercial app), a pre-populated private `HOME` makes the relocated copy work, and three Linux packages with the **system Python** (`.deb` Debian 13+/Ubuntu 25.10+, `.rpm` Fedora/openSUSE, PKGBUILD Arch); `imageio-ffmpeg` is optional (MP4 only) and the DE430t ephemeris is downloaded from Settings
- 088 · El tipo orbital de un objeto confirmado se lee del tipo **oficial del MPC** (nueva fuente `mpc_orbits.get_orb` contra la API de órbitas `get-orb`), sólo para las familias NEO que el MPC nombra como nosotros (Atira/Aten/Apollo/Amor), con la regla local como respaldo, y se dice de quién es el dato; resuelve la discrepancia Apollo/Amor sin elegir por el observador / a confirmed object's orbital family comes from the MPC's **official type** (new `mpc_orbits.get_orb` source against the `get-orb` Orbits API), only for the NEO families the MPC names as we do (Atira/Aten/Apollo/Amor), with the local rule as fallback, and it says whose value it is; it settles the Apollo/Amor discrepancy without choosing for the observer
- 089 · Aceleración del apilado: el registro recorre la secuencia en bloques (la estimación de cada frame es independiente; la puerta y el fallback siguen en serie) y el warp y el combine ganan una ruta GPU **opcional**, apagada por defecto, sin dependencia dura y con fallback CPU, con la paridad fijada por un test (x7,1 el registro, x4,25 el run completo de 207 tomas reales en una RTX 3090) / stacking acceleration: the registration walks the sequence in chunks (each frame's estimate is independent; the gate and the fallback stay serial) and the warp and the combine gain an **optional** GPU path, off by default, no hard dependency and a CPU fallback, with the parity pinned by a test (x7.1 the registration, x4.25 the whole run of 207 real frames on an RTX 3090)
- 090 · El «flat sintético» del ADR-069 se retira y lo sustituye un **modelo de viñeteo muestreado del cielo**: una rejilla de teselas sobre veinte tomas repartidas, la mediana por tesela, un ajuste robusto de grado 6 que tira las teselas que una estrella o una mota estropearon, y nada más (sin máscara, sin relleno, sin píxeles calientes). Medido contra el master real: la misma banda del 4–5,5 % que el flat enmascarado (4,16 %) y **2,4 s contra 20**; lo único que se pierde es el polvo, **0,026 mag**. Se llama modelo y no flat porque no lleva polvo ni lo pretende / ADR-069's "synthetic flat" is retired and replaced by a **vignetting model sampled from the sky**: a grid of tiles over twenty spread frames, the median per tile, a robust degree-6 fit that drops the tiles a star or a mote spoiled, and nothing else (no mask, no fill, no hot pixels). Measured against the real master: the same 4–5.5 % band as the masked flat (4.16 %) and **2.4 s against 20**; the only thing lost is the dust, **0.026 mag**. It is called a model and not a flat because it carries no dust and does not pretend to
- 091 · Backend **OpenCL «de serie»**: `core/gpu.py` pasa a fachada, el código CuPy se mueve a `core/gpu_cuda.py` y nace `core/gpu_opencl.py`; un segundo ajuste (`astrometry_gpu_backend` = auto\
- 092 · Ajustes amables: lo común primero y lo avanzado tras UNA puerta por página que el buscador abre solo, el raíl con icono y nombre, Observatory dividido en sitio y firma de reportes, y el guiado del follow en Sesión; no se quita nada (revisa ADR-071) / friendly settings: the common first and the advanced behind ONE door per page that the search opens by itself, the rail with icon and name, Observatory split into site and report signing, and the follow guider under Session; nothing is removed (revises ADR-071)
- 093 · Ramas de mantenimiento: una por línea de versión (`release/vX.Y.x`), el parche se hace **en la rama** porque es la que se etiqueta y se construye, el tag se corta desde ahí y **todo lo que entra se adelanta a `main` el mismo día** (el `CHANGELOG` es el único que choca, así que se hace pronto); la dirección es la del `hotfix` clásico, porque la línea publicada tiene que ser construible de una pieza / maintenance branches: one per version line (`release/vX.Y.x`), the patch is made **on the branch** because that is what gets tagged and built, the tag is cut from there and **everything that lands is forwarded to `main` the same day** (the `CHANGELOG` is the only one that conflicts, so it is done early); the direction is the classic hotfix, because the published line has to be buildable in one piece