40 KiB
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 250–335 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. normalizeTimeduplicado en API y page.server.ymdUTCyaddMonthsUTCdefinidas 3 veces en rutas ICS y también en tests.sha256Hexreimplementado en múltiples tests pese a existir ensrc/utils/crypto.ts.- SimulatedResponseQueue replicado en varios tests.
- Formateo SQL-UTC con
-
Tipado/infra:
- tsc falla en apps/web por
$liby./$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).
- tsc falla en apps/web por
-
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:
git ls-files '*.ts' '*.tsx' '*.svelte' | xargs wc -l | sort -nr | head -n 30
git grep -n "toISOString().replace('T', ' ').replace('Z', '')"
git grep -n "normalizeTime(" && git grep -n "ymdUTC(" && git grep -n "addMonthsUTC(" && git grep -n "isValidYmd("
git grep -n "sha256Hex(" && git grep -n "SimulatedResponseQueue"
bunx tsc --noEmit --pretty false
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.
- Documento creado y versionado (commit
- 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:coreytypecheck:webconfigurados y verificados. Se utilizatsconfig.core.jsonpara aislar el typecheck del core con reglas laxas, mientras que la web usa su propia configuración de SvelteKit. Los shims ensrc/types/shims.d.tsresuelven conflictos de tipos entre Bun y el DOM. - Verificación exitosa:
bun run typecheck:coreybun run typecheck:webse ejecutan sin errores.
- Lote 0 completado: scripts
- 2025-11-09:
- 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.0–6.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.
- Scripts:
- Métricas:
bunx tsc --noEmitlimpio por paquete.- Desaparecen errores
$liby./$typestrassvelte-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.
- Crear helpers canónicos (por ejemplo,
- 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
sha256Hexpor import desdesrc/utils/crypto. - Centralizar SimulatedResponseQueue en
tests/helpers/queue.tsy reusarlo. - Unificar helpers de fechas en tests (
toIsoSql,ymdUTC,addDays) entests/helpers/dates.ts.
- Reemplazar implementaciones locales de
- Métricas:
git grep -n "async function sha256Hex"en tests → 0.git grep -n "SimulatedResponseQueue"→ 1 implementación única.
- Comprobaciones:
bun test --coverageestable o mejor.
Lote 3 — Tipos y endurecimiento suave - Completado
- Objetivo:
- Bajar ruido de TS con cambios mínimos, sin funcionalidad nueva.
- Cambios:
- Resolver
undefinedvsnullen utils como groupColor/whatsapp. - Corregir
HeadersInit/fetchen 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.
- Resolver
- Métricas:
bunx tsc --noEmitlimpio 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/addMonthsUTCal móduloapps/web/src/lib/server/ics.tsy usarlos desde aggregate/group/personal. - Mantener helpers de escape/folding/etag en el mismo módulo.
- Mover
- Métricas:
git grep -n "function ymdUTC"y"function addMonthsUTC"solo en ics.ts.
- Comprobaciones:
- Tests ICS (
tests/web/ics.*) verdes.
- Tests ICS (
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
AppShelly/app/+pagepara separar lógica de datos y presentación (stores y load).
- Dividir
- 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:
-
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.tsconbuildHeaders(),sendText(payload),sendReaction(payload)y manejo de errores/logging.
- Nuevo módulo:
- Tipar metadata y parsers:
- Tipo
QueueMetadata = { kind: 'onboarding' | 'reaction'; ... }, helpersparseQueueMetadata(),isReactionJob().
- Tipo
- Aislar limpieza/retención:
- Nuevo módulo:
src/services/queue/cleanup.tsconrunCleanupOnce(),startCleanupScheduler(),stopCleanupScheduler().
- Nuevo módulo:
- ResponseQueue queda como orquestador (claim → enviar → marcar), delegando a EvolutionClient y Cleanup.
- Extraer cliente Evolution (HTTP):
- 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(esquemaresponse_queue), teststests/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:
bunx tsc -p tsconfig.core.json --noEmit --pretty false bun test --coverage git grep -n "sendReaction\\|sendText" src
-
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.tsconpickNextDisplayCode(db).
- Nuevo:
- Extraer reacción al completar:
- Nuevo:
src/tasks/complete-reaction.tsconenqueueCompletionReactionIfEligible(db, taskId).
- Nuevo:
- Extraer mapeos a DTO:
- Nuevo:
src/tasks/mappers.tsconmapRowToTask(),mapRowsToListItem().
- Nuevo:
TaskService.createTask/completeTask/list*delegan en estos helpers.
- Extraer generación de display_code:
- 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, teststests/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:
bunx tsc -p tsconfig.core.json --noEmit --pretty false bun test --coverage git grep -n "display_code" src
-
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.tsconfetchGroups(),fetchMembers(groupId)y parseos robustos.
- Nuevo:
- Extraer repositorio/caché:
- Nuevo:
src/services/group-sync/repo.tsconupsertGroups(),ensureGroupExists(),getActiveCount(),cacheActiveGroups(),listActiveMemberIds(), getters/settersonboarding_prompted_at. - Nuevo:
src/services/group-sync/cache.tspara laMap<string,string>y helpers.
- Nuevo:
- Extraer reconciliación:
- Nuevo:
src/services/group-sync/reconcile.tsconreconcileGroupMembers(db, groupId, snapshot, nowIso?).
- Nuevo:
- 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).
- Añadir método en
GroupSyncServicequeda como fachada (APIs públicas y schedulers), delegando internamente.
- Extraer acceso Evolution API:
- 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, teststests/unit/services/group-sync.*.test.ts.
- Métricas de aceptación:
src/services/group-sync.ts≈ 600–700 LOC (fase 1), sin cambios funcionales.- Caché y métricas sin variaciones de semántica.
- Comprobaciones y comandos:
bunx tsc -p tsconfig.core.json --noEmit --pretty false bun test --coverage git grep -n "fetchAllGroups\\|participants" src/services/group-sync*
-
PR 5.5-d — WebhookServer (665 LOC)
- Motivos:
src/server.tsmezcla 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; usaGroupSyncService.getSecondsUntilNextGroupSync()). - Nuevo:
src/http/webhook-handler.tsconhandleMessageUpsert(data). - Nuevo:
src/http/bootstrap.tsconstartServices()(webhook-manager, schedulers, queues, maintenance).
- Nuevo:
src/server.tsqueda como wire-up (validateEnv → migrate → Bun.serve) y reexporta fachadaWebhookServerpara no romper tests.
- Controladores HTTP dedicados:
- 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:
bunx tsc -p tsconfig.core.json --noEmit --pretty false bun test --coverage git grep -n "handleRequest(\\|handleMessageUpsert" src
- Motivos:
-
-
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 ≈ 600–700, server.ts < 350.
bunx tsc --noEmitlimpio 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
dbcomo parámetro; reexportar desde fachada.
- Mitigación: módulos nuevos sin importar servicios de alto nivel; inyectar
- Cambios en logging o tiempos:
- Mitigación: mantener mensajes/claves existentes; conservar defaults; validar en tests de integración.
- Dividir módulos puede introducir imports cíclicos:
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
resolveDbAbsolutePathentre 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.
- Nuevos archivos:
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 (00–23:00–59).
-
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).
- TS estricto por módulos de forma incremental:
-
Organización:
- Helpers compartidos (fecha/validaciones/cripto) en módulo único (o shared).
- Tests reusan helpers de
tests/helpers.
-
SvelteKit:
svelte-kit syncantes detscpara 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 --noEmitlimpio en el ámbito del lote.- Cobertura estable o en subida en módulos afectados.
Siguientes pasos prácticos
- Estabilizar typecheck (Lote 0): Completado.
- Unificar fechas/validaciones (Lote 1): Completado.
- Centralizar helpers de tests y cripto (Lote 2): Completado.
- Endurecer tipos en core (Lote 3): Completado.
- Consolidar ICS (Lote 4): Completado.
- Dividir TaskItem y revisar AppShell (Lote 5): Completado.
- DI ligera para DB (Lote 6).
- Aumentar cobertura en módulos flojos (Lote 7).
- Documentación exhaustiva (Fase D): plan y PRs D0–D6.
- Internacionalización (Fase I): plan y PRs I0–I7.
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.).
- Reescribirlo con foco en:
-
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.
- Planes antiguos (
- Añadir
docs/legacy/README.mdexplicando que:- Son documentos históricos, útiles como contexto, pero no representan el estado actual ni la decisión vigente.
- Nuevo directorio:
-
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.mdactualizado 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.exampleque ya no se usan.
- variables usadas en código pero ausentes en
- Partir del
-
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
.envvs entorno), - qué variables son imprescindibles para arrancar,
- particularidades en test (variables que los tests fuerzan o suponen).
- cómo se cargan (ficheros
- Tabla con columnas:
-
Sincronizar
.env.examplecon 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.mdcubre el 100% de las variables utilizadas en código..env.examplesin variables huérfanas ni faltantes respecto al inventario.- Potencial check de CI: script simple que compare nombres de variables entre código,
env.mdy.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.exampley 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.
- Requisitos:
-
Alinear README raíz con este quickstart:
- Sección “Instalación rápida” condensando los pasos principales.
- Enlace explícito a
docs/quickstart.mdpara detalles.
Métricas de aceptación:
- Una persona que no conoce el proyecto puede seguir
docs/quickstart.mden 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.mdydocs/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…).
- Basado en
-
docs/usage/web.md:- Basado en
plan-diseno-web.md,plan-web-fases.mdy 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.
- estructura de
- Basado en
-
-
Dejar
docs/USER_GUIDE.mdcomo entrada que redirige:- Puede convertirse en un índice breve que apunte a
usage/commands.mdyusage/web.md, o bien moverse adocs/legacy/cuando la nueva estructura esté madura.
- Puede convertirse en un índice breve que apunte a
Métricas de aceptación:
- No hay duplicación significativa de contenido entre README,
USER_GUIDE,usage/commandsyusage/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.mdydocs/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.
- uso de
- Schedulers y tareas periódicas:
- sincronización de grupos,
- recordatorios,
- limpieza de cola de respuestas,
- cómo ajustar intervalos y qué implicaciones tienen.
- Despliegue:
- Basado en
-
Conectar con variables de entorno:
- Enlazar a
docs/config/env.mdpara cada opción relevante (p. ej.GROUP_SYNC_INTERVAL_MS,RQ_*,METRICS_*).
- Enlazar a
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.mdy 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).
- Componentes principales:
- Integrar/actualizar contenido de
-
Mantener
docs/database.mdcomo detalle de esquema:- Puede centrarse en:
- descripción tabla a tabla,
- índices relevantes,
- ejemplos de consultas típicas,
- enlazado desde
docs/architecture.md.
- Puede centrarse en:
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),
- typecheck core (
- 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.
- Contenido:
-
Consolidar ADRs:
- Mantener
docs/adr/*.mdcomo 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).
- Mantener
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
-
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/.
-
Ejecutar D1 (env) y D2 (quickstart) en paralelo:
- cerrar la historia de “cómo configuro esto” y “cómo lo echo a andar”.
-
Completar D3 (uso commands/web) apoyándose en la guía de usuario actual, reorganizando sin perder contenido útil.
-
Redondear con D4 (operaciones) y D5 (arquitectura/DB) usando este plan de refactor como referencia.
-
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 0–6),
- 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 1–2 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:
- Completar Lote 7 (cobertura) para congelar comportamiento.
- PRs D0–D6 (documentación) en paralelo donde sea posible.
- PRs I0–I7 (i18n) en tandas pequeñas, con tests en ambos locales.