reconciliar_snapshot estampa current_date en fecha_ultima_actualizacion de los 2413 vehiculos en cada import - es el escritor dominante de MOVILBA-145 y no tenia ticket
Medido 2026-09-23 por db03-mba-l-cc, SELECT only, las dos bases. EL HALLAZGO: rutap.reconciliar_snapshot(integer,boolean), linea 484 del cuerpo instalado, dice literal 'fecha_ultima_actualizacion = current_date', BYTE-IDENTICO EN DEV Y PROD. Corre sobre todos los vehiculos existentes en cada import. Censo de escritores de la columna en las dos bases: solo fn_archive_vehiculos() (copia al archivo, no decide valor) y esta. La app NUNCA la escribe - solo la EXPONE en la descarga de Parque Movil (src/repositories/parque-movil.ts:205) y en el contrato de la API externa (src/lib/openapi/components.ts:626 y 646, y esta en la lista de 'required'). POR QUE IMPORTA: MOVILBA-145 borro el DEFAULT CURRENT_DATE en DEV y dejo este escritor intacto en los dos lados. El default solo actuaba en el INSERT de un vehiculo nuevo; este corre sobre todo el parque. O sea DEV NO es el estado bueno al que PROD converge en este campo: los dos estan mal de la misma forma, en el camino que toca todo. EVIDENCIA VIVA EN PROD: la distribucion de la columna son 8 fechas distintas para 2413 vehiculos vivos - 944 en 2025-12-08, 907 en 2026-09-18, 259 en 2026-05-14, 132 en 2026-08-14, 125 en 2026-08-05, 25 en 2026-06-29, 4 en 2025-12-16, 3 en 2026-04-30. 944 vehiculos compartiendo fecha al dia es una corrida de import, no 944 modificaciones ese dia. HAY VALOR DE REEMPLAZO PARA TODAS LAS FILAS, medido: tomando max(fecha_alta) sobre los periodos de cada vehiculo en rutap.vehiculo_historial_normalizado, los 2413 vivos de PROD resuelven a una fecha del libro y CERO quedarian NULL. Solo 6 de 2413 ya coinciden con ese valor. O sea el argumento 'sacar el estampado deja la columna NULL' es FALSO para PROD. Y EL SIGNIFICADO YA ESTA DECIDIDO EN EL REPO, no hace falta volver a preguntarlo: el comentario de MOVILBA-98 en src/app/vehiculos/[dominio]/page.tsx:1219-1226 (y el gemelo en editar/colectivo/page.tsx:891) dice literal 'La fecha real de la ultima actualizacion es la del tramite mas reciente, que ya se ve en el historial', y por eso 98 SACO el rotulo de la ficha. Ese comentario tambien dice 'la aplicacion no la escribe en ningun lado - la grepee entera', que sigue siendo cierto, y 'ningun trigger la toca', que tambien - pero reconciliar_snapshot es una FUNCION llamada por el import, no un trigger, y quedo fuera de esa busqueda. Esa es la razon por la que este escritor sobrevivio a 98 y a 145. LO QUE HAY QUE HACER ANTES DE TOCAR NADA (no necesita aprobacion, no escribe): correr la derivacion candidata como COMPARACION de solo lectura y reportar por vehiculo el valor actual, el candidato y el delta en dias, mas (i) cuantas filas cambian, (ii) la distribucion de los deltas, (iii) si el candidato alguna vez mueve la fecha HACIA ADELANTE del valor actual, y (iv) cuantos vehiculos tienen mas de un periodo, que son los unicos donde 'el mas reciente' es una eleccion real. Ese reporte es el insumo de MOVILBA-146. LO QUE NO SE DECIDE ACA: si el arreglo es escribir la fecha del tramite en la columna, o sacarle el camino de escritura y dejar que el historial sea la unica fuente - que es lo que 98 ya eligio para la UI. Las dos son defendibles, asi que es decision de Elazar, no carve-out de error obvio. HUECOS, nombrados: no hay registro de por que la pata PROD de la migracion de 145 nunca corrio. Y la afirmacion 'la app no la escribe' cubre este repo y las dos bases, no a los consumidores de la API externa, que no puedo enumerar desde aca.
Activity
-
PRECEDENTE EN EL REPO, verificado por coder03 leyendo el archivo: MOVILBA-98 ya decidio que significa la columna y ya la saco de la pantalla. src/app/vehiculos/[dominio]/page.tsx:1219-1226, literal: 'El rotulo prometia la ultima vez que se actualizo el vehiculo y la columna no guarda eso ... Es la fecha en que una importacion creo la fila ... La fecha real de la ultima actualizacion es la del tramite mas reciente, que ya se ve en el historial.' El mismo comentario esta en editar/colectivo/page.tsx:891. Ademas 98 ya habia medido la misma forma con el parque de entonces: 16 fechas distintas para 1736 vehiculos con 1094 en un solo dia, y 1015 de prod (~59%) con un tramite POSTERIOR a esa fecha. POR QUE SOBREVIVIO EL ESTAMPADOR A 98 Y A 145: el mismo comentario afirma 'ningun trigger la toca en dev ni en prod' y 'la aplicacion no la escribe en ningun lado - la grepee entera'. Las dos afirmaciones siguen siendo CIERTAS. reconciliar_snapshot es una FUNCION que llama el import: ni trigger ni codigo de app, asi que quedo afuera de las DOS busquedas. La columna parecia no tener escritor para dos tickets consecutivos - es la forma de [[reference_a_key_too_narrow_to_see_the_distinguishing_field]] aplicada a un censo de escritores. CONSECUENCIA PARA 146: la pregunta abierta es mas angosta de lo que decia el blackboard. No es 'que significa la columna' - 98 lo contesto - sino ESCRIBIR la fecha del tramite en la columna, o SACARLE el camino de escritura y dejar que el historial sea la unica fuente, que es lo que 98 ya eligio para la UI. La segunda opcion la levanto audit-general-cc como especulacion; tiene precedente en el repo.
-
COMPARACION DE SOLO LECTURA CORRIDA, los cuatro puntos contestados. Artefacto m540_fua_compare.sql (md5 39a6d8e55b4bb3d1502a5bed4f75fc10), salidas m540_prod.txt y m540_dev.txt, los tres en /data-lezama/movilba/evidencia/MOVILBA-540/. No escribe nada: CREATE TEMP VIEW + SELECT. Fuente del libro = rutap.v_vehiculo_historial_normalizado_fuente_actual, la vista canonica que resuelve el snapshot vivo; en PROD se corre con superuser porque su ACL es NULL en las DOS bases A PROPOSITO (owner-chaining declarado en docs/migrations/2026-09-18-movilba-294-...:37-39) - eso NO es un defecto de permisos, se chequeo antes de reportarlo. (i) CUANTAS FILAS CAMBIAN - PROD: 2407 de 2413, solo 6 ya coinciden. DEV: 2422 de 2422, CERO coinciden, y ademas 9 vehiculos tienen la columna en NULL, que es el unico efecto observable del DEFAULT que 145 bajo en DEV (un INSERT nuevo sin valor ya no recibe fecha). (ii) DISTRIBUCION DE DELTAS en dias, PROD, candidato max(fecha_alta): minimo -456, maximo +280, promedio -77, mediana -34. Buckets: adelante 31-365 dias 910, atras 31-365 827, atras 366-1095 385, atras 1-30 146, adelante 1-30 139, exacto 6. DEV es mucho mas viejo: mediana -202, promedio -205, y solo 28 adelante. (iii) EL CANDIDATO MUEVE LA FECHA HACIA ADELANTE EN 1049 DE 2413 VEHICULOS DE PROD (43%). Es el hallazgo nuevo de esta corrida: el valor de hoy no es solo viejo, en 43% del parque esta del LADO EQUIVOCADO - hay un tramite del libro POSTERIOR a lo que la columna declara como ultima actualizacion. Concuerda en forma con lo que 98 ya habia medido (1015 de 1736, ~59%) con el parque de entonces. Ejemplo repetido: actual 2025-12-08, candidato 2026-03-17, +99 dias, en un lote de dominios AC316I*/AA5*. (iv) MULTI-PERIODO, PROD: 1768 de 2413 tienen mas de un periodo (645 tienen uno solo; 1387 tienen 2-3; 381 mas de 3). Esos 1768 son los unicos donde 'el mas reciente' es una eleccion real. LO QUE LA CORRIDA AGREGA Y NO ESTABA EN LA DESCRIPCION - la eleccion entre derivaciones IMPORTA, medida, no es academica: max(fecha_alta) y max(greatest(fecha_alta,fecha_baja)) dan valores DISTINTOS para 807 vehiculos en las dos bases (346 de ellos multi-periodo). Y la tercera derivacion, 'ultimo MODIF', queda DESCARTADA como candidato unico: deja 672 vehiculos de PROD en NULL (681 en DEV) porque no todo vehiculo tiene un tramite Modif. El censo de tramites de la fuente viva son exactamente dos valores, Modif 3023 y Alta 2505 (DEV suma 25 filas con tramite NULL, de origen app). COBERTURA: se midio la columna fecha_ultima_actualizacion de public.vehiculos vivos contra tres derivaciones del snapshot vivo, en las dos bases. NO se midio ningun consumidor de la API externa, NO se toco la funcion, y NO se recomienda cual derivacion escribir - eso sigue siendo la definicion que falta y es de Elazar o Eugenia.
-
CORRECCION DE MI PROPIA DESCRIPCION, y cambia el espacio de decision. El titulo y la descripcion de este ticket dicen que reconciliar_snapshot estampa 'todo vehiculo en cada import'. ES FALSO: cite el SET y no lei el WHERE. EL TEXTO REAL, lineas 473-493 del cuerpo instalado: el UPDATE lleva WHERE v.id = s.vehiculo_id AND (dominio, subtipo_id, chasis_id, numero_serie_chasis, numero_serie_motor, anio_modelo, chasis_modelo, asientos, motor_fabricante_id, motor_unidad_propulsora_id) IS DISTINCT FROM la tupla del libro. Es un UPDATE CON DETECCION DE CAMBIO: solo alcanza los vehiculos cuya fila proyectada cambio de verdad. El comentario del propio cuerpo (469-472) dice por que existe ese IS DISTINCT FROM - sin el, el trigger de archive escribiria una fila por vehiculo por corrida y el archive dejaria de medir reescrituras para medir corridas del import. MIS PROPIOS NUMEROS YA LO FALSIFICABAN Y NO LO VI: si estampara todo el parque en cada corrida, PROD tendria UNA fecha (la ultima corrida), no OCHO, y seria IMPOSIBLE que 1049 vehiculos tengan un candidato del libro POSTERIOR al sello. Las dos cosas que medi solo cierran con un WHERE selectivo. QUE SIGNIFICA LA COLUMNA HOY, corregido: no es residuo de lote. Es 'fecha de la ultima importacion que cambio la fila proyectada de este vehiculo', que es una lectura defendible de 'ultima actualizacion' a nivel sistema. Eso AGREGA UNA TERCERA OPCION a la decision: (A) escribir la fecha del tramite del libro, (B) sacarle el camino de escritura y que el historial sea la unica fuente - lo que 98 ya eligio para la UI -, (C) DEJARLA COMO ESTA y documentar/renombrar lo que significa, porque ya mide algo coherente. EL DEFECTO QUE QUEDA ES MAS ANGOSTO Y SIGUE SIENDO REAL - NO VE LAS EDICIONES DE LA APP. Medido en PROD ahora: 166 vehiculos vivos tienen edicion registrada en audit_log (table_name='vehiculos', 331 filas: 224+13 UPDATE, 72+22 CREATE), y en 99 de esos 166 la ultima edicion es POSTERIOR al valor de la columna. Sumado a los 1049 de 2413 con tramite del libro posterior al sello, la columna subdeclara por dos caminos distintos: el libro y la app. LO QUE NO CAMBIA de lo ya medido: los 2407 de 2413 que cambiarian, el delta mediano -34, los 807 vehiculos donde max(fecha_alta) y max(greatest(alta,baja)) se separan, los 672 que 'ultimo MODIF' dejaria en NULL, y los 9 NULL de DEV como unica huella del DEFAULT que bajo 145. El punto (iii) se reformula: 'el valor de hoy esta del lado equivocado en 43%' pasa a ser '43% tiene un tramite del libro posterior al sello de import', que es el mismo numero con la causa correcta. CONFIRMADO ADEMAS, lo habia afirmado sin medir: las 25 filas con tramite NULL de DEV son origen='app', 25 de 25.
-
CORRECCION DE CONTEO, mia: la descripcion dice 'OCHO fechas distintas para 2413 vehiculos' en PROD. MEDIDO 2026-09-23 11:00: son 18 fechas distintas, 0 NULL (count(distinct fecha_ultima_actualizacion) sobre vehiculos vivos). Los dos picos (944 en 2025-12-08, 907 en 2026-09-18) se mantienen. DEV al mismo momento: 5 fechas distintas, 2383 de 2423 en 2026-09-16, 10 NULL - un vehiculo mas y un NULL mas que mi corrida de las 04:00, alguien creo un vehiculo en DEV entre medio. Blackboard bb-coder03-mba-l-cc-mue67l2o-bzdlmg cerrado 3/3: db03 y audit-mba-l-ca eligen (B) sacar columna+escritor+campo de API; db04-mba-l-ca elige (C) conservar y documentar. Decision de Elazar.
-
SEGUNDO BLACKBOARD bb-coder03-mba-l-cc-mue6c190-uzp4yh (medicion propia obligatoria, sin contexto del opener) CERRADO 3/3: db03, db04-mba-l-ca y audit-mba-l-ca CONVERGEN en RETIRAR la columna, su escritor y el campo de API, sin backfill. Nadie eligio conservar ni derivar. Dos hechos que los otros carriles midieron y yo re-verifique ahora, y que cambian la FORMA del retiro, no la decision: (1) existe un indice idx_vehiculos_fecha_actualizacion sobre la columna en las DOS bases (pg_indexes, verificado) - se va con ella; (2) docs/reference/parque-movil-api/data-contract.md:11 fija la regla del contrato: renombrar o borrar un campo solo entra como /api/v2/, v1 se mantiene estable mientras tenga consumidores; el consumidor nombrado es SOTAMIT (:3). O sea el retiro del campo de API es un cambio de contrato versionado, no un DROP: v1 puede seguir publicando null (el campo ya es nullable en components.ts:626) mientras v2 lo omite. Tercero, de db04: el UPDATE de vehiculos_datos_colectivos dentro del mismo reconciliador NO toca la columna, asi que ni siquiera como metadato operativo es completo. Sigue UNMEASURED por los tres: si SOTAMIT lee el campo en vivo. DECISION DE ELAZAR: retiro (B) con contrato v2, o conservar (C).
-
Retirada la columna vehiculos.fecha_ultima_actualizacion y su escritor (145b2, 1d8b968d) y la copia en vehiculos_archive (145c, 92a37eb4), DEV y PROD, auditado. Sin backfill, como convergio el blackboard 3/3.