Parte del EPIC #120 · ficha docs/features/15-reporte-validez-de-puntos.md (§3.1, §3.2).
Objetivo
Modelar en el contexto resources la captura del reporte de validez y la lógica de "punto en duda".
Tareas
Criterios de aceptación
- TDD en verde: umbral N=3, dedup por usuario, invariante "solo disputable si visible", idempotencia de
flagDisputed, exclusión del dueño.
pnpm --filter api build y pnpm --filter api test en verde.
Decisiones aplicadas (ficha §8): arquitectura propia en resources, N=3 global, excluir dueño, sin TTL en MVP.
Parte del EPIC #120 · ficha
docs/features/15-reporte-validez-de-puntos.md(§3.1, §3.2).Objetivo
Modelar en el contexto
resourcesla captura del reporte de validez y la lógica de "punto en duda".Tareas
Resource: añadirdisputed: boolean(default false) +disputedAt: Date | null. Métodos de dominioflagDisputed(at)(solo sipublicStatus ∈ {active, saturated, paused}) yclearDispute(). EventosResourceDisputed/ResourceDisputeResolved.ResourceValidityReport+ enumValidityReason(closed | nonexistent | moved | outdated), constatus(open | accepted | dismissed),noteyphotoUrlsopcionales.ResourceValidityReportRepository+ adapter Drizzle.apps/api/drizzle/0028_resource_validity_reports.sql: tablaresource_validity_reports(+ índice único parcial(resource_id, reporter_user_id) WHERE status='open', índices por recurso/emergencia) yALTER TABLE resources ADD COLUMN disputed/disputed_at. Aplicar en dev y test (global-setup) víapsql.ReportResourceValidity— valida recurso visible; upsert del reporte abierto del usuario; recuenta reportantes distintos; si≥ 3y no estaba en duda →flagDisputed(). Excluye al dueño del recuento.ResolveResourceDispute—confirm_closed(→changePublicStatus(Closed)+ reportesaccepted+clearDispute) /dismiss(→ reportesdismissed+clearDispute).GetDisputedResources(cola) yGetResourceValidityReports(detalle).Criterios de aceptación
flagDisputed, exclusión del dueño.pnpm --filter api buildypnpm --filter api testen verde.Decisiones aplicadas (ficha §8): arquitectura propia en
resources, N=3 global, excluir dueño, sin TTL en MVP.