Esta página é o contrato do domínio. Toda decisão de modelagem, endpoint ou teste do projeto deve poder ser justificada por uma regra aqui. Quando o código discordar da regra, o código está errado (ou a regra precisa de uma RFC antes de mudar).
Convenção de severidade
MUST = invariante de domínio, violação é bug.
SHOULD = comportamento padrão, override exige justificativa.
MAY = abertura intencional, decidir caso a caso.
Qualquer pessoa MUST poder se cadastrar via POST /auth/signup sem autenticação.
U-02
Email MUST ser único (case-insensitive, normalizado em lowercase).
U-03
Senha MUST ter ≥ 12 caracteres e ser hasheada com bcrypt (PasswordUtils.hash).
U-04
Cadastro MUST retornar 201 com {user_id, access_token, refresh_token}.
U-05
Login (POST /auth/login) MUST aceitar email+senha e devolver os tokens.
U-06
Refresh (POST /auth/refresh) MUST validar refresh_token e emitir par novo.
U-07
Usuário MAY atualizar nome / foto via PATCH /users/me. Email é imutável.
U-08
Soft-delete (DELETE /users/me) MUST marcar is_active=False e revogar tokens emitidos via lista negra de jti.
Justificativa: signup público é o gatilho do funil. Email único impede duplicatas; bcrypt impede vazamento de senha em caso de leak. Soft-delete preserva integridade referencial (pedidos antigos continuam apontando pro user inativo).
Qualquer user MUST poder criar organização via POST /organizations.
O-02
Um user MUST NOT criar mais que 2 organizações ativas simultâneas (count de Organization com owner_id = user.id AND is_active = true).
O-03
Quem cria vira OWNER automaticamente — Membership(role=OWNER) na transação do create.
O-04
Cada organização MUST ter exatamente 1 OWNER. Transferência via POST /organizations/{id}/transfer-ownership (target deve já ser membro).
O-05
OWNER MAY deletar a organização (soft-delete). Deleta a org não libera o slot até o cleanup async (TaskIQ) zerar membros e converter pedidos abertos em cancelados.
O-06
Slug da organização MUST ser único globalmente (acme-supplies).
Justificativa: limite de 2 orgs prende abuso de criação em massa pra reset de quotas. OWNER único elimina conflito de governança.
OWNER/ADMIN MAY criar convite via POST /organizations/{id}/invitations com {email, role}.
I-02
Convite gera token opaco (generate_opaque_token(48)), armazena hash (hash_opaque_token) e envia link por email (Jinja2 via EmailUtils.render_template).
I-03
Convite MUST expirar em 7 dias (expires_at).
I-04
POST /invitations/{token}/acceptMUST validar: convite válido + não expirado + usuário logado tem o mesmo email.
I-05
Aceite cria Membership(role=convite.role) na transação que marca o convite ACCEPTED.
I-06
Convite duplicado para o mesmo email MUST invalidar o anterior antes de criar o novo (status=SUPERSEDED).
I-07
OWNER/ADMIN MAY revogar via DELETE /invitations/{id} antes do aceite (status=REVOKED).
I-08
Aceite MUST falhar com 409 quando a organização já estourou os 10 membros.