Feature Specification: Núcleo de agenda de CitaClara
Feature Branch: 001-nucleo-agenda
Created: 2026-09-29
Status: Draft
Input: User description: "Núcleo de agenda de CitaClara. Contexto: clínicas pequeñas (2-5 profesionales); la recepción gestiona la agenda; los pacientes, de momento, solo existen como fichas. Alcance de la 001: Entidades: clínica (con clave de panel; auth simplificada v1, deuda consciente), profesionales (nombre, especialidad), servicios (nombre, duración en minutos, precio en euros), pacientes (nombre, teléfono, email) y citas. Una cita une profesional + servicio + paciente con inicio y fin (fin = inicio + duración del servicio). Estados: reservada → completada | cancelada | no_asistida. "No asistida" significa exclusivamente que el paciente no se presentó a una cita que seguía reservada. Semilla determinista según constitución p.5: Clínica Eleva, 3 profesionales (María y Jorge, fisioterapia; Lucía, nutrición), 4 servicios (sesión fisio 45' 40 €, primera visita fisio 60' 50 €, consulta nutrición 30' 35 €, primera nutrición 45' 45 €), ~40 pacientes, 8 semanas de historia con ~10 % de no asistencia y ~8 % de cancelaciones, 2 semanas futuras con reservas. Fuera de alcance de la 001 (van en specs propias): cualquier acceso del paciente, recordatorios, analítica, pagos online."
Clarifications
Session 2026-09-29
- Q: ¿Qué campos son obligatorios y únicos al registrar un paciente como ficha en la 001? → A: Los tres obligatorios y con validación estricta de formato (nombre, teléfono y email siempre exigidos y validados).
- Q: ¿Qué franja horaria debe mostrar la agenda del día por profesional en la 001? → A: Jornada configurable por clínica, por defecto 09:00–20:00.
- Q: ¿Se pueden editar o eliminar profesionales, servicios o pacientes que ya tienen citas asociadas en la 001? → A: Prohibido eliminar fichas con citas; solo editar nombre/contacto sin romper citas.
- Q: ¿Cómo debe comportarse el acceso con clave de clínica ante intentos fallidos repetidos en la 001? → A: Sin límite ni bloqueo; cada fallo muestra aviso genérico en español.
- Q: ¿Qué reglas de unicidad aplican a profesionales, servicios y pacientes en la 001? → A: Solo nombre de servicio único por clínica; resto admite duplicados.
User Scenarios & Testing
User Story 1 — Agenda del día por profesional (Priority: P1)
La persona de recepción elige un día y un profesional y ve de un vistazo qué huecos están ocupados (con la cita correspondiente) y qué huecos están libres, para informar en mostrador o por teléfono y decidir dónde colocar una nueva cita.
Why this priority: Es el uso central diario de la recepción. Sin esta vista no hay gestión de agenda posible; todo lo demás (alta, desenlaces) depende de ella.
Independent Test: Se puede probar por completo abriendo el panel con los datos de demostración, eligiendo un día y cada profesional, y comprobando que las citas de ese día y profesional aparecen en su tramo horario y que los huecos sin cita se distinguen como libres. Aporta valor por sí sola aunque aún no se pueda crear ni cerrar citas.
Acceptance Scenarios:
- Given la recepción ha accedido con la clave de la clínica y hay citas el día D para la profesional María, When consulta la agenda del día D de María, Then ve cada cita de María de ese día situada en su tramo (hora de inicio y fin) con servicio y paciente identificables, y ve el resto del horario como libre.
- Given un día sin citas para el profesional Jorge, When consulta su agenda de ese día, Then ve la jornada como libre, sin citas mostradas y sin errores.
- Given la agenda del día D, When la consulta desde un portátil de recepción y desde un móvil, Then en ambos casos la información es legible, sin jerga técnica, con contraste y tamaño de letra accesibles, y no necesita manual para entender qué está ocupado y qué está libre.
User Story 2 — Alta de cita sin solapes ni pasado (Priority: P1)
La persona de recepción crea una cita eligiendo profesional, servicio, paciente, día y hora de inicio. El sistema calcula el fin (inicio + duración del servicio) y solo registra la cita si no solapa otra cita vigente del mismo profesional y si no está en el pasado.
Why this priority: Es la operación que hace crecer la agenda y donde vive la regla capital (RN1 cero solapes) y RN2 (no pasado). Sin alta fiable no hay producto.
Independent Test: Se puede probar por completo intentando crear citas válidas, citas solapadas y citas en el pasado, y comprobando cuáles quedan registradas y cuáles se rechazan con un mensaje claro en español de España. Entrega valor aunque aún no existan las acciones de desenlace.
Acceptance Scenarios:
- Given un hueco libre el día D de 10:00 a 10:45 para Lucía, When la recepción crea una cita de «consulta nutrición» (30 min) a las 10:00 para un paciente existente, Then la cita queda registrada como
reservadacon inicio 10:00 y fin 10:30, y aparece en la agenda del día. - Given una cita reservada de María de 10:00 a 10:45, When la recepción intenta crear otra cita de María que solape aunque sea parcialmente (p. ej. 10:30–11:00), Then la segunda cita no se registra y se muestra un aviso claro de que ese tramo ya está ocupado.
- Given dos intentos simultáneos de reservar el mismo hueco del mismo profesional, When llegan a la vez, Then solo una de las dos citas queda registrada y la otra se rechaza como ocupada, sin que existan nunca dos citas vigentes solapadas del mismo profesional.
- Given cualquier fecha y hora ya pasada, When la recepción intenta crear una cita con inicio en el pasado, Then la cita no se registra y se muestra un aviso claro de que no se pueden crear citas en el pasado.
User Story 3 — Registrar el desenlace de la cita (Priority: P2)
La persona de recepción marca lo que pasó con cada cita reservada: se completó, se canceló o el paciente no se presentó. «No asistida» solo se aplica a una cita que seguía reservada y a la que el paciente no acudió.
Why this priority: Cierra el ciclo diario (lo que pasó hoy queda reflejado) y alimenta la historia de 8 semanas con sus porcentajes de no asistencia y cancelación. Depende de US1/US2 porque necesita citas que ver y crear.
Independent Test: Se puede probar por completo tomando una cita en estado reservada y marcándola como completada, cancelada o no_asistida, y comprobando que el estado final es el esperado y que las transiciones ilegales se rechazan. Entrega el cierre operativo de la agenda.
Acceptance Scenarios:
- Given una cita en estado
reservada, When la recepción la marca comocompletada, Then la cita pasa acompletaday deja de poder cambiarse a ningún otro estado desde recepción. - Given una cita en estado
reservada, When la recepción la marca comocancelada, Then la cita pasa acancelada, libera su tramo (ese tramo ya no bloquea nuevas reservas) y no admite más cambios. - Given una cita en estado
reservadaa la que el paciente no acudió, When la recepción la marca comono_asistida, Then la cita pasa ano_asistiday no admite más cambios. - Given una cita ya en estado final (
completada,canceladaono_asistida), When se intenta cambiarla a cualquier otro estado, Then el cambio se rechaza con un aviso claro.
User Story 4 — Entrar con la clave de la clínica y partir de datos conocidos (Priority: P3)
La persona de recepción accede al panel introduciendo la clave de su clínica (auth simplificada v1, deuda consciente) y trabaja siempre sobre datos conocidos y reproducibles (Clínica Eleva y su semilla), con textos e importes en formato español de España.
Why this priority: Es la puerta de entrada y la base de demostración, pero no aporta valor asistencial por sí sola; por eso va después del ciclo ver → crear → cerrar.
Independent Test: Se puede probar por completo entrando con la clave correcta, entrando con una clave incorrecta y comprobando que los datos iniciales (profesionales, servicios, pacientes y citas de ejemplo) son siempre los mismos al regenerar la semilla. Aporta confianza aunque no modifica la agenda.
Acceptance Scenarios:
- Given la pantalla de acceso al panel, When la recepción introduce la clave correcta de su clínica, Then entra en la agenda y opera con normalidad.
- Given la pantalla de acceso al panel, When introduce una clave incorrecta, Then no entra y ve un aviso claro en español de España, sin revelar información sobre claves válidas.
- Given una instalación nueva, When se cargan los datos de demostración, Then existen la Clínica Eleva, sus 3 profesionales, sus 4 servicios y sus pacientes de ejemplo con los nombres, especialidades, duraciones y precios previstos, y la historia y las reservas futuras descritas en esta spec.
Edge Cases
- ¿Qué pasa si una cita termina exactamente cuando empieza otra del mismo profesional (p. ej. 10:00–10:45 y 10:45–11:30)? No es solape: los tramos contiguos están permitidos; solo solapa si hay al menos un minuto común.
- ¿Qué pasa si se intenta crear una cita a caballo entre dos días o con inicio válido pero fin en otro día? Se rechaza si el tramo no pertenece íntegramente al día gestionado; la recepción ve un aviso claro.
- ¿Cómo se comporta el sistema si el servicio cambia de duración después de crear citas? Las citas ya creadas conservan su inicio y fin originales; solo las nuevas usan la duración vigente.
- ¿Qué pasa si se marca como
no_asistidauna cita yacompletadaocancelada? Se rechaza:no_asistidasolo se aplica desdereservada. - ¿Qué pasa si una cita
canceladadeja hueco libre? Ese tramo vuelve a estar disponible para nuevas reservas del mismo profesional. - ¿Qué pasa si el reloj del sistema y el de recepción difieren ligeramente al validar «pasado»? La comparación usa un único instante de referencia del sistema («ahora») documentado en la spec; cualquier inicio estrictamente anterior a «ahora» se considera pasado.
- ¿Qué pasa si el nombre del paciente tiene tildes, ñ o apellidos compuestos? Se guardan y se muestran tal cual, sin alteraciones.
- ¿Qué pasa si se consulta la agenda de un profesional en un día con cambio de hora estacional? Las horas se muestran en hora local de la clínica en España (Europe/Madrid) sin ambigüedad (día, mes, año, hora y minutos).
Requirements
Functional Requirements
- FR-001: El sistema MUST soportar en el modelo de datos una o más clínicas, cada una con nombre, teléfono de contacto obligatorio y una clave de panel que da acceso a su agenda. El teléfono se muestra en los avisos de fuera de plazo del portal y de los recordatorios (enmienda S-01 aceptada de la revisión cruzada
000-revision-cruzada-jul2026.md). En la 001 solo opera la Clínica Eleva de la semilla; no se implementa CRUD de clínicas en esta feature. - FR-002: El sistema MUST exigir la clave de la clínica para entrar al panel de recepción (auth simplificada v1, deuda consciente registrada en Supuestos); con clave incorrecta el acceso MUST denegarse con un aviso genérico en español de España, sin límite de intentos ni bloqueo.
- FR-003: El sistema MUST permitir registrar profesionales con nombre y especialidad, y servicios con nombre, duración en minutos (entero positivo) y precio en euros con céntimos. El nombre del servicio MUST ser único por clínica (MUST rechazarse duplicados con aviso claro); los nombres de profesionales y las fichas de pacientes admiten duplicados. El sistema MUST NOT permitir eliminar ninguna ficha (profesional, servicio o paciente) que tenga citas asociadas (la desactivación mediante bandera
activo=falsees el mecanismo permitido en lugar del borrado); MUST permitir editar datos no estructurales (p. ej. nombre, contacto) sin romper las citas existentes. - FR-004: El sistema MUST exigir al registrar un paciente como ficha los tres campos obligatorios — nombre, teléfono y correo electrónico — con validación estricta de formato español y sin exigencia de unicidad (se admiten duplicados); MUST rechazar el alta si falta alguno o el formato es inválido, con aviso claro en español de España.
- FR-005: El sistema MUST permitir crear una cita que une exactamente un profesional, un servicio y un paciente existentes, con fecha y hora de inicio elegidas por recepción.
- FR-006: El sistema MUST calcular el fin de la cita como inicio + duración vigente del servicio en el momento de la creación, y MUST guardar inicio y fin juntos con la cita. El sistema MUST copiar además el precio vigente del servicio en la cita como precio congelado (céntimos enteros) en el momento de la reserva; ese precio congelado MUST NOT cambiar aunque la tarifa del servicio cambie después (enmienda S-05 aceptada de la revisión cruzada
000-revision-cruzada-jul2026.md; fuente única para recordatorios y analítica). - FR-007 (RN1 — capital): El sistema MUST impedir que se registre una cita que solape con otra cita del mismo profesional en estado
reservadaocompletada. Solape significa cualquier intersección de rangos[inicio, fin); todas las entradas se truncan a precisión de minuto en escritura, por lo que en la práctica equivale a «al menos un minuto común». Los tramos contiguos (fin = inicio) no son solape. Las citascanceladayno_asistidano bloquean nuevos solapes. - FR-008 (RN1 concurrente): Si dos peticiones de reserva del mismo profesional y tramo solapado llegan a la vez, el sistema MUST registrar como máximo una de ellas; la otra MUST rechazarse como hueco ocupado. En ningún caso pueden quedar dos citas vigentes solapadas del mismo profesional.
- FR-009 (RN2): El sistema MUST rechazar toda creación de cita cuyo inicio sea anterior al instante de referencia «ahora» del sistema, con un aviso claro en español de España.
- FR-010: Toda cita nueva MUST nacer en estado
reservada. - FR-011: El sistema MUST permitir cambiar una cita
reservadaacompletada, acanceladao ano_asistida, y MUST rechazar cualquier otro cambio de estado (incluido cualquier cambio desde un estado final y cualquier cambio directo entre estados finales). La cancelación iniciada por el propio paciente (portal o enlace de recordatorio) MUST ejecutarse a través del caso de uso únicocancelarCita(specs/005-cancelacion-paciente/spec.md): mismas reglas de estados, antelación de 24 h con borde incluido y precisión de minuto, idempotencia («ya cancelada») y liberación del tramo; ninguna feature implementa su propia transición. - FR-012: El estado
no_asistidaMUST significar exclusivamente «el paciente no se presentó a una cita que seguía reservada»; el sistema MUST rechazar marcar comono_asistidacualquier cita que no esté en estadoreservada. - FR-013: El sistema MUST mostrar la «agenda del día» filtrada por profesional y día dentro de la jornada configurable de la clínica (horario por defecto 09:00–20:00 y días de apertura por defecto Lun–Vie): cada cita vigente o final de ese profesional y día en su tramo horario, y el resto de la jornada visiblemente como libre. El sistema MUST rechazar con aviso claro en español de España toda creación de cita cuyo tramo
[inicio, fin)no pertenezca íntegramente a un único día dentro de la jornada vigente de la clínica (422 FUERA_JORNADA/DIA_PARTIDO); la reconfiguración de la jornada se hace vía semilla en la 001 (endpoint de gestión de jornada fuera de alcance, ver T052). - FR-014: La agenda del día MUST mostrar por cada cita al menos el tramo (inicio–fin), el servicio y el paciente, y el estado, con lenguaje de clínica pequeña y sin jerga técnica.
- FR-015: La interfaz de recepción (agenda, alta y acciones de estado) MUST ser moderna, limpia y responsive: legible y operable en portátil de recepción y en móvil, con contraste y tamaños de letra accesibles.
- FR-016: Todos los textos visibles, avisos y errores MUST estar en español de España.
- FR-017 (exactitud — dinero): Todos los precios y totales MUST cuadrar al céntimo. Regla de redondeo: los precios se guardan y operan en céntimos enteros; si alguna operación futura generase fracciones de céntimo, se redondea al céntimo más cercano (mitad hacia arriba). Formato de visualización: euros con dos decimales y símbolo € (p. ej. «40,00 €»). Ejemplo límite: una «sesión fisio» de 40,00 € se muestra como «40,00 €», nunca como «40 €» ni «40.00€».
- FR-018 (exactitud — tiempo): Toda fecha y hora visible MUST mostrarse sin ambigüedad para una clínica española: día, mes, año, hora y minutos (p. ej. «29/09/2026, 10:00–10:45»). La duración se expresa en minutos. Ejemplo límite: una cita de 45 min que empieza a las 10:00 termina a las 10:45 del mismo día.
- FR-019 (semilla determinista): El sistema MUST incluir un juego de demostración determinista y versionado («misma semilla, misma historia») con el contenido siguiente:
- Clínica «Eleva» (con teléfono de contacto obligatorio y días de apertura Lun–Vie incluidos en la semilla).
- 3 profesionales: María (fisioterapia), Jorge (fisioterapia), Lucía (nutrición).
- 4 servicios: «sesión fisio» 45 min 40,00 €; «primera visita fisio» 60 min 50,00 €; «consulta nutrición» 30 min 35,00 €; «primera nutrición» 45 min 45,00 €.
- Unos 40 pacientes de ejemplo (38–42, tolerancia ±2) con nombre, teléfono y correo españoles verosímiles.
- 8 semanas de historia pasada con mezcla de
completada(82 %),no_asistida(10 %) ycancelada(~8 %), y 2 semanas futuras solo con citasreservada. - Regenerar la semilla MUST producir exactamente los mismos profesionales, servicios, pacientes y citas (mismos tramos, estados y repartos).
- FR-020 (alcance): Quedan fuera de la 001 y MUST NOT implementarse en esta feature: cualquier acceso o acción del paciente, recordatorios, analítica o informes, y pagos online.
Key Entities
- Clínica: Consulta que usa CitaClara. Atributos: nombre, teléfono de contacto obligatorio (mostrado en los avisos de fuera de plazo), clave de panel (auth simplificada v1), jornada configurable (horario por defecto 09:00–20:00 y días de apertura
diasLaborablespor defecto Lun–Vie; enmienda S-06 pedida por 006-tiempo-referencia) que delimita la agenda del día. Relación: posee profesionales, servicios, pacientes y citas. - Profesional: Persona sanitaria que atiende citas (2–5 por clínica en la realidad; 3 en la semilla: María y Jorge de fisioterapia, Lucía de nutrición). Atributos: nombre, especialidad. Relación: protagoniza sus citas; la regla antisolape se aplica por profesional.
- Servicio: Prestación ofrecida (p. ej. sesión fisio). Atributos: nombre (único por clínica), duración en minutos, precio en euros con céntimos. Relación: cada cita referencia un servicio que determina su duración; el precio vigente del servicio se copia a la cita como precio congelado al reservar (FR-006).
- Paciente (ficha): Persona atendida; en la 001 solo existe como ficha gestionada por recepción, sin acceso propio. Atributos obligatorios: nombre, teléfono y correo electrónico (los tres exigidos y con validación estricta de formato español). Relación: cada cita referencia un paciente.
- Cita: Encuentro reservado. Atributos: profesional, servicio, paciente, inicio, fin (= inicio + duración del servicio), precio congelado (= precio del servicio al reservar, inmutable, FR-006), estado (
reservada,completada,cancelada,no_asistida). Reglas: nacereservada; solo pasa a un estado final;no_asistidasolo desdereservadapor incomparecencia; RN1 antisolape frente areservada/completadadel mismo profesional (incluido caso concurrente); RN2 sin inicios en el pasado. Las proyecciones de lectura («En curso»,reservadacon fin pasado) no son estados: unareservadacon fin pasado sigue siendoreservada(cita sin desenlace).
Success Criteria
Measurable Outcomes
- SC-001: La recepción consulta la agenda de cualquier profesional y día y distingue en menos de 10 segundos qué tramos están ocupados y cuáles libres, sin formación ni manual. Medición: protocolo manual con cronómetro sobre Playwright en 1440px y 390px (ver
e2e/agenda-dia.spec.ts); no es aserción automática de tiempo. - SC-002: El 100 % de los intentos de crear citas solapadas (incluidos intentos simultáneos del mismo hueco) resulta en como máximo una cita registrada; el resto se rechaza con aviso claro. Cero solapes vigentes del mismo profesional en cualquier verificación.
- SC-003: El 100 % de los intentos de crear citas con inicio en el pasado se rechaza con aviso claro; ninguna cita registrada tiene inicio anterior a su instante de creación.
- SC-004: La recepción completa el alta de una cita válida en menos de 1 minuto y marca el desenlace (completada / cancelada / no asistida) en menos de 15 segundos por cita. Medición: protocolo manual con cronómetro sobre los e2e
alta-citaydesenlace; no es aserción automática de tiempo. - SC-005: Regenerar la semilla reproduce exactamente la misma historia (mismos pacientes, tramos, estados y repartos ~10 % no asistencia y ~8 % cancelaciones) en el 100 % de las regeneraciones.
- SC-006: La agenda, el alta y las acciones funcionan y son legibles tanto en portátil como en móvil, y todos los textos, importes (p. ej. «40,00 €») y fechas (p. ej. «29/09/2026, 10:00») aparecen en formato español de España.
Assumptions
- Auth simplificada v1 como deuda consciente: una única clave compartida por clínica para todo el personal de recepción; sin usuarios individuales, roles, caducidad ni bloqueo tras intentos fallidos en la 001. Se sustituirá por autenticación individual en una spec posterior sin romper las reglas de agenda.
- Horario de visualización: la agenda del día muestra la jornada configurable de la clínica (horario por defecto 09:00–20:00, días de apertura por defecto Lun–Vie) con los tramos ocupados y libres; las citas solo pueden crearse dentro de esa jornada salvo reconfiguración explícita.
- Alta de cita: solo con profesional, servicio y paciente ya existentes; la creación de profesionales, servicios y pacientes se asume disponible como gestión básica de fichas (no es el foco de la 001, pero es necesaria para el alta). La gestión básica MUST NOT incluir eliminar fichas con citas asociadas.
- Instante «ahora» para RN2: instante único del sistema en el momento de validar; un inicio estrictamente anterior se considera pasado, con precisión de minuto.
- Zona horaria: hora local de la clínica en España (Europe/Madrid, nombre canónico para tiempo y ventanas compartidas en
specs/006-tiempo-referencia/spec.md); los ejemplos usan formato día/mes/año y hora de 24 h. - Precios: importes fijos por servicio en euros con dos decimales; en la 001 no hay descuentos, impuestos desglosados ni pagos (van en specs propias).
- Semilla: los ~40 pacientes y los porcentajes (10 % no asistencia, 8 % cancelaciones) admiten una tolerancia de redondeo de ±2 puntos o ±2 pacientes al regenerar, siempre que la regeneración sea bit a bit determinista; las 8 semanas son las 8 anteriores a la fecha de carga y las 2 futuras las 2 siguientes.
- Fuera de alcance confirmado: acceso del paciente, recordatorios, analítica e informes, y pagos online van en specs propias y no se implementan aquí.