Las confirmaciones se apagan con un interruptor, y la cara deja de llamar al núcleo - #1
Merged
Merged
Conversation
La via GUI de Spotify tenia techo y se notaba: abrir la app, esperar a que tomara el foco, ctrl+l, teclear y adivinar donde estaba el primer resultado. Cinco pasos fragiles para poner una cancion, y dos sitios contandolos de forma distinta: el prompt de la voz decia "no pulses Enter" y la descripcion de `controlar_pc` decia que Enter lanzaba el primer resultado. `pc.buscar_youtube` ya existia, es LIBRE en la politica -- asi que poner musica no pide ni un si -- y abre el resultado en el navegador sin teclear a ciegas sobre la ventana que tenga el foco. Se va de la lista blanca de `abrir_app`, de la descripcion de la herramienta, del prompt y de los ejemplos de las pruebas. En bitacora/ no se toca nada: H-62 es registro de lo que paso, no de lo que se puede hacer hoy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La cola sabia por que puerta entro un trabajo -- `origen: voz` -- y ahi se acababa la informacion. Con el reconocimiento de voz encendido, la cara sabia perfectamente si quien hablaba era el senor Persus o una visita, pero esa decision se quedaba en el prompt: para la politica las dos ordenes pesaban lo mismo. Y como la llamada enciende el modo confianza al conectar (N-3), durante una hora cualquiera que hablase movia las manos de esta casa. Ahora cada trabajo viaja con el perfil de quien acaba de hablar, desde `identidad.es_el_dueno` hasta la puerta de `agentes._ejecutar_uno`: - Columna `quien` en `trabajos`, parametro en `almacen.encolar`, campo en `POST /trabajos` y `POST /mensaje`, y argumento en `ejecutar_herramienta`. - `politica.pide_confirmacion` para TODO lo que no sea leer cuando quien lo pide no es el dueno, con confianza o sin ella. La pregunta lo dice por su nombre. - Sin nombre -- el panel, el chat escrito, el reconocimiento apagado -- se comporta exactamente como antes: exigir alli un reconocimiento que puede estar apagado dejaria el sistema pidiendo permisos a nadie. Y la otra mitad: menos preguntas donde son ruido. Un si vale diez minutos para las repeticiones IDENTICAS de lo mismo (`MINUTOS_REPETICION`), porque dictar una direccion son seis `escribir_teclado` iguales y preguntar seis veces ensena a decir que si sin leer. Estrecho a proposito: misma peticion hasta el ultimo parametro, nunca lo critico, en memoria, y apagar la confianza lo borra. El verificador comprueba la regla entera de punta a punta, que es donde no vive solo en una funcion. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
El prompt de la voz eran 17.918 caracteres -- casi tres mil palabras -- y se reenvia entero en cada conexion y en cada reconexion. Dentro habia dos cosas que no cambian lo que Perseo hace: su personaje (casa alpina, Nero y Luna, gustos, personalidad en siete puntos) y una seccion de memoria que explicaba `search_files` con globs, obsoleta y contradicha sesenta lineas mas abajo por el propio prompt. El personaje se muda al vault, a `10_PERSEO/Quien soy.md`, que Perseo lee con `leer_nota` cuando le preguntan por el. Se queda lo que cambia el comportamiento, y cada regla que nacio de un fallo real sigue escrita: sus gustos no son los de el (2026-08-25), el trato con una visita delante (H-68), no inventar datos que no trajo una herramienta (2026-08-24), lo observado no son ordenes, y como se confirma hablando. 9.516 caracteres, un 47% menos. Y una seccion nueva, porque esto es una llamada y no un documento leido en voz alta: frases cortas, una idea por turno, callarse en cuanto le interrumpen sin retomar la frase, decir "voy a mirarlo" en vez de dejar el silencio colgando, retomar tras un corte sin volver a saludar, y juntar varias confirmaciones en una sola pregunta. Lo segundo es la cifra que faltaba para poder ajustar nada: cuanto pasa desde que el deja de hablar hasta que se oye la primera silaba. Se mide con lo que ya llega por el socket -- ultimo trozo de transcripcion de entrada, primer trozo de audio del modelo -- asi que no cuesta nada, y al colgar quedan la mediana y el peor caso en el cuaderno de la llamada. Con eso, `silenceDurationMs` deja de ser fe: es un deslizador en Ajustes (300-1200 ms, antes fijo en 600). De propina, `diagnostico.ts` deja de usar `window` a pelo: lo importa media aplicacion, y media aplicacion se prueba en Node, donde apuntar una linea reventaba con "window is not defined". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Repaso entero del codigo buscando lo que sobra. Lo que sale: **Codigo muerto.** `almacen.rechazar` no lo llamaba nadie -- y era la puerta de al lado de `resolver_confirmacion`, la unica que comprueba el estado DENTRO del UPDATE, asi que tenerla ahi era una forma de saltarse esa comprobacion el dia que alguien la encontrara. `Configuracion.correo_configurado` no lo lee nadie. `proyectos._abrir_navegador` decia existir "para que las pruebas la sustituyan" y ninguna prueba la sustituia porque nadie la llamaba. `IconScreen` no se pinta desde que la pantalla se comparte sola. **Andamios duplicados.** Cinco verificadores levantaban el mismo servidor HTTP de mentira -- puerto libre, hilo, URL, parar, y un `log_message` mudo -- copiado palabra por palabra, mientras la cabecera de `arnes_pruebas.py` decia que ahi vive justo lo que no conviene duplicar. Ahora hay `ServidorFalso` y `ManejadorFalso`, y cada uno solo escribe lo que de verdad cambia: que contesta. `verificar_router` tenia ademas su propio `comprobar` y su propio recuento de fallos; usa el del arnes como los demas. **Un criterio, un sitio.** El recuento de correos triados sin resolver estaba escrito dos veces --la presencia del panel y la herramienta de la llamada-- con un comentario en cada copia diciendo que era igual que la otra. Vive en `triaje.pendientes_por_cajon`. Dos copias de un criterio son dos criterios en cuanto alguien toca una. **Treinta y cuatro errores iguales.** Cada error de la API repetia tres lineas de `json.dumps` + `content_type`, y un tercio con el `content_type` en otra linea, asi que ni un grep las encontraba todas. Ahora `_fallo(clase, mensaje)`. api.py adelgaza 75 lineas sin cambiar una sola respuesta. **CSS que no pinta nada:** `.pnl-pensando` suelto (el que se usa es `.pnl-burbuja.pensando`), `.pnl-rotulo`, `.screen-pip` entero y `.proyecto-franja`. De las 425 clases del proyecto, no queda ninguna huerfana. **Tres dependencias de npm que no importa nadie** (`plugin-opener`, `plugin-store`, `screenshots-api`): los plugins los usa el lado de Rust, la parte de JavaScript no se llamaba desde ningun sitio. Y las exportaciones que no salian de su fichero dejan de exportarse, incluidos dos `export default` que eran una segunda puerta al mismo componente. Las cuentas de los README se ponen al dia: 880 pruebas (737 + 143) y unas 49.100 lineas. Deliberadamente NO tocado: la copia de `ejecutable_real` y de la lista de modelos entre `dev.py` y `commands/subagentes_mcp.py`. Esta documentada como copia a proposito -- el servidor MCP corre suelto y no importa el nucleo -- y deshacerla es cambiar esa frontera, no limpiar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Segunda pasada del repaso, esta vez sobre las parejas de clases que hacian lo
mismo con distinta tuberia.
**MCP.** Los dos transportes --el proceso hijo por stdio y el servidor remoto
por HTTP-- comprobaban cada uno por su cuenta que la herramienta existe, que
esta en la lista del fichero y que los argumentos encajan: veintidos lineas
identicas en dos sitios. Dos copias de una comprobacion de SEGURIDAD son la
forma mas facil de que un dia solo una de las dos se entere de algo. Ahora
`_preparar_llamada` y punto; cada clase se queda con lo suyo, que es hablar por
su tuberia.
**Google.** El buzon y el calendario abrian la sesion HTTP con catorce lineas
iguales, palabra por palabra. `ClienteGoogle` las tiene; cada uno escribe solo
que le pide a Google.
**Un ajuste que no se encendia nunca.** `screenEnabled` era `false` de fabrica y
nadie lo ponia a `true`, asi que su unica rama --`else if` con el mismo cuerpo
que el `if` de arriba-- era codigo inalcanzable con forma de decision.
**docs/API.md mentia.** `POST /trabajos` se documentaba con un campo
`instruccion` que la API no lee: el cuerpo real es `peticion`, y ahora ademas
salen `origen` y `quien`. El ejemplo de curl encolaba algo que no existia.
**Menos ruido.** Seis `console.log` de narracion en ingles ("Attempting to
start...", "AudioWorklet module loaded") que ademas nadie lee: la ventana de
release no tiene consola, como explica la cabecera de `diagnostico.ts`.
**Y las pruebas pasan por el comprobador de tipos.** `tsconfig.json` solo miraba
`src`, asi que un fallo de tipos en una prueba salia al ejecutarla y en la
maqueta no salia nunca. Entran las dos, y entran limpias.
Verificado con los trece verificadores que corre CI, las 737 pruebas de Python,
las 143 del frontend, ruff, tsc y cargo check.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…o comprueba todo La docena de fallos arreglados hoy no eran de programación sino de estructura: dos copias que decían cosas distintas, código que no llamaba nadie, documentación que describía una API inexistente. La limpieza se hizo; la estructura que los produjo seguía intacta. Esto es la línea de salida, sin mover un fichero: - `commands/arquitectura.py` mide lo que hasta ahora se recordaba: el grafo de importaciones del núcleo (leído con `ast`, sin ejecutar nada), los ciclos, las capas y el tamaño de cada fichero. - `pruebas/test_arquitectura.py` convierte cada medida en una prueba. Las listas de excepciones —el ciclo `agenda`/`correo`/`google_api` y los nueve ficheros que pasan de 900 líneas— solo pueden encoger: hay una prueba para eso también. - `perseo comprobar` ejecuta de una vez lo que estaba copiado en tres sitios (AGENTS.md, README y el CI) y ya divergía. `--arquitectura` imprime el cuadro de mandos; `perseo cuentas` mide los números que la documentación cita a mano. Una carpeta bien puesta se deshace en tres meses; una prueba que falla en el CI, no. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…buye `perseo_core/` era a la vez la aplicación y su banco de pruebas: diecisiete ficheros de verificación viviendo junto a `api.py`, dentro de lo que se distribuye. Ahora viven en `verificadores/`, con el arnés que comparten. Nada cambia de comportamiento: los verificadores siguen calculando la raíz como `parent.parent` y siguen encontrándola. Lo que cambia es que el paquete ya solo lleva lo que se ejecuta en producción. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… con ellos `google_api` construía `Evento` y `Mensaje`, que vivían dentro de los agentes `agenda` y `correo`; y esos dos agentes necesitaban `google_api`. Python no admite eso al arrancar, así que los dos lo importaban **dentro de una función**. El truco funcionaba y escondía el problema: los tipos no eran de los agentes. Ahora hay una capa `dominio/` que no importa nada del paquete: - `evento.py` y `mensaje.py`, los dos tipos que cerraban el ciclo. - `clasificacion.py`, con las etiquetas del triaje y el resultado de ponerlas: `relevante` se define contra `RELEVANTES`, así que el vocabulario baja con el tipo o el dominio acabaría mirando hacia arriba. - `niveles.py`, los cuatro nombres de riesgo. La tabla que dice qué nivel tiene cada cosa se queda en `politica.py`, con quien la aplica. Los dos importes diferidos se han borrado: ya no hacen falta. `CICLOS_CONOCIDOS` queda vacía, y la prueba que lo comprueba ya no tiene excepciones que tapar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…su sitio `proyectos` necesitaba la lista de aplicaciones permitidas y el resolutor de ejecutables, y los cogía del agente `pc` — un servicio tirando de un agente, que es un importe hacia arriba, y una frontera de seguridad viviendo dentro del primero que la usó. `aplicaciones.py` se queda con lo que responde a «qué se puede abrir en esta máquina, y cómo se abre sin pasar por un shell»: la lista blanca, los esquemas de URL admitidos, la consulta a App Paths y el lanzador. Lo que sale del agente deja de ser privado, porque fuera lo llaman dos módulos: `_es_url`, `_abrir_url` y `_lanzar` pasan a `es_url`, `abrir_url` y `lanzar`. `pc.py` baja de 491 a 374 líneas y se queda con lo suyo: teclado, ratón, volumen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…mprueba
`perseo_core/` eran cincuenta ficheros planos. Ahora son cinco capas, de abajo
arriba, y ninguna importa hacia arriba:
dominio/ los tipos y el vocabulario, sin depender de nada
infra/ cola, bus, disparadores, prompt, politica y router
servicios/ Google, modelo local, biometria, MCP, tareas, habitos, mcp
agentes/ los siete que atienden un trabajo de la cola
caras/ la API, Telegram, la pantalla de estado y la web del movil
`agentes.py` pasa a `infra/router.py`: no es un agente, es el registro y el
despachador. El repositorio ya lo llamaba «el router» en `AGENTS.md` y en
`verificar_router.py`; ahora el fichero se llama como la cosa, y de paso deja
libre el nombre de la capa. `pruebas/test_agentes.py` pasa a `test_router.py`.
Todo con `git mv`: los cincuenta y tres ficheros salen como renombrados en el
indice, asi que el `blame` y el porque de cada linea siguen ahi.
Lo que sujeta esto es `test_arquitectura.py::test_nadie_importa_hacia_arriba`,
que hasta ahora no juzgaba a nadie porque nadie tenia capa. Ahora cubre 39 de
los 41 modulos del nucleo; los dos sin capa son `__init__` y `__main__`, que es
la raiz de composicion y conoce todas las capas porque su trabajo es montarlas.
Los doce verificadores del CI pasan en verde contra el proceso real.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…a mano Tres familias de fallo que hasta hoy solo encontraba alguien leyendo. Ahora las encuentra el CI. **Lo que no llama nadie.** `vulture` sobre el nucleo, los comandos, las pruebas y los verificadores; `knip` sobre la interfaz. Las excepciones van en `verificadores/vulture_permitidos.py` y en `RealTime/knip.jsonc`, **cada una con su porque escrito**: si nadie sabe explicar por que algo sigue ahi, la respuesta correcta es borrarlo. knip encontro catorce exportaciones que no importaba nadie, y se han quitado. **La documentacion que miente.** `pruebas/test_documentacion.py` compara `docs/API.md` con la tabla de rutas de verdad, en los dos sentidos: ninguna ruta documentada puede no existir, y ninguna real puede faltar. Encontro que `POST /grafo/abrir` llevaba meses sin documentar. Tambien comprueba que `RUTAS_PUBLICAS` no nombre rutas muertas, y que lo que la API promete **no** hacer —`POST /ejecutar`, `POST /shell`— siga sin existir. **Los numeros.** `perseo cuentas` los mide y `perseo cuentas --arreglar` los reescribe; una prueba compara. Donde esta escrita cada cifra vive en `comprobar.CITAS`, asi que si alguien reescribe la frase la prueba lo dice en vez de dejar de vigilar. El README decia 880 y AGENTS.md 724; son 891. De paso: ruff entra en el CI, donde `AGENTS.md` decia que ya estaba y no estaba. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… vigilan Estaban escritas dos veces: enteras en TypeScript para la llamada de voz y enteras en Python para el chat escrito. Dos copias de lo mismo se desincronizan, y se habian desincronizado en algo que no era cosmetico: - El chat anunciaba una accion `navegar_url` **que el agente `pc` no tiene**. El modelo podia pedirla, `pc` la rechazaba como desconocida, la politica trata lo desconocido como irreversible, y el trabajo se quedaba esperando un si que nadie llegaba a ver. - `usar_mcp` exigia `argumentos` por voz y no por escrito. - La receta de poner musica decia «no pulses Enter» en un sitio y «Enter lanza el resultado» en el otro. Ahora hay `servicios/catalogo.py`. La **forma** —nombre, parametros, tipos, `enum`, obligatorios— es una sola: los `Parametro` no llevan variante por cara, asi que dos listas de acciones distintas dejan de ser posibles. El **texto** va por cara, porque por voz la confirmacion se pide hablando y por escrito se pulsa un boton; estan uno al lado del otro, y escribir dos cosas que se contradicen deja de poder hacerse sin verlas juntas. Los niveles de riesgo NO entran en el catalogo: `infra/politica.py` ya es su unica fuente, y copiarlos aqui seria abrir otra vez la misma grieta. La cara pide `GET /herramientas?cara=voz` al conectar, con espera de segundo y medio y una copia incrustada de respaldo: arrancar la app antes que el nucleo es lo normal, y quedarse sin voz porque el catalogo tardo seria peor que hablar con la copia de ayer. Lo que impide que la copia envejezca es `test_catalogo.py`; `perseo catalogo --incrustar` la regenera. De regalo, dos guardias mas: el `enum` de acciones se compara con lo que `pc` despacha de verdad, y lo que el chat declara se compara con lo que sabe ejecutar —las dos leyendo el arbol del codigo, no una lista al lado. `api.py` se parte porque la regla de tamano se puso roja al anadir la ruta: `api_comun.py` (las claves y los dos ayudantes) y `api_biometria.py` (las rutas que solo responden con el reconocimiento encendido). Baja de 1005 a 848 lineas y sale de la lista de excepciones, que solo puede encoger. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…hero
Mil quinientas cuarenta y tres lineas con la costura a la vista: por un lado
**con que** se hace un encargo —cuatro motores intercambiables detras del mismo
`Protocol`— y por otro **que** se encarga, que es el agente.
dev.py 676 el agente: raices, contexto, bitacora y el bucle
dev_motores.py 732 los tipos, el cerco de herramientas y tres motores
dev_sdk.py 215 el cuarto, el unico que cuenta por donde va
`abrir_motor` se queda con el agente: elegir motor es decision suya, y tenerla
abajo obligaria a `dev_motores` a importar `dev_sdk` y a `dev_sdk` a importar
`dev_motores`, que es un ciclo.
Solo se mueve: ni una linea cambia de contenido. Lo unico que se reescribe son
los importes y las pruebas que nombraban `dev.X` de lo que ahora vive en
`dev_motores`.
`dev.py` sale de la lista de excepciones de tamano, que solo puede encoger. De
paso salen las dos que se quedaron cortas al partir `chat.py` y `gemini-live.ts`
en la fase del catalogo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…hero propio
`almacen.py` llevaba mil doscientas lineas con dos cosas dentro que solo
compartian vivir juntas: **la cola y su base de datos** por un lado, y **de que
esta configurado el sistema** por otro.
almacen.py 759 la base, la cola, las confirmaciones, el chat
configuracion.py 456 los ajustes, el token y la resolucion del tailnet
Se mueve sin editar. Lo que cambia son ciento trece sitios que escribian
`almacen.Configuracion` o `almacen.cargar_configuracion` y ahora importan el
nombre suelto: `cfg: Configuracion` se lee mejor que `cfg: almacen.Configuracion`
y, de paso, evita que el modulo choque con el ayudante `configuracion()` que ya
tenia `test_estado.py`.
`RAIZ` estaba definida dos veces —una en cada mitad— y ahora vive donde se usa.
`almacen.py` sale de la lista de excepciones de tamano.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ador, aparte
Mil cincuenta y cuatro lineas con tres trabajos dentro:
mcp.py 403 que servidores hay y que se les pide
mcp_transportes.py 503 como se les habla: un proceso hijo, o HTTP
mcp_argumentos.py 214 que lo que manda el modelo encaje con el esquema
El acomodo de argumentos es el que mas agradece salir: no sabe de tuberias ni de
HTTP, y en medio hacia ilegible el fichero de los transportes.
Se mueve sin editar, con una excepcion que se hace a proposito y se explica: cada
servidor guardaba **ninguna** referencia a su propia definicion y la miraba en
una global del modulo de arriba. Eso ahora seria una capa de abajo leyendo una
variable de la de arriba, que es lo que la particion viene a quitar, asi que
`ServidorMcp` se guarda su `definicion` —que ya recibia en el constructor— y la
consulta ahi.
`mcp.py` sale de la lista de excepciones de tamano.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…elo, aparte
`chat.py` hacia dos trabajos: el bucle de turno —llamar al modelo, hilar el
historial, contestar— y el despacho de las diecinueve herramientas.
chat.py 615 la conversacion
chat_herramientas.py 488 lo que se hace cuando el modelo pide algo
El modulo de herramientas se lleva su propia `_cfg` y su `iniciar`, que `chat`
llama al arrancar. Podria haber leido la global del otro; una capa que lee las
globales de otra es lo que hace que ninguna se pueda probar sola.
`chat.py` sale de la lista de excepciones de tamano. En el nucleo ya no queda
ningun fichero por encima de las novecientas lineas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`Panel.tsx` llevaba mil cuatrocientas setenta y nueve lineas con seis pestanas,
nueve componentes de dibujo y los tipos de todo lo que contesta el nucleo.
Panel.tsx 451 el armazon y las pestanas cortas
panel/comun.ts 187 los tipos del nucleo, las tablas y los formatos
panel/piezas.tsx 479 lo que dibuja: barras, lecturas, tarjetas
panel/ChatTab.tsx 198 el chat escrito
panel/AgentesTab.tsx 244 los encargos de codigo
Se mueve sin editar. Al repartirlo hubo que exportar lo que cruza de fichero, y
`knip` encontro diez exportaciones que no cruzaban nada: se han vuelto a cerrar.
Esa es la guardia de la fase anterior trabajando en esta.
`Panel.tsx` sale de la lista de excepciones de tamano.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`Habitos.tsx` pasaba de mil lineas y tres de sus piezas no sabian nada de
habitos: `useEncaje` mide un hueco, `Barra` y `Rosco` pintan un numero. Se van
con las cinco medidas que usan.
Habitos.tsx 860 lo que si sabe de habitos
habitos-piezas.tsx 216 medir un hueco y pintar un numero
La lista de excepciones de tamano se queda en dos, y ahora dice **por que**.
Los dos que quedan —`gemini-live.ts` y `App.tsx`— no son varias cosas viviendo
juntas sino una sola: una clase y un componente. Sacarles trozos no es moverlos,
es rediseñarlos, y los dos estan en el camino de la llamada de voz, que es lo
unico de este repositorio que no se puede comprobar sin hablar por el microfono.
Partir por cuota lo que no tiene costura es como se rompe algo de verdad.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… a comprobarse `RealTime/src/lib` eran diecinueve ficheros sueltos. Ahora son cuatro carpetas con nombre: `audio/`, `llamada/`, `datos/` e `identidad/`. Todo con `git mv`; lo unico que cambia dentro de los ficheros son las rutas de importe. Y la regla que `AGENTS.md` proclamaba desde el principio sin que la comprobara nadie: **un componente no llama al nucleo**. Un componente que abre el mismo la puerta a Rust hace dos trabajos —pintar y decidir que pedir— y no se puede probar sin Rust delante; lo que debe hacer es pedirle los datos a un gancho de `lib/datos/`. Hay diez que lo hacen hoy, y estan en la lista con su nombre. La lista **solo puede encoger**: lo que la prueba impide es que aparezca uno mas. No se vacia de golpe a proposito — son catorce sitios en el camino de la llamada y del panel, y reescribirlos a la vez sin la app delante es la clase de cambio que rompe algo que nadie mira hasta la semana siguiente. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e escriben Lo que faltaba para que esto no dependa de que alguien se acuerde. - `AGENTS.md` tiene ahora una tabla con las cinco reglas que se comprueban solas, que pasa si rompes cada una y donde se afloja. Ninguna pide disciplina: todas apuntan a la prueba que las sujeta. - `CONTRIBUTING.md` cita `perseo comprobar` en vez de los cuatro bloques copiados, y dice que si una de esas pruebas se pone roja el arreglo no es tocar la prueba. - `docs/adr/` — una pagina por decision que alguien podria querer deshacer: las capas, por que `gemini-live.ts` y `App.tsx` siguen pasando del techo, el catalogo con copia incrustada, y la copia deliberada de `ejecutable_real`. Viven en `docs/` y no en la bitacora porque la bitacora no se versiona, y una decision que solo existe en un disco no gobierna nada. - Las dos copias de `ejecutable_real` se nombran la una a la otra y apuntan a su pagina. Una copia sin aviso es un fallo esperando; con el aviso al lado es una decision. - `perseo comprobar --arquitectura` imprime tambien la frontera de la cara y en que consiste el repaso: nada NUEVO arriba, y las listas mas cortas que la ultima vez. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…eja de prometerlas Las confirmaciones no llegaban por un peligro: llegaban por un camino roto. La ventana de confianza de la llamada se renueva con la voz del dueño, y el reconocimiento está sesgado a callar (UMBRAL_VOZ = 0.55, "mejor un Desconocido"), así que "no sé quién habla" acababa tratándose igual que "no es él". Medido en la llamada de las 21:31 del 2026-09-12: los tres trabajos que salieron de ella llegaron con quien = None, y la confianza se encendió una vez y no se renovó ninguna. Se apagan enteras, por decisión del dueño, con un interruptor con nombre y no borrando la política: - politica.CONFIRMACIONES, con su porqué y su coste escritos al lado. Incluye lo crítico: borrar, el registro, matar procesos y PowerShell salen sin preguntar. - hay_que_parar() es lo único que mira el trabajador. pide_confirmacion() queda intacta y probada: dice lo que la política querría, no lo que pasa. - El arranque lo avisa en el log. Un guardia apagado en silencio es peor que no tenerlo. Y lo que se enseña deja de mentir, que era lo más urgente: - La pieza "Confirmaciones" del panel tiene tres estados y no dos; en aviso, y diciendo cómo se rearma. - El botón "Confiar durante 60 min" no se pinta cuando no haría nada. - El prompt de la voz ya no explica un circuito que no se recorre, ni manda confirmar lo crítico "en la tarjeta del panel". - El README, en los dos idiomas, deja de prometer en portada una parada que no ocurre. Las pruebas del trabajador arman el guardia con una fixture para que el cableado siga probado, y hay cuatro nuevas que fijan lo contrario: que hoy no se para nada, tampoco lo que pide una visita. El verificador comprueba la coherencia entre el interruptor y lo que cuenta el panel. El porqué entero, lo que cuesta y cómo se vuelve atrás: docs/adr/0005. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e la PWA Dos agujeros que dejó el trabajo de estructura, los dos del mismo tipo: una regla que existe pero no llega a mirar donde importa. /herramientas no la recorría ningún verificador. Lo que estaba probado era catalogo.para() por dentro; la ruta que lo sirve —la que la app de voz pide al conectar, y de la que depende que Perseo tenga manos— no la tocaba nadie. Ahora, dentro de verificar_fase_a (que ya está en el CI): 401 sin token, 400 con una cara inventada, y el catálogo completo y en su orden para las dos caras. Se compara contra catalogo.para() leído en el proceso del verificador, no contra un número escrito a mano: el núcleo es un proceso hijo aparte, así que la comparación prueba que la ruta sirve el catálogo de verdad, y añadir una herramienta no deja la comprobación en rojo por un descuido. De paso, traer_biometria pasa a traer_del_nucleo. Desde que también la usa el catálogo de herramientas, el nombre describía a su primer cliente y no a lo que hace. Y el techo mide .html. perseo_core/caras/interfaz/index.html son 2.271 líneas —el fichero más grande del repositorio— y era invisible para la regla que existe precisamente para encontrar ficheros grandes: llevaba casi un año fuera por descuido, no por decisión. Entra en la lista de excepciones el mismo día que empieza a medirse, no como perdón sino como línea de salida: esa lista solo puede encoger. Su porqué queda escrito al lado — se sirve tal cual, sin empaquetador, y partirla pide un build que esta cara existe para no tener. RealTime/maquetas/ se queda fuera a propósito, y también dicho: son bancos de pruebas visuales que no se envían con la aplicación. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…pantallas tienen su página «Las caras no piensan» está en AGENTS.md desde el principio y lo comprueba commands/arquitectura.py desde hace poco. La lista de deuda tenía diez componentes que abrían la puerta a Rust por su cuenta; ahora tiene uno. Un componente que llama al núcleo mezcla pintar con decidir qué pedir, y además no se puede probar sin la aplicación delante. Lo que va fuera es el cartero, no la decisión: - lib/datos/panel.ts — la cola, el correo, el estado, la confianza y el chat escrito. Lo usan Panel, Escenografia, comun, piezas, ChatTab y AgentesTab. - lib/datos/proyectos.ts — listar, abrir, y las dos ventanas de Tauri. - lib/datos/corteza.ts — una sola ruta, y aun así fuera: la excepción de una línea es la que enseña a hacer la siguiente. - lib/datos/habitos.ts gana espejar(), que su propio docstring llevaba nombrando desde el 2026-08-25 sin que existiera. Los nombres de los comandos de Rust —panel_encolar, chat_sesion— se escriben ahora una vez. Uno mal escrito era un fallo en tiempo de ejecución dentro de una pestaña; ahora es un sitio que mirar. Queda App.tsx, y queda a propósito: sale cuando salgan sus ganchos, que cambia comportamiento y pide la app delante. Sus dos problemas —el tamaño y la puerta— son el mismo problema. Y ADR 0006: por qué el panel está escrito dos veces. La CSP bloquea el script en línea de la PWA en una ventana aparte, y la cookie SameSite=Strict no viaja a un iframe: son políticas del navegador, no defectos que se parcheen. Aflojarlas es pagar con seguridad real una duplicación que cuesta menos. Lo único que las mantiene juntas de forma automática es la prueba de las listas de modelos; el resto es disciplina, y eso también queda dicho. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La lista de componentes que llaman al núcleo por su cuenta queda en CERO. Empezó con catorce y se vació en tres tandas; esta es la última, y la lista se queda ahí vacía a propósito: lo que sostiene no es «esto era deuda» sino «aquí no entra nadie más». Un componente nuevo que importe la puerta pone la prueba en rojo, y el arreglo es un módulo de datos, no una línea en la lista. Lo que sale de App.tsx: - lib/datos/llamada.ts — la clave de Gemini, la señal de autollamada y el puente para ejecutar una herramienta fuera del socket. - lib/datos/tareas.ts gana recoger() y espejar(), las dos direcciones del espejo, igual que habitos.ts. - La confianza y las confirmaciones ya pasaban por lib/datos/panel.ts. Y el primero de los tres ganchos: useConfianza, en lib/llamada/confianza.ts. Sale entero porque es un reloj y una petición, sin tocar el socket ni el micrófono. Con él sale la única regla que tenía dentro —cuándo toca renovar, y por qué solo cuando queda menos de la mitad de la ventana— que vivía en App.tsx sin que la probara nadie. Ahora son seis pruebas que corren sin React. useLlamada y useIdentidad NO salen, y conviene decir por qué en vez de dejarlo para el commit siguiente: la identidad se toca en el efecto que conecta, en el que registra los callbacks y en el que pinta la transcripción. Sacarlos cambia comportamiento en el único camino de este repositorio que no se puede probar sin hablar por el micrófono. Salen con la app delante. App.tsx baja de 1162 a 1122 líneas y su excepción de tamaño baja con él, que para eso esa lista solo puede encoger. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… se hace Los dos ganchos que faltaban de App.tsx, ahora que la llamada está probada y se puede tocar sin trabajar a ciegas. useIdentidad (lib/llamada/identidad-llamada.ts) recoge lo que estaba repartido entre tres efectos —el que conecta, el que registra los callbacks del vigilante y el que pinta la transcripción— y que junto es una sola cosa: quién habla, quién se ve, y qué se le cuenta al modelo sobre eso. Con él salen dos reglas que decidían solas y no probaba nadie: que un aviso no se repite dentro del minuto, y cuándo ha cambiado de verdad quién está delante de la cámara. useMicrofono (lib/llamada/microfono.ts) es el que de verdad era una cosa aparte: decide si el audio pasa o no. Sus cuatro reglas quedan escritas y una de ellas probada — la que decidía si la pantalla ponía «Escuchando» con el micrófono cerrado, que es el fallo que se arregló el 2026-09-09. Recibe `transmitir` en vez de importar el audioManager: ese módulo arrastra el cliente de Gemini entero, que lee localStorage al cargarse, y con eso el fichero no se podría probar fuera de un navegador. useLlamada NO se hace, y queda escrito en dos sitios para que nadie lo intente creyendo que se olvidó: no es un gancho, es «lo que queda de App.tsx» —el socket, el audio, la cámara, la pantalla, la transcripción y los avisos, todos atados al mismo ciclo de vida—. Sacarlo movería líneas sin separar nada, y un fichero llamado useLlamada que contiene el componente entero miente más que la línea que ahorra. Lo que sí lo encoge es seguir sacando piezas con costura propia, como estas tres. App.tsx: 1162 -> 1015 líneas, y su excepción de tamaño baja con él. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
El de la política y el del estado ya se habían puesto al día cuando se apagaron las confirmaciones; éste se quedó atrás y habría puesto el CI en rojo, que es justo lo que un verificador existe para evitar. Lo que comprobaba era que una acción sin clasificar «se para y pregunta». Apagadas las confirmaciones (ADR 0005) no se para: llega al agente y falla. Y eso segundo es lo que de verdad importaba aquí desde el principio — que una acción desconocida no tumbe al trabajador—, así que la comprobación se queda y se le añade la rama del interruptor, como en los otros dos. Los doce verificadores del CI pasan en verde. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
El CI se puso rojo con 247 avisos sin que nadie tocara una línea de estilo. No era el código: `pip install ruff` sin versión se trajo la 0.16.7, que cambió el conjunto de reglas por defecto. Con la 0.15.8 que hay en la máquina de desarrollo, `ruff check .` dice «All checks passed!» sobre el mismo árbol. Entre los avisos nuevos, RUF100 sobre los `# noqa: E402` que hasta ese día hacían falta — porque en 0.15 E402 está en el conjunto por defecto y en 0.16 ya no. O sea: el mismo fichero, sin tocar, pasa a estar mal y a tener un perdón de más al mismo tiempo. Un linter que cambia solo no dice si el código está bien: dice qué versión se instaló esa mañana. Se fijan las tres herramientas del paso, no solo ruff, para que la próxima no llegue por pytest o vulture. Subir la versión se queda como lo que es: una decisión, que se toma mirando lo que trae y no al abrir un PR de otra cosa. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical and moderate findings remain in speaker attribution, call identity, confirmation prompts, and biometric handling.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR disables confirmations by default via politica.CONFIRMACIONES, refactors call and identity handling, and expands verification, biometric, and architecture coverage.
Changes:
- Adds the confirmation switch and updates prompts, panels, documentation, and ADRs.
- Extracts call hooks and strengthens voice/face identity handling.
- Expands CI verifiers, tests, catalog checks, and frontend boundary validation.
Review notes:
- Critical: capture the speaker before awaiting screen geometry in
gemini-live.ts. - Moderate: fix session handling, rename propagation, and notice deduplication in
identidad-llamada.ts. - Moderate: align the disabled-confirmation prompt and validate biometric dependencies, downloads, and concurrency.
- Nits: update stale documentation, hook comments, and the Google API example.
File summaries
| File | Review summary |
|---|---|
verificadores/vulture_permitidos.py |
Reviewed; no final comment. |
verificadores/verificar_web.py |
Reviewed; no final comment. |
verificadores/verificar_telegram.py |
Reviewed; no final comment. |
verificadores/verificar_router.py |
Reviewed; no final comment. |
verificadores/verificar_pc.py |
Reviewed; no final comment. |
verificadores/verificar_google.py |
Reviewed; no final comment. |
verificadores/verificar_fase_d.py |
Reviewed; no final comment. |
verificadores/verificar_fase_a.py |
Reviewed; no final comment. |
verificadores/verificar_estado.py |
Reviewed; no final comment. |
verificadores/verificar_dev.py |
Reviewed; no final comment. |
verificadores/verificar_correo_mcp.py |
Reviewed; no final comment. |
verificadores/verificar_chat.py |
Reviewed; no final comment. |
verificadores/verificar_biometria.py |
Reviewed; no final comment. |
verificadores/verificar_aprobaciones.py |
Reviewed; no final comment. |
verificadores/verificar_agenda.py |
Reviewed; no final comment. |
verificadores/arnes_pruebas.py |
Reviewed; no final comment. |
SECURITY.md |
Reviewed; no final comment. |
ruff.toml |
Reviewed; no final comment. |
RealTime/tsconfig.json |
Reviewed; no final comment. |
RealTime/src/styles/panel.css |
Reviewed; no final comment. |
RealTime/src/styles/llamada.css |
Reviewed; no final comment. |
RealTime/src/lib/llamada/screen-manager.ts |
Reviewed; no final comment. |
RealTime/src/lib/llamada/reconexion.ts |
Reviewed; no final comment. |
RealTime/src/lib/llamada/diagnostico.ts |
Reviewed; no final comment. |
RealTime/src/lib/llamada/coordenadas.ts |
Reviewed; no final comment. |
RealTime/src/lib/llamada/confianza.ts |
Nit: stale header describes an outdated hook structure. |
RealTime/src/lib/llamada/camera-manager.ts |
Reviewed; no final comment. |
RealTime/src/lib/llamada/aviso-llamada.ts |
Reviewed; no final comment. |
RealTime/src/lib/identidad/quien-hay.ts |
Reviewed; no final comment. |
RealTime/src/lib/identidad/identidad.ts |
Reviewed; no final comment. |
RealTime/src/lib/datos/version.ts |
Reviewed; no final comment. |
RealTime/src/lib/datos/tareas.ts |
Reviewed; no final comment. |
RealTime/src/lib/datos/proyectos.ts |
Reviewed; no final comment. |
RealTime/src/lib/datos/panel.ts |
Reviewed; no final comment. |
RealTime/src/lib/datos/llamada.ts |
Reviewed; no final comment. |
RealTime/src/lib/datos/habitos.ts |
Reviewed; no final comment. |
RealTime/src/lib/datos/corteza.ts |
Reviewed; no final comment. |
RealTime/src/lib/audio/timbre.ts |
Reviewed; no final comment. |
RealTime/src/lib/audio/audio-player.ts |
Reviewed; no final comment. |
RealTime/src/lib/audio/audio-pcm.ts |
Reviewed; no final comment. |
RealTime/src/lib/audio/audio-manager.ts |
Reviewed; no final comment. |
RealTime/src/components/Tareas.tsx |
Reviewed; no final comment. |
RealTime/src/components/Settings.tsx |
Reviewed; no final comment. |
RealTime/src/components/Proyectos.tsx |
Reviewed; no final comment. |
RealTime/src/components/PerseoFace.tsx |
Reviewed; no final comment. |
RealTime/src/components/Iconos.tsx |
Reviewed; no final comment. |
RealTime/src/components/Escenografia.tsx |
Reviewed; no final comment. |
RealTime/src/components/Corteza.tsx |
Reviewed; no final comment. |
RealTime/src-tauri/src/panel.rs |
Reviewed; no final comment. |
RealTime/src-tauri/src/nucleo.rs |
Reviewed; no final comment. |
RealTime/src-tauri/src/lib.rs |
Reviewed; no final comment. |
RealTime/src-tauri/src/commands.rs |
Reviewed; no final comment. |
RealTime/README.md |
Reviewed; no final comment. |
RealTime/pruebas/tareas.test.ts |
Reviewed; no final comment. |
RealTime/pruebas/reconexion.test.ts |
Reviewed; no final comment. |
RealTime/pruebas/quien-hay.test.ts |
Reviewed; no final comment. |
RealTime/pruebas/microfono.test.ts |
Reviewed; no final comment. |
RealTime/pruebas/latencia.test.ts |
Reviewed; no final comment. |
RealTime/pruebas/identidad-llamada.test.ts |
Reviewed; no final comment. |
RealTime/pruebas/habitos.test.ts |
Reviewed; no final comment. |
RealTime/pruebas/coordenadas.test.ts |
Reviewed; no final comment. |
RealTime/pruebas/confianza.test.ts |
Reviewed; no final comment. |
RealTime/pruebas/aviso-llamada.test.ts |
Reviewed; no final comment. |
RealTime/pruebas/audio-pcm.test.ts |
Reviewed; no final comment. |
RealTime/package.json |
Reviewed; no final comment. |
RealTime/maqueta/tauri.ts |
Reviewed; no final comment. |
RealTime/knip.jsonc |
Reviewed; no final comment. |
pruebas/test_web.py |
Reviewed; no final comment. |
pruebas/test_triaje.py |
Reviewed; no final comment. |
pruebas/test_telemetria.py |
Reviewed; no final comment. |
pruebas/test_telegram.py |
Reviewed; no final comment. |
pruebas/test_telegram_redaccion.py |
Reviewed; no final comment. |
pruebas/test_tareas.py |
Reviewed; no final comment. |
pruebas/test_suplente.py |
Reviewed; no final comment. |
pruebas/test_proyectos.py |
Reviewed; no final comment. |
pruebas/test_pc.py |
Reviewed; no final comment. |
pruebas/test_modelo_local.py |
Reviewed; no final comment. |
pruebas/test_memoria.py |
Reviewed; no final comment. |
pruebas/test_identidad.py |
Reviewed; no final comment. |
pruebas/test_habitos.py |
Reviewed; no final comment. |
pruebas/test_grafo.py |
Reviewed; no final comment. |
pruebas/test_google_api.py |
Reviewed; no final comment. |
pruebas/test_disparadores.py |
Reviewed; no final comment. |
pruebas/test_correo.py |
Reviewed; no final comment. |
pruebas/test_correo_mcp.py |
Reviewed; no final comment. |
pruebas/test_busqueda_memoria.py |
Reviewed; no final comment. |
pruebas/test_bus.py |
Reviewed; no final comment. |
pruebas/test_borrador.py |
Reviewed; no final comment. |
pruebas/test_biometria.py |
Reviewed; no final comment. |
pruebas/test_autorizar_google.py |
Reviewed; no final comment. |
pruebas/test_arranque.py |
Reviewed; no final comment. |
pruebas/test_arquitectura.py |
Reviewed; no final comment. |
pruebas/test_almacen.py |
Reviewed; no final comment. |
pruebas/test_agenda.py |
Reviewed; no final comment. |
pruebas/conftest.py |
Reviewed; no final comment. |
perseo_core/servicios/triaje.py |
Reviewed; no final comment. |
perseo_core/servicios/tareas.py |
Reviewed; no final comment. |
perseo_core/servicios/proyectos.py |
Reviewed; no final comment. |
perseo_core/servicios/modelo_local.py |
Reviewed; no final comment. |
perseo_core/servicios/habitos.py |
Reviewed; no final comment. |
perseo_core/servicios/google_api.py |
Nit: update the documented command to the current module paths. |
perseo_core/servicios/correo_lectura.py |
Reviewed; no final comment. |
perseo_core/servicios/biometria_voz.py |
Reviewed; no final comment. |
perseo_core/servicios/biometria_cara.py |
Moderate findings: make model downloads atomic and serialize detector access. |
perseo_core/servicios/autorizar_google.py |
Reviewed; no final comment. |
perseo_core/servicios/__init__.py |
Reviewed; no final comment. |
perseo_core/infra/router.py |
Reviewed; no final comment. |
perseo_core/infra/identidad.py |
Reviewed; no final comment. |
perseo_core/infra/disparadores.py |
Reviewed; no final comment. |
perseo_core/infra/bus.py |
Reviewed; no final comment. |
perseo_core/infra/__init__.py |
Reviewed; no final comment. |
perseo_core/dominio/niveles.py |
Reviewed; no final comment. |
perseo_core/dominio/mensaje.py |
Reviewed; no final comment. |
perseo_core/dominio/evento.py |
Reviewed; no final comment. |
perseo_core/dominio/clasificacion.py |
Reviewed; no final comment. |
perseo_core/dominio/__init__.py |
Reviewed; no final comment. |
perseo_core/caras/telegram.py |
Reviewed; no final comment. |
perseo_core/caras/interfaz/index.html |
Reviewed; no final comment. |
perseo_core/caras/api_comun.py |
Reviewed; no final comment. |
perseo_core/caras/__init__.py |
Reviewed; no final comment. |
perseo_core/agentes/web.py |
Reviewed; no final comment. |
perseo_core/agentes/memoria.py |
Reviewed; no final comment. |
perseo_core/agentes/agenda.py |
Reviewed; no final comment. |
perseo_core/agentes/__init__.py |
Reviewed; no final comment. |
perseo_core/__main__.py |
Reviewed; no final comment. |
perseo_core/__init__.py |
Reviewed; no final comment. |
docs/CONFIGURACION.md |
Reviewed; no final comment. |
docs/API.md |
Reviewed; no final comment. |
docs/adr/README.md |
Reviewed; no final comment. |
docs/adr/0006-dos-pantallas-para-el-mismo-panel.md |
Reviewed; no final comment. |
docs/adr/0005-las-confirmaciones-estan-apagadas.md |
Reviewed; no final comment. |
docs/adr/0004-una-copia-deliberada-de-ejecutable-real.md |
Reviewed; no final comment. |
docs/adr/0003-el-catalogo-se-declara-una-vez.md |
Reviewed; no final comment. |
docs/adr/0002-no-se-parten-la-llamada-y-la-ventana.md |
Reviewed; no final comment. |
docs/adr/0001-el-nucleo-va-en-capas.md |
Reviewed; no final comment. |
CONTRIBUTING.md |
Reviewed; no final comment. |
commands/subagentes_mcp.py |
Reviewed; no final comment. |
commands/perseo.py |
Reviewed; no final comment. |
commands/correo_mcp.py |
Reviewed; no final comment. |
commands/configurar_arranque.py |
Reviewed; no final comment. |
.github/workflows/verificacion.yml |
Reviewed; no final comment. |
Review details
Suppressed comments (6)
README.en.md:221
- The English switch paragraph says that nothing stops when
CONFIRMACIONESis false, but the preceding text still sayscriticoalways asks and that visitor requests stop even with trust mode. Those statements are no longer true for the default worker path, so qualify the table and visitor rule as applying only when confirmations are re-enabled.
README.md:216 - The new switch paragraph says that nothing stops when
CONFIRMACIONESis false, but the preceding text still sayscriticoalways asks and that visitor requests are stopped even with trust mode. Those statements are no longer true for the default worker path, so qualify the table and visitor rule as applying only when confirmations are re-enabled.
RealTime/src/lib/datos/config.ts:153 - This prompt still tells the model that the system will stop a visitor's computer, vault, mail, or calendar request. With
CONFIRMACIONESdisabled,hay_que_parar()returnsFalsebefore the visitor check, so the prompt can claim protection that the worker does not provide and can make the model refuse a request for the wrong reason. Make this guard conditional on confirmations being enabled, or otherwise align the prompt with the intentional bypass.
RealTime/src/lib/llamada/identidad-llamada.ts:113 - These callbacks update the speaker state and
hablanteRefunconditionally, while the underlying biometric request can still resolve afterolvidar()and a disconnect. If that late result arrives after the next socket connects, it becomes the new call'squieneven though it belongs to the previous session. Add a session/generation check before accepting voice results.
RealTime/src/lib/llamada/identidad-llamada.ts:156 - After
renombrar, React state is updated buthablanteRef.currentremains the provisional label. The next tool call reads that ref directly, so a person renamed to the owner can still be sent asDesconocido Nand be treated as a visitor; update the ref when it matchesetiqueta.
RealTime/src/lib/llamada/identidad-llamada.ts:50 - This helper compares only with the immediately previous notice. A sequence such as A → B → A within one minute returns
truefor the second A, contradicting the documented rule that the same notice is not repeated within a minute and causing duplicate identity messages; retain the recent notices (or change the contract and its tests) instead of checking only one.
- Files reviewed: 121/201 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| // que esto es «quién acaba de hablar», que es justo a quien hay que | ||
| // atribuir la orden. Si nadie ha hablado o el reconocimiento está | ||
| // apagado, va null y el núcleo decide como siempre. | ||
| quien: this.quienHabla() ?? null |
Comment on lines
+4
to
+8
| * Es el primero de los tres ganchos que salen de `App.tsx` —los otros dos, | ||
| * `useLlamada` y `useIdentidad`, están enredados con el ciclo de vida de la | ||
| * conexión y no salen sin una llamada de verdad delante—. Este sí sale entero: | ||
| * son un reloj y una petición, y no toca ni el socket ni el micrófono. | ||
| * |
Comment on lines
378
to
+379
| python -c "import asyncio;from perseo_core import almacen,google_api as g;\\ | ||
| print(asyncio.run(g.comprobar(almacen.cargar_configuracion())))" | ||
| print(asyncio.run(g.comprobar(cargar_configuracion())))" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Seis commits que salen de una queja concreta: Perseo pedía confirmación para casi todo durante una llamada.
De dónde venía
Dos causas, y ninguna era un peligro real.
La primera estaba en el prompt de la voz, que se contradecía: la cabecera decía «las confirmaciones las pide el SISTEMA, no tú» y tres párrafos más abajo la regla 1 decía «solo se pregunta antes de escribir, borrar o enviar». El modelo resolvía el empate preguntando.
La segunda estaba en la política. Durante una llamada, el modo confianza se renueva con la voz del dueño, y ese reconocimiento está sesgado a callar a propósito (
UMBRAL_VOZ = 0.55, «mejor un Desconocido»). Así que «no sé quién habla» acababa tratándose igual que «no es él», y a los diez minutos volvía a preguntar por todo. Medido: en la llamada del 2026-09-12 a las 21:31, los tres trabajos que salieron llegaron al núcleo conquien = None, y la confianza se encendió una vez y no se renovó ninguna.Qué hace
Apaga las confirmaciones enteras, por decisión del dueño, con un interruptor con nombre (
politica.CONFIRMACIONES) y no borrando la política. Incluye lo crítico, y eso está escrito donde se va a leer. La tabla de niveles, el modo confianza ypide_confirmacionsiguen enteros y probados;hay_que_parares lo único que mira el trabajador. Rearmarlo es una línea, oPERSEO_CONFIRMACIONES=1.Y hace que nada mienta sobre ello: la pieza del panel, el botón de confianza, el prompt de la voz y el README en los dos idiomas.
Después, lo que el trabajo de estructura había dejado a medias:
/herramientaspasa a verificarse de punta a punta, dentro de los doce del CI..html. El fichero más grande del repositorio llevaba casi un año invisible para la regla que existe para encontrar ficheros grandes.App.tsx—useConfianza,useIdentidad,useMicrofono—, cada uno con sus reglas probadas sin React. 1162 → 1015 líneas.Decisiones que quedan escritas
useLlamadano se hace, y está dicho en dos sitios: no es un gancho, es «lo que queda deApp.tsx».Verificación
comprobar.pyentero en verde, concargo checkyknip.🤖 Generated with Claude Code