Dos estadísticas, dos definiciones de "capturado"
El dashboard de Greenhouse muestra dos estadísticas relacionadas: una insignia de “Captured today” y una racha de días consecutivos. Se supone que coinciden, y en general coincidían, hasta que se capturaba una idea y se la promovía a proyecto activo el mismo día. Ahí “Captured today” se apagaba en silencio, aunque se hubiera capturado algo esa misma mañana.
El bug era dos funciones calculando el mismo concepto de dos formas distintas. La función de racha tomaba el created_at de cada elemento sin importar en qué estado estuviera ahora, a propósito, con un comentario en el código explicando que un día de captura no debía desaparecer solo porque la idea se promoviera después. Pero el conteo de “captured today” filtraba solo por status = 'fresh_idea'. En el momento en que se promovía una idea, dejaba de ser una idea fresca, y dejaba de contar, aunque la racha siguiera acreditando el día correctamente. Los mismos datos de base, dos lentes distintos, dos respuestas distintas.
Mientras rastreaba esto encontré una segunda pregunta relacionada, sin resolver, en el mismo rincón del código: importar una carpeta existente desde el disco marca created_at en el momento de la importación y también alimentaba la racha en silencio. Al importar una carpeta llena de ideas de canciones de hace seis meses, Greenhouse acreditaba haber capturado algo hoy. Pero el PRD entiende algo más estrecho por “capturado,” y es explícito: “Captured an idea today?” se refiere al flujo de Capture, el acto creativo real de sentarse con una idea nueva. Adoptar una carpeta que alguien ya creó no es eso.
Arreglé ambas cosas a la vez: agregué una columna que marca las filas como importadas o genuinamente capturadas, y apunté las dos estadísticas a la misma consulta subyacente, filtrada para excluir importaciones. Las filas existentes se rellenan por defecto como “no importadas,” lo que significa que las importaciones viejas de antes de este arreglo siguen contando retroactivamente. Decidí que estaba bien así. Es un empujón motivacional, no un libro contable, y el PRD es explícito en que las rachas son estadísticas, nunca candados.
Todo el arreglo vivía en la capa de base de datos, sin interfaz que recorrer, así que la verificación fueron cuatro pruebas nuevas que ejercitaban directamente las funciones reales de producción en vez de un render simulado. A veces la cantidad correcta de ceremonia para un bug es una migración y unas pocas pruebas unitarias, no un navegador.
Lecturas relacionadas
La base de datos vacía que parecía perfectamente sana
SQLite trata un archivo de cero bytes como una base de datos nueva y válida, así que todas las verificaciones de corrupción daban bien, y la poda de respaldos habría borrado las copias buenas en una semana.
El guardián que nunca estuvo
Agregar una verificación de estado a una función de Rust hizo que ya no pudiera llamar a otra función de la misma manera, y esa restricción abrió en silencio un hueco que ninguna de las dos funciones tenía por sí sola.
Una prueba que no demostró nada, y el bug que se suponía que iba a atrapar
Un lote de limpieza con cinco hallazgos donde el ítem más chico fue el que más importó: una prueba de regresión que falló porque el bug al que apuntaba es estructuralmente inalcanzable.