GET /api/vehiculos/[id] emite las cuatro columnas de validadora porque findById selecciona la vista de detalle entera y ni el servicio ni la ruta recortan
Medido por mi contra el arbol el 2026-10-08, no tomado del reporte. src/app/api/vehiculos/[id]/route.ts:42 llama vehiculoService.getVehiculoById y :51-54 devuelve NextResponse.json({vehiculo}) entero. src/services/vehiculos.ts:147-160 solo loguea y pasa la fila tal cual, no proyecta ni filtra. src/repositories/vehiculos.ts:501-504 selecciona getViewSelectedFields(vVehiculosDetalle), que es TODA la vista, y drizzle/schema.ts:2318-2321 confirma que la vista trae sube_mounting_kit, sube_id_validadora, openpass_mounting_kit y openpass_id_validadora. Asi que la ruta emite los cuatro valores. MECANISMO DISTINTO AL DE MOVILBA-753, por eso va separado: 753 es un filtro que vivia en la capa de display y una ruta que no la llamaba; esto es un SELECT ancho que arrastra columnas que nadie pidio. Arreglar uno no toca al otro. TRAMPA A NOMBRAR ANTES DE ESCRIBIR EL ARREGLO: los nombres NO coinciden entre superficies. El set HIDDEN_VEHICLE_HISTORY_FIELDS de 753 (src/lib/hidden-vehicle-history-fields.ts) lleva subeValidator, subeMountingKit, openpassValidator, openpassMountingKit - los nombres del modelo de historial. La vista de detalle expone subeMountingKit, subeIdValidadora, openpassMountingKit, openpassIdValidadora. Dos de los cuatro se llaman distinto. Reusar ese set aca filtraria dos campos y dejaria pasar los dos ids, y el diff se veria correcto. El arreglo tiene que nombrar las columnas de ESTA vista y el test tiene que assertar la ausencia de los cuatro por el nombre que esta ruta emite, no por el del historial. Es un HIDE, no una remocion: nada de DDL, no se tocan public.validadoras, public.validadoras_historial, las cuatro columnas de vehiculos_datos_colectivos ni la proyeccion de la vista. Se recorta en la salida. Roto en prod contra una regla dura, se shipea y despues se reporta, sin GO. Censo de assistant: OpenAPI, Scalar Try-it y docs-reference no tienen referencias a validadoras, y 742/582/583/589 quedaron limpios.
Activity
-
parent=#4105
-
GET vehicle-detail response trim and route regression test prepared; awaiting audit.
-
Two-path diff ready for audit. GET /api/vehiculos/[id] excludes the detail-view keys subeMountingKit, subeIdValidadora, openpassMountingKit, openpassIdValidadora from the vehiculo JSON response; ordinary DTO fields remain. The repository/view projection and stored data are unchanged. New tools/tests/vehiculo-detail-hidden-fields.test.cjs loads the real route and asserts all four exact key/value pairs absent while dominio remains. SHA-256: route 95d4a158, test 89346886. git diff --check clean. Per project workflow coder did not run lint, typecheck or tests; audit to run gates and failing-before control. PUT returns a different update DTO that does not select the detail view.
-
Read-only follow-up while current SHA is under audit: v_vehiculos_detalle also projects historial JSONB; its aggregate includes 'cambios', vh.cambios. The route-level top-field filter does not inspect historial[].cambios, so an old/new validator value recorded there can still cross this same JSON boundary. Audit is checking a nested fixture as the failing-before control. No edit during audit; next revision must filter nested cambios without changing stored data.
-
Revision superseding top-level-only route 95d4a158: GET /api/vehiculos/[id] still omits exact detail-view keys subeMountingKit, subeIdValidadora, openpassMountingKit, openpassIdValidadora, and now also removes validator keys and field-tagged changes from historial[].cambios JSON before NextResponse.json. Stored view/repository data untouched. Test seeds top-level secrets AND nested cambios with camel, snake and field-tagged validator entries, asserts ordinary dominio survives and all secret values disappear. Pins: route.ts da568ad2; tools/tests/vehiculo-detail-hidden-fields.test.cjs 99f7faec. Audit should see nested test fail against 95d4a158 or with nested filter removed, then pass on tree/gates. No HTTP runtime yet; coder did not run gates. git diff --check clean.
-
Supersedes denylist da568ad2/99f7faec. GET detail now selects an explicit response shape: 56 current non-validator v_vehiculos_detalle columns plus five repository overlays, history period keys, seven scalar cambios keys, and three datosSsotPdf keys. New view/JSON fields drop by default; existing four validator columns remain in DB/view. Candidate SHA route 358982db66464de10eff3866359e5e98f0bb42545a5cbb38f7ccb87e1be34aa0, test e6685e9ad5733b2b15359564d2a9b86507fe7f136b8838f665817a73a87c2c68. Source census: schema 60 columns, only four validator columns omitted, five overlays added. Test seeds known validator values and unknown future top-level, PDF, period and changes fields, plus nested object shape; audit should see it fail against prior denylist candidate and pass on current bytes, then run gates. Coder did not run gates or HTTP. git diff --check clean.
-
# MOVILBA-754 — audit verdict: PASS, CODE AND CONTROL, with a live census of both bases. Author coder04-mba-l-ca. audit-mba-l-cc, 2026-10-08 src/app/api/vehiculos/[id]/route.ts 358982db6646… (modified, TRACKED) tools/tests/vehiculo-detail-hidden-fields.test.cjs e6685e9a… (new, untracked) Both re-hashed from the working tree and exact against the pins; the route is tracked, so this change has a real pre-image and control A below is a diff against it. ## 1. The allowlist is complete against the live view, measured by me in BOTH bases SELECT-only, `information_schema.columns` on `public.v_vehiculos_detalle`: columns PROD 60 DEV 60 column-name sets IDENTICAL (diff empty) live columns outside the allowlist sube_id_validadora, sube_mounting_kit, openpass_id_validadora, openpass_mounting_kit — and nothing else, in either base allowlist entries that are not view columns camarasInternasId, datosSsotPdf, marcaMotorNombre, subtipoTransporteCodigo, tipoVehiculo Those five are exactly the overlays `findById` adds at `repositories/vehiculos.ts:556-572` (`camarasInternasId` is the rename of the view's `camarasInternas`, which stays declared as well). So `VEHICLE_DETAIL_FIELDS` admits every column the response used to carry except the four validator ones, and the drizzle mirror agrees at 60. coder04's census is reproduced, not carried. ## 2. The history allowlists are complete too, population-wide and in both bases `jsonb_each` over every period of `historial` in `v_vehiculos_detalle`: periods PROD 5558 DEV 5727 distinct period keys exactly the 15 in VEHICLE_HISTORY_FIELDS, in both bases, no sixteenth non-scalar period values only `cambios`, object, on every period — nothing else is an object or array distinct cambios keys 7: carroceria_id, chasis_id, dominio, estado_id, interno, linea_id, motor_fabricante_id **The cambios allowlist is camelCase and the stored keys are snake_case**, and it is correct only because `transformObjectKeys` recurses into nested objects and arrays (`lib/transformers.ts:56`, `:63-68`), so `findById`'s `transformFromDatabase` camelizes `historial[].cambios` before the route ever sees it — `parseHistoryArray` at `repositories/vehiculos.ts:349` is a pass-through and does nothing here. Had that transform been shallow, `VEHICLE_CHANGE_FIELDS` would have dropped five of seven change keys on all 5558 PROD periods. See finding 4.1: the test cannot tell those two worlds apart. ## 3. Gates and controls typecheck (next typegen + tsc --noEmit, tree-wide) rc=0 test:source rc=0, fail 0 (68 pass) test:tools rc=0, fail 0 (118 pass) eslint --no-warn-ignored, route + test rc=0 rc read unpiped, under the hub lock `gates-checkout-movilba-web`. The new test is in the `tools/tests/*.test.*js` glob, so it is gated on both ship paths (`mba-deploy-dev.sh:178`, `mba-deploy-prod.sh:61`). **Control seen to fail three ways**, each restored before the next: - **A — candidate test against HEAD route bytes**: rc=1, `AssertionError` on the payload deep-equal. This is the real pre-image, not a mutant. - **B — nested `cambios` pick disabled** (`if ('cambios' in period)` forced false): rc=1, so the nested leak the 00:04:58Z revision was filed over is actually covered. - **C — `futureValidatorToken` added to `VEHICLE_DETAIL_FIELDS`**: rc=1, so the default-private claim is exercised and not merely asserted in a comment. Route restored and re-hashed `358982db6646…` exact; candidate re-run rc=0. ## 4. Findings — none blocks the PASS ### 4.1 The test passes equally under a broken key transform Fixture `cambios` carries `subeIdValidadora` (camel, `:63`) and `openpass_id_validadora` (snake, `:64`), and both are dropped — one as undeclared-in-`VEHICLE_CHANGE_FIELDS`, the other because the snake spelling is never declared. That is the correct output, but it is also the output a shallow `transformObjectKeys` would produce for the *seven real change keys*: all of them snake, all dropped, test still green. The dependency measured in section 2 is load-bearing and invisible to the gate. One assert on a fixture whose `cambios` carries the seven camel keys and expects all seven back would close it. ### 4.2 `visibleHistoryPeriod` enforces a second, undocumented policy: non-scalar means dropped `:59-60` keeps a period entry only when it is `cambios`, nullish, or a string/number/boolean. So a declared period key that ever becomes an object or an array is silently removed even though `VEHICLE_HISTORY_FIELDS` admits it. Measured today: zero occurrences, in either base, across 11285 periods — `cambios` is the only non-scalar. It is a latent contract rule, it is not named in the item, and it is the kind of filter that will be read as a bug the first time a period gains a nested field. ### 4.3 PUT on the same route returns the service DTO unfiltered — measured, and it does not leak `:176` returns `{ vehiculo }` with no pick. That is safe for the four validator columns, not by filtering but by projection: `repositories/vehiculos.ts:700-715` returns ten columns of `public.vehiculos` (`id`, `dominio`, `vehiculoSubtipoTransporteId`, the two serials, `anioModelo`, `motorFabricanteId`, `motorUnidadPropulsoraId`, `chasisId`, `carroceriaId`) plus four overlay keys, and the validator columns live in `vehiculos_datos_colectivos`, which that path never reads. So GET and PUT now have different response shapes for the same resource, and only GET is allowlisted. Worth a line in the item rather than a change. ### 4.4 Pre-existing, NOT caused by this change: four keys two screens read that GET has never emitted `vehiculos/[dominio]/page.tsx:568` reads `vehiculoData.regimenServicioDescripcion`, its `VehiculoData` declares `statusHistory`, `entidadHistory`, `lineaHistory`, and `editar/colectivo/page.tsx:560` reads `vehiculoData?.lineaNumeroAsignado`. None of those is a column of `v_vehiculos_detalle` in either base and none is a `findById` overlay, so the detail API returned them as `undefined` before this change as well — the allowlist removes nothing that was there. `regimenServicioDescripcion` is produced only on the colectivo path (`repositories/vehiculos.ts:1739`, `:1801`), which the ficha reaches through a different call. Separate item if anyone cares; it is a stale type, not a regression. ## 5. NOT EXERCISED - **No HTTP and no browser.** The route ran under `node --test` with `auth`, `rbac`, `NextResponse` and the service all mocked; nothing exercised the real session, the real permission check, or Next's serializer. The two screens that consume this endpoint (`vehiculos/[dominio]/page.tsx:606`, `editar/colectivo/page.tsx:300`) were read, never rendered. - **No write and no DDL.** Every database figure above is SELECT-only on `information_schema` and on `v_vehiculos_detalle`; neither base was modified, and the four validator columns stay in place per the HIDE. - The PUT and DELETE handlers were read, not executed; 4.3's claim rests on the repository projection, not on a PUT response anyone observed. - `node --test` aborts the file at the first failing assert, so in controls A, B and C only the first failing assert was seen; the `json.includes` loops at `:89-99` were never the failing line. - `next build` runs on no ship path, so nothing here speaks to the production bundle. The test file is untracked: no pre-image for a later amendment of it.
-
audit pass: CODE AND CONTROL plus a live SELECT-only census of BOTH bases: route 358982db (tracked, real pre-image) and tests/vehiculo-detail-hidden-fields e6685e9a. Gates typecheck/source/tools/eslint. NO HTTP, no browser, no write, no DDL; PUT/DELETE read not run.
payload
{"scope":"CODE AND CONTROL plus a live SELECT-only census of BOTH bases: route 358982db (tracked, real pre-image) and tests/vehiculo-detail-hidden-fields e6685e9a. Gates typecheck/source/tools/eslint. NO HTTP, no browser, no write, no DDL; PUT/DELETE read not run.","outcome":"pass","evidence":"audit-mba-l-cc, 2026-10-08. PASS. Author coder04-mba-l-ca. Both pins re-hashed exact; the route is tracked, so control A is against the real pre-image.\n\nALLOWLIST COMPLETE, RE-MEASURED BY ME IN BOTH BASES, SELECT-only on information_schema: v_vehiculos_detalle has 60 columns in PROD and 60 in DEV with IDENTICAL name sets, and the only live columns outside VEHICLE_DETAIL_FIELDS are sube_id_validadora, sube_mounting_kit, openpass_id_validadora, openpass_mounting_kit. The five allowlist entries that are not view columns are exactly findById's overlays (repositories/vehiculos.ts:556-572). coder04's census reproduced, not carried.\n\nHISTORY ALLOWLISTS, POPULATION-WIDE: jsonb_each over every period, PROD 5558 / DEV 5727. Distinct period keys are exactly the 15 in VEHICLE_HISTORY_FIELDS, no sixteenth in either base; the only non-scalar value is cambios (object, every period); distinct cambios keys are 7, all snake_case. VEHICLE_CHANGE_FIELDS is camelCase and is correct ONLY because transformObjectKeys recurses (lib/transformers.ts:56, :63-68), so transformFromDatabase camelizes historial[].cambios before the route; parseHistoryArray (:349) is a pass-through. Shallow transform would have dropped 5 of 7 change keys on all 5558 PROD periods.\n\nGATES, rc unpiped, under the lock: typecheck 0 tree-wide, test:source 0 fail 68 pass, test:tools 0 fail 118 pass, eslint 0 on route+test. The test is in the tools/tests glob, so it gates both ship paths (dev:178, prod:61).\n\nCONTROL SEEN TO FAIL, 3 WAYS, restored between runs: A candidate test against HEAD route bytes rc=1 on the payload deep-equal; B nested cambios pick disabled rc=1; C futureValidatorToken added to the allowlist rc=1, so default-private is exercised. Restored, re-hashed 358982db exact, candidate rc=0.\n\nFINDINGS in the note, none blocks. 4.1 the test passes equally under a broken key transform - its snake fixture key is dropped as undeclared, the same output a shallow transform would give for the seven real keys. 4.2","reportRef":null} -
Read-only control measurement: the 7 SQL JSONB change keys are dominio, interno, linea_id, estado_id, chasis_id, carroceria_id, motor_fabricante_id. A shallow outer-key transform leaves nested JSONB keys snake_case; route VEHICLE_CHANGE_FIELDS then retains only dominio and interno and drops 5/7 in a fully populated period (actual sparse period: up to 5, depending on non-NULL keys, because jsonb_strip_nulls removes NULLs). In an in-memory shallow-transform mutant, tools/tests/vehiculo-historia-hidden-fields.test.cjs still PASS (1/1, exit 0): it exercises /api/vehiculos/[id]/historia, not the detail route's history filter/transform path. Smallest fail-loud fix: add a detail-route regression using all seven snake_case changes through the real transformFromDatabase and assert all seven survive the route filter; shallow-transform mutant should fail. No shared checkout writes.
-
Implementation ready for scoped audit. Test hunk in tools/tests/vehiculo-historia-hidden-fields.test.cjs feeds seven snake_case v_vehiculo_historial cambios keys through actual transformFromDatabase and GET /api/vehiculos/[id]. Candidate SHA256: test aaafcb6bc05659093df8136d9cb18e09971e260cfa93a25756cc0eb327521c47; detail route and transformer unchanged. On unmutated candidate: targeted test 2/2 pass. In-memory shallow-transform mutant: detail-route regression failed, showing only dominio/interno and dropping lineaId, estadoId, chasisId, carroceriaId, motorFabricanteId (5/7). No lint/typecheck run.
-
Final separate 754 regression guard, supersedes earlier combined-test note: test tools/tests/vehiculo-detail-hidden-fields.test.cjs SHA256 00f1147a3fdfa6020b1dd723f3a6bb0bea49f13851679b221d67f03d5802678d. It transforms a seven-key snake_case nested cambios fixture using actual src/lib/transformers.ts transformFromDatabase, then exercises the actual GET detail route and asserts all seven camelized keys are emitted. Targeted test on current bytes passes 2/2. Shallow-transform mutant FAILS this test and shows only dominio/interno retained; five keys are dropped. No lint/typecheck run.
-
# MOVILBA-754 — audit verdict: PASS, HUNK-SCOPED. Author coder03assistant-mba-l-ca. audit-mba-l-cc, 2026-10-08 tools/tests/vehiculo-detail-hidden-fields.test.cjs 00f1147a3fdfa6020b1dd723f3a6bb0bea49f13851679b221d67f03d5802678d Scope is the new case `vehicle detail API preserves all seven camelized period change keys` (`:110-140`) plus the `transformFromDatabase` loader helper it needs (`:39-45`, `transformersFile` at `:10`). The pre-existing first case is untouched by this hunk and keeps its PASS of 00:57:31.779Z. The classification work in `vehiculo-historia-hidden-fields.test.cjs` is 753's and is verdicted there. Base is `dev` `353eff10` (the MOVILBA-59 ship), NOT `a7da6ffe`; `git diff a7da6ffe..353eff10` touches only the 59 migration, the two 59 tool files and the version bump, so it is disjoint from this verdict. My earlier run measured the superseded location of this case (inside `vehiculo-historia-hidden-fields.test.cjs`, pin `aaafcb6b`) and is not cited: the 22:31 repin moved it here and everything below was re-measured on `00f1147a`. Pins re-hashed exact in the checkout before and after the gate run, with `gates-checkout-movilba-web` HELD for it (22:33-22:37) and `tsconfig.json` unchanged after `next typegen`. Mutants ran in a FROZEN COPY (`scratchpad/v753`); `src/lib/transformers.ts` `be00b693` and `src/app/api/vehiculos/[id]/route.ts` `358982db` are byte-identical to the checkout after each. ## 1. What it closes The 753/754 PASS of 00:57:31.779Z registered this limit: `VEHICLE_CHANGE_FIELDS` (`route.ts:40`) is camelCase and is correct ONLY because `transformObjectKeys` recurses into nested objects (`transformers.ts:63-68`); under a shallow transform `cambios` stays snake_case, the allowlist matches 2 of 7 keys, and no test noticed. The new case sends seven snake_case change keys through the real `transformFromDatabase` and asserts the route's emitted `cambios` keys against the seven camelCase names, written literally. ## 2. Both legs seen to fail, each forced alone pinned bodies rc=0, 2 pass / 0 fail nested recursion in transformObjectKeys -> passthrough rc=1, emitted ['dominio','interno'], 5 of 7 dropped motorFabricanteId removed from VEHICLE_CHANGE_FIELDS rc=1, emitted missing motorFabricanteId So the case guards the transform recursion AND the route allowlist — more than the limit asked for. Both mutants are **detected by the same single `assert.deepEqual`**; nothing throws earlier, and the mutant of `transformers.ts` is what the test's own helper exercises while the allowlist mutant is on the route's output. Two distinct legs, one assert. ## 3. Gates, run by me on the checkout bytes, under the lock pnpm typecheck rc=0 pnpm test:source rc=0, 68 pass / 0 fail pnpm test:tools rc=0, 119 pass / 0 fail `eslint` reports `tools/tests/*.cjs` as **ignored by config**: no lint rule covers this file on any path, and `lint:strict` runs on no ship path at all. ## 4. NOT EXERCISED - No HTTP, no browser, no PDF, no ficha, no SQL, neither base read. `auth`, `rbac`, the service and `NextResponse` are mocked in an in-process `vm` loader; `v_vehiculo_historial` was never queried, so **the claim that those seven columns are what the view builds `cambios` from is a comment (`route.ts:39`), not a measurement**. - The case pre-computes the transform in the test and hands the result to the mocked service, so it does not prove the production path applies `transformFromDatabase` to that payload. It proves the transform recurses and the route does not drop keys. - The helper at `:39-45` loads `transformers.ts` with no `require` in scope, so it would break if that module ever gained an import; nothing guards that. - `next build` ran on no path. `PUT` in the same route is untouched and unexercised.
-
audit pass: NEW CASE ONLY in tools/tests/vehiculo-detail-hidden-fields.test.cjs 00f1147a on dev 353eff10, plus its transform helper; the first case keeps its 00:57:31.779Z PASS. Gates typecheck/test:source/test:tools under the checkout lock, 2 mutants in a frozen copy. NO HTTP, no SQL, no next build.
payload
{"scope":"NEW CASE ONLY in tools/tests/vehiculo-detail-hidden-fields.test.cjs 00f1147a on dev 353eff10, plus its transform helper; the first case keeps its 00:57:31.779Z PASS. Gates typecheck/test:source/test:tools under the checkout lock, 2 mutants in a frozen copy. NO HTTP, no SQL, no next build.","outcome":"pass","evidence":"audit-mba-l-cc 2026-10-08. PASS. Author coder03assistant. Pin 00f1147a re-hashed exact before AND after the gates, lock gates-checkout-movilba-web HELD 22:33-22:37, tsconfig unchanged after next typegen. Mutants only in a frozen tree copy: transformers be00b693 and [id]/route 358982db byte-identical after each.\n\nBASE CORRECTION: dev 353eff10, not a7da6ffe - the MOVILBA-59 ship landed mid-review and is disjoint. My earlier run measured this case at its SUPERSEDED location inside vehiculo-historia-hidden-fields.test.cjs (aaafcb6b); the 22:31 repin moved it here and this is all re-measured on 00f1147a.\n\nCLOSES: the limit I registered at 00:57:31.779Z - VEHICLE_CHANGE_FIELDS (route.ts:40) is camelCase and correct ONLY because transformObjectKeys recurses (transformers.ts:63-68); shallow, cambios stays snake_case and the allowlist matches 2 of 7 with nothing noticing. The case sends 7 snake_case keys through the real transformFromDatabase and asserts the route's emitted keys against the 7 camelCase names written LITERALLY.\n\nBOTH LEGS SEEN TO FAIL, EACH FORCED ALONE: pinned 2 pass/0 fail rc=0; recursion replaced by passthrough rc=1, emitted ['dominio','interno'], 5 of 7 dropped; motorFabricanteId removed from VEHICLE_CHANGE_FIELDS rc=1, that key absent. Both are caught by the SAME single assert.deepEqual, so it is two legs through one assert.\n\nGATES: typecheck rc=0; test:source 68/68; test:tools 119/119. eslint IGNORES tools/tests/*.cjs by config; lint:strict runs on no ship path.\n\nNOT EXERCISED: no HTTP, browser, PDF, ficha or SQL, neither base read; v_vehiculo_historial never queried, so route.ts:39's claim about those 7 columns is a COMMENT, not a measurement. The case pre-computes the transform and feeds a MOCKED service, so it does not prove the production path applies transformFromDatabase to that payload. The helper loads transformers.ts with no require in scope and breaks if that module gains an import; ungated. No next build; PUT untouched.","reportRef":null} -
Shipped and LIVE on both surfaces. dev ec6a1b94/patch 222, prod main merged and app.movilba.com/api/health answers serviceId 31325 = patch 222, probed 2026-10-08 23:33 ART. Root cause of the earlier 221 reading: the post-receive hook on /srv/movilba-web.git runs synchronously inside the push and my push was killed by a 2-minute timeout at 23:15:55, exactly when pnpm build finished writing BUILD_ID - so the hook never reached its restart stage (deploy.json has install and build entries and NO restart entry) and next-server kept serving the 221 build from 22:28. Fixed by completing the hook step the kill skipped: systemctl restart movilba-web.service. SIN EJERCITAR: only the health route was probed; no screen, PDF, XLSX or pod. POD leg still owed.
-
CORRECCION que supersede mi evento de 02:21:46Z: el SHA de dev esta mal escrito ahi. dev = ec4b68642158f9bba09edbeb5562c34a8ecbd790 (ec4b6864), NO ec6a1b94, que no es un objeto de este repo. prod main = 3dd4b3ec971026c2c5dbb6ebb298aa734938bc05. Todo lo demas de ese evento se sostiene sin cambios: prod responde serviceId 31325 = patch 222 y la causa del 221 fue la etapa restart del hook que el kill de los 2 minutos salteo.
-
FLEET PARITY alcanzada: las TRES superficies sirven patch 222. prod app.movilba.com serviceId 31325 (23:33 ART), dev01 ya estaba en 222 por el deploy, y el pod permisos-movilba-dev.gcba.gob.ar devolvio 31325 a las 23:31 ART despues de que Tekton terminara - mba-deploy-pod.sh espejo el arbol de dev ec4b6864 a repositorio-asi, force-push fe6d299 -> b5feafd en develop y EventListener HTTP 202. Las dos primeras sondas al pod a las 23:27 y 23:28 devolvieron 31188 (patch 221) y eso era el pipeline todavia corriendo, no una falla: no hay sesion de oc para mirar los builds (oc whoami da Unauthorized, el token sale de la consola OCP y eso es Elazar en el navegador), asi que el pipeline no se observo, solo su resultado. SIN EJERCITAR en las tres superficies: ninguna pantalla, PDF, XLSX ni consulta - la unica ruta probada es /api/health.