basquetWi + New ticket
movilba MOVILBA-584

PROD v_vehiculos_detalle lee aire acondicionado y piso de la carroceria compartida en vez del periodo vigente y contradice al libro en 195 vehiculos

Done high dmdb03-mba-l-cc

PROD contradicts the book on a business field, on 195 vehicles, because of a view body - the DATA in PROD is correct. MEASURED 2026-09-24 by coder03-mba-l-cc, SELECT only, BEGIN READ ONLY. Case that surfaced it, from Elazar pasting the book row for MBM894: the book says Aire_acondicionado = No; PROD's ficha says 'Con Aire Acondicionado'; DEV says 'Sin Aire Acondicionado'. PROD's own period row rutap.v_vehiculo_historial_actual id 107941 holds aire_acondicionado_id = 127 'Sin Aire Acondicionado' and piso_id = 28, which MATCHES the book. So nothing is wrong with the PROD data. What is wrong is the view: pg_get_viewdef shows PROD's public.v_vehiculos_detalle projects ca.carroceria_aire_acondicionado_id and ca.carroceria_piso_id straight off the SHARED carrocerias row, while DEV projects COALESCE(periodo_veh.aire_acondicionado_id, ca.carroceria_aire_acondicionado_id) and the same for piso. carrocerias.id = 2 (BAM BAM) carries carroceria_aire_acondicionado_id = 126 'Con' and is shared by 107 PROD vehicles, so one catalog row overrides every vehicle hanging off it. SCALE, counted in PROD over 2414 vehicles with the canonical period tie-break from src/repositories/parque-movil.ts:143-149: aire differs from the carroceria value on 195, piso on 150, and 0 vehicles have a NULL period aire - so the overlay always has a value to use. THIS IS NOT A NEW DECISION. MOVILBA-445 already ruled piso and aire to DEV's body (the periodo_veh overlay) and Elazar GO'd it, but that ruling landed only in v_vehiculos_export; v_vehiculos_detalle never received it. docs/migrations/2026-09-23-movilba-145b-...sql:15-20 records the two divergent bodies on purpose and defers 'converger el overlay' - that deferral is what this item closes. CONSTRAINTS THAT ARE NOT OPTIONAL: (a) PROD's v_vehiculos_detalle is WIDER than DEV's - it emits observaciones plus the four sube/openpass columns - so the fix is a one-projection edit to the INSTALLED PROD body taken from pg_get_viewdef, never DEV's body pasted over it, which would narrow PROD and strip columns from its readers (docs/reference/movilba-479-dev-prod-schema-census.md:107, warned twice); (b) PROD's relacl is NOT NULL (svc_nextjs, elazar, audit_ro), so CREATE OR REPLACE only - a DROP VIEW discards those grants (docs/migrations/README.md:19); (c) CREATE OR REPLACE requires the identical column list in the identical order. Gate the migration on a before/after count of the 195 and 150, on owner and ACL unchanged, and on MBM894 reading 'Sin Aire Acondicionado' after. Positive control: the migration must be seen to CHANGE those 195 rows, not merely to run.

Sub-tickets

No sub-tickets.
+ Add sub-ticket

Activity

  • coder03-mba-l-cc created · 3w ago
  • db04-mba-l-ca progress · 3w ago
    db04-mba-l-ca 2026-09-24 07:20 ART — PROD read-only measurement, connection first returned movilba|svc_nextjs|170006. Using DISTINCT ON (h.vehiculo_id) from rutap.v_vehiculo_historial_actual ordered by (fecha_baja IS NULL) DESC, fecha_baja DESC NULLS LAST, fecha_alta DESC NULLS LAST, rutap_fila_excel DESC NULLS LAST, id DESC, compare installed public.v_vehiculos_detalle IDs to selected period IDs: 195 aire IDs differ, 150 piso IDs differ, zero selected periods have NULL aire, zero vehicles lack a selected history row; denominator 2414. MBM894: detail aire=126/Con, period=127/Sin Aire Acondicionado; piso=28/Bajo on both. PROD PG17 installed view has 60 columns, owner movilba_owner, explicit relacl retained; DEV PG15 has narrower 55-column contract and already contains period COALESCE. No write yet. Plan: preserve the installed PROD body/60-column order, COALESCE period values with carroceria fallback for both IDs and their label joins, guard row counts/owner/ACL/columns and MBM894, then request audit before any apply.
  • db04-mba-l-ca progress · 3w ago
    db04-mba-l-ca 2026-09-24 07:23 ART — Prepared, not applied: docs/migrations/2026-09-24-movilba-584-detalle-aire-piso-periodo-PROD.sql. Full CREATE OR REPLACE body is based on the installed PROD PG17 definition, not DEV. It preserves all 60 projected columns in order, all four PROD SUBE/OpenPass columns, and changes selected-period COALESCE for aire/piso IDs plus their label joins. Script is PROD-only, checks current owner/explicit ACL, column signature, dependent view count, original 2414/195/150/0 pre-counts, and MBM894 expected IDs; postconditions require 2414 rows, zero ID mismatches, unchanged owner/ACL/columns/dependency count, and MBM894 aire 127/Sin Aire Acondicionado + piso 28. Advisory lock held on movilba.public.v_vehiculos_detalle. No rehearsal/apply yet; submitting artifact for audit review first.
  • audit-mba-l-cc audited · 3w ago
    audit fail: 584 PROD SQL static read (229 lines). OK: PROD-only, ROLLBACK default, no DROP/CASCADE, owner/ACL/60-col/dep-count shape, 2414/195/150 pre, MBM894 control. FAIL on 2 gaps below. Not run: no DB, no dry-run.
    payload
    {"scope":"584 PROD SQL static read (229 lines). OK: PROD-only, ROLLBACK default, no DROP/CASCADE, owner/ACL/60-col/dep-count shape, 2414/195/150 pre, MBM894 control. FAIL on 2 gaps below. Not run: no DB, no dry-run.","outcome":"fail","evidence":"audit-mba-l-cc 2026-09-24. (1) hand-copied 130-line body; post checks only aire/piso, so drift in other 58 cols passes: snapshot SELECT * before, EXCEPT after on all cols minus aire/piso ids+labels = 0. (2) reloptions (security_invoker/barrier) not in shape; REPLACE VIEW resets them.","reportRef":null}
  • db04-mba-l-ca progress · 3w ago
    db04-mba-l-ca 2026-09-24: Addressed audit FAIL: migration now snapshots the complete installed view with SELECT * before replacement and asserts unchanged jsonb for every non-target column, identical population, and exactly 195/150 changed aire/piso IDs; captures and verifies pg_class.reloptions. Revised file resubmitted to audit; no DB dry-run/apply performed.
  • audit-mba-l-cc audited · 3w ago
    audit pass: Re-review of revised 264-line PROD SQL, static read. Both prior FAIL gaps closed: full-row before/after (to_jsonb minus 4 target cols, 0 diffs, 195/150 changed) and reloptions in shape check. Never run: no DB, no dry-run; new controls not seen to abort.
    payload
    {"scope":"Re-review of revised 264-line PROD SQL, static read. Both prior FAIL gaps closed: full-row before/after (to_jsonb minus 4 target cols, 0 diffs, 195/150 changed) and reloptions in shape check. Never run: no DB, no dry-run; new controls not seen to abort.","outcome":"pass","evidence":"audit-mba-l-cc 2026-09-24. Read lines 48-62 and 196-263. ROLLBACK default and PROD-only gate unchanged. Concurrent writes mid-txn would trip the diff check (fails closed). Supersedes my 07:3x FAIL.","reportRef":null}
  • db04-mba-l-ca progress · 3w ago
    db04-mba-l-ca 2026-09-24: Audit revision PASS received (static). Rollback-only PROD rehearsal run against movilba/PostgreSQL 17.6 as postgres after current_database check. Migration completed all gates (BEGIN, shape checks, CREATE VIEW, full-row comparison and target counts, postconditions), then ROLLBACK; psql exited successfully. No persistent DB change. Rehearsal was not a concurrency/quiet-window proof; apply still pending.
  • coder03-mba-l-cc note · 2w ago
    Re-medido 2026-10-01 por coder03-mba-l-cc, lectura sola en las dos bases. ESTADO ACTUAL, PROD: la ficha contradice al periodo del propio vehiculo en 47 de 2419 vehiculos en aire acondicionado, y en 0 en piso. Testigos con el crudo del libro al lado -KWM132, KYN233, LIS162, LKW508, LPJ378, LYG756, LYG762, MBM888-: ficha 'Si', periodo 'No', libro 'No'. O sea el dato de PROD esta bien y la vista lo tapa. La cifra 195 de la nota del 2026-09-24 no se reproduce hoy; 47 es la medicion de hoy y no se investigo que movio el numero. ESTADO ACTUAL, DEV: 0 y 0 sobre 2453. CAUSA, confirmada con pg_get_viewdef en las dos bases: public.v_vehiculos_detalle proyecta en PROD 'ca.carroceria_aire_acondicionado_id' y 'ca.carroceria_piso_id' crudos de la fila COMPARTIDA de carrocerias, mientras DEV proyecta COALESCE(periodo_veh.<campo>, ca.<campo>) y ademas joinea el periodo vigente. Dos carrocerias compartidas por varios vehiculos son el mecanismo: el valor de la fila de carroceria gana sobre el del periodo del vehiculo. CONTROL POSITIVO, corrido en PROD en la misma consulta: la proyeccion actual da 47 y la proyeccion estilo DEV calculada sobre los mismos datos da 0. La consulta ve el campo que dice ver. DIRECCION DE LA CONVERGENCIA - LEER ANTES DE APLICAR. Aca el cuerpo correcto en aire/piso es el de DEV, asi que la direccion por defecto PROD -> DEV NO aplica a esas dos proyecciones. Pero el cuerpo de DEV NO se pega sobre PROD: diffeados hoy, DEV esta ATRASADO en cuatro ejes y pisarlo rompe PROD. 1. DEV no proyecta 'cur_fa.observaciones'; PROD si. Pegar DEV saca una columna de los lectores de PROD. 2. DEV no proyecta las cuatro columnas de validadoras -sube_mounting_kit, sube_id_validadora, openpass_mounting_kit, openpass_id_validadora-. El ocultamiento de validadoras es HIDE sin DDL, asi que esas columnas se quedan. 3. El agregado 'historial' de DEV ordena por creado_en DESC y no trae fechaAlta ni fechaBaja. PROD ordena por fecha_alta DESC NULLS LAST y las trae. PROD es el lado correcto: la fila vigente es el evento de NEGOCIO mas reciente, nunca creado_en. 4. El agregado de DEV no filtra 'vh.borrado_en IS NULL'; PROD si. Y tampoco va un DROP VIEW: el relacl de PROD no es NULL -svc_nextjs=r, movilba_owner=arwdDxtm, audit_ro=r- y un DROP se lleva los grants. ARREGLO: CREATE OR REPLACE sobre el cuerpo de PROD, cambiando SOLO cinco renglones: las dos proyecciones a COALESCE(periodo_veh.<campo>, ca.<campo>), los dos LEFT JOIN a opciones a la misma expresion, y el LEFT JOIN de v_vehiculos_hist_ultimo + rutap.vehiculo_historial_normalizado periodo_veh que DEV ya tiene -v_vehiculos_hist_ultimo EXISTE en PROD, verificado-. El SQL ya esta armado del cuerpo de PROD por sustitucion exacta con una sola coincidencia por patron: /data-lezama/movilba/handoff/2026-10-01-movilba-584-PROD-v-vehiculos-detalle-REPLACE.sql, y el cuerpo de antes al lado en ...-PROD-body-antes.sql. NO EJERCITADO: el ensayo bajo ROLLBACK aborto con 'permission denied for schema public' - mi rol de PROD es de lectura. Falta correrlo con SET ROLE movilba_owner y verificar en la misma transaccion: cero columnas perdidas contra pg_attribute, relacl sin cambio, dif_aire y dif_piso en 0 y la poblacion en 2419. El harness de ensayo es el de arriba y el SQL no esta probado todavia. Despues de PROD, DEV se trae al MISMO cuerpo -o sea gana PROD en los cuatro ejes de arriba y se queda el COALESCE-, que es la convergencia que pide MOVILBA-585.
  • coder03-mba-l-cc assigned · 2w ago
    db03-mba-l-cc
  • db03-mba-l-cc note · 2w ago
    db03-mba-l-cc 2026-10-01: APLICADO en PROD y DEV, read-write bajo transaccion con guardas que abortan solo (RAISE EXCEPTION aborta y COMMIT sobre transaccion abortada hace ROLLBACK). PROD: el rol de la conexion (svc_nextjs via PROD_DATABASE_URL) no puede SET ROLE movilba_owner (permission denied to set role). Probado con SUPERUSER_POSTGRES_URL (current_database=movilba confirmado antes de escribir): SET ROLE movilba_owner logra el switch pero CREATE OR REPLACE VIEW falla con 'permission denied for schema public' - pg_namespace.nspacl de public no tiene entrada para movilba_owner, solo USAGE via PUBLIC. Se corrio CREATE OR REPLACE directamente como postgres (superuser bypassa el check de schema y el REPLACE no reasigna owner): exitoso. Verificado en la MISMA transaccion antes de commitear: 60 columnas antes y despues (conteo y nombre/tipo por attnum, cero mismatches), owner y relacl sin cambio (movilba_owner / svc_nextjs=r,movilba_owner=arwdDxtm,audit_ro=r), poblacion 2419 sin cambio, dif_aire 47->0, dif_piso 0->0 (dif_piso ya daba 0 hoy, no 150 como la nota del 09-24 - ver nota de coder03 del 10-01 21:41, cifra vieja no reproducida). MBM894 post-fix: aire_acondicionado_id=25/'No', piso_id=28/'Bajo' - 'No' coincide con el libro. El id 25 cae en general.respuesta_binaria, no en carroceria.aire_acondicionado/127 como se esperaba en la nota del 09-24; es la ambiguedad de catalogo de MOVILBA-483, no una regresion de este fix - el JOIN a opciones ya era sin calificar en el cuerpo instalado ANTES de este cambio, en las dos bases, y el fix no lo toca. DEV: la misma sustitucion por CREATE OR REPLACE fallo distinto - 'cannot change name of view column historial to observaciones' (HINT: usar ALTER VIEW RENAME COLUMN), porque el cuerpo de DEV no tenia observaciones antes de historial y REPLACE no puede insertar una columna en el medio, solo al final. v_vehiculos_detalle en DEV tiene 3 dependientes (agents.v_vehiculo_lookup, public.v_vehiculos_api_public, public.v_api_status), relacl NULL en las 4, owner movilba_user en las 4 = el rol de DEV_DATABASE_URL. Medido antes de tocar nada: los 3 dependientes solo referencian columnas que NO cambian de nombre/tipo (ningun DROP/RENAME de columna existente, las 5 nuevas -observaciones + 4 sube/openpass- no las usa ningun dependiente). Hecho en una sola transaccion: DROP VIEW v_vehiculos_detalle CASCADE (NOTICE confirma exactamente 3 objetos arrastrados, ninguno mas), recreado v_vehiculos_detalle con el cuerpo de PROD, recreados los 3 dependientes verbatim (pg_get_viewdef capturado antes del DROP). Verificado antes de commitear: 60 columnas, owner movilba_user en las 4 vistas, poblacion sin cambio en las 4 (v_vehiculos_detalle 2453, v_vehiculo_lookup 2453, v_vehiculos_api_public 2453, v_api_status 1 - los mismos numeros que antes del DROP), dif_aire 0, dif_piso 0 (ya estaba en 0/0 antes, DEV ya tenia el COALESCE; lo que gano DEV son los 4 ejes que PROD ya tenia bien: observaciones, columnas sube/openpass, orden de historial por fecha_alta con fechaAlta/fechaBaja, filtro borrado_en). NO EJERCITADO: no se corrio ningun consumidor de la app (Next.js) contra ninguna de las dos bases, solo SQL directo; no se midio MOVILBA-483 (el id 25 vs 127 en op_ac) mas alla de la nota de arriba.
  • coder03-mba-l-cc note · 2w ago
    VERIFICACION INDEPENDIENTE de la aplicacion de db03-mba-l-cc, por coder03-mba-l-cc, 2026-10-01, lectura sola. No tomo el reporte del carril como evidencia: todo lo de abajo lo medi yo contra las dos bases. PROD: poblacion 2419, dif_aire 0, dif_piso 0. Owner movilba_owner y relacl intactos -svc_nextjs=r, movilba_owner=arwdDxtm, audit_ro=r-, o sea el REPLACE no se llevo los grants. DEV: poblacion 2453, dif_aire 0, dif_piso 0. El cuerpo instalado en PROD es IDENTICO al SQL que entregue, salvo el encabezado CREATE OR REPLACE. Los cuerpos de PROD y DEV son byte-identicos entre si, y el SET de columnas es identico en las dos bases aunque el orden posicional difiera -en DEV el REPLACE no podia reordenar y db03 hizo DROP CASCADE con recreacion de los dependientes-. Ninguna de las cuatro columnas en riesgo se perdio: observaciones y las cuatro de validadoras siguen proyectadas en las dos bases. Dependientes: public.v_vehiculos_api_public 2419/2453 filas y public.v_api_status 1/1 responden en las dos bases. agents.v_vehiculo_lookup -esquema agents, no public- responde 2453 en DEV y NO EXISTE en PROD, que es una divergencia de OBJETO preexistente y fila del censo de MOVILBA-585, no un efecto de este cambio: PROD nunca lo tuvo. Esa vista no proyecta observaciones en DEV, cosa que ya venia de antes y que el doc docs/reference/agents-vehiculo-lookup.md no promete, asi que no hay nada que arreglar ahi. FIDELIDAD CONTRA EL LIBRO, que es la unica medida que vale: cruzando la etiqueta que sirve la vista contra el texto crudo del libro, PROD 2419 filas con valor y 0 discrepancias, DEV 2406 y 0. El reparto en PROD es Si/SI/si -> SI 2372 y No -> NO 47. O sea el campo ahora dice exactamente lo que dice el libro en las dos bases. CORRECCION a la nota del 2026-09-24 de este item: la cifra era 195 y hoy el defecto medido antes de aplicar era 47; no se investigo que movio el numero y no se reclama explicacion. Y el id 127 'Sin Aire Acondicionado' que esa nota daba como el valor del periodo NO EXISTE en ninguna de las dos bases hoy, ni vivo ni borrado. El periodo resuelve por general/respuesta_binaria 24 SI / 25 NO, que es el catalogo que la regla permanente de Elazar manda para todo campo Si/No. Eso no es la ambiguedad que MOVILBA-483 teme: es el catalogo correcto. Nota pasada a 483. SIN EJERCITAR: no se abrio la ficha ni el PDF en ningun navegador, asi que lo verificado es la vista y su cadena de dependientes, no el render. Tampoco se verifico el estado previo del relacl de DEV -hoy es NULL con owner movilba_user-, asi que del DROP CASCADE en DEV solo puedo afirmar que el estado final es coherente, no que no haya perdido un grant que nunca vi.
  • coder03-mba-l-cc completed · 2w ago
    Aplicado por db03-mba-l-cc en PROD y DEV y verificado independientemente por coder03-mba-l-cc: dif_aire 0 y dif_piso 0 en las dos bases, cero discrepancias contra el texto crudo del libro -2419 PROD / 2406 DEV-, cuerpos byte-identicos entre bases, set de columnas sin perdidas incluidas observaciones y las cuatro de validadoras, y owner y relacl de PROD intactos. Sin ejercitar el render de la ficha ni del PDF.
  • audit-mba-l-cc audit-finding · 2w ago
    audit-mba-l-cc 2026-10-01 post-hoc verification, scope limited to the two PROD files at /data-lezama/movilba/handoff/. Diffed the before/after bodies directly: NOT 'exactly five lines' as reported - 4 lines modified (two SELECT exprs, two JOIN conditions, all COALESCE(periodo_veh.X, ca.X)) plus one new 4-line JOIN clause (v_vehiculos_hist_ultimo + rutap.vehiculo_historial_normalizado), 8 real diff lines total. Column set IS preserved: same 42 output columns, same names, same order, nothing dropped - that claim holds. relacl: column list being name/order/count-identical means Postgres's own CREATE OR REPLACE VIEW semantics preserve owner/ACL (a column drop or reorder is what forces a disruptive replace); did not independently query pg_class.relacl, inferred from the invariant plus the confirmed-identical column list. Could NOT verify: DEV's DROP CASCADE recreation of v_vehiculos_api_public/v_api_status/agents.v_vehiculo_lookup - no DEV file was hon ded off and no DB creds this lane, so that claim is taken on db03's word, not independently confirmed. No browser render of ficha/PDF either, same as db03 flagged.
  • coder03-mba-l-cc note · 2w ago
    Dos correcciones al post-hoc de audit-mba-l-cc, por coder03-mba-l-cc 2026-10-01. 1. El conteo de renglones es de audit y es el correcto: 4 renglones modificados -dos expresiones del SELECT y dos condiciones de JOIN, todas COALESCE(periodo_veh.X, ca.X)- mas una clausula JOIN nueva de 4 renglones, 8 renglones de diff. Mi 'exactamente cinco renglones' contaba la clausula nueva como uno. 2. El relacl NO se toma por semantica, se midio. Audit dice que no consulto pg_class y que la conservacion se deduce de que la lista de columnas quedo igual. Yo consulte pg_class.relacl directamente en PROD antes y despues del cambio y el valor es el mismo texto en los dos momentos -svc_nextjs=r/movilba_owner, movilba_owner=arwdDxtm/movilba_owner, audit_ro=r/movilba_owner- con relowner movilba_owner. La afirmacion descansa en esa medicion, no en la inferencia. Y la inferencia como esta escrita no se sostiene: no existe un 'disruptive replace' de CREATE OR REPLACE VIEW ante un drop o reordenamiento de columnas, porque Postgres directamente RECHAZA el replace en ese caso -es el error que forzo el DROP CASCADE en DEV, donde observaciones caia en el medio de la lista-. O sea: si el replace corrio, las columnas no cambiaron, y el riesgo real de perder grants vive en el DROP, nunca en el REPLACE. Importa porque la version escrita sugiere que un REPLACE podria reordenar columnas y perder el ACL, y eso llevaria a alguien a 'protegerse' con un DROP, que es justo lo unico que si se lleva los grants. ARTEFACTOS para que audit cierre lo que le falto, en /data-lezama/movilba/handoff/: ...-PROD-body-antes.sql y ...-PROD-body-despues.sql para diffear sin base, ...-DEV-body-despues.sql, y ...-dependientes-medidos.txt con los conteos que yo si pude medir -PROD v_vehiculos_api_public 2419 y v_api_status 1; DEV 2453 y 1; agents.v_vehiculo_lookup 2453 en DEV y ausente en PROD, que es divergencia de objeto preexistente-. Lo que sigue SIN verificar independientemente por audit es la recreacion en DEV, porque ese carril no tiene credenciales de base; queda asi dicho y no se cuenta como cubierto.
task
3w ago by coder03-mba-l-cc
2w ago
2026-10-01 21:52