Para evitar divergências entre código e documentação, precisamos de um registro central (fonte única) com:
Isso reduz retrabalho e evita “esqueci de documentar uma rota” em cada mudança.
routes/web.phproutes/console.phproutes/channels.phpGET / — home — App\Livewire\HomeGET /p/{token} — play-card.public — App\Livewire\PublicPlayCard — Play Card só leitura (UUID public_card_token); sem auth; não expõe email nem Academy; meta noindex, nofollow (@stack('head') no layout)GET /docs — docs — view docs.indexGET /docs/architecture — docs.architecture — view docs.markdown (README de docs/architecture/; links .md → docs.read)GET /docs/changes — docs.changes — view docs.markdown (README de docs/changes/; idem)GET /docs/read/{path} — docs.read — App\Http\Controllers\DocsReadController — qualquer *.md sob docs/ (path com /; recusa .. fora de docs/)Middleware: guest
GET /login — login — App\Livewire\Auth\LoginGET /cadastro — onboarding.register — App\Livewire\Onboarding\RegisterPlayerMiddleware: auth
GET /dashboard — dashboard — App\Livewire\DashboardPOST /logout — logout — App\Http\Controllers\Auth\LogoutControllerMiddleware: auth + actor_context + role:player
GET /booking — booking — App\Livewire\BookingGET /community — community — App\Livewire\CommunityGET /my-card — my-card — App\Livewire\MyCard — Play Card: players.level, players.skill_rating, apelido (updateNickname), partilha (sharePlayCard, EnsurePlayerPublicCardShareUrlAction, RegeneratePlayerPublicCardShareUrlAction → play-card.public), faixa (Player::playCardTierLabel()); Academy: presenças (ListPlayerAcademyAttendanceForMyCardAction), só leituraGET /my-classes — my-classes — App\Livewire\Academy\MyEnrollments — turmas em que o jogador está inscrito (ListPlayerAcademyEnrollmentsAction); convites pendentes (ListPendingInvitationsForPlayerAction); pedidos de entrada pendentes (ListPlayerPendingEnrollmentRequestsAction + CancelAcademyClassEnrollmentRequestAction); ação Livewire leaveClass + LeaveAcademyClassAsMemberAction (AcademyClassPolicy::leaveAsMember)GET /academy/browse-classes — academy.browse-classes — App\Livewire\Academy\BrowseAcademyClasses — lista turmas com accepts_enrollment_requests e não arquivadas, excluindo turmas em que o jogador já é membro (ListOpenAcademyClassesForPlayerAction); pedido via CreateAcademyClassEnrollmentRequestAction (AcademyClassPolicy::requestEnrollment)GET /academy/invitations/{token} — academy.invitations.respond — App\Livewire\Academy\InvitationRespond — convite do treinador (AcademyClassInvitation); aceitar AcceptAcademyClassInvitationAction / recusar DeclineAcademyClassInvitationAction (AcademyClassInvitationPolicy)GET /my-location — my-location — App\Livewire\MyLocationMiddleware: auth + actor_context + role:coach + permission:academy.class.view + active_actor:coach
GET /academy/classes — academy.classes.index — App\Livewire\Academy\MyClasses
academy_classes em que o utilizador é coach_user_id.403.Middleware: mesmo grupo coach acima e permission:academy.class.create_as_coach
GET /academy/linked-venues — academy.linked-venues — App\Livewire\Academy\CoachLinkedAcademyVenues
venues com venue_coaches ativo para o utilizador (ListVenuesLinkedToCoachAction).GET /academy/venues/{venue}/classes/new — academy.venues.classes.create — App\Livewire\Academy\CoachCreateAcademyClass
{venue} UUIDVenuePolicy::createClassAsCoach (permissão + papel coach + vínculo ativo na arena)CreateAcademyClassAction com coach_user_id = ator (não-dono não pode indicar outro treinador)Middleware: auth + actor_context + role:coach + permission:academy.coach.credentialed + active_actor:coach
GET /coach/playcard-evaluations — coach.playcard-evaluations — App\Livewire\Coach\PlaycardEvaluationHub
ListOpenBeachPlaycardEvaluationRequestsForCoachAction), procura de atletas (SearchPlayersForBeachPlaycardEvaluationAction), agendar (ScheduleBeachPlaycardEvaluationRequestAction) ou arquivar (DismissBeachPlaycardEvaluationRequestAction).GET /coach/playcard-evaluations/evaluate/{player} — coach.playcard-evaluations.form — App\Livewire\Coach\PlaycardEvaluationForm
{player} UUID (Kolos\Modules\Player\Models\Player)?request= UUID (BeachPlaycardEvaluationRequest) — deve pertencer ao treinador e ao atleta.PlayerPolicy::evaluateBeachPlaycard (qualquer treinador credenciado pode avaliar); pedido associado validado na ação.FinalizeBeachPlaycardEvaluationAction — grava beach_playcard_evaluations, atualiza players.beach_playcard_snapshot, marca pedido completed quando aplicável. Motor: BeachPlaycardMethodology2026 — Playcard estatístico (drills com acertos × pesos → IEA 0–99 por fundamento; média global; faixas D 0–19 / C 20–45 / B 46–70 / A 71–90 / PRO 91–99; travas: categoria B exige DEF ≥ 45; A na média exige SRV e SMA ≥ 71; PRO na média com SRV/SMA insuficientes desce a A). A categoria no cartão é escolhida pelo treinador dentro de [min(trava, média−1), média] (não se pode subir acima da média IEA; piso coerente com travas e um degrau abaixo da média). methodology_weights.category_after_gates guarda a sugestão pós-travas.Middleware: auth + actor_context
GET /academy/classes/{academyClass} — academy.classes.roster — App\Livewire\Academy\ClassRoster
{academyClass} UUID (Kolos\Modules\Academy\Models\AcademyClass)AcademyClassPolicy::view — treinador designado (academy.class.view) ou dono da arena da turma (academy.class.create + owner_user_id)404 se o UUID não existirListPendingEnrollmentRequestsForClassAction, aprovar ApproveAcademyClassEnrollmentRequestAction, rejeitar RejectAcademyClassEnrollmentRequestAction); pesquisa de atletas (debounce) + inscrição imediata (EnrollPlayerInAcademyClassAction) ou convite com email (InvitePlayerToAcademyClassAction + PlayerInvitedToAcademyClassMail em fila) e remoção (RemoveAcademyClassMemberAction) quando AcademyClassPolicy::manageStudents permite; capacidade max_members aplicada em inscrições e pedidosplayers.level, players.skill_rating); edição só se AcademyClassPolicy::updateMemberOfficialPlayCard (treinador credenciado da turma). Leitura para os restantes com view.Middleware: auth + actor_context
GET /academy/classes/{academyClass}/sessions — academy.classes.sessions — App\Livewire\Academy\ClassSessions
{academyClass} UUIDAcademyClassPolicy::view para ver; manageStudents para criar sessão e guardar presençasacademy_class_sessions (uma por (turma, data)); academy_class_session_attendances (present / absent / excused)academy.classes.roster (path mais específico).Middleware: auth + role:venue_owner|super_admin
GET /venues — venues.index — App\Livewire\Venue\MyVenues
canAny(venue.create|venue.update|venue.manage_courts)GET /venues/{venue}/courts — venues.courts — App\Livewire\Venue\ManageCourts
{venue} UUIDVenuePolicy::manageCourts (dono da arena ou super_admin)CourtPolicy::create(User, Court::class, Venue)venues.daily_opens_at / daily_closes_at): formulário na mesma página; alimenta a grelha de horas livres do picker Academy (ListAvailableCourtPickerHoursForWeekdayAction + VenueCourtPickerHourRange); vazio = fallback config('kolos_academy.court_picker_*')UpdateCourtAction + CourtPolicy::updateGET /venues/{venue}/schedule — venues.schedule — App\Livewire\Venue\VenueSchedule
{venue} UUIDVenuePolicy::manageCourts — agenda semanal por quadra (bookings da arena: BOOKING, MAINTENANCE, ACADEMY_CLASS_HOLD); link para roster da turma quando aplicável (AcademyClassPolicy::view); criação de reserva tipo BOOKING em nome do dono (CreateBookingAction); legenda do horário de funcionamento da arena (VenueCourtPickerHourRange::labelForVenue)GET /venues/{venue}/maintenance — venues.maintenance — App\Livewire\Venue\DeclareMaintenance
{venue} UUIDVenuePolicy::declareMaintenance (dono da arena ou super_admin)GET /venues/{venue}/coaches — venues.coaches — App\Livewire\Venue\ManageVenueCoaches
{venue} UUIDVenuePolicy::manageAcademyvenue_coaches (treinadores ativos na arena); AttachVenueCoachAction / DetachVenueCoachAction (não remove se existir turma ativa não arquivada com esse coach_user_id)GET /venues/{venue}/training-classes — venues.training-classes — App\Livewire\Venue\TrainingClasses
{venue} UUIDVenuePolicy::manageAcademy (dono da arena + academy.class.create, ou super_admin via before)SummarizeVenueAcademyAttendanceAction): n.º de sessões, última data, % presenças nos últimos 30 dias por turmavenue_coaches com estado active (exceto super_admin em CreateAcademyClassAction / UpdateAcademyClassAction); horários em academy_class_schedule_slots (sync + rótulo automático se schedule_label vazio); max_members (opcional), accepts_enrollment_requestsacademy_classes.archived_at: dono arquiva/reativa (SetAcademyClassArchivedAction, AcademyClassPolicy::archive); turma arquivada bloqueia novas sessões, inscrições, gravação de presenças e Play Card oficial (ver AcademyClassArchiveRules)GET /venues/{venue}/training-classes/attendance.csv — venues.training-classes.attendance-csv — App\Http\Controllers\Venue\ExportVenueAcademyAttendanceCsvController — CSV de presenças (turma, data, aluno, email, estado); query opcional from e to (Y-m-d, ambos obrigatórios se um for enviado; intervalo máx. 731 dias; filtra session_date); sem query = histórico completo; policy via ExportVenueAcademyAttendanceCsvAction (manageAcademy)GET /api/cep/{cep} — api.cep.lookup — App\Http\Controllers\CepLookupController
{cep} regex [0-9\-]{8,9}inspire — Exibe uma frase inspiradora.player:regeocode-address {userId?} [--email=] [--clear-cache] [--force] [--inspect] [--allow-low-confidence] [--min-confidence=] [--omit-neighborhood] [--omit-number] [--query=]
App.Models.User.{id}
(int) $user->id === (int) $id).active_actor:{role} — App\Http\Middleware\EnsureActiveActorMatches — exige que ActorContext::get($user) coincida com {role} (ex.: coach).Sempre que uma rota/command/channel for criado/alterado:
docs/changes/.sail artisan route:listsail artisan listtests/Feature/**