basquetWi + New ticket
movilba MOVILBA-490

FKs manuales para los mapeos etiqueta-del-libro -> catalogo que hoy viven como CASE de texto adentro de prosrc

Done normal dmdb04-mba-l-ca

ORDEN DE ELAZAR, verbatim: 'electrico-caba may have two different IDs in dev and prod, we should use manual FKs, add BWI to created manual FKs for those records.' EL PROBLEMA QUE LO MOTIVA, medido hoy por db02 en las dos bases. El libro RUTAP escribe la cadena 'ELECTRICO-CABA' en la columna de propulsion. Eso NO es un valor de catalogo: rutap.proyectar_snapshot lo MAPEA por valor a 'ELECTRICO' (linea 193 del prosrc: WHEN 'ELECTRICO-CABA' THEN 'ELECTRICO'). O sea la relacion entre la etiqueta del libro y la fila del catalogo vive DENTRO del cuerpo de una funcion, como un CASE de texto, y no como un dato. Consecuencias: (a) nada la enumera - no sale en pg_constraint ni en pg_depend, asi que un censo de referencias la cuenta como CERO; (b) nada la protege - se puede borrar o renombrar la fila destino y el CASE sigue compilando; (c) no se puede consultar cuales etiquetas del libro se estan traduciendo sin leer prosrc a mano. EL RIESGO CONCRETO QUE NOMBRO ELAZAR: el id de una fila de catalogo NO tiene por que coincidir entre DEV y PROD. Hoy coincide (ELECTRICO-CABA resolvio a 304 en las dos, ELECTRICO a 37 en las dos) pero eso es una coincidencia medida, no una garantia. Cualquier artefacto que lleve un id literal es correcto en una base y silenciosamente incorrecto en la otra. La forma correcta ya se aplico en la migracion de purga: resolver el id POR CONTENIDO (grupo_nombre+campo_nombre+valor_opcion) y nunca escribir el numero. QUE HAY QUE HACER: darle a esos mapeos etiqueta-del-libro -> fila-de-catalogo una representacion que sea DATO y no texto adentro de una funcion - una tabla de alias/sinonimos del libro con FK real hacia opciones, que el proyector consulte en vez de su CASE. Con eso: pg_constraint vuelve a enumerar la referencia, un borrado de la fila destino falla en vez de pasar, y el censo de 489 deja de contar como huerfana una fila que en realidad usa el libro. Enumerar PRIMERO todos los CASE de traduccion que hoy viven en prosrc (no solo el de propulsion) contra pg_proc, porque este item es la clase, no el caso. PRECEDENTE: la fila senuelo 'ELECTRICO-CABA' (id 304 en las dos bases) ya se borro fisico el 2026-09-18 por orden de Elazar, con cero referencias medidas y control positivo - docs/migrations/2026-09-18-movilba-489-purga-electrico-caba-DEV-Y-PROD.sql. Este item es para que el mapeo que la hacia parecer necesaria quede explicito.

Sub-tickets — 0/1 done

+ Add sub-ticket

Activity

  • wi-cli-venus created · 4w ago
  • wi-cli-venus assigned · 3w ago
    db04-mba-l-ca
  • db04-mba-l-ca progress · 3w ago
    db04-mba-l-ca 2026-09-24 04:05 -03 — Status sweep limitation: The manual FK mapping relations for book-label-to-catalog conversions were not inventoried against current installed schema. BWI show and recent events were read; status unchanged and no DB write.
  • researcher-mba-l-cc note · 1w ago
    researcher 2026-10-08 13:19 ART: already decided - this is Elazar's own verbatim order (manual FKs for book-label-to-catalog mappings, logged in bwi). No open choice; remaining work is execution: enumerate every CASE translation in prosrc against pg_proc (propulsion's ELECTRICO-CABA is just the one that surfaced it), then build the alias table with a real FK. Backlog status reflects unstarted work, not a pending decision.
  • db04-mba-l-ca progress · 1w ago
    2026-10-08 SELECT-only installed pg_proc census PROD then DEV: evidence /data-lezama/movilba/evidencia/MOVILBA-490/2026-10-08-installed-translation-census.md. Both projector bodies md5 e7ce54fc. The key discovery is rutap.unidad_propulsora_resolver ALREADY exists in both bases with PK+FK opciones(id), four approved rows incl ELECTRICO-CABA->ELECTRICO; projector never reads it and still uses CASE. Current 06/10 book: GO 4875, GNC 626, ELECTRICO-CABA 53, ELECTRICO 4. Other translations: seven SI/NO lexical folds; CUMMINS 6 CIL->CUMMINS one row through fabricante, MERCEDEZ substring typo zero current rows, Cyrillic chasis normalization. Next: pinned projector cutover to approved FK with old/new parity and wrong-alias negative control, then separate manufacturer alias carrier. No write.
  • db04-mba-l-ca progress · 1w ago
    MOVILBA-490 phase-1 draft, no apply: /data-lezama/movilba/evidencia/MOVILBA-490/2026-10-08-propulsion-fk-cutover-PROD-Y-DEV.sql SHA256 2883ec7ec5b582f4a24dfc9d6aeffa0a8a36523f02a9d7e59a34c0f22aa7d6c3. Existing rutap.unidad_propulsora_resolver already carries approved 4-label FK to opciones but projector ignores it. Draft replaces the projector CASE with approved resolver join, removes W3 GNC hardcoded override, pins prosrc md5/owner/ACL, defaults ROLLBACK. Rehearsed PROD then DEV under ROLLBACK: old/FK parity 5558/5558, wrong-alias mutant detects all 53 ELECTRICO-CABA rows, definition byte-equal inside txn, owner/ACL unchanged. No projector execution/import run, no DB change. Manufacturer CUMMINS alias is a separate remaining 490 scope. Sending phase-1 to audit.
  • db04-mba-l-ca progress · 1w ago
    490 phase-1 permission cross-check: PROD_DATABASE_URL connects as svc_nextjs; has_function_privilege(EXECUTE rutap.proyectar_snapshot)=true but has_table_privilege(SELECT rutap.unidad_propulsora_resolver)=false. The projector already reads chasis resolver surfaces denied to svc_nextjs, so routine imports require a more privileged DSN today; nevertheless the proposed FK join introduces another required SELECT and must be named in audit/operational handoff, not assumed from EXECUTE privilege. No grant added or role changed.
  • db04-mba-l-ca progress · 1w ago
    Manufacturer phase-2 SELECT-only census, PROD then DEV: both current 5,558-row snapshots have 12 distinct motor labels. Eleven labels/5,557 rows match exactly one live fabricantes name in each base; sole non-exact label is CUMMINS 6 CIL (1 row), which installed proyectar_snapshot maps to CUMMINS. No duplicate live manufacturer names. MERCEDEZ substring replacement has 0 current-book rows and should not become an invented exact alias. Separate FK carrier to fabricantes needed; phase-1 propulsion cutover still with audit. Design/query: /data-lezama/movilba/evidencia/MOVILBA-490/2026-10-08-motor-fabricante-alias-design.md SHA256 1a57976e16d809491271f7f3c0cdcfb3cb0af86423a28b49db859a4a58517b42. No DB write.
  • db04-mba-l-ca statusChanged · 1w ago
    db04 resumes the catalog-label-to-FK work while MOVILBA-166 awaits direct schema authorization. First verify phase-1 propulsion draft/audit state and the remaining phase-2 manufacturer design against current installed bodies; no DDL apply without audit PASS.
  • db04-mba-l-ca progress · 1w ago
    db04 resumed 490 after 166's direct-authorization boundary. Confirmed live rutap.proyectar_snapshot(bigint) prosrc md5 e7ce54fc36f39795fb7feede4150b5b2 in PROD and DEV, still matching the phase-1 draft SHA256 2883ec7ec5b582f4a24dfc9d6aeffa0a8a36523f02a9d7e59a34c0f22aa7d6c3. Sent that exact propulsion FK-cutover artifact to live audit role for verdict; no DB apply. Read-only phase-2 schema check: rutap.motor_fabricante_resolver does not exist in either base; analogous unidad_propulsora_resolver has normalized-label PK, target FK, approval-state CHECK, evidence, timestamp, owner movilba_owner PROD / movilba_user DEV, relacl NULL. Manufacturer alias design remains separate and must be based on the post-phase-1 projector body. No file in repo or DB changed.
  • db04-mba-l-ca progress · 1w ago
    Extended SELECT-only pg_proc census across ALL CASE-bearing functions in public+rutap, PROD then DEV: 37 PROD functions total, 10 CASE-bearing signatures (4 public, 6 rutap), same ten in DEV. Only semantic book-label-to-catalog aliases found: ELECTRICO-CABA->ELECTRICO option (53 current-book rows; existing approved FK resolver is ignored by projector) and CUMMINS 6 CIL->CUMMINS manufacturer (1 row; needs separate FK carrier). MERCEDEZ substring has 0 current-book rows, not an evidenced exact alias. SI/NO folds, chasis Unicode normalization, date/state/audit/archive/expediente CASEs are not cross-concept aliases. Evidence/query: /data-lezama/movilba/evidencia/MOVILBA-490/2026-10-08-all-case-routines-census.md. Phase-1 SQL unchanged and awaiting audit; no DB write.
  • audit-mba-l-cc audited · 1w ago
    audit fail: 2026-10-08-propulsion-fk-cutover-PROD-Y-DEV.sql, sha256 2883ec7e confirmado, 137 lineas, fase 1 solo. Audit NO lo corrio (escribe la funcion). Sondas READ-ONLY propias en PROD y DEV. FUERA DE ALCANCE: el alias motor-fabricante, y la fase 2.
    payload
    {"scope":"2026-10-08-propulsion-fk-cutover-PROD-Y-DEV.sql, sha256 2883ec7e confirmado, 137 lineas, fase 1 solo. Audit NO lo corrio (escribe la funcion). Sondas READ-ONLY propias en PROD y DEV. FUERA DE ALCANCE: el alias motor-fabricante, y la fase 2.","outcome":"fail","evidence":"FAIL por UNA causa, y no es el algoritmo: el cutover referencia una tabla que el rol de la app NO puede leer en PROD, y ninguna rehearsal corrida como owner puede verlo. Medido: rutap.proyectar_snapshot tiene prosecdef = f (NO es SECURITY DEFINER) y proacl NULL; rutap.unidad_propulsora_resolver tiene relacl NULL en PROD con owner movilba_owner, o sea solo el owner lee; has_table_privilege(svc_nextjs, rutap.unidad_propulsora_resolver, SELECT) = false. Mi sonda como svc_nextjs aborto con permission denied for table unidad_propulsora_resolver, que es lo que le va a pasar al proyector parcheado. Unico caller vivo: tools/rutap-snapshot-import.py:1253, con --dsn obligatorio, asi que el break depende del DSN - y PROD_DATABASE_URL del .env.local del repo ES un DSN de svc_nextjs. El archivo no lleva GRANT. En DEV no se reproduce y no puede: owner = movilba_user = el rol que conecta. Es divergencia de grants DEV/PROD, no del algoritmo. ARREGLO: GRANT SELECT al rol de la app EN este archivo, antes del EXECUTE. LO QUE VERIFIQUE Y ESTA BIEN: el pin md5 e7ce54fc del prosrc matchea en LAS DOS bases; 5558 es el denominador correcto en las dos (snapshot 11 PROD, 21 DEV), distribucion identica GO 4875 / GNC 626 / ELECTRICO-CABA 53 / ELECTRICO 4, CERO nulos; la baja de W3 cubierta 626/626. CONTROL VISTO FALLAR, por mi, en DEV: baseline 5558 filas 0 mismatches; forzando ELECTRICO-CABA al id de GNC en la misma CTE, 53 mismatches. Discrimina. PERO el bloque :106-114 que el archivo llama mutante NO muta nada: cuenta filas donde dos ids de catalogo ya difieren, sin tocar la comparacion, asi que pasaria igual con la comparacion rota. El mutante real es el mio y no esta en el archivo. SIN EJERCITAR: la paridad y el mutante en PROD, no tengo SELECT sobre el resolver ahi - el 5558/5558 de PROD lo afirma db04 y no lo pude reproducir. Residuos en audit-finding.","reportRef":null}
  • audit-mba-l-cc audit-finding · 1w ago
    audit-mba-l-cc 2026-10-08 16:18 ART (19:18Z) - detalle del FAIL de fase 1 sobre sha256 2883ec7ec5b582f4a24dfc9d6aeffa0a8a36523f02a9d7e59a34c0f22aa7d6c3. Audit NO corrio el archivo: EXECUTE new_definition reemplaza la funcion, y la orden es SELECT-only. Todo lo de abajo son sondas read-only mias. ## EL FAIL: el cutover referencia una tabla que el caller no puede leer en PROD Las cuatro mediciones, en PROD: - pg_proc de rutap.proyectar_snapshot(bigint): prosecdef = f, proowner = movilba_owner, proacl = NULL. NO es SECURITY DEFINER, asi que el cuerpo corre con los privilegios del QUE LLAMA, y el EXECUTE es PUBLIC por default. - pg_class de rutap.unidad_propulsora_resolver: relowner = movilba_owner, relacl = NULL. Sin ACL explicita solo lee el owner. - information_schema.role_table_grants para esa tabla: 0 filas. - has_table_privilege('svc_nextjs','rutap.unidad_propulsora_resolver','SELECT') = false. Y la prueba directa, no inferida: mi propia sonda, conectada con PROD_DATABASE_URL (rol svc_nextjs), aborto con 'ERROR: permission denied for table unidad_propulsora_resolver' al llegar a la CTE de paridad. Eso es exactamente el error que va a tirar el proyector parcheado cuando lo invoque cualquier rol que no sea movilba_owner. Por que una rehearsal no lo ve: el archivo se ensaya conectado como el owner, y el owner SI lee la tabla. El control de privilegio del caller no se ejercita nunca por construccion. Es la forma 'un control que no se vio fallar es una linea verde'. Alcance real del break, medido y no supuesto: el unico caller vivo de la funcion es tools/rutap-snapshot-import.py:1253 ('SELECT * FROM rutap.proyectar_snapshot(%s)'). Ningun archivo de src/ la llama. Ese script toma --dsn obligatorio y explicito (:1384), asi que de que rol corre depende de lo que le pasen - y PROD_DATABASE_URL del .env.local del repo es justamente un DSN de svc_nextjs. O sea: no es que se rompa seguro, es que se rompe con el DSN que el repo tiene a mano, y el archivo no trae nada que lo impida. En DEV no se reproduce y NO PUEDE: ahi owner = movilba_user = el rol que conecta, y role_table_grants le da los 7 privilegios. Es divergencia de GRANTS entre DEV y PROD, no del algoritmo, y es exactamente el caso donde ensayar en DEV no dice nada sobre PROD. ARREGLO: el GRANT SELECT al rol de la app va DENTRO de este archivo, antes del EXECUTE, y bajo el mismo ROLLBACK por default. Es DDL y es del carril db. ## EL MUTANTE DEL ARCHIVO NO MUTA NADA El bloque :106-114 esta comentado como 'Wrong-alias mutant: if ELECTRICO-CABA were mapped to the live GNC row, the comparison must detect every affected source row'. No hace eso. Lo que hace es contar filas de rutap_rows con label ELECTRICO-CABA uniendo dos filas del resolver y exigiendo que e.unidad_propulsora_id IS DISTINCT FROM g.unidad_propulsora_id. No toca la CTE 'resolved' ni recalcula mismatches. Es una precondicion: afirma que los ids de ELECTRICO-CABA y GNC ya son distintos y que hay 53 filas con ese label. Si la comparacion de paridad de :76-103 estuviera rota y devolviera siempre mismatches = 0, este bloque daria 53 igual y el gate pasaria. Lo corri verbatim en DEV: da 53, confirmado. Y da 53 por la razon equivocada. ## EL CONTROL REAL, VISTO FALLAR (en DEV) Replique la CTE 'resolved' del archivo tal cual y la corri dos veces: - baseline, sin mutar: n = 5558, mismatches = 0 - mutada de verdad: con s.label = 'ELECTRICO-CABA' resolviendo al unidad_propulsora_id de la fila aprobada de GNC en lugar del propio, dentro de la misma CTE: n = 5558, mismatches = 53 Asi que la comparacion SI discrimina, y ahora esta visto. Pero ese mutante es mio y no esta en el archivo. Si fase 1 se re-shipea, el mutante que corresponde es este, no el de :106-114. ## LO QUE ESTA BIEN, verificado en LAS DOS bases - el pin de drift md5(prosrc) = e7ce54fc36f39795fb7feede4150b5b2 matchea en movilba y en movilba_dev. Un pin unico para dos bases es fragil por diseno, pero hoy es correcto. - el denominador hardcodeado 5558 es el real en las dos: snapshot max(id) = 11 en PROD y 21 en DEV, ambos con 5558 filas en rutap_rows. Los ids de snapshot difieren y el conteo no. - la distribucion de labels es identica: GO 4875, GNC 626, ELECTRICO-CABA 53, ELECTRICO 4. Suma 5558, CERO nulos y cero labels fuera de los cuatro. Importa porque mismatches cuenta old_id IS NULL como falla, asi que un solo valor en blanco o un quinto label abortaria el gate - no dispara hoy, pero es una propiedad de ESTE snapshot, no permanente. - las cuatro filas aprobadas del resolver: ELECTRICO -> 37, ELECTRICO-CABA -> 37, GNC -> 157, GO -> 124. El CASE viejo mandaba ELECTRICO y ELECTRICO-CABA al mismo valor_opcion 'ELECTRICO', asi que la paridad se sostiene y el colapso semantico de ELECTRICO-CABA en 37 es PREEXISTENTE, no lo introduce este archivo. Coincide con el mapeo fijo de .claude/rules/catalogos.md. - LA BAJA DE W3 ESTA CUBIERTA, y era el riesgo serio: W3 updateaba keyeando sobre upper(btrim(h.raw_unidad_propulsora)) = 'GNC' en la tabla NORMALIZADA, mientras W1 keyea sobre la fuente del snapshot. Son predicados distintos, que es la forma clasica de 'dos escritores, se saca uno y quedan filas sin escribir'. Medido: 626 filas de vehiculo_historial_normalizado con raw GNC, y 626 de esas 626 alcanzables por la clave normalizada del resolver. 626/626, no queda ninguna huerfana. - las grafias crudas de la tabla normalizada son solo las cuatro mas 169 NULL. Los 169 NULL no estan en el snapshot vigente (que tiene cero), asi que son anteriores y W3 tampoco los tocaba. ## RESIDUO, no bloqueante normalizar_clave_chasis es la IDENTIDAD en los cuatro labels: GO->GO, GNC->GNC, ELECTRICO->ELECTRICO, ELECTRICO-CABA->ELECTRICO-CABA, los cuatro true. Asi que el join nuevo acopla la resolucion de PROPULSION a un normalizador de CHASIS sin que eso haga nada hoy. Si ese normalizador se toca por un motivo de chasis, la propulsion cambia en silencio y ningun test lo cubre. No pido cambiarlo en fase 1. ## SIN EJERCITAR - la paridad y el mutante en PROD. No tengo SELECT sobre rutap.unidad_propulsora_resolver ahi, asi que el 5558/5558 de PROD que reporta db04 lo afirma db04 y yo NO lo pude reproducir. Mi cobertura de PROD se limita a md5, denominador, distribucion de labels, grants y catalogo de pg_proc/pg_class. - la funcion parcheada corriendo de verdad: nadie invoco proyectar_snapshot con el cuerpo nuevo, en ninguna base. La igualdad byte a byte de :122 compara el texto instalado contra el texto parcheado, no el comportamiento. - el control de owner/ACL de :125-128 no puede fallar: CREATE OR REPLACE FUNCTION preserva proowner y proacl por definicion, asi que ese IF es una linea verde, no un control. - el alias motor-fabricante y la fase 2, fuera de alcance por pedido de db04.
  • db04-mba-l-ca progress · 1w ago
    Audit FAIL 2883ec7e addressed in revised phase-1 evidence SQL SHA256 b73dddcebe692202765088d3e85053b5a69455e705a5b2d6beb4a70326a4c841 at /data-lezama/movilba/evidencia/MOVILBA-490/2026-10-08-propulsion-fk-cutover-PROD-Y-DEV.sql. PROD-only GRANT SELECT on rutap.unidad_propulsora_resolver to svc_nextjs now occurs before projector EXECUTE in same default-ROLLBACK transaction; added ACL-delta guard asserts exactly that one privilege added, none removed. DEV has no svc_nextjs role, so checks existing caller SELECT without GRANT. Replaced fake mutant with same resolved CTE comparing baseline catalog ID and actual wrong-target ELECTRICO-CABA->GNC ID: baseline 0 mismatches / mutant 53, 5558 rows, both bases. Rehearsed script PROD superuser then DEV under ROLLBACK, both clean; PROD separate BEGIN/GRANT/SET LOCAL ROLE svc_nextjs/SELECT/ROLLBACK proved caller can read 4 approved resolver rows after grant. Post-rollback projector prosrc md5 e7ce54fc both bases, PROD app SELECT false and relacl NULL, so no persistent change. Resubmitting exact SHA to audit; no apply.
  • db04-mba-l-ca progress · 1w ago
    Fresh SELECT-only PROD installed-body pin for 340→490→367 sequence: movilba, 2026-10-08 19:16:45.511664 UTC, rutap.proyectar_snapshot(bigint) md5(prosrc)=e7ce54fc36f39795fb7feede4150b5b2. ef750d17 is not current. Revised phase-1 file remains under audit; apply held behind db03's 340 lock and audit PASS. Recheck installed md5 immediately after 340 release and before any 490 apply.
  • audit-mba-l-cc audited · 1w ago
    audit pass: Fase 1 revisada: 2026-10-08-propulsion-fk-cutover-PROD-Y-DEV.sql sha256 b73dddce, 188 lineas. Audit NO lo corrio (escribe). Lectura completa mas sondas READ-ONLY en PROD y DEV, y la CTE revisada corrida por mi en DEV. FUERA DE ALCANCE: alias motor-fabricante, fase 2, el apply.
    payload
    {"scope":"Fase 1 revisada: 2026-10-08-propulsion-fk-cutover-PROD-Y-DEV.sql sha256 b73dddce, 188 lineas. Audit NO lo corrio (escribe). Lectura completa mas sondas READ-ONLY en PROD y DEV, y la CTE revisada corrida por mi en DEV. FUERA DE ALCANCE: alias motor-fabricante, fase 2, el apply.","outcome":"pass","evidence":"Las dos causas del FAIL de 19:16Z cerradas. (1) GRANT: :91 da SELECT a svc_nextjs ANTES del EXECUTE, preflight :88 aborta si el rol no existe o YA tiene SELECT, verificacion :92, delta de ACL :97-114 con aclexplode y EXCEPT ALL exigiendo 1 agregado / 0 removidos / el agregado grantee=svc_oid SELECT no grantable, re-chequeo :177. Rama DEV :116 verifica que el caller lee. GRANT MINIMO Y SUFICIENTE, medido: svc_nextjs es el UNICO rol de login no-owner con SELECT sobre rutap.rutap_rows, o sea el unico caller no-owner posible; los otros seis (svc_api, svc_staging, next_app, movilba_app_user, audit_ro, elazar-devandtesting) tienen EXECUTE del proyector por el default PUBLIC pero NO leen rutap_rows, asi que ya fallaban antes del resolver - preexistente. Sin segunda capa de privilegio: normalizar_clave_chasis es expresion pura, has_function_privilege(svc_nextjs) = true. (2) MUTANTE REAL: :128-129 y :155-156 lo ponen DENTRO de la misma CTE resolved, ELECTRICO-CABA al id de GNC, aserto 53. Corri la CTE revisada en DEV read-only: n = 5558, mismatches = 0, mutant_failures = 53 - los tres valores que el archivo asevera, medidos por mi, control VISTO FALLAR sobre la comparacion que decide. LA REHEARSAL NO DEJO NADA EN PROD: relacl del resolver sigue NULL, has_table_privilege(svc_nextjs,SELECT) = false, md5(prosrc) sigue e7ce54fc. HALLAZGO NUEVO que la paridad NO ve: normalizar_clave_chasis reescribe GAS|GNC|CNG a GNC y el cuerpo actual la llama CERO veces, asi que el join nuevo es la primera llamada. Hoy no hay fila GAS ni CNG en todo rutap_rows, pero un valor futuro resolveria a GNC donde el CASE viejo dejaba NULL. Auto-mapeo silencioso: por la regla del 2026-09-24 es una DECISION. No bloquea. SIN EJERCITAR: paridad y mutante en PROD (no tengo SELECT sobre el resolver ahi); la funcion parcheada nunca se invoco; el guard de ACL no se vio fallar; el IF :173-176 no puede fallar, CREATE OR REPLACE preserva proowner y proacl. De un solo uso: tras el apply, re-correrlo aborta en :89.","reportRef":null}
  • db04-mba-l-ca progress · 1w ago
    Audit PASS b73dddce superseded BEFORE apply to avoid introducing an unapproved future GAS/CNG->GNC mapping through rutap.normalizar_clave_chasis. Revised phase-1 SQL SHA256 1b340b5340adc58c69475491c0d761e37c70a7aa48b6dd8b30dbdafed9133ec4 replaces that call with exact already-uppercase/btrimmed s.prop_n in the installed join and s.label in the parity CTE; fails preflight if approved exact GAS/CNG resolver aliases exist. SELECT-only synthetic GAS/CNG in both bases: old CASE NULL, new exact FK NULL, rejected chassis normalizer would yield GNC. Full script rehearsed PROD superuser then DEV under ROLLBACK, 5558/5558 parity and real wrong-target mutant 53 both bases. No apply; resubmitting new SHA to audit. db03's MOVILBA-340 projector lock still blocks apply.
  • db04-mba-l-ca progress · 1w ago
    CORRECTION of my prior 'ef750d17 stale/not installed' wording: same installed function has two valid hashes of DIFFERENT strings. Measured PROD 19:27:43Z and DEV 19:27:45Z: md5(prosrc)=e7ce54fc36f39795fb7feede4150b5b2, length 35723; md5(pg_get_functiondef(oid))=ef750d175e3a65bf889ceeacc78f720a, length 35964. 490 uses prosrc, 340/367 use functiondef; no conflict. Evidence/query: /data-lezama/movilba/evidencia/MOVILBA-490/2026-10-08-projector-two-digests-correction.md. No migration applied, 340 lock held.
  • audit-mba-l-cc audit-finding · 1w ago
    # MOVILBA-490 phase 1 — audit verdict on 1b340b53 (NOT APPLIED) audit-mba-l-cc, 2026-10-08. `1b340b5340adc58c69475491c0d761e37c70a7aa48b6dd8b30dbdafed9133ec4`, 194 lines, `/data-lezama/movilba/evidencia/MOVILBA-490/2026-10-08-propulsion-fk-cutover-PROD-Y-DEV.sql`. **This supersedes my b73dddce PASS before apply.** That verdict is withdrawn as the operative one; nothing was applied under it. audit did not run the file — it replaces a function, and the SELECT-only standing order holds. Everything below is static reading plus my own read-only SQL, except the PROD parity, which I could not execute (see NOT EXERCISED). ## Verified by my own measurement 1. **The exact-key change is real and complete.** `normalizar_clave_chasis` now appears **0** times in the file. The resolver join keys on the already upper/btrimmed value at `:26` (`upr.valor_fuente_normalizado = s.prop_n`) and `:145` (`u.valor_fuente_normalizado = s.label`). 2. **`s.prop_n` really is pre-normalized, as db04 asserts.** The installed projector body line 151 reads `upper(btrim(coalesce(r.unidad_propulsora,''))) prop_n`. md5 of `prosrc` is `e7ce54fc36f39795fb7feede4150b5b2` in **both** bases and matches the file's pin at `:65`. 3. **My own residue finding on the b73dddce revision is now closed, and the closure is measured, not asserted.** Future `GAS`/`CNG` book values resolve to **NULL on both sides** — old CASE NULL, exact FK NULL — while `normalizar_clave_chasis` would have returned **`GNC`** for each. So removing the normalizer from the join is precisely what keeps the silent auto-map from arriving, and the GAS/CNG decision stays Elazar's. Measured in DEV; the function is identical in both bases. 4. **The GAS/CNG preflight abort at `:86-89` is live and currently vacuous in the right way:** 0 approved aliases for `GAS`/`CNG` today, so it does not abort spuriously, and it will abort if one is ever approved without a decision. 5. **DEV parity and the mutant, run by me on the exact key:** n=**5558**, mismatches=**0**, mutant_failures=**53**. **The control was seen to fail on the comparison that decides** — resolving `ELECTRICO-CABA` to GNC's approved row makes exactly the 53 affected rows mismatch. The mutant now lives inside the `resolved` CTE, so unlike the b73dddce `:106-114` block it mutates the comparison rather than counting a precondition. 6. **The PROD preconditions for the GRANT all hold, and the GRANT is genuinely required.** Measured in PROD: `svc_nextjs` exists; it **cannot** currently read `rutap.unidad_propulsora_resolver` (so the `:92-96` preflight will not abort spuriously); the projector is `prosecdef = f`, i.e. SECURITY INVOKER, so the caller's own privileges decide and a patched body referencing the resolver would fail for the app without the GRANT; `relacl IS NULL` on the resolver, so the `aclexplode(coalesce(relacl, acldefault(...)))` fallback is the correct baseline and the delta guard's "exactly 1 added, 0 removed" is the right assertion. This is the defect that made b73dddce's predecessor FAIL, and it is closed. 7. **The GRANT is minimal and sufficient.** `svc_nextjs` is the only non-owner login role that can reach the projector as a caller in PROD, and the GRANT is `SELECT` on one table, guarded to `current_database()='movilba'`. ## Findings — none blocking 1. **The parity gate aborts on a blank that production tolerates.** The projector coalesces a NULL raw value to `''`; the parity CTE keys on `upper(btrim(r.unidad_propulsora))`, leaving NULL as NULL, and `mismatches` counts `old_id IS NULL OR new_id IS NULL` as a failure. So one NULL or all-blank `unidad_propulsora` in the pinned snapshot aborts the migration, although production would resolve it to `''`, leave `prop_resuelta` NULL and flag `unidad_propulsora_no_resuelta` at body line 233. Measured: **0 NULL and 0 blank-after-btrim** in the current snapshot of **both** bases, so this is latent, not active. It is brittleness in the gate, not in the change. 2. **The denominator 5558 is hardcoded at `:164`.** It is correct for the current pinned snapshot in both bases (I measured 5558 in each), but a new snapshot changes it and the abort message will read as a parity failure rather than a stale constant. 3. **The file is single-use by construction.** A second run aborts at the `:92-96` preflight, because `has_table_privilege` for `svc_nextjs` will by then be true. Correct as a guard; worth knowing before someone re-runs it to "confirm". ## NOT EXERCISED - **PROD parity, by audit: `ERROR: permission denied for table unidad_propulsora_resolver`.** The repo's `PROD_DATABASE_URL` connects as `svc_nextjs`, which is exactly the role the file is about to GRANT, so audit cannot reproduce the PROD 5558/0/53 before the GRANT exists. db04's census on MOVILBA-648 confirms `audit_ro` cannot read it either (SELECT on 1/25 `rutap.*` relations, `rutap` USAGE false). **PROD parity therefore rests on db04's rehearsal, which I read and did not reproduce.** This is the concrete instance of 648, not a standing complaint. - The file itself was never executed by audit in either base. - The post-apply byte-equality check at `:176` and the owner/ACL check at `:179-182` have not been observed to run. As on the previous revision, the owner/ACL check cannot fail by construction: `CREATE OR REPLACE FUNCTION` preserves both. - No projector run against real data after the patch: parity compares the two resolution paths, it does not execute `proyectar_snapshot`. - The W3 removal's effect on `rutap.vehiculo_historial_normalizado` was not re-measured on this revision.
  • audit-mba-l-cc audited · 1w ago
    audit pass: 1b340b53, 194 lineas, supersede mi PASS de b73dddce ANTES de aplicar; nada se aplico bajo aquel. Lectura estatica + SQL read-only mio. NO corri el archivo (reemplaza una funcion). Paridad DEV corrida por mi; paridad PROD NO EJERCITADA POR AUDIT, permission denied. Detalle completo en audit-finding.
    payload
    {"scope":"1b340b53, 194 lineas, supersede mi PASS de b73dddce ANTES de aplicar; nada se aplico bajo aquel. Lectura estatica + SQL read-only mio. NO corri el archivo (reemplaza una funcion). Paridad DEV corrida por mi; paridad PROD NO EJERCITADA POR AUDIT, permission denied. Detalle completo en audit-finding.","outcome":"pass","evidence":"VERIFICADO POR MI: normalizar_clave_chasis queda en 0 ocurrencias y el join keyea la etiqueta exacta ya upper/btrimmeada en :26 y :145. El cuerpo instalado linea 151 define prop_n con upper(btrim(coalesce(...))), asi que lo que afirma db04 se sostiene, y el md5 del prosrc es e7ce54fc en LAS DOS bases, igual al pin de :65. MI HALLAZGO ANTERIOR QUEDA CERRADO Y MEDIDO: GAS y CNG resuelven NULL en AMBOS lados mientras el normalizador habria devuelto GNC, asi que sacarlo del join es lo que evita el auto-mapeo silencioso y deja la decision en Elazar. El abort de :86-89 esta vivo y hoy no aborta de mas: 0 alias aprobados. PARIDAD DEV CORRIDA POR MI con la clave exacta: n=5558, mismatches=0, mutant_failures=53, y EL CONTROL SE VIO FALLAR sobre la comparacion que decide, porque el mutante ahora vive DENTRO del CTE resolved y no cuenta una precondicion como el bloque :106-114 anterior. PRECONDICIONES PROD VERIFICADAS POR MI: svc_nextjs existe y hoy NO puede leer el resolver, el projector es prosecdef=f o sea SECURITY INVOKER y por eso el GRANT es necesario, y relacl IS NULL, asi que el guard de 1 agregado / 0 quitados es la asercion correcta. Esa era la causa del FAIL anterior y queda cerrada. HALLAZGOS no bloqueantes: (1) el gate aborta ante un blanco que produccion tolera, porque el projector coalesce NULL a vacio y el CTE deja NULL contandolo como falla; medido 0 NULL y 0 vacios en AMBAS bases, latente. (2) el denominador 5558 esta hardcodeado en :164. (3) de un solo uso: una segunda corrida aborta en :92-96. SIN EJERCITAR: paridad PROD por permission denied sobre el resolver, porque PROD_DATABASE_URL conecta como svc_nextjs, justo el rol que el archivo va a GRANTear, y el censo de 648 confirma que audit_ro tampoco lo lee; descansa en el ensayo de db04, que lei y no reproduje. Tampoco: el owner/ACL de :179-182 no puede fallar porque CREATE OR REPLACE preserva ambos; ninguna corrida real del projector post-patch.","reportRef":null}
  • db04-mba-l-ca progress · 1w ago
    After MOVILBA-340 applied PROD then DEV, re-pinned phase-1 SQL md5(prosrc) e7ce54fc... -> 017b35d5d9578da56ef5ee48bf031f6b (not md5(functiondef), which is edb0748ad9ed895afe1616bab6b9d03f). New SHA256 f4f0be9f5bd5af477d2585ce49485b3dd9577c64b9bf9c51a3b70046fa5ea918, same path /data-lezama/movilba/evidencia/MOVILBA-490/2026-10-08-propulsion-fk-cutover-PROD-Y-DEV.sql. One-line pin change only. ROLLBACK rehearsal PROD then DEV passed: 5558/5558 old-vs-FK parity and wrong-target mutant 53 each. Post-rollback installed prosrc md5 remains 017b35d5 in both; PROD svc_nextjs resolver SELECT remains false. No live 490 DDL/GRANT applied. Prior audit PASS on 1b340b53 does NOT cover new bytes; resubmitting to audit.
  • audit-mba-l-cc audited · 1w ago
    audit pass: 490 fase 1, SHA f4f0be9f, las 194 lineas, PROD y DEV: pin nuevo, los dos anchors contra el cuerpo post-340, simulacion del replace, valores almacenados vs FK, paridad y mutante. SIN EJERCITAR: la pata PROD del resolver (conecto como svc_nextjs y no lo lee), el apply, y el bloque ACL :92-120.
    payload
    {"scope":"490 fase 1, SHA f4f0be9f, las 194 lineas, PROD y DEV: pin nuevo, los dos anchors contra el cuerpo post-340, simulacion del replace, valores almacenados vs FK, paridad y mutante. SIN EJERCITAR: la pata PROD del resolver (conecto como svc_nextjs y no lo lee), el apply, y el bloque ACL :92-120.","outcome":"pass","evidence":"Probes read-only propias en las dos bases: md5(prosrc)=017b35d5 coincide con el pin :65 en movilba y movilba_dev (fdef edb0748a, prosrc_len 38375). old_join hits=1 y old_w3 hits=1 en las dos, reproduciendo la aritmetica :68-71 - 340 no toco ninguno de los dos anchors. Simulacion en memoria del replace(replace()): normalizar_si_no 24 antes y 24 despues (divisor correcto 23; mi primera corrida imprimio 23 por dividir por 24), delta_len -575, old_join hits=0 despues. Un re-run ABORTA en el pin :65, no es idempotente. SET unidad_propulsora_id 1->0 y el campo queda escrito una sola vez en W1: up.id prop_resuelta en linea instalada 163, insert en 292 contra la lista de columnas de 256. Valores: stored vs FK en vehiculo_historial_normalizado del snapshot pineado, 5558 filas 0 difieren 0 nulos 0 etiquetas vacias (el archivo NO mide esto - compara CASE vieja contra FK, nunca contra lo almacenado, y la override W3 era la escritora efectiva de las filas GNC). Paridad y mutante reproducidos en DEV: 5558/0/53. Precondiciones: 4 etiquetas aprobadas, 0 alias GAS/CNG, 1 FK a opciones. Fuente: 5558 filas, 53 ELECTRICO-CABA, 0 etiquetas fuera de las cuatro, 0 nulos. NO pude diffear contra 1b340b53: no guarde los bytes y el archivo se sobrescribio in situ. Corroborado en parte - 194 lineas en los dos, los cuatro anchors en las mismas lineas, y mi probe de 1b340b53 registro su pin como e7ce54fc, mismo campo md5(prosrc), asi que la LINEA del pin cambio como se afirma. No prueba que no cambio nada mas, por eso re-audite de cero.","reportRef":null}
  • audit-mba-l-cc audited · 1w ago
    audit pass: 490 fase 1, SHA f4f0be9f, las 194 lineas, PROD y DEV: pin nuevo, los dos anchors contra el cuerpo post-340, simulacion del replace, valores almacenados vs FK, paridad y mutante. SIN EJERCITAR: la pata PROD del resolver (conecto como svc_nextjs y no lo lee), el apply, y el bloque ACL :92-120.
    payload
    {"scope":"490 fase 1, SHA f4f0be9f, las 194 lineas, PROD y DEV: pin nuevo, los dos anchors contra el cuerpo post-340, simulacion del replace, valores almacenados vs FK, paridad y mutante. SIN EJERCITAR: la pata PROD del resolver (conecto como svc_nextjs y no lo lee), el apply, y el bloque ACL :92-120.","outcome":"pass","evidence":"Probes read-only propias en las dos bases: md5(prosrc)=017b35d5 coincide con el pin :65 en movilba y movilba_dev (fdef edb0748a, prosrc_len 38375). old_join hits=1 y old_w3 hits=1 en las dos, reproduciendo la aritmetica :68-71 - 340 no toco ninguno de los dos anchors. Simulacion en memoria del replace(replace()): normalizar_si_no 24 antes y 24 despues (divisor correcto 23; mi primera corrida imprimio 23 por dividir por 24), delta_len -575, old_join hits=0 despues. Un re-run ABORTA en el pin :65, no es idempotente. SET unidad_propulsora_id 1->0 y el campo queda escrito una sola vez en W1: up.id prop_resuelta en linea instalada 163, insert en 292 contra la lista de columnas de 256. Valores: stored vs FK en vehiculo_historial_normalizado del snapshot pineado, 5558 filas 0 difieren 0 nulos 0 etiquetas vacias (el archivo NO mide esto - compara CASE vieja contra FK, nunca contra lo almacenado, y la override W3 era la escritora efectiva de las filas GNC). Paridad y mutante reproducidos en DEV: 5558/0/53. Precondiciones: 4 etiquetas aprobadas, 0 alias GAS/CNG, 1 FK a opciones. Fuente: 5558 filas, 53 ELECTRICO-CABA, 0 etiquetas fuera de las cuatro, 0 nulos. NO pude diffear contra 1b340b53: no guarde los bytes y el archivo se sobrescribio in situ. Corroborado en parte - 194 lineas en los dos, los cuatro anchors en las mismas lineas, y mi probe de 1b340b53 registro su pin como e7ce54fc, mismo campo md5(prosrc), asi que la LINEA del pin cambio como se afirma. No prueba que no cambio nada mas, por eso re-audite de cero.","reportRef":null}
  • audit-mba-l-cc audit-finding · 1w ago
    audit-mba-l-cc 2026-10-08 20:29Z - detalle del PASS de fase 1 sobre sha256 f4f0be9f. NOTA DE REGISTRO: hay DOS eventos audited identicos, 20:28:06Z y 20:28:34Z, los dos mios y los dos el mismo veredicto. El segundo es un reenvio mio: lei el listado de bwi events con tail sobre una lista ordenada de mas nuevo a mas viejo, vi el evento de db04 de las 20:17 arriba y concluí que el primer PASS no habia quedado. Habia quedado. Un solo veredicto, no dos. # MOVILBA-490 phase 1 @ f4f0be9f — audit verdict: PASS (NOT APPLIED by audit) audit-mba-l-cc, 2026-10-08. /data-lezama/movilba/evidencia/MOVILBA-490/2026-10-08-propulsion-fk-cutover-PROD-Y-DEV.sql f4f0be9f5bd5af477d2585ce49485b3dd9577c64b9bf9c51a3b70046fa5ea918 194 lines audit did not execute this file in either base. SELECT-only standing order; every figure below comes from read-only probes against the installed post-340 projector. ## I could NOT diff against 1b340b53, so this is a full re-audit db04's claim is "only the one-line `old_md5(prosrc)` pin changed". **I cannot verify that claim as stated.** I did not retain the `1b340b53` bytes, and the file was overwritten in place at the same path, so the superseded version no longer exists anywhere I can read. What I can corroborate: line count is 194 both times; the four anchors my prior verdict cited by line still sit on their old line numbers (exact-key join `:26` and `:145`, GAS/CNG abort `:86-89`, hardcoded `5558` at `:164`); and my own `1b340b53` probe recorded that revision's pin as `e7ce54fc36f39795fb7feede4150b5b2`, also `md5(prosrc)`, so the pin LINE did change in exactly the way claimed — same field, same representation, value advanced to the post-340 body. That is *consistent with* a one-line in-place swap and proves it for the pin line, but it does not prove nothing else changed. So I re-verified the file's load-bearing properties from scratch rather than inheriting them. The PASS stands on that re-verification, not on db04's characterization of the delta. ## Verified by my own measurement 1. **The new pin matches the installed body in BOTH bases.** `md5(prosrc)` of `rutap.proyectar_snapshot(bigint)` = `017b35d5d9578da56ef5ee48bf031f6b` in `movilba` and in `movilba_dev`, matching `:65`. `md5(pg_get_functiondef(...))` = `edb0748ad9ed895afe1616bab6b9d03f`, `length(prosrc)` = 38375, identical in both. db04's "distinct representation" note is correct: the two digests describe the same body under different serializations, which is why 340 and 490 pinning different ones was never a conflict. 2. **THE DECISIVE CHECK — 340's rewrite left both text anchors intact, exactly once, in both bases.** This is the thing a re-pin cannot carry and the only way 490 could have been silently broken by 340: `old_join` hits = 1 and `old_w3` hits = 1 in each base, reproducing the file's own `:68-71` arithmetic. 340 patched 24 si/no sites and none of them is inside either anchor — `old_w3` keys on bare `upper(btrim(h.raw_unidad_propulsora))`, consistent with `raw_unidad_propulsora` being one of the four columns 340's gate deliberately excludes. 3. **The patch does not disturb 340's work.** I simulated `replace(replace(...))` in memory, read-only, against the installed body: `rutap.normalizar_si_no(` occurrences are **24 before and 24 after**, unchanged, re-counted with the correct divisor — my first run printed 23/23 because I divided by 24 while `length('rutap.normalizar_si_no(')` is 23; 24 is the measured figure and matches 340's own `:282` assertion. (The other three divisors in that probe were right.) `delta_len` = -575 and `old_join` hits = 0 after the simulated patch. That makes a re-run **abort**, not succeed as a no-op: it dies at the `:65` pin, and would die at `:68` even if the pin passed. Not idempotent — guarded. 4. **Removing the W3 override does not orphan the column, and the stored VALUES do not move.** Printed every projector line naming `unidad_propulsora` and `prop_resuelta`: the join produces `up.id prop_resuelta` at installed line 163, it is carried into the W1 INSERT at line 292 against the column list at line 256, and lines 342-350 are the W3 override the patch deletes. `SET unidad_propulsora_id` goes 1 → 0 and `raw_unidad_propulsora` 3 → 2, so after the cutover the field is written once, in W1, from the FK. The file's parity check compares old-W1 CASE against the new FK and never against what the old body actually STORED, and the W3 override was the old body's effective writer for the GNC rows — so I measured that separately in DEV: stored `unidad_propulsora_id` on `rutap.vehiculo_historial_normalizado` for the pinned snapshot against the FK-resolved id, keyed on `upper(btrim(coalesce(raw_unidad_propulsora,'')))`, is **5558 rows, 0 differing, 0 stored NULL, 0 blank labels**. The cutover is value-neutral on today's data, which the file's own checks do not establish. 5. **Parity and the mutant reproduce exactly, in DEV.** Running the file's own `:128-163` CTE read-only: n = 5558, mismatches = 0, mutant_failures = 53. Matches db04's rehearsal figures. Resolver preconditions also hold: 4 approved labels among the four source keys, 0 approved `GAS`/`CNG` aliases, 1 FK to `public.opciones`. 6. **The source population is what the hardcodes assume.** Max snapshot has 5558 rows, 53 `ELECTRICO-CABA`, **0 labels outside the four** and **0 NULL** `unidad_propulsora` cells. 7. **The wrong-target mutant is a real control, not a green line.** It re-resolves `ELECTRICO-CABA` to GNC's approved row through the same comparison and must fail on exactly the 53 affected rows; it does. A mutant that discriminates on the one alias where old CASE and new FK could plausibly disagree is the right choice of mutant. ## Findings — none blocking 1. **The parity harness's source expression is not the projector's, and the divergence hides a blank.** `:129` reads `upper(btrim(r.unidad_propulsora))`; the projector reads `upper(btrim(coalesce(r.unidad_propulsora,'')))` (installed line 154). A NULL cell therefore yields label NULL in the harness, matches neither join, counts as a mismatch and **aborts the migration at `:165`** — while in the projector that same cell becomes `''`, matches no approved row, and is **not** flagged: the flag at line 235 is `prop_resuelta IS NULL AND prop_n <> ''`, so the empty-string case is excluded by construction and the row stores a silent blank `unidad_propulsora_id`. So the two behaviours diverge in both directions — the instrument is stricter than the subject on a NULL cell, and the subject is silent where it should arguably flag. Latent only: 0 NULL cells and 0 blank labels today, both measured. New finding, not carried from `1b340b53`; the blank-not-flagged half is a projector observation, outside 490's scope, and belongs to whoever owns the no-resuelta flags. 2. **`n <> 5558` at `:164` pins the file to this snapshot.** Any re-import makes it abort. Safe polarity for a preflight, but it means the file has a shelf life tied to `max(rutap.snapshots.id)`, not just to the projector digest. Carried from my prior verdict. 3. **490 asserts nothing about 340's normalizer surviving its own rewrite.** I proved it survives (24 → 24, finding above), and it survives *provably* because neither deleted anchor contains a normalizer call — but the file takes that on faith. One line (`IF occurrences of 'rutap.normalizar_si_no(' in new_definition <> 24 THEN RAISE`) would make the next re-pin in this chain self-checking instead of depending on an auditor re-deriving it. Cheap, and the chain has two more re-pins to go (367, then 749). 4. **The PROD ACL delta block at `:103-120` is sound and is the part I would have expected to go wrong.** It explodes before/after ACLs and demands exactly one added entry, zero removed, and that the added one is `svc_nextjs`/`SELECT`/not-grantable. That is a real assertion rather than a privilege smoke test. Unexercised by me — see below. ## NOT EXERCISED - **The entire PROD leg.** My PROD connection is `svc_nextjs`, which `has_table_privilege(...,'rutap.unidad_propulsora_resolver','SELECT')` = **false** — the concrete instance of MOVILBA-648. So in PROD I verified only the digest pin, the two anchor counts and the source population; I could not run the parity CTE, the resolver preconditions, or anything touching the resolver table. All PROD parity evidence is db04's rehearsal, which I read and did not reproduce. Findings 1 and 5 and the stored-value check in item 4 are measured in DEV only. **Why that gap does not block the PASS:** the file carries its own PROD-side protection — the `:164-170` parity and mutant assertions run inside the same transaction, against PROD's own resolver and catalog, and abort before `EXECUTE new_definition` on any divergence. The unexercised leg is audit's reproduction of the evidence, not the file's coverage of the risk. - The `:92-120` PROD-only branch: the `svc_nextjs` preflight, the GRANT, and the ACL delta arithmetic have been exercised by nobody but db04's ROLLBACK rehearsal. - No apply, no COMMIT, in either base, by audit. No app surface (ficha, XLSX, PDF, API) opened against a projection from the patched body. - Finding 1's NULL path is exercised by nothing — not by the data, not by the file. - I did not re-audit phase 2 or anything downstream; this covers the 194 lines of phase 1 only. ## Sequencing (unchanged, and coder03 has since published the same order) 340 applied → **490 `f4f0be9f`** → 367 re-pinned off 490's post-apply digests → 749 (chr13) last. Each re-pin is a new SHA with its own verdict. My PASS on `1b340b53` did not travel here, and this PASS does not travel to a 490 re-pinned for any later reason.
  • db04-mba-l-ca progress · 1w ago
    Phase 1 APPLIED 2026-10-08 after audit PASS on exact SHA256 f4f0be9f5bd5af477d2585ce49485b3dd9577c64b9bf9c51a3b70046fa5ea918. Order: PROD movilba COMMIT then postcheck, DEV movilba_dev COMMIT then postcheck. Each apply printed FK parity 5558/5558 and wrong-target mutant 53. Both installed bodies now identical: md5(prosrc)=ade30cdf61b58fb67cbd5c2b2112fced (length 37800), md5(pg_get_functiondef)=3ba101fbf32733c941af10d836b5c685. Function owners preserved movilba_owner PROD / movilba_user DEV, proacl NULL. PROD resolver ACL now adds exactly svc_nextjs SELECT; has_table_privilege=true. DEV caller SELECT=true. No real snapshot import/projector execution under new body yet. 367 may re-pin against these digests; 749 follows 367. SQL evidence path /data-lezama/movilba/evidencia/MOVILBA-490/2026-10-08-propulsion-fk-cutover-PROD-Y-DEV.sql.
  • db04-mba-l-ca progress · 1w ago
    Post-apply SHIP candidates now in repo: docs/migrations/2026-10-08-movilba-490-propulsion-fk-cutover-PROD-Y-DEV.sql (byte-identical SHA f4f0be9f to audited/applied evidence) and docs/migrations/README.md (new 490 row only, documents PROD→DEV COMMIT, installed prosrc/functiondef digests, PROD svc grant, 5558 parity/53 mutant, real import unexercised). Shared tree has unrelated lane edits; no repo lint/type/test run per lane handoff rule. Requesting post-apply audit on these two paths before SHIP to coder03.
  • audit-mba-l-cc audited · 1w ago
    audit pass: Post-apply de 490: verificacion propia del cuerpo instalado en PROD y DEV, la copia del repo docs/migrations/2026-10-08-movilba-490-propulsion-fk-cutover-PROD-Y-DEV.sql, y el hunk de 490 del README. FUERA: los otros 4 modificados y 8 untracked, de otros items. Gates: ninguno, el hunk es doc.
    payload
    {"scope":"Post-apply de 490: verificacion propia del cuerpo instalado en PROD y DEV, la copia del repo docs/migrations/2026-10-08-movilba-490-propulsion-fk-cutover-PROD-Y-DEV.sql, y el hunk de 490 del README. FUERA: los otros 4 modificados y 8 untracked, de otros items. Gates: ninguno, el hunk es doc.","outcome":"pass","evidence":"Post-apply medido por mi, read-only, las dos bases, no tomado de db04: md5(prosrc)=ade30cdf61b58fb67cbd5c2b2112fced y md5(pg_get_functiondef)=3ba101fbf32733c941af10d836b5c685, IDENTICOS en movilba y movilba_dev, len 37800. El cuerpo nuevo es el esperado por FORMA y no solo por digest: unidad_propulsora_resolver referenciado 1 vez, SET unidad_propulsora_id 0 veces (la override W3 se fue), marca MOVILBA-490 presente 1 vez, y normalizar_si_no sigue en 24 - o sea 490 no deshizo 340, que es exactamente lo que mi hallazgo 2 decia que el archivo no asserta. Owner y ACL conservados: PROD movilba_owner, DEV movilba_user, proacl NULL en las dos, iguales a mi medicion pre-apply. La concesion de PROD quedo: conecto como svc_nextjs y has_table_privilege sobre rutap.unidad_propulsora_resolver ahora da TRUE, donde antes del apply daba false - eso cierra por medicion propia la pata PROD que mi PASS pre-apply dejo SIN EJERCITAR, y es la instancia concreta de 648 resuelta para esta tabla. Copia del repo: sha256 f4f0be9f, identica byte a byte a la de /data-lezama/movilba/evidencia/MOVILBA-490/ por diff -q. Hunk del README: cada afirmacion verificable coincide con mis mediciones - PROD primero, PASS sobre f4f0be9f, resolver mas FK en lugar del CASE, eliminacion del override W3, GRANT solo SELECT a svc_nextjs por ser SECURITY INVOKER, paridad 5558/5558, mutante 53/53, los dos digests instalados, owner y ACL conservados. Declara SIN EJERCITAR que ningun import real corrio bajo el cuerpo nuevo, correcto y es el mismo limite que reporte yo. SIGUE SIN EJERCITAR: ningun import, y ninguna superficie de app (ficha, XLSX, PDF, API) abierta contra una proyeccion del cuerpo nuevo.","reportRef":null}
  • db04-mba-l-ca completed · 1w ago
    Phase 1 complete and live: audited SQL SHA f4f0be9f applied PROD then DEV, committed in docs/migrations and shipped by coder03 at patch 207 across PROD/DEV/pod. Projector now reads approved FK-backed rutap.unidad_propulsora_resolver rather than the hardcoded propulsion CASE; installed md5(prosrc)=ade30cdf61b58fb67cbd5c2b2112fced, md5(functiondef)=3ba101fbf32733c941af10d836b5c685 in both bases. No real import under new body yet. Scope boundary: manufacturer alias CUMMINS 6 CIL -> CUMMINS with its own FK carrier is NOT done here; successor MOVILBA-752 tracks that phase 2. MOVILBA-746 separately corrects the diagnostic-view false positive. This closure does not claim either remaining item complete.
tech-debt
4w ago by wi-cli-venus
1w ago
2026-10-08 20:46