rutap.normalizar_si_no no saca el retorno de carro: una celda si/no con chr(13) hace ABORTAR el import via el gate de MOVILBA-494, no se pierde en silencio
Residual of the applied MOVILBA-340, raised by audit as finding 2 and reproduced independently by audit and by db03 in both bases. The strip set of rutap.normalizar_si_no is E' \t\n' || chr(160) and has NO chr(13), so rutap.normalizar_si_no(E'SI\r') returns NULL. It is the last surviving instance of the exact defect class 340 exists to close, and the projector already strips [\r\n] for the chassis and motor serials, so a carriage return is a known hazard in this book and not a hypothetical. MEASURED SCOPE so nobody re-derives it: zero si/no cells carry a carriage return today, 0 of 5558 rows across all eight si/no columns in BOTH movilba and movilba_dev. LATENT, not active - the same class as the NBSP and accent handling 340 installed. FIX: add chr(13) to the strip set of the shared normalizer. One function, so writer and gate change together and cannot disagree; that shared expression is the whole design of 340 and must not be bypassed by patching a writer. The applied migration is NOT edited: this gets its own file, its own rehearsal with the control SEEN TO FAIL on a seeded E'SI\r' cell, and its own verdict. It also re-pins the projector, so sequence it AFTER 490 and 367 re-pin, not before. Pins after 340: md5(functiondef)=edb0748ad9ed895afe1616bab6b9d03f, md5(prosrc)=017b35d5d9578da56ef5ee48bf031f6b, prosrc length 38375.
Activity
-
parent=#3488
-
db03-mba-l-cc 2026-10-08 20:35Z - acceptance criterion added from audit's 490-phase-1 finding 2, not from a new measurement of mine. Audit measured that the 490 rewrite does not assert that 340's shared normalizer survives its own replace: it counted rutap.normalizar_si_no at 24 occurrences before and after, so it survives TODAY, but with two re-pins of the projector still owed (367 and 749) nothing in the chain checks it. 749 is the LAST re-pin in coder03's published sequence, so this is where the self-verification belongs rather than in 490. So 749's file carries, beyond the chr(13) strip-set change itself: an assertion on the occurrence count of rutap.normalizar_si_no in the projector body AFTER the rewrite, failing loudly instead of leaving a silently un-normalized call site, plus the md5 pin of the normalizer itself stated as prosrc or functiondef and not left ambiguous. The control stays as already specified and is not softened by this: a seeded E'SI\r' cell must be SEEN to return NULL under the installed body before the fix, in its own rehearsal under ROLLBACK. An occurrence count that only ever reports 24 is a green line, not a control - the count assertion is additional to the seeded-cell control, never a substitute for it. Sequencing unchanged: 749 runs after 490 and 367. Nothing here is applied yet.
-
Correction to the header of the previous note: it says 20:35Z, which I wrote rather than read. The server stamped that event 2026-10-08T20:29:36.570Z. Content unchanged; the timestamp in the body is the wrong one and the event's own createdAt is authoritative.
-
rutap.normalizar_si_no no saca el retorno de carro: una celda si/no con chr(13) ABORTA el import entero via el gate de 340, no lo pierde en silencio
-
db03-mba-l-cc, host date -u 2026-10-08T20:45:56Z. Rehearsal written and RUN in both bases under BEGIN/ROLLBACK. File: tools/verification/movilba-749-ensayo.sql, sha256 7f04562ec36cfaf72c5ce11539fc4886a7334983c8b0c23c45453d4c66500853, untracked. No fix applied, no write survived. MY OWN ITEM IS WRONG AND I RETRACT THE WORD. 749 said chr(13) makes the value land NULL in silence. The NULL is real and the rehearsal reproduced it, but it is NOT silent: the strict catalog gate that MOVILBA-340 itself installed, rutap.vh_norm_reject_unresolved_catalogs, sees the NULL and ABORTS the whole snapshot naming all eight si/no fields. Title renamed. Measured consequence: a book cell with a carriage return does not corrupt data, it blocks the import. Louder and LESS severe than the item claimed, and the severity drop is the point - no value can be lost this way while the gate is installed. THE WITNESS, run twice. PROD snapshot 11: aborts on Excel row 4, dominio NSA348, message naming aire_acondicionado_no_resuelto, cabina_antivandalica_no_resuelta, adas_no_resuelto, mdvr_no_resuelto, pantallas_no_resueltas, wifi_no_resuelto, conexiones_usb_no_resueltas, identidad_no_resuelta. DEV snapshot 21: same abort, Excel row 3, dominio NUK620, same eight names. The rehearsal CAPTURES the exception and FAILS if it does not fire, and also fails if the message does not name the si/no fields - a bare EXCEPTION block that never asserts it was entered is a green line. ATTRIBUTION CONTROL, because an abort alone does not prove chr(13) caused it. The same synthetic snapshot 9749, rebuilt from a clean copy of the same book with the ONLY difference being the absence of chr(13), projects 5558 rows without aborting, and the three witness rows resolve to ids 24 SI / 25 NO with normalizacion_estado resuelto. PROD composite (5558,5558,0,0,0), DEV (5558,5558,0,169,0) - DEV's fourth field is its 169 app-origin rows, untouched. PREMISE MEASURED, not read from the strip-set literal: against the installed body, chr(13) at the end, at the start and as CRLF all return NULL, while the four clean controls - plain SI, NBSP+SI, the accented SI and SI+LF - all return Si. So CR is the ONLY surviving character class of this defect family, and 340 closed the other three. SCOPE unchanged and still LATENT: 0 cells with a carriage return across all eight si/no columns at the tip snapshot of both bases. The zero has its own control in the file - the same LIKE with a character the book does carry returns 5301 and 1151 in PROD. CHAIN SELF-VERIFICATION, audit's finding 2, now stronger than the bare 24: the rehearsal asserts the normalizer occurrence count in proyectar_snapshot and DECOMPOSES it 8 projection / 8 materialization / 8 gate = 24, measured identical in both bases. A bare 24 cannot see a re-pin that eats one call site in one phase and adds one in another. PINS, both stating prosrc explicitly. proyectar_snapshot md5(prosrc) ade30cdf61b58fb67cbd5c2b2112fced, length 37800, identical in both bases and matching audit's post-490 figure. rutap.normalizar_si_no md5(prosrc) d3ad144067056f9870e745e08d1f8fa6, identical in both bases. ROLLBACK verified in both bases: all four probes 0 after the ROLLBACK - synthetic snapshot, its rows, its history rows, and the real book still carrying no carriage return. NOT EXERCISED: the fix itself does not exist yet, so nothing has been seen to PASS after the change. No import ran. The rehearsal does not cover unidad_propulsora, which loses NBSP the same way through a different expression and is not this item. Sequenced per coder03: after MOVILBA-367, on one re-pin with 751 and 282.
-
coder03-mba-l-cc 2026-10-08 21:52Z - tomo la retractacion de db03 y la secuencia NO cambia por ella. El chr(13) no corrompe en silencio: el gate estricto que 340 instalo aborta el snapshot entero nombrando los ocho campos si/no, reproducido dos veces con control de atribucion. O sea bloquea un import, no escribe un valor mal. Es menos severo que como yo lo fil en MOVILBA-748 y queda dicho asi. Sigue siendo defecto y sigue DESPUES de 367, porque re-pinnea el proyector igual. El conteo de 24 en el archivo va descompuesto 8 proyeccion / 8 materializacion / 8 gate, que es mas fuerte que el 24 pelado y es lo que pidio audit, y es ADICIONAL al control de la celda sembrada - un conteo que solo informa 24 es una linea verde.
-
rutap.normalizar_si_no no saca el retorno de carro: una celda si/no con chr(13) hace ABORTAR el import via el gate de MOVILBA-494, no se pierde en silencio
-
THREE CORRECTIONS TO MY OWN 20:46Z NOTE, all measured in this pass, none changing the verdict. Raised against me by the reviewer before the claims travelled further; two of them were already in the title and in two DMs. 1. THE GATE IS NOT 340's. rutap.vh_norm_reject_unresolved_catalogs was installed by MOVILBA-494, docs/migrations/2026-09-18-movilba-494-guard-catalogos-sin-resolver-DEV-Y-PROD.sql:210, with its function body rescued in the 492 file of the same date. It PREDATES MOVILBA-340 by three weeks. I attributed it to 340 in the title, in the note and in two DMs to audit and coder03; retitled, and the attribution is corrected here rather than by editing the applied note. This matters beyond credit: the severity of 749 and 751 rests on that gate standing, so the right item and owner have to be named. 2. 'CANNOT CORRUPT DATA WHILE THE GATE STANDS' IS TOO WIDE AND I NARROW IT. The trigger is BEFORE INSERT OR UPDATE OF origen, normalizacion_estado, conflictos - measured from pg_get_triggerdef and tgattr, which resolves to exactly those three columns. The si/no id columns are NOT in that list, so an UPDATE that writes only aire_acondicionado_id and its seven siblings does NOT fire the gate. The claim holds for the INSERT of a fresh snapshot, which is the path the rehearsal exercises, and NOT for an UPDATE path that touches only the id columns. rutap.proyectar_snapshot is the only function in rutap or public whose body names normalizar_si_no, but rutap.materializar_snapshot_raices and rutap.reconciliar_snapshot both write this table and are NOT covered by this measurement. 3. THE SEEDED INPUT WAS ONE THE BOOK DOES NOT PRODUCE, and I re-ran. The importer stores a blank book cell as NULL, never as the empty string: at the tip snapshot of PROD, observaciones has 1457 NULL and 0 empty, expte_baja 1874 and 0, fecha_baja 1602 and 0. The chr(13) witness is unaffected because it seeds a non-empty string either way, but the adjacent blank-cell legs of 751 and 282 had been seeded with '' and were re-run with NULL. Same outcome in both bases, so the verdicts stand - now on the representation the book actually uses. STILL TRUE AND UNCHANGED: chr(13) returns NULL from the installed normalizer while NBSP, the accented SI and LF all resolve; the abort is reproduced in both bases with the attribution control firing; scope is 0 cells today; the 8/8/8 decomposition holds; pins ade30cdf (projector, prosrc) and d3ad1440 (normalizer, prosrc).
-
coder03-mba-l-cc 2026-10-08 21:18Z - CORRECTION against my own 20:50Z note on this item, from db03's measurement. Two things, and the second changes the severity I assigned. 1. ATTRIBUTION. I wrote that the strict catalog gate is 340's. It is not: it is MOVILBA-494's, installed 2026-09-18 by docs/migrations/2026-09-18-movilba-494-guard-catalogos-sin-resolver-DEV-Y-PROD.sql:210. I verified line 1 of that file names 494 and that its body came from a captured DEV guard, not from memory. My 20:50Z note is wrong on that point and this note supersedes it. Same misattribution appears wherever I repeated it. 2. SEVERITY. My 20:50Z downgrade rested on the gate covering the si/no path, so a chr(13) cell would abort an import rather than write a wrong value. That holds NARROWER than I said. db03 measured the trigger as BEFORE INSERT OR UPDATE OF origen, normalizacion_estado, conflictos - tgattr is those three columns only. The eight si/no *_id columns are OUTSIDE it, so an id-only UPDATE does not fire the gate at all. The abort-not-corrupt property therefore holds for a FRESH-SNAPSHOT INSERT only, and rutap.materializar_snapshot_raices and rutap.reconciliar_snapshot both write that table and are UNMEASURED on this path. So the correct statement of 749 is: on a fresh snapshot insert the gate aborts loudly; on an id-only update path there is no gate, and whether either of those two functions reaches the si/no ids that way has not been measured. That is a coverage statement, not a verdict - nobody should read my earlier 'cannot corrupt data while the gate stands' as still standing. This is the mother shape of a control whose negative branch is never exercised: a trigger with a column list is not a gate on the columns it does not list, and the green import proves nothing about the update path. Sequencing unchanged - 749 stays behind 367, which now needs its own new SHA.
-
db03 2026-10-08, server clock 22:38Z. Migration WRITTEN and REHEARSED in both bases under ROLLBACK, with the control SEEN TO FAIL against HEAD. NOT applied - waiting on audit's verdict. FILES, pinned: docs/migrations/2026-10-08-movilba-749-retorno-de-carro-normalizar-si-no-PROD-Y-DEV.sql sha256 8c142e30 tools/verification/movilba-749-poblacion-si-no.sql sha256 174f8dc8 (new, the population and regression measurement) tools/verification/movilba-749-ensayo.sql sha256 7f04562e (unchanged, the failing-before witness) THE FIX: one character. btrim's strip set in the shared rutap.normalizar_si_no goes from E' \t\n' || chr(160) to E' \t\n\r' || chr(160). Everything else is byte-for-byte 340's body. The applied 340 migration is NOT edited. MEASUREMENT COUNTED BY CELL, NOT BY ROW, as asked. 0 of 44,464 si/no cells carry chr(13) in each base - 44,464 = 5558 rows x 8 columns, none null. Also 0 chr(10), 0 chr(160), 0 tab, 0 edge whitespace. Control of that zero: the SAME LIKE against a character the book does carry returns 19,962. So the defect is LATENT and no existing row changes. 729'S LESSON APPLIED - tested against every distinct value, not the motivating one. The entire population of the eight si/no columns is FIVE distinct values, identical byte-for-byte in both bases (hex-verified, no hidden bytes): 'No' 24234, 'Si' 19953, 'SI' 263, 'si' 9, 'NO' 5. They sum to 44,464, so that is the population and not a sample. Old body vs new body over all five: 0 of 5 change. The migration re-derives this itself from the data rather than from a written list, snapshots the old answers to a temp table before the replace, and asserts afterwards that none moved. THE CONTROL IS A MUTANT, NOT A GREEN LINE. I ran the same file twice in each base: faithful, and with the CREATE OR REPLACE deleted and every assert kept. Faithful passes in both bases (NOTICE: chr(13) at the edge resolves, interior still NULL by design, 0 of 5 changed, 24 call sites intact). Mutant ABORTS in both bases at the first assert - 'el arreglo no resuelve el retorno de carro al borde'. So the assert has been seen to fail against HEAD's behavior and pass against the tree's. STATED LIMIT, asserted in the migration so it cannot be read as more: btrim strips from the EXTREMES, so an INTERIOR carriage return E'S\rI' still returns NULL after this change, exactly as before. This closes chr(13) at the edge, not in the middle. Assert 2 of the post block FAILS if the interior ever starts resolving, because that would mean the header's scope is wrong. The book carries neither form today. TWO CORRECTIONS TO THIS ITEM'S OWN TEXT, both measured this pass. 1) It says the fix 'also re-pins the projector'. It does not: a CREATE OR REPLACE of the normalizer does not touch rutap.proyectar_snapshot's prosrc. The only claim made about the projector is that its 24 normalizer call sites survive, asserted before and after. 2) The projector pins this item recorded (prosrc 017b35d5, length 38375) were ALREADY STALE. The projector is now prosrc ade30cdf, length 37800, md5(functiondef) 3ba101fb, IDENTICAL in both bases, because MOVILBA-367 and MOVILBA-490 phase 1 applied and re-pinned. The sequencing this item asked for - after 490 and 367 - is therefore SATISFIED, measured, not assumed. PREFLIGHT refuses to run unless the installed normalizer is md5(prosrc) d3ad144067056f9870e745e08d1f8fa6, the body I measured, so a changed body aborts instead of being silently overwritten. New body after the change: md5(prosrc) bf68806075c65c4bfc1f0032c73a921d, md5(functiondef) 6b874af453ce073bac9713d50cfc3f04, prosrc length 203 - identical in both bases. Every pin here says WHICH string was hashed. SIN EJERCITAR: no live import run - the failing-before witness projects a synthetic snapshot under ROLLBACK, it does not import a real book. No application surface, no screen, no export. Only snapshot 11 (PROD) and 21 (DEV), which is one row each in rutap.snapshots; earlier books are not present to measure. The interior-chr(13) case is asserted to STAY broken and is not fixed. Nothing applied in either base: all four runs ended in ROLLBACK and the post-rollback probes are in the ensayo file.
-
audit pass: PER FILE: migration 8c142e30 and measurement 174f8dc8, re-hashed by me. The one-char body change, the preflight, the five post asserts and EVERY figure of the header re-measured by me in BOTH bases, SELECT-only. Execution NOT mine - nothing applied, db03 ran the ROLLBACK legs.
payload
{"scope":"PER FILE: migration 8c142e30 and measurement 174f8dc8, re-hashed by me. The one-char body change, the preflight, the five post asserts and EVERY figure of the header re-measured by me in BOTH bases, SELECT-only. Execution NOT mine - nothing applied, db03 ran the ROLLBACK legs.","outcome":"pass","evidence":"PASS on 8c142e30 (185 lines) and 174f8dc8 (85); full body in the audit-finding.\n\nTHE ONE CHARACTER VERIFIED AGAINST INSTALLED BYTES, not the header: md5(prosrc)=d3ad1440, 201 chars, BOTH bases, equal to the :64 pin. Printed via cat -A - installed strip set E' \\t\\n'||chr(160), new one adds \\r. Translate pair, both WHEN arms and IMMUTABLE STRICT byte for byte the 340 body.\n\nCONTROL SEEN TO FAIL, all four inputs of assert 1, measured by me against the installed function in BOTH bases: 'SI'+CR, CR+'SI', 'SI'+CRLF, 'NO'+CR all NULL today, so assert 1 cannot pass on HEAD. Clean controls DO resolve ('SI', NBSP+'SI', 'SÍ', 'SI'+LF -> Si; 'x' -> NULL), so the NULL is attributable to chr(13).\n\nREGRESSION BY POPULATION, RECOMPUTED BY ME, both bases identical: 5558 x 8 = 44464 cells, 0 null, 5 distinct - No 24234, Si 19953, SI 263, si 9, NO 5, summing to 44464. Evaluating OLD and NEW strip sets over each: 0 differ. 0 cells carry CR, LF, NBSP, tab or edge space; control on a character the book does carry gives 19962. Section 4 of the measurement is a positive control on that diff instrument.\n\nTHE DECLARED LIMIT IS ASSERTED: assert 2 fails if an INTERIOR chr(13) ever resolves. NULL today.\n\nIMMUTABLE TRAP NOT ARMED: no index, generated column, constraint or default depends on this function in either base; only other consumer is proyectar_snapshot (24 sites, asserted twice, prosrc ade30cdf/37800 both bases, so 367+490 did re-pin it); no view uses it; proacl default, so REPLACE loses no grant. The 494 trigger exists: vh_norm_reject_unresolved_catalogs on rutap.vehiculo_historial_normalizado.\n\nBOUNDARY THE HEADER OMITS: rutap.snapshots holds ONE row per base (PROD 11, DEV 21) and rutap_rows only that snapshot, so \"all snapshots\" is one. The 5-value population is the current book, not its history - the latent case this fix is for.\n\nNOT EXERCISED: no leg run by me; no import with a CR cell, so the 494 abort is reasoned from the trigger's existence, not observed.","reportRef":null} -
# MOVILBA-749 — audit verdict: PASS, per file. audit-mba-l-cc, 2026-10-08, signed by this event's createdAt docs/migrations/2026-10-08-movilba-749-retorno-de-carro-normalizar-si-no-PROD-Y-DEV.sql 8c142e303c723ed9cec1d60967d98357bbfe9e06d47a4b169ef5f4ed2a2e9f53 (185 lines) tools/verification/movilba-749-poblacion-si-no.sql 174f8dc893d8d22580f1ffa790570301334a87d82853ed393728dc0a4887ea36 (85 lines, new) Both re-hashed by me. **Nothing applied; execution is not mine** — db03 ran the ROLLBACK legs and owns them. Every figure below I measured myself with SELECT-only psql in BOTH bases, rather than quoting the report. ## 1. The "one character" claim is verified against the installed bytes `md5(prosrc)` of `rutap.normalizar_si_no(text)` is `d3ad144067056f9870e745e08d1f8fa6`, length 201, **identical in `movilba` 17.6 and `movilba_dev` 15.17**, and equal to the preflight pin at `:64`. I printed `prosrc` through `cat -A`: installed: btrim(p_valor, E' \t\n' || chr(160)) proposed: btrim(p_valor, E' \t\n\r' || chr(160)) The `translate` pair, the two `WHEN` arms and `IMMUTABLE STRICT` are byte for byte the MOVILBA-340 body. The header's claim is exact. ## 2. The control was SEEN TO FAIL — measured, not reasoned All four inputs of `$post$` assert 1, evaluated against the **installed** function in both bases: normalizar_si_no('SI'||chr(13)) -> NULL normalizar_si_no(chr(13)||'SI') -> NULL normalizar_si_no('SI'||chr(13)||chr(10)) -> NULL normalizar_si_no('NO'||chr(13)) -> NULL So assert 1 **cannot** pass on HEAD. And the clean controls do resolve today — `'SI'`, `chr(160)||'SI'`, `'SÍ'`, `'SI'||chr(10)` all give `Si`, `'x'` gives NULL — which is what makes the NULL above attributable to `chr(13)` and not to a function broken some other way. The preflight asserts both halves (`:69-78`), so the migration refuses to run if either premise stops holding. ## 3. Non-regression is by population, and I recomputed the population 5558 rows x 8 columns = **44464 cells, 0 of them null**, and exactly **5 distinct values**, byte identical in both bases: | value | cells | |---|---| | `No` | 24234 | | `Si` | 19953 | | `SI` | 263 | | `si` | 9 | | `NO` | 5 | | **sum** | **44464** | Sum equals the cell count, so this is the population and not a sample. I evaluated the OLD and the NEW strip set over each of the five: **0 differ**. Separately, 0 cells carry `chr(13)`, `chr(10)`, `chr(160)`, tab or an edge space, and the control on a character the book DOES carry (`'%i%'`) returns 19962 — so the four zeros are a measurement, not a dead predicate. The measurement file's section 4 is a **positive control on the diff instrument itself**: the same comparison against a deliberately wrong strip set (`\t` removed) must report changes. Without it a `0 cambian` could not be distinguished from an instrument that reports 0 for everything. That is the part most such files omit. ## 4. The declared limit is asserted, not merely written `$post$` assert 2 (`:145-147`) FAILS if an interior `chr(13)` ever starts resolving. Measured NULL today in both bases. So the header's scope sentence is a control, not a disclaimer. ## 5. The IMMUTABLE trap is not armed — the check this class of change usually misses `normalizar_si_no` is `IMMUTABLE`, so a behavior change would silently corrupt anything that had cached its value. Measured in both bases: indexes using it -> none generated columns -> none check constraints -> none column defaults -> none views / matviews -> none other functions -> rutap.proyectar_snapshot ONLY proacl -> default (no explicit grant to lose on REPLACE) provolatile / proisstrict -> i / true, preserved by the new declaration `proyectar_snapshot` is `prosrc ade30cdf61b58fb67cbd5c2b2112fced`, length 37800, **equal in both bases**, with 24 call sites — so the header is right that MOVILBA-367 and MOVILBA-490 phase 1 already re-pinned it and the stale pins the item carried (`017b35d5` / 38375) are correctly retired. The call site count is asserted both before (`:80-84`) and after (`:169-172`). The MOVILBA-494 gate the header blames for today's behavior exists: `vh_norm_reject_unresolved_catalogs` on `rutap.vehiculo_historial_normalizado`, both bases. ## 6. One coverage boundary the header does not state `rutap.snapshots` holds **exactly one row per base** — PROD 11, DEV 21 — and `rutap_rows` holds only that snapshot. So the measurement file's "ALL snapshots" is literally true and trivially so: the five-value population is the CURRENT book, not the book's history. That is not a defect in the fix — it is precisely why the defect is latent and why the control had to be a mutant — but a reader could take `0 de 44464` as a statement about every snapshot ever imported, and it is not. ## 7. NOT EXERCISED - **No leg run by me.** db03's PROD and DEV rehearsals remain theirs; I verified the artifact and re-derived its figures independently. - **No import was run with a CR-bearing cell**, so the end-to-end consequence (494 aborts the whole snapshot) is reasoned from the trigger's existence, not observed. - `pnpm lint` / `typecheck` / `test:source` / `test:tools` were not run and are not the control for a `.sql` artifact; for this item the gate IS the measurement, which is the db lane's to run. - The `tools/verification/movilba-749-ensayo.sql` referenced at `:42` exists (13817 bytes) but I did not execute it.
-
APPLIED in both bases, PROD first, under the hub lock rutap.normalizar_si_no (bare locks() returned an empty list before the acquire; released after). Migration 8c142e30, re-hashed against its pin immediately before the PROD run. Audit PASS verified by me in bwi events, not taken from a DM: audited 22:42:47Z, audit-finding 22:42:48Z, per file, same two pins. PROD applied first, 19:57 ART, rc=0, COMMIT. DEV applied after from the SAME bytes, rc=0, COMMIT. Both printed the same NOTICE: 'MOVILBA-749 OK: chr(13) al borde resuelve, el interior sigue NULL por diseno, 0 de 5 valores del libro cambiaron, 24 call sites intactos.' Figures for the README entry, re-measured AFTER commit and OUTSIDE any transaction, identical in both bases: - installed normalizer: md5(prosrc) bf68806075c65c4bfc1f0032c73a921d, md5(pg_get_functiondef) 6b874af453ce073bac9713d50cfc3f04, prosrc length 203. Prior body was md5(prosrc) d3ad144067056f9870e745e08d1f8fa6. - edge chr(13) now resolves: 'NO'+chr(13) -> No, 'SI'+chr(13) -> Si, chr(13)+'SI' -> Si. - the declared limit holds: an INTERIOR chr(13), E'S\rI', still returns NULL. That is by design and post-assert 2 of the migration fails if it ever stops being true. - clean controls unchanged: 'Si' -> Si, 'No' -> No, 'x' -> NULL. - the book's population: 5 distinct values over 44,464 si/no cells, 0 of them NULL under the new body, so 0 of 5 changed answer - the same figure measured before applying. - rutap.proyectar_snapshot(bigint) untouched: md5(prosrc) ade30cdf61b58fb67cbd5c2b2112fced and 24 call sites to the normalizer, both bases. A CREATE OR REPLACE of the normalizer does not touch the projector's prosrc, as stated in the header. Idempotence control, SEEN TO FIRE rather than asserted: re-running the same file against each base now ABORTS at the preflight - 'ERROR: MOVILBA-749: el cuerpo instalado de normalizar_si_no es md5(prosrc)=bf68806075c65c4bfc1f0032c73a921d, se esperaba d3ad144067056f9870e745e08d1f8fa6'. Measured after that refusal: the body is still bf688060, length 203, in both bases, so the aborted re-run changed nothing. SIN EJERCITAR: no reprojection was run, so no stored row changed and none was expected to - this migration only replaces a function body. The 57 rows that MOVILBA-371 and MOVILBA-765 measure on id 37 are untouched by it. No HTTP surface was exercised. The interior-chr(13) case remains unresolved BY DESIGN and no row in the book carries it (0 of 44,464 cells carry chr(13) at all today, against a LIKE control of 19,962 on a character the book does carry).
-
db03-mba-l-cc closes. The fix is installed and serving on all three surfaces, and the figures were re-measured after COMMIT outside any transaction. DDL: rutap.normalizar_si_no replaced in PROD first, then DEV from the same bytes, under the hub lock. Installed body identical in both bases - md5(prosrc) bf68806075c65c4bfc1f0032c73a921d, md5(functiondef) 6b874af453ce073bac9713d50cfc3f04, prosrc length 203; prior body d3ad144067056f9870e745e08d1f8fa6. Both apply legs rc=0 with the same NOTICE. CODE/README: coder03 reports DEV c2851fce, PROD in the b00c4b4 lineage and the pod at serviceId 29955 = patch 212, so the README entry pinned at ea98862f carries these figures on every surface. Regression covered by POPULATION, not by a sample: 5 distinct values over the 44,464 cells of the eight si/no columns, 0 NULL, 0 of 5 changed. Zero cells carry chr(13) today, so the instrument's claim rests on the CONTROL - 19,962 cells over a character the book does carry, i.e. it was seen to find something before reporting zero. Mutant run in both bases: the faithful file passes, the same file without the CREATE OR REPLACE aborts at the first assert, seen failing against HEAD. Idempotence SEEN to fire: a re-run aborts at the preflight naming the new md5 and the body is unchanged after the refusal. Limit DECLARED, not discovered later: btrim strips from the extremes only, so an interior CR still resolves to NULL by design, and post-assert 2 fails the day that stops being true. rutap.proyectar_snapshot untouched at ade30cdf with its 24 call sites. SIN EJERCITAR: no real RUTAP import has run with this body, no app surface was opened after the apply, and no cell containing chr(13) exists today to exercise the fixed path on live data - the edge cases were exercised with synthetic literals only. Audit PASS 22:42:47Z, per file, over the SQL and the measurement, with every header figure re-measured by audit in both bases, SELECT-only; execution was mine, not audit's.