chore: release v0.3.1 — CHANGELOG + política de versiones en CLAUDE.md
- CHANGELOG.md: historial completo desde v0.1.0 hasta v0.3.1 - CLAUDE.md: sección 'Gestión de versiones' con reglas semver, tabla MAJOR/MINOR/PATCH, proceso de release copy-paste - Scope y archivos del proyecto actualizados al estado real (v0.3.1) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
141
CHANGELOG.md
Normal file
141
CHANGELOG.md
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
# Changelog — Pedrito Hechakuaa
|
||||||
|
|
||||||
|
Todos los cambios notables se documentan acá.
|
||||||
|
|
||||||
|
Formato: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||||
|
Versiones: [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.3.1] — 2026-05-26
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- **`/health/history` requiere auth** — `Authorization: Bearer <HEALTH_AUTH_TOKEN>`; sin token
|
||||||
|
devuelve 401 con header `WWW-Authenticate`. Sin auth configurada, retorna 401 igualmente.
|
||||||
|
- **chat_ids enmascarados en historial** — reemplazados por hash SHA256[:8] opaco (`usr_a3b4c5d6`);
|
||||||
|
preserva correlación de eventos sin exponer Telegram IDs (PII).
|
||||||
|
- **`/health` público mínimo** — sin auth devuelve solo `{status, timestamp}`. Con auth, respuesta
|
||||||
|
completa con métricas internas. Elimina exposición de conteo de tenants y timing del poller.
|
||||||
|
- **Env vars borradas del proceso** — después de cargar `Settings()`, se eliminan de `os.environ`:
|
||||||
|
`FERNET_KEY`, `TELEGRAM_BOT_TOKEN`, `PJ_USUARIO_ENC`, `PJ_PASSWORD_ENC`, `WEBHOOK_SECRET`,
|
||||||
|
`HEALTH_AUTH_TOKEN`, `INVITE_CODE`. Protege contra `/proc/<pid>/environ`.
|
||||||
|
- **Race condition TOCTOU en invite codes eliminada** — `SELECT + UPDATE` reemplazado por
|
||||||
|
`UPDATE WHERE codigo=? AND usado=0`; `rowcount=0` indica código no válido o ya usado.
|
||||||
|
Imposible que dos requests simultáneos consuman el mismo código.
|
||||||
|
- **Timing oracle en validación de código mitigado** — delay mínimo garantizado de 800ms en
|
||||||
|
`_paso_codigo()` independientemente del resultado; `secrets.compare_digest()` para comparación
|
||||||
|
contra código del `.env`.
|
||||||
|
- **Respuestas a usuarios no registrados reducidas al mínimo** — `_rechazar_no_registrado` ya no
|
||||||
|
revela la arquitectura de comandos; texto libre de usuarios sin registro recibe silencio total.
|
||||||
|
- **Webhook inválido devuelve 404** — en vez de 403, no confirma la existencia del endpoint.
|
||||||
|
- **Nginx rate limiting** documentado en `DEPLOY.md`: `limit_req_zone` 30r/s para webhook,
|
||||||
|
10r/min para health; headers `X-Content-Type-Options`, `X-Frame-Options`.
|
||||||
|
- **`HEALTH_AUTH_TOKEN`** agregado a `config.py` y a la guía de `.env` en `DEPLOY.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.3.0] — 2026-05-25
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Docker** — `Dockerfile` (imagen `ghcr.io/astral-sh/uv:python3.12-bookworm-slim`),
|
||||||
|
`docker-compose.yml` con `restart: unless-stopped` y healthcheck, `.dockerignore`.
|
||||||
|
- **Health check HTTP** (`health.py`) — servidor aiohttp en puerto 8080:
|
||||||
|
- `GET /health` — estado del bot (ok/degraded)
|
||||||
|
- `GET /health/history` — últimos N eventos del audit log
|
||||||
|
- Watcher loop que alerta al admin via Telegram si el poller no actualiza en N horas
|
||||||
|
- **Schema migrations** — directorio `migrations/001_initial_schema.sql`, función
|
||||||
|
`run_migrations()` en `database.py`; idempotente con `CREATE TABLE IF NOT EXISTS`.
|
||||||
|
Tabla `schema_migrations` lleva registro de versiones aplicadas.
|
||||||
|
- **Webhook opt-in** — `POST /webhook` en aiohttp valida `X-Telegram-Bot-Api-Secret-Token` con
|
||||||
|
`compare_digest` y pone el Update en `application.update_queue` de PTB. Se activa con
|
||||||
|
`WEBHOOK_URL` en `.env`; vacío = long-polling.
|
||||||
|
- `aiohttp>=3.9` como nueva dependencia.
|
||||||
|
- `WEBHOOK_URL`, `WEBHOOK_SECRET`, `HEALTH_PORT`, `HEALTH_ALERT_HOURS` en `config.py`.
|
||||||
|
- Poller notifica a health vía callback `on_poll_done(tenant_count)`.
|
||||||
|
- `revisar_ahora()` retorna `int` (cantidad de tenants revisados).
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `init_db()` reemplazado por `run_migrations()` en `main.py`; `init_db()` queda como alias
|
||||||
|
para compatibilidad con scripts.
|
||||||
|
- `main.py` arranca `HealthServer` antes del poller; elige polling vs webhook según config;
|
||||||
|
registra webhook con Telegram en modo webhook; elimina webhook al detener.
|
||||||
|
- `DEPLOY.md` actualizado con guía completa de 20 secciones: DNS Bluehost/cPanel (con mensaje
|
||||||
|
exacto en inglés para delegar), Nginx, Certbot/SSL, Docker, webhook, health check, rate limiting.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.1] — 2026-05-25
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Campos del API del PJ corregidos** — la API real usa `fechaNotificacion` (no `fecha`),
|
||||||
|
`descripcionDespacho` (no `juzgado`), `descripcionCircunscripcion` (no `circunscripcion`).
|
||||||
|
`NotificacionPendiente.__init__` ahora usa fallback `or` para ambos nombres.
|
||||||
|
- **PDF callback** — usaba `cod_notificacion_origen` como ID de actuación; corregido a
|
||||||
|
`cod_actuacion_caso` (campo `codActuacionCaso` de la API).
|
||||||
|
- **403 en `listar_notificaciones`** — manejado explícitamente como `CSJAuthError` en vez de
|
||||||
|
caer en `raise_for_status()` genérico.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.2.0] — 2026-05-25
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Multi-tenant** — `database.py` con SQLite vía aiosqlite; tabla `tenants` con credenciales
|
||||||
|
cifradas por chat_id; `list_active_tenants()`, `save_tenant()`, `delete_tenant()`.
|
||||||
|
- **Onboarding flow** (`onboarding.py`) — máquina de estados de 8 pasos:
|
||||||
|
código de invitación → consentimiento → nombre → tono → timezone → cédula → password → guardado.
|
||||||
|
- **Códigos de invitación** — tabla `invite_codes`, validación de un solo uso atómica,
|
||||||
|
fallback al código maestro del `.env`; `scripts/generar_codigo.py`.
|
||||||
|
- **Audit log** — tabla `audit_log`, función `audit.log()` fire-and-forget; cobertura de todos
|
||||||
|
los eventos de onboarding, polling, comandos y errores.
|
||||||
|
- **Rate limiting** (`utils/rate_limiter.py`) — ventana deslizante por chat_id, 20 req/60s;
|
||||||
|
reset en `/resetear`.
|
||||||
|
- **FERNET_KEY rotation** (`scripts/rotar_fernet_key.py`) — re-cifra todos los tenants de forma
|
||||||
|
atómica; hace backup de `.env` antes de modificar; aborta si algún tenant falla.
|
||||||
|
- Comando `/exp` — búsqueda de expedientes por número o carátula.
|
||||||
|
- Comando `/pdf` — descarga PDF de la actuación más reciente via `permisoFirmado`.
|
||||||
|
- Tono personalizable por abogado: formal / cotidiano / informal.
|
||||||
|
- Zona horaria configurable por abogado.
|
||||||
|
- `TELEGRAM_EXTRA_IDS` para autorizar IDs adicionales sin onboarding.
|
||||||
|
- `DEPLOY.md` — primera versión de la guía de despliegue en VPS Ubuntu con systemd.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `poller.py` itera sobre todos los tenants activos (era single-tenant).
|
||||||
|
- Snapshots por tenant: `data/snapshot_{chat_id}.json` (era `data/snapshot.json` global).
|
||||||
|
- `vault.py` usa derivación de clave Fernet por chat_id: `base64(SHA256(FERNET_KEY + str(chat_id)))`.
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- Contraseña del PJ borrada de memoria inmediatamente tras cifrar (`del password` en onboarding).
|
||||||
|
- Mensaje de Telegram con la contraseña eliminado del chat vía `delete_message` antes de procesar.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.1.0] — 2026-05-25
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Login al sistema judicial `apps.csj.gov.py`:
|
||||||
|
`GET /login` → cookie `cookiesession1` → `POST /autenticador/login` → bearer token (TTL 1h).
|
||||||
|
- Polling de `GET /Notificaciones/PorRecibir` cada N minutos en horario hábil (lun-vie, 7-18hs).
|
||||||
|
- Diff vs snapshot JSON local — solo notifica notificaciones con `codNotificacionOrigen` nuevo.
|
||||||
|
- Relogin automático transparente al expirar el token.
|
||||||
|
- Bot de Telegram con comandos: `/start`, `/notif`, `/estado`, `/ayuda`, `/salir`.
|
||||||
|
- Credenciales del PJ cifradas con Fernet en `.env` (`scripts/cifrar_credenciales.py`).
|
||||||
|
- `scrub()` en `utils/sanitize.py` — sanitiza bearer tokens, JWTs, cookies y passwords
|
||||||
|
antes de pasar cualquier dato externo a los logs.
|
||||||
|
- Logging estructurado con `structlog` (JSON en prod, ConsoleRenderer en dev vía `isatty`).
|
||||||
|
- Configuración vía `pydantic-settings` — único punto de config de la app (`config.py`).
|
||||||
|
- Stack base: `httpx`, `python-telegram-bot[ext] v21`, `aiosqlite`, `cryptography`, `structlog`,
|
||||||
|
`pydantic-settings`, `apscheduler`.
|
||||||
|
- Manejo de señales `SIGINT`/`SIGTERM` para shutdown limpio.
|
||||||
69
CLAUDE.md
69
CLAUDE.md
@@ -6,19 +6,21 @@ Pedrito Hechakuaa: un bot de Telegram que monitorea notificaciones pendientes
|
|||||||
en el sistema judicial de Paraguay (`apps.csj.gov.py`) y avisa cuando aparece
|
en el sistema judicial de Paraguay (`apps.csj.gov.py`) y avisa cuando aparece
|
||||||
algo nuevo. Uso propio de un solo abogado (single-tenant).
|
algo nuevo. Uso propio de un solo abogado (single-tenant).
|
||||||
|
|
||||||
**Scope estricto de la v0:**
|
**Versión actual: v0.3.1** — ver historial completo en `CHANGELOG.md`.
|
||||||
- Login al PJ + polling de notificaciones pendientes
|
|
||||||
- Diff vs snapshot anterior → solo notifica lo nuevo
|
|
||||||
- Telegram como canal de salida
|
|
||||||
- Correr en local o VPS con docker-compose
|
|
||||||
|
|
||||||
**Fuera del scope de v0 (no agregar sin consulta explícita):**
|
**Scope implementado:**
|
||||||
|
- Login al PJ + polling de notificaciones pendientes (multi-tenant)
|
||||||
|
- Diff vs snapshot por tenant → solo notifica lo nuevo
|
||||||
|
- Telegram como canal de salida con onboarding, tono personalizable y timezone
|
||||||
|
- Búsqueda de expedientes y descarga de PDFs
|
||||||
|
- Infraestructura: Docker, health check HTTP, schema migrations, webhook opt-in
|
||||||
|
|
||||||
|
**Fuera del scope actual (no agregar sin consulta explícita):**
|
||||||
- Motor de reglas de plazos
|
- Motor de reglas de plazos
|
||||||
- LLM / conversación libre
|
- LLM / conversación libre
|
||||||
- Multi-tenant
|
|
||||||
- Web de onboarding
|
- Web de onboarding
|
||||||
- Actuaciones de expedientes (es v1)
|
- Actuaciones de expedientes (es v1)
|
||||||
- Acuse de notificaciones (nunca en MVP)
|
- Acuse de notificaciones (nunca — efecto procesal irreversible)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -253,9 +255,54 @@ Ante cambio de schema del PJ: actualizar `csj_client.py` y documentar en
|
|||||||
| Archivo | Propósito |
|
| Archivo | Propósito |
|
||||||
|---------|-----------|
|
|---------|-----------|
|
||||||
| `CLAUDE.md` | Este archivo — reglas para Claude Code |
|
| `CLAUDE.md` | Este archivo — reglas para Claude Code |
|
||||||
| `OBSERVATIONS.md` | Hallazgos durante el desarrollo (crear si no existe) |
|
| `CHANGELOG.md` | Historial de versiones — actualizar antes de cada release |
|
||||||
| `TECH_DEBT.md` | Deuda técnica anotada (crear si no existe) |
|
| `DEPLOY.md` | Guía de despliegue en VPS: Docker, Nginx, SSL, DNS |
|
||||||
| `data/snapshot.json` | Estado persistido del poller |
|
| `OBSERVATIONS.md` | Hallazgos durante el desarrollo (schema real del PJ, etc.) |
|
||||||
|
| `TECH_DEBT.md` | Deuda técnica anotada |
|
||||||
|
| `migrations/` | Archivos `.sql` de schema — nunca editar los ya aplicados |
|
||||||
|
| `data/pasante.db` | Base de datos SQLite (tenants, audit_log, invite_codes) |
|
||||||
|
| `data/snapshot_*.json` | Estado del diff de notificaciones por tenant |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Gestión de versiones
|
||||||
|
|
||||||
|
### Esquema: Semantic Versioning (semver 2.0)
|
||||||
|
|
||||||
|
`MAJOR.MINOR.PATCH` — tags git con prefijo `v` (ej: `v0.3.1`).
|
||||||
|
|
||||||
|
| Número | Incrementar cuando... | Ejemplos |
|
||||||
|
|--------|----------------------|---------|
|
||||||
|
| `MAJOR` | Cambio incompatible con versiones anteriores: migración de DB no retrocompatible, cambio de protocolo del PJ, rediseño de arquitectura | `v1.0.0` (migración a Postgres) |
|
||||||
|
| `MINOR` | Feature nuevo sin romper lo existente: comando nuevo, integración nueva, mejora visible al usuario | `/recordatorio`, modo silencioso |
|
||||||
|
| `PATCH` | Bug fix, security patch, mejora interna, documentación operacional | corrección de campo del PJ, hardening |
|
||||||
|
|
||||||
|
### Reglas que Claude Code sigue siempre
|
||||||
|
|
||||||
|
1. **Cada release requiere entrada en `CHANGELOG.md`** con fecha y secciones
|
||||||
|
`Added / Changed / Fixed / Security` según corresponda.
|
||||||
|
2. **El tag se crea después del commit de changelog**, no antes.
|
||||||
|
3. **`[Unreleased]`** acumula todos los cambios hasta que Marcos decide liberar.
|
||||||
|
4. **Claude propone el número de versión** basándose en las reglas de arriba;
|
||||||
|
Marcos lo aprueba antes de tagear.
|
||||||
|
5. **`git tag` y `git push --tags` los ejecuta Claude solo cuando Marcos lo confirma** —
|
||||||
|
son acciones externas (modifican el remoto) que requieren aprobación explícita.
|
||||||
|
6. **Tags retroactivos** son válidos para dar historia a commits anteriores;
|
||||||
|
se crean con `git tag -a vX.Y.Z <hash>`.
|
||||||
|
|
||||||
|
### Proceso de release (copy-paste)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. CHANGELOG.md ya actualizado (mover [Unreleased] → [X.Y.Z] con fecha)
|
||||||
|
git add CHANGELOG.md
|
||||||
|
git commit -m "chore: release vX.Y.Z"
|
||||||
|
|
||||||
|
# 2. Tag anotado
|
||||||
|
git tag -a vX.Y.Z -m "vX.Y.Z — resumen en una línea"
|
||||||
|
|
||||||
|
# 3. Push incluyendo el tag
|
||||||
|
git push origin main --tags
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user