Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions docs/02 - produto/feedback/feedback-reputacao.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,9 +388,13 @@ enum FeedbackAttrEnum {
}
```

> ⚠️ Este bloco é **proposta de produto**, não especificação técnica final. A
> decisão entre tabela relacional e array de enum deve sair da spike da seção 16,
> considerando o custo da query de agregação por perfil.
> ✅ **Decidido em 19/08/2026** pela
> [spike de schema](../../03%20-%20tecnico/spikes/spike-schema-atributos-feedback.md):
> **tabela relacional**, como proposto acima. O critério não foi desempenho — em
> memória, agregar ~120 linhas por perfil é irrelevante — e sim **integridade**:
> só a tabela impede, no banco, que o mesmo eixo seja contado duas vezes na mesma
> avaliação. Com array de enum o Postgres aceita repetição, e a contagem pública
> — que é o valor da corroboração — fica à mercê de um bug de aplicação.

### 12.2 Agregação para o portfólio

Expand Down Expand Up @@ -533,15 +537,14 @@ Documentos técnicos relacionados:
**Ordem de execução:**

1. ⛔ **Encerramento manual de projeto** (épico #553) — bloqueia tudo
2. 🔬 **Spike de schema** — estrutura dos atributos e custo da agregação
2. ✅ ~~Spike de schema~~ — concluída em 19/08/2026: tabela relacional
3. 🛠️ Migration + rotas (criação, agregação, publicação de texto)
4. 🖥️ Tela `/dashboard/feedbacks` (abas Pendentes e Recebidos)
5. 🎨 Bloco de corroborações no portfólio público
6. ✉️ E-mail de notificação (sem conteúdo)
7. 📄 FAQ e termos: o que acontece com o feedback ao excluir a conta
**Decisões ainda em aberto:**

- Estrutura final do schema dos atributos (spike);
- Prazo do blind duplo — 14 dias é proposta, não validada;
- N mínimo = 3 — proposta, revisar com dados reais;
- Manutenção ou remoção definitiva do `rating` interno;
Expand All @@ -558,6 +561,7 @@ Documentos técnicos relacionados:
| v2 | Rótulos com verbo e lastro; proibidos superlativos e traços inatos |
| v2 | Proibida representação com teto (termômetro, barra, estrelas) |
| v2 | Anonimato público fixo; identificação interna permanente |
| 19/08/2026 | Schema dos atributos: **tabela relacional**, por integridade — ver [spike](../../03%20-%20tecnico/spikes/spike-schema-atributos-feedback.md) |
| v2 | Blind duplo como mitigação de retaliação |
| v2 | N mínimo de 3 atestados para exibição pública |
| v2 | Duplo denominador (pessoas × projetos) |
Expand Down
48 changes: 48 additions & 0 deletions docs/02 - produto/projetos/gestao-projetos.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,54 @@ participantes.

------------------------------------------------------------------------

### 6.4 Revisão de 19/08/2026 — notificar em vez de bloquear

**Decisão:** a restrição da seção 6.3 passa a ser **notificação**, não bloqueio.
O owner pode editar o projeto após a formação da equipe; as pessoas envolvidas
são avisadas da alteração.

**Por que mudou.** A regra foi implementada e desativada em 16/08/2026, por dois
defeitos que só apareceram no uso:

1. **Escopo largo demais.** A verificação tratava `name`, `deadline` e
`totalValue` como estrutura, travando edições sem relação com a composição da
equipe — inclusive corrigir um erro de digitação na descrição.

2. **Falso positivo na comparação de datas.** O formulário carregava o prazo sem
a hora (`deadline.split('T')[0]`) enquanto o banco guardava o timestamp
completo. Os dois nunca coincidiam, então **abrir a tela de edição e salvar
sem alterar nada já disparava o bloqueio.** Na prática a regra não protegia a
estrutura: travava a edição inteira, sem saída pela interface.

**Por que notificar é melhor.** O bloqueio parte da premissa de que mudar é
errado. Notificar parte de que **mudar sem avisar** é que é. Num projeto onde as
pessoas estão aprendendo a trabalhar em equipe, a segunda premissa é mais fiel ao
que acontece no mercado: escopo muda, e a equipe é comunicada.

O bloqueio também criava um beco sem saída — uma vez formada a equipe, nem o
owner nem um ADMIN conseguiam corrigir nada pela tela.

**A regra da observação com data continua valendo.** Ela é o registro histórico
da mudança; a notificação é o aviso ativo. As duas se complementam.

#### O que ainda precisa ser definido

- **Quem é notificado** — todos que assumiram stack, ou apenas os afetados pela
alteração específica?
- **Por qual canal** — e-mail (o projeto já usa Resend), aviso no painel, ou os
dois?
- **O que a mensagem informa** — que houve alteração, ou quais campos mudaram?
- **Alguma alteração ainda exige confirmação prévia?** Trocar uma stack já
assumida afeta diretamente o trabalho de alguém — talvez mereça tratamento
diferente de corrigir a descrição.

> ⚠️ **Estado atual do código:** a verificação existe em
> `src/app/api/team-project/[id]/route.ts`, desativada por uma constante, com o
> motivo documentado no próprio arquivo. Os três testes que a cobriam estão como
> `it.skip`, descrevendo o comportamento esperado caso a regra volte.

------------------------------------------------------------------------

## 7. Impacto em Dados

- Criação de registros em Project
Expand Down
143 changes: 143 additions & 0 deletions docs/03 - tecnico/spikes/spike-schema-atributos-feedback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Spike — Estrutura dos atributos de Feedback

**Classificação:** Documento Técnico / Spike
**Camada:** 3 — Documentos Técnicos
**Status do documento:** concluído
**Data:** 19/08/2026
**Origem:** seção 12.1 de [`feedback-reputacao.md`](../../02%20-%20produto/feedback/feedback-reputacao.md)

---

## 1. A pergunta

O documento de produto propõe uma tabela relacional para os atributos, mas
registra a decisão como pendente:

> Este bloco é **proposta de produto**, não especificação técnica final. A decisão
> entre tabela relacional e array de enum deve sair da spike, considerando o
> custo da query de agregação por perfil.

---

## 2. As duas opções

**A — Tabela relacional**

```prisma
model Feedback {
attributes FeedbackAttribute[]
}

model FeedbackAttribute {
feedbackId String
attribute FeedbackAttrEnum
feedback Feedback @relation(fields: [feedbackId], references: [id], onDelete: Cascade)

@@unique([feedbackId, attribute])
@@index([attribute])
}
```

**B — Array de enum**

```prisma
model Feedback {
attributes FeedbackAttrEnum[]
}
```

---

## 3. Resultado

**As duas são válidas no Prisma 7.9.1** — `prisma validate` passa em ambas. A
decisão não é sobre viabilidade.

### 3.1 O que o SQL gerado mostra

Comparando `prisma migrate diff` das duas:

| | Opção A | Opção B |
|---|---|---|
| Restrição de atributo repetido | `UNIQUE (feedbackId, attribute)` | **nenhuma** |
| Índice por atributo | `INDEX (attribute)` | **nenhum** |
| Exclusão em cascata | `ON DELETE CASCADE` | implícita na linha |
| Tabelas | 2 | 1 |

A diferença decisiva está na primeira linha. Na opção B, o Postgres aceita
`{APOIA_A_EQUIPE, APOIA_A_EQUIPE, APOIA_A_EQUIPE}` sem reclamar — **o mesmo eixo
contado três vezes na mesma avaliação**.

Isso não é detalhe de implementação. A regra da seção 5.2 — no máximo 3 eixos por
pessoa avaliada — existe para impedir que o sinal seja esvaziado por marcação de
cortesia. Com array, a integridade dessa regra depende inteiramente do código da
aplicação; um bug ou uma rota nova esquecendo a validação corrompe a agregação em
silêncio. Com a tabela, o banco recusa.

### 3.2 Custo da agregação

A consulta pública precisa, por eixo: contagem de avaliadores distintos e de
projetos distintos.

**Nenhuma das duas opções é expressável no `groupBy` do Prisma**, porque ele não
suporta `COUNT(DISTINCT)` sobre coluna de tabela relacionada. Em ambas o caminho
é o mesmo: buscar os feedbacks do perfil e agregar em memória, ou usar SQL cru.

**Estimativa de volume por perfil:**

```
10 projetos concluídos × 4 colegas = ~40 feedbacks recebidos
40 feedbacks × até 3 eixos = ~120 linhas de atributo
```

Nessa ordem de grandeza, agregar em memória é irrelevante em custo. O argumento
de desempenho — que motivava a spike — **não diferencia as opções**.

Se o volume crescer a ponto de importar, SQL cru resolve nos dois casos. Na
opção B ele exige `unnest`, que é menos legível; na A é um `GROUP BY` comum.

---

## 4. Decisão

**Opção A — tabela relacional.**

O critério que decide não é desempenho, e sim **integridade**: só ela impede, no
banco, que o mesmo eixo seja contado mais de uma vez na mesma avaliação. Como o
valor público da corroboração vem justamente da contagem, um dado duplicado
corrompe exatamente aquilo que a funcionalidade entrega.

Fatores secundários que reforçam:

- índice por atributo, útil para a métrica da seção 17 (distribuição por eixo);
- `onDelete: Cascade` explícito, relevante para a exclusão de conta (seção 13);
- consulta de agregação mais legível se um dia precisar de SQL cru;
- adicionar um eixo novo é alterar o enum nos dois casos — sem diferença.

O custo é uma tabela a mais. É pouco pelo que se ganha.

---

## 5. O que isto destrava

A etapa 3 da ordem de execução (seção 20 do documento de produto): migration e
rotas. A estrutura de `FeedbackAttribute` proposta na seção 12.1 fica confirmada
como especificação técnica.

**Continuam em aberto**, e não dependiam desta spike:

- prazo do blind duplo (14 dias é proposta);
- N mínimo para exibição (3 é proposta);
- manutenção ou remoção do `rating` legado;
- existência de fluxo de denúncia pelo avaliado.

---

## 6. Como este resultado foi obtido

```bash
prisma validate --schema opcao-a.prisma # e opcao-b
prisma migrate diff --from-empty --to-schema opcao-a.prisma --script
```

Executado com Prisma CLI 7.9.1, mesma versão do projeto.
Loading