Skip to content

feat(squads): squad_applications + ApplyToSquad gated no APTO (#360) - #533

Open
sirelves wants to merge 2 commits into
feat/squadsfrom
story/360-squad-applications
Open

sirelves wants to merge 2 commits into
feat/squadsfrom
story/360-squad-applications

Conversation

@sirelves

Copy link
Copy Markdown
Contributor

Materializa a candidatura a um squad, o único fluxo que o módulo conduz, gated no APTO do onboarding.

  • Migration squad_applications: status (default pending), message, decided_by, decided_at, INDEX (squad_id, status) e o partial UNIQUE (squad_id, user_id) WHERE status = 'pending'
  • Model SquadApplication (PHPDoc @property, #[Table], #[UseFactory], casts) + SquadApplicationFactory + enum ApplicationStatus (pending/approved/rejected)
  • Action ApplyToSquad: lê OnboardingCompletionGate::isCompleted($user, OnboardingType::Squads) e barra quem não é APTO com NotAptForSquads, cuja mensagem já nomeia o onboarding exigido para a UI direcionar
  • Candidatura pendente duplicada cai no índice parcial e vira ApplicationAlreadyPending

Sobre a duplicata pendente

Não tem SELECT antes do insert: duas candidaturas simultâneas leriam "nenhuma pendente" e inseririam as duas. Quem decide é o índice parcial, e a Action traduz a violação para a exception de domínio. Como candidatura recusada sai do índice, candidatar-se de novo depois continua valendo (tem teste pra isso).

Fora de escopo (por dependência)

Plano de testes

  • ApplyToSquadTest.php: APTO abre pending com mensagem, não-APTO barrado (sem onboarding, onboarding em progresso, só o Welcome concluído), duplicata pendente barrada, candidatura recusada não bloqueia nova, pendente em outro squad não bloqueia. 7/7 verdes
  • Suite de app-modules/squads: 47/47 verdes
  • make check (rector + pint + phpstan) limpo

Closes #360

Abre o único fluxo que o módulo conduz: a candidatura. Entrar exige o gate
APTO do onboarding, então quem não concluiu o onboarding Squads é barrado e
direcionado para lá.

O limite de uma candidatura pendente por squad é o índice parcial
UNIQUE (squad_id, user_id) WHERE status = 'pending', não um SELECT prévio:
duas candidaturas simultâneas leriam "nenhuma pendente" e inseririam as
duas. O insert decide. Uma candidatura recusada sai do índice, então
candidatar-se de novo depois continua valendo.

A decisão do capitão (aprovar/recusar) fica na #361, e a exclusividade de
um squad ativo por pessoa na #362.
@sirelves
sirelves requested a review from a team August 31, 2026 14:44
@stherzada stherzada added this to the Squads milestone Aug 31, 2026
@stherzada stherzada linked an issue Aug 31, 2026 that may be closed by this pull request
5 tasks
BrunaDomingues
BrunaDomingues previously approved these changes Sep 8, 2026

@BrunaDomingues BrunaDomingues left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Comment thread app-modules/squads/src/Actions/ApplyToSquad.php Outdated
GabrielFVDev
GabrielFVDev previously approved these changes Sep 14, 2026

@GabrielFVDev GabrielFVDev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

O catch pegava qualquer UniqueConstraintViolationException e assumia
que era sempre candidatura pendente. Agora reconfere se existe mesmo
uma candidatura pendente pra esse squad e usuário antes de mapear pra
ApplicationAlreadyPending, e relança a exceção original se não existir.

O insert precisou ir pra dentro de um DB::transaction porque, sem
isolar numa savepoint, o insert que falha aborta a transação inteira
no Postgres e a query de reconferência quebra junto.

@davicbtoliveira davicbtoliveira left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@hefeus hefeus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

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.

feat(squads): squad_applications + ApplyToSquad (gated em APTO)

7 participants