Skip to content

feat(squads): pivot squad_members + SquadRole + recorder de eventos (#355)#408

Open
sirelves wants to merge 1 commit into
feat/squadsfrom
story/355-squad-members-events
Open

feat(squads): pivot squad_members + SquadRole + recorder de eventos (#355)#408
sirelves wants to merge 1 commit into
feat/squadsfrom
story/355-squad-members-events

Conversation

@sirelves

@sirelves sirelves commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Parent

#342 · Closes #355 · Desbloqueada por #354 (merged)

O que construir

Pivot de membros, papéis in-squad e o recorder da trilha append-only de eventos de membership — módulo profundo reusado por todas as mutações (S3/S6/S8/S9/S10).

Entregue (estritamente o escopo do #355)

Migrations (via artisan make:migration --module=squads, datas Tz, jsonb)

  • squad_membersid uuid, FKs tenant_id/squad_id/user_id, role, joined_at, left_at?, UNIQUE (squad_id, user_id)
  • squad_membership_events (append-only) — actor_id? (nullOnDelete = sistema), action, from_role?/to_role?, reason?, metadata jsonb?, occurred_at; índices (squad_id, occurred_at) e (user_id, occurred_at)

EnumsSquadRole (captain/sub_captain/member/ex_member), MembershipAction (join/leave/promote/demote)

ModelsSquadMember (Pivot + HasUuids) e SquadMembershipEvent, com PHPDoc @property sincronizado, #[Table], #[UseFactory] e casts de enum/data. O evento tem guard de append-only no model (booted() barra update/delete de instância via MembershipEventIsImmutable) — o docblock deixa claro que é contrato de fluxo da app, não trava de banco (query-builder/cascade passam por design).

RecorderRecordMembershipEvent (Action): grava um evento por mutação, actor nulo = sistema, occurredAt default now(), tenant_id derivado do squad. Docblock pede que o caller chame dentro da transação da mutação (atomicidade).

Testes — recorder (transição completa, evento de sistema, metadata jsonb), imutabilidade (update e delete), e SquadMember (persistência uuid/cast/timestamps + a UNIQUE(squad_id,user_id)).

Critérios de aceite

  • Migrations + PHPDoc sincronizado (datas Tz, jsonb)
  • Enums SquadRole e MembershipAction
  • Recorder grava evento com action, from_role/to_role, actor_id, occurred_at
  • Eventos são append-only (nunca update/delete)
  • Teste do recorder

Fora de escopo (deferido de propósito)

  • Índices parciais da ADR-0002 (capitão único / exclusividade) — a issue só pede UNIQUE(squad_id,user_id); os parciais vêm com as Actions de join/liderança que mantêm essas invariantes.
  • Contratos Filament nos enums, relações members()/membershipEvents() no Squad e DTO do recorder — pertencem às issues de painel/fluxos, não ao feat(squads): pivot squad_members + SquadRole + recorder de eventos #355.

Testes

  • pest app-modules/squads23 passed / 55 assertions
  • rector ✅ · pint ✅ · phpstan (src) ✅
  • Cadeia de migrations aplica limpa.

BDD

# language: pt
Funcionalidade: Pivot de membros e trilha de eventos
  Cenário: Recorder registra uma transição
    Dado um squad e um usuário
    Quando ocorre uma mutação de membership (ex.: join)
    Então um squad_membership_event é gravado com action, papéis from/to, ator e occurred_at

  Cenário: A trilha é imutável
    Dado um evento de membership já gravado
    Quando qualquer fluxo do módulo é executado
    Então o evento existente nunca é atualizado nem removido

…ts (#355)

- squad_members pivot (SquadMember) + SquadRole enum
- squad_membership_events append-only trail (SquadMembershipEvent, immutable) + MembershipAction enum
- RecordMembershipEvent recorder, reused by every membership/role mutation
- factories + recorder and immutability tests
@sirelves sirelves requested a review from a team July 9, 2026 19:40
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.

1 participant