Tasks: Cancelación por el paciente (005)
Input: Design documents from /specs/005-cancelacion-paciente/ (spec.md, plan.md, research.md, data-model.md, contracts/cancelar-cita.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 rechazos, bordes de 24 h y concurrencia con una sola aplicada (SC-001…SC-003) 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: Base de BD y verificación del punto de partida
✓- [X] T001 Verificar migración base y esquema actual en prisma/schema.prisma (ausencia de Clinica.telefono y de columnas de traza)
✓- [X] T002 [P] Añadir telefono a la semilla Eleva en prisma/seed.ts y lib/semilla.ts (número español verosímil)
Phase 2: Foundational (Blocking Prerequisites)
Purpose: Esquema, catálogo y decisión pura que bloquean todas las historias
⚠️ CRITICAL: No user story work can begin until this phase is complete
✓- [X] T003 Crear migración prisma/migrations/XXXX_005_cancelacion_paciente/migration.sql (Clinica.telefono con DEFAULT '910123456' + backfill UPDATE de filas existentes antes de SET NOT NULL, Cita.cancelacionOrigen, Cita.canceladaEn) y aplicar en prisma/schema.prisma
✓- [X] T004 Añadir ESQUEMA_CANCELACION_PACIENTE en lib/validacion.ts (pacienteId uuid, origen portal|email)
✓- [X] T005 Implementar decidirCancelacion + AVISOS_CANCELACION en lib/cancelacion.ts (orden ENLACE_INVALIDO → CITA_AJENA → YA_CANCELADA/ESTADO_FINAL → PLAZO_VENCIDO, 006 FR-005, teléfono interpolado)
✓- [X] T006 [P] Test unitario del catálogo y bordes en tests/unit/test_cancelacion.test.ts (24 h 00 min dentro, 23 h 59 min fuera, textos con teléfono) — debe FALLAR antes de T005
Checkpoint: Foundation ready - user story implementation can now begin in parallel
Phase 3: User Story 1 - Cancelar en plazo desde cualquier canal (Priority: P1) 🎯 MVP
Goal: cancelarCita aplica reservada → cancelada con confirmación desde portal o email, libera el tramo RN1 y deja traza visible.
Independent Test: Cita reservada en plazo + confirmación por cada origen → aplicada, cancelada, tramo libre y ficha «Cancelada por el paciente desde el portal/email el DD/MM/AAAA a las HH:MM». Visita sin confirmación no muta.
Tests for User Story 1 ⚠️
NOTE: Write these tests FIRST, ensure they FAIL before implementation
✓- [X] T007 [US1] Test de integración aplicada por portal y email en tests/integration/test_cancelacion_paciente.test.ts (estado, tramo libre reutilizable con POST /api/citas, ficha con origen)
✓- [X] T008 [US1] Test de que GET/visualización no escribe en tests/integration/test_cancelacion_paciente.test.ts (mismo fichero que T007: redactar en secuencia, sin marcador [P])
Implementation for User Story 1
✓- [X] T009 [US1] Implementar ejecutarCancelarCita en lib/cancelacion.ts (transacción serializable, capturarAhora, escritura condicional updateMany, canceladaEn/cancelacionOrigen, reintento P2034)
✓- [X] T010 [US1] Implementar PATCH /api/citas/[id]/cancelar/route.ts (zod, identidad verificada 007, contrato contracts/cancelar-cita.md, fichaTexto con formatearFechaHora)
✓- [X] T011 [US1] Registrar solo rechazos en log de servidor con origen, instante y código en lib/cancelacion.ts
Checkpoint: At this point, User Story 1 should be fully functional and testable independently
Phase 4: User Story 2 - Rechazos que nunca mutan (Priority: P1)
Goal: Matriz de rechazos con código y texto exactos sin mutar nada.
Independent Test: PLAZO_VENCIDO, ESTADO_FINAL/YA_CANCELADA, CITA_AJENA, ENLACE_INVALIDO, ORIGEN_INVALIDO → estado inmutable y aviso exacto.
Tests for User Story 2 ⚠️
✓- [X] T012 [US2] Test de matriz de rechazos en tests/integration/test_cancelacion_paciente.test.ts (cada caso verifica estado inmutable + texto con teléfono; mismo fichero que T007/T008: sin marcador [P])
Implementation for User Story 2
✓- [X] T013 [US2] Cubrir CITA_AJENA sin revelar datos y ENLACE_INVALIDO previo a toda lectura en lib/cancelacion.ts y app/api/citas/[id]/cancelar/route.ts
✓- [X] T014 [US2] Cubrir ORIGEN_INVALIDO (422, sin mutar) en app/api/citas/[id]/cancelar/route.ts
Checkpoint: At this point, User Stories 1 AND 2 should both work independently
Phase 5: User Story 3 - Concurrencia que solo aplica una vez (Priority: P2)
Goal: Doble clic / doble petición / carrera con recepción → exactamente una aplicada.
Independent Test: N ejecuciones concurrentes → una aplicada, resto YA_CANCELADA/ESTADO_FINAL, cero dobles efectos.
Tests for User Story 3 ⚠️
✓- [X] T015 [US3] Test concurrente Promise.all doble PATCH en tests/integration/test_cancelacion_paciente.test.ts (exactamente una aplicada; mismo fichero: sin [P])
✓- [X] T016 [US3] Test de carrera contra PATCH /api/citas/[id]/estado a completada en tests/integration/test_cancelacion_paciente.test.ts (mismo fichero: sin [P])
Implementation for User Story 3
✓- [X] T017 [US3] Endurecer reintento y relectura post-carrera en lib/cancelacion.ts (perdedora devuelve YA_CANCELADA o ESTADO_FINAL, sin estados intermedios)
Checkpoint: All user stories should now be independently functional
Phase 6: Polish & Cross-Cutting Concerns
Purpose: Puertas de merge de la constitución y la spec
✓- [X] T018 [P] Validar quickstart.md de la 005 (tabla de escenarios portal/email/rechazos/concurrente/RN1)
✓- [X] T019 [P] Verificar SC-004 por grep: portal/recordatorios no implementan su propia transición reservada → cancelada fuera de lib/cancelacion.ts
✓- [X] T020 [P] Pasar npm run typecheck, npm run lint, npm test y npm run comprobar:es en 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): Can start after Foundational (Phase 2) - May integrate with US1/US2 but should be independently testable
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 T006 pueden ir en paralelo (distintos ficheros)
- T007, T008, T012, T015, T016 comparten
tests/integration/test_cancelacion_paciente.test.ts: redactar en secuencia (sin [P] entre ellos) - T018, T019, T020 (polish) pueden ir en paralelo
Parallel Example: User Story 1
# Redactar los tests de User Story 1 juntos:
Task: "Test de integración aplicada por portal y email en tests/integration/test_cancelacion_paciente.test.ts"
Task: "Test de que GET/visualización no escribe en tests/integration/test_cancelacion_paciente.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
- 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-008:
cancelacionOrigen/canceladaEnsolo se informan enaplicadapor paciente; cancelaciones de recepción dejanNULL