---
id: "0092"
title: "kanban: archivo automatico para cards en columnas Done con +30 dias"
status: pendiente
type: feature
domain:
  - kanban
scope: multi-app
priority: media
depends: []
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---

## Problema

Las columnas Done acumulan decenas de cards completadas que ya no participan en el trabajo activo. Lastran el board, gastan render (incluso con el split memoizado por perf) y obligan a scroll horizontal hasta el infinito sin aportar valor diario.

## Solucion

Archive `archived_at` por card (independiente de papelera `deleted_at`). Card archivada sale del board pero sigue en BD. Cajon "Hecho (archivo)" en el sidebar bajo Papelera con boton para sacar una card de vuelta a su columna Done.

### Migration

`apps/kanban/backend/migrations/012_card_archived.sql`:

```sql
ALTER TABLE cards ADD COLUMN archived_at TEXT;
```

`NULL` = card activa. ISO timestamp = card archivada.

### Backend

- `Card.ArchivedAt *string` json `archived_at`.
- `ListCardsWithTime` filtra `archived_at IS NULL`.
- `ArchiveCard(id)`, `UnarchiveCard(id)`, `ListArchivedCards()`.
- `AutoArchiveDoneOlderThan(d)`: UPDATE cards SET archived_at=now WHERE archived_at IS NULL AND deleted_at IS NULL AND column_id IN (SELECT id FROM columns WHERE is_done=1) AND id IN (SELECT card_id FROM card_column_history WHERE exited_at IS NULL AND entered_at < cutoff).
- `maybeAutoArchive(db)` con throttle `archiveSweepEvery = 30m` y `archiveAfter = 30d`. Disparado lazy desde `handleGetBoard`.
- Rutas:
  - `GET /api/archive`
  - `POST /api/cards/{id}/archive`
  - `POST /api/cards/{id}/unarchive`

### Frontend

- Tipo `Card.archived_at: string | null`.
- `api.listArchive()` / `api.archiveCard(id)` / `api.unarchiveCard(id)`.
- `App` state `archive`, `archiveOpen`, `reloadArchive`, `handleArchiveCard`, `handleUnarchiveCard`. `reloadArchive` se llama tras login + tras archivar/desarchivar.
- UI: cajon "Hecho (archivo)" bajo "Papelera" en el sidebar. Toggle similar (`Badge` con conteo, `IconChevronRight/Down`). Solo boton "Restaurar" (`IconArrowBackUp`). No hay "purgar permanentemente" desde el archivo — solo borrado via card normal en su columna.
- `KanbanCard`: nuevo `Menu.Item` "Archivar" (`IconArchive` teal) condicionado a `isDone && onArchive` antes del divisor + Borrar.

### Tests

- Playwright `e2e/archive.spec.ts`: archivar manual via menu de card en columna done, asserta desaparece del board, abre cajon "Hecho", des-archiva, asserta vuelve al board.
- Auto-archive 30d sin test e2e (timing real no es practico). Cubierto por audit manual del flag `archiveAfter` y la query SQL.

## Criterios de aceptacion

- [ ] Cards con +30 dias en columna Done se auto-archivan tras la primera lectura del board (max 30m).
- [ ] Cajon "Hecho (archivo)" visible bajo Papelera. Muestra conteo.
- [ ] Boton "Restaurar" devuelve la card a su columna sin reset de historial.
- [ ] Menu de card en columna Done muestra "Archivar" para archivado manual.
- [ ] Cards archivadas NO aparecen en `/api/board`.
- [ ] E2E `archive.spec.ts` pasa.

## Rama / commits

- Rama: `issue/0092-kanban-done-archive`
- Merge `--no-ff` a master.
