basquetWi + New ticket
movilba MOVILBA-42 · child of MOVILBA-125 RUTAP full-history refactor Backlog

Refactor: historial de vehiculo pasa a log append-only con workflow (reemplaza el parche de forzar Otro)

Done high cmcoder02-mba-l-cc

PROPUESTA DE CLAVE DE EVENTO, MEDIDA (coder-mba-l-cc, 2026-08-20 08:15). El ticket estaba trabado no en permiso sino en una pregunta: se puede DEFINIR una clave de evento durable, derivada de la fuente, que reemplace a uq_vehiculo_historial_activo bajo el modelo append-only. La respuesta medida es SI, y no es el expediente solo. CANDIDATA GANADORA, contra el libro (2026-08-20-0211-Parque_RUTAP.xlsx, hoja Base, 5086 filas): (dominio, Fecha_Alta, Fecha_Baja, Expte. Alta) -> 5086 claves distintas, CERO colisiones. (chasis, Fecha_Alta, Fecha_Baja, Expte. Alta) -> igual, cero colisiones. Descartadas por medicion, no por opinion: dominio+fecha_alta 20 colisiones; +tramite 14; dominio+expte_alta 92 en 88 claves (el expediente solo NO alcanza, que es lo que MOVILBA-38 ya insinuaba desde el otro lado); dominio+fecha_alta+fecha_baja 5. Las 5 colisiones de esa ultima se resuelven TODAS agregando Expte. Alta - verificado fila por fila: son AD956UK (dos Alta el mismo dia con expedientes distintos, que es MOVILBA-37) y cuatro Modif de la linea 68 del 26/12/2025 donde el expte_alta es el unico campo que difiere. CONTRA PROD, que es la prueba que importa: la misma clave sobre vehiculo_historial (4045 filas) da 7 colisiones. NO son fallas de la clave, son DATOS DE PROD que el libro no tiene: - 3 pares (vehiculo 3793, 3953, 5181): una fila VIVA con estado_id 116 creada 2026-06-22 mas una fila soft-deleteada creada en la fecha del tramite. Dos cargas distintas de la misma cosa. - 3 pares (4093, 4109, 4110): las dos borradas, MISMO expediente y MISMA fecha, pero DISTINTA entidad y linea (41/77 contra 65/131). La clave era demasiado angosta para ver el campo que los distingue - la forma madre [[reference_a_key_too_narrow_to_see_the_distinguishing_field]], septima instancia, encontrada mientras se disenaba una clave. - 1 par (3766): identico en todo. Duplicado real. AGREGANDO entidad_id y linea_id a la clave: 4 colisiones en total y CERO entre las filas vivas. CLAVE PROPUESTA, entonces: UNIQUE (vehiculo_id, fecha_alta, COALESCE(fecha_baja,'9999-12-31'), COALESCE(expte_alta,''), COALESCE(entidad_id,-1), COALESCE(linea_id,-1)) Los COALESCE no son cosmetica: en Postgres un UNIQUE trata los NULL como distintos, asi que sin ellos el indice NO impide repetir una fila abierta (fecha_baja NULL en 1599 filas) ni una sin expediente (447 en prod, 727 en el libro). Un indice que no cubre el caso mas comun es peor que ninguno porque parece que protege. LO QUE ESTO HABILITA Y LO QUE NO. Habilita el append-only: se puede insertar N filas por vehiculo y seguir teniendo idempotencia de re-corrida, que era la unica funcion real que cumplia uq_vehiculo_historial_activo del lado de la escritura. NO habilita dropear el indice viejo hoy: primero hay que resolver los 4 duplicados de prod (3 pares de doble carga y 1 identico), porque el indice nuevo no se puede crear con ellos presentes. Ese es el trabajo, y es medible: 4 filas. PENDIENTE DE ELAZAR, una sola cosa: si acepta esta clave. No es una decision de implementacion - define que significa 'el mismo evento' para el sistema. Dato que ayuda a decidir, medido por db02 el 2026-08-20: hoy TODOS los vehiculos vivos de dev tienen una fila viva de historial, sin excepciones, asi que el camino de alta de vehiculo solo es alcanzable para un vehiculo sin historial vivo - el indice actual lo prohibe activamente.

Sub-tickets

No sub-tickets.
+ Add sub-ticket

Activity

  • wi-cli-venus created · 2026-08-14
  • wi-cli-venus assigned · 2026-08-20
    coder02-mba-l-cc / coder
  • wi-cli-venus commented · 2026-08-20
    DECISION QUE NECESITA A ELAZAR (medido por db02): 42 y uq_vehiculo_historial_activo son INCOMPATIBLES, y no se arregla cambiando el alcance del indice. MODELO DE HOY: el indice ENFUERZA 'exactamente una fila vigente por vehiculo' - vigente es un hecho que el indice garantiza. APPEND-ONLY (42): muchas filas vivas por vehiculo por definicion, el indice hay que DROPEARLO, no re-alcanzarlo, y 'vigente' deja de estar enforzado y pasa a ser DERIVADO (proyeccion 1, en cada lectura). No se pueden tener los dos. Todo consumidor que hoy confia en 'la fila viva es la fila vigente' tendria que pasar a la regla derivada EN EL MISMO DEPLOY, y ese conjunto es mas grande que la superficie de 46: incluye v_vehiculos_hist_ultimo y, a traves de ella, el export, la ficha y el listado.
  • wi-cli-venus commented · 2026-08-20
    MEDICION QUE CONVIERTE ESTO EN UNA DECISION CON PRECIO (db02, PROD 06:12). Hasta hoy 42 (mantener el indice de una-fila-viva vs pasar a append-only) era una preferencia de modelado. Ya no. MOVILBA-83 necesita limpiar borrado_en en 2309 filas para mover la Fecha Baja a fecha_baja. uq_vehiculo_historial_activo es UNIQUE(vehiculo_id) WHERE borrado_en IS NULL, o sea que esas 2309 filas estan HOY fuera del alcance del indice UNICAMENTE por la sobrecarga. Medido sobre los 1736 vehiculos con historial: al limpiar borrado_en, 1293 vehiculos quedarian con MAS DE UNA fila viva (74% de la flota), 443 con exactamente una, 3602 filas en el conjunto que viola, el peor vehiculo con 7. Hoy los vehiculos con mas de una fila viva son 0. O sea que el backfill de 83 no roza el indice en un caso borde: es RECHAZADO en la primera sentencia, sobre el 74% de los vehiculos. NO PUEDE CORRER mientras el indice exista en su forma actual. CONSECUENCIA PARA LA DECISION, y no es un argumento por ningun lado: las dos preguntas son UNA. Mantener el indice es tambien elegir que borrado_en siga significando dos cosas en vehiculo_historial PARA SIEMPRE, porque la columna que deberia guardar la fecha de cierre no se puede liberar. Ir a append-only es lo que hace posible limpiarlo. SECUENCIA RESULTANTE, inversa al orden en que se abrieron los tickets: decide 42 -> recien ahi 83 es posible -> recien ahi el monitor de 73 se reescribe contra fecha_baja y puede ir a prod. NO afecta a 76: los pasos 1-4 no tocan borrado_en ni el indice; el paso 1 agrega fecha_baja vacia, que es prerequisito de 83 y no un paso hacia el.
  • wi-cli-venus descriptionChanged · 2026-08-20
    was: DECISION DE ELAZAR 2026-08-14: en vez de parchear el modelo actual, refactorizar el historial de vehiculo a un log APPEND-ONLY simple, con workflow encima. Origen: pedido de Eugenia (stakeholder, equipo Parodi) en el nuevo todo-importacion-app-general.xlsx, asunto 12 hoja online-stakeholder: "que todos los cambios al vehiculo sean visibles, no solo los que tienen expediente", mas requisitos futuros para el PDF y el export Excel. La lista nueva reemplaza a la anterior. ESTADO MEDIDO HOY (dev, 2026-08-14): - vehiculo_historial.expediente es NULLABLE: una fila sin expediente ya es legal, no hace falta DDL para eso. - motivo_id FK a opciones, y "Otro" ya existe: id 172 (vehiculo/motivo_asignacion_linea) y id 166 (vehiculo/motivo_transferencia). Ver mem:reference_motivo_two_catalogs_one_column. - cambios jsonb ya guarda {campo:{anterior,nuevo}} y ExpedienteForm.tsx:245 ya renderiza un bucket "Otro". - observaciones es NOT NULL: toda fila autogenerada necesita texto generado al escribir, no inventado por la UI (regla de Elazar). EL BLOQUEANTE ES ESTRUCTURAL, NO DE CATALOGO: uq_vehiculo_historial_activo UNIQUE (vehiculo_id) WHERE borrado_en IS NULL admite UNA sola fila activa por vehiculo. Para insertar la siguiente hay que poner borrado_en en la anterior, o sea "borrado" significa "tramite cerrado". Eso es MOVILBA-12 y es lo que mantuvo las cadenas invisibles hasta el fix de vista del 14/08. Con auto-generacion de filas la colision pasa de ocasional a cada edicion. FORMA PROPUESTA (a validar antes de codear): tabla de eventos append-only, sin borrado logico como mecanismo de vigencia, sin indice de fila unica activa; la vigencia se deriva del ultimo evento, no de un NULL. El expediente pasa a ser un ATRIBUTO del evento, no su condicion de existencia. El workflow (que evento requiere expediente, cual no, quien lo puede registrar) va encima, no adentro del modelo de datos. ALCANCE A DEFINIR CON ELAZAR/EUGENIA antes de tocar codigo: que cuenta como evento visible (toda edicion de campo? solo un subconjunto?), que se muestra en el PDF y en el export, y que pasa con las 4045+ filas historicas existentes (migracion vs corte). Padre natural de MOVILBA-12. NO empezar sin GO explicito.
  • wi-cli-venus commented · 2026-08-20
    CORRECCION DE EJECUCION, verificada por db02 sobre PROD el 2026-08-20 08:33 (solo lectura). La clave como esta escrita en la descripcion NO SE PUEDE CORRER EN PROD tal cual: nombra fecha_alta y expte_alta, que son los nombres POST-paso-3 de MOVILBA-76 y existen SOLO EN DEV. Prod sigue teniendo fecha_expediente y expediente - el paso 3 fue una ventana de dev y prod nunca lo recibio. La primera corrida de db02 fallo con: column "fecha_alta" does not exist. Traduccion para prod, hasta que el paso 3 llegue a produccion: fecha_alta -> fecha_expediente, expte_alta -> expediente. Queda escrito aca en vez de corregir la descripcion porque en dev los nombres de la descripcion SI son los correctos; el que pega el SQL contra prod tiene que traducir, y el error que va a ver parece decir que la clave esta mal cuando lo unico que esta mal es el entorno. LOS DOS NUMEROS SON LOS DOS CIERTOS - no reconciliarlos en uno. Son alcances distintos del mismo dato, medidos por db02 sobre prod (version traducida): solo filas vivas, clave completa -> 0 colisiones TODAS las filas, clave completa -> 4 grupos / 8 filas <- lo que hay que limpiar TODAS las filas, clave SIN entidad_id + linea_id -> 7 grupos / 14 filas O sea: exactamente 3 grupos se rescatan al agregar entidad+linea (los "tres de los siete que solo diferian por entidad+linea" de la descripcion), y los 4 que quedan son la limpieza previa al indice. Las dos frases describen el mismo conjunto. EL NUMERO QUE MANDA PARA DECIDIR ES EL 0. La clave propuesta ya es unica sobre TODO lo que esta vivo hoy en prod. El indice se puede crear despues de limpiar 4 grupos historicos, sin tocar UNA SOLA fila viva. El pedido es mucho mas chico que "hay que limpiar antes de poder indexar".
  • wi-cli-venus commented · 2026-08-20
    ADVERTENCIA DEL QUE MIDIO (db02, sobre su propia cifra): el 0 y el 4 son un CENSO de prod al 2026-08-20 08:33, no una propiedad de la clave. Una escritura entre ese momento y el CREATE INDEX invalida cualquiera de los dos. El que finalmente cree el indice tiene que re-contar las colisiones DENTRO DE LA MISMA TRANSACCION que el indice, no confiar en el numero de hoy. Es feedback_counts_come_from_the_target_db_not_a_ticket aplicado a la cifra propia.
  • wi-cli-venus commented · 2026-08-20
    RESUELTO DEL LADO DE LA BASE, 2026-08-20 10:53, sin limpieza y sin borrado. Elazar acepto la clave; lo que faltaba era el ALCANCE del indice, y se midio dos veces por metodos distintos (db-mba-l-cc y db02-mba-l-cc) con el mismo resultado. INDICE ACORDADO: UNIQUE (vehiculo_id, fecha_alta, COALESCE(fecha_baja,'9999-12-31'), COALESCE(expte_alta,''), COALESCE(entidad_id,-1), COALESCE(linea_id,-1)) WHERE fecha_baja IS NULL AND borrado_en IS NULL Cero filas tocadas. Cero DELETE. No choca con el gate de "la baja es un ESTADO, no un borrado". POR QUE NO ES NINGUNA DE LAS DOS FORMAS QUE SE PROPUSIERON PRIMERO: - Indice COMPLETO (todas las filas): imposible sin un hard DELETE sobre vehiculo_historial, que el gate permanente rechaza aun con GO. Los 4 grupos duplicados son pares de filas exactas; soft-deletear no cambia los valores de la clave. - Indice PARCIAL sobre `borrado_en IS NULL` a secas: recrea el problema que 42 existe para sacar. En ESTA tabla borrado_en NO significa borrado - 2301 de 2309 filas con borrado_en NOT NULL tienen tambien fecha_baja, o sea que guardan la Fecha Baja de RUTAP (MOVILBA-83). Un indice sobre ese predicado queda RECHAZADO por el backfill de 83 en el 74% de la flota, exactamente como el uq_vehiculo_historial_activo de hoy. POR QUE EL PREDICADO COMPUESTO ES ESTABLE A TRAVES DE 83, y esto es lo que cierra el ticket: 83 mueve la Fecha Baja FUERA de borrado_en, o sea que solo toca filas que TIENEN Fecha Baja. Las 2301 ya quedan afuera del indice por la pata `fecha_baja IS NULL`; limpiarles borrado_en no puede meter ninguna adentro. Las unicas filas cuya pertenencia podria cambiar son las 8 que tienen borrado_en sin fecha_baja - y 83 no las toca, justamente porque no tienen Fecha Baja que mover. Poblacion del indice invariante. QUE SON ESAS 8 FILAS (medido): son los 4 vehiculos con mas de una fila abierta por fecha_baja - ids 3766, 4093, 4109, 4110, dos filas cada uno. Todas con creado_por='import-rutap-2026-08-14-prod', creadas y soft-deleteadas en el mismo lote. En 3 de los 4 el par difiere UNICAMENTE en entidad_id/linea_id (41/77 contra 65/131): el importador inserto y despues corrigio la asignacion. El par de 3766 (ids 8933/8934) es identico en toda la clave y es el unico duplicado real. Los cuatro quedan inertes: estan soft-deleteados y el indice no los ve. Lectura, no prueba - si a 83 le importa el origen de esas 8, que lo confirme quien es duenio del importador de RUTAP. RETRACTACION QUE HIZO POSIBLE TODO ESTO (db02): el "0 colisiones en filas vivas" que figura mas arriba en este ticket se midio bajo `borrado_en IS NULL`, que es EXACTAMENTE el predicado que uq_vehiculo_historial_activo ya enfuerza. Era circular: cualquier clave que empiece con vehiculo_id da cero ahi, sea correcta o no. Se midio una propiedad del indice viejo y se reporto como una propiedad de la clave nueva. Bajo el test de vigencia correcto la clave NO esta libre de colisiones hoy (1 grupo real), y por eso el indice va con el predicado compuesto y no a secas. SECUENCIA: va DESPUES del paso 3 de MOVILBA-76, para que la clave use fecha_alta/expte_alta sin traduccion. El recuento de colisiones va DENTRO de la misma transaccion que el CREATE INDEX, nunca confiando en la cifra de hoy. PENDIENTE: el CREATE INDEX es una escritura en prod y necesita su propio GO de Elazar.
  • wi-cli-venus statusChanged · 2026-08-20
    alcance del indice resuelto y medido dos veces; falta el paso 3 en prod y un GO para el CREATE INDEX
  • wi-cli-venus commented · 2026-08-20
    CORRECCION AL COMENTARIO ANTERIOR (mio, 10:53). Dos parrafos de ese comentario son FALSOS y no hay que usarlos. Los dejo en pie en vez de reescribirlos porque el ticket ya se leyo asi, pero lo que vale es esto. LO QUE DIJE MAL: escribi que las 8 filas con borrado_en sin fecha_baja "son artefactos de correccion del importador, no bajas reales" y que "83 no las toca, justamente porque no tienen Fecha Baja que mover". Las dos cosas son falsas. db03-mba-l-ca lo midio primero y db02-mba-l-cc lo confirmo por separado: para las 8 de 8, borrado_en es IGUAL a una Fecha Baja que RUTAP efectivamente tiene para ese vehiculo, emparejada por fecha (ids 8222, 8244, 8247, 8933, 8934, 8965, 8972, 9028). O sea que **las 8 son la MISMA sobrecarga que las 2301**: 2309 de 2309. La categoria "8 bajas reales" no existe. Lo unico distinto es que a esas 8 nunca se les poblo nuestra columna fecha_baja. QUE CAMBIA Y QUE NO: - NO cambia que el indice se puede crear HOY con cero limpieza y cero DELETE. Medido bajo el predicado compuesto: 1599 filas, 0 grupos de colision, 0 vehiculos con mas de una fila. Sigue siendo cierto. - SI cambia la razon por la que es estable a traves de 83. **No es una propiedad del indice: es un REQUISITO SOBRE 83.** Si 83 MUEVE la fecha (setea fecha_baja desde borrado_en y despues limpia borrado_en), las 8 quedan afuera por la pata fecha_baja y el indice sobrevive: 0 y 0. Si 83 solamente LIMPIA borrado_en sin llevar la fecha, esas 8 pierden su unica marca de cierre, quedan abiertas por las dos patas, y aparecen 4 vehiculos con varias filas abiertas y 1 colision. Las dos cosas se ven igual desde afuera: "83 hecho". - **ACCION PARA 83: mover la fecha, nunca solo limpiar la columna. Va como test de aceptacion de 83, no como confianza en quien lo implemente.** SEGUNDO CAVEAT, de db02, y aplica a como se cita el cero: el predicado compuesto es un SUBCONJUNTO ESTRICTO de `borrado_en IS NULL`, que uq_vehiculo_historial_activo ya restringe a una fila por vehiculo_id. La unicidad sobre el superconjunto implica unicidad sobre cualquier subconjunto, asi que el 0 de hoy esta PARCIALMENTE IMPLICADO por el indice viejo y no es independiente de el. Alcanza y sobra para que el CREATE INDEX corra - los datos de hoy satisfacen la clave - pero NO es evidencia de que la clave sea una buena definicion de "el mismo evento". Eso es un juicio de diseno y ya lo dio Elazar. Al reportar hay que separar las dos frases: "el indice se puede crear hoy sin limpieza" esta medido; "la clave es la definicion correcta de evento" no se mide asi. COMO SE ENCONTRO: db-mba-l-cc contesto "ninguna de las 8 esta en alcance de 83" y db03 contesto lo contrario, las dos sobre prod y las dos read-only. No eran mediciones contradictorias: eran respuestas a DOS PREGUNTAS DISTINTAS - una pregunto si RUTAP deja en blanco la Fecha Baja de un tramite cerrado (no, nunca), la otra si RUTAP TIENE una Fecha Baja para estos vehiculos (si, con filas). Las dos ciertas a la vez, y ninguna contestaba la que decidia el indice, que era sobre la REGLA de 83 y no sobre el contenido de RUTAP.
  • wi-cli-venus completed · 2026-08-20
    INDICE CREADO EN PROD. uq_vehiculo_historial_evento, COMMIT 2026-08-20 12:00:55 ART, exit 0, menos de 1 segundo. GO textual de Elazar 'go 42' (el 'go 24' previo se freno por numero equivocado: 24 cerro el 08-14 y un GO autoriza lo DESCRITO, no un numero que apunta a otro ticket). Script docs/migrations/2026-08-20-movilba-42-indice-evento-historial-PROD.sql md5 10ba057f931212c0377db5e0ce4a281a, re-verificado byte a byte contra el ensayo de 11:57:51. CLAVE: (vehiculo_id, fecha_alta, COALESCE(fecha_baja,'9999-12-31'), COALESCE(expte_alta,''), COALESCE(entidad_id,-1), COALESCE(linea_id,-1)) WHERE fecha_baja IS NULL AND borrado_en IS NULL. Los COALESCE son la clave del asunto: en un UNIQUE de Postgres los NULL son distintos, asi que sin ellos el indice se saltea las 1599 filas abiertas y las 447 sin expediente - reportaria unico porque se niega a comparar casi toda la tabla. GATES: 0 base+7/7 columnas+uq_activo presente+uq_evento ausente+4045 filas; 1 CERO colisiones sobre 1599 filas abiertas RE-MEDIDAS al momento del apply (no la cifra heredada de las 11:31, aunque coincidieron); 2 UNIQUE y parcial, uq_vehiculo_historial_activo INTACTO; 3 4045 filas. GATE 1 es un gate real y no un adorno: la misma consulta sin el predicado parcial devuelve 4 grupos de colision, con el predicado 0 - detecta colisiones cuando existen. Verificado DESPUES del commit como svc_nextjs (no con la superuser que lo creo): indisvalid=t, 136 kB, y el indice viejo en pie 96 kB. MEDIDO: se creo SIN limpieza previa y SIN tocar una sola fila. NO MEDIDO: que la clave sea la definicion correcta de 'evento' - eso es juicio de diseno de Elazar, y el 0 esta PARCIALMENTE IMPLICADO porque el predicado compuesto es subconjunto estricto de 'borrado_en IS NULL', que uq_vehiculo_historial_activo ya limita a una fila por vehiculo. Las dos frases no se reportan como una sola. EFECTO PERMANENTE, vivo desde el commit: el indice RECHAZA INSERT/UPDATE colisionantes. No es una sentencia que pasa. MOVILBA-83 es lo primero que lo toca y el requisito se ENDURECE: mover la fecha (setear fecha_baja desde borrado_en, y recien despues limpiar borrado_en), nunca solo limpiar. Si limpia sin mover, las 8 filas con borrado_en sin fecha_baja quedan abiertas por las dos patas -> 4 vehiculos multi-abiertos y 1 colision, y ahora el backfill FALLA en vez de degradarse en silencio. Eso es mejora, no obstaculo. ANULADA la nota de traduccion de nombres del ticket (fecha_alta->fecha_expediente, expte_alta->expediente): quedo obsoleta a las 11:30:07 con el paso 3 en prod; los nombres corren literales desde entonces. NO cubierto por este GO ni por este ticket: dropear uq_vehiculo_historial_activo, el refactor append-only, y 83. Rollback si hiciera falta: DROP INDEX public.uq_vehiculo_historial_evento - no hay datos que revertir.
  • wi-cli-venus statusChanged · 2026-08-20
    REABIERTO por coder02, 12:02, correccion propia. Lo cerre a las 12:01 con el indice creado, pero el TITULO de este ticket es el refactor a log append-only con workflow, y eso NO se hizo. El GO 'go 42' cubrio UNA sentencia: el CREATE UNIQUE INDEX. Explicitamente NO cubrio dropear uq_vehiculo_historial_activo, ni el append-only, ni el workflow, ni 83. Cerrarlo como done decia que el refactor esta hecho, y no lo esta - el proximo lector habria heredado 'append-only shipped' cuando lo unico vivo es una clave de evento nueva conviviendo con el indice viejo de una-fila-abierta. HECHO Y EN PROD (el detalle completo quedo en el evento de cierre anterior, no lo repito): uq_vehiculo_historial_evento, COMMIT 12:00:55, 0 colisiones sobre 1599 filas abiertas, sin tocar filas, indisvalid=t, el indice viejo intacto. LO QUE FALTA, que es el ticket propiamente dicho: decidir si uq_vehiculo_historial_activo se retira (hoy conviven dos indices que responden preguntas distintas - 'una sola fila abierta por vehiculo' contra 'un solo evento por vehiculo'), el modelo append-only en si, y el workflow que reemplaza el parche de forzar Otro. Nada de eso tiene GO ni plan escrito. PRECONDICION QUE YA ESTA VIVA Y NO SE PUEDE IGNORAR: el indice nuevo rechaza colisiones desde el commit, asi que 83 tiene que MOVER la fecha (setear fecha_baja desde borrado_en y recien despues limpiar), nunca solo limpiar.
  • wi-cli-venus note · 2026-08-20
    PRECISION sobre el primer item pendiente, medida por db02 en prod 12:03 - el drop del indice viejo NO es un item separado: es el PRIMER PASO del append-only. Definiciones textuales de los dos indices que hoy conviven: uq_vehiculo_historial_activo = UNIQUE (vehiculo_id) WHERE borrado_en IS NULL. uq_vehiculo_historial_evento = UNIQUE (vehiculo_id, fecha_alta, COALESCE(fecha_baja,'9999-12-31'), COALESCE(expte_alta,''), COALESCE(entidad_id,-1), COALESCE(linea_id,-1)) WHERE fecha_baja IS NULL AND borrado_en IS NULL. El VIEJO es estrictamente MAS FUERTE por vehiculo: permite UNA SOLA fila con borrado_en IS NULL. El NUEVO permite VARIAS filas abiertas del mismo vehiculo mientras difieran en algun campo de la clave. O sea: el nuevo NO subsume al viejo, y retirar el viejo no es limpieza - es exactamente el cambio que HABILITA el append-only, porque hoy uq_vehiculo_historial_activo es lo que lo prohibe. Consecuencia operativa: no dropear el indice viejo antes de que el codigo sepa convivir con varias filas abiertas por vehiculo. Listarlo como bullet independiente invita a que alguien lo dropee 'para dejar uno solo' y abra la puerta sin que nada este listo para pasar.
  • wi-cli-venus blocked · 2026-08-21
    PREREQUISITO DURO NUEVO, medido por db02 en PROD 2026-08-21 10:01: MOVILBA-73 hay que arreglarlo ANTES de este ticket, no despues. v_vehiculos_hist_ultimo ordena por (borrado_en IS NOT NULL) primero, asi que hoy elige bien SOLO porque 1737 de 1738 vehiculos tienen exactamente una fila viva y el desempate por fecha/creado_en nunca se alcanza. Dropear uq_vehiculo_historial_activo - que es literalmente el primer paso de este refactor - es lo que permite varias filas abiertas por vehiculo, y en ese momento la vista cae al desempate roto. Y ya no es inocuo: 124 de 1738 vehiculos varian entidad_id entre sus filas de historial y 121 varian linea_id (cuando se escribio 73 eran 0 de 28), asi que una eleccion equivocada ahora cambia lo que la vista PROYECTA. Hay 6 vehiculos con empate real de fecha_alta dentro del mismo grupo de liveness (AD919PL, AD956UK, AE922FX, AE971FE, AE971FF, AG358KX) y 3 de ellos varian entidad, 3 varian linea. Mismo acople con MOVILBA-83 (borrado_en cargando la Fecha Baja de RUTAP). Orden obligado: 73 y 83 -> 42.
  • wi-cli-venus note · 2026-08-21
    PREREQUISITO NUEVO Y DURO: MOVILBA-73, medido por db02-mba-l-cc en prod 10:01. Este refactor dropea uq_vehiculo_historial_activo, que es lo unico que mantiene una sola fila viva por vehiculo - y v_vehiculos_hist_ultimo elige correctamente HOY solo porque su primera clave de orden es (borrado_en IS NOT NULL) y esa unicidad hace que el desempate por fecha/creado_en nunca se alcance. En cuanto 42 aterrice, la vista cae al desempate, sobre una poblacion donde 124 de 1738 vehiculos varian entidad_id entre sus filas y 121 varian linea_id, asi que una eleccion equivocada cambia lo que la vista PROYECTA. O sea que 42 rompe 73 salvo que 73 se arregle antes. Lo mismo vale para MOVILBA-83, que le saca a borrado_en el significado del que depende ese orden. Secuencia: 73 primero, despues 83 y 42.
  • wi-cli-venus note · 2026-08-21
    REFINAMIENTO del prerequisito, medido por db-mba-l-cc en PROD 2026-08-21 10:01 y confirmado independiente de db02: los 6 empates de MOVILBA-73 son empates COMPLETOS - fecha_alta Y creado_en iguales - asi que el ORDER BY no elige ganador en absoluto y DISTINCT ON toma la fila que el plan entregue primero. No es un desempate debil: es un desempate AUSENTE, y un VACUUM o un cambio de plan lo mueve en silencio. En 3 de los 6 (AE922FX, AE971FE, AE971FF) las filas empatadas llevan entidad_id Y linea_id distintos, o sea que el no-determinismo llega a las columnas proyectadas. Hoy no se alcanza porque los 6 empates son entre filas MUERTAS y la clave de liveness va primera. 1295 vehiculos tienen mas de una fila de historial: son los que caen al desempate el dia que este ticket dropee uq_vehiculo_historial_activo. EXIGENCIA que se suma al fix de 73: el orden tiene que quedar TOTAL, con una clave final determinista (h.id), no solo 'mejor' - un desempate que puede empatar es la misma forma que un gate que no puede dudar de su propia clave.
  • wi-cli-venus note · 2026-08-21
    LA CLAVE DE EVENTO CANDIDATA ESTA REFUTADA, no incompleta. Medido por coder02 en PROD 2026-08-21 (SELECT only). El indice uq_vehiculo_historial_evento existe y es valido (248 kB), pero es PARCIAL: WHERE fecha_baja IS NULL AND borrado_en IS NULL. Aplicada la misma clave a TODAS las filas - que es lo que exige un log append-only - colisiona en 4 eventos: (vehiculo_id, fecha_alta, COALESCE(fecha_baja), COALESCE(expte_alta), COALESCE(entidad_id), COALESCE(linea_id)) repetido. Los cuatro, con su procedencia: AE060XC ids 3715/7582, AE631MI 3855/7891 y PQP070 4919/8863 son cada uno UNA fila de system_import (viva) mas UNA de import-rutap-2026-08-14-prod (muerta), misma fecha_alta, misma fecha_baja, mismo expediente, misma entidad, misma linea, mismo tramite_id 318 - o sea DOS CARGADORES INSERTARON EL MISMO EVENTO. Es exactamente la advertencia de db03 sobre re-corridas de loader, pero ya ocurrida y en la base, no pronosticada. El cuarto, AD919PL ids 8933/8934, son dos filas IDENTICAS del MISMO cargador en la MISMA corrida (creado_por import-rutap-2026-08-14-prod, creado_en identico), y ademas AD919PL es uno de los 6 vehiculos con empate TOTAL de MOVILBA-73: 73 y 42 no estan solamente secuenciados, comparten testigo. CONSECUENCIA: una clave propuesta que ya tiene contraejemplos en prod no es un hueco de diseño, es un diseño REFUTADO. Antes de dropear uq_vehiculo_historial_activo hay que decidir que se hace con esas 4 colisiones (son duplicados reales a resolver, o la clave necesita otro componente) - hoy quedan tapadas porque en cada par una fila esta muerta y el indice parcial no las ve. No re-derivar: esta medido.
  • wi-cli-venus note · 2026-08-21
    CORRECCION DE LA NOTA ANTERIOR: son OCHO colisiones, no cuatro. Mi cifra de 4 fue la medicion mas debil y la retiro. Reconciliado contra la medicion independiente de db-mba-l-cc (prod 10:27:45), las dos claves corridas lado a lado en la misma sesion (prod 10:29). CLAVE QUE VALE (db-mba-l-cc): (vehiculo_id, fecha_alta, fecha_baja, tramite_id), GROUP BY ... HAVING count(*)>1 -> 8 grupos de 2 filas. CLAVE QUE RETIRO (mia): la lista de columnas de uq_vehiculo_historial_evento - (vehiculo_id, fecha_alta, COALESCE(fecha_baja,'9999-12-31'), COALESCE(expte_alta,''), COALESCE(entidad_id,-1), COALESCE(linea_id,-1)), tambien GROUP BY -> 4 grupos. LA CAUSA NO ES NULL. Se propuso que un probe por igualdad perdiera los fecha_baja NULL; NO es eso: todos mis terminos anulables van COALESCEados y use GROUP BY, no '='. La causa es la COMPOSICION de la clave: la de db lleva tramite_id y NO lleva expte_alta/entidad_id/linea_id; la mia lleva esas tres y no lleva tramite_id. Los 4 grupos que ella ve y yo no son grupos que COINCIDEN en cuando-paso y DIFIEREN en que-dice, y mis columnas de payload los parten: - AD956UK 7394/7400: misma fecha_alta 2025-12-10, misma fecha_baja 2025-12-11, mismo tramite 317, misma entidad 49, misma linea 20; expediente DISTINTO (Ex-2025-53390264 vs Ex-2025-53084543). Es el par exacto de MOVILBA-37, o sea que 37 es un tercer ticket sobre esta misma poblacion. - AE922FX 8247/9028, AE971FE 8222/8972, AE971FF 8244/8965: misma fecha_alta 2025-07-08, las dos ABIERTAS, mismo expediente, tramite_id NULL en ambas; entidad y linea DISTINTAS, y los tres pares muestran el MISMO par de valores: 41/77 contra 65/131. Las 8 filas tienen borrado_en NOT NULL, o sea que hoy ninguna es alcanzable. POR QUE IMPORTA MAS QUE EL NUMERO: esos tres pares son un vehiculo registrado en DOS entidades el mismo dia, sin fecha de cierre en ninguna de las dos. Bajo la clave de db eso es UN evento con contenido contradictorio - un duplicado. Bajo la mia son DOS eventos legitimos y distintos, porque la entidad que difiere forma parte de mi identidad. O sea: mi clave no solo encuentra menos colisiones, ACEPTARIA esas filas dentro de un log append-only y las llamaria historia. UNA CLAVE QUE INCLUYE EL PAYLOAD NO PUEDE DETECTAR UN DUPLICADO CUYO PAYLOAD DISCREPA, que es justo el modo de falla que la clave tiene que atrapar. La composicion correcta para 42 es la de db. REGLA PARA QUIEN ESCRIBA ESTO DESPUES: el ticket nombra LA CLAVE y LA FORMA DE LA CONSULTA, no solo el numero. Un conteo sin su clave es lo que costo esta vuelta entera - dos cifras sobre la misma tabla casi siempre son dos preguntas distintas.
  • wi-cli-venus note · 2026-08-21
    DOS MECANISMOS DE DUPLICACION DISTINTOS EN UNA SOLA POBLACION - no arreglar uno y cerrar. Aportado por db02 y verificado en la cuarta reproduccion independiente en PROD 2026-08-21. Los 8 pares se parten en dos clases por PROCEDENCIA, no por forma: (A) DOS CARGADORES, 3 casos - AE060XC, AE631MI, PQP070: una fila de system_import y otra de import-rutap-2026-08-14-prod. Es la carrera entre cargadores. (B) UN SOLO CARGADOR, 5 casos - AD919PL, AD956UK, AE922FX, AE971FE, AE971FF: LAS DOS filas de cada par llevan creado_por = import-rutap-2026-08-14-prod. Verificado columna por columna en los tres AE9*, las seis filas, un solo escritor. O sea que 'dos cargadores compitiendo' NUNCA explico mas que los 3 de la clase A; la clase B es un cargador insertando el mismo evento dos veces dentro de su propio alcance, y necesita su propia causa raiz. CONSECUENCIA PARA LA NOTA DE PROCEDENCIA DE db (10:28): el solapamiento entre las colisiones y los dominios que el paquete de DEV toco como flips de estado clase-A es exactamente los TRES de la clase A, no los ocho. Sigue abierto si el duplicado es causa o consecuencia de la divergencia; nadie lo convierte en afirmacion causal. Detalle de la clase B en los AE9*: cada par comparte fecha_alta 2025-07-08, fecha_baja NULL, tramite_id NULL y el MISMO expediente (EX-2025-28926906 / 28924710 / 28922576), y se parte 41/77 contra 65/131, con borrado_en 2025-07-08 contra 2025-10-22. Un mismo acto administrativo, un vehiculo, dos entidades, ninguna cerrada.
  • wi-cli-venus note · 2026-08-21
    CORRECCION DE MI PROPIA CLAUSULA DE CIERRE - premisa FALSA, conclusion CIERTA. Yo escribi 'las 8 filas llevan borrado_en NOT NULL, o sea que ninguna es alcanzable hoy'. Lo levanto db02 y lo reproduje en PROD 2026-08-21: son 16 filas (8 PARES, no 8 filas) y TRES estan VIVAS - AE060XC 3715, AE631MI 3855, PQP070 4919, con borrado_en NULL. Las otras 13 soft-deleted. Mi error: medi la liveness sobre las 8 filas de mi conjunto original de 4 pares y enuncie el resultado sobre la poblacion de 8 pares, que es otra. LA CONCLUSION SOBREVIVE POR OTRA RAZON, y es la que hay que escribir: NINGUN PAR TIENE LAS DOS MITADES VIVAS - medido, 0 grupos duplicados entre filas con borrado_en IS NULL. Cada una de esas tres es la mitad viva de un par de dos-cargadores cuya otra mitad (7582 / 7891 / 8863) esta soft-deleted, asi que un lector de datos vivos ve exactamente una fila por vehiculo y ningun duplicado es alcanzable por usuario. ENUNCIARLO ASI - 'ninguna colision tiene dos filas vivas' - y NUNCA como 'todas las filas estan borradas': lo segundo es checkeable y falso, lo primero es lo que de verdad protege al usuario. ESTRUCTURA, no coincidencia: el corte vivo/muerto coincide EXACTO con el corte de mecanismo. Los 3 vivos son los 3 casos de dos cargadores; los 5 casos de un solo cargador tienen las DOS mitades muertas. Los dos mecanismos difieren tambien en resultado: uno dejo sobreviviente, el otro enterro a los dos. NOTA DE METODO: era una afirmacion sobre NULL que nunca corri como consulta capaz de devolver la fila que la desmentia. Toda afirmacion de liveness lleva su count(*) filter (where borrado_en is null), como cualquier otra.
  • wi-cli-venus note · 2026-08-21
    PRERREQUISITO NUEVO, medido en PROD 2026-08-21 10:40 (coder02-mba-l-cc): dropear uq_vehiculo_historial_activo rompe consumidores que asumen una-sola-fila-viva SIN deduplicar. El indice es: CREATE UNIQUE INDEX uq_vehiculo_historial_activo ON vehiculo_historial (vehiculo_id) WHERE borrado_en IS NULL. No es solo una restriccion del modelo: hay vistas que se joinean a historial 1:1 apoyandose en el, sin DISTINCT ON. VERIFICADO, una: v_vehiculos_api_public (drizzle/schema.ts:1983) - el contrato publico de parque-movil (SOTAMIT), congelado. Su CTE se llama 'latest_historial' pero NO tiene DISTINCT ON, ni ORDER BY, ni GROUP BY: es SELECT vehiculo_id, estado_id, creado_en FROM vehiculo_historial WHERE borrado_en IS NULL. El nombre promete un desempate que el codigo no hace. Hoy sale bien - vehiculos con >1 fila viva = 0, la vista da 1738 filas / 1738 vehiculos distintos - y lo unico que lo sostiene es el indice. Al dropearlo, un vehiculo con dos filas vivas duplica su fila en la API publica, en silencio. SIN VERIFICAR, scoping y no hallazgo: otras 11 vistas tocan vehiculo_historial sin DISTINCT ON y comparten la exposicion. v_vehiculo_cadena, v_vehiculo_historial, v_vehiculos_detalle, v_vehiculos_export, v_vehiculos_lineas_entidades, v_db_users_footprint_detalle, v_audit_entidades_sin_vinculo, v_audit_lineas_sin_vinculo, v_audit_vehiculo_vs_linea_entidad, v_audit_vehiculos_sin_vinculo, v_audit_vinculos_a_padre_borrado. Cada una necesita su lectura; no las lei. QUE AGREGA A LA SECUENCIA: la secuencia era 73 -> 83 -> 42. Esto suma un paso DENTRO de 42, antes del DROP: auditar los 12 consumidores y darles un desempate explicito. El DROP no es el primer paso del append-only, es el ultimo de esa auditoria - hoy el indice esta haciendo de deduplicador de facto para vistas que no deduplican solas. Camino contrario descubierto: el indice de evento uq_vehiculo_historial_evento SI existe hoy en prod, sobre (vehiculo_id, fecha_alta, COALESCE(fecha_baja,'9999-12-31'), COALESCE(expte_alta,''), COALESCE(entidad_id,-1), COALESCE(linea_id,-1)) WHERE fecha_baja IS NULL AND borrado_en IS NULL - o sea, la clave CON payload que ya sabemos que no atrapa el duplicado cuyo payload discrepa (reference_a_key_that_includes_the_payload). Las 8 colisiones de la clave de db (vehiculo_id, fecha_alta, fecha_baja, tramite_id) coexisten con ese indice justamente porque el indice las deja pasar.
  • wi-cli-venus commented · 2026-08-21
    PREREQUISITO INTERNO QUE NADIE HABIA ENUNCIADO, de coder02-mba-l-cc (prod 10:40): el DROP de uq_vehiculo_historial_activo NO puede ser el primer paso del append-only. Ese indice esta actuando hoy como DEDUPLICADOR DE FACTO para vistas que no deduplican solas. Caso probado: la CTE de v_vehiculos_api_public se llama 'latest_historial' y NO deduplica - SELECT vehiculo_id, estado_id, creado_en FROM vehiculo_historial WHERE borrado_en IS NULL, sin DISTINCT ON, sin ORDER BY, sin GROUP BY. Joinea 1:1 asumiendo una sola fila viva por vehiculo. Hoy se cumple (vehiculos con >1 fila viva = 0; la vista devuelve 1738 filas / 1738 vehiculo_id distintos) y lo UNICO que lo sostiene es ese indice unico. Al dropearlo, las filas se multiplican EN EL CONTRATO PUBLICO de parque-movil/SOTAMIT, en silencio. El nombre promete un desempate que el codigo nunca hace - misma familia que el slot de creado_en, un nivel arriba. Otras ONCE vistas tocan vehiculo_historial sin DISTINCT ON y comparten la exposicion: v_vehiculo_cadena, v_vehiculo_historial, v_vehiculos_detalle, v_vehiculos_export, v_vehiculos_lineas_entidades, v_db_users_footprint_detalle y las cinco v_audit_*. coder02-mba-l-cc leyo SOLO api_public - nombrarlas es alcance, no hallazgo; cada una necesita su propia lectura. SECUENCIA CORREGIDA: 73 -> 83 -> 42 se mantiene, pero 42 gana un paso interno ANTES del DROP: auditar los 12 consumidores y darle a cada uno un desempate explicito. El DROP es el ULTIMO paso de esa auditoria, no el primero del append-only. Y el dato que cierra el circulo del round de hoy: uq_vehiculo_historial_evento YA EXISTE en prod sobre (vehiculo_id, fecha_alta, COALESCE(fecha_baja,'9999-12-31'), COALESCE(expte_alta,''), COALESCE(entidad_id,-1), COALESCE(linea_id,-1)) WHERE fecha_baja IS NULL AND borrado_en IS NULL. Es la clave CON payload, en produccion, ahora - y por eso las 8 colisiones de db-mba-l-cc conviven con un indice unico que en teoria las prohibe: las deja pasar por construccion. La auditoria de los 12 consumidores esta SIN OWNER.
  • wi-cli-venus commented · 2026-08-21
    EL DROP NO ES UN PASO A SECUENCIAR: ES UN CAMBIO DE REGIMEN DE ENFORCEMENT. Hallado por db02-mba-l-cc contra pg_indexes en prod, y corrige la razon que yo habia dado. POR QUE uq_vehiculo_historial_evento deja pasar las 8 colisiones - NO es por sus columnas de payload. Su PREDICADO es un subconjunto estricto del otro indice: uq_vehiculo_historial_activo: UNIQUE (vehiculo_id) WHERE borrado_en IS NULL uq_vehiculo_historial_evento: UNIQUE (vehiculo_id, fecha_alta, COALESCE(...)x4) WHERE fecha_baja IS NULL AND borrado_en IS NULL 'activo' ya permite como maximo UNA fila viva por vehiculo_id. 'evento' esta acotado a vivas Y abiertas - subconjunto de vivas - y su clave EMPIEZA por vehiculo_id. Entonces como maximo una fila por vehiculo puede caer bajo el predicado de evento, y un indice unico sobre un conjunto con una sola fila por valor de clave inicial NO PUEDE COLISIONAR. Medido: 1600 filas bajo el predicado de evento, 1600 vehiculo_id distintos, maximo 1 por vehiculo. O sea que uq_vehiculo_historial_evento ES INERTE EN PRODUCCION Y NUNCA PUDO DISPARAR. Seria igual de inerte con una clave perfecta, porque activo domina su predicado. Es una restriccion de unicidad que no puede dispararse - la forma recurrente de hoy, ahora en un CONSTRAINT y no en un filtro, un gate o un ORDER BY. Quinta instancia. LO QUE ESTO LE HACE A LA SECUENCIA. Dropear 'activo' hace DOS cosas a la vez y solo la primera estaba enunciada: 1. Saca el deduplicador de facto del que dependen los 12 consumidores que no deduplican solos (hallazgo de coder02-mba-l-cc, se mantiene). 2. ACTIVA 'evento' POR PRIMERA VEZ EN PRODUCCION. En cuanto activo se va, el predicado de evento deja de estar dominado y empieza a hacer cumplir la clave CON payload - exactamente la clave que coder02-mba-l-cc retiro esta manana por no poder detectar duplicados cuyo payload discrepa. El append-only quedaria policiado por la restriccion equivocada: rechazaria repeticiones de payload exacto y admitiria las de forma AE9*. Entonces 42 necesita una decision EXPLICITA sobre 'evento' en el MISMO cambio - dropearlo tambien, o re-cortarlo a (vehiculo_id, fecha_alta, fecha_baja, tramite_id) - no despues.
  • wi-cli-venus commented · 2026-08-21
    EL REGIMEN ENTRANTE ATRAPA 1 DE 4 - medido por coder02-mba-l-cc en prod 10:43, y convierte el argumento de esta manana en una cifra. Primero, coder02-mba-l-cc retira su propio mecanismo y yo el mio: uq_vehiculo_historial_evento es inerte por DOMINACION DE PREDICADO, no por llevar payload en la clave. Seria igual de inerte con una clave perfecta. Le atribuimos a la composicion de la clave lo que causa la contencion de predicados. QUE FILAS SE ACTIVARIAN. De las 16 filas en colision, 0 caen hoy bajo el predicado de evento. Ocho tienen fecha_baja IS NULL, y no son 8 sueltas de 8 pares: son CUATRO PARES COMPLETOS, vehiculos 3766, 4093, 4109, 4110 - que son ademas cuatro de los seis grupos empatados de MOVILBA-73. La poblacion de 73 y la de 42 son las mismas filas llegando por una TERCERA via. Precision de coder02-mba-l-cc sobre su propio numero: para esos cuatro vehiculos las filas con fecha_baja IS NULL son TRES cada uno, doce en total, no ocho - el ocho cuenta solo filas dentro de un par en colision, y cada vehiculo lleva ademas una fila abierta de system_import con otra fecha_alta. Hoy solo una por vehiculo es tambien borrado_en IS NULL, y eso es exactamente lo que mantiene inerte a evento. QUE HARIA EVENTO, por vehiculo, contando claves distintas entre las filas abiertas: 3766 - 3 abiertas, 2 claves distintas. Ids 8933 y 8934 son identicas en payload. evento RECHAZA. 4093 - 3 abiertas, 3 claves distintas. evento ADMITE. 4109 - 3 abiertas, 3 claves distintas. evento ADMITE. 4110 - 3 abiertas, 3 claves distintas. evento ADMITE. 25% de efectividad sobre la poblacion conocida. Las tres que admite difieren solo en entidad_id/linea_id - 41/77 contra 65/131 - con expte_alta IDENTICO dentro de cada par: son las que cualquier lector llamaria duplicados a simple vista. Y una razon de por que esto sobrevivio hasta produccion, de db-mba-l-cc: un indice unico inerte es INVISIBLE justo para la auditoria que lo cazaria, porque no tiene violaciones que mostrar ni fallas en ningun log. La unica forma de encontrarlo es comparar predicados.
  • wi-cli-venus note · 2026-08-21
    CORRECCION Y CIERRE del prerrequisito, medido en PROD 2026-08-21 10:47 (coder02-mba-l-cc, sobre el trabajo de db02-mba-l-cc y db-mba-l-cc). 1) MI MECANISMO ESTABA MAL. Dije que uq_vehiculo_historial_evento deja pasar las colisiones PORQUE su clave lleva payload. Es por DOMINACION DE PREDICADO: su predicado (fecha_baja IS NULL AND borrado_en IS NULL) es subconjunto estricto del de activo (borrado_en IS NULL), y su clave empieza por vehiculo_id, que activo ya hace unico sobre un superconjunto. Verificado: 1600 filas bajo su predicado, 1600 vehiculo_id distintos, maximo 1 por vehiculo. NUNCA TUVO DOS FILAS QUE COMPARAR y seria igual de inerte con una clave perfecta. 2) DROPEAR activo ES UN CAMBIO DE REGIMEN DE ENFORCEMENT, no solo una perdida de deduplicacion. Al irse activo, evento deja de estar dominado y empieza a policiar - con la clave que lleva payload. Medido sobre la poblacion conocida: de las 16 filas de colision, 8 tienen fecha_baja IS NULL y forman CUATRO PARES COMPLETOS (vehiculos 3766, 4093, 4109, 4110 - que son ademas cuatro de los seis grupos empatados de MOVILBA-73). Claves de evento distintas entre las filas abiertas de cada vehiculo: 3766 -> 2 de 3: ids 8933/8934 son identicos en payload, evento RECHAZA. 4093 -> 3 de 3: evento ADMITE. 4109 -> 3 de 3: evento ADMITE. 4110 -> 3 de 3: evento ADMITE. ATRAPA 1 DE 4, ADMITE 3 DE 4. Las tres que admite difieren solo en entidad_id/linea_id (41/77 contra 65/131) con expte_alta identico dentro del par: exactamente los que cualquiera llamaria duplicados a simple vista. 3) LA EXPOSICION SON 3 VISTAS, NO 12. Mi lista estaba mal construida: grepee las definiciones por 'vehiculo_historial' Y '!~ DISTINCT ON', y publique el resultado como 'vistas que tocan historial'. El filtro ya habia sacado a la consumidora que SI cumple, asi que 12 nunca fue el numero de consumidores - era el numero de sobrevivientes de mi filtro, y el lector siguiente no podia chequear completitud. Por pg_depend son 13. Clasificacion de db-mba-l-cc, verificada por mi: DEDUPLICAN (4): v_vehiculos_hist_ultimo (DISTINCT ON), v_vehiculos_export y v_db_users_footprint_detalle (GROUP BY), v_vehiculo_cadena (window). SEMI-JOIN (3): v_audit_entidades_sin_vinculo, v_audit_lineas_sin_vinculo, v_audit_vehiculos_sin_vinculo - llegan por EXISTS, verificado. Un existencial ABSORBE la multiplicacion. MULTI-FILA LEGITIMA (1): v_vehiculo_historial, es un listado de historial. EXPUESTAS (3): v_vehiculos_api_public, v_vehiculos_detalle, v_vehiculos_lineas_entidades - las tres dan 1738 filas / 1738 vehiculos distintos hoy, sostenido solo por el indice. SIN RESOLVER (1): v_audit_vehiculo_vs_linea_entidad devuelve CERO filas, y cero es el valor sano de una vista de excepciones. Necesita control positivo antes de contarla limpia. 4) 42 NECESITA CUATRO COSAS EN UN SOLO CAMBIO: (a) desempate explicito en las 3 vistas expuestas; (b) decision sobre evento - dropearlo, o re-cortarlo a (vehiculo_id, fecha_alta, fecha_baja, tramite_id); (c) el control positivo sobre v_audit_vehiculo_vs_linea_entidad; (d) recien ahi el DROP de activo. El DROP es el ULTIMO paso. db-mba-l-cc tomo la auditoria de consumidores. Nada aplicado, solo SELECT.
  • wi-cli-venus parentChanged · 7w ago
    parent=MOVILBA-125
  • wi-cli-venus note · 7w ago
    App workflow scope measured 2026-08-26 after append-only decision: remove the soft-close UPDATEs in registrarExpediente (src/actions/vehiculos.ts), createHistorialRecord and updateEstado (src/repositories/vehiculos.ts), and replace their raw one-live-row reads. Also move shared vehiculoRepository.getCurrentHistorial (consumed by assignToEntidad/assignToLinea) from raw borrado_en IS NULL + LIMIT 1 to v_vehiculos_hist_ultimo. All four must wait for unified DEV migration and generated schema; db03 owns selector/view DDL.
  • wi-cli-venus note · 7w ago
    Scope correction from strict app audit: src/repositories/parque-movil.ts:129-133 is a fifth current-row consumer (lateral raw vehiculo_historial WHERE borrado_en IS NULL ORDER BY id DESC LIMIT 1). Move it to v_vehiculos_hist_ultimo with the 3 soft-close writers and getCurrentHistorial. The entidad/linea history endpoints are legitimate multi-event lists and remain unchanged.
  • wi-cli-venus note · 7w ago
    Parque Movil implementation detail: it publishes observaciones, not appended to the canonical selector. Use v_vehiculos_hist_ultimo.historial_id as the deterministic selector and join back to vehiculo_historial by PK only to read observaciones; do not recreate a live-history ORDER BY/LIMIT rule.
  • wi-cli-venus note · 7w ago
    2026-08-26 status: corrected DEV rollback rehearsal PASS under Snapshot 6 after audit PASS. All view/index/output gates passed; no persistent DB changes. Awaiting separate DEV apply GO, then DB03 apply handoff -> coder02 runs introspect.sh -> app patch for three append-only writers plus getCurrentHistorial and Parque Movil selector query.
  • wi-cli-venus assigned · 7w ago
    coder02-mba-l-ca
  • wi-cli-venus statusChanged · 7w ago
    DEV rehearsal PASS under Snapshot 6, DEV apply GO issued
  • wi-cli-venus note · 7w ago
    DEV append-only migration is applied by db03. App integration patched: current reads use v_vehiculos_hist_ultimo and history writers append without soft-closing; Parque Móvil joins selector via historial_id. Drizzle schema/relations regenerated. Targeted ESLint, full typecheck, and diff-check PASS. Full audit review requested; awaiting audit-mba-l-cc reply before commit.
  • wi-cli-venus statusChanged · 7w ago
    Audit-PASS fix for the fecha_alta selector blocker is ready for DEV code deployment: all five app insertion paths now write fechaAlta (fechaExpediente or CURRENT_DATE fallback). DEV DDL remains applied; no migration re-apply is required. Next: coder deploys the reviewed app change, DB03 verifies live selector behavior, then audit.
  • wi-cli-venus note · 7w ago
    Audit PASS after fecha_alta correction. Committed app integration as 5b31b5f5 (actions/vehiculos.ts, repositories/vehiculos.ts, repositories/parque-movil.ts); unrelated vdcJoin predicate edit excluded. Release handoff sent to coder-mba-l-ca; waiting only for clean schema-delta/release-checkout coordination before DEV deployment, then db03 live selector verification and audit deployed-diff check.
  • wi-cli-venus note · 7w ago
    Release identified the generated schema dependency: 5b31b5f5 imports expanded selector columns absent from v5.4.53 schema. Canonical generator output committed as follow-up 4a5aacac (drizzle/schema.ts only; relations.ts deliberately excluded). Full audit confirmation requested; release coder will reconstruct/deploy exact approved files after PASS, then DB03 live selector verification and audit deployed-diff check.
  • wi-cli-venus note · 7w ago
    Both commits audit PASS: 5b31b5f5 app paths and 4a5aacac drizzle/schema.ts only. Exact DEV release handoff resent to registered coder-mba-l-ca; deployment still unconfirmed and deployed SHA requested. DB03 and audit are queued for immediate live-selector/deployed-diff verification. PROD held.
  • wi-cli-venus verified · 7w ago
    Audit-PASS app selector integration deployed to Dev v5.4.54 / 8d75d0d3. Dev/origin refs match; health serviceId 8309 confirms patch 54. DB03 live selector and audit deployed-diff verification requested; PROD remains held.
  • wi-cli-venus note · 7w ago
    DEV deployment confirmed by release coder. DB03 live append-only selector verification and audit deployed-diff verification explicitly requested now. PROD remains held until both report PASS.
  • wi-cli-venus progress · 7w ago
    DEV app release v5.4.54 / 8d75d0d3 is live. Audit deployed-diff PASS: audited selector rewire and fechaAlta insert fixes match live code; five recent stamped history rows correctly win their selector slots. Remaining pre-PROD verification: DB03's single isolated app-write selector test, awaiting PM authorization. PROD untouched.
  • wi-cli-venus audit-finding · 7w ago
    Audit deployed-diff PASS for Dev v5.4.54 / 8d75d0d3: selector rewire and fechaAlta on all insert sites match reviewed content; no unreviewed change shipped. Remaining gate before PROD decision: DB03 isolated app-write selector verification.
  • wi-cli-venus note · 7w ago
    DEV deployed-diff audit PASS. Sole remaining pre-PROD gate: DB03 isolated app-write selector test, awaiting explicit PM GO; DB03 will not write before authorization. PROD held.
  • wi-cli-venus note · 7w ago
    PM narrow DEV GO confirmed (mta35hshyv36). DB03 instructed to run the authorized safe app-write selector test with cleanup and report PASS/BLOCK. PROD remains held pending that result.
  • wi-cli-venus verified · 7w ago
    Final PM-authorized isolated DEV app-write selector test PASS: test vehicle 6067/ZZ001ZZ appended historial 10780 with fecha_alta 2026-08-26; v_vehiculos_hist_ultimo selected it immediately. Cleanup soft-deleted only 10780 and selector reverted to 9654/2026-03-17; audit independently verified zero residue. Entire M42 chain is audit-PASS; PROD decision pending PM.
  • wi-cli-venus note · 7w ago
    Final DEV gate PASS: DB03 authorized app-write selector test selected appended event immediately; cleanup reverted selector and left zero residue. Audit independently PASSed full selector/docs/app/deployed-diff/live-test chain. PM explicitly asked for PROD GO/BLOCK; PROD remains untouched pending that decision.
  • wi-cli-venus progress · 7w ago
    Full DEV chain PASS: v5.4.54/8d75d0d3 deployed; isolated PM-authorized app-write test on ZZ001ZZ/vehiculo 6067 inserted historial 10780 with fecha_alta 2026-08-26 and selector immediately chose it; cleanup soft-deleted only 10780 and selector reverted to 9654, independently audit-verified with zero residue. DEV selector/docs/app/deployed-diff/live-write chain is complete. PROD remains untouched and requires a fresh scope-specific GO.
  • wi-cli-venus progress · 7w ago
    PM issued explicit PROD GO (hub tag mta4hy7qywgq) covering the MOVILBA-42 app/schema production deploy and DB03's audited DDL migration. Audit-PASS MOVILBA-135 diagnostics may ride with branch promotion. MOVILBA-128 PROD app is excluded. Coder/DB03 instructed to coordinate order, acquire exact hub locks, execute, and return production gates.
  • wi-cli-venus note · 7w ago
    PM explicit PROD GO received for audited M42 DDL plus app/schema promotion. M135 audit-PASS diagnostics allowed with branch; M128 Validadoras PROD app explicitly excluded/held. Execution sequenced: DB03 PROD DDL and post-gates first, coder deploy after DB PASS, then audit production verification.
  • wi-cli-venus progress · 7w ago
    PROD DDL PASS/APPLIED by DB03 under explicit GO. Artifact SHA256 ca8a91d5c843c0970186f8fd548b5d1b745e484c76c454919bf23c5ccb408571. First insufficient-role CREATE VIEW attempt fully rolled back; postgres rerun committed. Post-gates: Snapshot6 5,258; M112 scope 648 vehicles/1,037 closed rows/12 quarantined chains; selector 1,740 unique; API/list/export 2,388 unique; zero selector duplicates; both old history unique indexes absent. Lock released. Coder released for production app/schema deploy; audit pending final live verification.
  • wi-cli-venus note · 7w ago
    PROD M42 DDL applied by DB03 with PASS post-gates: canonical selector and API/list/export uniqueness pass, legacy indexes absent, lock released. Release coder proceeding with exact approved app/schema deploy; audit verifying PROD DB and then live deployed diff. M128 excluded.
  • wi-cli-venus note · 7w ago
    PROD app deploy BLOCKED after DB DDL PASS: canonical gitdeployprod is indivisible dev-to-main and current delta includes M128 commit 8dbdcd6d/app paths, while PM GO explicitly excludes M128. No selective/cherry-pick bypass. PM asked to authorize M128 carryover, authorize audited removal from dev, or hold M42 app. PROD DB DDL remains applied/PASS.
  • wi-cli-venus blocked · 7w ago
    PROD DB DDL is applied/PASS, but app promotion is blocked by scope contradiction: canonical dev→main is indivisible and includes MOVILBA-128 commit 8dbdcd6d/files, while PM GO explicitly excluded M128. Coder correctly refused a selective/cherry-pick bypass. PM ruling requested: expand GO to M128 or hold M42 app until canonical branch reconciliation. No further PROD action.
  • wi-cli-venus unblocked · 7w ago
    PM ruling A (hub tag mta587qhglgs): PROD GO expanded to the full canonical dev→main promotion containing MOVILBA-42 app/schema, MOVILBA-128 app, and MOVILBA-135 diagnostics. Branch-scope contradiction cleared. Coder released for canonical production deploy; M42 DB DDL already applied/PASS and must not be reapplied.
  • wi-cli-venus note · 7w ago
    PM ruling A expanded PROD GO to full canonical dev-to-main promotion: M42 app/schema + M128 app + M135 diagnostics. Branch blocker cleared. Release coder instructed to run canonical gitdeployprod; audit queued for live verification. M42 PROD DDL already applied/PASS and will not be reapplied.
  • wi-cli-venus progress · 7w ago
    Canonical PROD deployment completed after DB DDL PASS: gitdeployprod exit 0 and typecheck PASS; merge df7dd5ef75784011fd0ab060ffe17ab0fc67f5fc from base e766da8d with DEV parent 8d75d0d3; merge tree equals DEV tree; lezama/main and origin/main agree; live /api/health status ok, serviceId 8309 (v5.4.54). Independent live audit requested.
  • wi-cli-venus note · 7w ago
    Canonical PROD gitdeployprod completed successfully; main/origin synchronized, approved DEV tree promoted, typecheck and live health PASS. Final independent audit verification requested for M42/M128/M135 live surfaces and full deployed diff. M42 DDL remains applied/PASS; no reapply.
  • wi-cli-venus verified · 7w ago
    PROD independent audit PASS at merge df7dd5ef / v5.4.54 / serviceId 8309. Audit confirmed deployed diff, origin/main, snapshot6=5258, retired indexes absent, and zero selector duplicates; DB03 post-deploy reconfirmed selector 1740/1740 and API/list/export 2388/2388 with zero NULL fecha_alta. Authenticated UI walk remains outside this deployment gate.
  • wi-cli-venus verified · 7w ago
    PROD full-chain PASS at merge df7dd5ef75784011fd0ab060ffe17ab0fc67f5fc / v5.4.54 / serviceId 8309. Canonical gitdeployprod exit 0 and refs synchronized. DB03 post-deploy: selector 1,740/1,740 distinct; api/list/export 2,388/2,388; zero selector duplicates; retired indexes absent; zero live vehiculo_historial rows with fecha_alta NULL. Independent audit PASS recorded. No DDL reapply.
  • wi-cli-venus note · 7w ago
    PROD release chain complete: canonical deploy live, independent audit PASS, DB03 post-deploy DB PASS; selector/API/list/export uniqueness pass, zero duplicates, retired indexes absent, zero live NULL fecha_alta. No further M42 PROD action or DDL reapply. Separate authenticated refresh reproduction remains tracked under M137.
  • wi-cli-venus assigned · 7w ago
    coder02-mba-l-cc / coder
  • wi-cli-venus note · 7w ago
    MOVILBA-114 audit output RECIBIDO y es PASS (db03-mba-l-ca, 2026-08-28 10:33/10:34). Control codificado en docs/migrations/2026-08-26-movilba-42-114-112-append-only-history-DEV-PROD.sql: gates de igualdad antes/despues sobre api_public, detalle, lineas_entidades, audit, list, export; gates de una-fila-por-vehiculo para selector y API; gate estructural pg_depend de que la vista de audit ata al selector. Verificacion post-estado fresca read-only PASS en movilba_dev Y movilba: legacy history indexes=0, selector duplicate vehicles=0, API duplicate vehicles=0, v_audit_vehiculo_vs_linea_entidad ata v_vehiculos_hist_ultimo=true, v_vehiculos_api_public ata selector=true. Grafo de alcance directo 24 dependientes; v_vehiculos_detalle seguro via selector agregado/DISTINCT; v_vehiculos_api_public era el camino expuesto y fue reescrito al selector. La dependencia que bloqueaba el cierre de 42 queda satisfecha.
  • wi-cli-venus completed · 7w ago
    Cierra con la ultima dependencia satisfecha: MOVILBA-114 audit output = PASS en dev y prod (db03, 2026-08-28). El refactor append-only ya estaba aplicado y deployado a PROD el 2026-08-26 (merge df7dd5ef / v5.4.54 / serviceId 8309) con audit independiente PASS y verificacion post-deploy de db03 PASS: selector 1740/1740 distintos, api/list/export 2388/2388, cero duplicados de selector, indices retirados ausentes, cero filas vivas con fecha_alta NULL. NOTA: la descripcion del item quedo desactualizada - dice 'PENDIENTE DE ELAZAR: si acepta esta clave', pero la clave fue aceptada, implementada, deployada y auditada; el log de eventos manda sobre la descripcion. No queda accion de PROD ni reaplicacion de DDL. La reproduccion del refresh autenticado sigue trackeada aparte en MOVILBA-137.
task
2026-08-14 by wi-cli-venus
7w ago
2026-08-28 13:37