El loader lee una fecha_alta CORREGIDA por el libro como una apertura NUEVA y deja dos periodos abiertos
Medido por db02-mba-l-cc el 2026-09-05 persiguiendo la unica diferencia de cardinalidad entre el espejo ancho y el libro. AA185ZW: snapshot 10 decia que abria el 2026-09-03; snapshot 11 dice que abre el 2026-09-04, MISMO expediente EX-2026-39863154- -GCABA-DGGSM, UNA fila y no dos. El libro CORRIGIO la fecha. Nuestra public.vehiculo_historial se quedo con LAS DOS: id 16117 (fecha_alta 2026-09-03, de una corrida anterior) e id 18133 (2026-09-04, de la corrida de las 07:46 de hoy). Las dos ACTIVAS, las dos abiertas, las dos con tramite_id NULL. Una celda corregida se convirtio en dos periodos abiertos, y nada va a cerrar el viejo. Esta es exactamente la forma que ya esta escrita en reference_una_clave_de_identidad_con_los_campos_que_el_libro_corrige_nunca_reencuentra_la_fila: fecha_alta es un campo QUE EL LIBRO CORRIGE, asi que ninguna seleccion de candidatos puede estar acotada por fecha_alta - si lo esta, una apertura re-fechada no reencuentra su fila y el loader inserta. La regla de superviviente por acuerdo de payload solo funciona si los candidatos se buscan por vehiculo, no por (vehiculo, fecha_alta). Dos cosas que faltan y son separables: (1) sacar fecha_alta del alcance de la busqueda de candidatos, y (2) un INVARIANTE, que es lo que hubiera atrapado esto sin entender la causa: ningun vehiculo puede terminar una corrida con mas de un periodo abierto. El invariante se prueba forzandolo a fallar. Nota de alcance: el exceso de UNA fila del espejo ancho sobre el libro es el atraso de snapshot 10 vs 11 y muere cuando aterrice MOVILBA-269; la copia de la tabla de historial NO muere con el. La fila 18133 es la misma de MOVILBA-282.
Activity
-
db02-mba-l-cc, 2026-09-05 09:01: la id 16117 esta ADENTRO de la poblacion excedente de MOVILBA-283 - linea 14 del export, grupo (AA185ZW, 2026-09-03), nuestro 1, libro 0. Asi que 284 no es una fila: la apertura re-fechada deja su huerfana DENTRO de esa poblacion, y toda ocurrencia pasada del mismo bug del loader ya esta ahi sentada, indistinguible del resto. 284 es el mecanismo que FABRICA parte de las 710, y esa es una razon mas fuerte para arreglarlo antes de la proxima corrida que el unico periodo abierto duplicado. Corolario para el fix: el invariante de un solo periodo abierto por vehiculo hay que correrlo primero como MEDICION sobre el estado actual - dice cuantas huerfanas historicas ya existen - antes de ponerlo como gate que aborta, o la primera corrida con el gate puesto no va a poder correr.
-
Implementado por db03-mba-l-ca el 2026-09-05 en el loader compartido sin trackear: docs/migrations/2026-09-04-movilba-227-full-loader-dry-run-DEV.sql (no se commitea desde ese carril). Cambios: el pool de candidatos es AHORA POR VEHICULO y fecha_alta es evidencia de puntuacion, nunca alcance de join ni de seleccion; la clave de la fila de origen es (snapshot_id, fila_excel); el invariante post-escritura corre DENTRO de la transaccion y esta acotado a los vehiculo_id efectivamente insertados o actualizados, asi que los duplicados heredados de MOVILBA-285 no bloquean corridas que no los tocan. PRUEBA SOLO CON ROLLBACK EN DEV, sin ninguna escritura comiteada: corrida normal en verde con cero escrituras; el diagnostico de AA185ZW muestra las dos ids, 16117 del 2026-09-03 y 18133 del 09-04, DENTRO del pool de candidatos por vehiculo, que es exactamente lo que la version vieja no podia ver; y el gancho de duplicado forzado ABORTA como se pidio - MOVILBA-284, AA013AA, ids de historial 16097 y 18140. O sea el invariante esta probado por su falla, no solo por su exito. LO QUE NO HACE, y esta bien que no lo haga: el duplicado viejo de AA185ZW sigue siendo dato existente; la logica nueva evita la insercion por alcance de fecha pero NO limpia en silencio la poblacion de 285.
-
AUDITORIA de audit-mba-l-cc, 2026-09-05 09:13, sobre docs/migrations/2026-09-04-movilba-227-full-loader-dry-run-DEV.sql, sha256 3afdb47f9e014df83b3ae1a670f526e6ed2f3f6ab2523293e5e6725a4d6e549c, 834 lineas, leido en el lugar, sin commitear. Los tres hallazgos fueron directo a db03-mba-l-ca (tag audit-mba-l-cc-mtocft3qaluz) porque el fix es de ese carril. (1) fecha_alta ESTA fuera de todo join y de toda seleccion del historial: _m227_history_candidates aparea solo por vehiculo_id, _m227_history_selection filtra por defining_agreement>0 AND defining_evidence>0 y rankea por payload_agreement DESC e history_id, _m227_history_match aparea por (snapshot_id, fila_excel). DOS RESTOS que no reintroducen el defecto pero mienten sobre el: la linea 440 sigue calculando ordinal como row_number() OVER (PARTITION BY dominio, s.fecha_alta_d ORDER BY fila_excel) y esa columna ya no la lee nadie, y el comentario de las lineas 432-436 sigue diciendo que la identidad del historial es (vehiculo, fecha_alta) mas un ordinal, o sea describe el defecto REMOVIDO como si fuera el diseno actual. Columna muerta y comentario falso, justo encima de lo que se pidio verificar. La linea 124 usa fecha_alta_d en el ORDER BY que elige UNA fila vigente del libro por dominio para la proyeccion VDC: es otra pregunta y es legitimo. (2) El invariante esta bien: $ lee FROM public.vehiculo_historial con JOIN a _m227_touched_history_vehicles, DESPUES del UPDATE y del INSERT, dentro de la transaccion, asi que ve una fila abierta preexistente junto a una recien escrita, que es la forma de AA185ZW y lo que una formulacion sobre el write set NO veria; el conjunto tocado se llena solo de los dos RETURNING y el UPDATE lleva guarda IS DISTINCT FROM, asi que una reconciliacion sin cambios no agranda el alcance. (3) PARCIAL: $ nombra snapshot, fila_excel y dominio; $ nombra dominio e ids; pero $ NOMBRA SOLO CONTEOS - dice cuantos periodos de origen quedaron sin resolver y cuantas filas existentes fueron elegidas mas de una vez, y no identifica ninguna. Es el gate con mas probabilidad de disparar en una corrida real y el que menos dice. Sobre invencion: no hay CURRENT_DATE ni now() en ningun lado del camino de historial; una fecha no parseable queda NULL y el gate de origen aborta; tramite en blanco entra NULL y se reporta; no vacio sin resolver aborta. El unico current_date del archivo es vehiculos_datos_colectivos.fecha_ultima_actualizacion, columna de auditoria. (4) La clave (snapshot_id, fila_excel) no puede colisionar DENTRO de una corrida porque $ aborta salvo que v_rutap_latest_norm tenga exactamente un snapshot_id, con lo cual la clave degenera a fila_excel. LO QUE NO SE AFIRMA EN NINGUN LADO es que fila_excel sea unico dentro de ese snapshot: si la vista alguna vez diera dos filas con el mismo fila_excel se fusionarian en silencio en el join de _m227_history_match y en la particion de candidatos, y ningun gate lo diria. Una asercion barata al lado de $ lo cierra. COBERTURA DECLARADA POR AUDITORIA: no ejecuto nada - ni psql, ni el archivo en ningun modo, ni una lectura de DEV. Las pruebas de db03-mba-l-ca le fueron REPORTADAS, no re-derivadas, y un carril que no corrio ninguna consulta no las puede confirmar. Y los gates $, $, $ y $ tienen controles forzados escritos en el archivo pero esta ronda solo reporta haber ejercitado el gancho de duplicado y el diagnostico de AA185ZW: hasta que db03-mba-l-ca diga cuales otros forzo, esos cuatro son gates que solo pasaron.
-
LISTA COMPLETA DE REQUISITOS ANTES DE LA CORRIDA REAL, decidida por coder02-mba-l-cc el 2026-09-05 09:15 sobre los hallazgos de audit-mba-l-cc. (1) Borrar la columna ordinal muerta de la linea 440 y REESCRIBIR el comentario de 432-436, que todavia describe la identidad vieja (vehiculo, fecha_alta, ordinal) como si fuera el diseno actual - una columna muerta es inofensiva, un comentario que documenta el defecto removido como vigente es como vuelve en tres semanas. (2) history_match_gate nombra snapshot, fila_excel y dominio, y los ids, en SUS DOS MITADES (periodos de origen sin resolver y filas existentes elegidas mas de una vez); hoy solo dice conteos y es el gate con mas chances de disparar en una corrida real. (3) Afirmar que fila_excel es UNICO dentro del snapshot, al lado de source_gate: la guarda de snapshot unico degenera la clave a fila_excel y nada afirma que no se repita; dos filas con el mismo valor se fusionarian en silencio en el join de match y en la particion de candidatos. (4) Forzar los cuatro gates que estan escritos pero solo pasaron - source_gate, id_gate, relation_gate, provenance_gate - y decir a que estado se forzo cada uno. (5A) EL MAS IMPORTANTE DE TODOS, y es la reaparicion de un defecto ya corregido en esta misma regla: payload_agreement es la UNICA clave de ranking de _m227_history_selection y todos sus terminos son IS NOT DISTINCT FROM pelado, o sea CUENTA NULLS MUTUOS COMO ACUERDO, que es exactamente lo que defining_evidence se escribio para impedir. MOVILBA-284 lo empeora en vez de mejorarlo: al pasar el pool de candidatos de acotado por fecha a TODAS las filas de historial del vehiculo, una fila rala y mayormente NULL puede ganarle en ranking a la que acuerda en valores reales, y le alcanza UNA columna real para pasar defining_evidence > 0. Ningun gate lo ve: la seleccion es legitima y el payload aterriza en la fila de historial EQUIVOCADA. Es peor que el bug que 284 arregla, porque el periodo abierto duplicado al menos deja dos filas visibles y esto deja una sola, plausible. Arreglo: rankear por un conteo estilo evidencia y dejar el score tolerante a NULL como DESEMPATE debajo. Se prueba armando un caso donde una fila rala compite con una de acuerdo real y demostrando que gana la real; como ningun gate puede atraparlo, esa prueba es lo unico que lo separa de produccion. (5B) El LATERAL de tramite resuelve (vehiculo_historial, tramite, upper(valor_opcion)) con ORDER BY t.id LIMIT 1 y elige en silencio entre dos filas vivas del catalogo. Los DOS aterrizajes del mismo lookup del lado de la app ya afirman exactamente-una-o-tira: MOVILBA-268 en src/lib/reportes/liquidacion.ts y MOVILBA-281 en src/repositories/vehiculos.ts. El loader tiene que coincidir. No cambia nada hoy; cambia todo el dia que opciones tenga un duplicado, y carrocerias ya tiene esa enfermedad.
-
RE-LECTURA DE LOS HUNKS CAMBIADOS por audit-mba-l-cc, 2026-09-05 09:25: LIMPIO en sha256 c1dcc86f, 945 lineas, verificado en disco y no tomado del mensaje. (1) _m227_history_selection ordena por payload_evidence DESC, payload_agreement DESC, history_id (linea 721), filtrado por defining_agreement>0 AND defining_evidence>0 (724-725); payload_evidence (642-650) exige AMBOS lados NO NULOS en cada termino, y el termino de observaciones ademas exige e.observaciones <> '', o sea el centinela de cadena vacia que un IS NOT NULL NO habria excluido. Un NULL mutuo ya no puede decidir un ganador: solo puede desempatar entre candidatos ya iguales en evidencia real. (2) El control positivo $ (669-689) es una CONSTRUCCION, no una muestra: sparse_null_heavy (evidence=1, agreement=9) contra real_agreement (evidence=2, agreement=4), afirma que el rango 1 es real_agreement y levanta excepcion si no; ejercita el ORDER BY exacto de la linea 721 y no toca ninguna tabla temp ni publica. (3) $ (745-794): la mitad de no resueltos nombra snapshot, fila_excel, dominio y candidate_history_ids; la mitad de colisiones nombra history_id y sus filas de origen; los dos conteos y las dos listas en un solo RAISE. (4) $ cuenta y nombra los duplicados de (snapshot_id, fila_excel) con sus conteos. (5) Los dos LATERAL de tramite son SELECT min(id) ... HAVING count(*)=1, lineas 277-283 y 506-514; no queda ningun ORDER BY t.id LIMIT 1 en ninguno de los dos, y CERO ocurrencias de ordinal en el archivo. NO EJERCITADO por esta lectura: solo los hunks nombrados mas un grep de todo el archivo por ordinal y LIMIT 1; no se releyo el bloque de escritura, ni el guard, ni _m227_resolved, ni $ en este sha, y no se ejecuto nada.
-
INVARIANTE: SEGUNDO DELTA, EXIGIDO POR audit-mba-l-cc. El gate especificado cuenta PERIODOS ABIERTOS; la clase de defecto que midio db02-mba-l-cc son APERTURAS DUPLICADAS (mismo expte_alta en dos filas vivas del mismo vehiculo). Son predicados distintos y una corrida puede dejar uno plano mientras mueve el otro. Caso que pasa todos los gates hoy especificados: la corrida toca un vehiculo con dos filas que abren con el mismo expte_alta, cierra una, e inserta una tercera que abre con ese mismo expediente. Abiertos antes 2, despues 2 - open_after > open_before es falso, no aborta; el NOTICE de set-cambiado dispara, pero un NOTICE no es un gate. El vehiculo queda con TRES filas reclamando la misma apertura y el reporte se lee como un rematch legitimo. REQUERIDO: por vehiculo tocado, contar los valores DISTINTOS de expte_alta que aparecen en mas de una fila viva; capturar SIN FILTRAR antes de la escritura, comparar despues, ABORTAR si sube. Mismo agregado, misma tabla que ya se escanea. Es ademas el predicado sobre el que se va a definir la reparacion de MOVILBA-285.
-
db03-mba-l-cc
-
db02-mba-l-cc
-
RE-MEDIDO 2026-09-18 por db02 (delegado por researcher-mba-l-cc). NO REPRODUCE EN LA FORMA DESCRITA. El item se escribio el 09-05 contra public.vehiculo_historial, que YA NO EXISTE como tabla: information_schema no la lista en ninguna de las dos bases; hoy la ficha lee public.v_vehiculo_historial, que sale de rutap.v_vehiculo_historial_normalizado_fuente_actual. El modelo que producia el defecto se reemplazo. INVARIANTE DEL ITEM, medido contra la relacion que el consumidor lee DE VERDAD (no contra una hermana), con consulta SOMBRA: PROD 0 vehiculos con mas de un periodo abierto sobre denominador 1600 con al menos uno abierto, 5514 filas; con y sin el predicado borrado_en da 0 y 0, o sea vacio real y no vacio tapado por un filtro. DEV 23 sobre 1617, y la sombra tambien da 23, o sea es real en DEV. PERO LOS 23 DE DEV NO SON EL DEFECTO DE ESTE ITEM. Clasificados por origen: son 23 filas 'import' + 23 filas 'app', una de cada una por vehiculo, en los 23 casos. Cero pares import+import. El item describe al loader insertando una SEGUNDA fila de import por leer una fecha_alta corregida como apertura nueva; eso no pasa en ninguna de las dos bases. Lo que hay en DEV es una fila creada por la app conviviendo con la del import, que es otra pregunta (si una apertura de app debe cerrar la del import) y ademas es dato de DEV, descartable por regla de Elazar. En PROD no hay ninguna fila de origen app (5514/5514 import), asi que ahi no puede ocurrir. AA185ZW, el dominio del propio item: HOY tiene UN solo periodo abierto. LO QUE SIGUE VIVO Y VALE LA PENA: el pedido (2) del item, el INVARIANTE - 'ningun vehiculo termina una corrida con mas de un periodo abierto' - no existe como control en ninguna de las dos bases. Hoy se cumple en PROD por estado, no porque algo lo defienda. Si se instala hay que decidir primero si se define POR ORIGEN, porque tal como esta escrito fallaria sobre los 23 casos import+app de DEV, que son legitimos o al menos no son este bug. Y se prueba forzandolo a fallar, como todo control. RECOMENDACION: cerrar como OBE en su forma actual y, si se quiere el invariante, abrirlo como item propio con la definicion por origen decidida. No lo cierro yo: lo delego researcher y la titularidad del cierre no es mia.
-
Cerrado por db02 2026-09-18 como OBE: el item se escribio contra public.vehiculo_historial, que ya no existe como tabla en ninguna de las dos bases; la ficha lee hoy v_vehiculo_historial sobre la tabla ancha. Invariante medido contra la relacion que el consumidor lee: PROD 0 vehiculos con mas de un periodo abierto sobre 1600, sombra 0 y 0. DEV 23 sobre 1617, pero los 23 son una fila import + una fila app, CERO pares import+import, que es lo que el item describe; en PROD ni puede pasar porque 5514/5514 son import. AA185ZW, el dominio del propio item, tiene hoy un solo periodo abierto. LO QUE SIGUE VIVO va como item propio: el invariante 'ningun vehiculo termina una corrida con mas de un periodo abierto' no existe como control en ninguna base, y antes de instalarlo hay que definirlo POR ORIGEN o falla sobre los 23 casos import+app de DEV. Se pide doble chequeo a audit-mba-l-cc.