Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e4259a9
fix(pc): fuera Spotify, la musica se pone por YouTube
PersusUS Sep 12, 2026
343c3da
feat(politica): quien habla decide, y un si vale para lo identico
PersusUS Sep 12, 2026
5cea3cc
feat(llamada): el prompt pierde el personaje, y la espera se mide
PersusUS Sep 12, 2026
6c8d691
refactor: quitar lo que estaba escrito dos veces y lo que no usa nadie
PersusUS Sep 12, 2026
2a1ddca
refactor: una sola copia de lo que se comprueba dos veces
PersusUS Sep 12, 2026
968d8b5
feat(arquitectura): medir la forma del repositorio, y una orden que l…
PersusUS Sep 12, 2026
8e7763a
refactor(verificadores): los andamios salen del paquete que se distri…
PersusUS Sep 12, 2026
d8bc46b
refactor(dominio): los tipos que cruzan capas bajan, y el ciclo muere…
PersusUS Sep 12, 2026
55461c4
refactor(aplicaciones): la lista blanca sale del agente y se pone en …
PersusUS Sep 12, 2026
04146e3
refactor(capas): el nucleo se ordena en cinco capas, y la regla se co…
PersusUS Sep 12, 2026
ff183ab
feat(guardias): lo muerto, la documentacion que miente y los numeros …
PersusUS Sep 12, 2026
2f49b70
feat(catalogo): las herramientas se declaran una vez, y las copias se…
PersusUS Sep 12, 2026
846d6cf
refactor(dev): el agente y sus motores dejan de vivir en el mismo fic…
PersusUS Sep 12, 2026
e7ec4b2
refactor(configuracion): la configuracion sale de la cola y tiene fic…
PersusUS Sep 12, 2026
adcae04
refactor(mcp): los transportes, el acomodo de argumentos y el orquest…
PersusUS Sep 12, 2026
0171de5
refactor(chat): sostener la conversacion y atender lo que pide el mod…
PersusUS Sep 12, 2026
7437f2e
refactor(panel): una pestana, un fichero
PersusUS Sep 12, 2026
6393d01
refactor(habitos): el encaje y los dos dibujos salen de la pantalla
PersusUS Sep 12, 2026
6a437f5
refactor(cara): lib en cuatro carpetas, y «las caras no piensan» pasa…
PersusUS Sep 12, 2026
e8f84ae
docs(gobierno): las reglas apuntan a sus guardias, y las decisiones s…
PersusUS Sep 12, 2026
e1edbf4
feat(politica): un interruptor apaga las confirmaciones, y el panel d…
PersusUS Sep 12, 2026
53df9ba
test(api): /herramientas se verifica de punta a punta, y el techo mid…
PersusUS Sep 12, 2026
91972df
refactor(cara): los componentes dejan de llamar al núcleo, y las dos …
PersusUS Sep 12, 2026
381d26c
refactor(llamada): App.tsx cierra la última puerta, y sale useConfianza
PersusUS Sep 12, 2026
223c731
refactor(llamada): salen useIdentidad y useMicrofono, y useLlamada no…
PersusUS Sep 12, 2026
359561f
fix(verificadores): memoria comprueba lo que pasa, no lo que pasaba
PersusUS Sep 13, 2026
18c1eaf
ci: las herramientas de estilo van fijas, y ruff con nombre propio
PersusUS Sep 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions .github/workflows/verificacion.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,29 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -r perseo_core/requirements.txt
pip install pytest
# Las herramientas van FIJAS, y ruff con nombre propio. El
# 2026-09-13 este paso se puso rojo sin que nadie tocara una linea
# de estilo: ruff 0.16 cambio su conjunto de reglas por defecto y
# aparecieron 247 avisos nuevos, entre ellos RUF100 sobre los
# `# noqa: E402` que hasta entonces hacian falta. Un linter que
# cambia solo no dice si el codigo esta bien: dice que version se
# instalo esa manana. Subirla es una decision, y se toma mirando lo
# que trae, no al abrir un PR de otra cosa.
pip install pytest==9.1.1 ruff==0.15.8 vulture==2.16

- name: Pruebas
run: python -m pytest

- name: Estilo
run: python -m ruff check .

# Lo que no llama nadie. Las excepciones, con su porque, en
# verificadores/vulture_permitidos.py -- y una excepcion sin porque no vale.
- name: Codigo muerto
run: >
python -m vulture perseo_core commands verificadores pruebas
verificadores/vulture_permitidos.py --min-confidence 60

tipos:
name: Tipos y build del frontend
runs-on: ubuntu-latest
Expand All @@ -72,6 +90,13 @@ jobs:
working-directory: RealTime
run: npm test

# El equivalente de vulture para la interfaz: ficheros, exportaciones y
# dependencias que ya no usa nadie. La configuracion, comentada, en
# RealTime/knip.jsonc.
- name: Codigo muerto
working-directory: RealTime
run: npm run muertos

- name: Build
working-directory: RealTime
run: npm run build
Expand Down Expand Up @@ -141,5 +166,5 @@ jobs:
set -e
for script in fase_a aprobaciones telegram fase_d agenda memoria pc dev web politica google estado; do
echo "--- $script ---"
python "perseo_core/verificar_$script.py"
python "verificadores/verificar_$script.py"
done
75 changes: 56 additions & 19 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,37 @@ componente de React, estás en el sitio equivocado.

## Dónde está cada cosa

El núcleo va en cinco capas, de abajo arriba, y **nadie importa hacia arriba**:
`dominio/` (los tipos), `infra/` (cola, bus, política, router), `servicios/` (la
maquinaria), `agentes/` (los que atienden un trabajo) y `caras/` (API, Telegram,
la web del móvil). No es una costumbre: lo comprueba `pruebas/test_arquitectura.py`.

| Vas a tocar | Mira primero |
|---|---|
| La cola, el bus, la API | `perseo_core/api.py`, `bus.py`, `almacen.py` |
| A qué agente va cada cosa | `perseo_core/agentes.py` (el router) |
| Un agente concreto | `perseo_core/<nombre>.py` — se llaman como el agente |
| Qué necesita confirmación | `perseo_core/politica.py` |
| El prompt compartido | `perseo_core/identidad.py` |
| La llamada de voz | `RealTime/src/lib/gemini-live.ts` y `RealTime/src/App.tsx` |
| La cola, el bus, la API | `perseo_core/caras/api.py`, `infra/bus.py`, `infra/almacen.py` |
| A qué agente va cada cosa | `perseo_core/infra/router.py` (el router) |
| Un agente concreto | `perseo_core/agentes/<nombre>.py` — se llaman como el agente |
| Qué necesita confirmación | `perseo_core/infra/politica.py` |
| El prompt compartido | `perseo_core/infra/identidad.py` |
| La llamada de voz | `RealTime/src/lib/llamada/gemini-live.ts` y `RealTime/src/App.tsx` |
| El panel | `RealTime/src/components/Panel.tsx` |
| Los puentes a Rust | `RealTime/src-tauri/src/commands.rs` y `nucleo.rs` |
| La web del móvil | `perseo_core/interfaz/index.html` — un solo fichero, sin build |
| La web del móvil | `perseo_core/caras/interfaz/index.html` — un solo fichero, sin build |
| Las piezas de la app | `RealTime/src/lib/` en cuatro carpetas: `audio/`, `llamada/`, `datos/`, `identidad/` |
| Configuración | [`docs/CONFIGURACION.md`](docs/CONFIGURACION.md) |

Los ficheros que pasan de mil líneas —`Panel.tsx`, `dev.py`, `chat.py`,
`almacen.py`, `gemini-live.ts`, `mcp.py`, `api.py`— se leen **por rangos tras
un `grep -n`**, no de una sentada.
Hay un **techo de tamaño**: blando a 600 líneas, duro a 900, con una lista de
excepciones en `commands/arquitectura.py` que solo puede encoger. Los que hoy
siguen por encima se leen **por rangos tras un `grep -n`**, no de una sentada.
`python commands/perseo.py comprobar --arquitectura` dice cuáles son.

## Ver lo que has cambiado

| Tocaste | Para que se vea | ¿Basta con `npm run build`? |
|---|---|---|
| `RealTime/src/**` | `python commands/perseo.py actualizar` | **No** — la interfaz va incrustada dentro del binario |
| `perseo_core/*.py`, `commands/*.py` | Reiniciar el núcleo: `perseo parar` y luego `perseo on` | **No** — el proceso viejo se queda con el código viejo |
| `perseo_core/interfaz/index.html` | Recargar el navegador | Sí: el núcleo lo sirve del disco |
| `perseo_core/caras/interfaz/index.html` | Recargar el navegador | Sí: el núcleo lo sirve del disco |

Para el **aspecto** del panel no hace falta pagar los dos minutos de
reconstrucción: la maqueta sirve las pantallas de verdad con datos de mentira
Expand All @@ -55,18 +62,23 @@ Rutas de la maqueta: `/` el panel, `#tareas` el tablero, `#habitos` y
## Antes de dar algo por bueno

```bash
python -m pytest # 724 pruebas
python -m ruff check .
cd RealTime && npx tsc --noEmit && npm test # 138 pruebas
cd RealTime/src-tauri && cargo check --locked
python commands/perseo.py comprobar
```

Las cuatro corren también en CI, en Linux y en Windows.
Eso es pytest, ruff, `tsc`, las pruebas del frontend y `cargo check`, en orden
de coste: lo que tarda segundos primero. Las cinco corren también en CI, en
Linux y en Windows. `--rapido` se salta Rust, que es la que tarda.

Está escrito en **un solo sitio** a propósito. Antes eran cuatro bloques
copiados —aquí, en el README y en el fichero del CI— y los recuentos de pruebas
que llevaban dentro ya no coincidían en ninguno. Los números que cita la
documentación salen ahora de `perseo cuentas`, y `perseo cuentas --arreglar` los
reescribe; hay una prueba que compara.

Si tocaste el comportamiento de verdad —no solo el aspecto— pasa además el
verificador que le toque: son diecisiete, están en `perseo_core/verificar_*.py`
y ninguno toca el estado real (se montan un directorio temporal y servidores de
mentira).
verificador que le toque. Están en `verificadores/`, menos el de la palabra
clave, que vive en `commands/` porque necesita micrófono. Ninguno toca el estado
real: se montan un directorio temporal y servidores de mentira.

## Trampas que cuestan una hora

Expand Down Expand Up @@ -101,6 +113,31 @@ mentira).
- **Nada de secretos en el código.** Si algo necesita una clave, se lee del
entorno o de `<datos>`, y `<datos>` está en el `.gitignore`.

## Las reglas que no dependen de que te acuerdes

Cinco cosas que antes eran costumbre y ahora las comprueba
`pruebas/test_arquitectura.py` y `pruebas/test_documentacion.py`. Si alguna se
pone roja, el arreglo **no** es tocar la prueba:

| Regla | Qué pasa si la rompes | Dónde se afloja |
|---|---|---|
| El núcleo va en capas y nadie importa hacia arriba | rojo, con el importe señalado | el orden de `CAPAS` en `commands/arquitectura.py` |
| Ningún fichero pasa de 900 líneas | rojo, con el fichero y su cuenta | pártelo; la lista de excepciones solo encoge |
| Un componente no llama al núcleo | rojo, con el componente | pídele los datos a un gancho de `lib/datos/` |
| `docs/API.md` describe las rutas que existen, y todas | rojo, con la ruta | documéntala o bórrala |
| Los recuentos que cita el README son los de verdad | rojo, con la cifra | `perseo cuentas --arreglar` |

Las excepciones vivas —dos ficheros grandes y diez componentes— tienen nombre y
apellidos en `commands/arquitectura.py`, y las dos que necesitan explicación la
tienen en [`docs/adr/`](docs/adr/). Una excepción sin porqué no vale: si nadie
sabe explicar por qué algo sigue ahí, la respuesta correcta es quitarlo.

Para ver cómo va todo de un vistazo:

```bash
python commands/perseo.py comprobar --arquitectura
```

## Lo que no hay que hacer

- Meter lógica en una cara.
Expand Down
21 changes: 13 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,24 @@ es más de lo que esperaba, así que gracias.
## Antes de abrir un PR

```bash
python -m pytest
python -m ruff check .
cd RealTime && npx tsc --noEmit && npm test
cd RealTime/src-tauri && cargo check --locked
python commands/perseo.py comprobar
```

Las cuatro tienen que estar en verde. Corren también en CI, en Linux y en
Windows, así que si fallan allí y aquí no, suele ser una ruta con `\` o un
final de línea.
Eso es pytest, ruff, el buscador de código muerto, los tipos y las pruebas del
frontend, `knip` y `cargo check`, en orden de coste. Todo tiene que estar en
verde. Corre también en CI, en Linux y en Windows, así que si falla allí y aquí
no, suele ser una ruta con `\` o un final de línea. `--rapido` se salta Rust.

Algunas de esas comprobaciones no miran lo que hace el código sino **dónde vive**:
que el núcleo siga en capas, que ningún fichero pase de novecientas líneas, que
`docs/API.md` describa las rutas que existen y que los recuentos del README sean
los de verdad. Están explicadas en [`AGENTS.md`](AGENTS.md) y las decisiones que
hay detrás, en [`docs/adr/`](docs/adr/). Si una se pone roja, el arreglo no es
tocar la prueba.

Si cambias comportamiento, añade o ajusta la prueba que lo cubre. Si cambias
algo del sistema entero —la cola, la política, un agente— pasa además su
verificador (`perseo_core/verificar_*.py`).
verificador (`verificadores/verificar_*.py`).

## Cómo se escribe aquí

Expand Down
57 changes: 39 additions & 18 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@
voice, desktop panel, phone web app, and alerts in your pocket.**

It talks to you by voice in real time, triages your inbox before you open it,
writes to your memory, delegates coding tasks to other agents — and asks for
permission before doing anything it can't undo.
writes to your memory and delegates coding tasks to other agents. It grades
everything it does by risk — in the worker, not in the prompt — and can stop
anything irreversible to wait for your yes. That stop ships switched off:
see [ADR 0005](docs/adr/0005-las-confirmaciones-estan-apagadas.md).

[![Verification](https://github.com/PersusUS/Perseo/actions/workflows/verificacion.yml/badge.svg)](https://github.com/PersusUS/Perseo/actions/workflows/verificacion.yml)
[![MIT licence](https://img.shields.io/badge/licence-MIT-black.svg)](LICENSE)
[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-black.svg)](https://www.python.org/)
[![Tauri 2](https://img.shields.io/badge/tauri-2-black.svg)](https://tauri.app/)
[![862 tests](https://img.shields.io/badge/tests-862-black.svg)](#verification)
[![919 tests](https://img.shields.io/badge/tests-919-black.svg)](#verification)

[What it is](#what-it-is) · [What it looks like](#what-it-looks-like) ·
[How it works](#how-it-works) · [Install](#install) · [Privacy](#privacy) ·
Expand Down Expand Up @@ -46,7 +48,7 @@ Raspberry Pi tomorrow without rewriting a line of interface code.
| 📬 **Mail triaged before you read it** | Every message lands in a bucket — ignore, interesting, needs action, not sure — decided by a **local** model, on your GPU |
| 🧠 **Real memory** | Markdown notes in your Obsidian vault. It searches, reads and **appends**; never overwrites, never deletes |
| 👤 **It knows who's talking** | Recognises voices and faces with local models, and learns people it hasn't met. Off by default |
| 🛑 **It asks first** | Three confirmation levels enforced in the worker, not in the prompt. Anything irreversible stops and waits for your yes |
| 🛑 **It grades risk, and knows who asked** | Four levels enforced in the worker, not in the prompt. Switched on, anything irreversible stops and waits for your yes — and an order from a guest stops even while you are right there. **They ship off** as of 2026-09-12: [ADR 0005](docs/adr/0005-las-confirmaciones-estan-apagadas.md) |
| 📱 **It follows you to your phone** | A PWA over your home VPN: chat, queue, mail and status. No build step, one single file |
| 🤖 **It delegates code** | Hands tasks to sub-agents (Claude Code or opencode) and tells you how they're going while they work |
| 🔌 **It speaks MCP** | Its own client for local and remote servers: vault, browser, Windows, triaged mail, sub-agents |
Expand Down Expand Up @@ -208,9 +210,26 @@ three levels enforced **in the worker**, before the agent runs:
| `reversible` | Append to the vault, edit code | Runs, and is logged |
| `irreversible` | Typing blind — and **anything not classified** | Stops and asks for a yes |

There is a fourth one, `critico` — deleting, touching the registry, killing
processes — that **always** asks, trust mode or not.

> **And one switch above all of it.** As of 2026-09-12
> `politica.CONFIRMACIONES` is `False`, and the right-hand column reads
> "runs" on all four rows: nothing stops, `critico` included. The table, the
> levels and their tests are all still there — the only thing that never
> happens is the stop. Why, what it costs and how to re-arm it — one line, or
> `PERSEO_CONFIRMACIONES=1` — are in [ADR 0005](docs/adr/0005-las-confirmaciones-estan-apagadas.md).

You give the yes from the web app, from the Telegram alert, or **out loud
during the call**. *Trust mode* lowers irreversible to reversible while you're
sitting there, and expires on its own.
sitting there, and expires on its own: during a call it is renewed by your
voice, so it switches itself off if you walk away.

**Who asked counts too.** Every job travels with the profile of whoever spoke —
set by the voice recognition running on your own machine — and an order from
someone who isn't you stops even with trust mode on. Your yes also covers exact
repeats of the same request for ten minutes: dictating an address is six
identical orders, and asking six times teaches you to say yes without reading.

---

Expand Down Expand Up @@ -312,8 +331,8 @@ goes in `perseo_core/datos/`, outside git, because it carries a
```

```bash
python -m perseo_core.autorizar_google # opens consent and stores the token
python -m perseo_core.google_api # checks the credentials work
python -m perseo_core.servicios.autorizar_google # opens consent and stores the token
python -m perseo_core.servicios.google_api # checks the credentials work
PERSEO_CORREO=gmail PERSEO_AGENDA=google python -m perseo_core
```

Expand Down Expand Up @@ -357,11 +376,13 @@ the tables read fine in any language.
None of this is checked by eye, and it's checked two ways.

**Unit tests** — each piece on its own, no network, no subprocesses. They tell
you *what* broke: **862** in total.
you *what* broke: **919** in total.

```bash
python -m pytest # 724, core and commands
cd RealTime && npm test # 138, the interface
python commands/perseo.py comprobar # everything, cheapest first

python -m pytest # 760, core and commands
cd RealTime && npm test # 159, the interface
cd RealTime/src-tauri && cargo check # and that the Rust compiles
```

Expand All @@ -370,13 +391,13 @@ cd RealTime/src-tauri && cargo check # and that the Rust compiles
data directory and fake servers.

```bash
python perseo_core/verificar_fase_a.py # the core: queue, restarts, SSE
python perseo_core/verificar_aprobaciones.py # the confirmation path
python perseo_core/verificar_politica.py # the levels and trust mode
python perseo_core/verificar_pc.py # injection attempts against `pc`
python perseo_core/verificar_web.py # `web`, without touching the internet
python perseo_core/verificar_biometria.py # voices and faces: learn, rename, delete
# …seventeen in total, all in perseo_core/verificar_*.py
python verificadores/verificar_fase_a.py # the core: queue, restarts, SSE
python verificadores/verificar_aprobaciones.py # the confirmation path
python verificadores/verificar_politica.py # the levels and trust mode
python verificadores/verificar_pc.py # injection attempts against `pc`
python verificadores/verificar_web.py # `web`, without touching the internet
python verificadores/verificar_biometria.py # voices and faces: learn, rename, delete
# …seventeen in total, all in verificadores/verificar_*.py
```

The first blocks run on every push
Expand Down Expand Up @@ -412,7 +433,7 @@ details are in [`docs/PRIVACIDAD.md`](docs/PRIVACIDAD.md).

Perseo works and gets used daily, but it's a personal project: built for
**one** person on **one** Windows machine, and it shows. Behind it are roughly
48,600 lines, 862 tests and 17 verifiers.
49,100 lines, 919 tests and 17 verifiers.

If you clone it and something won't start, open an
[issue](https://github.com/PersusUS/Perseo/issues) — and if you fix it, even
Expand Down
Loading
Loading