Files
task-stream/docs/10-futuro.md
Josep Piguillem a32c937765 fix(api): a read-side failure waits for its event instead of losing it
The Redis consumer used to leave a failed event pending and go on to the
next ones, retrying it only at the next start. With Mongo down for a
while, a later change of the same ticket was then projected first, and
the version check dropped the earlier one for good (a status change
could vanish from the read model), besides the read side lagging until
a restart.

Now a failed event is retried in place, waiting longer each time (up to
30 s), and the next one waits for it. Failures of the infrastructure
(Mongo, Redis, the network: isTransientError) wait as long as it takes,
so the read side catches up by itself; an event that keeps failing on
its own goes, after 5 attempts, to <stream>:dead with its error, so it
does not hold up the rest.

GET /api/health reports the way from write to read (events waiting in
the outbox, pending to be projected, set aside) and says `degraded`,
still with 200, when something is stuck.

Tried for real: with Mongo stopped, two status changes went in; the
first failed to project with ECONNREFUSED, and once Mongo was back the
ticket's history had both steps, in order, without restarting the API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 22:24:19 +02:00

138 lines
13 KiB
Markdown

# 10. Lo que queda
Lo pendiente, ordenado de más a menos urgente: primero lo que hace falta para dar el producto por terminado en
producción, después los fallos y la deuda conocidos y al final las ideas. Cada punto dice qué es, por qué importa y,
cuando hay pistas, por dónde empezar.
## 10.1 Para dar el producto por terminado
| Qué | Por qué | Por dónde empezar |
| ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Comprobar en producción que el tiempo real ya no se pierde | Ya hay _ping_ cada 10 s, reconexión si no hay respuesta y recarga de cada pantalla al reconectar, pero no se ha probado allí | Dejar una pestaña abierta más que la vida del token y tras suspender el portátil, y mover tickets desde otra. Si aún falla, mirar el token de `connectionParams`, que solo se manda al conectar |
| Pruebas de navegador en la integración continua | Los fallos del tablero (no se podía arrastrar entre columnas) y de la ficha (no se refrescaba) solo se vieron usándolo | Ya existe la receta: API real con usuarios simulados (`IdentityProvider` sustituido), `nx serve web` y Playwright con la sesión en `localStorage` (`task-stream.session`). Convertirla en `apps/web-e2e` |
| Copias de seguridad de Postgres y Mongo | Postgres guarda el event store, la fuente de verdad; Mongo se puede reconstruir desde él, Postgres no | Copias programadas en Dokploy y **una restauración de prueba** |
| Limpiar los datos de prueba de producción | DEMO, TEST y los tickets de las pruebas del MCP | Borrar los proyectos desde Configuración (borrado definitivo con su clave) |
| Registrar el webhook del cliente de contraseña en Pireon | Sin él, revocar un token de quien entra con contraseña no se nota hasta que caduca la caché | Solo si `PIREON_PASSWORD_CLIENT_ID` tiene valor: webhook en ese cliente y su secret en `PIREON_PASSWORD_WEBHOOK_SECRET` (ver [Autenticación](05-autenticacion.md)) |
| Una versión 1.0 | Un punto fijo al que volver | Desplegar todo, etiquetar `v1.0.0` y redactar la lista de cambios (las notas de versión de TaskStream dan el borrador) |
| Revisión de seguridad | Hay rutas sin sesión (proyectos públicos, escaparate, propuestas, panel para clientes) y un MCP abierto a Internet | Permisos por rol, qué ve un anónimo, límites de peticiones (solo las propuestas tienen), CORS, cabeceras |
| Registro de errores en producción | Hoy un fallo solo se conoce si alguien avisa | Sentry (o similar) en la API, la web y el MCP |
## 10.2 Fallos y deuda conocidos
- **Error al crear subtareas en producción**: lo reportó el usuario y no se reprodujo en local; con el arreglo de la
ficha la lista ya se refresca, pero falta el mensaje exacto si vuelve a salir.
- **Imágenes pegadas**: ya no salen en la lista de adjuntos, pero si se borran del texto el adjunto sigue guardado.
- **Seguimiento**: los tickets que ya existían no se apuntan a quien los creó o los tiene asignados (solo los nuevos).
- **El MCP no hace el flujo OAuth del protocolo**: el cliente tiene que llevar un token de Pireon en la cabecera y
renovarlo a mano cuando caduca.
## 10.3 Siguientes funcionalidades
### En curso
Nada: la tanda está hecha (ver 10.5).
### Aceptadas, para después de esta tanda
- **Sincronizar con issues de Gitea o GitHub**: que un issue del repositorio cree o actualice un ticket, y al revés.
- **Búsqueda con lenguaje de consulta**: `estado:abierto asignado:yo navegador:Safari prioridad>=alta`, guardable como
vista.
- **Entornos de despliegue**: en qué entorno está cada ticket (pruebas, producción), desde los avisos de la
integración continua.
- **Página pública de novedades**: las notas de versión publicadas en una página por proyecto, con RSS.
### Otras ideas apuntadas, sin decidir
- **Resumen diario por correo**: lo asignado, lo que vence hoy y lo que ha cambiado en lo que sigues, en un solo correo.
- **Tickets recurrentes**: «revisar copias cada lunes», creados solos con la plantilla del tipo.
- **Crear tickets por correo**: una dirección por proyecto; lo que llega es un ticket (con adjuntos) y las respuestas,
comentarios. Necesita un buzón de entrada.
- **Posponer un ticket**: «recuérdamelo el jueves».
- **Reunión diaria**: qué cerró cada uno ayer, qué tiene hoy y qué le bloquea, sacado del historial.
- **Requisitos al cambiar de estado**: campos obligatorios para entrar en un estado (estimación para «En curso»…).
- **Plazos por prioridad**: «lo crítico, en 24 h», con aviso antes de vencer.
- **Matriz impacto/esfuerzo** para ordenar el backlog.
- **Planning poker** y **retrospectiva del sprint** (las acciones se convierten en tickets).
- **Avisar a quien propuso** cuando su propuesta se hace o se descarta.
## 10.4 Descartado por ahora
Mucho trabajo para lo que aportan hoy; se pueden recuperar si alguien los necesita:
- **Registro de horas** dedicadas a cada ticket.
- **Objetivos u OKR** por encima de las épicas.
- **App móvil**: la web ya se adapta a pantallas estrechas.
- **Asistente en la web** (resumir tickets, sugerir tipo, prioridad y área, redactar notas de versión): el MCP ya deja
hacerlo desde un cliente de IA.
- **Avisos a Discord, Slack o Telegram** con mensajes legibles: los webhooks salientes siguen ahí para quien los
necesite.
- **Conector de claude.ai** (OAuth del protocolo en el MCP y registro dinámico de clientes en Pireon).
## 10.5 Hecho desde la primera versión de esta lista
- Tiempo real: _ping_ y reconexión del WebSocket, y recarga de lo que se ve al reconectar.
- Arrastrar a una columna prohibida devuelve la tarjeta a su sitio.
- Reacciones y «Bloqueado» en componentes propios (los estilos de la ficha vuelven a caber en 8 kB).
- Límite de propuestas en Redis, válido con varias instancias.
- Aviso `PROPOSED` a propietarios y admins cuando llega una propuesta.
- `voted` también por REST.
- Borrar un proyecto borra su integración con Git.
- Sin avisos de lint.
- La web pública (landing-web) enseña las propuestas más votadas, el enlace al formulario y los votos de la hoja de
ruta.
- Un proyecto de la landing creado sin tablero puede abrirlo después: «Crear en TaskStream» en su ficha o
`taskstream: true` en un `PATCH`.
- Paquete inicial de la web de 2,13 MB a 1,88 MB con `@pi/ui` 0.0.4, que ya no registra Chart.js al importarse ni
depende de `lodash`.
- Descripciones y comentarios con editor visual (guardado en Markdown), con menciones y capturas pegadas.
- Ids: `uuid` en todas las tablas de Postgres (el orden de los eventos en `seq`) y, en Mongo, `id` con el uuid del
dominio más el `_id` `ObjectId` propio.
- Enlazar tickets y colgar de un padre eligiendo en un selector con búsqueda; casillas de `@pi/ui` en toda la web.
- Mover tickets entre releases: «Release…» en la edición en bloque, filtro por release en la lista (también desde cada
release, «Ver en la lista»), y al archivar una release, elegir adónde va lo pendiente.
- Calendario suscribible (iCalendar) desde «Mi trabajo»: tickets con fecha, sprints y releases, con URL secreta
renovable.
- Notificaciones push (Web Push) en cada dispositivo, con la web instalable.
- Campos personalizados por proyecto (texto, número, fecha o lista de opciones): se definen en Configuración, se
editan en la ficha, se ven bajo el título en la lista y se filtran por los de opciones (también por GraphQL, REST y
MCP).
- Dependencias en la hoja de ruta: flechas entre las épicas que se bloquean o dependen unas de otras, y aviso cuando
una empieza antes de que acabe la que la bloquea.
- Plantillas de proyecto: al crear uno, «Empezar con la configuración de» otro que administras copia sus estados,
transiciones, tipos, áreas, campos personalizados y automatizaciones. (El burndown y la velocidad de sprint ya
existían, en Sprints.)
- Informe para imprimir (o guardar en PDF) de un sprint o una release, desde Sprints y Releases.
- Panel del proyecto, primera parte: sprint en curso (¿llega a tiempo?), tickets en riesgo (vencidos, que vencen esta
semana, bloqueados y atascados), antigüedad de lo que está en curso y cuellos de botella. También por REST, GraphQL
y MCP (`get_project_insights`).
- Panel del proyecto, segunda parte: lo que ha cambiado desde tu última visita, previsión de las próximas releases al
ritmo real (Monte Carlo), carga del equipo en puntos y salud del backlog con enlaces a la lista filtrada.
- Panel del proyecto, tercera parte: calidad (bugs abiertos por prioridad, nuevos y cerrados por semana, QA por área,
bugs tras cada release) y abiertos por opción de un campo de lista.
- Escrituras a la vez en un ticket: la API repite sola la orden que choca con otra (por ejemplo, con una saga), en
lugar de devolver un 409.
- Vistas guardadas en «Mi trabajo»: filtros propios entre proyectos (proyectos, asignados a mí, que sigo, estados,
prioridades, solo bugs, vencidos, texto), en una barra encima de «Mi trabajo».
- Definición de «Hecho» por proyecto: con ella activada, un ticket no se cierra con la checklist a medias (el
propietario y los admins sí pueden).
- Archivar un proyecto: queda en solo lectura (se ve todo, no cambia nada) hasta que su propietario lo desarchiva.
- Equipos dentro del proyecto: grupos de miembros en Configuración; los tickets se asignan a uno, la lista filtra por
equipo y «@equipo» avisa a todos sus miembros.
- Presencia: en cada ticket se ve quién más lo está viendo o editando en ese momento.
- Rol «invitado»: para clientes o gente de fuera; en un proyecto privado solo ve los tickets en los que participa,
abre tickets y comenta en los suyos.
- Vista de cartera (`/portfolio`): todos tus proyectos activos de un vistazo, con su riesgo, sprint, releases y épicas.
- Una portada con vida: saludo y resumen del día, la semana que viene, lo último en tus proyectos, quién está ahora,
la semana en cifras (tuya y del equipo, con racha), sprints en curso, «Retomar», tarjetas de proyecto con actividad,
qué coger cuando no tienes nada y primeros pasos para quien empieza. También por MCP (`get_my_week`).
- Panel del proyecto, cuarta parte: actividad de 12 semanas en un mapa de calor, el recorrido de un ticket (dónde se
va el tiempo de creado a hecho, espera incluida, y la parte que es trabajo) y el embudo de propuestas del público
con las más votadas sin respuesta.
- Modo pantalla del panel: a pantalla completa para una tele del equipo, un bloque cada vez que cambia solo, en
directo y con la hora.
- Panel para clientes: un enlace de solo lectura, sin cuenta, con los bloques que elijan el propietario y los admins
(flujo, sprint, releases, épicas, actividad); se renueva o se retira desde Configuración.
- De escritura a lectura: si un evento no se puede proyectar, se reintenta en su sitio (el siguiente espera) hasta
que Mongo vuelve, sin reiniciar ni perder cambios; los que fallan por sí mismos se apartan a
`task-stream:events:dead`, y `GET /api/health` avisa de eventos atascados o apartados.