Tasks: Identidad compartida del paciente (007)
Input: Design documents from /specs/007-identidad-paciente/ (spec.md, plan.md, research.md, data-model.md, contracts/identidad.md, quickstart.md)
Prerequisites: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/
Tests: La spec exige matriz de variantes equivalentes, gemelas denegadas sin fugas, sesión que caduca/cierra y enlace que solo opera sobre su cita (SC-001…SC-004) y la constitución VI exige test trazable por regla. Se incluyen tareas de test obligatorias.
Organization: Tasks are grouped by user story to enable independent implementation and testing of each story.
Format: [ID] [P?] [Story] Description
- [P]: Can run in parallel (different files, no dependencies)
- [Story]: Which user story this task belongs to (e.g., US1, US2, US3)
- Include exact file paths in descriptions
Path Conventions
- Monolito Next.js:
app/,lib/,prisma/,tests/en la raíz (tema5/citaclara/)
Phase 1: Setup (Shared Infrastructure)
Purpose: Verificación del punto de partida (sin migración en esta feature)
- T001 Verificar esquema actual y ausencia de tablas/columnas nuevas necesarias en
prisma/schema.prisma(se usaPacientede la 001 tal cual, FR-009) - T002 [P] Verificar catálogos y patrones reutilizables en
lib/auth.ts(firma HMAC),lib/validacion.ts(ESQUEMA_EMAIL,ESQUEMA_TELEFONO_ES) ylib/cancelacion.ts(catálogoENLACE_INVALIDO)
Phase 2: Foundational (Blocking Prerequisites)
Purpose: Normalización única, resolución y avisos que bloquean todas las historias
⚠️ CRITICAL: No user story work can begin until this phase is complete
- T003 Implementar
normalizarCorreo,normalizarTelefono, validadores yAVISO_IDENTIDAD_*enlib/identidad.ts(FR-002, FR-008; Q1/Q5) - T004 Implementar
resolverPacientePorCredenciales(incorrecto/duplicado/ok, comparación en memoria, sin enumerar fichas) enlib/identidad.ts(FR-001, FR-003, FR-004) - T005 [P] Test unitario de la matriz de normalización y resolución en
tests/unit/test_identidad.test.ts(variantes US1 + no-válidos como «incorrecto» + gemelas como «duplicado») — debe FALLAR antes de T003/T004
Checkpoint: Foundation ready - user story implementation can now begin in parallel
Phase 3: User Story 1 - Una sola normalización para todos los canales (Priority: P1) 🎯 MVP
Goal: Portal y recordatorios resuelven con el mismo algoritmo; las variantes equivalentes dan la misma ficha y las no válidas son «incorrecto».
Independent Test: Matriz de variantes (mayúsculas, espacios, +34, 0034, guiones) sobre una ficha conocida resuelve a la misma ficha en portal y en recordatorios; las no válidas dan el aviso único.
Tests for User Story 1 ⚠️
NOTE: Write these tests FIRST, ensure they FAIL before implementation
- T006 [US1] Test de resolución portal + recordatorio con la matriz de variantes en
tests/integration/test_identidad_canales.test.ts(misma ficha en ambos canales, no-válidas como «incorrecto»)
Implementation for User Story 1
- T007 [US1] Implementar
POST /api/portal/sesion/route.tsusandoresolverPacientePorCredencialesdelib/identidad.ts(sin normalización propia; portal importa el algoritmo único) - T008 [US1] Exponer
decidirEnvioRecordatorioenlib/identidad.ts(envuelve aresolverPacientePorCredencialespara recordatorios; sin algoritmo propio; verificación por grep SC-005)
Checkpoint: At this point, User Story 1 should be fully functional and testable independently
Phase 4: User Story 2 - Duplicados que nunca filtran entre fichas (Priority: P1)
Goal: Correo + teléfono ambiguo deniega en todos los canales sin mostrar ni mutar nada y sin enumerar fichas; recordatorios no envían y anotan duplicado.
Independent Test: Dos fichas gemelas → portal deniega sin citas, enlace deniega, recordatorios no envían y queda incidencia duplicado.
Tests for User Story 2 ⚠️
- T009 [US2] Test de gemelas en
tests/integration/test_identidad_canales.test.ts(portal 409 sin citas + enlace denegado + cero envíos con incidenciaduplicado; mismo fichero que T006: sin marcador [P])
Implementation for User Story 2
- T010 [US2] Cubrir denegación por duplicado sin fugas en
app/api/portal/sesion/route.tsylib/enlace-verificado.ts(aviso FR-004 + teléfono, sin mostrar ni cancelar ninguna cita) - T011 [US2] Cubrir rama
duplicado/incorrectosin envío víadecidirEnvioRecordatorioenlib/identidad.ts(devuelveenviar: false+ motivo para el informe de incidencias)
Checkpoint: At this point, User Stories 1 AND 2 should both work independently
Phase 5: User Story 3 - Sesión que caduca y cierra de verdad (Priority: P2)
Goal: Cookie firmada con 30 min deslizantes, «Cerrar sesión» instantáneo, sin persistencia más allá de la cookie.
Independent Test: 30 min sin actividad exige reidentificación; cerrar sesión invalida el token al instante; cero restos en navegador.
Tests for User Story 3 ⚠️
- T012 [P] [US3] Test unitario de sesión en
tests/unit/test_sesion_paciente.test.ts(caducidad 30 min, renovación con uso, cierre instantáneo, firma inválida rechazada) - T013 [US3] Test de rutas de sesión en
tests/integration/test_identidad_canales.test.ts(cookiecitaclara_portal, cierre limpia cookie, token reutilizado rechazado; mismo fichero que T006/T009: sin [P])
Implementation for User Story 3
- T014 [US3] Implementar
crearSesionPortal,verificarSesionPortal,renovarSesionPortalycerrarSesionPortalenlib/sesion-paciente.ts(HMAC + Map en memoria + limpieza perezosa, FR-005) - T015 [US3] Implementar
DELETE /api/portal/sesion/route.tsy emisión/renovación de cookie enapp/api/portal/sesion/route.ts(httpOnly,SameSite=Lax,Max-Age=1800, sinlocalStorage)
Checkpoint: All user stories should now be independently functional
Phase 6: User Story 4 - Enlace verificado que cierra la deuda (Priority: P2)
Goal: Token firmado citaId + pacienteId con caducidad min(inicio, envío+72h) y antiflood 10/h que ejecuta cancelarCita solo sobre su cita.
Independent Test: Enlace vigente sin sesión opera solo sobre su cita; caducado o con flood se rechaza sin mutar.
Tests for User Story 4 ⚠️
- T016 [P] [US4] Test unitario de enlace en
tests/unit/test_enlace.test.ts(vigente verifica, caducado por inicio y por 72 h rechaza, token de otra cita no cruza, >10/h bloquea) - T017 [US4] Test de extremo a extremo del enlace contra
cancelarCitaentests/integration/test_identidad_canales.test.ts(vigente aplica solo su cita, caducado/flood/ajeno no mutan; mismo fichero: sin [P])
Implementation for User Story 4
- T018 [US4] Implementar
crearEnlaceVerificado,verificarEnlaceyregistrarIntentoEnlaceenlib/enlace-verificado.ts(firma HMAC, caducidadmin(inicio, +72h), antiflood en memoria, FR-006) - T019 [US4] Aceptar
tokende enlace verificado paraorigen: emailenapp/api/citas/[id]/cancelar/route.ts(verifica firma/caducidad/flood/coincidenciacitaId, derivapacienteIdVerificadoaejecutarCancelarCita; sin transición propia)
Checkpoint: All user stories should now be independently functional
Phase 7: Polish & Cross-Cutting Concerns
Purpose: Puertas de merge de la constitución y la spec
- T020 [P] Validar
quickstart.mdde la 007 (matriz US1, gemelas US2, caducidad/cierre US3, vigente/caducado/flood/ajeno US4, grep SC-005) - T021 [P] Verificar SC-005 por grep: unicidad de normalización en
lib/identidad.ts, ceromerge/fusión, ningún canal con avisos propios fuera del catálogo - T022 [P] Pasar
npm run typecheck,npm run lint,npm testynpm run comprobar:esen verde
Dependencies & Execution Order
Phase Dependencies
- Setup (Phase 1): No dependencies - can start immediately
- Foundational (Phase 2): Depends on Setup completion - BLOCKS all user stories
- User Stories (Phase 3+): All depend on Foundational phase completion
- User stories can then proceed in parallel (if staffed)
- Or sequentially in priority order (P1 → P2 → P3)
- Polish (Final Phase): Depends on all desired user stories being complete
User Story Dependencies
- User Story 1 (P1): Can start after Foundational (Phase 2) - No dependencies on other stories
- User Story 2 (P1): Can start after Foundational (Phase 2) - May integrate with US1 but should be independently testable
- User Story 3 (P2): Depends on Foundational + US1/US2 (sesión presupone identidad); independently testable con reloj controlado
- User Story 4 (P2): Depends on Foundational + US1–US3 (enlace requiere identidad y
cancelarCita005); independently testable con token
Within Each User Story
- Tests (if included) MUST be written and FAIL before implementation
- Models before services
- Services before endpoints
- Core implementation before integration
- Story complete before moving to next priority
Parallel Opportunities
- T002 y T005 pueden ir en paralelo (distintos ficheros)
- T012 y T016 pueden ir en paralelo (distintos ficheros unitarios)
- T006, T009, T013, T017 comparten
tests/integration/test_identidad_canales.test.ts: redactar en secuencia (sin [P] entre ellos) - T020, T021, T022 (polish) pueden ir en paralelo
Parallel Example: User Story 1
# Redactar los tests de User Story 1 juntos:
Task: "Test de resolución portal + recordatorio con la matriz de variantes en tests/integration/test_identidad_canales.test.ts"
Implementation Strategy
MVP First (User Story 1 Only)
- Complete Phase 1: Setup
- Complete Phase 2: Foundational (CRITICAL - blocks all stories)
- Complete Phase 3: User Story 1
- STOP and VALIDATE: Test User Story 1 independently
- Deploy/demo if ready
Incremental Delivery
- Complete Setup + Foundational → Foundation ready
- Add User Story 1 → Test independently → Deploy/Demo (MVP!)
- Add User Story 2 → Test independently → Deploy/Demo
- Add User Story 3 → Test independently → Deploy/Demo
- Add User Story 4 → Test independently → Deploy/Demo
- Each story adds value without breaking previous stories
Notes
- [P] tasks = different files, no dependencies
- [Story] label maps task to specific user story for traceability
- Each user story should be independently completable and testable
- Verify tests fail before implementing
- Commit after each task or logical group
- Stop at any checkpoint to validate story independently
- Trazabilidad FR-007: ninguna tarea fusiona ni reasigna fichas/citas/envíos; la fusión queda fuera para spec posterior