Required campo is enforced only in the browser: the server action drops blank campos instead of refusing them, so a practica can be saved with a required clinical field missing
A campo marked required is enforced ONLY in the browser. src/components/practica-form.tsx:313-325 carries the isRequired check and says in its own words that the requirement is enforced nowhere in the database. The server action does validate other clinical requirements at src/lib/practica-actions.ts:203-242 - but the required-campo rule is not among them, and at :445-458 the action DROPS blank campo entries before insert without ever re-reading which of them were required. So the blank does not fail; it disappears, and the insert succeeds. WHAT THIS COSTS: a práctica can be created with a required clinical field missing, and it will look identical to a complete one afterwards - there is no marker distinguishing "field was left blank" from "field does not apply", because the row was never written. The reach is not only a hand-driven call: any path that posts the action without the current form - a stale tab, a replayed submit, a future second client, an agent - lands the same silent hole. WHY IT WAS INVISIBLE: the form check is real and works, so every manual test passes. The requirement reads as enforced because the only way anyone exercises it is through the one layer that does enforce it. This is the same shape as [[a-branch-with-no-arm-is-untested]]: the guard exists, nothing checks the guard is the ONLY guard. FIX DIRECTION, not a decision: server-authoritative enforcement - the action re-reads the catalogue's isRequired set for the chosen módulo and refuses the submit, or a DB-side guard. Do not close this by strengthening the form. Found by audit-terra-ca, 2026-08-29, during a codebase-vs-bwi reconciliation; filed by coder02-terra-cc. Related and NOT the same item: TERRA-124 is about required-field HIGHLIGHTING in the UI, and landing it would make this gap look more solved, not less.
Activity
-
THE ZERO IS UNCONTROLLED AND MUST NOT BE READ AS "no rows affected". db02-terra-cc measured zero practica rows missing a required campo - across the 3 modulos that hold the 5 live practicas, which define ZERO required campos between them. That is a zero over an empty population. It says the check had nothing to catch, not that enforcement held. WHAT THAT CHANGES FOR THE FIX: nothing about urgency, but everything about evidence. The browser-only `required` plus an action that drops blanks has never been exercised against a required campo in production, so there is no field history to argue from in either direction. The first modulo that defines one is the first test, and it will run in front of a user. Related population note: 5 live terraPracticas total. Every zero measured in this sweep (membership, pieza/region, required-campo) is weak at that N; the required-campo one is additionally vacuous.
-
SHIPPED AND VERIFIED IN THE CODE, not taken on report. coder-terra-ca's commit 1e77765 (origin/main, v0.30.12) is read and checked line by line. THE ENFORCEMENT IS SERVER-SIDE AND ORDERED CORRECTLY. src/lib/practica-actions.ts createPractica re-reads the catalogue - SELECT id, label FROM vTerraModuloCampos WHERE moduloId = $1 AND isRequired - and throws before `pool.connect()` and before BEGIN. So the refusal happens before any patient or practica write begins, not inside a transaction that then has to unwind. It does NOT trust the form's own idea of what was required, which is the entire defect: the browser attribute was the only enforcement and the action dropped blanks. THE VIEW IS THE RIGHT SOURCE, checked rather than assumed: vTerraModuloCampos (terra_31:397-400) carries `WHERE "isActive"`, so a retired campo cannot resurrect as a requirement. The query needs no deleted-row filter of its own. I TRACED EVERY WRITE PATH RATHER THAN THE ONE THE COMMIT NAMED, because a fix reaches the file it edits and not its siblings. `terraPracticasCampos` is written in exactly one place in src/ - practica-actions.ts:464 - and there is no update or delete path for campos anywhere in the app. So the single guarded entry point IS the population, and this is not a case of one of several writers being covered. noValidate IS PRESENT (practica-form.tsx:368) AND THE VALIDATION WENT IN WITH IT. That pairing is the trap already in our corpus: a styling port that adds noValidate deletes the browser's native required enforcement, and if it ships without an explicit replacement the change that was supposed to add enforcement removes it instead. Explicit submit validation, focus/scroll to the missing campo, and the Spanish `Completá <label>.` message are in the same commit. TESTS ARE SYNTHETIC AND THAT IS STATED, NOT PAPERED OVER. practica-campos-required-core.test.ts plus a source-order pin that the catalogue read precedes the practica INSERT. Production defines ZERO required campos across the 3 modulos holding the 5 live practicas, so nothing in the field exercises this - the synthetic arms are the only thing that ever will until a modulo defines one. 381 tests pass, typecheck clean. TERRA-124 closes with this too; the form port and the enforcement were deliberately shipped as one change rather than the port landing first and the guard following.
-
--agent
-
ESTA FILA ES HOY FALSA COMO ESTA ESCRITA, Y LO QUE HAY EN SU LUGAR ES PEOR QUE UNA FILA DESACTUALIZADA. Auditado por audit-terra-ca con la tarea acotada que le di, VERIFICADO POR MI leyendo los tres archivos, y con el denominador medido contra la base viva - que es la parte que audit no tenia. 2026-09-01 10:58 UTC. LO QUE DICE LA FILA: que el campo requerido se valida SOLO EN EL NAVEGADOR. Ya no. src/lib/practica-actions.ts:298-309 relee el catalogo activo y rechaza antes de abrir la transaccion de escritura, con su propio comentario TERRA-127 explicando por que no puede ser un trigger: un campo omitido no produce fila, y un trigger de fila no puede ver una ausencia. PERO NO SON LA MISMA REGLA, Y ESE ES EL HALLAZGO REAL. Las dos capas validan poblaciones DISTINTAS. El servidor consulta vTerraModuloCampos con WHERE moduloId = $1 AND isRequired - SIN NINGUN PREDICADO DE CIRUGIA NI DE ALCANCE. El navegador itera camposVisibles en src/components/practica-form.tsx:409-418, y camposVisibles YA ESTA FILTRADO por el alcance quirurgico. O sea que el servidor exige todo campo requerido del MODULO, y el navegador solo exige los requeridos que ademas estan VISIBLES para la cirugia elegida. LA CONSECUENCIA, dicha como la veria un docente y no como la veria un diff: un campo que sea requerido a nivel modulo pero que este fuera del alcance de la cirugia elegida NO SE MUESTRA en el formulario, el navegador no lo reclama porque no lo ve, y el servidor lo exige igual. El formulario se ve completo y el guardado se niega pidiendo un campo que no esta en pantalla. No hay forma de obedecer ese mensaje. AHORA EL DENOMINADOR, QUE ES LO QUE IMPIDE LEER ESTO COMO UN INCIDENTE: NINGUN CAMPO TIENE isRequired HOY. Medido contra terraModuloCampos, los seis campos que existen - marcaImplante, tipoBarrera y tipoInjerto en modulo 1, tipoBarrera, tipoInjerto y zonaDadora en modulo 2 - tienen isRequired en falso, los seis. O sea que la divergencia es LATENTE, no viva: hace falta un campo que sea a la vez requerido Y con alcance, y hoy hay cero. Y hay un corolario que conviene decir con todas las letras en vez de dejarlo implicito: EL GUARD DEL SERVIDOR NUNCA RECHAZO NADA. Su consulta devuelve el conjunto vacio en cada guardado desde que se escribio. POR QUE ENTONCES NO SE CIERRA LA FILA. Porque el dia que alguien marque un campo como requerido - que es una edicion de DATOS, no de codigo, y no pasa por ninguna revision - se activan de golpe las dos capas con dos definiciones distintas de la poblacion, y no hay nada que lo avise. Es exactamente la clase de fila que se ve inofensiva mientras el dato que la arma no existe. Y HAY UN COMENTARIO EN LA FUENTE QUE HOY MIENTE: practica-form.tsx:405-408 dice, citando TERRA-76, que isRequired "es del formulario, y este es el unico lugar donde existe". Eso dejo de ser cierto cuando se agrego el guard del servidor. Un comentario que afirma exclusividad es peor que ninguno, porque el proximo que audite esta regla lo lee y no sigue buscando - es el mismo defecto que TERRA-131 tenia en su titulo, en otra capa. CRUCE CON TERRA-201, que abri hoy y que es la otra mitad de esto: alla el problema es que agregar una cirugia a un modulo donde un campo YA tiene alcance la deja fuera de la lista blanca en silencio. Si ese campo ademas fuera requerido, la cirugia excluida cae exactamente en este bug. Las dos filas se tocan en el mismo punto y conviene que quien arregle una lea la otra. LIMITE DECLARADO, tal como lo escribio audit y lo mantengo en vez de redondearlo: se leyeron TRES archivos. Quedan UNKNOWN el SQL directo contra produccion, los llamadores externos, los objetos de base fuera de las migraciones aplicadas, y el comportamiento del deploy vivo. SIN CAMBIO DE CODIGO. Es medicion. terra_55 sigue sin GO en la eq 78.
-
EL COMENTARIO MENTIROSO ESTA CORREGIDO EN LA FUENTE. Lo escribio coder-terra-ca en su lane, lo VERIFIQUE YO leyendo el archivo, no acepte el reporte. src/components/practica-form.tsx:405-415. QUE DICE AHORA, y cubre las cuatro cosas que la fila necesitaba que dijera: que isRequired tiene DOS capas que enumeran POBLACIONES DISTINTAS; que el servidor relee vTerraModuloCampos con WHERE moduloId = $1 AND isRequired y SIN predicado de cirugia ni de alcance; que esta capa de navegador itera camposVisibles ya filtrado por el alcance de la cirugia elegida; y que por lo tanto un campo requerido a nivel modulo pero fuera de ese alcance queda OCULTO y no reclamado aca, pero igual rechazado por el servidor al guardar. Ademas lleva el denominador y el corolario: los seis campos actuales tienen isRequired en falso, el guard del servidor devolvio conjunto vacio en cada guardado, y se activa por una edicion de DATOS y no por un cambio de codigo. Tambien quedo retageado de TERRA-76 a TERRA-127, que es donde vive el hallazgo. LO QUE LE PEDI CORREGIR ANTES DE PUSHEAR, Y ES LA MISMA LECCION QUE NOS COSTO EL DIA: la frase "los seis campos tienen isRequired en falso" esta escrita EN PRESENTE Y SIN FECHA. Es una medicion disfrazada de hecho permanente. Deja de ser cierta por una edicion de datos - el disparador que el propio comentario nombra - y cuando eso pase el comentario va a seguir afirmando que la divergencia es latente, leido por alguien sin motivo para dudar. Pedi que se feche y se marque como foto: "Measured 2026-09-01: ...". No pedi mecanismo ni test; pedi que la frase declare que es una foto y de cuando. Es la tercera vez hoy que aparece la misma forma en este proyecto - el titulo de TERRA-131, el comentario viejo de TERRA-76 afirmando exclusividad, y ahora este. Vale dejarlo escrito: en esta base, una afirmacion en presente sobre el estado de los datos o del codigo se escribe FECHADA o no se escribe. SIN CAMBIO DE COMPORTAMIENTO, DATOS NI CONFIGURACION. Solo comentario. Queda sin commitear hasta que le agregue la fecha.
-
CERRADA LA MITAD DE COMENTARIO. Pusheado por coder-terra-ca como 4f1f1fc, version 0.31.32, mensaje "docs(practica): date required-field scope measurement". VERIFICADO POR MI EN TRES LUGARES y no aceptado del reporte: el alcance del commit por gh api - package.json mas src/components/practica-form.tsx, nada mas -, y el texto del comentario leyendolo en el archivo. QUEDO ESCRITO ASI, y la fecha entro donde tenia que entrar: "Measured 2026-09-01: all six current terraModuloCampos rows HAD isRequired=false, so the server guard HAD returned an empty set on every save". Pasado, no presente, y con fecha. La frase ahora se declara foto en vez de afirmarse permanente, que era todo lo que faltaba. LO QUE ESTO NO ARREGLA, dicho para que nadie lea el commit como el cierre de la fila: NO CAMBIO NINGUN COMPORTAMIENTO. Las dos capas siguen enumerando poblaciones distintas - servidor sin predicado de alcance, navegador sobre camposVisibles ya filtrado - y el dia que alguien marque un campo como requerido, la divergencia se activa igual. Lo unico que cambio es que ahora esta escrita donde el que la active la va a leer. LA FILA SIGUE ABIERTA POR ESO. Y el gate: 464 tests, lint 0 errores con 3 warnings preexistentes, typecheck y build de produccion. coder-terra-ca aclaro por su cuenta, las dos veces, que eso es evidencia de gate de fuente y NO de verificacion en runtime. La distincion es correcta y la dejo escrita para que no se la lea de mas despues.