You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
taskbot/docs/2025-11-01-plan-refactor-te...

778 lines
40 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Plan de Refactor Técnico — 2025-11-01
Este documento describe un plan de refactor seguro, incremental y medible para estabilizar tipado, reducir duplicación, mejorar estructura y preparar la base para evolución a largo plazo. Se prioriza no añadir dependencias externas y mantener el comportamiento actual cubierto por tests.
## Objetivos
- Reducir duplicación (fechas/horas, validaciones, helpers de tests, cripto).
- Endurecer tipado y contratos en TypeScript (mientras mantenemos compatibilidad).
- Clarificar capas (utils compartidos, servicios, API web, UI SvelteKit, tests).
- Estabilizar convenciones (UTC, formato SQLite, alias/imports, nombres).
- Mantener comportamiento: cambios sin funcionalidad nueva, validados por tests.
## Hallazgos clave (del diagnóstico inicial)
- Archivos grandes:
- apps/web/src/lib/ui/data/TaskItem.svelte ~911 LOC (punto caliente claro).
- Varias páginas Svelte de 250335 LOC (AppShell, /app, /app/groups, etc.).
- Duplicación evidente:
- Formateo SQL-UTC con `toISOString().replace('T',' ').replace('Z','')` aparece 35+ veces en app y tests.
- `normalizeTime` duplicado en API y page.server.
- `ymdUTC` y `addMonthsUTC` definidas 3 veces en rutas ICS y también en tests.
- `sha256Hex` reimplementado en múltiples tests pese a existir en `src/utils/crypto.ts`.
- SimulatedResponseQueue replicado en varios tests.
- Tipado/infra:
- tsc falla en apps/web por `$lib` y `./$types` (faltan tipos generados SvelteKit) y por DOM (navigator/document) y Bun types (HeadersInit/fetch).
- apps/web/vite.config.ts incompatibilidad de tipos (UserConfig).
- Errores “unknown” por resultados de SQLite (faltan tipos/casts).
- Inconsistencias null vs undefined (p. ej., groupColor, whatsapp utils).
- Cobertura mejorable:
- webhook-manager (~32%), maintenance (~17%), contacts (~26%), onboarding (~30%), response-queue (~50%).
- Organización:
- Inyección de DB mediante propiedades estáticas dispersas; funcional pero frágil. Falta un “locator” central.
## Fase 1 — Diagnóstico asistido (Completada)
Comandos ejecutados con éxito el 2025-11-01; a continuación se conservan para referencia y repetibilidad:
```bash
git ls-files '*.ts' '*.tsx' '*.svelte' | xargs wc -l | sort -nr | head -n 30
```
```bash
git grep -n "toISOString().replace('T', ' ').replace('Z', '')"
```
```bash
git grep -n "normalizeTime(" && git grep -n "ymdUTC(" && git grep -n "addMonthsUTC(" && git grep -n "isValidYmd("
```
```bash
git grep -n "sha256Hex(" && git grep -n "SimulatedResponseQueue"
```
```bash
bunx tsc --noEmit --pretty false
```
```bash
bun test --coverage
```
Resultados esperados después del refactor: disminución drástica de duplicados, tsc limpio por paquete, y cobertura estable o al alza.
## Progreso
- 2025-11-01:
- Documento creado y versionado (commit a104b69).
- Diagnóstico ejecutado (wc -l, git grep, tsc, coverage) y hallazgos consolidados en este documento.
- Pendiente iniciar Lote 0 (Infra de typecheck): a la espera de revisar tsconfig raíz, apps/web/tsconfig y apps/web/vite.config.
- 2025-11-02:
- Lote 1 completado: util canónica de fechas/UTC creada (src/utils/datetime.ts) y wrapper web (apps/web/src/lib/server/datetime.ts); migración de rutas ICS y reemplazos en producción (hooks, login, complete/uncomplete, calendar tokens, dev-seed, response-queue, maintenance, group-sync, migrator).
- Commits relevantes: f4f7d95, 882f5c9, c1f12ff, df27161, a0f35b8.
- Tests verdes. Cobertura actual: 85.91% funciones / 82.12% líneas.
- 2025-11-07:
- Lote 0 completado: scripts `typecheck:core` y `typecheck:web` configurados y verificados. Se utiliza `tsconfig.core.json` para aislar el typecheck del core con reglas laxas, mientras que la web usa su propia configuración de SvelteKit. Los shims en `src/types/shims.d.ts` resuelven conflictos de tipos entre Bun y el DOM.
- Verificación exitosa: `bun run typecheck:core` y `bun run typecheck:web` se ejecutan sin errores.
- 2025-11-09:
- Lote 2 completado: centralización de helpers de tests (sha256Hex, toIsoSql), unificación de SimulatedResponseQueue y helpers ICS (ymdUTC, addDays) en tests.
- Commits relevantes: 77e318e, 1ad36ee, y este commit.
- 2025-11-10:
- Lote 3 completado: endurecimiento de tipos (noImplicitAny, exactOptionalPropertyTypes, strictNullChecks), centralización de normalizeTime y tipado ligero de SQLite en servicios; inclusión de servicios en typecheck; limpieza de as any en puntos clave.
- Verificación: typecheck core/web limpios y tests verdes.
- Lote 4 completado: ICS central y rutas homogéneas; rate limiting por token; títulos “Wtask.org Tareas Personal/Grupo/Agregado” y PRODID “-/Wtask.org//ICS 1.0//ES”; eventos de día completo; ETag/304 y Cache-Control; actualización de last_used_at solo en 200.
- Commit relevante: 234053c.
- Lote 5 completado: división de TaskItem en subcomponentes (SVGs a iconos, TaskDueBadge, TaskAssignees, TaskCompleteButton, TaskActions, TaskText, TaskMeta); sin cambios funcionales; tests verdes y typecheck web limpio.
- Lote 5.5-a completado: ResponseQueue extraído (EvolutionClient, limpieza modular, parseo de metadata); sin cambios funcionales; commits: 1b7420e, 2032712.
- Lote 5.5-c completado: TaskService extraído (display_code, reacción al completar, mapeadores); sin cambios funcionales; LOC actual en src/tasks/service.ts ≈ 621; commits: e3ec820, a72184f.
- Lote 5.5-b completado: GroupSyncService modularizado (api.ts, repo.ts, cache.ts, reconcile.ts) y desacople de Onboarding A3 (publishGroupCoveragePrompt); umbral aplicado; tests y typecheck limpios; commits: 1b0d2ec, 0ce3ecb, 2f24806.
- Lote 5.5-d completado: WebhookServer modularizado (/metrics, /health y bootstrap a src/http; handleMessageUpsert extraído a src/http/webhook-handler.ts); sin cambios funcionales; tests verdes; commits: 46bec52, 7189756, e430fc1.
- Lote 6.0-6.2 completados: DB Locator mínimo, conexión en bootstrap con setDb y ruta única de DB (centralización y reexport en web); sin cambios funcionales; tests y typecheck limpios; commits: 9222242, 6196dba, 2669d42.
- Lote 6.3 completado: adopción piloto con fallback en ResponseQueue y TaskService; añadido smoke test de fallback (tests/unit/locator.fallback.test.ts); tests y typecheck limpios; commit relevante: 77ad9d7.
- Lote 6.4 completado: adopción progresiva de servicios al locator (fallback parámetro → .dbInstance → getDb()) en CommandService, RemindersService, MaintenanceService, AdminService y fachada de GroupSync; tests y typecheck limpios; commits: cd83455, f786ba8.
- Lote 6.5 completado: limpieza de .dbInstance, añadido resetDb/clearDb en locator, migración de tests al locator; nota: ~8 tests frágiles en paralelo; en CI ejecutar la suite en un solo worker.
- Estado: Lote 6 completado.
## Estado actual (2025-11-10)
- Fase 1 — Diagnóstico asistido: Completada.
- Fase 2 — Plan de refactor por lotes: En curso.
- Lote 0 — Infra de typecheck: Completado.
- Lote 1 — Utilidades de fecha/hora y validaciones: Completado.
- Lote 2 — Helpers de test y cripto: Completado.
- Lote 3 — Tipos y endurecimiento suave: Completado.
- Lote 4 — ICS central y rutas homogéneas: Completado.
- Lote 5 — Svelte: dividir componentes grandes: Completado.
- Lote 5.5 — Refactor de servicios grandes (god classes): Completado.
- Lote 6 — DB Locator / DI ligera: Completado (PRs 6.06.5).
- Lote 7 — Cobertura en módulos flojos: Pendiente.
## Fase 2 — Plan de refactor por lotes (PRs pequeñas y seguras)
Cada lote incluye objetivo, cambios, métricas y comprobaciones. Mantener tests verdes en cada paso.
### Lote 0 — Infra de typecheck (preparación) - Completado
- Objetivo:
- Separar el chequeo de tipos por paquete (core vs web) para aislar errores de SvelteKit/Bun/DOM.
- Cambios:
- Scripts: `typecheck:core` (tsc -p tsconfig.core.json), `typecheck:web` (apps/web: svelte-kit sync + tsc).
- En raíz, excluir apps/web del typecheck general si hace falta hasta que esté sincronizado.
- Ajustar lib/types: en core, definir tipos mínimos para HeadersInit/fetch si no se quiere añadir bun-types; en web, asegurar DOM y tipos SvelteKit (via `svelte-kit sync`).
- Revisar apps/web/vite.config.ts para que encaje con tipos `UserConfig`.
- Métricas:
- `bunx tsc --noEmit` limpio por paquete.
- Desaparecen errores `$lib` y `./$types` tras `svelte-kit sync`.
- Comprobaciones:
- `cd apps/web && bunx svelte-kit sync && bunx tsc --noEmit`.
### Lote 1 — Utilidades de fecha/hora y validaciones - Completado
- Objetivo:
- Centralizar y unificar: SQL-UTC, validadores YMD y HH:mm, utilidades ICS (ymdUTC, addMonthsUTC).
- Cambios:
- Crear helpers canónicos (por ejemplo, `toIsoSqlUTC`, `isValidYmd`, `normalizeTime`, `ymdUTC`, `addMonthsUTC`) en un módulo compartido (core o shared) y exportarlos también para web si aplica.
- Reemplazar todas las ocurrencias del patrón `toISOString().replace(...)`.
- Unificar las 3 rutas ICS para usar las mismas utilidades.
- Métricas:
- `git grep "toISOString().replace('T', ' ').replace('Z', '')"` → 1 referencia (en el helper) o 0 si se encapsula internamente.
- `git grep "ymdUTC("` y `"addMonthsUTC("` solo en helpers + imports.
- Comprobaciones:
- Tests ICS y API siguen verdes.
### Lote 2 — Helpers de test y cripto - Completado
- Objetivo:
- Eliminar duplicación en tests.
- Cambios:
- Reemplazar implementaciones locales de `sha256Hex` por import desde `src/utils/crypto`.
- Centralizar SimulatedResponseQueue en `tests/helpers/queue.ts` y reusarlo.
- Unificar helpers de fechas en tests (`toIsoSql`, `ymdUTC`, `addDays`) en `tests/helpers/dates.ts`.
- Métricas:
- `git grep -n "async function sha256Hex"` en tests → 0.
- `git grep -n "SimulatedResponseQueue"` → 1 implementación única.
- Comprobaciones:
- `bun test --coverage` estable o mejor.
### Lote 3 — Tipos y endurecimiento suave - Completado
- Objetivo:
- Bajar ruido de TS con cambios mínimos, sin funcionalidad nueva.
- Cambios:
- Resolver `undefined` vs `null` en utils como groupColor/whatsapp.
- Corregir `HeadersInit`/`fetch` en servicios (group-sync, webhook-manager, contacts) para encajar con tipos.
- Añadir refinamientos y cheques donde TS marca “posiblemente undefined”.
- Tipar resultados de SQLite `.get()`/`.all()` en tests y servicios.
- Métricas:
- `bunx tsc --noEmit` limpio en core.
- Comprobaciones:
- Tests verdes.
### Lote 4 — ICS central y rutas homogéneas - Completado
- Objetivo:
- Consolidar construcción de ICS (escape, folding, etag) en un módulo central.
- Cambios:
- Mover `ymdUTC`/`addMonthsUTC` al módulo `apps/web/src/lib/server/ics.ts` y usarlos desde aggregate/group/personal.
- Mantener helpers de escape/folding/etag en el mismo módulo.
- Métricas:
- `git grep -n "function ymdUTC"` y `"function addMonthsUTC"` solo en ics.ts.
- Comprobaciones:
- Tests ICS (`tests/web/ics.*`) verdes.
### Lote 5 — Svelte: dividir componentes grandes
- Objetivo:
- Reducir complejidad de UI; mejorar mantenibilidad.
- Cambios:
- Dividir `TaskItem.svelte` (~911 LOC) en subcomponentes: Header/Status, Assignees, DueBadge, Actions, Metadata, etc.
- Revisar `AppShell` y `/app/+page` para separar lógica de datos y presentación (stores y load).
- Métricas:
- `TaskItem.svelte` < 300 LOC.
- Comprobaciones:
- Pruebas E2E web verdes.
### Lote 5.5 — Refactor de servicios grandes (god classes)
- Objetivo general:
- Reducir acoplamiento y responsabilidades múltiples en servicios grandes para mejorar testabilidad, DX y preparar Lote 6 (locator/DI) sin cambios funcionales.
- Mantener APIs públicas y rutas de import estables, delegando a nuevos módulos internos.
- Alcance y orden de PRs:
1) PR 5.5-a ResponseQueue (673 LOC)
- Motivos: mezcla de repositorio (SQLite), backoff, envío HTTP (texto/reacciones), idempotencia, limpieza/retención, métricas y lógica de onboarding.
- Cambios clave:
- Extraer cliente Evolution (HTTP):
- Nuevo módulo: `src/clients/evolution.ts` con `buildHeaders()`, `sendText(payload)`, `sendReaction(payload)` y manejo de errores/logging.
- Tipar metadata y parsers:
- Tipo `QueueMetadata = { kind: 'onboarding' | 'reaction'; ... }`, helpers `parseQueueMetadata()`, `isReactionJob()`.
- Aislar limpieza/retención:
- Nuevo módulo: `src/services/queue/cleanup.ts` con `runCleanupOnce()`, `startCleanupScheduler()`, `stopCleanupScheduler()`.
- ResponseQueue queda como orquestador (claim enviar marcar), delegando a EvolutionClient y Cleanup.
- Archivos a consultar:
- `src/services/response-queue.ts` (origen), `src/utils/datetime.ts`, `src/services/metrics.ts`, `src/services/identity.ts`, `src/utils/whatsapp.ts`, `src/db/migrations/index.ts` (esquema `response_queue`), tests `tests/unit/services/response-queue.test.ts`.
- Métricas de aceptación:
- `src/services/response-queue.ts` < 350 LOC.
- No variación en defaults (`MAX_ATTEMPTS`, `REACTIONS_MAX_ATTEMPTS`, backoff).
- Idempotencia de reacciones (24h) intacta.
- Comprobaciones y comandos:
```bash
bunx tsc -p tsconfig.core.json --noEmit --pretty false
bun test --coverage
git grep -n "sendReaction\\|sendText" src
```
2) PR 5.5-c TaskService (Completado; ~621 LOC)
- Motivos: mezcla de creación (códigos display), queries de listado/contadores, reglas de negocio (claim/unassign), y reacción al completar.
- Cambios clave:
- Extraer generación de display_code:
- Nuevo: `src/tasks/display-code.ts` con `pickNextDisplayCode(db)`.
- Extraer reacción al completar:
- Nuevo: `src/tasks/complete-reaction.ts` con `enqueueCompletionReactionIfEligible(db, taskId)`.
- Extraer mapeos a DTO:
- Nuevo: `src/tasks/mappers.ts` con `mapRowToTask()`, `mapRowsToListItem()`.
- `TaskService.createTask/completeTask/list*` delegan en estos helpers.
- Archivos a consultar:
- `src/tasks/service.ts` (origen), `src/services/allowed-groups.ts`, `src/services/response-queue.ts` (reacción), `src/utils/whatsapp.ts`, `src/db/migrations/index.ts`, tests `tests/unit/tasks/*.test.ts`, `tests/unit/services/command.*.test.ts`.
- Métricas de aceptación:
- `src/tasks/service.ts` < 500 LOC.
- TTL y gating de reacciones sin cambios de comportamiento.
- Comprobaciones y comandos:
```bash
bunx tsc -p tsconfig.core.json --noEmit --pretty false
bun test --coverage
git grep -n "display_code" src
```
3) PR 5.5-b GroupSyncService (1310 LOC) Completado
- Motivos: agrupa API (Evolution), parseos, upsert/caché, scheduling, reconciliación de miembros, allowed-groups y publicación de onboarding.
- Cambios clave:
- Extraer acceso Evolution API:
- Nuevo: `src/services/group-sync/api.ts` con `fetchGroups()`, `fetchMembers(groupId)` y parseos robustos.
- Extraer repositorio/caché:
- Nuevo: `src/services/group-sync/repo.ts` con `upsertGroups()`, `ensureGroupExists()`, `getActiveCount()`, `cacheActiveGroups()`, `listActiveMemberIds()`, getters/setters `onboarding_prompted_at`.
- Nuevo: `src/services/group-sync/cache.ts` para la `Map<string,string>` y helpers.
- Extraer reconciliación:
- Nuevo: `src/services/group-sync/reconcile.ts` con `reconcileGroupMembers(db, groupId, snapshot, nowIso?)`.
- Mover publicación de mensajes de cobertura de alias a OnboardingService:
- Añadir método en `src/services/onboarding.ts`: `publishGroupCoveragePrompt(db, groupId, ratio, options)`.
- `GroupSyncService` queda como fachada (APIs públicas y schedulers), delegando internamente.
- Archivos a consultar:
- `src/services/group-sync.ts` (origen), `src/services/allowed-groups.ts`, `src/services/identity.ts`, `src/services/metrics.ts`, `src/utils/datetime.ts`, `src/db/migrations/index.ts`, tests `tests/unit/services/group-sync.*.test.ts`.
- Métricas de aceptación:
- `src/services/group-sync.ts` 600700 LOC (fase 1), sin cambios funcionales.
- Caché y métricas sin variaciones de semántica.
- Comprobaciones y comandos:
```bash
bunx tsc -p tsconfig.core.json --noEmit --pretty false
bun test --coverage
git grep -n "fetchAllGroups\\|participants" src/services/group-sync*
```
4) PR 5.5-d WebhookServer (665 LOC)
- Motivos: `src/server.ts` mezcla handlers HTTP (/metrics, /health), bootstrap y manejo del webhook (message upsert).
- Cambios clave:
- Controladores HTTP dedicados:
- Nuevo: `src/http/metrics.ts` (render y headers).
- Nuevo: `src/http/health.ts` (cálculo payload; usa `GroupSyncService.getSecondsUntilNextGroupSync()`).
- Nuevo: `src/http/webhook-handler.ts` con `handleMessageUpsert(data)`.
- Nuevo: `src/http/bootstrap.ts` con `startServices()` (webhook-manager, schedulers, queues, maintenance).
- `src/server.ts` queda como wire-up (validateEnv migrate Bun.serve) y reexporta fachada `WebhookServer` para no romper tests.
- Archivos a consultar:
- `src/server.ts` (origen), `src/services/webhook-manager.ts`, `src/services/response-queue.ts`, `src/services/maintenance.ts`, `src/services/reminders.ts`, `src/services/group-sync.ts`, `src/services/admin.ts`, `src/services/allowed-groups.ts`, `src/services/contacts.ts`, `src/services/command.ts`.
- Métricas de aceptación:
- `src/server.ts` < 350 LOC.
- Rutas/contratos HTTP y logs sin cambios de comportamiento.
- Comprobaciones y comandos:
```bash
bunx tsc -p tsconfig.core.json --noEmit --pretty false
bun test --coverage
git grep -n "handleRequest(\\|handleMessageUpsert" src
```
- Métricas de aceptación (global Lote 5.5):
- Tests verdes y sin cambios funcionales observables.
- Reducción de LOC por fichero objetivo:
- response-queue.ts < 350, tasks/service.ts < 500, group-sync.ts 600700, server.ts < 350.
- `bunx tsc --noEmit` limpio en core.
- Cobertura igual o superior en piezas extraídas (api/reconcile/cleanup/complete-reaction).
- Riesgos y mitigación:
- Dividir módulos puede introducir imports cíclicos:
- Mitigación: módulos nuevos sin importar servicios de alto nivel; inyectar `db` como parámetro; reexportar desde fachada.
- Cambios en logging o tiempos:
- Mitigación: mantener mensajes/claves existentes; conservar defaults; validar en tests de integración.
### Lote 6 — DB Locator / DI ligera
- Objetivo:
- Simplificar inyección de DB en runtime y tests.
- Cambios:
- Crear un dbLocator (getDb/setDb/withDb) central y migrar servicios a usarlo progresivamente, manteniendo compatibilidad con propiedades estáticas mientras dura la transición.
- Unificar `resolveDbAbsolutePath` entre core y web (una sola fuente, con reexport en web para no romper imports).
- Métricas:
- Sin cambios de comportamiento; tests de servicios verdes.
- PRs propuestos y archivos a tocar:
PR 6.0 Locator mínimo (infraestructura, sin usos en servicios) Completado
- Nuevos archivos:
- src/db/locator.ts export { setDb, getDb, withDb }.
- tests/unit/db/locator.test.ts pruebas básicas del locator.
- Cambios en docs:
- Este documento (apartado de Lote 6).
PR 6.1 Conexión en bootstrap (setDb al arrancar) Completado
- Archivos a modificar:
- src/http/bootstrap.ts importar setDb desde src/db/locator y llamarlo en el arranque (cuando se abra la DB).
- src/server.ts si es quien abre la DB, llamar setDb(db) justo después de crearla (o delegar en bootstrap).
- Servicios: sin cambios (compatibilidad total con dbInstance estático).
PR 6.2 Ruta única de DB (centralizar resolveDbAbsolutePath) Completado
- Nuevos archivos:
- src/env/db-path.ts export function resolveDbAbsolutePath(filename = 'tasks.db'): string.
- Archivos a modificar:
- apps/web/src/lib/server/env.ts reexportar desde src/env/db-path.ts para mantener compatibilidad (sin cambios de API pública).
- src/server.ts o src/http/bootstrap.ts actualizar imports a src/env/db-path.
- Comprobaciones esperadas:
- bunx tsc -p tsconfig.core.json --noEmit
- bun test --coverage
PR 6.3 Adopción piloto con fallback (2 servicios) Completado
- Archivos a modificar:
- src/services/response-queue.ts usar (ResponseQueue as any).dbInstance ?? getDb().
- src/tasks/service.ts usar (TaskService as any).dbInstance ?? getDb().
- Tests a ajustar (si aplica):
- tests/unit/services/response-queue.test.ts asegurar setDb(memdb) en beforeAll cuando no se inyecte dbInstance.
- tests/unit/tasks/*.test.ts idem.
- Comprobaciones esperadas:
- bunx tsc -p tsconfig.core.json --noEmit
- bun test --coverage
PR 6.4 Adopción progresiva (resto de servicios principales) Completado
- Archivos a modificar (por tandas pequeñas):
- src/services/group-sync/*.ts (api.ts, repo.ts, cache.ts, reconcile.ts) fallback a getDb().
- src/services/command.ts, src/services/reminders.ts, src/services/maintenance.ts, src/services/admin.ts, src/services/contacts.ts, src/services/webhook-manager.ts fallback a getDb().
- Tests a ajustar según servicio:
- tests/unit/services/**/*.test.ts añadir setDb(memdb) donde no se use dbInstance explícita.
- Comprobaciones esperadas:
- bunx tsc -p tsconfig.core.json --noEmit
- bun test --coverage
PR 6.5 Limpieza final Completado
- Hecho:
- Eliminadas propiedades estáticas dbInstance de servicios migrados a locator.
- Añadidos resetDb/clearDb en src/db/locator.ts para tests.
- Migrados tests a setDb(...) y resetDb()/clearDb(); se observa flakiness en ejecución paralela en ~8 tests. Recomendación: ejecutar la suite en un solo worker en CI.
### Lote 7 — Cobertura en módulos flojos
- Objetivo:
- Aumentar cobertura en módulos con lógica y baja cobertura (webhook-manager, maintenance, contacts, onboarding, response-queue).
- Cambios:
- Añadir pruebas de ramas de error y caminos menos transitados.
- Métricas:
- Cobertura de líneas > 80% en esos módulos.
## Convenciones acordadas
- Fechas/horas:
- Persistir en UTC en SQLite con formato ISO-SQL “YYYY-MM-DD HH:MM:SS[.SSS]”.
- Un solo helper para serializar/deserializar timestamps.
- Validadores canónicos: YMD (YYYY-MM-DD), HH:mm (0023:0059).
- Tipado:
- TS estricto por módulos de forma incremental: `strict`, `noImplicitAny`, `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes` (activación progresiva).
- Resultados SQLite tipados (interfaces pequeñas y casts controlados).
- Organización:
- Helpers compartidos (fecha/validaciones/cripto) en módulo único (o shared).
- Tests reusan helpers de `tests/helpers`.
- SvelteKit:
- `svelte-kit sync` antes de `tsc` para generar tipos.
- Rutas ICS homogéneas con helpers de `lib/server/ics`.
## Riesgos y mitigación
- Fechas/UTC: posibles regresiones en límites y recordatorios.
- Mitigación: snapshots/fixtures antes/después de serializaciones, comparar salidas ICS.
- DI de DB: transición puede generar inconsistencias si se mezcla estático/locator.
- Mitigación: migración gradual y adaptador de compatibilidad temporal.
- Tipado estricto: ruido inicial.
- Mitigación: endurecer flags por paquete/módulo en varias PRs pequeñas.
## Métricas de aceptación por lote
- Tests verdes y sin cambios funcionales.
- Reducción de duplicados medible (git grep antes/después).
- `bunx tsc --noEmit` limpio en el ámbito del lote.
- Cobertura estable o en subida en módulos afectados.
## Siguientes pasos prácticos
1) Estabilizar typecheck (Lote 0): **Completado**.
2) Unificar fechas/validaciones (Lote 1): **Completado**.
3) Centralizar helpers de tests y cripto (Lote 2): **Completado**.
4) Endurecer tipos en core (Lote 3): **Completado**.
5) Consolidar ICS (Lote 4): **Completado**.
6) Dividir TaskItem y revisar AppShell (Lote 5): **Completado**.
7) DI ligera para DB (Lote 6).
8) Aumentar cobertura en módulos flojos (Lote 7).
9) Documentación exhaustiva (Fase D): plan y PRs D0D6.
10) Internacionalización (Fase I): plan y PRs I0I7.
## Información adicional a recopilar (para Lote 0)
- tsconfig.json (raíz)
- apps/web/tsconfig.json
- apps/web/vite.config.ts
Con estos 3 archivos podremos definir cambios mínimos para tener un typecheck limpio por paquetes sin introducir dependencias externas.
## Fase D — Documentación (nueva)
El objetivo de esta fase es frenar la “entropía documental” actual, ofrecer una narración clara para quien llega nuevo y, a la vez, dejar una base sólida para seguir evolucionando el sistema sin añadir deuda técnica.
### Principios
- Markdown en /docs como fuente de verdad; sin dependencias nuevas; build opcional más adelante.
- Documentación estructurada por **qué quiere hacer la persona lectora** (evaluar, desplegar, operar, contribuir), no por “lista de archivos existentes”.
- README raíz orientado a adopción (pitch, casos de uso, requisitos), con enlaces claros a la documentación detallada.
- Documentación “v2” claramente separada de planes históricos y documentos de trabajo.
- Verificación simple en CI para comprobar “completitud” (p. ej., grep de variables de entorno usadas vs documentadas).
### D0: Reset estructural y guía de estilo
Objetivo: redefinir la estructura de documentación y marcar qué es canónico vs histórico, sin perder información.
Cambios:
- **README raíz (del repo)**:
- Reescribirlo con foco en:
- Tagline y pitch corto (qué resuelve, para quién).
- Casos de uso y “no es para ti si…”.
- Requisitos para desplegarlo (Evolution API, hosting, etc.).
- Vista de alto nivel de cómo funciona.
- Enlace claro a `docs/` (Quickstart, Guía de uso, Arquitectura, etc.).
- **Definir documentos canónicos en `docs/`** (columna vertebral “v2”):
- `docs/README.md` — índice general y guía de estilo.
- `docs/overview.md` — visión general y flujos clave (evaluación).
- `docs/quickstart.md` — de cero a bot funcionando (dev/prod).
- `docs/config/env.md` — referencia única de variables de entorno.
- `docs/usage/commands.md` — uso por WhatsApp (comandos).
- `docs/usage/web.md` — uso de la interfaz web.
- `docs/architecture.md` — arquitectura lógica y DB locator.
- `docs/operations.md` — operación, métricas, backups, health/metrics.
- `docs/contributing.md` — cómo tocar el código sin romper convenciones.
- **Crear un área de documentación histórica**:
- Nuevo directorio: `docs/legacy/` (o similar).
- Mover allí:
- Planes antiguos (`plan-*.md`, `*-plan.md`) que ya no encajen con el estado actual.
- Documentos de trabajo y notas de diseño que han quedado superadas por este plan.
- Añadir `docs/legacy/README.md` explicando que:
- Son documentos históricos, útiles como contexto, pero **no** representan el estado actual ni la decisión vigente.
- **Guía de estilo mínima en `docs/README.md`**:
- Idioma principal: español para texto narrativo; identificadores y nombres técnicos en inglés.
- Convenciones de nombres de archivos (kebab-case, sufijos como `-guide`, etc.).
- Expectativas por documento (público objetivo y nivel técnico).
Métricas de aceptación:
- README raíz reescrito y enlazando a la nueva estructura.
- `docs/README.md` actualizado con el índice “v2” y la guía de estilo.
- Todos los planes/documentos obsoletos movidos a `docs/legacy/` y etiquetados como históricos.
### D1: Referencia de variables de entorno
Objetivo: consolidar una tabla única y fiable de configuración por entorno.
Cambios:
- Construir un inventario completo:
- Partir del `git grep process.env` (ya recogido en este documento).
- Complementar con las variables definidas en `.env.example`.
- Identificar:
- variables usadas en código pero ausentes en `.env.example`,
- variables presentes en `.env.example` que ya no se usan.
- Crear `docs/config/env.md`:
- Tabla con columnas:
- **VARIABLE** | **Descripción** | **Tipo** | **Por defecto** | **Obligatoria** | **Ámbito (core/web)** | **Sensible (sí/no)**.
- Sección explicando:
- cómo se cargan (ficheros `.env` vs entorno),
- qué variables son imprescindibles para arrancar,
- particularidades en test (variables que los tests fuerzan o suponen).
- Sincronizar `.env.example` con la tabla:
- Mismas variables.
- Comentarios alineados con la descripción de `docs/config/env.md`.
- README raíz solo enumera un subconjunto (“configuración esencial”) y enlaza a este documento para el resto.
Métricas de aceptación:
- `docs/config/env.md` cubre el 100% de las variables utilizadas en código.
- `.env.example` sin variables huérfanas ni faltantes respecto al inventario.
- Potencial check de CI: script simple que compare nombres de variables entre código, `env.md` y `.env.example`.
### D2: Quickstart e instalación
Objetivo: permitir que alguien nuevo pase de “clonar el repo” a “tengo el bot funcionando” sin leer todo el resto.
Cambios:
- Crear/actualizar `docs/quickstart.md`:
- Requisitos:
- Bun/Node versión recomendada.
- Instancia de Evolution API.
- Almacenamiento para SQLite.
- Pasos:
- Clonar repositorio e instalar dependencias.
- Copiar `.env.example` y rellenar variables mínimas.
- Arrancar en desarrollo (core + web).
- Arrancar en producción (comandos de build + start).
- “Happy path”:
- ejemplo de mensaje de WhatsApp para crear una tarea,
- cómo verla en la web (/app),
- cómo confirmar que /health y /metrics responden.
- Alinear README raíz con este quickstart:
- Sección “Instalación rápida” condensando los pasos principales.
- Enlace explícito a `docs/quickstart.md` para detalles.
Métricas de aceptación:
- Una persona que no conoce el proyecto puede seguir `docs/quickstart.md` en una máquina limpia y terminar con:
- webhook recibiendo eventos,
- tareas visibles en /app,
- métricas en /metrics.
### D3: Uso (web + comandos)
Objetivo: documentar claramente la experiencia de uso, separando canales (WhatsApp vs web) y evitando duplicidad.
Cambios:
- Reorganizar la documentación de uso actual en dos archivos:
- `docs/usage/commands.md`:
- Basado en `docs/USER_GUIDE.md` y `docs/commands-inventory.md`.
- Contenido:
- prefijo de comandos y principios (DM vs grupo, fechas, rate limit),
- catálogo de comandos con alias, parámetros y ejemplos,
- notas sobre:
- interpretación de fechas (“hoy”, “mañana”, YYYY-MM-DD),
- menciones reales vs tokens `@número`,
- comportamiento en grupo vs DM,
- sección de administración (/admin…).
- `docs/usage/web.md`:
- Basado en `plan-diseno-web.md`, `plan-web-fases.md` y el estado actual de la UI.
- Contenido:
- estructura de `/app` (lista de tareas, grupos, preferencias),
- flujo típico de uso desde web (reclamar/soltar, editar, completar),
- relación entre lo que ves en WhatsApp y lo que ves en la web,
- vistas de calendario/ICS y cómo se consumen.
- Dejar `docs/USER_GUIDE.md` como entrada que redirige:
- Puede convertirse en un índice breve que apunte a `usage/commands.md` y `usage/web.md`, o bien moverse a `docs/legacy/` cuando la nueva estructura esté madura.
Métricas de aceptación:
- No hay duplicación significativa de contenido entre README, `USER_GUIDE`, `usage/commands` y `usage/web`.
- Los ejemplos de comandos están actualizados y probados manualmente (al menos los principales).
### D4: Operaciones y despliegue
Objetivo: facilitar la vida a quien opera Taskbot en producción (observabilidad, mantenimiento, CI/CD).
Cambios:
- Crear/actualizar `docs/operations.md`:
- Basado en `operations.md`, `metrics-plan.md`, `CI-CD-PLAN.md` y `docs/grafana/`.
- Contenido:
- **Despliegue**:
- opciones típicas (proceso único vs supervisado por systemd, contenedores, etc.),
- recomendaciones de recursos y almacenamiento,
- interacción con Evolution API (timeouts, health checks).
- **Migraciones y base de datos**:
- cómo y cuándo se ejecutan migraciones,
- política de WAL y PRAGMAs más importantes,
- estrategias de backup/restore de SQLite.
- **Observabilidad**:
- uso de `/health` (qué comprueba, señales de fallo),
- uso de `/metrics` (Prometheus/JSON, flags de configuración),
- referencia al dashboard de Grafana (`docs/grafana/taskbot-metrics.json`): qué gráficos hay y qué significan.
- **Schedulers y tareas periódicas**:
- sincronización de grupos,
- recordatorios,
- limpieza de cola de respuestas,
- cómo ajustar intervalos y qué implicaciones tienen.
- Conectar con variables de entorno:
- Enlazar a `docs/config/env.md` para cada opción relevante (p. ej. `GROUP_SYNC_INTERVAL_MS`, `RQ_*`, `METRICS_*`).
Métricas de aceptación:
- Operador externo puede entender:
- qué mirar en caso de problemas (health/metrics/logs),
- cómo hacer backup/restore sin corromper la DB,
- cómo cambiar la frecuencia de tareas periódicas con seguridad.
### D5: Arquitectura y DB
Objetivo: ofrecer una visión clara de cómo está organizado el sistema internamente, especialmente tras el refactor (servicios extraídos, DB locator, ICS central).
Cambios:
- Consolidar `docs/architecture.md`:
- Integrar/actualizar contenido de `architecture.md`, `overview.md`, `database.md` y este propio plan.
- Contenido:
- **Componentes principales**:
- servidor HTTP (WebhookServer y handlers /webhook, /health, /metrics),
- servicios de dominio (TaskService, ResponseQueue, GroupSync, Reminders, Admin…),
- cola de respuestas y cliente Evolution,
- app web SvelteKit.
- **DB Locator**:
- motivación,
- API básica (`getDb`/`setDb`/`withDb`),
- cómo se usa en servicios y tests.
- **Flujos clave**:
- mensaje entrante → comando → creación/actualización de tarea → encolado de respuesta/reacción,
- sincronización de grupos y membresías,
- recordatorios.
- **Base de datos**:
- tablas principales (tasks, task_assignments, groups, group_members, response_queue, user_preferences…),
- invariantes importantes,
- decisiones de diseño (WAL, migraciones up-only).
- Mantener `docs/database.md` como detalle de esquema:
- Puede centrarse en:
- descripción tabla a tabla,
- índices relevantes,
- ejemplos de consultas típicas,
- enlazado desde `docs/architecture.md`.
Métricas de aceptación:
- Contributors nuevos pueden entender dónde “colgar” código nuevo (por ejemplo, un nuevo servicio, una nueva tabla o un nuevo comando).
- Este plan de refactor técnico se percibe como coherente con la arquitectura documentada (no como un documento separado y divergente).
### D6: Contribución y ADRs
Objetivo: ayudar a futuras personas contribuidoras (incluido tú mismo “del futuro”) a cambiar el sistema sin romper las convenciones ni repetir debates ya resueltos.
Cambios:
- Crear/actualizar `docs/contributing.md`:
- Contenido:
- cómo montar entorno de desarrollo (dependencias, comandos básicos),
- cómo ejecutar:
- typecheck core (`bun run typecheck:core`),
- typecheck web (`bun run typecheck:web`),
- tests y cobertura (`bun test --coverage`),
- convenciones de código:
- UTC y formato de timestamps en SQLite,
- uso de helpers centralizados (datetime, crypto, helpers de test),
- tipos estrictos en TS (flags activados y expectativas),
- uso del DB locator.
- criterios para abrir PRs:
- tests verdes,
- sin cambios funcionales en lotes que se declaran “refactor internos”,
- cuándo y cómo añadir un ADR.
- referencia rápida a how-tos existentes:
- `how-to/adding-command.md`,
- `how-to/adding-migration.md`,
- `how-to/adjusting-group-sync.md`,
- `how-to/adding-env.md`.
- Consolidar ADRs:
- Mantener `docs/adr/*.md` como registro de decisiones de arquitectura.
- Añadir ADRs nuevos cuando:
- se tomen decisiones de largo plazo (p. ej., i18n, políticas de rate limiting, cambios de almacenamiento).
- Desde `docs/contributing.md`, explicar:
- cuándo merece la pena un ADR,
- cómo estructurarlo (título, contexto, decisión, consecuencias).
Métricas de aceptación:
- Contributors pueden:
- saber qué comandos ejecutar antes de abrir PR,
- entender las decisiones ya tomadas (UTC, DB locator, ICS, estructura de servicios),
- no tener que “descubrirlas” leyendo código o este plan.
### Orden recomendado de ejecución para Fase D
1. Ejecutar **D0** (reset estructural):
- nuevo README raíz orientado a adopción,
- índice v2 en `docs/README.md`,
- movimiento de documentos antiguos a `docs/legacy/`.
2. Ejecutar **D1** (env) y **D2** (quickstart) en paralelo:
- cerrar la historia de “cómo configuro esto” y “cómo lo echo a andar”.
3. Completar **D3** (uso commands/web) apoyándose en la guía de usuario actual, reorganizando sin perder contenido útil.
4. Redondear con **D4** (operaciones) y **D5** (arquitectura/DB) usando este plan de refactor como referencia.
5. Cerrar con **D6** (contribución+ADRs), conectando:
- decisiones técnicas ya tomadas,
- how-tos existentes,
- y el pipeline de typecheck/tests.
Esta Fase D se coordina con los lotes técnicos previo/posteriores:
- depende de que el estado del core/web esté relativamente estable (Lotes 06),
- y sirve como preparación explícita antes de acometer cambios más invasivos (i18n, nuevos comandos, cambios de esquema), reduciendo el riesgo de deuda técnica futura.
## Fase I — Internacionalización (nueva)
- Principios:
- Sin dependencias externas: diccionarios JSON + helper t() con plantillas {var}.
- en-US como base del código; es-ES como primera localización.
- Locale configurable por ENV y, en web, preferencia por usuario (con fallback).
- PR I0: Inventario y glosario
- Auditoría de textos visibles (servicios, UI, errores “para usuario”, títulos ICS).
- Glosario EN/ES y convenciones de keys (scope.key).
- Aceptación: listado consolidado.
- PR I1: Normalización de identificadores a inglés
- Renombrar funciones/variables/módulos que sigan en español (sin tocar textos visibles).
- Aceptación: grep de identificadores no ingleses → 0 o lista de excepciones (nombres propios).
- PR I2: Infra i18n mínima (core)
- Helper t(locale, key, params) y carga de diccionarios.
- ENV APP_LOCALE y orden de resolución (env → usuario → default).
- Aceptación: conmutación en 12 mensajes de ejemplo entre en-US/es-ES.
- PR I3: Externalización de mensajes en servicios
- WhatsApp/CLI/respuestas de comandos → keys + params.
- Mantener es-ES como referencia y crear en-US equivalentes.
- Aceptación: tests de comandos verdes en ambos locales; 0 strings hardcoded en servicios.
- PR I4: UI web (SvelteKit)
- Carga de diccionarios por ruta/layout, helper en frontend, marcadores para traducciones faltantes.
- Preferencia de idioma por usuario y fallback Accept-Language.
- Aceptación: cambiar idioma desde preferencias refleja cadenas principales.
- PR I5: Formatos y convenciones por locale
- Fechas/números con Intl; labels de días; ymd→dmy solo en vistas.
- ICS: título/PRODID localizables; contenido sigue en UTC.
- Aceptación: pruebas de formateo y snapshots actualizados.
- PR I6: Logs y métricas
- Política: logs en inglés estable; mensajes de usuario traducibles; métricas/labels estáticos.
- Aceptación: política documentada y aplicada.
- PR I7: QA i18n en CI
- Comprobador: claves sin traducción, claves huérfanas, placeholders inválidos.
- Smoke tests en en-US y es-ES (web y comandos básicos).
- Aceptación: CI bloquea claves faltantes.
- Orden recomendado:
1) Completar Lote 7 (cobertura) para congelar comportamiento.
2) PRs D0D6 (documentación) en paralelo donde sea posible.
3) PRs I0I7 (i18n) en tandas pequeñas, con tests en ambos locales.