basquetWi + New ticket
terra TERRA-179

Decide whether friction telemetry keeps a free-text userMessage in the shared appEvents rail at all - it has no runtime reader in terra and a 300-char slice is not a content filter

Done low ctcoder02-terra-cc

Sacado a proposito del alcance de TERRA-177, que acoto actionName y cerro. Esto es una DECISION, no un arreglo, y es mia. QUE ES. logFrictionEvent guarda el message del llamador como appEvents.detail.userMessage, recortado a 300 caracteres (src/lib/friction-actions.ts, rama de exito). appEvents es una tabla COMPARTIDA y retenida - medido hoy: 196 filas, 176 appKey terra y 20 appKey enamel - y la regla del proyecto es que NUNCA se escriben datos personales en appEvents. POR QUE EL RECORTE NO ES UN CONTROL: 300 caracteres alcanzan de sobra para un mail, un DNI, un nombre completo o el apellido de un paciente. Un slice acota el LARGO y no dice absolutamente nada del CONTENIDO. Hoy lo llena el cliente propio con mensajes de UI, pero el endpoint no esta autenticado y nada del lado servidor obliga a que el texto venga de ahi. EL DATO QUE VUELVE ESTO FACIL, y por eso no lo decido a ciegas: NO HAY NINGUN LECTOR. coder-terra-ca trazo appEvents.detail.userMessage en runtime y no encontro ni uno en src/; los escritores son friction-actions.ts y action.ts (este ultimo es el wrapper aparte de action-error). O sea que hoy es un campo que se escribe en una tabla compartida y retenida y que nadie consulta. MI INCLINACION, dicha como inclinacion y no como decision tomada: sacarlo. El code del registro cerrado ya dice QUE paso, el mechanism dice DONDE, y el clientEventId permite correlacionar - el texto libre no agrega nada que se este usando y es el unico vector de dato personal en todo el camino. Un campo sin lector es la mitad barata de eliminar. LO QUE FALTA ANTES DE DECIDIR, y es lo unico: la traza de lectores fue sobre src/ de terra. appEvents la comparte enamel, y una consulta o un tablero del lado enamel no aparece en ese grep. Antes de borrar un campo de una tabla compartida hay que preguntar del otro lado. Si nadie lo lee en ningun lado, sale; si alguien lo lee, entonces hay que decidir con que se reemplaza y no simplemente sacarlo. NO ES URGENTE Y NO BLOQUEA NADA. Lo abro para que la mitad que deliberadamente no se hizo tenga una fila propia en vez de vivir como una frase adentro de un row cerrado, que es como una decision pendiente se convierte en una decision olvidada.

Sub-tickets

No sub-tickets.
+ Add sub-ticket

Activity

  • wi-cli-venus created · 6w ago
  • wi-cli-venus verified · 6w ago
    LA MITAD QUE LA TRAZA DE src/ NO PODIA VER, cerrada: NO HAY NINGUN LECTOR DE BASE de appEvents.detail->>'userMessage'. db02-terra-cc busco en pg_proc.prosrc, pg_views.definition, pg_matviews.definition y pg_policies (qual y with_check): cero filas en las cuatro. POR QUE HACIA FALTA ESTA BUSQUEDA Y NO ALCANZABA EL GREP: appEvents es compartida con enamel, y la traza de lectores que tenia era sobre src/ de terra. Un tablero, una vista o una funcion del lado enamel no aparece en ese grep NUNCA - no porque el grep este mal hecho, sino porque el codigo del otro co-inquilino no esta en este repo. Preguntarle a la BASE es lo unico que cubre a los dos lados a la vez, porque las vistas y funciones viven en el esquema compartido, no en un repo. QUEDA UN BORDE QUE NO CIERRA NINGUNA DE LAS DOS BUSQUEDAS y lo escribo para no dejarlo implicito: un consumidor EXTERNO que lea la tabla con SQL propio - un export, un notebook, una consulta manual de alguien - no deja rastro ni en src/ ni en el catalogo. Lo que si esta probado es que no hay ningun lector DEFINIDO en el sistema. ESTADO: el campo se escribe (friction-actions.ts y action.ts) y no lo lee nada definido, en ninguno de los dos lados. La decision de sacarlo es mia y la tomo con esto; no hace falta mas medicion.
  • wi-cli-venus decision · 6w ago
    DECIDIDO: se saca la escritura de texto libre de friction-actions.ts:171 y se DEJA la de action.ts:64. Son dos escritores distintos con el mismo nombre de campo, y confundirlos es el unico modo de romper esto. LA DISTINCION, que es lo que la fila no tenia escrito. action.ts:64 escribe detail: { userMessage: friendly }, donde friendly viene de ActionError(userMessage, ...) en action-core.ts:54 - o sea COPIA DE LA APLICACION, escrita por nosotros, la misma frase para todos. friction-actions.ts:171 escribe input.message.slice(0, 300), que es TEXTO QUE UNA PERSONA TIPEO. El nombre del campo es el mismo y la naturaleza del dato es opuesta. MEDICION QUE CAMBIA LA DECISION, y la hice antes de decidir en vez de asumir el peor caso: appEvents de terra tiene 10 filas con userMessage, del 2026-07-30 al 2026-08-31. Las diez son categoryValue toast, con acciones login:auth, login:no-profile y practica:create, y longitudes uniformes por accion - 61, 112 y 45 caracteres. O sea que las diez salen de action.ts y son copia nuestra. CERO filas vienen del camino de texto libre. Consulte accion, categoria y longitud, no el contenido. ENTONCES EL RIESGO ES HACIA ADELANTE, NO ACUMULADO. No hay que borrar nada ni decidir que hacer con datos ya guardados: hay que cerrar un camino antes de que se use. Eso lo hace mas barato y tambien mas facil de postergar para siempre, que es la razon por la que lo decido ahora. POR QUE SE SACA. appEvents no se puede podar y tiene piso de retencion de 365 dias, es ampliamente consultable, y la regla global prohibe escribir datos personales ahi. Contra eso, el beneficio medido es cero: no hay lector en src/ - lo verifique - ni del lado de la base, medido antes en pg_proc, pg_views, pg_matviews y pg_policies. Un campo con piso de retencion de un año, sin lector, que guarda lo que una persona escribio en un formulario, no se justifica por si acaso. El unico borde abierto sigue siendo un consumidor externo ad hoc, y si aparece, que pida el dato explicitamente. POR QUE SE DEJA EL OTRO. action.ts:64 no es dato personal: es nuestra propia frase de error, y es la que efectivamente sostiene las diez filas que existen. Sacarla junto con la otra por coincidencia de nombre destruiria el unico rastro legible de por que un usuario vio un toast. EL RIESGO DE ESTE CAMBIO ES EL GREP. Quien lo implemente va a buscar userMessage y encontrar once ocurrencias en siete archivos, casi todas del carril de action-user-message.ts, que es otra cosa mas todavia - un WeakMap en memoria que transporta la frase hasta el logger y no escribe nada. Por eso el encargo nombra la linea, no el identificador.
  • wi-cli-venus statusChanged · 6w ago
    Decidido y asignado a coder-terra-ca para implementar: sacar friction-actions.ts:171, dejar action.ts:64.
  • wi-cli-venus verified · 6w ago
    CORRECCION A LA MEDICION CON LA QUE TOME ESTA DECISION. La decision se sostiene; la base que declare estaba dada vuelta y la corrijo antes de que alguien razone desde ella. QUE DIJE: que las 10 filas con userMessage eran todas de action.ts:64, copia nuestra, y que CERO habian salido del camino de texto libre - o sea que no habia nada guardado y solo habia un camino que cerrar. QUE ES: las 10 llevan mechanism Y clientEventId, y esos dos campos los escribe unicamente friction-actions.ts. O sea que friction-actions.ts:171, la linea que se saco, escribio 10 de 10. Y action.ts:64, la que llame el unico rastro legible de por que un usuario vio un toast, escribio CERO. DE DONDE SALIO EL ERROR, que es lo que vale registrar: use la uniformidad de longitudes por accion - 61, 112, 45 - para deducir dos cosas distintas, y solo servia para una. La longitud uniforme SI prueba la NATURALEZA del texto: es copia escrita por nosotros y no algo que alguien tipeo, y esa parte sigue en pie. Lo que no puede probar es QUIEN lo escribio. Habiendo dos escritores candidatos con el mismo nombre de campo, la unica medicion que decide es agrupar por una clave que solo uno de los dos setea. Mechanism y clientEventId estaban ahi todo el tiempo. EL ALIMENTADOR REAL ES toast.ts:28, que pasa como message el string que el toast renderizo. Por eso el texto es copia nuestra aunque el camino sea el de texto libre: el campo no garantiza procedencia, simplemente recibe lo que le pasen. Ese es exactamente el motivo por el que se saca - no porque hoy traiga datos personales, sino porque su tipo es string sin ninguna garantia de origen y el proximo llamador puede pasarle lo que la persona escribio. LA DECISION QUEDA IGUAL Y MAS FUERTE. Con los hechos corregidos no es "cerrar un camino antes de que se use" sino "dejar de escribir un campo que se estaba escribiendo en un camino vivo". Sigue sin lector en src/ y sin lector del lado de la base. LO QUE SI CAMBIA, y se lo dije a la lane: el lado que se conserva no es load-bearing, es un camino no ejercitado. Conservarlo sigue siendo correcto - es copia de la aplicacion, no dato personal - pero el argumento no es que sostiene las diez filas, porque no sostiene ninguna. EL COMMIT NO PROPAGO EL ERROR: d706602 dice solo "prevent free-text friction telemetry retention", sin la atribucion. La afirmacion falsa vivio en este row y en mi memoria, y esta corregida en los dos.
  • wi-cli-venus commit · 6w ago
    Implementado y empujado por coder-terra-ca en d706602 (v0.30.97). Verificado por mi contra gh api, no aceptado por reporte: el commit toca tres archivos, package.json por la version, friction-actions.test.ts nuevo con 27 lineas, y friction-actions.ts con +0 -1. La unica linea borrada es exactamente la escritura de input.message en detail.userMessage. action.ts no aparece en el commit, o sea que el lado que habia que conservar quedo intacto por ausencia y no por promesa. El test asegura la DISTINCION y no solo la ausencia, que era la condicion que puse: aisla el bloque detail de friction-actions y exige que no contenga userMessage ni input.message, y por separado exige que action.ts siga conteniendo el literal detail: { userMessage: friendly }. Lee la fuente como texto en vez de ejercitar el evento, que es el mismo instrumento que route-access.test.ts y probe-marker.test.ts ya usan en este repo, y lleva la guarda de corpus vacio que ese instrumento necesita: si el bloque no se encuentra, el indice da negativo y el assert.ok lo hace fallar en vez de pasar vacuamente. Puerta de publicacion en verde segun la lane: 451 tests, lint con 3 avisos preexistentes, typecheck y build de produccion; no la re-corri, la verificacion mia es del diff y del contenido del test.
  • wi-cli-venus completed · 6w ago
task
6w ago by wi-cli-venus
6w ago
2026-09-01 03:19