El horizonte que perdió el oeste
shade-engine responde a “¿está este sitio a la sombra ahora mismo?” comparando la posición del sol con un ráster precalculado: para cada metro cuadrado de la ciudad, el ángulo de elevación que tapa el cielo en cada una de 64 direcciones de brújula. Un build offline, consultas de milisegundos. El caso de estudio cuenta el camino feliz; esta nota va de la semana en que el artefacto mintió.
Mal en una sola dirección
El síntoma tenía forma. Las mañanas parecían correctas: sombras largas apuntando al oeste, las calles sombreadas en el lado correcto del cañón. Las tardes eran absurdas: un día de junio a las ocho de la tarde, el sol a 18 grados sobre el horizonte oeste, y el overlay de sombra casi vacío - una ciudad entera supuestamente al sol en plena hora dorada.
Nada se rompió. Ni excepción, ni 500, ni línea de log. Ese es el modo de fallo de los artefactos precalculados: la API lee un número de un ráster, lo compara con la elevación del sol y devuelve un booleano con total confianza. Simplemente el número estaba mal - y solo algunos números.
Veinte bandas a cero
El horizonte vive en un ráster de 64 bandas, una por sector de brújula de 5,6 grados, con los ángulos cuantizados a 8 bits. Un histograma por banda tarda segundos y cantó: las bandas 45 a 64 - todos los sectores desde el oeste hasta el nornoroeste - estaban idénticamente a cero. Hacia el oeste, que el motor supiera, ningún edificio de la ciudad era lo bastante alto para tapar nada. El sol se pone por el oeste; cada consulta de tarde comparaba su elevación contra cero y ganaba.
Por eso el síntoma se agrupaba tan limpio. El invariante detrás de todo el motor cabe en una frase - un píxel está a la sombra exactamente cuando el sol queda por debajo del horizonte de ese píxel hacia su azimut - así que cuando desaparece una rebanada del horizonte, las respuestas erróneas calcan los azimuts que faltan y nada más se degrada. Una geometría imposible es fácil de ver cuando sabes hacia dónde mirar: un cañón de calle norte-sur con el sol bajo al oeste no puede estar entero al sol.
Reconstruir sin drama
El arreglo no fue un parche; un artefacto corrupto solo tiene una cura. El barrido de horizonte se relanzó de noche - doce horas y cuarenta minutos de marcha de rayos sobre 56 millones de píxeles por 64 sectores en un VPS pequeño - hacia un directorio de staging, mientras producción seguía sirviendo la versión rota-pero-estable. El build se verifica a sí mismo al terminar, y una pasada independiente de verificación revisó los artefactos antes de mover nada.
El swap fueron dos renombres con la API parada: unos quince segundos de corte para un rebuild de trece horas, con el artefacto viejo guardado al lado como rollback instantáneo. Después se regeneraron los 83 overlays de tiles contra el horizonte sano y las comprobaciones en vivo por fin coincidieron con el cielo: las ocho de la tarde, sol en azimut 286, y las calles de la ciudad de la demo en sombra donde la geometría dice que deben estarlo.
Lo que queda
- Los artefactos fallan en silencio. El código lanza excepciones; un ráster solo devuelve valores. La verificación tiene que vivir pegada al build y pegada al swap - no en el momento en que alguien entorna los ojos mirando una puesta de sol.
- Escribe el invariante. “En sombra si y solo si el sol queda bajo el horizonte de su sector” es barato de enunciar y barato de comprobar, y sus violaciones se agrupan de formas que señalan la causa (solo oeste, solo tardes).
- Staging y luego swap. Un rebuild que escribe en el directorio servido convierte horas de build en horas de inconsistencia. Staging más un renombre atómico lo convirtieron en quince segundos.
- No te fíes dos veces. El modo exacto del barrido está validado bit a bit contra un oráculo de fuerza bruta en la suite de tests, y cada build termina en verificación. Los ceros originales nunca se reprodujeron; las barandillas que volverían a cazarlos son permanentes.