# Estado do projeto e handoff **Atualizado em:** 5 de agosto de 2026 **Fase:** Fases 1 a 6 concluídas; Fase 7 em curso (identidade, saúde, recuperação e ligação de leitura ao Snipe-IT de produção concluídas) **Último agente:** Codex ## Resultado disponível - Projeto oficial Laravel 13 React Starter Kit criado. - Autenticação local via Fortify, recuperação de palavra-passe, verificação de email, 2FA e passkeys. - Registo público desativado conforme o plano. - Estrutura relacional de utilizadores, identidades externas, perfis e permissões. - Cache local de ativos Snipe-IT. - Modelo completo inicial para reservas, itens, aprovações, bloqueios, entrega/devolução, sincronizações e auditoria. - Proteção PostgreSQL contra períodos sobrepostos para o mesmo equipamento através de `EXCLUDE USING gist` e intervalos `[início, fim)`. - Serviço de disponibilidade que considera estado local, atribuição, reservas e bloqueios. - Criação transacional de uma requisição pendente com vários equipamentos e registo de auditoria. - Dashboard pessoal, catálogo com pesquisa/filtros, formulário de nova requisição e histórico. - Docker Compose com Nginx, PHP-FPM, PostgreSQL, Redis, Node/Vite, filas, scheduler e Mailpit. - Dados fictícios e duas contas locais para desenvolvimento. - Cliente Snipe-IT autenticado com paginação, timeout, retry seletivo e validação de respostas. - Sincronização convergente de ativos com histórico, hash de conteúdo, lock, job único e desativação apenas após execução completa. - Comando `snipe:sync-assets`, execução horária e painel de diagnóstico exclusivo para administradores. - Pesquisa do catálogo por início/fim que exclui atribuições atuais, reservas bloqueantes e bloqueios administrativos. - Propagação do período consultado do catálogo para o formulário e consulta atualizável de equipamentos livres. - Cancelamento pelo próprio requisitante, com validação de estado, libertação transacional dos itens e auditoria. - Expiração de pré-reservas vencidas através do comando `reservations:expire` e job único agendado a cada cinco minutos. - Calendário mensal pessoal, isolado por utilizador e com navegação entre meses. - Área de aprovação protegida por perfil, com pesquisa por referência/requisitante. - Aprovação, rejeição e pedido de esclarecimentos transacionais, registados em `approval_actions` e `audit_logs`. - Aprovação revalida disponibilidade e estado operacional do ativo antes de promover itens `held` para `reserved`. - Notificações assíncronas por email e base de dados para submissões, decisões, esclarecimentos e substituições. - Caixa interna de notificações, contador de não lidas, marcação individual/coletiva e isolamento por utilizador. - Resposta auditada do requisitante a esclarecimentos, com regresso a `pending_approval` e novo prazo de 48 horas. - Substituição de equipamento apenas por ativo livre da mesma categoria, mantendo a pré-reserva e todo o histórico. - Fila de aprovação com filtros por referência, requisitante, categoria e período, incluindo histórico de ações. - Cliente Snipe-IT operacional com token separado, checkout para utilizador, check-in para a localização habitual e validação de erros transportados em HTTP 200. - Preflight remoto obrigatório antes de checkout; ativos já atribuídos são recusados sem qualquer pedido de escrita. - Check-in condicionado a checkout confirmado pelo portal para o mesmo requisitante e a atribuição remota atual ao respetivo utilizador Snipe-IT. - Snipe-IT de produção formalmente limitado a leitura, checkout e check-in: criar, editar ou eliminar inventário é proibido no código, diagnóstico e procedimentos de agentes. - Área diária do operador protegida pelo perfil `inventory_operator`/`administrator`, com leitura por scanner-teclado ou pesquisa de referência, requisitante, etiqueta e ID Snipe-IT. - Preparação, entrega, devolução e conclusão com máquina de estados, condição física, observações, operador, destinatário e resposta externa em `handover_records`. - Associação persistente entre o utilizador do portal e o respetivo ID no Snipe-IT, confirmada durante uma entrega bem-sucedida. - Tratamento explícito de falhas parciais: movimentos já aceites são preservados, a requisição passa a `incident` e a falha fica em `audit_logs` para reconciliação. - Registo de falta, atraso e incidente; um incidente pode criar um bloqueio temporal do equipamento que entra imediatamente no motor de disponibilidade. - Gestão administrativa de utilizadores com pesquisa, perfis múltiplos, estado ativo, departamento, ID Snipe-IT e reposição de palavra-passe. - Proteção contra desativação da própria conta administrativa e contra remoção do último administrador ativo. - Políticas de reserva editáveis para antecedência, duração, margem operacional, prazo de aprovação e limite de equipamentos. - Margens temporais persistidas por item e garantidas pela restrição de exclusão PostgreSQL, sem reescrever reservas existentes quando a política muda. - Calendário institucional global para aprovadores, operadores, administradores e auditores. - Relatórios por período com totais, horas de utilização, incidentes, atrasos, estados, categorias, departamentos e equipamentos mais requisitados. - Exportação CSV protegida contra formula injection e limitada a períodos de 366 dias. - Consulta paginada e filtrável de `audit_logs`, apenas para administradores e auditores. - Modelos editáveis de assunto/mensagem para os seis eventos de notificação, com variáveis validadas e auditoria das alterações. - Autenticação Microsoft Entra/OIDC de tenant único com descoberta `.well-known`, Authorization Code Flow, PKCE, `state`, `nonce` e validação criptográfica RS256. - Associação Microsoft explícita após login local e confirmação recente da palavra-passe; nenhuma conta é associada automaticamente por email. - Login Microsoft para identidades previamente associadas, bloqueio de contas inativas e eventos de associação/login/remoção em auditoria. - Endpoints `/health/live` e `/health/ready`, sendo readiness dependente de PostgreSQL e cache sem divulgar detalhes internos. - Health check Docker do Nginx sobre readiness. - Scripts operacionais de backup PostgreSQL custom com checksum e restauro de teste numa base temporária isolada. - Checklist versionado de produção, incluindo Entra, Snipe-IT, SMTP/TLS, segredos, monitorização, recuperação, segurança e piloto. ## Limites conhecidos - A URL e os dois tokens distintos estão configurados localmente e responderam com sucesso a pedidos `GET` em 5 de agosto de 2026. A sincronização integral de leitura importou 2367 ativos para o cache local, dos quais 716 estavam atribuídos e 144 marcados como requisitáveis. Não foi executada qualquer escrita real. Falta confirmar institucionalmente a versão, o menor privilégio efetivo dos tokens e as categorias/estados autorizados. - Microsoft Entra ID está implementado e testado com tokens assinados simulados; falta registar/configurar a aplicação no tenant institucional e validar Conditional Access/MFA real. - As notificações estão implementadas com Mailpit em desenvolvimento; falta validar SMTP institucional antes do piloto. - O calendário atual é a vista mensal pessoal do MVP; vistas diária, semanal, global e por equipamento/categoria/localização ficam para as áreas operacional e administrativa. - O leitor operacional aceita scanners QR/código de barras que funcionem como teclado. A leitura direta pela câmara do navegador ainda não foi adicionada. - Uma falha depois de o Snipe-IT confirmar um movimento e antes do commit local é uma transação distribuída ambígua; não existe retry automático de escritas. O operador deve reconciliar pelos registos Snipe-IT e `audit_logs` conforme o runbook. - As exportações administrativas estão disponíveis em CSV; XLSX poderá ser acrescentado se o processo institucional o exigir. ## Próxima tarefa recomendada Implementar a **Fase 7 — hardening e entrada em produção**: 1. configurar e validar Microsoft Entra no tenant institucional, incluindo Conditional Access/MFA; 2. confirmar com o responsável Snipe-IT a versão, o âmbito de menor privilégio dos tokens e as categorias/estados requisitáveis; validar checkout/check-in apenas no primeiro movimento institucional legítimo; 3. configurar SMTP institucional, domínio, HTTPS e gestão externa de segredos; 4. ligar os health checks a monitorização/alertas e agendar backups cifrados externos; 5. executar testes de segurança, concorrência, desempenho e end-to-end com equipamentos reais; 6. preparar formação, dados iniciais e piloto com um grupo reduzido; 7. corrigir resultados do piloto e preparar a disponibilização geral. ## Testes da última sessão Validação concluída em 5 de agosto de 2026: - `docker compose exec -T app composer test`: passou; - Laravel Pint: 141 ficheiros sem desvios; - PHPStan/Larastan nível 7: 107 ficheiros, zero erros; - Pest: 110 testes passaram, 617 asserções e 2 testes de registo ignorados por a funcionalidade estar deliberadamente desativada; - TypeScript (`tsc --noEmit`): passou; - ESLint: passou; - Prettier: passou; - Vite: builds cliente e SSR concluídos, 2314 módulos cliente e 103 módulos SSR transformados; - PostgreSQL 17: `migrate:fresh --seed` concluído, incluindo extensão `btree_gist` e restrição de exclusão temporal; - PostgreSQL 17: migration incremental do marcador `last_seen_sync_run_id` aplicada com sucesso; - PostgreSQL 17: nenhuma migration pendente; comando de expiração executado e agendamentos horário/a cada cinco minutos confirmados; - PostgreSQL 17: migration `notifications` aplicada com sucesso; - PostgreSQL 17: migration `snipe_user_id` aplicada e fluxo operacional exercitado contra API simulada; - PostgreSQL 17: migration de `portal_settings` e buffers temporais aplicada; restrição GiST recriada sobre o intervalo com margem; - smoke test HTTP: `/login` respondeu com estado 200 através do Nginx; - Vite em Docker: URL pública, HMR e CORS verificados em `localhost:5173`; corrigida a página em branco causada pela publicação anterior de `0.0.0.0:5173`; - Inertia SSR: renderização do login validada através do serviço `node`; corrigido o componente de mensagens flash que usava `usePage()` fora do contexto Inertia; - verificação visual no browser integrado: a inspeção controlada de `localhost` continua impedida por uma política administrativa temporariamente indisponível; aprovações, esclarecimentos e notificações foram validados por testes Inertia, TypeScript e builds cliente/SSR. - testes focados de Microsoft Entra e health checks: 9 passaram, com 57 asserções; - PHPStan/Larastan depois da integração OIDC: 107 ficheiros, zero erros; - TypeScript, ESLint e Prettier depois das alterações de login/segurança: passaram; - backup PostgreSQL real em formato custom criado com checksum e validado por `pg_restore --list`; - restauro real numa base temporária: passou e confirmou 12 migrations antes da remoção automática; - smoke tests via Nginx: `/health/live` devolveu `ok` e `/health/ready` devolveu `ready`. - diagnóstico de login local anterior à ligação de produção: a base de desenvolvimento estava vazia apesar de migrada; `db:seed --force` repôs 2 utilizadores ativos, 5 perfis e, nesse contexto sem Snipe-IT configurado, 18 ativos fictícios; - login real no browser validado com `demo@escola.pt`: redirecionou para `/dashboard` e não produziu erros de consola. - testes focados das salvaguardas Snipe-IT: 17 passaram, com 106 asserções, incluindo prova de ausência de `POST` em ativos atribuídos e check-in sem proveniência ou destinatário válidos; - tokens Snipe-IT confirmados como configurados e distintos; ambos responderam a uma consulta `GET` com sucesso, sem revelar credenciais ou URL; - sincronização real exclusivamente de leitura concluída: 2367 ativos processados e criados no cache local, sem qualquer pedido de escrita no Snipe-IT; - cache local após sincronização: 2367 ativos ativos, 716 atribuídos e 144 marcados como requisitáveis; - frontend: TypeScript, ESLint, Prettier e build Vite passaram novamente; Composer Audit sem avisos conhecidos; - smoke tests após a sincronização: liveness `ok`, readiness `ready` e `/login` HTTP 200. - nova recuperação do login após deteção de 0 utilizadores/0 perfis: seeder tornado idempotente e restrito a `local`/`testing`; repôs 2 utilizadores e 5 perfis sem alterar os 2367 ativos reais no cache; - login administrativo validado de ponta a ponta no browser integrado: `admin@escola.pt` redirecionou para `/dashboard` e apresentou as áreas administrativas; - testes específicos do seeder: reposição repetida sem duplicados e preservação do cache Snipe-IT configurado; suite total com 110 testes e 617 asserções; - regra permanente adicionada: testes Laravel usam `composer test`/SQLite em memória e nunca `php artisan test` diretamente sobre a configuração PostgreSQL local. ## Decisões pendentes do contexto institucional Estas decisões não bloqueiam o desenvolvimento com defaults seguros, mas devem ser confirmadas antes do piloto: - versão e URL da instância Snipe-IT; - categorias e estados requisitáveis; - tenant, client ID e redirect URI do Entra ID; - matriz final de aprovadores por categoria/departamento; - duração máxima, antecedência e margens por categoria; - domínio institucional e método de emissão TLS; - SMTP institucional. ## Próximo ficheiro a abrir Começar em `docs/PRODUCTION_CHECKLIST.md`. Para Snipe-IT, confirmar versão, categorias/estados e permissões efetivas com o responsável institucional; não executar testes de escrita. A próxima alteração de código pode preparar testes de concorrência/desempenho e observabilidade local. Entra, SMTP e o primeiro movimento Snipe-IT só ficam concluídos com validação institucional, sem guardar segredos no repositório.