# Bot de incidencias Atiende con Claude las incidencias de `CriptoMart/addons-cm` en Forgejo: responde las preguntas funcionales o de configuración y abre un PR cuando hace falta código. - [bot.py](bot.py): orquestador. Habla con la API de Forgejo, prepara un clon limpio, lanza `claude -p` y publica el resultado. - [prompt.md](prompt.md): instrucciones del bot (criterios y tono), comunes a todos los repos. - [../../.gitea/issue-bot.md](../../.gitea/issue-bot.md): qué es este repo (compartido entre clientes) y sus convenciones. Se añade a las instrucciones; es lo único que cambia de un repo a otro. - [../../.gitea/workflows/issue-bot.yml](../../.gitea/workflows/issue-bot.yml): disparo por Actions al abrir o comentar una incidencia. ## Qué hace con cada incidencia Actúa cuando el último mensaje de una incidencia abierta es de una persona: | Decisión | Acción | | ------------ | ------------------------------------------------------------ | | `answer` | Comenta la respuesta | | `needs_info` | Comenta pidiendo los datos que faltan | | `code` | Commit en `bot/issue-N`, push, PR contra `18.0` y comentario | | `none` | Nada | No actúa sobre PR, incidencias cerradas ni las que llevan la etiqueta `no-bot`, y deja de responder tras `BOT_MAX_REPLIES` respuestas en la misma incidencia. Nunca fusiona ni cierra nada. Claude trabaja sin el token de Forgejo y sin herramientas web: solo lee el repo y las fuentes de Odoo, edita ficheros y pasa pre-commit. Un cambio que toque `.gitea/`, `tools/ci/`, `tools/issue_bot/` o `tools/pr_bot/` se rechaza. Los PR que abre los revisa después el [bot de revisión](../pr_bot/README.md), que comparte con este la imagen del runner, la cuenta y los secretos. ## Puesta en marcha En Forgejo, una vez: 1. Dar a la cuenta del bot (`claudia_eco`) permiso de escritura en código, incidencias y PR: como colaborador con rol de escritura o desde un equipo propio, no desde el equipo de las personas del cliente. 2. Proteger la rama `18.0` exigiendo aprobación, para que solo se fusione tras revisión. 3. Crear la etiqueta `no-bot`. ### Con Actions Los jobs corren en la máquina del runner de ecocentral, con su imagen (Claude Code, pre-commit, dependencias de Odoo 18 y Chromium) y sus montajes: las fuentes de Odoo/OCA en `/odoo-src` y la caché `issue-bot-cache`. Cómo se monta y se mantiene (`Dockerfile`, `update.sh`, `sources.txt`, `runner-config.yml`, cron semanal) está documentado en el `tools/issue_bot/README.md` de ecocentral, y sus ficheros viven allí: aquí no hay copia, para no tener dos listas de fuentes para una misma máquina. Un runner de Forgejo solo atiende el ámbito en el que se registra, y este repo (organización `CriptoMart`) y el de ecocentral (`Ecocentral`) están en organizaciones distintas. Por eso en esa máquina hay un segundo registro, hecho en la organización `CriptoMart`, que comparte imagen, montajes y caché con el de ecocentral: 1. Con el usuario `runner`, una carpeta propia para el segundo daemon, con una copia del `runner-config.yml` de ecocentral (misma etiqueta, `network: host`, montajes, `valid_volumes`, `force_pull: false`, `capacity: 1`): ```bash mkdir ~/runner-criptomart && cd ~/runner-criptomart cp ~//runner-config.yml . forgejo-runner register --config runner-config.yml \ --instance http://localhost:3000 --token \ --name criptomart --labels issue-bot:docker://eco-issue-bot:latest ``` El token sale de los ajustes de Actions de la organización `CriptoMart` (Ajustes → Actions → Runners → Crear nuevo runner). 2. Un servicio aparte para este daemon (copia de la unidad de systemd del de ecocentral con `WorkingDirectory=` en `~/runner-criptomart` y `forgejo-runner daemon --config runner-config.yml`), y arrancarlo. 3. Activar Actions en el repo. 4. Secretos del repo: `BOT_FORGEJO_TOKEN` (token de `claudia_eco`) y uno de `ANTHROPIC_API_KEY` o `CLAUDE_CODE_OAUTH_TOKEN` (lo genera `claude setup-token`). Son secretos por repo: no se heredan de ecocentral. Cada daemon tiene su `capacity: 1`, así que un job de cada repo puede correr a la vez. Por eso el PostgreSQL del job `test` de aquí escucha en el `54330` y no en el `54329` de ecocentral. Los dos comparten la caché `issue-bot-cache` (entornos de pre-commit y los `.pot` del bot de revisión, que van por commit). ### Tests del CI En cada PR contra `18.0`, y en cada push a esa rama, el job `test` de [ci.yml](../../.gitea/workflows/ci.yml) lanza [../ci/run_tests.py](../ci/run_tests.py) con el esquema del CI de la OCA: crea una base de datos con las dependencias de los addons y después instala los addons con `--test-enable`, de modo que solo corren los tests del repo. PostgreSQL es un servicio del job, en el puerto 54330 de `127.0.0.1`. Odoo arranca desde las fuentes montadas en `/odoo-src/18.0`, así que: - Todo repo del que dependa un addon de aquí tiene que estar en la parte `18.0` del `sources.txt` del runner (en ecocentral). Si falta, el job falla diciendo qué módulo no encuentra, en lugar de dejar el addon sin probar. - Una dependencia Python nueva (`external_dependencies`) pide añadirla al `Dockerfile` del runner (en ecocentral). - Ambas cosas llegan al runner al ejecutar `update.sh`, no al fusionar. `/odoo-src/18.0` trae también un clon de este repo, porque ecocentral depende de él. El job lo salta (`ODOO_SRC_SKIP: addons-cm` en `ci.yml`) y prueba el checkout del PR. Los tests corren contra la OCB y la OCA de la última actualización semanal, no contra commits fijados. ### Sin Actions (poll) En una máquina con `claude` autenticado, `pre-commit` y el token en `~/.config/gitea-addons-cm.token`: ```bash */5 * * * * flock -n /tmp/addons-cm-issue-bot.lock python3 /ruta/al/repo/tools/issue_bot/bot.py poll ``` El primer `poll` solo anota la hora: el histórico no se atiende. Para pasar el bot por incidencias ya abiertas: ```bash python3 tools/issue_bot/bot.py run 12 15 --dry-run # enseña lo que publicaría python3 tools/issue_bot/bot.py run 12 15 ``` ## Configuración | Variable | Por defecto | | ------------------------------------------- | --------------------------------------------- | | `BOT_FORGEJO_URL` | `https://git.criptomart.net` | | `BOT_FORGEJO_REPO` | `CriptoMart/addons-cm` | | `BOT_FORGEJO_TOKEN` / `..._TOKEN_FILE` | `~/.config/gitea-addons-cm.token` | | `BOT_REPO_DIR` (clon propio del bot) | `~/.cache/eco-issue-bot/addons-cm` | | `BOT_BASE_BRANCH` (`18.0` en los workflows) | rama por defecto del repo (`16.0`) | | `BOT_ODOO_SRC` (carpetas separadas por `:`) | `/media/snt/data/odoo` | | `BOT_MODEL` (pide Claude Code ≥ 2.1.280) | `claude-opus-5-5` | | `BOT_MAX_BUDGET_USD` (por incidencia) | `5` | | `BOT_TIMEOUT` (segundos) | `2400` | | `BOT_MAX_REPLIES` | `8` | | `BOT_STATE_FILE` | `~/.local/state/eco-issue-bot/addons-cm/since` | `bot.py`, `prompt.md` y los ficheros de [../pr_bot/](../pr_bot/) son copia de los de ecocentral; solo cambian los valores por defecto de esta tabla, el de `BOT_I18N_LANGS`, las referencias al `sources.txt` del runner y el mensaje del commit de traducciones del bot de revisión (`[I18N] …`). Un arreglo en uno de los dos repos se copia tal cual al otro.