Passo-a-passo detalhado do skill, referenciando as fases cognitivas:
1SENSE — Identificar mudança e classificar risco
```sql
-- Verificar tamanho da tabela antes de migrar
SELECT reltuples::bigint AS row_estimate
FROM pg_class WHERE relname = 'users';
-- Verificar dependências
SELECT tc.table_name, kcu.column_name, ccu.table_name AS foreign_table
FROM information_schema.table_constraints AS tc
WHERE ccu.table_name = 'target_table';
```
2CONTEXTUALIZAR — Avaliar estratégia
| Tipo | Risco | Estratégia |
|------|-------|-----------|
| ADD COLUMN nullable | Baixo | Direto, sem downtime |
| ADD COLUMN NOT NULL | Médio | Add nullable → backfill → add constraint |
| ALTER COLUMN TYPE | Alto | Expand-contract ou ghost table |
| DROP COLUMN/TABLE | Crítico | Apenas após contrato com código removido |
3RECOMMEND — Gerar migration (Prisma)
```typescript
// prisma/migrations/20260411_add_user_status/migration.sql
SET lock_timeout = '5s'; -- CRÍTICO: prevenir cascata de locks
-- Fase 1: Expand (safe)
ALTER TABLE users ADD COLUMN status TEXT;
-- Índice sem lock (demora mais, mas não bloqueia queries)
CREATE INDEX CONCURRENTLY idx_users_status ON users(status);
RESET lock_timeout;
```
4RECOMMEND — Script de backfill (para ADD NOT NULL)
```python
BATCH_SIZE = 5_000
offset = 0
while True:
updated = db.execute(
"UPDATE users SET status = 'active' "
"WHERE id IN (SELECT id FROM users WHERE status IS NULL LIMIT %s)",
[BATCH_SIZE]).rowcount
if updated == 0:
break
time.sleep(0.1) # pause para não saturar o banco
offset += updated
```
5EVALUATE — Testar DOWN (rollback)
```sql
-- Testar que o rollback funciona
BEGIN;
-- Aplicar DOWN migration
ALTER TABLE users DROP COLUMN IF EXISTS status;
DROP INDEX CONCURRENTLY IF EXISTS idx_users_status;
ROLLBACK; -- só verificando, não commitando
```
6REFLECT — Validar e documentar
Confirmar integridade: contagem de linhas, constraints, foreign keys
Adicionar ao CHANGELOG com data, autor e ticket de referência
Reportar telemetria via mcp-skillschain