Skip to content

feat(resources): auto-ocultado opcional como política por emergencia#423

Open
nopestack wants to merge 6 commits into
GlobalEmergency:mainfrom
nopestack:feature/171-dispute-auto-hide
Open

feat(resources): auto-ocultado opcional como política por emergencia#423
nopestack wants to merge 6 commits into
GlobalEmergency:mainfrom
nopestack:feature/171-dispute-auto-hide

Conversation

@nopestack

Copy link
Copy Markdown
Contributor

Closes #171

Resumen

Implementa la política opt-in por emergencia autoHideOnDispute (§4 Futuro de docs/features/15-reporte-validez-de-puntos.md): cuando está activa, un handler de ResourceDisputed cierra el punto automáticamente al alcanzar el umbral de disputa, reutilizando el mismo camino de ResolveResourceDispute (confirm_closed) que usa un coordinador humano — mismo estado (Closed), mismos efectos, y traza en el audit trail atribuida a "system". Por defecto está desactivada, así que el comportamiento del MVP (punto visible con badge, confirma un humano) no cambia para ninguna emergencia existente.

Backend

  • Emergency: nuevo flag autoHideOnDispute (default false) + setAutoHideOnDispute(); migración 0059_emergency_auto_hide_on_dispute.sql (columna auto_hide_on_dispute boolean NOT NULL DEFAULT false, junto al umbral de disputa de Disputa: umbral configurable por emergencia #169).
  • SetEmergencyAutoHideOnDispute (caso de uso) + PUT /emergencies/:emergencyId/auto-hide-on-dispute (emergency:configure, igual que el umbral); se expone en GET /emergencies/mine (vista autenticada, igual que resourceDisputeThreshold).
  • resources: nuevo handler para el evento resource.disputed (antes sin suscriptor) en la cola existente del consumer resources. El caso de uso AutoHideDisputedResource:
    • lee la política vía el puerto EmergencyAutoHideOnDisputeReader (adaptador Drizzle compartido, mismo patrón que el umbral);
    • si está activa, ejecuta ResolveResourceDispute.execute({ resolution: 'confirm_closed', coordinatorId: 'system' })no una segunda vía para cerrar el recurso;
    • si el punto ya fue resuelto por un humano mientras tanto, no hace nada (idempotente, no revienta el worker);
    • registra el cambio en el audit trail vía un nuevo puerto AuditTrail (adaptador Drizzle compartido que escribe directo a audit_log, ya que no hay request HTTP para AuditInterceptor), con actorUserId: null / actorName: 'system'.
  • Con la política desactivada (default), nada cambia: el evento se recibe pero el caso de uso hace no-op.

Web

  • Toggle "Auto-ocultar al alcanzar el umbral de disputa" en EmergencyControls (panel de coordinación /emergencies/[slug]/manage), junto a pausar/reanudar y el comunicado oficial — mismo patrón de useActionState + server action. El valor actual se obtiene de GET /emergencies/mine (no existía aún consumidor web para esa vista).
  • pnpm gen:api ejecutado; packages/api-client/src/schema.ts y apps/api/openapi.json actualizados.

Pendiente / fuera de alcance de este PR

No existe todavía una pantalla de "ajustes de emergencia" dedicada — el toggle se añadió al panel de coordinación existente (EmergencyControls), en el lugar más consistente con el umbral de disputa (que tampoco tenía UI antes de este PR). Si se decide construir una pantalla de ajustes dedicada más adelante, ambos controles deberían migrar juntos.

Test plan

  • pnpm install --frozen-lockfile
  • pnpm --filter api build
  • pnpm --filter api exec eslint "{src,apps,libs,test}/**/*.ts" --max-warnings=0
  • pnpm --filter api exec prettier --check "src/**/*.ts" "test/**/*.ts"
  • pnpm --filter api test (256 suites / 1654 tests)
  • pnpm --filter @reliefhub/api-client build && pnpm --filter web build
  • pnpm --filter web lint (1 warning preexistente sin relación, opengraph-image.tsx)
  • pnpm --filter web test (118 tests)
  • Unit tests nuevos: dominio Emergency (política on/off), SetEmergencyAutoHideOnDispute, AutoHideDisputedResource (política off → no-op, política on → cierra + audita, idempotente si un humano ya resolvió), resourceDisputedHandler, DTO de validación.

nopestack added 5 commits July 6, 2026 09:54
…lobalEmergency#171)

Añade el flag por emergencia autoHideOnDispute (default off, migración 0059).
Con la política activa, un handler de ResourceDisputed reutiliza el mismo
camino de ResolveResourceDispute (confirm_closed) para cerrar el punto
automáticamente al alcanzar el umbral de disputa, dejando traza en el audit
trail atribuida a "system". Con la política desactivada, el comportamiento
del MVP no cambia (el punto sigue visible con badge hasta que un coordinador
confirma o descarta).

- Emergency: autoHideOnDispute + SetEmergencyAutoHideOnDispute, endpoint
  PUT /emergencies/:id/auto-hide-on-dispute (permiso emergency:configure),
  expuesto en GET /emergencies/mine.
- resources: AutoHideDisputedResource + handler de resource.disputed en el
  consumer existente, puertos EmergencyAutoHideOnDisputeReader y AuditTrail
  (adaptadores Drizzle compartidos, mismo patrón que el umbral de disputa).
- web: toggle de la política en el panel de EmergencyControls (coordinación).
- pnpm gen:api tras el nuevo endpoint/DTO.
@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

@nopestack is attempting to deploy a commit to the GlobalEmergency Team on Vercel.

A member of the Team first needs to authorize it.

@nopestack

Copy link
Copy Markdown
Contributor Author

Nota: renombré la migración de 0059 a 0060 — al abrir esta rama y la de #420 (PR #204) desde el mismo main, ambas asignaron independientemente el número 0059 a su migración. Renombrada aquí para no colisionar; verificado que global-setup la aplica correctamente con el hueco en 0059 (esperado por convención, AGENTS.md).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Disputa: auto-ocultado opcional como política por emergencia

1 participant