Feature Specification: Recordatorios de cita

Feature Branch: 003-recordatorios-cita

Created: 2026-09-29

Status: Draft

Input: User description: "Recordatorios de cita. El dolor número uno del cliente piloto: la no asistencia. Alcance: un proceso diario genera un recordatorio por email para cada cita reservada de las próximas 24-48 horas, sin duplicar envíos; el email incluye los datos de la cita y una forma de que el paciente cancele si no va a ir (mejor un hueco libre que un no-show). Correo en modo simulado sin SMTP configurado: se escriben ficheros .eml en datos/salida-correo/. Preguntas cerradas para Sara: antelación exacta, qué pasa si el paciente cancela desde el email y con cuánta antelación puede, y si el recordatorio se reenvía cuando la cita se mueve. Fuera de alcance v1: SMS y WhatsApp."

Clarifications

Session 2026-09-29 (respuestas de Sara)

  • Q1 — Antelación exacta → A: ventana 24–48 h. El proceso diario genera un único envío por cita reservada cuyo inicio caiga entre 24 y 48 h desde la ejecución (bordes incluidos, precisión de minuto, hora local Europe/Madrid).
  • Q2 — Efecto de cancelar desde el email → B: cancelación con confirmación. El enlace abre una página donde el paciente confirma, y solo entonces la cita pasa a cancelada y libera el hueco según la 001.
  • Q3 — Plazo de cancelación → A: hasta 24 h antes del inicio. Después el enlace muestra «fuera de plazo, llama a la clínica» y no cambia la cita.
  • Q4 — Cita movida → A: se reenvía. El envío anterior queda como histórico y se genera uno nuevo si el nuevo tramo cae en ventana y aún no tiene envío para ese tramo.

Session 2026-09-30 (respuestas de Sara)

  • Q5 — Nombre del .eml cuando hay más de un envío → A: sufijo por tramo. Un fichero por envío, con nombre determinista recordatorio-<citaId>-AAAAMMDD-HHMM.eml (AAAAMMDD-HHMM es el inicio del tramo). El fichero del tramo vigente es el actual y los de tramos anteriores quedan intactos como histórico; nunca se sobrescribe ninguno.
  • Q6 — Campos que forman la «configuración recordada» → A: la cita completa: paciente, profesional, servicio, inicio y fin. Cualquier cambio de cualquiera de ellos genera un envío nuevo si el nuevo tramo cae en ventana.
  • Q7 — Momento de ejecución del proceso diario → A: a las 00:00 hora local de la clínica (Europe/Madrid) por defecto, configurable. Así la ventana de 24–48 h cubre exactamente las citas del día siguiente y ninguna cita queda fuera por su hora.
  • Q8 — Tope superior del plazo de cancelación → A: no hay tope. Se admite cancelar desde el enlace mientras falten 24 h o más, aunque la cita se haya movido o el recordatorio sea antiguo.
  • Q9 — Seguridad del enlace de cancelación → A: solo el enlace en v1, como deuda consciente temporal con caducidad (riesgo de reenvío aceptado; el email avisa de no reenviarlo). La ejecución delega en el caso de uso único cancelarCita (001 FR-011) y la verificación reutiliza el mecanismo del portal (specs/005-cancelacion-paciente/spec.md, specs/007-identidad-paciente/spec.md) en la spec posterior que cierre la deuda.
  • Q10 — Citas que se quedan sin recordar (proceso no ejecutado o ventana ya perdida) → A: sin recuperación. Cada cita tiene como máximo un recordatorio en su ventana; si el proceso no se ejecutó y la cita ya superó las 48 h, no se recuerda nunca.
  • Q11 — Forma de la trazabilidad de envíos (FR-011) → A: una señal en la ficha de la cita, no una pantalla nueva de envíos.
  • Q12 — Constancia de la cita no enviable (paciente sin email válido) → A: informe de incidencias en fichero revisable por recepción, sin tocar la agenda y sin reintentar en la siguiente ejecución.
  • Q13 — Precio en el email → A: sí, con el formato exacto de la 001 («40,00 €»).
  • Q14 — Cómo se lanza el proceso diario → A: automático por horario y manual bajo demanda, con el mismo comportamiento y la misma garantía de no duplicidad; el manual es necesario para demostrar la feature en el piloto y para medir SC-001 y SC-002.
  • Q15 — Teléfono de la clínica en el email → A (revisado jul2026, S-01): sí se incluye. La 001 incorpora telefono obligatorio en la entidad Clínica (ver 001 FR-001); el email lo muestra y el aviso de fuera de plazo indica el teléfono concreto de la clínica.
  • Q16 — Cambio del email del paciente en su ficha → A: sí se reenvía. Si recepción corrige el email y la cita sigue en ventana, se genera un recordatorio nuevo al email correcto y el anterior queda como histórico.
  • Q17 — Detalle de la señal en la cita → A: «Recordatorio enviado el 29/09/2026 a las 00:05» o «Recordatorio pendiente». Sin rutas ni nombres de fichero en pantalla.
  • Q18 — Cita reservada con menos de 24 h de antelación → A: no genera recordatorio en v1 y el hueco se acepta. La cita queda reservada, su tramo ocupado y visible en la agenda.

User Scenarios & Testing

User Story 1 — La clínica recuerda cada cita próxima sin duplicar (Priority: P1)

Cada día, un proceso ejecutado a las 00:00 hora local de la clínica genera un recordatorio por email para cada cita en estado reservada cuyo inicio cae dentro de las próximas 24–48 horas, sea cual sea su hora de inicio. Si el proceso se ejecuta dos veces el mismo día, ningún paciente recibe el mismo recordatorio dos veces.

Why this priority: Ataca directamente el dolor número uno del piloto (la no asistencia). Sin el envío diario con garantía de no duplicidad no hay feature; el resto (contenido, cancelación, modo simulado) depende de que este envío exista y sea fiable.

Independent Test: Se puede probar por completo cargando la semilla de la 001 (Clínica Eleva con 2 semanas futuras de reservas), ejecutando el proceso diario una vez y comprobando que cada cita reservada en ventana tiene exactamente un recordatorio generado, y ejecutándolo una segunda vez para comprobar que no se genera ningún duplicado. Aporta valor por sí sola aunque el email aún no incluya la vía de cancelación.

Acceptance Scenarios:

  1. Given una cita reservada para dentro de 30 horas, When se ejecuta el proceso diario, Then se genera exactamente un recordatorio para esa cita.
  2. Given una cita reservada para dentro de 5 días (fuera de ventana), When se ejecuta el proceso diario, Then no se genera ningún recordatorio para esa cita.
  3. Given que el proceso diario ya generó el recordatorio de una cita, When se vuelve a ejecutar el proceso el mismo día sin que la cita haya cambiado, Then no se genera un segundo recordatorio para esa cita.
  4. Given una cita en estado cancelada, completada o no_asistida, When se ejecuta el proceso diario, Then no se genera ningún recordatorio para esa cita aunque su antiguo tramo caiga en ventana.
  5. Given una cita ya recordada que la clínica mueve a un tramo que vuelve a caer en ventana, When se ejecuta el proceso diario, Then se genera un nuevo recordatorio con la nueva fecha y hora, y el anterior queda como histórico (no se borra ni se sobrescribe).
  6. Given la semilla con citas en todas las franjas horarias del día siguiente, When se ejecuta el proceso diario de las 00:00, Then toda cita reservada del día siguiente queda cubierta por la ventana de aviso con independencia de su hora.
  7. Given una cita reservada creada por recepción para dentro de 12 h, When se ejecuta el proceso diario, Then no se genera recordatorio para esa cita, que sigue reservada con su tramo ocupado y visible en la agenda.
  8. Given que el proceso diario no se ejecutó durante un día y una cita ya ha superado las 48 h, When vuelve a ejecutarse el proceso, Then no se genera ningún recordatorio tardío para esa cita.
  9. Given una cita reservada en ventana a la que la clínica le cambia el servicio, When se ejecuta el proceso diario, Then se genera un nuevo recordatorio con el nombre del servicio vigente y el precio congelado en la cita (001 FR-006).
  10. Given una cita ya recordada a la que recepción le corrige el email en la ficha del paciente, When se ejecuta el proceso diario y la cita sigue en ventana, Then se genera un recordatorio nuevo al email correcto y el anterior queda como histórico.

User Story 2 — El paciente recibe qué, dónde y cómo cancelar (Priority: P1)

El paciente recibe un email en español de España con los datos de su cita (clínica, día y hora, profesional, servicio y precio) y una forma clara de cancelar si no va a ir, porque un hueco liberado a tiempo vale más que un no-show.

Why this priority: El recordatorio solo reduce no-shows si el paciente entiende cuándo y con quién tiene cita y tiene una salida fácil (cancelar) cuando no puede venir. Sin este contenido, el envío de la US1 es ruido.

Independent Test: Se puede probar por completo abriendo el email generado para una cita conocida de la semilla y comprobando que aparecen la clínica, el día y tramo horario, el profesional, el servicio y su precio, más una vía de cancelación comprensible sin manual. Entrega valor aunque el transporte aún sea simulado.

Acceptance Scenarios:

  1. Given una cita reservada de «sesión fisio» con María el 30/09/2026 de 10:00–10:45, When el paciente abre su recordatorio, Then ve la clínica, el día y tramo («30/09/2026, 10:00–10:45»), el profesional (María) y el servicio, todo en español de España y sin jerga técnica.
  2. Given un paciente que no puede acudir, When lee su recordatorio, Then encuentra una forma visible de cancelar (enlace o instrucción) sin necesidad de llamar o de manual.
  3. Given un paciente que no puede acudir a una cita de la que le han mandado recordatorio, When pulsa el enlace de cancelar y confirma en la página que le aparece, Then su cita pasa a cancelada, el tramo queda libre y ve una confirmación clara en español de España.
  4. Given un paciente que pulsa el enlace de cancelar, When llega a la página de confirmación pero no confirma, Then su cita sigue reservada y el tramo sigue ocupado (no se cancela nada por haber solo visitado el enlace).
  5. Given un paciente cuya cita empieza en menos de 24 h, When pulsa el enlace de cancelar, Then ve «fuera de plazo» con el teléfono de la clínica y su cita sigue reservada.
  6. Given el recordatorio de una cita de «sesión fisio» de 40 €, When el paciente abre el correo, Then ve el precio congelado de la cita con el formato «40,00 €» de la 001 y ve el teléfono de la clínica; no ve referencia a ningún otro canal de contacto.
  7. Given un paciente con un recordatorio antiguo cuya cita se ha movido a dentro de 5 días, When pulsa el enlace de cancelar y confirma, Then su cita pasa a cancelada y el tramo queda libre: no hay tope superior de antelación mientras falten 24 h o más.

User Story 3 — Correo simulado sin SMTP: ficheros .eml revisables (Priority: P2)

Cuando no hay SMTP configurado, el sistema no intenta enviar a internet: escribe un fichero .eml por recordatorio en datos/salida-correo/, que recepción puede abrir y revisar como prueba de lo que el paciente habría recibido.

Why this priority: Permite demostrar y validar la feature en el piloto sin infraestructura de correo, manteniendo el coste a cero. Depende de US1/US2 porque necesita recordatorios que materializar.

Independent Test: Se puede probar por completo ejecutando el proceso diario sin SMTP configurado y comprobando que por cada recordatorio aparece un fichero .eml legible en datos/salida-correo/ con destinatario, asunto y cuerpo correctos. Aporta el modo demostrable de la feature.

Acceptance Scenarios:

  1. Given que no hay SMTP configurado, When se ejecuta el proceso diario y hay 3 citas en ventana, Then aparecen exactamente 3 ficheros .eml nuevos en datos/salida-correo/, uno por cita.
  2. Given un fichero .eml generado, When recepción lo abre con un lector de correo o editor de texto, Then ve el destinatario (email del paciente), un asunto comprensible y el cuerpo con los datos de la cita en español de España.
  3. Given que ya existe el .eml de una cita (recordatorio ya generado), When se re-ejecuta el proceso sin cambios, Then no se escribe ningún .eml duplicado para esa cita.
  4. Given una cita ya recordada que se mueve de tramo y vuelve a entrar en ventana, When se genera el nuevo recordatorio, Then aparece un .eml nuevo cuyo nombre lleva la fecha y hora del tramo nuevo, y el .eml del tramo anterior sigue en datos/salida-correo/ sin modificación alguna.
  5. Given dos recordatorios de la misma cita (tramo anterior y tramo vigente), When recepción revisa datos/salida-correo/, Then cada envío conserva su propio fichero y ninguno se ha sobrescrito.
  6. Given una cita en ventana cuyo paciente no tiene email válido, When se ejecuta el proceso diario, Then no se escribe ningún .eml para esa cita y la cita queda anotada en el informe de incidencias de correo.

Edge Cases

  • ¿Qué pasa si una cita se cancela o completa después de generar su recordatorio pero antes de su inicio? No se envía un segundo correo; el recordatorio ya generado queda como histórico y no se reintenta.
  • ¿Qué pasa si una cita en ventana no tiene email de paciente o el formato es inválido? No se genera recordatorio ni .eml para esa cita; el proceso continúa con el resto y la cita queda anotada en el informe de incidencias de correo, sin reintento posterior y sin tocar su estado ni su tramo (el alta estricta de email de la 001 hace este caso excepcional, limitado a datos previos).
  • ¿Qué pasa si el proceso diario se ejecuta dos veces seguidas (reintento, doble ejecución)? La segunda ejecución no genera duplicados: cada cita tiene como máximo un recordatorio por configuración recordada.
  • ¿Qué pasa si el proceso diario no se ejecuta un día (caída, fin de semana, sistema apagado)? No hay recuperación: la cita que ya superó las 48 h sin recordatorio no se recuerda después y no se envían correos a deshora. El hueco se acepta de forma consciente.
  • ¿Qué pasa con una cita que recepción reserva para dentro de menos de 24 h? Nunca entra en la ventana, por lo que no genera recordatorio en v1: la cita queda reservada, su tramo ocupado y visible en la agenda para que recepción la gestione por otros medios.
  • ¿Qué pasa con una cita de primera hora (p. ej. 00:30) o de última hora (p. ej. 23:00) con el proceso de las 00:00? Ambas entran en la ventana: a las 00:00 del día anterior, la cita está a 24 h y 30 min o a 47 h de su inicio, respectivamente. Ninguna cita se queda fuera de la ventana por su hora de inicio.
  • ¿Qué pasa si una cita ya recordada cambia de servicio (y con él su duración o su precio)? Se reenvía: el recordatorio nuevo lleva el nombre del servicio vigente y el precio congelado en la cita (001 FR-006), y el anterior queda como histórico.
  • ¿Qué pasa si recepción corrige el email de un paciente que ya tiene un recordatorio? Se reenvía al email correcto si la cita sigue en ventana; el .eml ya enviado queda como histórico y no se borra.
  • ¿Qué pasa con el paciente que no tiene email válido en su ficha? No se genera recordatorio ni .eml; la cita se anota en el informe de incidencias de correo con el motivo, no se reintenta en la siguiente ejecución y su estado de cita no cambia.
  • ¿Qué pasa con un paciente que ya tiene un recordatorio y al que recepción le cambia el email a uno que usa otra ficha? Cada cita se juzga por su propia configuración recordada: la cita con el email corregido genera su envío nuevo y el histórico se conserva, sin afectar a la otra cita. La comparación de emails usa la normalización compartida (specs/007-identidad-paciente/spec.md); esta feature MUST NOT definir la suya propia.
  • ¿Qué pasa si recepción consulta si una cita tiene recordatorio? Ve una señal legible en la ficha de la cita («Recordatorio enviado el 29/09/2026 a las 00:05» o «Recordatorio pendiente»), sin rutas, identificadores de envío ni nombres de fichero.
  • ¿Qué pasa si dos ejecuciones del proceso diario se solapan en el tiempo (lanzamiento concurrente)? Solo una puede registrar el envío de una cita dada; en ningún caso se generan dos recordatorios ni dos .eml para la misma cita y la misma configuración.
  • ¿Qué pasa si una cita ya recordada se mueve de día/hora? Se reenvía: el recordatorio anterior queda como histórico y se genera uno nuevo para el nuevo tramo si este cae en ventana y no tiene aún envío. Si el nuevo tramo queda fuera de ventana, no se envía nada.
  • ¿Qué pasa si una cita se mueve a un tramo que ya tenía un recordatorio emitido anteriormente (p. ej. se adelanta y luego se devuelve al día original)? No se duplica: una configuración ya recordada no se vuelve a recordar.
  • ¿Qué pasa si el paciente pulsa dos veces el enlace o reenvía el email a otra persona? La primera cancelación que se confirma deja la cita en cancelada; los intentos posteriores muestran un aviso claro de que la cita ya está cancelada y no cambian nada. Que el enlace no lleve verificación adicional es una deuda consciente temporal (ver FR-017 y Q9); el email MUST avisar de no reenviar el enlace.
  • ¿Qué pasa si el paciente cancela una cita que recepción ya había marcado como completada o no_asistida? Se rechaza con aviso claro: el enlace solo actúa sobre citas reservada (reglas de la 001).
  • ¿Qué pasa si la cita cae exactamente en el borde de la ventana (24 h o 48 h justos desde la ejecución)? Entra en la ventana: los bordes están incluidos, con precisión de minuto.
  • ¿Qué pasa si la cancelación se intenta cuando faltan exactamente 24 h para el inicio? Está dentro de plazo y se admite (borde incluido). Por debajo de 24 h, «fuera de plazo, llama a la clínica».
  • ¿Qué pasa si la cita se mueve mientras el paciente tiene el enlace abierto? El enlace apunta a la cita, no a un tramo fijo: la página muestra siempre los datos vigentes y, si ya no está en plazo, se aplica el mismo aviso de fuera de plazo.
  • ¿Qué pasa si el paciente ya vio la página de confirmación pero la clínica cancela la cita antes de que confirme? El intento de confirmar se rechaza con aviso claro y la cita queda como está.
  • ¿Qué pasa si hay cambio de hora estacional entre el envío y la cita? Las horas del email se muestran en hora local de la clínica en España con día, mes, año, hora y minutos sin ambigüedad, igual que la 001.
  • ¿Qué pasa con el aviso «fuera de plazo, llama a la clínica» si el paciente no tiene el teléfono de la clínica a mano? El email incluye el teléfono de la clínica (Q15 revisada, 001 FR-001) y el aviso de fuera de plazo lo muestra, para que el paciente pueda llamar sin buscar el contacto.

Requirements

Functional Requirements

  • FR-001 (ventana 24–48 h): El sistema MUST ejecutar un proceso diario que seleccione como candidatas todas las citas en estado reservada cuyo inicio caiga dentro de la ventana de 24 a 48 horas antes del inicio de la cita, con los bordes incluidos y precisión de minuto, evaluada en hora local de la clínica (Europe/Madrid). El sistema MUST NOT recuperar citas que ya hayan salido de la ventana sin ejecución: una cita cuyo inicio quede a menos de 24 h en el momento de la ejecución, incluidas las creadas por recepción con menos de 24 h de antelación, MUST NOT generar recordatorio.
  • FR-002: El sistema MUST excluir del envío toda cita que no esté en estado reservada en el momento de la ejecución (completada, cancelada y no_asistida MUST NOT generar recordatorio).
  • FR-003 (no duplicidad): El sistema MUST garantizar que cada cita recibe como máximo un recordatorio por configuración recordada, donde la configuración recordada es la cita completa: paciente (incluido el email vigente de su ficha, por ser el destinatario), profesional, servicio, inicio y fin. Si el proceso se re-ejecuta sin cambios, MUST NOT generar un segundo envío ni un segundo .eml. Cada envío MUST quedar registrado de forma que una re-ejecución lo detecte. Esta garantía MUST cumplirse también ante ejecuciones concurrentes del proceso (como máximo un envío por cita y configuración).
  • FR-004 (contenido mínimo del email): Cada recordatorio MUST incluir, en español de España y sin jerga técnica: nombre de la clínica, día y tramo horario con formato sin ambigüedad (p. ej. «30/09/2026, 10:00–10:45»), nombre del profesional, nombre del servicio, precio congelado de la cita con el formato exacto de la 001 («40,00 €», céntimos exactos y coma decimal, fuente 001 FR-006), teléfono de la clínica (Clínica.telefono, 001 FR-001) y una vía visible de cancelación. El email MUST avisar de no reenviar el enlace de cancelación. El destinatario MUST ser el email de la ficha del paciente (validación estricta heredada de la 001, normalización compartida en specs/007-identidad-paciente/spec.md). El email MUST NOT referenciar ningún otro canal de contacto.
  • FR-005 (asunto comprensible): El asunto del email MUST identificar que es un recordatorio de cita con la clínica y el día/hora (p. ej. «Recordatorio: tu cita en Eleva el 30/09/2026 a las 10:00»), en español de España.
  • FR-006 (cancelación con confirmación): El email MUST incluir un enlace de cancelación que no exija llamar a la clínica. Ese enlace MUST llevar a una página de confirmación en español de España que muestre los datos vigentes de la cita y en la que el paciente confirme expresamente la cancelación; solo esa confirmación MUST ejecutar el caso de uso único cancelarCita (001 FR-011; specs/005-cancelacion-paciente/spec.md), que pasa la cita a cancelada y libera el tramo. Esta feature MUST NOT implementar su propia transición. Hasta la confirmación, la cita MUST permanecer reservada y su tramo MUST permanecer ocupado.
  • FR-007 (plazo de cancelación): El sistema MUST admitir la cancelación desde el email únicamente mientras falten 24 horas o más para el inicio de la cita (borde de 24 h incluido, precisión de minuto), con independencia de cuándo se envió el recordatorio y de cuántas veces se haya movido la cita. El cómputo (instante «ahora», borde, precisión) es el del caso de uso único cancelarCita (001 FR-011); esta spec no lo redefine. El plazo MUST NOT tener tope superior: una cita a más de 48 h sigue siendo cancelable mientras falten 24 h o más. Fuera de ese plazo MUST rechazar la cancelación mostrando «fuera de plazo, llama a la clínica» con el teléfono de la clínica (001 FR-001) y MUST NOT modificar el estado de la cita.
  • FR-008 (cita movida — reenvío): Si una cita ya recordada cambia su paciente, su email, su profesional, su servicio o su tramo, el sistema MUST generar un nuevo recordatorio con los datos vigentes cuando la nueva configuración vuelva a caer en la ventana de 24–48 h y no tenga todavía un envío para ella. El recordatorio anterior MUST conservarse como histórico (no se borra ni se sobrescribe) y MUST NOT contar como el envío vigente. Si la nueva configuración no cae en ventana, MUST NOT enviarse nada.
  • FR-009 (cancelación sobre estados finales): El enlace de cancelación MUST operar solo sobre citas en estado reservada vía el caso de uso único cancelarCita (001 FR-011); MUST rechazarse con aviso claro en español de España cualquier intento sobre una cita ya cancelada, completada o no_asistida, sin modificar su estado. Los intentos repetidos MUST ser idempotentes: la segunda cancelación de la misma cita MUST mostrar un aviso de que la cita ya está cancelada y MUST NOT producir ningún efecto adicional.
  • FR-010 (modo simulado): Cuando no hay SMTP configurado, el sistema MUST NOT intentar enviar a internet; MUST escribir un fichero .eml por recordatorio en datos/salida-correo/ (un fichero por envío, contenido con cabeceras To/Subject/Date y cuerpo en español de España), con nombre determinista recordatorio-<citaId>-AAAAMMDD-HHMM.eml donde AAAAMMDD-HHMM es el inicio del tramo. Cada envío MUST tener su propio fichero y el sistema MUST NOT sobrescribir ni borrar el .eml de ningún envío anterior. La ausencia de SMTP MUST detectarse por configuración ausente, no por fallo de red enmascarado.
  • FR-011 (trazabilidad mínima): La ficha de la cita MUST mostrar una señal legible para recepción con el estado del recordatorio: «Recordatorio enviado el 29/09/2026 a las 00:05» si ya se generó, «Recordatorio pendiente» si aún no se ha generado, todo en español de España y sin jerga técnica. La señal MUST NOT exponer rutas, nombres de fichero ni identificadores técnicos de envío.
  • FR-012 (formato y lengua): Todos los textos del email, avisos y errores MUST estar en español de España. Todas las fechas y horas MUST mostrarse sin ambigüedad (día, mes, año, hora y minutos, hora local de la clínica en España); los importes MUST seguir la regla de la 001 (céntimos exactos, formato «40,00 €»).
  • FR-013 (alcance): El sistema MUST NOT implementar en esta feature envío por SMS ni WhatsApp, ni ningún otro canal distinto del email (simulado o SMTP posterior). Cualquier mención a esos canales en interfaces o textos se considera defecto.
  • FR-014 (convivencia con la 001): Las reglas de la 001 prevalecen sobre esta spec en caso de conflicto: solo reservada se recuerda; cancelada libera tramo (RN1); no_asistida solo desde reservada; ningún envío puede crear, mover ni solapar citas. Esta feature MUST NOT introducir solapes ni estados nuevos en la cita.
  • FR-015 (ejecución del proceso): El sistema MUST ejecutar el proceso diario automáticamente a las 00:00 hora local de la clínica (Europe/Madrid), con horario configurable de forma que siga cubriendo todas las citas del día siguiente. El sistema MUST ofrecer además un disparador manual bajo demanda con el mismo comportamiento y las mismas garantías, para que recepción pueda ejecutar y comprobar el proceso en el piloto sin esperar al horario.
  • FR-016 (incidencias de envío): Toda cita en ventana que no pueda recibir recordatorio por falta de email válido MUST quedar anotada en un informe de incidencias en fichero (fecha y hora, identificador de la cita y motivo), MUST NOT modificar el estado de la cita ni su tramo, y MUST NOT reintentarse en ejecuciones posteriores.
  • FR-017 (seguridad del enlace): En v1 el acceso a la cancelación se apoya en el enlace recibido en el email, sin verificación adicional de la identidad del paciente: deuda consciente temporal con caducidad (ver Q9). El email MUST avisar de no reenviar el enlace. La spec posterior que cierre la deuda MUST reutilizar la identidad verificada del portal (specs/007-identidad-paciente/spec.md) y ejecutar el caso de uso único cancelarCita (specs/005-cancelacion-paciente/spec.md).

Key Entities

  • Cita (heredada de la 001): Profesional + servicio + paciente con inicio, fin y estado. Solo el estado reservada es recordable. Relación: cada cita en ventana origina como máximo un recordatorio por configuración recordada.
  • Recordatorio / Envío: Hecho de haber avisado de una cita. Atributos: cita de referencia, configuración recordada (paciente, email de su ficha, profesional, servicio, inicio y fin), instante de generación, referencia al .eml y marca de si sigue vigente. Regla: uno por cita y configuración; la re-ejecución lo detecta y no duplica; los envíos de configuraciones anteriores se conservan como histórico.
  • Correo recordatorio (.eml en v1): Mensaje con destinatario (email de la ficha del paciente), asunto y cuerpo (datos de la cita, precio congelado en la cita, teléfono de la clínica y enlace de cancelación con aviso de no reenviarlo). En v1 vive como fichero en datos/salida-correo/ cuando no hay SMTP; no hay otros canales.
  • Solicitud de cancelación: Confirmación expresa del paciente de que no podrá acudir. Atributos: cita de referencia, instante de la confirmación, resultado (aplicada o rechazada por plazo o por estado). Regla: solo actúa sobre citas reservada con 24 h o más de antelación, sin tope superior; aplicada una vez, la cita queda cancelada y el tramo libre.
  • Informe de incidencias de correo: Registro en fichero de las citas en ventana que no pudieron recibir recordatorio por falta de email válido, con fecha y hora, identificador de cita y motivo. No modifica la agenda y no dispara reintentos.
  • Paciente (ficha, heredada de la 001): Aporta nombre y email de destino. Sin email válido no hay envío para sus citas, y el cambio de email genera un envío nuevo si la cita sigue en ventana.

Success Criteria

Measurable Outcomes

  • SC-001: Con la semilla de la 001 cargada, el proceso diario de las 00:00 genera exactamente un recordatorio por cada cita reservada en ventana y cero para las citas fuera de ventana o en estado final. Medición: recuento automatizado sobre las 2 semanas futuras de reservas (0 duplicados tolerados), comprobando además que ninguna cita reservada del día siguiente queda fuera de la ventana por su hora de inicio.
  • SC-002: Re-ejecutar el proceso diario N veces, de forma automática y mediante el disparador manual, sin cambios genera cero envíos adicionales y cero .eml adicionales. Medición: diff del directorio datos/salida-correo/ y del registro de envíos entre la primera y las siguientes ejecuciones.
  • SC-003: El 100 % de los .eml generados contiene clínica, día y tramo, profesional, servicio, precio con formato «40,00 €» y vía de cancelación, todo en español de España y con fecha/hora sin ambigüedad. Medición: inspección automatizada de contenido sobre una muestra de la semilla más revisión manual de un ejemplo.
  • SC-004: Un paciente que no puede venir consigue cancelar desde el email sin llamar y sin manual, con más de 24 h de antelación, y su tramo queda libre para una nueva reserva según la 001. Medición: protocolo manual extremo a extremo sobre una cita de prueba.
  • SC-005: Un paciente que cancela con menos de 24 h de antelación ve «fuera de plazo» con el teléfono de la clínica y su cita sigue reservada en el 100 % de los intentos. Medición: prueba automatizada con reloj controlado sobre una cita a 23 h y otra a 24 h justos.
  • SC-006: Sin SMTP configurado no se intenta ningún envío de red: el 100 % de los recordatorios aparece como .eml en datos/salida-correo/ y cero mensajes salen al exterior. Medición: ejecución en entorno sin credenciales SMTP con red observada.
  • SC-007: Cero envíos por SMS/WhatsApp u otros canales en esta feature: ninguna dependencia, pantalla o texto los ofrece. Medición: revisión de alcance (grep + revisión manual).
  • SC-008: El número de ficheros en datos/salida-correo/ es igual al número de envíos registrados, en toda la vida del sistema: ningún .eml se sobrescribe ni se borra cuando una cita cambia de tramo. Medición: recuento de ficheros frente a recuento de envíos tras una secuencia de movimientos de cita.
  • SC-009: El 100 % de las citas en ventana sin email válido aparecen en el informe de incidencias de correo con su motivo, y ninguna de ellas genera .eml ni cambia de estado. Medición: prueba automatizada con un paciente sin email en la semilla.

Assumptions

  • El proceso es diario y opera sobre la hora local de la clínica (Europe/Madrid); se ejecuta a las 00:00 por defecto con horario configurable, de modo que la ventana de aviso de 24–48 h cubre exactamente las citas del día siguiente sea cual sea su hora. El plazo de cancelación desde el email es de 24 h antes del inicio, sin tope superior (decisiones de Sara, 2026-09-29 y 2026-09-30).
  • Se aceptan de forma consciente dos huecos de cobertura, sin añadir mecanismo para cerrarlos: (a) no hay recuperación cuando el proceso no se ejecuta y la cita ya superó las 48 h; (b) una cita creada con menos de 24 h de antelación no se recuerda. Ninguno de los dos huecos altera el estado de la cita ni su tramo.
  • La configuración recordada de una cita es la cita completa (paciente, profesional, servicio, inicio y fin); el email vigente del paciente forma parte de ella porque es el destinatario, de modo que corregirlo genera un envío nuevo si la cita sigue en ventana.
  • Se acepta como deuda consciente temporal (con caducidad, ver Q9 y FR-017) que la cancelación se apoye solo en el enlace del email: quien reciba el correo reenviado puede cancelar la cita de otra persona. Se sustituirá por la identidad verificada del portal (specs/007-identidad-paciente/spec.md) ejecutando el caso de uso único (specs/005-cancelacion-paciente/spec.md) en una spec posterior sin cambiar el comportamiento observable descrito aquí.
  • Se reutilizan la semilla determinista, los formatos de fecha/importe y las reglas de estado de la 001; esta spec no redefine agenda, precios ni auth. El teléfono de la clínica se toma de la 001 (001 FR-001; Q15 revisada jul2026) y el precio mostrado es el congelado en la cita (001 FR-006).
  • En la 001 el email del paciente es obligatorio y validado; los casos sin email solo pueden venir de datos previos y se tratan como «sin envío + anotación en el informe de incidencias», sin reintento posterior.
  • datos/salida-correo/ es el destino del modo simulado pedido explícitamente en el encargo, con un fichero por envío y nombre determinista por tramo; la ruta concreta del informe de incidencias, el transporte SMTP real, la plantilla (HTML o texto plano), los detalles técnicos del enlace (firma, caducidad, antiflood) y el mecanismo concreto del disparador manual se deciden en el plan, sin cambiar el comportamiento observable de esta spec.
  • SMS y WhatsApp quedan fuera de la v1 por decisión explícita del encargo; si el piloto los pide, van en spec propia posterior.
  • La cancelación desde el email actúa sobre citas reservada según la máquina de estados de la 001 y exige confirmación expresa del paciente antes de liberar el tramo; visitar el enlace sin confirmar no libera nada.