El camino de import no escribe estado_id en la capa normalizada: 16126 de 16126 filas origen=import lo tienen NULL
El resolver devuelve estado NULL para 375 vehiculos IMPORTADOS, y no es MOVILBA-331. La causa es el camino de IMPORT, que no puebla estado_id en la capa normalizada. POBLACION, medida por db02-mba-l-cc el 2026-09-07 ~16:56 sobre DEV: **378 vehiculos** con v_vehiculos_hist_ultimo.estado_id NULL, periodo abierto, y estado_actual_id NO NULL en la ficha. Desglosados por `origen` de la fila rank-1 detras de cada uno: - `import`: **375** - `app`: **2** - sin fila en rutap.vehiculo_historial_normalizado: **1** Techo independiente del lado de la app: **5 vehiculos distintos** tienen alguna fila `origen='app'` no borrada en esa tabla, en toda su historia. O sea que como maximo 5 vehiculos pasaron por el formulario en DEV. CORRECCION DE ATRIBUCION, y el error es mio: yo lei el 378 de db02 y le puse la causa "todo vehiculo alguna vez editado por el formulario". Falso. El conteo era de db02, el mecanismo lo invente yo, y los dos numeros -378 y 5- no se parecen. El radio de MOVILBA-331 en DEV es de **5 vehiculos**, no 378: el defecto de arrastre solo podia dispararse sobre una fila que hubiera escrito el formulario. MECANISMO REAL de los 375: el camino de import no popula estado_id en la capa normalizada. Emparentado con reference_estado_id_motivo_id_not_book_backed_loader_excludes_2026_09_04 - estado_id no tiene encabezado en el libro RUTAP y el loader lo excluye. NO es daño nuevo de MOVILBA-315: la vista vieja tampoco tenia estado para estas filas, asi que 315 no lo introdujo ni lo empeoro. POR QUE IMPORTA: 315 ya esta aplicado, asi que la lectura resuelve por la capa normalizada y estos 375 resuelven estado NULL AHORA. No se ve en pantalla porque la ficha lee otra columna -estado_actual_id en v_vehiculos_detalle, que NO es estado_id- pero cualquier consumidor que lea el estado por el historial ve NULL. Por la regla del custodio fiel, una diferencia entre lo que dice el sistema y lo que dice el libro cuenta igual sea vigente o baja. DECIDIR ANTES DE ESCRIBIR, y no inventar: - Si el libro RUTAP tiene el estado por periodo, sale de ahi. Si no lo tiene, hay que decidir si se deriva de la ficha, y la ficha es el estado de HOY: escribirlo en un periodo historico es afirmar algo que no se midio. Puede que lo correcto sea reparar SOLO el periodo abierto y documentar el hueco de los cerrados. - La reparacion es una ESCRITURA en DEV: pide su propio GO y su propio antes/despues. No viaja de prestado en la verificacion de 331. CONTROL POSITIVO OBLIGATORIO: derivado y computado ANTES del cambio, nunca una lista literal de dominios. Dos de los sujetos "obvios" ya estan dañados por el defecto y un control armado con ellos no puede pasar - la trampa que costo una corrida en 315. NO USAR 6720 ZZ999XX: es el registro de daño de 315/331. CIFRAS: 378, 375 y 5 son de esta noche y de DEV. Se re-miden antes de usarse. La poblacion ademas se achica sola cada vez que alguien edita por el formulario ya arreglado.
Activity
-
was: MOVILBA-331 (59a48dce + 028a00d0) frena que se sigan escribiendo periodos sin estado_id, pero NO toca los que ya estan. Este item es la reparacion de DATOS, que es una escritura y necesita su propio GO. POBLACION, medida por db02-mba-l-cc el 2026-09-07 ~16:54 sobre DEV: **378 vehiculos** con v_vehiculos_hist_ultimo.estado_id NULL, periodo abierto, y estado_actual_id NO NULL en la ficha. Esa cifra es de ESA fecha y ESE entorno: se re-mide antes de usarla, no se recita. Ademas es una poblacion que se achica sola - cada edicion por el formulario ya arreglado saca un vehiculo de la lista, que es justamente el punto. POR QUE IMPORTA HOY y no despues: MOVILBA-315 ya esta aplicado, asi que la lectura resuelve por la capa normalizada. Los 378 resuelven estado NULL AHORA MISMO. No se ve en pantalla solo porque la ficha lee otra columna -estado_actual_id en v_vehiculos_detalle, que no es estado_id- asi que cualquier consumidor que lea el estado por el historial ve NULL. Es una diferencia real entre lo que dice el sistema y lo que dice el libro, y por la regla del custodio fiel cuenta igual que cualquier otra. LO QUE UNA EDICION HACE Y LO QUE NO: una edicion por el formulario arreglado escribe el valor de la ficha HACIA ADELANTE, en el periodo sucesor. NO repara el periodo cerrado que nacio sin estado. O sea que el historial de esos 378 queda con un agujero permanente aunque el vehiculo se edite - se arregla el presente, no el registro. DECIDIR ANTES DE ESCRIBIR, y no inventar: - de donde sale el valor: la ficha (estado_actual_id) es lo unico disponible, pero la ficha es el estado de HOY y estos periodos pueden ser viejos. Escribir el estado actual en un periodo historico es afirmar algo que no se midio. Puede que la respuesta correcta sea reparar SOLO el periodo abierto -el que efectivamente describe el presente- y dejar los cerrados como estan, documentando el hueco. - si el libro RUTAP tiene el estado por periodo, sale de ahi y no de la ficha. CONTROL POSITIVO OBLIGATORIO: derivado, computado ANTES del cambio, nunca una lista literal de dominios - dos de los sujetos "obvios" ya estan dañados por el propio defecto y un control armado con ellos no puede pasar. Misma trampa que costo una corrida en 315. NO USAR 6720 ZZ999XX: es el registro de daño de 315/331. Depende de: MOVILBA-331 (el codigo tiene que estar arreglado primero, si no la reparacion se vuelve a llenar).
-
El resolver devuelve estado NULL para 375 vehiculos importados: el import no popula estado_id en la capa normalizada
-
El camino de import no escribe estado_id en la capa normalizada: 16126 de 16126 filas origen=import lo tienen NULL
-
2026-09-07 17:00 - MECANISMO CONFIRMADO EN LA FUENTE, y es CATEGORICO, no una muestra. Medido por db02-mba-l-cc sobre DEV, rutap.vehiculo_historial_normalizado, filas no borradas: - origen='import': estado_id NULL en **16126 de 16126**. CERO excepciones. El import no popula esa columna en absoluto - no 'casi siempre', no 'para estos 375': la propiedad es de la COLUMNA ENTERA. - origen='app': 10 filas, 4 con estado, 6 sin. Esas 6 son la huella real de MOVILBA-331 a nivel fila, consistente con el techo de 5 vehiculos. - public.vehiculo_historial (capa vieja): 5491 filas no borradas, 1530 con estado. O sea que la tabla vieja SI trae estado para un subconjunto, y por eso algunos vehiculos todavia resuelven uno y las dos capas se contradicen. CORRECCION DE ENCUADRE, hallazgo de db02: el denominador de este item son **16126 filas**, no 375 vehiculos. Titularlo por los 375 lo SUBESTIMA y ademas se va a leer como arreglado en cuanto esos 375 particulares cambien, sin que el loader haya cambiado nada. Por eso el titulo ahora nombra la INVARIANTE -el import no escribe estado_id- y el conteo se re-deriva. CRITERIO DE ACEPTACION FALSIFICABLE, y no necesita navegador: despues del arreglo, count(estado_id) sobre origen='import' tiene que ser NO CERO. Hoy es CERO de forma demostrable, asi que no puede pasar en vacio. Es el control positivo derivado que 315 tuvo que aprender a construir. Todas las cifras -16126, 10/4/6, 5491/1530, 5- son de esta noche, de DEV, y re-derivables. Ninguna se congela en este item.
-
2026-09-07 17:03 - CORRECCION DEL CRITERIO DE ACEPTACION. Hallazgo de audit-mba-l-cc, y anula lo que escribi en el evento anterior. 'count(estado_id) NO CERO' sirve como FALSIFICADOR del estado actual -hoy es cero de forma demostrable, no puede pasar en vacio- pero es DEMASIADO DEBIL como condicion de aprobacion: UNA sola fila poblada de 16126 lo satisface. El titulo de este item afirma una invariante universal y el test tiene que tener la misma forma. CRITERIO CORRECTO, acotado a la corrida: despues del re-import, count(*) FILTER (WHERE estado_id IS NULL) = 0 **sobre las filas que ESA corrida escribio**, no sobre la tabla entera. El alcance importa por dos razones: un backfill parcial mas historia sin tocar se leeria como fallado para siempre, y una invariante nueva se afirma sobre lo que la corrida toco. SEGUNDO, y define el arreglo: esto se arregla en el LOADER, y el loader excluye los campos que no tienen encabezado en el libro RUTAP - estado_id es uno de ellos (ver reference_estado_id_motivo_id_not_book_backed_loader_excludes_2026_09_04). O sea que lo que llegue a poblar estado_id va a estar DERIVANDO un valor, no copiandolo. Y un conteo de no-nulos NO distingue una derivacion correcta de una constante: si el loader escribe 116 en las 16126 filas, el test de arriba pasa perfecto y el dato es basura. POR ESO, junto al conteo va un PAR DE VALORES: unos cuantos dominios con el valor del libro al lado del estado_id resultante, para que se pueda auditar la derivacion y no solo su no-nulidad. Es la regla de reference_un_conteo_solo_es_infalsificable_para_el_que_lo_lee_van_los_pares_de_valores. QUEDA POR DECIDIR ANTES DE ESCRIBIR NADA: cual es la regla de derivacion, y de donde sale. No se inventa.
-
2026-09-08 00:05 - COMO SE DERIVA HOY EL ESTADO DE LA FICHA Y SI ES REPRODUCIBLE POR PERIODO. Medido por coder02-mba-l-cc sobre DEV (PG 15.17); codigo en origin/dev. 1. estado_actual_id NO es columna: v_vehiculos_detalle la define como vdc.estado_id (pg_get_viewdef). La ficha es vehiculos_datos_colectivos.estado_id. 2. Quien la escribe: tools/rutap-sync-prod.sql:745-769. Regla POR DOMINIO, no por periodo: dominio con alguna fila abierta en el libro (Fecha_Baja vacia) -> ALTA (116); sin ninguna abierta -> BAJA (117), con expte_baja de la ultima cerrada. Es la misma regla que el gate post-import de rutap-snapshot-import.py:30-33. 3. El libro NO tiene tramite Baja: tramite es Alta (317) o Modif (318) unicamente; cerrado x tramite en import: Alta/cerrada 6768, Modif/cerrada 4554, Modif/abierta 4082, Alta/abierta 706, sin tramite 16. La baja se codifica SOLO por fecha_baja en el ultimo periodo del dominio. 4. Regla por periodo que reproduce la ficha: sobre la fila que v_vehiculos_hist_ultimo elige, estado = BAJA si fecha_baja no es null, si no ALTA -> 0 desacuerdos sobre 2405 vehiculos con fila import y ficha en 116/117. La regla 'BAJA solo si el tramite es Baja' da 805/2405 de desacuerdo (porque no existe ese tramite). 5. Por periodo, con lead() por vehiculo: 808 cerradas terminales (= baja), 1599 abiertas terminales (= alta vigente), 10514 cerradas con sucesor (el vehiculo siguio vivo: la fecha_baja de esas es un cierre por modificacion, NO una baja), 3205 ABIERTAS con sucesor (anomalia del libro: doble fila abierta, ya conocida). Para una fila cerrada con sucesor 'estado en ese periodo' = ALTA es defendible; el header del loader (rutap-snapshot-import.py:35-37) dice explicitamente NO backfillear porque estado_id es una foto y el libro no la da. 6. CONSUMIDORES de v_vehiculos_hist_ultimo.estado_id en origin/dev src: NINGUNO. parque-movil.ts:131 lee entidad_id/linea_id/expte_alta de la vista y el estado de vdc.estado_id. liquidacion.ts:86 y editar/colectivo/page.tsx:341 solo la citan. El NULL no llega a ninguna pantalla ni export hoy; es fidelidad del registro, no un bug visible. Denominadores: 16126 filas import; 2405 vehiculos con ultima fila import y vdc en 116/117. Se re-miden antes de usarse.
-
2026-09-08 00:15 - SINTESIS DEL BLACKBOARD bb-coder02-mba-l-cc-mts23v5o-s05pb6 (coder-mba-l-ca, coder03-mba-l-cc, db02-mba-l-cc, db03-mba-l-ca; todos midieron DEV y PROD, SELECT-only). Elevado a Elazar en CLI. ACUERDO DE LOS CUATRO (338): el escritor de la app abre el periodo desde la FICHA post-update en la misma transaccion (vehiculos + vehiculos_datos_colectivos + carrocerias para piso/aire + entidades/lineas/fabricantes por FK); raw_* (39) y las 4 de procedencia RUTAP quedan NULL a proposito (no hubo libro); nunca copiar de un predecesor app; sin fuente estructurada: carroceria_categoria, tipo_motor, motivo_id. PROD: 0 filas origen='app' (5388 import) -> nada que reparar en prod. DEV: 11 filas app, 3 con ancestro import (AA033TK 1, AE801OE 2), 8 nacidas angostas (ZZ999XX 3, ZZ998YY, ZZ997WW, FB444EP 3); son datos de prueba, no se backfillean como hecho historico (coder-ca, db03). LA UNICA BIFURCACION DE DISEÑO, para Elazar: los gemelos de etiqueta (cuit, linea_numero, tramite, motor_fabricante, carroceria_fabricante...). Medido por coder03: cuit en import = entidades.cuit en 15706/15706, o sea etiqueta congelada de un FK que ya esta en la fila. coder03: NO escribirlas en filas app (feedback_nunca_congelar_la_etiqueta) y hacer que el diff resuelva por id. db02/db03/coder-ca y coder02: SI escribirlas, resueltas por FK al momento de escribir, porque el historial ES el libro y el libro las lleva; el diff y la reconciliacion comparan periodo contra periodo. Recomendacion elevada: escribirlas. ACUERDO DE LOS CUATRO (333): derivar SOLO filas abiertas del ULTIMO snapshot (h.rutap_snapshot_id = max(rutap.snapshots.id)); sin ese filtro coder03 midio 4 filas / 3 vehiculos (AD631QS, AF023VE x2, AE352LD) con ficha BAJA que recibirian ALTA. Forma: SET estado_id = dc.estado_id FROM vehiculos_datos_colectivos dc (db03/coder03), no literal 116; hoy da lo mismo (db02: vdc.estado_id no borrado toma solo 116 x1608 y 117 x808; abiertas ultimo snapshot 1600/1600 ALTA), pero no deriva si aparece un tercer estado. Cerradas quedan NULL y se documenta (una cerrada es modificacion, no baja: solo existen tramites Alta/Modif). El loader aplica la misma regla por snapshot. Control positivo ANTES del write: count candidatas; count FILTER (dc.estado_id <> ALTA via v_opciones) = 0; el predicado SIN filtro de snapshot tiene que mostrar las 4. Sin consumidor en src de v_vehiculos_hist_ultimo.estado_id (parque-movil.ts lee vdc.estado_id). RETIRADO: la afirmacion de coder03 de 4 vehiculos MANTENIMIENTO/TRANSFERENCIA con fila abierta - los 4 (5186, 5187, 5216, 5217) tienen cero filas en la tabla normalizada; retirado por coder03 mismo en DM. ESPERANDO decision de Elazar: A) escribir gemelos si/no; B) GO para el UPDATE en DEV (carril db ejecuta con los controles).
-
db02-mba-l-cc / db
-
Elazar GO 2026-09-08 00:20 ART (CLI): UPDATE en DEV sobre abiertas del ultimo snapshot, estado desde la ficha; ejecuta db02-mba-l-cc con los 4 gates de la sintesis. Regla del loader = follow-up.
-
2026-09-08 00:06 - Prerrequisito resuelto: MOVILBA-59 aplicado en DEV por db03 (ver evento en MOVILBA-59). Sigue: db02 rehearsal + import --apply del snapshot 2026-09-07-2350, despues el UPDATE de 333 con conteos re-derivados.
-
DEV apply 2026-09-08 00:09 ART by db02-mba-l-cc, movilba_user@movilba_dev 10.9.10.54, PostgreSQL 15.17/150017. File docs/migrations/2026-09-08-movilba-333-estado-id-periodos-abiertos-import-DEV.sql sha256 2fee8f3be69394a6860a71f94611f63afc7aa36af4377c10ca269f9af17d3a55 (uncommitted, coder02 commits). Decision A (coder02 relay 00:08): ran against snapshot 11, the snapshot the historial carries; the 2350 workbook was imported AFTER as rutap.* only (snapshot 12, md5 33fd410d…, 5388 Base + 47 Hoja2, rutap_expedientes first populated), historial still on 11 because no per-snapshot projector exists (coder02 files that item). BEFORE: import rows with estado_id 0 of 16126. Gates: ALTA=1 id via v_estado_colectivo; 1600 candidates (open, snapshot 11, estado_id NULL), 0 with ficha not ALTA; negative control without snapshot filter = 4 open rows with ficha 117 (AD631QS/snap9, AE352LD/snap9, AF023VE/snap9+10) - excluded by the filter, proves gate 1 can fail. UPDATE 1600, ids 75182..80567 (list in /data-lezama/movilba/movilba-333-dev-apply-2026-09-08.log = rollback key). AFTER: import rows with estado_id 1600 of 16126, all on open snapshot-11 rows; closed import periods with estado_id = 0; app rows untouched (7 of 13). Resolver residual (v_vehiculos_hist_ultimo open+NULL with ficha set): 2 - vehiculo 6720 ZZ999XX (315/331 evidence, not repaired by rule) and 6067 ZZ001ZZ (no normalizado rows, 9 legacy rows only). NOT exercised: closed periods (by design, stay NULL), snapshot 12 (not projected), PROD (untouched). One-run file: a second run aborts at gate 3 by design.
-
00:09-00:15 ART, DEV. Step 2 of decision A (raw --apply of the 2350 workbook, snapshot 12) COMMITTED before coder02's REVISED stop arrived (00:10). Measured regression: every reader filters rutap_snapshot_id = max(rutap.snapshots.id) (v_vehiculos_hist_ultimo, rutap.v_vehiculo_historial_actual, vehiculos.ts:875/930/1044, rutap-historial.ts:114, entidad/linea history routes); snapshot 12 has 0 projected historial rows, so resolver residual (open, estado NULL, ficha set) went 2 -> 376: the app sees no import period on dev01. Nothing else changed; the 333 UPDATE on snapshot 11 is intact. Revert prepared: docs/migrations/2026-09-08-movilba-333-revert-snapshot12-crudo-DEV.sql sha256 de98aa7ec51a304505458b4a06dacdc691bb66660cfc18e71e0019ed05a321d0, audit PASS (bytes) 00:15; three DELETEs children-first (FKs onto snapshots are NO ACTION; the only cascade is vh_norm_raw_fk historial->rutap_rows), gates: projected(12)=0 before, historial total unchanged after, residual back to 2. Row dump before at /data-lezama/movilba/snapshot12-revert-2026-09-08/ (1/5388/47, mode 600). Awaiting Elazar GO via coder02. Projector gap = MOVILBA-269 (db03). Cause on my side: executed step 2 without measuring the max(snapshot) consumers first.
-
00:32 ART: snapshot 12 projected on DEV via rutap.proyectar_snapshot (MOVILBA-269, sha 63693859), which applies the 333 rule in the same run: 1602 open rows of 12 carry estado_id = ficha (ALTA), closed 0. Regression from 00:09 closed: resolver residual 376 -> [6720, 6067]. Revert file 1ec5cbf8 not run; stays as fallback. Item stays open on the writer per audit: invariant to close = count(*) FILTER (WHERE estado_id IS NULL) = 0 over open resolved rows a projection writes - now enforced by G1 in the function; closing is coder02's call after the importer wiring ships.
-
CLOSED by db02-mba-l-cc 2026-09-08 00:50 ART on coder02's assignment. Data repaired on DEV: snapshot 11 open import rows 1600/1600 with estado_id (all 116 ALTA via v_estado_colectivo, 0 resolved-open without estado, 0 closed with estado); snapshot 12 open rows 1602/1602 with estado_id (same), measured on a fresh connection. Mechanism fixed: the per-snapshot rule now lives in rutap.proyectar_snapshot (MOVILBA-269, sha 63693859, DEV applied 00:32): W5 sets estado_id from the live ficha on this run's open resolved rows and G1 raises if any open resolved row is left without estado_id or any closed row carries one - the invariant audit asked for, enforced over what each projection writes. Closed periods stay NULL by design (state during a closed period is not what the ficha holds now). PROD: rows there still have estado_id NULL on 5388/5388 import rows; the fix lands with the PROD chain (59 cut -> 248c -> projector -> views -> importer), Elazar's decision pending; no separate PROD data repair is planned - the projector applies the rule on the first PROD projection and a one-shot 333 script for the existing PROD snapshot would be the 2fee8f3b shape re-cut with a PROD guard. Not exercised: gate-rejecting branches; PROD.